mega-plant/data/source/invoice-phi171.json
David baf0bddde6 Invoices: line-item extraction, booking to plant items, VAT-inclusive income
Adds an Invoices sub-menu under /income with a summary table (one row per
invoice, click to expand the full detail) and the data model behind it.

Data:
- invoices + invoice_lines PocketBase collections, created idempotently by
  scripts/setup-invoice-collections.mjs
- line items transcribed from PHI 166/167/171 with per-invoice arithmetic
  validation in the seed; a bad invoice is refused without blocking the rest
- invoice-*.json in data/source are auto-imported, so adding an invoice is
  one file

Booking:
- manual book-in per line (Booked to), grouped by category
- POST /api/invoice-lines/:id/book moves the income row rather than adding a
  second one, so a booking can be changed freely
- scripts/book-invoice.mjs is idempotent: rows are stamped with
  monthly_income.invoice_line and replaced on re-run
- booked amount follows lib/invoice-booking-policy.json (bookInclusiveVat),
  read by both the app and the CLI so they cannot disagree

Money:
- the R24,205.13 column on these invoices is PLUS VAT, not escalation. The
  seed now rejects VAT that disagrees with the stated rate.
- booked amounts are VAT-inclusive per the client ruling of 2026-10-01; the
  VAT stays visible rather than hidden.

Uploads:
- drag-and-drop PDF attached to invoices.source_pdf
- GET /api/invoices/:id/document proxies it — the collection is rule-locked,
  so a direct PocketBase /api/files link 404s in a browser

Fixes a month-aggregation bug this introduced: buildIncomeRows assigned into
months[i], so a machine with two income rows in one month (one per invoice,
which is normal multi-site use) showed only the last. SS01 September read
R20,700 instead of R35,305 while its own YTD included both. Months now
accumulate, per-month profit is computed once after accumulation, and
lib/rows.test.ts pins it.

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

80 lines
No EOL
2.7 KiB
JSON

{
"_comment": "Extracted from PHI 171.pdf (Std_ReqHireInvoice_DN.rpt) by coordinate-aware extraction AND confirmed by reading both rendered pages visually. Totals block spans two pages: page 1 shows SUB TOTAL 25,500.00 / Escalation 0.00, page 2 shows PLUS VAT 3,825.00 / INVOICE TOTAL 29,325.00. So 3,825.00 is 15% VAT, NOT an escalation; escalation is zero. INVOICE TOTAL is VAT-INCLUSIVE. Only amount_excl is booked to income.",
"invoice_no": "PHI 171",
"invoice_date": "2026-09-18",
"order_no": "PHR 173",
"debtor": "Mega Infrastructure",
"debtor_no": "09MIS001",
"period_from": "2026-08-21",
"period_to": "2026-09-18",
"description": "Hire Plant Sep 2026 Cosmo City",
"site": "Cosmo City",
"section": "Plant Hire",
"category": "External Plant Hire",
"currency": "ZAR",
"vat_reg_no": "4830324812",
"source_file": "PHI 171.pdf",
"vat_pct": 15,
"subtotal": 25500,
"escalation": 0,
"vat": 3825,
"total_excl": 25500,
"total_incl": 29325,
"lines": [
{
"line_no": 1,
"machine_code": "MPLANT04",
"asset_code": "OC01",
"_note": "No MPLANT04 in the register; the only Office Containers are OC01 and OC02. OC01 chosen as the consistent target - change it in the Booked to column if it should be OC02.",
"description": "Office Container",
"qty": 2,
"unit": "Month",
"rate": 1500,
"rate_type": "Dry Rate",
"amount_excl": 3000,
"vat": 450,
"total_incl": 3450
},
{
"line_no": 2,
"machine_code": "MPLANT05",
"asset_code": "SC01",
"_note": "Client ruling 2026-10-01: same code, rate and period as line 2 of PHI 166 (West Park). NOT a duplicate - the same container was used on more than one site this month, so both book to the single machine SC01. Matches the target used on PHI 166.",
"description": "Storage Container",
"qty": 2,
"unit": "Month",
"rate": 1200,
"rate_type": "Dry Rate",
"amount_excl": 2400,
"vat": 360,
"total_incl": 2760
},
{
"line_no": 3,
"machine_code": "SS-01-CASE",
"asset_code": "SS01",
"_note": "Register holds SS01 and SS02, both 'CASE420 (430)'. The invoice prints SS-01, so it maps to SS01.",
"description": "430 Skidsteer",
"qty": 90,
"unit": "Hr",
"rate": 200,
"rate_type": "Dry Rate",
"amount_excl": 18000,
"vat": 2700,
"total_incl": 20700
},
{
"line_no": 4,
"machine_code": "TLB07-CASE",
"asset_code": "TLB07",
"description": "570 ST",
"qty": 7.5,
"unit": "Hr",
"rate": 280,
"rate_type": "Dry Rate",
"amount_excl": 2100,
"vat": 315,
"total_incl": 2415
}
]
}