MEGAINFRA-WEBAPP/docs/audits/DELIVERY_NOTES_AGENT_HANDOFF.md
DEls 3ba004ffd3 fix(dn): harden delivery-note matching, posting and PO reconciliation
Fresh duplicate check before posting, dismiss route sets stage, free accessory
lines no longer crash promotion, readiness treats only posted notes as terminal,
document-reference/usable-PO helpers, PO delivery reconciliation API + docs and
audit register.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-28 21:16:24 +02:00

13 KiB
Raw Blame History

Delivery-note inbox: agent handoff

Checkpoint: 27 September 2026, 22:43 Africa/Johannesburg. The user's request is to process the delivery-note inbox PO by PO, book each genuine delivery once at the PO's unit and rate, preserve supporting scans, and make future matching/reconciliation easier. The user expressly confirmed that the Vula 2.5 m pipe lines are ordered and booked in metres. Work has begun on genuine Vula PO MEGCPO9109 (0oxer2vuae370m4).

Read this file and the two linked audits before modifying data. The working tree contains extensive uncommitted edits by both Codex and a previous MiniMax agent. Preserve them; do not reset or blindly commit .env.local, .claude/worktrees, .codex, chat export.md, or temporary scripts. Follow root AGENTS.md: this is a changed Next.js version, so read the relevant node_modules/next/dist/docs/ guide before editing Next code.

Live checkpoint: do not post these again

The Vula PO now has seven posted delivery receipts, verified by the read-only reconciliation endpoint:

Supplier invoice delivery_notes.id PO quantities booked
VCINV073787 urvc9qjgg9p14pl 450 mm: 137.5 m
VCINV074058 21gz03njx23wh4w 1050 mm: 27.5 m
VCINV074078 fo06jfxxi81qp4c 1050 mm: 27.5 m; zero-booked free rings
VCINV074143 r1mj5nt4yo4y9fx 1050 mm: 32.5 m; zero-booked free rings
VCINV074056 zcrehww97ulrpt6 1050 mm: 27.5 m; zero-booked free rings
VCINV073935 3tdjr8cw3wv4jqb 750 mm: 27.5 m; 900 mm: 27.5 m
VCINV073801 b4jtyc0wj3l9hd0 450 mm: 85 m; 600 mm: 32.5 m

The last two were posted in this session from source pages 36 and 28 respectively, after visual review. They returned HTTP 200 and were read back from the ledger. VCINV073801's other scans on pages 33, 39, and 59 were marked dismissed with manual_overrides.duplicate_of = wiw9a6r4hywr8vg and dn_review_events audit entries. Its retained page 28 candidate is now posted. VCINV073935 had no same-reference scan in the batch. No other unposted Vula invoice has been promoted during this session.

The live PO balance after those postings is:

PO line Line ID Ordered m Posted m Open m Posted value ex VAT
450 mm p6wa4072t7pvztl 222.5 222.5 0 R75,574.35
600 mm s4dss10wzjrkfoj 237.5 32.5 205 R16,714.75
675 mm ydiqzdj7krnm34w 150 0 150 R0.00
750 mm 9bmxea7bhsyt80z 27.5 27.5 0 R21,678.25
900 mm 1qxm32y8bml4k1x 78 27.5 50.5 R28,320.33
1050 mm qgqpfpv460bceej 252.5 115 137.5 R182,162.30

Ordered total is R811,626.64 ex VAT; posted total is R324,449.98 ex VAT. The 900 mm line has a half-cent rounding boundary: 27.5 m × R1,029.83/m is R28,320.33, matching the printed invoice amount. currencyLineValue now uses integer cents and quantity ten-thousandths to avoid the earlier R28,320.32 preview. Full test suite: 185 files / 1,844 tests passed, npm run typecheck passed, git diff --check passed after the change.

Remaining Vula delivery decisions

There are ten distinct unposted Vula invoices on this PO. The table gives one proposed source page to retain, but compare its image and current ledger again before any posting. Pages are in batch 3ce6tdvkinny9is. Dismiss extra scans only with an explicit audit link to the retained candidate; original page files remain in PocketBase. The signed delivery notes on pages 55 and 72 are separate supporting documents, not extra deliveries.

