Commit graph

3 commits

Author SHA1 Message Date
7b3ec2fbd8 feat(income): posted income per site, and the LIVE month on the overview
Two changes to the income overview, sharing one rule for what "the month"
means (lib/income-month.ts).

POSTED INCOME PER SITE

/income gains Vehicles / Sites views, and /sites defaults to the SAME
posted-income view with a separate Internal billed invoices view.
buildSiteIncomeRows (lib/site-income.ts) is the shared calculation: actual
monthly_income supplies the amount, year and month, and the linked invoice
supplies its historical site. It never uses assets.site, fuel-log
locations or today's map assignment.

- Unposted invoice lines are billing, not posted income.
- Historical workbook income with no site evidence stays in Unallocated
  historical income; external income, whose site field names the SUPPLIER,
  stays in Unallocated external income.
- Missing source references stay visible as unallocated invoice income.
  No group is dropped to make a site list look cleaner and no site is
  guessed to force a total.
- No site profit is calculated without an explicit cost-allocation rule.
- Invoice writes use revalidateInvoicePages, which invalidates both
  registers, Income, Sites/details, Charts and Dashboard.

THE LIVE MONTH

The tiles show the year-to-date income and profit AND the current month,
and every vehicle card shows the current month income beside its year
figure.

- The month is the live CALENDAR month and it is shown even at R0. Income
  for a month is usually entered near the end of it, so the current month
  is a figure that starts at R0 and grows.
- The month index is computed on the SERVER and passed down as a number.
  The browser never works it out for itself: two clocks disagreeing would
  show two different months on the same page.
- A month profit is a dash until that month costs are posted. September
  and October carry invoice income with no posted fixed costs (their costs
  exist only as forecast rows), so a profit there would be income minus
  zero - a 100% margin that does not tie back to the year-to-date profit.
  It becomes a number by itself the moment the workbook posts that month.
- The 12-month strip shows the live month as R0 rather than a dash, so a
  card cannot contradict itself. Months after it stay a dash.
- The sparkline on a collapsed card is hidden below xl: the content column
  is only ~700px wide between md and xl, and the vehicle name must not be
  truncated to pay for a decorative trend.
2026-11-05 00:19:06 +02:00
David
813c187272 Sites: a card list that reads like the income screen
The client likes the vehicles overview and asked for the site list to work
the same way, so /sites now defaults to Cards — same toolbar, same card
shape, same 12-month strip on expand, same three-up footer as IncomeView.
The old table is kept as a Grid toggle rather than deleted.

The detail link lives inside the expanded panel only: a card is a <button>,
so a nested <a> there would be invalid HTML.
2026-10-03 09:20:20 +02:00
David
876619c3ac Invoices: keep the PDF on the invoice, and search the invoice's own words
Two things this question turned up.

The upload DID store the PDF, but on the intake record, never on the invoice
— so "Open invoice PDF" 404'd for every invoice imported through the app
while working for the older ones. The file now lives on invoices.source_pdf
and the intake record keeps only the provenance (who sent it, what was
parsed), rather than a second copy of the file. The document endpoint also
falls back to the intake record, which makes the seven already-stored
documents reachable again without a migration. All 10 now open.

The machine combobox searched only the register's own words, so an invoice
line reading "CB22 2.2 TON Roller Vibration" could never be matched to the
asset filed as "CAT 2,4TON RV" — the words simply were not in the list. The
line's own invoice code and description are now searchable too, so the two
sides of the same machine can actually be found from either direction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-02 19:09:16 +02:00