A purchase order buys parts and repairs against a plant item, so its money is
running cost and belongs in the same per-machine, per-month series the Overview
already reads. Without it September showed nothing at all.
Merged at READ time, never copied into monthly_running_costs: that collection is
deleted and rebuilt by every seed, so a purchase written there would vanish on the
next one and have to be re-uploaded. purchase_lines is never deleted, so the money
lives in one place and the Overview adds it up.
- Month comes from the PO date (when it was ordered), not "Required By":
MEGPPO50532 is dated 07/10 but required 29/08 and those must not be confused.
- Only lines allocated to a machine are counted. An unallocated line is real money
not yet on a plant item; it stays on /purchases, reported, rather than being
hidden or added to the fleet total.
- A PO with no readable date is excluded and named, never guessed into January.
- Bottom total row shows incl ABOVE excl per month and for the YTD. Workbook
figures carry no VAT column, so RUNNING_VAT_PCT (15, confirmed by every PO line)
is the single constant to revisit if they are ever restated ex-VAT.
September now reads R 5 842 incl / R 5 080 excl from the two September POs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A screenshot at phone width showed an "INVOICE | LINES | TOTAL | STATUS"
header row while the data under LINES was money. The header and its data cell
had drifted apart: the earlier pass hid the Lines <td> but not the matching
<th>, because the loop that added the hide classes only matched text-left
headers and Lines is text-right. So the Total values rendered under a heading
that named a column which no longer existed.
The same screenshot showed the totals clipped — "41,710.50" reading as
"710,50". With only three columns left, the 520px min-width was still forcing
a sideways scroll, and the opaque sticky first column then sat on top of the
left of the money values. The mobile floor is gone, so the three remaining
columns size to their content, and the sticky and status gutters are trimmed
at mobile widths to let them fit 375px.
Below sm the table is now exactly Invoice, Total, Status — and every header
matches a visible cell.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part A — Round 3 regressions:
- A.1: verify-cost-reconciliation.mjs now loads .env.local, wraps in try/catch, exits 1 on any failure
- A.2: Vehicle Costs restored Santam — back to R 14,996.31 (matches row-by-row sum)
- A.3: incl<excl / VAT math / VAT>20% now FATAL — seed exits 2 with full report (4 anomalies on Charl-CCS + Buildsmart)
- A.4: Reconciliation target R 694,962.93 with both deltas itemised (Personnel +R19,800, Santam -R5,322.44, unexplained R 0.00)
- A.5: 'Lines' view uses actual lines (recharts LineChart), no fills; fills only in 'Cost stack'
Part B — Mobile standard:
- B.1: header wraps to 2 rows on mobile; YTD badge cards stack; page-wide overflow eliminated on /income, /charts, /overheads, /running, /expenses
- B.2: IncomeView card row layout stacked — name gets its own line on mobile, never truncated to single char
- B.3: Plant type filter is a <select> dropdown below lg (was 18 pills wrapping to 13 rows); same for sort on /income and /running
- B.4: Plant table first column sticky; -webkit-overflow-scrolling: touch; tables scroll inside their own container
- B.5: All buttons/links use min-h-[44px] tap targets; text below 12px is bumped where it matters
- B.6: Charts legend wraps below title; chart min-height h-44 → h-52 on mobile
- B.7: paddingTop env(safe-area-inset-top) on sticky header for notch/home indicator clearance
AGENTS.md created with the standing mobile checklist rule.