Repository navigation
lumexia: lot capture, validation and lot lookup - #168
Merged
Merged
Conversation
Add SPEC-lumexia: product links to Lumexia items, lot rules by item, procurement and inventory type, lot capture at Packing with the single-lot split rule, background validation, QA block on wrong-item lots, and Lot lookup. Record Lumexia's as-built REST API v1 in docs/lumexia-api.md. Mark lumexia as spec approved in SPEC.md. Refs #30
… migration - LumexiaClient adapter in core/adapters/lumexia: HTTP client (Zod schemas, 5 s timeout, cursor paging, findItems in batches of 200, 404 → null) and an in-memory fake, both run through one contract suite; fixtures written from docs/lumexia-api.md, plus an opt-in LUMEXIA_LIVE test. - lumexiaClientFor(db), lumexiaHealth (cached 30 s) and testLumexiaConnection; /api/health reports lumexia ok/unconfigured/down without failing or degrading; Settings → Lumexia gets Test connection (en, es). - New lumexia.manage permission (Admin). - Migration: Product link fields and lotOverride, the three Settings lot-rule lists, LotStatus, PackageItemLot with its quantity > 0 CHECK; drops PackageItem.lot_code / lumexia_lot_id and their index. Refs #160
- core/lumexia/rules.ts: pure needsLot(product, settings) (physical + linked, then override, else any of the three type ids switched on); getLotRules joins Lumexia's three type tables with the saved switches, reports switched-on ids Lumexia no longer returns, and falls back to the saved ids read-only (with the reason) when Lumexia is unconfigured or unreachable; setLotRules replaces the three lists offline, audited Settings / lumexiaLotRules with before/after. Both need lumexia.manage. - Contracts lumexia.lotRules / setLotRules (GET/PUT /lumexia/lot-rules). - Settings → Lumexia → Lot rules: a switch per type, "Tracked in Lumexia" tag, "Removed in Lumexia" with Switch off, read-only state; en and es. - Playwright: saving a rule, the tag and removed ids in en/es, read-only. Refs #161
- core/lumexia/items.ts: lumexiaProducts (every product with its link, item type name and needsLot), searchLumexiaItems, suggestLinks (SKU ↔ reference code or alias, case-insensitive, labels matchedOn, skips ambiguous SKUs, writes nothing), linkProduct / linkProducts (≤200, one findItems, all or nothing), unlinkProduct (clears link + override, keeps lots), setLotOverride (errors.lumexia.notLinked). All lumexia.manage; Lumexia errors map to their messageKey (errors.lumexia.unavailable etc.). Link changes audited on Product. - Daily lumexia.refreshItems job (04:00 Chicago) updates reference codes and type ids; a missing item keeps its values and shows "Not found in Lumexia" (lumexiaSyncedAt null). New lumexiaActor system actor audits refreshes. - Contracts + RPC under /lumexia/products, /lumexia/items, /lumexia/suggest-links. - /lumexia page (Admin): TanStack table, filters, Match by SKU review dialog, item search combobox, Unlink, three-way Lot select; unconfigured message with a link to Settings; nav entry. en and es. - Store sync test now pins every lumexia* field and lotOverride. - Playwright: Match by SKU → linked with Needs lot: Yes; unconfigured; Admin-only. Refs #162
At Packing a lot-needing box line shows "Lot needed"; scanning a printed lot number with the package selected applies the split rule (first lot covers every unit, later scans move one unit at a time). Pack and unpack keep lots covering the box line. A lot for another item is refused with one internal note on the package, written outside the refused scan's transaction. The packed gate gains lotMissing. - core/lumexia/codes.ts: parseLotCode (also @woolrus/core/lot-codes) - core/lumexia/split.ts: the split rule, pure, with a property test - core/lumexia/lots.ts: scanLot; lot-store.ts: lot reads/writes - core/lumexia/needs-lot.ts: needsLot moved here (re-exported from rules) so the gates can use it without an import cycle - packing reads: package items gain needsLot and lots Refs #163
scanLot resolves a QR code (the lot's UUID) through Lumexia with a 2 s budget: a found lot picks its item's line, a lot on another item is refused with the wrong-item note, and when Lumexia is down, slow, unconfigured or doesn't know the lot it is saved UNVERIFIED with only lumexiaLotId. When several lot-needing lines fit, scanLot refuses with errors.lumexia.chooseLine and the candidates; the client's Which line? dialog retries with packageItemId. setLotQuantity and removeLot edit a box line's lots from the chip dialog (the default lot gives or takes the difference). Refs #164
Lot lookup (/lots, order.read): one field for a lot number (any case) or a lot QR's UUID. lookupLot matches lotNumber, or lumexiaLotId for a UUID, and returns every package item holding the lot (repack-discarded packages left out, unshipped orders kept with no date). Download CSV streams the same rows and columns through core/csv. The order detail page and the pack screen's order drawer show lots per package item with their status; orders.get's package items gain needsLot and lots. Refs #167
Every new lot enqueues lumexia.validateLots on its write's transaction. The job resolves each lot (getLot by id, else findLotsByNumber) and sets VALID (filling a missing number or id), WRONG_ITEM (plus one internal note) or NOT_FOUND, audited as the lumexia actor. Lumexia down retries 5x from 30 s; a refused key ends the retries. A 10-minute sweep re-queues unverified lots and re-checks NOT_FOUND ones daily for 7 days. Pack chips and the order detail show each lot's status as icon + word. Refs #165
The verified gate gains lotWrongItem for every WRONG_ITEM lot, so Complete QA and the label purchase refuse it (REST too); NOT_FOUND and UNVERIFIED only warn. QA rows show their lots with status, and a wrong-item lot has Replace lot (qaReplaceLot: same item check and rejection note as Packing) and Remove (qaRemoveLot), both needing order.qa, QA and the claim. Refs #166
Merge claude/lot-lookup-167-a194eb into the module branch. - Order packages: keep #167's `needsLot` + `lots` on package items and show them with `LotList`, dropping #165's inline lot list on the order detail page (LotList covers it, plus "Lot needed"). - One lot status component: `LotStatus` / `useLotText` in `components/lots/lot-status.tsx`. #167's `LotStatusBadge`, its `lotText` and the `lotLookup.status.*` strings are removed; `LotList` and the lot lookup page use `LotStatus` and `lots.status.*`.
…he lot dialog - Lot lookup matches the upper-cased number exactly instead of ILIKE, so `%` and `_` aren't wildcards (`%` returned every lot) and the lot_number index is used. - A void that shrinks a box line takes the units out of its lots too, so the lots keep adding up to the box line. - Validation merges a lot scanned once by number and once by QR code (while Lumexia was down) into one row with both identifiers, so lookup finds every unit by either one. It used to leave both half-filled. - Lumexia's HTTP client reports a body that stalls past the timeout as LumexiaUnavailable, not as a bad response. - Pack: "Save count" is no longer disabled until the NumberField commits (it only commits on blur or Enter), and the scan banner names the lot that was scanned rather than the latest one.
…verified - The 10-minute lot sweep queues nothing while Lumexia isn't healthy (unconfigured, down or refusing the key), so a bad key no longer adds failed jobs every 10 minutes. It skips lots a validation job still holds, so a slow Lumexia doesn't check lots twice, and queues at most 2,000 lots per run, oldest first. - /api/health starts the Lumexia ping right after the database check and awaits it last, so its up-to-3 s no longer adds to the serial checks. - QA shows Unverified lots in the warning tone, like Not found (SPEC-lumexia "Reads and UI"); elsewhere Unverified stays neutral.
These specs had never run: Next can't load SWC in the agent sandbox.
CI showed four kinds of failure, each the same on desktop and tablet:
- settings: the Lumexia card holds the Lot rules section, so the card
has two status lines (the URL form's "Saved." and Lot rules' "isn't
set up yet"). The URL tests now read the URL form's own status, and
Test connection's result has a `lumexia-test-result` test id.
- settings: the lot-rule switch was clicked as a normal element, but
React Aria's switch input is visually hidden, so the click waited
until the test timed out. It now uses `click({ force: true })`, as
the carriers specs do.
- lumexia: in Spanish, "Sin vincular" is both a filter option and the
table cell. The check now looks in the table.
- lumexia-lots: "Put everything left here" moved into the package's
⋯ menu in the pack card redesign (#159). The spec now opens the menu.
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.
Implements the
lumexiamodule: SPEC-lumexia, against Lumexia's API contract in docs/lumexia-api.md. It also carries the approved spec commits fromlumexia/spec.Closes #30
Tickets
lotMissinggate)lotWrongItemgate)/lots) and CSV: every order that shipped a lot#167 was built in parallel with #164–#166 and merged in
fb63fb6. The order detail page uses #167'sLotList, and there is now a singleLotStatuscomponent.Review fixes
ILIKE, so%returned every lot. It now matches the upper-cased number exactly, which also uses the index./api/healthruns the Lumexia ping alongside the other checks.Rollout
20261109231847_lumexia_lots) adds the lot tables and columns, plus aquantity > 0CHECK. The init migration's hand-written constraints are untouched.Testing
biomepasses. Typecheck passes; the web app is checked with plaintsc --noEmit, becausenext typegenhits a local SWC error.settings.spec.ts,lumexia.spec.ts,lumexia-lots.spec.ts,lot-lookup.spec.ts) andpnpm build. Both fail in agent shells on this machine withERR_SWC_NATIVE_CACHE, so they need a local run before merge.pnpm auditreports 2 high advisories, indeepmerge-tsandmysql2, both pulled in throughprisma. They exist on main too; this PR changes no dependencies.