Commit graph

5 commits

Author SHA1 Message Date
David
5caa923375 Invoices: the app ingests PDFs itself, with a problem-invoice inbox
Extraction was previously manual: an agent read each PDF, mapped the columns
by hand and wrote data/source/invoice-*.json. That was the only way anything
got in, and the upload button just filed the document without reading it.

That supplier's report is one fixed template — the columns sit at identical
x-positions on every invoice, and only the line count and page count vary —
so the figures are now read by coordinate instead. Deliberately not an AI
extractor: this input does not need one, and a deterministic parse that fails
loudly is worth more here than a flexible one that can hallucinate a total.

lib/invoice-parser.ts reads the header, the machine lines across all pages,
the "Plant Hire Total" row and the SUB TOTAL / Escalation / PLUS VAT /
INVOICE TOTAL block, then applies the same arithmetic gates the seed does:
lines must sum to the subtotal, each line must satisfy qty x rate = amount
and amount + VAT = total, and VAT must match the stated rate. Anything that
does not reconcile is refused with the reason — never partially imported.

Validated by parsing all five existing invoices and comparing every field
against the hand-extracted JSON: identical, including the R24,205.13-VAT
column that a plain text read gets wrong. PHI 169 was then deleted and
re-ingested through the HTTP endpoint alone and came back field-for-field
identical, with no agent involved.

- "Add Invoice" drops a PDF in and imports it; lines are matched to plant
  items by the code the invoice prints, and an unmatched line (the
  container codes MPLANT04/MPLANT05) is left unassigned rather than guessed.
- A "Problem invoices" inbox lists every PDF that failed, with the reason,
  and Retry re-reads the stored document and re-runs the parse. Retry
  re-reports rather than claiming success, and only files imported data.
- invoice_intake collection records each attempt.

pdfjs-dist is in serverExternalPackages: bundled, it rewrites its own lazy
worker import to a .next path absent from the standalone image and every
parse dies with "Setting up fake worker failed". The parser also copies the
buffer before handing it over, since pdfjs transfers and detaches it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-02 14:30:17 +02:00
David
63016895b9 Plant Register: inline editing, active toggle and cascade-delete impact
WIP carried over from the working tree, committed separately so it does not
sit inside the invoice work.

- inline Edit cells for code / name / type and an active toggle
- GET /api/assets/:id/impact returns the cascade counts so the client can
  confirm before a delete
- DELETE /api/assets/:id hard delete; PocketBase cascade clears the dependent
  income / running / cost-line rows

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-01 15:59:51 +02:00
Mega Plant
0516220110 Round 4: regressions fixed + mobile standard
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.
2026-09-12 12:05:43 +02:00
Mega Plant
b70fc8c5c7 Round 2 (Parts B-D): asset register restructure, type field, sequential codes, flat sortable table, cost_lines, removed synthetic code, fail loudly
- B.1: removed running_cost from PB monthly_income schema
- B.2: 40 of 40 machines now have income (was 32/40)
- B.3: removed syntheticCode() + auto-create branch; deleted 2 ghost Z* assets; seed fails loudly on unmatched
- C.1: collapsed 13 cost-component rows into 5 machines + new cost_lines collection; Finance/Insurance/Tracking categories removed from assets
- C.2: type field on assets derived from code prefix
- C.3: sequential codes SC01-07, OC01-02, PC01-02; fixed SITE CONTANER spelling
- C.4: flat sortable plant register table with type filter pills
- C.5: wired filter input on plant register
- D: verified term 2 (insurance) reconciles for all 5 financed machines
2026-09-12 10:24:26 +02:00
Mega Plant
acf9d52e5f Mega Plant dashboard — fix & harden (Parts 0-5) 2026-09-12 09:35:48 +02:00