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:
2026-03-06 19:08:56 -05:00
parent c429dcc033
commit efa5aca35f
9 changed files with 560 additions and 74 deletions

View File

@@ -65,10 +65,23 @@ export function PaymentsPage() {
const fmt = (v: string) => parseFloat(v || '0').toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const invoiceOptions = invoices.map((i: any) => ({
value: i.id,
label: `${i.invoice_number} - ${i.unit_number || 'Unit'} - Balance: $${parseFloat(i.balance_due || i.amount).toFixed(2)}`,
}));
const formatPeriod = (inv: any) => {
if (inv.period_start && inv.period_end) {
const start = new Date(inv.period_start).toLocaleDateString(undefined, { month: 'short' });
const end = new Date(inv.period_end).toLocaleDateString(undefined, { month: 'short', year: 'numeric' });
return inv.period_start === inv.period_end ? start : `${start}-${end}`;
}
return '';
};
const invoiceOptions = invoices.map((i: any) => {
const period = formatPeriod(i);
const periodStr = period ? ` - ${period}` : '';
return {
value: i.id,
label: `${i.invoice_number} - ${i.unit_number || 'Unit'}${periodStr} - Balance: $${parseFloat(i.balance_due || i.amount).toFixed(2)}`,
};
});
if (isLoading) return <Center h={300}><Loader /></Center>;