Invoice / delivery ref Candidate to retain (page) Other pages Proposed new PO quantity and ex-VAT value Particular check
VCINV073799 / VDN074329 z0rriva39pj041f (18) 31, 70 600 mm: 85 m / R43,715.50 Page 70 is stamped “Tax Invoice copy” and prints a different item code while description, qty, ref and total agree. Inspect before dismissing it.
VCINV073800 / VDN074334 r65odltqtd5xjmi (32) 29, 38, 58 600 mm: 35 m / R18,000.50; 675 mm: 50 m / R37,312.00 Page 29's OCR said VCINV073900; its scan clearly prints VCINV073800. Audited override already saved.
VCINV073802 / VDN074348 lyw6s31sr7getrf (34) 30, 37, 57 675 mm: 75 m / R55,968.00 Page 30 OCR read item code P67S-100; image prints P675-100.
VCINV073832 / VDN074366 8rjtu7f9m5guwvz (26) 35, 41, 61 600 mm: 85 m / R43,715.50 Verify the same 34 pipes across scans.
VCINV074055 / VDN074559 le894m86keazh3h (48) 22, 66 1050 mm: 27.5 m / R43,560.55; 11 free rings Page 22 OCR misread VDN074555; audited override corrects it to VDN074559. If choosing page 48, check runner's terminal-candidate pending-identity behavior noted below.
VCINV074057 / VDN074588 yex1b01hzfaeq1b (20) 50, 53, 68 1050 mm: 27.5 m / R43,560.55 Same invoice in four scans.
VCINV074077 / VDN074606 tn9ho8j9l2ron2r (65) 46 1050 mm: 27.5 m / R43,560.55; 11 free rings Page 46 OCR said VCINV0746T7; source prints VCINV074077. Audited override saved.
VCINV074079 / VDN074616 2x71gsd21gobas8 (23) 45, 64, 71 1050 mm: 27.5 m / R43,560.55; 36 free rings Rings are explicit R0 extras, not pipe metres.
VCINV074063 / VDN074597 5xa0gs5dinunxff (47) supporting signed DN 55 1050 mm: 27.5 m / R43,560.55; 11 free rings Compare signed DN 55 and invoice 47; count once. DN has no price and must not be posted as another receipt.
VCINV074156 / VDN074699 i69vrng0sn0dj98 (43) supporting signed DN 72 675 mm: 15 m / R11,193.60 Compare signed DN 72 and invoice 43; count once.

If all ten prove genuine and are booked once, 600 mm and 1050 mm reach their PO quantities exactly. The 675 mm line should still have 10 m (four 2.5 m pipes) outstanding; 900 mm should still have 50.5 m outstanding. Do not invent documents to close those balances. The invoice and delivery-note references must both be checked for duplicates before posting.

Already posted Vula invoices still have active scan copies in the inbox: VCINV073787 (pages 27, 40, 60), VCINV074056 (21, 49, 67), VCINV074058 (51, 54, 69), VCINV074078 (44, 63), and VCINV074143 (42, 62). Their posted receipts are listed above. The signed DN on page 56 (VDN074673) supports posted VCINV074143 and must not add 13 pipes again. Compare each to the posted receipt/slip, then stand down the duplicate scan with an audit event.

Other POs and the current inbox

After the two new postings and three verified-copy dismissals, the numbered inbox had 57 Needs matching and zero Ready to post. Some 14 blank split-review placeholders in other batches are excluded from that numbered count. Outside the Vula PO, eleven numbered scans remain:

Document Candidate ID Current PO ID Main task
IN018888 hmwsh8kymxu2vel zr44xtb5u567npw Avidan pipe lengths/rolls; resolve PO line units and product mapping.
IO019079 cgcpg2nu2uhn3dz zr44xtb5u567npw Avidan related delivery; compare kit/component lines.
INA00005204 n9g3t22xcvr2iwu ctcz8y9kej4n51r Signs R Us; returned line and several unlinked components.
INV70963 ie00wpe98qtnzks none Ikhasi; OCR has MEGCPO9199 in alternate refs but PO field blank. Verify printed label and actual order before linking.
INV71060 xyfjrk4yx9ox7d9 none Ikhasi; OCR has MEGCP09255 in alternate refs but PO field blank. Verify.
090005815 br2uwjqtjlbyt4v none Raumix; printed #MEGCPO9185, genuine matching PO not found. Do not substitute a nearby order.
IRN010002024 ktrfy7v4h6alpyz y35epf1bpx9vai8 Raumix; distinct invoice despite shared supplier order BORN010000034; inspect ticket AS0002066.
22433700 7bhke1nqi5qjjfh jsx87ptwjvj1iwk Builders collection note already has posted counterpart; compare and stand down if same.
DEL53312 zxjwfcera04us6a m67a4jwi9ges4ma Product decision.
LI513543 nm0mbh6chvhj2vy s24atzm69empbwv Supplier relation missing despite checks; verify supplier identity before linking.
T0255611 qu12z7bt0r2lbbf pslduk2tyr4ool0 No line items extracted; read source before any booking.

