Chrome/Blink renders <input type="date"> with a 6-digit year field
unless min/max attributes restrict the range. Added a root-layout
hook that auto-sets min=1900-01-01, max=2100-12-31 on every date
input on mount and after navigation — no need to edit 19 form files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Account creation no longer requires the user to enter an FX rate.
On create:
- If account currency == company base → fxRateToBase = 1
- Otherwise → fetchRate(accountCurrency, baseCurrency) from the
fawazahmed0 FX API (same helper the daily scheduler uses)
- Fallback to 1 if API call fails
The manual override field is still shown on the edit form for admin
corrections, and the daily scheduler keeps it fresh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Visible whenever the user can manage. Always routes to the package
creation page. The link-existing dropdown still shows when other
packages exist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
List page expenses now show a "View details →" link that routes to
the detail page. The detail page gains:
- Invoice file upload (with Paperless push if configured)
- Paperless URL link field
- Link / unlink packages to the expense (many-to-many)
Same actions exist on both pages for convenience, but the detail page
is the primary workspace for managing an expense.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous Remaining Budget card subtracted approved expenses from
the account balance sum — but postExpenseTransaction already posts
negative-amount rows to the ledger, so the balance sum already reflects
them. Replaced with:
- Available Cash (= sum of account balances)
- Allocated (with % progress bar)
- Unallocated (cash not assigned to any project)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hero row is now a two-column green/red split showing Income and
Expenses side-by-side, with a full-width Net Position card below that
colours green or red based on the sign. Budget KPIs (Remaining,
Total, Allocated) moved to a secondary row underneath.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New expense detail at /companies/[id]/expenses/[expenseId] with full
info, edit form for admin/manager/accountant, and audit log entry on
every edit (`expense_updated`). Project view expense rows are now
clickable and navigate to the detail page. Ledger re-posts
automatically if an approved expense's amount or account changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>