bmc_hub/migrations/100_fixed_price_agreements.sql
Christian e4b9091a1b feat: Implement fixed-price agreements frontend views and related templates
- Added views for listing fixed-price agreements, displaying agreement details, and a reporting dashboard.
- Created HTML templates for listing, detailing, and reporting on fixed-price agreements.
- Introduced API endpoint to fetch active customers for agreement creation.
- Added migration scripts for creating necessary database tables and views for fixed-price agreements, billing periods, and reporting.
- Implemented triggers for auto-generating agreement numbers and updating timestamps.
- Enhanced ticket management with archived ticket views and filtering capabilities.
2026-02-08 01:45:00 +01:00

109 lines
4.1 KiB
PL/PgSQL

-- Migration 100: Fixed-Price Agreements
-- Creates table for monthly fixed-price hour agreements with binding periods
-- Ready for future subscription system integration (v2)
CREATE TABLE IF NOT EXISTS customer_fixed_price_agreements (
id SERIAL PRIMARY KEY,
agreement_number VARCHAR(50) UNIQUE NOT NULL,
-- Future subscription system integration
subscription_id INTEGER DEFAULT NULL,
-- Customer linkage
customer_id INTEGER NOT NULL,
customer_name VARCHAR(255),
-- Product definition (v2: move to subscription_products table)
monthly_hours DECIMAL(8,2) NOT NULL CHECK (monthly_hours > 0),
hourly_rate DECIMAL(10,2) NOT NULL CHECK (hourly_rate >= 0),
overtime_rate DECIMAL(10,2) NOT NULL CHECK (overtime_rate >= 0),
internal_cost_rate DECIMAL(10,2) DEFAULT 350.00, -- For profit calculation
rounding_minutes INTEGER DEFAULT 0 CHECK (rounding_minutes IN (0, 15, 30, 60)),
-- Contract lifecycle
start_date DATE NOT NULL,
binding_months INTEGER DEFAULT 0 CHECK (binding_months >= 0),
binding_end_date DATE,
end_date DATE,
notice_period_days INTEGER DEFAULT 30 CHECK (notice_period_days >= 0),
auto_renew BOOLEAN DEFAULT false,
-- Status tracking
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'expired', 'cancelled', 'pending_cancellation')),
cancellation_requested_date DATE,
cancellation_effective_date DATE,
cancelled_by_user_id INTEGER,
cancellation_reason TEXT,
-- Billing integration (v2: subscription_billing handles this)
billing_enabled BOOLEAN DEFAULT true,
last_billed_period DATE,
-- e-conomic integration (v2: move to subscription product mapping)
economic_product_number VARCHAR(50),
economic_overtime_product_number VARCHAR(50),
-- Metadata
notes TEXT,
created_by_user_id INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for performance
CREATE INDEX IF NOT EXISTS idx_fpa_customer ON customer_fixed_price_agreements(customer_id);
CREATE INDEX IF NOT EXISTS idx_fpa_subscription ON customer_fixed_price_agreements(subscription_id);
CREATE INDEX IF NOT EXISTS idx_fpa_status ON customer_fixed_price_agreements(status);
CREATE INDEX IF NOT EXISTS idx_fpa_dates ON customer_fixed_price_agreements(start_date, end_date);
-- Trigger to auto-generate agreement_number
CREATE OR REPLACE FUNCTION generate_fpa_number()
RETURNS TRIGGER AS $$
DECLARE
new_number VARCHAR(50);
day_count INTEGER;
BEGIN
-- Count agreements created today
SELECT COUNT(*) INTO day_count
FROM customer_fixed_price_agreements
WHERE DATE(created_at) = CURRENT_DATE;
-- Generate FPA-YYYYMMDD-XXX
new_number := 'FPA-' || TO_CHAR(CURRENT_DATE, 'YYYYMMDD') || '-' || LPAD((day_count + 1)::TEXT, 3, '0');
NEW.agreement_number := new_number;
-- Calculate binding_end_date
IF NEW.binding_months > 0 THEN
NEW.binding_end_date := NEW.start_date + (NEW.binding_months || ' months')::INTERVAL;
ELSE
NEW.binding_end_date := NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_generate_fpa_number
BEFORE INSERT OR UPDATE ON customer_fixed_price_agreements
FOR EACH ROW
EXECUTE FUNCTION generate_fpa_number();
-- Update timestamp trigger
CREATE TRIGGER trigger_fpa_updated_at
BEFORE UPDATE ON customer_fixed_price_agreements
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON TABLE customer_fixed_price_agreements IS
'Fixed-price monthly hour agreements. Ready for v2 subscription system integration via subscription_id column.';
COMMENT ON COLUMN customer_fixed_price_agreements.subscription_id IS
'Future: Links to unified subscription system (Phase 2)';
COMMENT ON COLUMN customer_fixed_price_agreements.internal_cost_rate IS
'Internal hourly cost for profit margin calculation in reporting';
COMMENT ON COLUMN customer_fixed_price_agreements.binding_end_date IS
'Auto-calculated from start_date + binding_months. Enforced during cancellation.';