Flag over-billed bills on PO return + standing COGS on cancel (audit-C4, audit-H8)#194
Conversation
…on cancel (closes audit-C4, audit-H8) Phase 1 (alert-only) for two purchasing-reversal gaps the workflow audit found: audit-C4 — PO returns vs. supplier bills: returnPurchaseOrder reverses stock and FIFO cost layers but never touches the supplier invoices already recorded against the PO. Returning billed goods left the AP liability at the full amount with no prompt. New pure helper computePurchaseOrderOverBilling() sums billed qty/value per PO line and compares against the quantity kept (received − returned). When a billed line is now over-billed, the return emits a return_overbilled_bill WARNING (machine-readable metadata: per-line over-billed qty/value + bill references) and the PO detail page shows an amber alert telling the operator to raise a supplier credit. audit-H8 — cancellation leaves consumed-unit COGS standing: reversePurchaseOrderCostLayersForCancellation only reverses remainingQty; units already sold/used keep COGS sourced from the cancelled receipt. New summarizeConsumedCostLayers() / readPurchaseOrderConsumedCostForCancellation() compute consumed = receivedQty − remainingQty and its value. The cancellation service reads this BEFORE the reversal runs (so the about-to-be-zeroed remaining qty is not miscounted as consumed), emits a cancelled_consumed_cogs_standing WARNING when non-zero, returns it on the result, and the client shows an amber post-cancel alert. Cancellation stays fully blocked once an invoice exists. Neither posts an accounting correction yet — phase 2 (credit memo / COGS journal) is tracked separately. Pure functions are DI-seam testable. Tests: purchasing-reversal-alerts (6) + po-cancellation consumed-cost & WARNING paths; 86/86 purchasing suite, type-check + lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…l review) Codex adversarial pass on PR #194: HIGH — C4 over-billing was read AFTER the return tx committed, so a concurrent second return could inflate the figure. Moved the read inside the transaction (qtyReturned now reflects exactly this return) and return the summary from db.$transaction; logging stays outside, isolated. HIGH — the consumed-COGS WARNING logActivity sat in the outer try with no local guard; a log failure after a committed cancellation would surface as { success:false }. Wrapped it in its own try/catch. MEDIUM — over-billing 'bills' listed EVERY invoice on the PO, not just the ones that billed an over-billed line; the WARNING count overstated N. Now tracks contributing invoiceIds and names only those. MEDIUM — the PO-detail alert heading was backwards ('returned goods exceed what was billed back'); corrected to 'supplier bill exceeds goods kept'. MEDIUM — negative netReceived (corrupt qtyReturned > qtyReceived) would inflate over-billing; clamp netReceived to >= 0. MEDIUM — dropped the hardcoded £ in both WARNING descriptions (values are base currency; label them so a non-GBP base isn't mislabelled). LOW — clarified the UI bills line shows gross totals incl. tax; documented why the consumed-cost reader keeps ORDER BY / omits FOR UPDATE; switched the over-billing/consumed epsilons to lte(0) for Decimal-exact comparison. Tests: +2 (contributing-bills filter, negative-clamp); 88/88 purchasing suite, type-check + lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex adversarial review — findings + fixes (commit 5f4e3b7)Codex was at its usage cap; the adversarial pass ran by hand with equal rigour. It found two HIGH issues plus several MEDIUM/LOW — all addressed. HIGH — C4 over-billing read after commit (race). The over-billing figure was re-read from the DB after the return transaction closed, so a concurrent second return could increment HIGH — consumed-COGS WARNING could fail a committed cancellation. The new MEDIUM — over-billing named every bill on the PO. MEDIUM — alert heading was backwards. "Returned goods exceed what was billed back" described the opposite scenario. Fixed to "Supplier bill exceeds goods kept after return." MEDIUM — negative MEDIUM — hardcoded LOW — clarity/consistency. UI bills line now states the totals are gross (incl. tax); added a comment on why the consumed-cost reader keeps Verified-clean angles (no action needed)
Validation
Deferred to phase 2 (alert-only scope, accepted)
|
Wave 1, item 4 of the workflow-audit remediation (epic onetwo3d-ims-r3xh). Phase-1 alert-only coverage for two purchasing-reversal gaps. Neither posts an accounting correction yet — phase 2 (credit memo / COGS journal) is tracked separately.
audit-C4 — PO returns don't flag the over-billed bill
returnPurchaseOrderreverses stock + FIFO cost layers but never touches supplier invoices recorded against the PO. Returning billed goods left the AP liability at the full amount silently.computePurchaseOrderOverBilling()sums billed qty/value per PO line (across all bills) and compares against the quantity now kept (received − returned).return_overbilled_billWARNING activity log with machine-readable metadata (per-line over-billed qty/value + bill references).audit-H8 — cancellation leaves consumed-unit COGS standing
reversePurchaseOrderCostLayersForCancellationonly reversesremainingQty; units already sold/used keep COGS sourced from the cancelled receipt — P&L silently carries it.summarizeConsumedCostLayers()(pure) +readPurchaseOrderConsumedCostForCancellation()compute consumed =receivedQty − remainingQtyand its value.cancelled_consumed_cogs_standingWARNING, returnsconsumedCoston the result, and the client shows an amber post-cancel alert. Cancellation stays fully blocked once an invoice exists (unchanged).Tests
purchasing-reversal-alerts.test.ts— 6 cases (over-billing across multiple bills, no-over-billing, unbilled PO, freight lines with no poLineId).po-cancellation.test.ts—summarizeConsumedCostLayerstotals + the service WARNING/consumedCost/read-ordering path; existing idempotency test updated for the new dep + result shape.Closes onetwo3d-ims-pbu4, onetwo3d-ims-j5ic.