Budget page load now computes per-project income (net of withholding)
from confirmed sales. Overview has a full-width Income KPI showing
total confirmed net revenue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New routes: /companies/[id]/sales (list + create) and [saleId] (detail).
Per-line tax rate, single withholding % on sale. Computed totals:
subtotal, tax, gross, withholding, net receivable. Status flow:
draft → confirmed → voided. Packages linked via sale_packages junction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added linkPackage/unlinkPackage actions and a collapsible package
checklist per expense. Linked packages display as clickable cyan chips
on the expense row.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expenses now show Pending Invoice badge when no file/link attached.
Upload action saves file via existing uploads helper, optionally
pushes to Paperless-ngx if PAPERLESS_URL + PAPERLESS_TOKEN env set.
Download endpoint serves attached invoice with attachment disposition.
Paperless URL link provides a zero-integration alternative.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expenses now have invoiceFileUrl, invoiceFileName, paperlessUrl,
paperlessDocumentId for supplier invoice attachment.
New expense_packages junction links expenses to multiple packages.
New sales + sale_line_items + sale_packages tables for income tracking
with per-line tax rate and per-sale withholding rate.
Added saleStatusEnum and 4 audit events: expense_invoice_uploaded,
sale_created, sale_confirmed, sale_voided.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scheduler checks every 15min; if 24h since last FX refresh, fetches
rates for all foreign-currency accounts and updates fxRateToBase.
Uses CDN primary + Cloudflare fallback with 10s timeout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accounts now have fxRateToBase (default 1.0). The budget total query
multiplies each transaction by the account's rate, so a USD account
with rate 34.5 contributes correctly to the THB-denominated budget.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Total budget is now sum(account transaction amounts) across all
non-deleted accounts. Removed the manual 'Add Budget' action and form.
Budget page is now read-only for the total; allocations still work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
C3: Budget allocation now verifies project belongs to company
M4: Expense approve/reject scoped by company via project join
H2: OIDC cookies get secure flag on HTTPS
H3: OIDC auto-link only when email_verified by provider
H4: Content-Security-Policy + X-Content-Type-Options in hooks
M7: SSRF favicon redirect depth capped at 3
M2: File downloads use attachment disposition (not inline)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mirrors the buildfor_life_repair workflow: SSH into LXC, reset working
tree, pull, npm ci, build, db:push, restart the buildfor-life-budget
systemd service, health-check.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stretched-link pattern: absolute-positioned overlay anchor covers the
card; action controls (edit/archive/delete + inline forms) get
`relative z-10` so they float above and stay clickable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The correlated subquery in the SELECT was returning 0 for every row.
Replaced with a separate grouped-sum query joined in JS — same data, more
reliable SQL generation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three new CSVs in the export bundle, with matching README entries:
- company_bank_accounts.csv
- company_cards.csv (last4 only, joined with linked bank account name)
- company_addresses.csv
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Visible to admin, manager, and accountant. Placed between
Integrations and Export so the broader-audience tab appears first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>