mega-plant/data/source/invoice-phi166.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

91 lines
No EOL
2.9 KiB
JSON

{
"_comment": "Extracted from PHI 166.pdf (Std_ReqHireInvoice_DN.rpt) by coordinate-aware extraction AND confirmed by reading the rendered pages visually. The totals block on page 2 reads: SUB TOTAL 161,367.50 / Escalation 0.00 / PLUS VAT 24,205.13 / INVOICE TOTAL 185,572.63. So 24,205.13 is 15% VAT, NOT an escalation - escalation is zero on every line. INVOICE TOTAL 185,572.63 is therefore VAT-INCLUSIVE. Do NOT hand-edit a figure to make it balance - the seed re-checks all of this and fails loudly.",
"invoice_no": "PHI 166",
"invoice_date": "2026-09-18",
"order_no": "PHR 170",
"debtor": "Mega Infrastructure",
"debtor_no": "09MIS001",
"period_from": "2026-08-21",
"period_to": "2026-09-18",
"description": "Plant Hire SEP 2026 West Park",
"site": "West Park",
"section": "Plant Hire",
"category": "External Plant Hire",
"currency": "ZAR",
"vat_reg_no": "4830324812",
"source_file": "PHI 166.pdf",
"vat_pct": 15,
"subtotal": 161367.5,
"escalation": 0,
"vat": 24205.13,
"total_excl": 161367.5,
"total_incl": 185572.63,
"lines": [
{
"line_no": 1,
"machine_code": "MG-01-DEZI",
"asset_code": "MG01",
"description": "MG80 Motor Grader",
"qty": 169.5,
"unit": "Hr",
"rate": 345,
"rate_type": "Dry Rate",
"amount_excl": 58477.5,
"vat": 8771.63,
"total_incl": 67249.13
},
{
"line_no": 2,
"machine_code": "MPLANT05",
"asset_code": "SC01",
"_note": "Client ruling 2026-10-01: MPLANT05 also appears on PHI 171 (Cosmo City) over the same period. NOT a duplicate - the same container was used on more than one site this month, so both lines book to the single machine SC01. The register has no MPLANT05 and seven Site Containers (SC01-SC07); SC01 was chosen as the consistent target - change it in the Booked to column if it should be another.",
"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": "RV-03-CAT",
"asset_code": "RV03",
"description": "CP-533C 12 TON Roller Vibration",
"qty": 171.5,
"unit": "Hr",
"rate": 240,
"rate_type": "Dry Rate",
"amount_excl": 41160,
"vat": 6174,
"total_incl": 47334
},
{
"line_no": 4,
"machine_code": "TLB05-CASE",
"asset_code": "TLB05",
"description": "570 T",
"qty": 171.5,
"unit": "Hr",
"rate": 280,
"rate_type": "Dry Rate",
"amount_excl": 48020,
"vat": 7203,
"total_incl": 55223
},
{
"line_no": 5,
"machine_code": "TP601-TATA",
"asset_code": "TP601",
"description": "1518 6 Cube Tipper Truck",
"qty": 43.5,
"unit": "Hr",
"rate": 260,
"rate_type": "Dry Rate",
"amount_excl": 11310,
"vat": 1696.5,
"total_incl": 13006.5
}
]
}