109 lines
4.1 KiB
MySQL
109 lines
4.1 KiB
MySQL
|
|
-- 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.';
|