Storefront Picking / Delivery Sheet
Storefront Picking is the workflow for invoices whose goods are prepared and handed out at a branch/storefront. It is different from route delivery and different from cashier POS. The work unit shown to the user is now a sales order (orderNo), while API acceptance, Lot/Batch persistence, delivery creation, and B1 stock movement still run one invoice at a time.
App Entry Points
| App | Target |
|---|---|
| Mobile | StorefrontListScreen -> StorefrontDetailScreen |
| POS Terminal | shippingStorefront -> StorefrontDetailForm in HomeScreen |
What This Menu Shows
The list is built from invoices with shippingType = 2. Rows with invoiceStatus = CANCELED are removed completely before grouping. The remaining rows are deduplicated and grouped by orderNo; an invoice-specific fallback key is used only when an order number is missing.
Each card displays the customer, sales order number, every included invoice number on its own row, sales employee, and warehouse employee. The NEW tab is evaluated from the grouped card: a group remains NEW until every included invoice is CONFIRMED.
Group card background colors
Color conditions are evaluated after canceled invoices have been removed and in this order:
| Group condition | Background |
|---|---|
Every invoice is CONFIRMED | Green #76ffa3 |
Any invoice is NEW and has hasDelivery != null | Purple #d382ff |
Any invoice has a pending cancel request (canceledNote != null and rejectCanceledAt == null) | Light purple #e7bce7 |
| All other mixed/unconfirmed groups | Yellow-green #cffd67 |
Opening a card passes all grouped invoice IDs plus orderNo into the detail flow. Detail data for every ID is loaded with includeLot=true and combined into one screen. Product rows retain sourceInvoiceId, sourceInvoiceNo, sourceInvoiceStatus, and the source acceptance owner so that validation and API work remain invoice-specific.
Main Workflow
Accept Job Rules
| Condition | Behavior |
|---|---|
| Main branch | Users in accept departments are prompted to accept the job when acceptedBy is empty. |
| Non-main branch | Accept flow is enabled, but Lot/Batch editing uses the same permission as the main branch and does not depend on isMain. |
| Every invoice already accepted | The prompt is skipped. |
| Mixed acceptance state | One modal is shown; confirmation calls the accept API sequentially only for invoices whose acceptedBy is empty. Already accepted invoices are not sent again. |
| Accepted by current user | That user can continue editing and saving. |
| Override user | User code 999997 is treated as an accepted/action user. |
Accept permission is controlled by departments 8, 12, 22, 28.
Item Checking And Lot/Batch Rules
Important item rules:
| Rule | Behavior |
|---|---|
| Editable status | Product checks are editable only when their source invoice is NEW. Rows from a CONFIRMED invoice remain visible but cannot open the Lot/Batch editor. |
| Invoice prefilled lots | A non-empty, valid items[].lots[] response from GET /invoices/{id}?includeLot=true is displayed as the initial value. Users can edit quantity, delete it, or select a replacement lot while the invoice is NEW and they have edit permission. |
| Lot selection | On the first tap after loading detail, recalculate the full invoice line quantity even if saved lots are prefilled. Reuse saved lots first, then fill the remainder in API expiry order. Later taps open the current selection for editing without reapplying saved lots. |
isMain | It does not lock or restrict Lot/Batch selection. Lot editing uses the same rule for main and non-main branches. |
| Available stock | Retain zero-available API lots for reservation recovery. Restore only this invoice line’s matching product/company/warehouse reservations, capped by API booking and physical stock when supplied. A saved batch missing from the response is added with its reserved quantity and its own dates. Recovery changes local picker balances only. |
| Insufficient stock | On the first tap, if total stock after recovery is below the full line quantity (including no stock), show an error with required/available/missing quantities before changing the selection or opening a modal. Manual picker reloads still display the no-data message and reload action when empty. |
| Required product context | itemCode, whsCode, and company are required before lot options can be loaded. |
| Duplicate lot | The same Lot/Batch cannot be selected twice for one item. |
| Lot balance | Entered quantity cannot exceed the selected lot balance. |
| Invoice line quantity | Entered quantity cannot exceed the pending quantity of that invoice line. |
| Completion | Every line must have checked quantity greater than or equal to invoice quantity before save. |
| Automatic selection | A complete reused selection skips the modal when there are at most two distinct positive-balance batches across all restored options. More than two opens the editor. With no reusable reservation, one sufficient lot remains automatic and multiple lots remain manual. |
For example, a line requiring 9 units with 8 reserved on 260812-NMA8033 and 15 available on 260812-NMA8034 selects 8 from the old batch plus 1 from the new batch. If both batches are the only selectable options, no modal opens. Reservations from other invoice lines are not returned to this line.
Mobile and POS Terminal use packages/utils/src/storefrontLots.ts, which shares reservation recovery and allocation with the shipping route flow. Selection does not release stock through an API; the existing per-invoice save contract below is unchanged.
Save To Delivery / B1 Rules
The submit button is shown when at least one grouped invoice is not CONFIRMED/CANCELED and the current user has action permission for that invoice. A CONFIRMED source invoice is display-only and is excluded from submit processing.
Before saving, the app blocks:
| Guard | Error Case |
|---|---|
| Permission | User cannot save storefront data. |
| SAP reference | refKey is missing, which means the invoice has not reached SAP/B1 context. |
| Cancellation request | canceledAt exists and rejectCanceledAt is still empty. |
| Incomplete checks | Not all items have been checked to the required quantity. |
Validation is performed per pending invoice against only its own product rows. When every pending invoice passes, Mobile and POS Terminal iterate through those invoices sequentially:
- Skip invoices already in
CONFIRMEDstate. - Add the current invoice to delivery
0when itsdeliveryIdis missing. - Build
invoices[].items[].lots[]from only that invoice's selected product rows. - Save delivery lots for the current invoice.
- Create delivery/send stock movement for the current invoice.
- Continue with the next invoice, then reload grouped detail and list state.
Grouped Detail Metadata
| Field | Display |
|---|---|
| Customer | Shared customer name from the grouped order |
| Sales order | orderNo |
| Picking invoice | Every invoiceNo, joined with , in detail metadata |
| Products | Product rows from every grouped invoice |
| Confirmed products | Visible as completed/read-only rows; Lot/Batch selection is disabled |
Cancel Rules
Cancellation is a request with a required reason. It does not hard-delete invoice history. In grouped detail, cancellation validation is checked across the group and the request is sent sequentially to non-confirmed invoices only. Printing an invoice from grouped detail prints every included invoice sequentially.
Permissions And Guards
| Topic | Behavior |
|---|---|
| Terminal menu key | shippingStorefront |
| Menu deptCode set | -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 20, 22, 23, 27, 28 |
| Accept departments | 8, 12, 22, 28 |
| Save/action departments | 2, 8, 22, or the user who accepted the job, or override user 999997 |
| Editable invoice status | Source invoice NEW only |
| Hidden submit states | CONFIRMED, CANCELED, or no action permission |
Mobile vs POS Terminal
| Topic | Mobile | POS Terminal |
|---|---|---|
| Navigation | Full-screen storefront stack | HomeScreen split-pane detail |
| Accept flow | Prompted in detail screen | Prompted in detail form |
| Lot selection | On the first tap after loading detail, recalculate the full invoice line quantity even if saved lots are prefilled. Reuse saved lots first, then fill the remainder in API expiry order. Later taps open the current selection for editing without reapplying saved lots. | |
| Save behavior | Delivery lots + create delivery | Same service behavior |
| Cancel | Reason modal | Reason modal |
Developer Handoff Map
| Area | Code |
|---|---|
| Mobile list/detail | apps/mobile/src/screens/storefrontStack/storefrontListScreen, storefrontDetailScreen |
| Mobile storefront helpers | apps/mobile/src/screens/storefrontStack/storefrontDetailScreen/helpers.ts |
| Terminal split data | apps/pos-terminal/src/services/splitMenuData.ts -> shippingStorefront |
| Terminal detail | apps/pos-terminal/src/screens/HomeScreen/StorefrontDetailForm.tsx |
| Terminal rules | apps/pos-terminal/src/screens/HomeScreen/StorefrontDetailHelpers.ts |
| Storefront service | apps/*/src/services/storefront.ts |
| Delivery/lot service | apps/*/src/services/shipping.ts |
Cancellation from this screen sends a request for review in the Invoice menu. Eligible invoices with explicit hasDelivery = null can instead be canceled without a pending request from the invoice list; see that page for owner/admin permissions, required notes, and global Sweet Alert behavior.