Add special financing feature

Data model:
- Migration 003: financing_plans and financing_payments tables
- Plans track total amount, due date, paycheck assignment (1, 2, or split)
- Payments linked to paycheck records, one per period

Payment calculation:
- Auto-calculated: (total - paid_so_far) / remaining_periods
- Split plans halve the per-period amount across both paychecks
- Recalculates each period as payments are made

Financing page (/financing):
- Create/edit/delete plans with name, amount, due date, paycheck assignment
- Progress bar showing paid vs total
- Overdue badge when past due with remaining balance
- Paid-off plans moved to a separate section

Paycheck view:
- New Financing section per column with payment checkbox
- Overdue badge on individual payments
- Running paid/total shown in bill meta
- Financing payments included in remaining balance calculation
- Auto-closes plan and reloads when fully paid off
- Lazy generation works: first interaction generates paycheck and payment records

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 20:28:09 -04:00
parent 45383b80cf
commit 2d152f301d
7 changed files with 705 additions and 2 deletions

View File

@@ -0,0 +1,21 @@
-- financing_plans: tracks a deferred/no-interest financing arrangement
CREATE TABLE IF NOT EXISTS financing_plans (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
total_amount NUMERIC(12, 2) NOT NULL,
due_date DATE NOT NULL, -- must be paid off by this date
assigned_paycheck INTEGER, -- 1, 2, or NULL (split across both)
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- financing_payments: one row per paycheck period for each active plan
CREATE TABLE IF NOT EXISTS financing_payments (
id SERIAL PRIMARY KEY,
plan_id INTEGER NOT NULL REFERENCES financing_plans(id) ON DELETE CASCADE,
paycheck_id INTEGER NOT NULL REFERENCES paychecks(id) ON DELETE CASCADE,
amount NUMERIC(12, 2) NOT NULL, -- calculated at generation time
paid BOOLEAN NOT NULL DEFAULT FALSE,
paid_at TIMESTAMPTZ,
UNIQUE (plan_id, paycheck_id)
);