feat: add flexible billing frequency support for invoices
Assessment groups can now define billing frequency (monthly, quarterly, annual) with configurable due months and due day. Invoice generation respects each group's schedule - only generating invoices when the selected month is a billing month for that group. Adds a generation preview showing which groups will be billed, period tracking on invoices, and billing period context in the payments UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
57
db/migrations/011-invoice-billing-frequency.sql
Normal file
57
db/migrations/011-invoice-billing-frequency.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
-- Migration 011: Add billing frequency support to invoices
|
||||
-- Adds due_months and due_day to assessment_groups
|
||||
-- Adds period_start, period_end, assessment_group_id to invoices
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_schema TEXT;
|
||||
BEGIN
|
||||
FOR v_schema IN
|
||||
SELECT schema_name FROM information_schema.schemata
|
||||
WHERE schema_name LIKE 'tenant_%'
|
||||
LOOP
|
||||
-- Add due_months and due_day to assessment_groups
|
||||
EXECUTE format('
|
||||
ALTER TABLE %I.assessment_groups
|
||||
ADD COLUMN IF NOT EXISTS due_months INTEGER[] DEFAULT ''{1,2,3,4,5,6,7,8,9,10,11,12}'',
|
||||
ADD COLUMN IF NOT EXISTS due_day INTEGER DEFAULT 1
|
||||
', v_schema);
|
||||
|
||||
-- Add period tracking and assessment group link to invoices
|
||||
EXECUTE format('
|
||||
ALTER TABLE %I.invoices
|
||||
ADD COLUMN IF NOT EXISTS period_start DATE,
|
||||
ADD COLUMN IF NOT EXISTS period_end DATE,
|
||||
ADD COLUMN IF NOT EXISTS assessment_group_id UUID
|
||||
', v_schema);
|
||||
|
||||
-- Backfill due_months based on existing frequency values
|
||||
EXECUTE format('
|
||||
UPDATE %I.assessment_groups
|
||||
SET due_months = CASE frequency
|
||||
WHEN ''quarterly'' THEN ''{1,4,7,10}''::INTEGER[]
|
||||
WHEN ''annual'' THEN ''{1}''::INTEGER[]
|
||||
ELSE ''{1,2,3,4,5,6,7,8,9,10,11,12}''::INTEGER[]
|
||||
END
|
||||
WHERE due_months IS NULL OR due_months = ''{1,2,3,4,5,6,7,8,9,10,11,12}''
|
||||
AND frequency != ''monthly''
|
||||
', v_schema);
|
||||
|
||||
-- Backfill period_start/period_end for existing invoices (all monthly)
|
||||
EXECUTE format('
|
||||
UPDATE %I.invoices
|
||||
SET period_start = invoice_date,
|
||||
period_end = (invoice_date + INTERVAL ''1 month'' - INTERVAL ''1 day'')::DATE
|
||||
WHERE period_start IS NULL AND invoice_type = ''regular_assessment''
|
||||
', v_schema);
|
||||
|
||||
-- Backfill assessment_group_id on existing invoices from units
|
||||
EXECUTE format('
|
||||
UPDATE %I.invoices i
|
||||
SET assessment_group_id = u.assessment_group_id
|
||||
FROM %I.units u
|
||||
WHERE i.unit_id = u.id AND i.assessment_group_id IS NULL
|
||||
', v_schema, v_schema);
|
||||
|
||||
END LOOP;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user