Commit graph

2 commits

Author SHA1 Message Date
5634c067c4 feat(running): the year to date AND the live month on the running-cost pages
The client asked for two things on the running-cost pages: drop the two prose
notes, and show the year-to-date figure next to the total for the current month.

- /running loses the "Shown to Jan, Feb..." and "A re-cut of costs..."
  paragraphs. The tiles carry the year and the month instead.
- /running shows "Nov so far" beside "YTD running cost", and the "made of"
  line is printed for the month as well as for the year, so the month's total
  is explained on the page and not only in the grid.
- The cards follow the income cards: YTD spend | Nov so far side by side from
  md, the month on its own line below md, and the sparkline yields its width
  below xl so the vehicle NAME is never truncated to pay for a decorative
  trend. Below sm the name wraps instead of being cut short.
- /running/insurance shows the premium in force this month, and its
  year-to-date figure now stops at the months that have happened. December is
  a rate on file, not money spent, so including it made this page a full year
  while the Overview showed eleven months - the same cost kind reading as two
  different numbers on two screens.
- /running/financing shows "Committed for Nov" beside a new year-to-date
  instalment figure.

One rule, three screens. elapsedMonths() in lib/income-month.ts counts the
months that have happened and is the same clock the income pages use, and
financeByAsset() / fleetFinanceByMonth() in lib/running-costs.ts are the only
finance rule the Overview and the financing register both read. The Overview's
insurance and finance year-to-date figures now equal the insurance and
financing pages' figures exactly.

lib/running-month.test.ts asserts it, including the insurance cross-check
between the register and the Overview.

A settled agreement still contributes nothing to any month - the instalments
TLB06 was paying in January-October are not in the Overview's finance figure.
The client confirmed the financing is correct as it stands, so that is left
alone and recorded in AGENTS.md rather than quietly changed.
2026-11-05 01:42:23 +02:00
David
f85b9378a2 Add Running Costs sub-pages: insurance, financing, purchases, oil, operators, mechanics
Splits the Running Costs group into six purpose-built pages, each with its own
collection and its own source data, plus a coordinate parser for purchase orders.

Insurance (R503 330/yr, 20 vehicles)
  A premium can move every month or three, so it is stored as a RATE PERIOD in
  insurance_rates and each month's cost is resolved against the rate in force
  that month. cost_lines insurance rows are derived from those periods. An absent
  rate is null, never 0 — nobody has entered the date, which is not the same as
  being insured for free.

Financing (20 agreements, R428 585/mo)
  Instalment from the sheet; start date, paid-off date and the settled toggle are
  human-entered and preserved across re-seeds. Months-left and outstanding are
  null until a date is entered, never R0 on a live debt. The two paid-off
  agreements (WB02, DT01) show as settled and are excluded from the run-rate.

Purchases + Oil Stock
  Uploads PO PDFs the way /income/invoices does. The parser reads columns by
  x-position, because plain text extraction reorders them, and refuses any PO
  whose lines do not sum to the sub-total. A cost code either names a machine
  ("SS-02" -> SS02) or a stock store ("MPLANT, ENGOIL") — never both, never
  guessed. Oil POs move litres into oil_stock_movements instead of onto a machine;
  stock is the sum of movements so the first usage is a subtraction from history.

Operators (19) and Mechanics (2)
  Both are people with an employee number, separately reconcilable by the seed so
  someone added in the app survives. Mechanics get a badge QR; a mechanic with no
  employee number cannot be printed, because the code is what the QR encodes.

Cross-cutting
  - The seed no longer deletes operators, mechanics or oil items: people are added
    in the app, and those rows exist in no file.
  - Re-pointing allocations that PocketBase clears because `assets` is rebuilt
    with new ids each seed (operators, purchase lines).
  - One QR encoder for screen and PDF, so a printed code cannot drift from the one
    on screen. pdf-lib writes real vector PDFs; pdfjs-dist only reads them.
  - Every figure is null-safe: unknown is never rendered as zero.

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