Shipping Lot Reselection
Mobile and POS Terminal use the same reservation recovery and allocation helpers in packages/utils/src/shippingLots.ts. This flow applies when selecting a route-picking product, including reselecting problem lots before retrying B1.
Why free stock alone is insufficient
GET /api/products/lot?itemCode=...&whsCode=...&company=... reports stock after reservations. For example, physical quantity = 15, booking = 8, and available = 7 can still satisfy an 8-unit request if those 8 reserved units belong to the invoices being edited.
The existing assignments come from detail.invoices[].products[].lots. Invoice quantities alone are not proof of a reservation. Match invoice ID, product, company, warehouse, and lotNo/batchNumber; exclude canceled invoices, b1Status = 1, and invoices outside the selected product's invoiceAllocations. Duplicate invoice records must not restore the same reservation twice.
“Restore” means recovering the right to reuse reserved stock in the picker. It does not release stock, alter invoices, or increase physical inventory on the server. Saving still uses the existing POST /api/deliveries/{id}/lots flow and invoice-level payload allocation.
Balances and missing lots
The app's getProductLots service preserves raw stock in lotQuantity and uses available as the selectable quantity. Shipping passes includeUnavailable: true so zero-available batches survive until reservation recovery. This flag is local to the service; it is not an API query parameter. Other callers retain the default positive-available filtering.
For a batch returned by the API, restoreShippingLotOptions adds only its matching own reservations, capped by the current booking and physical stock when those values are supplied. For a batch absent from the response, the helper creates an option from the eligible invoices' saved lots, limited to their reserved quantity. Its dates come only from those saved lots; missing dates stay empty (or null for dueDate), never copied from another batch.
| Situation | Selectable result after recovery |
|---|---|
| Same batch: 7 free + own reservations of 5 + 1 + 2 | 15 selectable; allocate only the requested 8 |
| Batch is fully booked: 0 free + own reservations of 8 | 8 selectable |
API has 260812-NMA8034 with 15 free; invoices hold 8 of missing 260812-NMA8033 | 8033 has 8 selectable; 8034 stays at 15 |
Invoice quantity is 8 but its lots is empty | No reservation is restored |
| Reservations belong to another invoice, company, warehouse, or product | No reservation is restored to this product |
buildShippingLotSelection uses saved reservations first, then fills any shortfall in the remaining options' order (the API options are sorted by expiry by the service). It combines allocations to the same option instead of creating duplicate selection rows. With old 8033 × 8 and new 8034 × 15, a request for 8 selects only 8033 × 8; a request for 9 selects 8033 × 8 and 8034 × 1.
Error and modal rules
These rules apply to the initial product selection when no local batch selection exists yet. Users can reopen a selected product to edit its batches.
| After restoring reservations | Result |
|---|---|
| Total selectable quantity is below the pending quantity, including zero options | Show an error immediately with requested, available after recovery, and missing quantities; do not apply a partial selection or open the batch modal |
| Own reservations are reused, quantity is complete, and the merged selectable options have at most 2 distinct batch numbers | Apply the selection without opening the modal |
| Own reservations are reused, quantity is complete, but merged options have more than 2 distinct batch numbers | Prefill the allocation and open the editor for review |
| No own reservations, one sufficient option | Keep the existing single-lot auto-selection |
| No own reservations, several sufficient options | Keep the existing manual picker/editor |
The two-batch threshold counts all distinct batch numbers in the merged selectable options, not just the batches selected to satisfy the request. Automatic selection updates local checked quantities; it does not submit the delivery automatically.
Debugging
In development builds, filter the Console by [ShippingProductLots] /products/lot invoices. The JSON snapshot records itemCode, whsCode, company, and invoice allocations (invoiceId, invoiceNo, customerName, qty) before the request, both on initial selection and when loading additional options.
Use the matching invoice products' lots to establish reservation ownership; the log's invoice quantities and the endpoint's aggregate booking alone cannot do that. To investigate repeated FiberNode warnings or debugger heap exhaustion, see swipe worklets.
Verification and source map
Run from the repository root:
yarn typecheck
yarn mobile:test --watchman=false --runInBand shippingLots.test.ts productLotAvailability.test.ts swipeableWorklets.test.tsx
The UAT integration at 25c9f884 passed both app typechecks and 54 focused tests (48 lot/service cases and 6 swipe cases). These tests cover reservation ownership, missing/zero-available lots, reuse before new stock, quantity caps, payload splitting, and compiled worklet closures. They do not establish that the modal/error UI has been tested on a physical device.
Manual acceptance cases on both apps:
- Same-batch 7 + 8 recovery and missing-batch 8 + 1 allocation complete without a modal when there are at most two merged options.
- More than two distinct merged options open the editor, even if the allocation itself uses only one or two batches.
- Insufficient total stock, including an empty response without own lots, shows an error without changing checked quantities.
- A product without old reservations retains single-lot automatic and multiple-lot manual behavior.
- Reopen/edit/remove batches, save the invoice-level allocation, and reload detail; never include canceled or B1-success invoices in recovered stock.
- Swipe invoice/product rows with DevTools open; check for normal action taps and absence of repeated FiberNode warnings.
| Responsibility | Source |
|---|---|
| Shared recovery and selection | packages/utils/src/shippingLots.ts |
| Mobile UI and guards | apps/mobile/src/screens/shippingStack/shippingDetailScreen/index.tsx |
| Terminal UI and guards | apps/pos-terminal/src/screens/HomeScreen/ShippingRouteDetailForm.tsx |
| API normalization | apps/mobile/src/services/common.ts, apps/pos-terminal/src/services/common.ts |
| Regression tests | apps/mobile/__tests__/shippingLots.test.ts, productLotAvailability.test.ts, swipeableWorklets.test.tsx |
See also route picking and shipping/transport workflow.