/** * The two quantities on an `order_items` row, and which one to use where. * * `order_items` carries two numbers that are equal on almost every row and * mean very different things on the rows where they aren't: * * `quantity` — what the CUSTOMER asked for. Drives the invoice, the * delivery note, and the "ordered" column on the picking * slip. This is the number the customer agreed to pay for. * * `order_qty` — what WE order from the supplier for this line. Drives the * PO PDF, the PO email and the PO total. The process page * labels this field "what we tell the supplier". * * They diverge in two real situations: * * - **Supplier MOQ.** Customer wants 4, the supplier won't sell fewer than * 10. `quantity = 4`, `order_qty = 10`. We bill 4 and book 6 into stock. * - **Shorting.** Customer wants 100, supplier A can only supply 40. The * operator sets `order_qty = 40` and splits the remaining 60 onto a new * line for supplier B (`splitOrderItemRemainder`). * * Getting this wrong is a money bug in both directions: reading `order_qty` * as the customer quantity over-bills on MOQ lines and under-bills on shorted * lines. Reading `quantity` as the supplier quantity puts the wrong number on * a purchase order. * * Background and the full list of miscounts this module was written to fix: * `docs/superpowers/specs/2026-08-30-quantity-handling-audit.md`. */ /** The subset of an order item these helpers need. */ export type QtyBearingItem = { quantity?: number | null; order_qty?: number | null; }; function finite(value: number | null | undefined): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } /** * What the customer buys and is billed for. * * Falls back to `order_qty` only for legacy rows that never had a * `quantity` — historically the two were written identically at creation, * so the fallback is safe and keeps old orders invoicing correctly. */ export function customerQty(item: QtyBearingItem): number { return finite(item.quantity) ?? finite(item.order_qty) ?? 0; } /** * What we order from the supplier — the number that prints on the PO. * * A literal `0` is meaningful here and is returned as-is: the operator * setting 0 means "order nothing from this supplier on this line". Only an * unset `order_qty` falls back to the customer quantity, which is the * common case where the operator never edited the supplier-side number. */ export function supplierQty(item: QtyBearingItem): number { return finite(item.order_qty) ?? finite(item.quantity) ?? 0; }