WFDASH-WEBAPP/src/app/api/v1/delivery-notes
David dce54934f0 fix(invoicing): bill only what's collected, support partial pickups + kit splits
The invoice qty rule used to read the customer's order qty regardless
of how much the driver actually collected. QuickBooks ended up billing
8-of-10 on one line and 7-of-10 on the other for Kusasalethu PO 390588
(INV10924) — a 5-unit overbilling that the mine's receiving team flags
as suspicious.

The rule now caps the invoice qty at min(qty_collected, quantity):
  - Short pickup → bill what's collected, the row stays at
    item_status="ready_invoice" with qty_invoiced < quantity, and a
    follow-up invoice fires automatically when the rest arrives.
  - MOQ overflow → still capped at the customer's order qty; the
    surplus-book flow continues to route the overflow to stock.

The Invoicing page now shows "N short" / "N units short" badges on
short-pickup rows, a "N hidden" badge when supplier-side rows are
suppressed, and a "Show supplier-side lines" toggle (default OFF) so
the invoicing operator doesn't see kit-component remainders unless she
needs to. A split-remainder dialog on the order process page lets the
operator explicitly flag a remainder as exclude_from_invoice=true when
the original line was a kit (e.g. "M20 bolt comes with nut and washer"
split across 3 suppliers).

PB schema additions (one-time, run scripts/nss-add-invoice-columns.ts):
  - order_items.qty_invoiced            (number)
  - order_items.customer_invoice_number (text)
  - order_items.invoiced_date           (date)

These three columns were referenced throughout the codebase but never
existed on the live schema, so writes were being silently dropped. The
schema fix unblocks the reconcile flow that stamps them after every
QB webhook returns.

Backfill for the live Kusasalethu order: scripts/nss-fix-kusasalethu-inv10924.ts
updates order_items.quantity 10→8 / 10→7 to match what was collected,
stamps the new columns, and flips order.invoiced=true.

New server helper reconcileOrderItemInvoicedStatus() runs after each
QB webhook and per-row decides:
  - item_status = "invoiced"     if qty_invoiced >= quantity (fully billed)
  - item_status = "ready_invoice" otherwise (still partial — stays in
    the to-invoice tab for a follow-up invoice once the rest arrives)

Wired into both the bulk invoicing page and the delivery-note-invoice
route. markOrderInvoiced now compares against qty_invoiced vs
quantity rather than the flat item_status check, and its error
message names the outstanding shortfall per row.
2026-09-17 13:53:36 +02:00
..
[id] fix(invoicing): bill only what's collected, support partial pickups + kit splits 2026-09-17 13:53:36 +02:00
combine
items/[id] feat(quotes): split attachments into internal vs outbound with operator upload UI 2026-09-09 13:43:56 +02:00
route.ts