Historical per-scan detail is in 2026-09-27-delivery-note-matching-review.md and the more recent 2026-09-27-delivery-note-remediation.md. Their old counts and row statuses are snapshots; use the live API/database for current state.

Safe continuation procedure

  1. Read state first. GET /api/purchase-orders/0oxer2vuae370m4/delivery-reconciliation with the existing x-ingest-secret: $N8N_INGEST_SECRET gives posted lines, pending groups, page paths and PO balances. Do not print the secret. A development server is currently at http://localhost:6004; it may need restarting. The endpoint is read-only, not a scheduler.
  2. Open the cached source page for each proposed invoice and compare printed invoice number, VDN number, date, PO, line code, count, rate and amount. dn_batch_pages.extraction holds the first OCR; the original PNG is available through /api/dn-batches/{batchId}/pages/{n} or PocketBase dn_batch_pages.image. Do not equate a signed DN with an invoice scan.
  3. For verified repeated scans, retain one candidate. Stand the other candidates down with an explicit duplicate_of relation in manual_overrides and a dn_review_events row. The current /dismiss route does not write an audit event and only sets status, so either improve it first or use an atomic PocketBase batch as this session did. Do not dismiss the posted receipt itself.
  4. Re-run runStagedBatch(pb, "3ce6tdvkinny9is") against cached pages after decisions. It reads 73 cached pages, takes roughly 1–2 minutes, and does not post. Check the retained candidate becomes stage=ready_to_post; if not, inspect its checks. Only then call the normal POST /api/dn-batches/{batchId}/notes/{noteId}/post route. Read the receipt and PO line quantities back immediately. Post one invoice at a time and recompute remaining balances.
  5. Known runner edge case to fix/test: run-staged.ts excludes terminal candidates from batchPrimaryClaims/batchReferenceClaims, but still registers them in pendingDocumentIdentities later in the loop. A dismissed earlier page can therefore block a retained later page (for example page 22 dismissed, page 48 retained). Add a focused failing test, then skip terminal candidates when registering pending identities. Avoid manual ready_to_post flags as a workaround.
  6. Known API preview gap: the PO reconciliation API's bookingPreview.lines[].alreadyReceived and outstandingAfterPosting are currently null because it passes PO lines without receipt totals into buildDeliveryNoteBookingPreview. The top-level lines[] balances are correct. Fill preview balances from posted totals and test it; do not interpret null as zero.
  7. After Vula, repeat the same read/compare/resolve/book cycle for the other PO groups. Never auto-post all Needs matching items. The separate n8n/agent endpoint and usage notes are in delivery-reconciliation-api.md; no daily schedule or n8n workflow has been created.

Code and data changes already made

  • The invoice accidentally imported as PO #MEGCP09109 (tkw4p3qq14vg3of) was quarantined as document_kind=tax_invoice; the true order is MEGCPO9109 (0oxer2vuae370m4). Four pre-existing 1050 mm receipts were moved to the genuine order in an audited transaction. Five blank Vula pipe units on the genuine PO were corrected to m after user confirmation.
  • PO matching now strips surrounding hashes and normalises the known MEGCP0/MEGCPO OCR form only when it identifies a unique exact order. New PO imports reject invoices. The export registration 21615145 is excluded from Vula document identity.
  • Duplicate checks distinguish posted receipts from other pending scans and hold both sides of a repeated primary/alternate reference. Explicit numeric R0 accessory lines can remain unlinked without being priced or booked as pipe quantity. Failed ledger reads and severe quantity plausibility failures block readiness.
  • The inbox groups scans and displays page references; document detail shows PO-based value and unit conversion. New GET /api/purchase-orders/[id]/delivery-reconciliation is read-only and reports ordered, posted and open quantities/values, grouped pending scans and warnings. The 900 mm half-cent valuation was corrected in src/lib/metrics/delivery-note-value.ts and src/lib/po-delivery-reconciliation.ts.
  • Three verified OCR reference corrections are recorded in manual_overrides and dn_review_events: page 29 VCINV073900 → VCINV073800, page 46 VCINV0746T7 → VCINV074077, page 22 VDN074555 → VDN074559. The source page extraction remains as originally read; overrides survive reruns.

Verification at handoff: npm run test:run passed 1,844 tests across 185 files; npm run typecheck and git diff --check passed. Re-run after further changes. The current work is uncommitted and there is no PR. Preserve the existing worktree and review the diff before packaging it.