feat(omron-ci): read every page of the Commercial Invoice form - #55
Merged
Merged
Conversation
The in-house form (00004-00202) holds eight commodity lines to a page, and a longer shipment runs onto further pages — one sheet per page in the workbook, one page per page in a print. Both were read as a one-page document: the PDF path refused a multi-page print outright, and the workbook path took the first sheet matching the form and filed a four-page invoice as its first eight lines with nothing said, which is the silent short declaration this tool exists not to produce. Every page repeats the whole header and states its own SUBTOTAL, so each is now read as the complete form it is printed as and the results merged. Two figures prove nothing went missing on the way in: the `PAGE: 2 of 4` box, and the grand `TOTAL (USD)` every page repeats, which still counts the goods on a page that never arrived. The grand total (less tax and freight, which are not commodity value) becomes the figure the rows reconcile against, so a missing page fails the blocking total-value check instead of balancing against itself. Duplicated pages, disagreeing page counts and pages of two different invoices are reported. Also fixed on the same form: - `SHIP DATE` is now imported and is the date of exportation. It has its own header field, so the vendor layouts — which state no ship date, and whose filed SLIs date box 2 from the invoice — are unchanged. - The country of ultimate destination is resolved from the consignee block, matched against the ISO country list so `ORADEA, 410085, BIHOR, ROMANIA` yields Romania and neither the county nor the postcode can be filed as one. A two-letter segment is never accepted: `Pleasanton, CA` is not Canada. The country tables move to `domain/` for it, re-exported from the keying sheet. - A struck-through header box (`-`, `N/A`) is read as empty rather than filed as the dash it holds. - Totals on a print are read from the cells to the right of their label, never the rightmost number on the row: `TAX` prints beside `NET WT (KG)`, and a blank tax cell was handing back the shipment's weight as an amount of money. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MfEau2Hgjm7Y7tBDQ2vBSw
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Status
FindingsNo issues found in changed scope. Proposed patchesNone CI failure mappingCI is still running (pending). Diagnostic plannpm ci
npm run lint
npm test -- --watch=false
npm run build
npx --yes markdown-link-check <changed-docs>
npx --yes cspell <changed-docs>Next actions
MetaProcessed at 2026-09-16 21:09:27.346 UTC |
Correctness, in the order they matter: - A missing page no longer reconciles against itself. Where the grand TOTAL (USD) cannot be read, the pages that did arrive balance against their own subtotals and the total-value check passes — the exact silent short declaration this work exists to prevent. `ParsedCipl.incompleteReason` carries what the document says about its own extent, and `reconcile` turns it into a blocking `document-complete` check. Duplicated pages and pages that disagree about any document-level box block on the same check. - The country resolver no longer files a person as a country. It matched any comma-separated segment of any line, so `Attn: Miller, Chad` under a Romanian address yielded Chad, and `Atlanta, Georgia, 30301` yielded Georgia. Only the last segment of a line is considered, and a block naming two countries establishes neither. - Tax and freight are taken from whichever page prints them, not all three totals from the page that first stated a grand total. A tax printed on page 2 alone was left inside the merchandise total, failing the arithmetic on a complete import and reporting it as missing a page. - The address blocks come from the page that states a consignee, not the first page to read any one of the three blocks. A page-1 band that read the shipper and not the consignee was winning over a complete page 2, and reporting nothing. - Printed line numbers past 99 are read. Eight lines to a page puts line 100 on page 13, now reachable; an unmatched LN does not shorten the table, it folds the line into the one above and drops the rest. - Pages bound after the invoice — a terms sheet, a signed SLI — are no longer read as pages of the form. The detector inspects page 1 only; the doc number in each page's title bar is what tells them apart. - The commodity lines take the document's order and invoice numbers, not the page's own, so a page whose box is struck through no longer files its rows against a different order number than the header carries. - Warnings and line provenance name the page the document prints, not the page of the import: pages 1 and 3 of a 3-page print reported a defect on "page 2", a sheet the reader does not have. - Every way the pages fail to be one document is reported, not the first. Accuracy of what the reviewer is shown: - `exportDate` and `resolveDestination` return the value with its source, and the draft, the review screen and the keying sheet all read them. The screen claimed "discharge port" for any non-null port, including one with no comma that box 7 was not filled from, and called the invoice date "superseded" where vendor-b states the same date in both boxes. Cleanup: `pagesToGrid` is `pageToGrid` and takes one page, so feeding it two is a type error rather than the baseline collision the comment warned about. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MfEau2Hgjm7Y7tBDQ2vBSw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The in-house form (00004-00202) holds eight commodity lines to a page, and a longer shipment runs onto further pages — one sheet per page in the workbook, one page per page in a print. Both were read as a one-page document:
… has 4 pages, but the Commercial Invoice form (00004-00202) is a single page).A real four-page invoice (30 lines, $3,764) now imports identically from either file.
Multi-page
Every page repeats the whole header and states its own SUBTOTAL, so each is read as the complete form it is printed as and the results merged (
parseOmronCiPages). Concatenating the pages' rows was never an option: four address bands and four totals bands in one grid mean nothing, and PDFyrestarts on every page.Two figures prove nothing went missing on the way in:
PAGE: 2 of 4box, the only place the document states its own extent;TOTAL (USD), repeated on every page, which still counts the goods on a page that never arrived.header.totalValuenow comes from that grand total less TAX and FREIGHT (which are not commodity value), so a missing page fails the blocking total-value check instead of balancing against itself — verified at 3 of 4 pages:expected 280.00, actual 108.00. Duplicated pages, disagreeing page counts and pages of two different invoices are each reported.Also fixed on the same form
SHIP DATEis imported and is the date of exportation. It gets its own header field, so the vendor layouts — which state no ship date, and whose filed SLIs date box 2 from the invoice — are unchanged.ORADEA, 410085, BIHOR, ROMANIAyields Romania, and neither the county nor the postcode can be filed as one. A two-letter segment is never accepted —Pleasanton, CAis not Canada. Previously this form established no country at all and the blocking check held every shipment on it.-,N/A) is read as empty rather than filed as the dash it holds. The invoice number-was becoming the output filename and the consignee PO on the SLI. Header grid only — a-in a commodity cell is still held for review.TAXprints to the right ofNET WT (KG). A blank tax cell handed back 9.7 kg as an amount of money. Now read rightward from the label.Refactor worth flagging
carriers/keying-sheet/countries.tsmoves todomain/countries.ts, re-exported from the keying sheet so its callers are unchanged. The parser needs the country list anddomain/must not import fromcarriers/; a duplicated name table was the worse alternative.Verification
npm run checkpasses locally — typecheck, lint, 914 tests, production build. New coverage: multi-page reads from both the workbook and a printed PDF, per-page line attribution, id uniqueness across pages, document-level figures not summed, the missing-page block, mismatched invoice numbers, page-named warnings, header and address fallback to a later page, tax/freight subtraction, and the country resolver's near misses.The 122 skipped tests are the suites needing the gitignored real-shipment fixtures. The vendor-a and vendor-b paths are unchanged (
shipDate: nullon both) but not exercised in this environment.🤖 Generated with Claude Code
https://claude.ai/code/session_01MfEau2Hgjm7Y7tBDQ2vBSw
Generated by Claude Code