Skip to main content

SOP: Packing

Document ID: WMS-PACK-001 Version: 1.0 Effective date: 04/30/2026 Owner: Warehouse Operations Manager Next review: [six months from effective date] Applies to: Packing-station staff turning picked goods into shipping-ready containers


1. Purpose

This procedure governs how a PICKED order moves through the packing station and becomes PACKED — physically sealed, weighed, measured, photographed, and ready for the shipping label. The system supports two packing modes (per the codebase): direct mode for orders that come out of pick without staged bins, and bin mode for orders pre-staged into one or more pick bins per WMS-PICK-002. Both end at the same place — Order.status: PACKED, weight + dimensions captured on the WorkTask, and the order ready for shipping per WMS-SHIP-001.

The pack station is the last quality-control checkpoint before the order leaves the warehouse. A packing scan that doesn't match what's in the box is the cheapest place to catch a wrong-SKU or short-pick error — once the carrier label prints, the cost of a mistake jumps by an order of magnitude.

2. Scope

In scope:

  • The /pack queue page — orders ready to pack, in-progress, and the bin-scan entry point
  • Direct mode: generate pack list via POST /fulfillment/:orderId/pack, verify each item by scan, complete with weight + dimensions
  • Bin mode: scan a bin barcode, verify items inside the bin (single bin or multi-bin), complete with weight + dimensions
  • The transition PICKED → PACKING → PACKED and the WorkTask of type PACKING
  • Capturing packing photos (PackingImage rows linked to order, task, and bin)
  • The two completion endpoints (/pack/complete-from-bin, /pack/complete-from-bins)
  • Pack failure modes and recovery

Out of scope:

  • Picking and pick-bin creation — see WMS-PICK-001 and WMS-PICK-002
  • Shipping label creation, ShipEngine, multi-carrier — see WMS-SHIP-001
  • Carrier label printing on a thermal label printer — see WMS-SHIP-002 (when written) — different printer (4×6 thermal), different ZPL flow
  • Customer-return packing — see WMS-RET-002 (separate flow)
  • Box recommendation / which box to use — surfaced upstream by OrderPackageService.recommendAndSave per WMS-INV-006 §4.4

3. Roles & permissions

API enforcement: all packing endpoints (/fulfillment/:orderId/pack, /fulfillment/:orderId/pack/:taskItemId/verify, /fulfillment/:orderId/pack/complete-from-bin, /fulfillment/:orderId/pack/complete-from-bins, /fulfillment/bin/:barcode) check authentication only — no inline role gate. The router-level /pack route gates on STAFF_ROLES (per router.tsx:296), same as /pick.

RoleView pack queuePack an orderCapture weight + dimensionsUpload packing photos
READONLY
STAFF
MANAGER
ADMIN
SUPER_ADMIN

Operational expectations:

  • A pack-station packer typically works one order at a time. Multiple packers running in parallel is fine — each on their own order via their own bin scan.
  • Photos are recommended for high-value orders, returns-prone customers, and orders flagged in the customer profile. The system doesn't enforce photo capture today — see §8.
  • The packer does not print the carrier label themselves in this SOP. Label generation is a separate step in WMS-SHIP-001 — typically auto-triggered by Order.status: PACKED or a separate "Print Label" CTA on the fulfillment page.

4. Procedures

4.1 The pack queue

Use when: Starting your shift, looking for the next order to work, or arriving back at the pack station.

Steps:

  1. Open /pack. The page lists orders with status IN ('PICKED', 'PACKING'), sorted with priority and age.
  2. The page also has a bin-scan input — a focused field that accepts any barcode and routes accordingly:
    • Barcodes starting with BIN- (the convention from WMS-PICK-002 §5.3) → looked up via GET /fulfillment/bin/:barcode. On match, navigate to /fulfillment/:orderId?fromBin=true (bin mode).
    • Order numbers (matching o.orderNumber or #{orderNumber}) → navigate to /fulfillment/:orderId (direct mode).
    • Anything else → fall back to bin-lookup attempt (some custom bin barcodes may not start with BIN-).
  3. Tap an order in the queue, or scan a bin/order barcode to enter the pack flow.

Auto-refresh: the queue refreshes every 30 seconds via setInterval, so newly-picked orders surface within 30s of becoming PICKED without the packer manually refreshing.

What's surfaced:

  • Order number, customer name, item count, time since order placed
  • Status badge — Ready (status PICKED) or Packing (status PACKING)
  • Priority badge for URGENT / HIGH (red border on the card)

What's not surfaced:

  • The packer who's actively working an in-progress order. Tapping a PACKING order will silently make you the active packer.
  • The bin count for multi-bin orders. The page shows item count, not bin count — which means multi-bin orders look the same as single-bin until you open them.

4.2 Direct mode — generate the pack list

Use when: The picker did not stage into bins (single-line orders, small orders, ad-hoc no-bin workflow). The order arrives at packing as PICKED with no PickBin rows or with a single PickBin whose contents you want to verify item-by-item.

Prerequisites (enforced by PackingService.generatePackList per packing.service.ts:112-145):

  • Order.status === 'PICKED'. Otherwise: HTTP 400 Cannot start packing: order is {status}, expected PICKED.
  • No active pack task already exists. Otherwise: Active pack task {taskNumber} already exists for this order.
  • A completed pick task exists with taskItems. Otherwise: No completed pick items found for packing.

Steps:

  1. From the queue, tap the order. The fulfillment page detects no bin-mode flag and calls POST /fulfillment/:orderId/pack.
  2. The service creates a WorkTask of type PACKING, status: PENDING, taskNumber: PACK-{orderNumber}-{shortId}, with one TaskItem per completed pick item.
  3. The page shows the first pack item with: SKU, image, product name, required quantity. Order status auto-advances to PACKING on first verify.

What this writes:

  • One WorkTask row, one TaskItem per pick item, one fulfillment_events row of type packing:started.

4.3 Direct mode — verify each item

Use when: You're walking the pack list one item at a time, scanning each unit before placing it in the box.

Steps:

  1. The page shows the current pack item.
  2. Scan the unit's barcode (UPC, vendor barcode, or SKU — same priority as receiving and picking).
  3. The page calls POST /fulfillment/:orderId/pack/:taskItemId/verify.
  4. The service runs verifyPackItem (per packing.service.ts:178-221):
    • Loads the task item; throws if task.type !== 'PACKING'.
    • If status === 'COMPLETED' already, returns success without re-incrementing.
    • Otherwise marks the task item COMPLETED, increments WorkTask.completedItems.
    • Emits packing:item_verified event.
  5. The page advances to the next item. Repeat.

⚠ Direct-mode pack-verify does not validate the scanned barcode against the variant. Looking at verifyPackItem, the scanned barcode is not passed to the API at all — the route only receives the taskItemId. The verification is purely "did the packer tap the verify button on this task item." Same gap as picking (per WMS-PICK-001 §4.3) but slightly worse — the barcode isn't even captured for audit. To wire scan-validation server-side, add scannedItemBarcode to the request body, compare against the variant, and reject mismatches. See §8.

4.4 Direct mode — complete with weight & dimensions

Use when: All pack items are COMPLETED and the box is sealed.

Prerequisites (enforced by completePacking per packing.service.ts:227-310):

  • All taskItems have status === 'COMPLETED'. Otherwise: {n} items still pending verification. Complete all items before finishing packing.
  • Task is type PACKING.

Steps:

  1. Place the sealed box on the scale. Weigh.
  2. Measure with the dimension tool (or use the box's known dimensions if a recommended box was applied per WMS-INV-006 §4.4).
  3. Enter weight (default unit ounce) and optional dimensions (length × width × height + unit) in the PackingCompleteForm UI block.
  4. (Optional) Capture packing photos — see §4.7.
  5. Tap Complete Packing. The page calls the route that wraps completePacking(taskId, { weight, weightUnit, dimensions, userId, orderId }).
  6. Order status: PACKED. Two events emitted: packing:completed (with weight + dimensions) and order:packed.

Result:

  • WorkTask.status: COMPLETED (and the same fields populated on the task: packedWeight, packedWeightUnit, packedDimensions).
  • Order.status: PACKED.
  • The order is now ready for shipping per WMS-SHIP-001.

4.5 Bin mode — scan a bin

Use when: The picker staged the order into one or more pick bins per WMS-PICK-002. The bin label is on the tote/box; you scan it at the pack station to start.

Steps:

  1. Bring the bin to the pack station.
  2. From /pack, scan the bin barcode (or type it into the bin-scan input).
  3. The page calls GET /fulfillment/bin/:barcode. The service runs PickingService.getOrderByBinBarcode(barcode) to look up the bin and return the order id.
  4. Page navigates to /fulfillment/:orderId?fromBin=true. The fromBin=true query param tells the page to render bin mode.
  5. The page shows the bin contents — each PickBinItem with sku, quantity ordered into the bin, verifiedQty (starting at 0), and the variant's image.

What's shown for multi-bin orders:

  • All bins for the order, with their statuses (STAGED / PACKING / COMPLETED).
  • The bin you scanned is the active bin — you verify its contents before moving to the next.
  • A "next bin" affordance once the current bin is fully verified (or the multi-bin completion endpoint per §4.6).

⚠ Bin lookup is by barcode, not by order. If the same barcode somehow exists for two bins (it shouldn't — PickBin.barcode has a unique constraint), the lookup is ambiguous. The unique constraint protects this, so it shouldn't happen. If it does, escalate to IT.

4.6 Bin mode — verify items in the bin

Use when: A bin is on the pack station and you're scanning items inside it before sealing.

Steps:

  1. The page shows the bin's contents — list of items with verifiedQty / quantity per row.
  2. Scan an item's barcode.
  3. The page calls a verify endpoint (route mounted at the fulfillment plugin, calling verifyBinItem per packing.service.ts:317-378):
    • Service finds an item in the bin where pv.upc === barcode || pv.barcode === barcode || pv.sku === barcode || pv.sku.toUpperCase() === barcode.toUpperCase().
    • If no match: HTTP 400 Item with barcode "{barcode}" not in this bin.
    • If already fully verified (verifiedQty >= quantity): no-op success, returns the current state.
    • Otherwise increments verifiedQty by 1 via incrementBinItemVerifiedQty.
  4. The row updates to {verifiedQty}/{quantity}. Repeat for each scan.
  5. When verifiedQty >= quantity for every bin item, the page enables the Complete CTA.

⚠ Bin-mode scan validation is real. Unlike direct-mode (per §4.3 callout), bin-mode actually compares the scanned barcode against pv.upc, pv.barcode, and pv.sku and rejects mismatches. The audit story for bin-mode is materially better than direct-mode. If your warehouse cares about pack-time scan integrity, prefer bin mode wherever possible. Picker-side discipline drives this — pickers should always stage to bins, not pick directly into the order box.

To complete a single bin (bin's contents fully verified):

  1. Capture weight + dimensions (and optional photos).
  2. Tap Complete Bin. The page calls POST /fulfillment/:orderId/pack/complete-from-bin with { binId, weight, weightUnit, dimensions }.
  3. The service runs completePackingFromBin (per packing.service.ts:403-490 and packing.repo.ts:453-525) in a transaction:
    • Re-checks all bin items are verified. Throws if not.
    • Updates all Allocation rows on the order from ALLOCATED to PICKED.
    • For each bin item, overwrites OrderItem.quantityPicked with the bin's quantity (see callout below).
    • Sets PickBin.status: COMPLETED, packedBy: userId, packedAt: now.
    • Sets Order.status: PACKED.
    • Retroactively creates a WorkTask row of type PACKING, status: COMPLETED, with the weight/dimensions captured. (Bin mode never has a task in PENDING or IN_PROGRESS.)
    • Writes a task_events row of type TASK_COMPLETED with type: PACKING_FROM_BIN.

⚠ Bin-mode completePackingFromBin is single-bin only — Order.status: PACKED is set on the first bin completion. Per packing.repo.ts:486-489, the order's status is updated to PACKED regardless of how many bins remain. For a true multi-bin order, the second and third bins still need to be packed (they have status STAGED and items to verify), but the order is already labeled PACKED. Use /pack/complete-from-bins (plural — see below) for multi-bin orders. The single-bin endpoint should only be called when there's only one bin on the order.

⚠ The OrderItem.quantityPicked overwrite. Per packing.repo.ts:478-480, quantityPicked is set to the bin's quantity, not incremented. For multi-bin orders where the same SKU appears in multiple bins, this is wrong — the second bin's call would overwrite the first bin's recorded quantity. Use the multi-bin endpoint (below) which handles this correctly. Single-bin endpoint is safe only when each SKU appears in exactly one bin.

To complete multiple bins (all bins for the order verified):

  1. Each bin has been verified per §4.6 step 1-5.
  2. The packer collects weight + dimensions for each bin.
  3. The page calls POST /fulfillment/:orderId/pack/complete-from-bins with { bins: [{ binId, weight, weightUnit, dimensions }, ...] }.
  4. The service runs completePackingFromBins (plural — handles multi-bin correctly).
  5. All bins flip to COMPLETED. Order flips to PACKED after the last bin is processed.

4.7 Capturing packing photos

Use when: High-value orders, fragile items, customer-flagged accounts, or any time visual proof of what was packed has operational value (chargeback defense, customer disputes, regulatory).

Steps:

  1. From the pack page (direct or bin mode), tap the Camera icon to open the PackingImageUpload block.
  2. Capture or upload one or more images (camera or file picker).
  3. Each image is uploaded to GCS and a PackingImage row is created with:
    • orderId (required), optional taskId, optional pickBinId
    • url, filename, size, contentType (default image/jpeg)
    • uploadedBy: <user>, createdAt: <now>
    • Optional reference (for cross-referencing — e.g., RMA number) and notes
  4. The image is now linked to the order. It surfaces on the order detail page and in customer-service tooling.

Result:

  • Persistent photographic record of what shipped.
  • Linked to the order, the task (if direct mode), and the bin (if bin mode).
  • Cascades: deleting an order cascades-deletes its packing images (per the schema).

⚠ Photos are optional and unenforced. No code path requires a photo before pack completion. If your operation needs photos for high-value orders, that's manager-discipline today. A "photo required" toggle on the variant or order would close this gap as a small engineering ticket.

5. Reference

5.1 Two packing modes — quick comparison

Direct modeBin mode
TriggerPOST /:orderId/pack from queueScan bin barcode at pack station
Pre-requisiteOrder is PICKED, no active pack task, completed pick task existsBin exists with verified pick items
Task createdWorkTask in PENDING, walked through item-by-itemWorkTask retroactively created in COMPLETED after bin completion
Per-item scan validationNot performedtaskItemId only, no barcode in payloadReal — barcode compared to pv.upc || pv.barcode || pv.sku
Per-item verifiedQty trackingNo — items go directly PENDING → COMPLETEDYes — increments per scan, full visibility
Multi-bin supportN/A (single task)Yes — one task per completion, but multi-bin endpoint preferred
Audit trailWorkTask.taskItems, task_events ITEM_COMPLETEDPickBinItem.verifiedQty per scan, task_events TASK_COMPLETED only

Recommendation: prefer bin mode. Better scan validation, better audit trail, supports multi-bin. Direct mode exists for compatibility with single-bin or no-bin workflows.

5.2 The WorkTask of type PACKING — fields used

Captured at completePacking or completePackingFromBin/s:

FieldValue
taskNumberPACK-{orderNumber}-{shortId}
typePACKING
statusPENDINGCOMPLETED (direct) / always COMPLETED (bin retroactive)
totalItemsNumber of pack items
completedItemsIncrements on verify; equals totalItems at complete
packedWeight, packedWeightUnitCaptured at complete
packedDimensionsJSON: { length, width, height, unit } if provided
verifiedAt, verifiedBySet at complete (bin mode)
startedAt, completedAtLifecycle timestamps
eventsOne-to-many task_events
Event typeWhen
PACKING_STARTEDgeneratePackList (direct mode)
PACKING_ITEM_VERIFIEDEach verifyPackItem (direct mode)
PICKBIN_COMPLETEDcompletePackingFromBin (bin mode)
PACKING_COMPLETEDcompletePacking and completePackingFromBins
ORDER_PACKEDAfter both pack endpoints — the universal "this order is now packed" signal

The shipping flow (per WMS-SHIP-001) listens for ORDER_PACKED to determine which orders are ready for label generation.

  • WMS-PICK-001 — Picking (the upstream — produces the PICKED order this SOP starts with)
  • WMS-PICK-002 — Pick bin labels (the bin barcode this SOP scans in §4.5)
  • WMS-PICK-003 — Short-pick recovery (pre-pack disposition decisions)
  • WMS-INV-006 §4.4 — Box recommendation (where boxLabel and dimensions come from)
  • WMS-SHIP-001 — Shipping label creation (the immediate downstream — fires on ORDER_PACKED)
  • WMS-AUD-002 — Pack-time variance investigation (when written)

6. Audit & compliance

The packing flow has strong audit data overall, with one significant scan-validation gap on the direct-mode path:

  • Bin-mode is fully auditable per item: PickBinItem.verifiedQty increments per scan, task_events records the bin's completion, the scan-validation rejects bad barcodes server-side. A regulator asking "show me proof every unit in this bin was scanned at pack" can reconstruct from verifiedQty and bin contents.
  • Direct-mode is auditable as "verify happened on this task item" but not as "the scanned barcode matched." The taskItemId is captured; the scanned barcode is not. A direct-mode order can be packed with the wrong contents and the audit trail won't catch it — it'll show "all items verified" with no record of what was actually scanned.
  • Weight + dimensions captured on WorkTask.packedWeight, packedWeightUnit, packedDimensions. Carried forward to shipping for carrier label generation.
  • Photos captured in packing_images table, with FKs to order, task, and bin. Cascade-deletes with the order.

Manager weekly review:

  • Pull WorkTask WHERE type = 'PACKING' AND status = 'COMPLETED' AND createdAt > now() - 7 days. Cross-reference with shipping events — should be a 1:1 correspondence between packed and (eventually) shipped orders.
  • Pull WorkTask WHERE type = 'PACKING' AND status = 'IN_PROGRESS' AND startedAt < now() - 4 hours — stale pack tasks. Each one is an order someone walked away from mid-pack.
  • Pull Order WHERE status = 'PACKED' AND updatedAt < now() - 24 hours AND status != 'SHIPPED' — packed orders that haven't shipped within a day. Either the shipping queue is backed up or labels are failing.

Quarterly governance:

  • Direct-mode rate vs. bin-mode rate. Lower direct-mode % is better (better audit).
  • Photos-attached rate per high-value order class. Should trend up if photo discipline is enforced.
  • Pack-time per order (mean, median, p95). Useful for staffing and throughput planning.
  • Wrong-pack rate (returns coded as "wrong item received" / packed orders, lagged). Cross-reference with WMS-RET-002.

7. Troubleshooting

SymptomCauseResolution
Cannot start packing: order is {status}, expected PICKED (HTTP 400)Order isn't PICKED yetCheck the order's actual status. If PICKING, the picker isn't done. If PACKED, refresh — the order's already packed. If PARTIALLY_SHIPPED, the order has been split via WMS-PICK-003 §4.4.
Active pack task {taskNumber} already exists (HTTP 400)Someone else (or you, in another tab) started packing this orderRefresh the queue, the order should now show PACKING. Tap to resume.
No completed pick items found for packing (HTTP 400)The pick task didn't complete cleanly, or all pick items went SHORT (no COMPLETED items)Investigate the pick task. If all items are SHORT, the order has no shipable contents — see WMS-PICK-003 for the recovery decision.
Bin lookup returns 404Wrong barcode, bin was cancelled, or bin belongs to a different orderVerify the physical bin label. Check the PickBin row for the barcode. If barcode is right but bin is CANCELLED, the order may have been re-binned — find the active bin.
Item with barcode "{barcode}" not in this bin (HTTP 400)Scanning the wrong item, or the picker put a wrong unit in the binVerify physically. If a wrong unit, do not complete the bin — return the item to inventory (per WMS-INV-001 §4.1) and request the correct unit from the pick area.
{n} items still pending verification (HTTP 400) on completePackingDirect-mode: not all task items are COMPLETEDFind the unverified items and verify them. The page should show progress; refresh if it doesn't.
{n} item(s) not fully verified: {skus} (HTTP 400) on completeBinBin-mode: bin items have verifiedQty < quantityContinue scanning. Each bin item must reach verifiedQty === quantity.
Order showed PACKED after first bin even though there are morePer §4.6 callout, single-bin endpoint sets Order.status: PACKED immediatelyMulti-bin orders should use /pack/complete-from-bins (plural). If first bin used the single endpoint by mistake, the second and third bins still need packing — use the same endpoint per remaining bin (it'll log Order.status as PACKED again, no-op). The packed/shipped audit will be slightly noisy but the result is correct.
Bin {binNumber} already completed (HTTP 400)Trying to re-complete an already-completed binThe bin is done. Move to the next bin or the next order.
Direct-mode pack completed but customer complains about wrong itemPer §4.3 callout, direct-mode doesn't validate the scanned barcode server-side. The task item said "verified" but the scan may have been ignoredProcess the customer return per WMS-RET-001. To prevent recurrence, switch to bin mode (always pre-stage). Engineering ticket: wire scan validation into direct-mode verifyPackItem.
Photos didn't uploadGCS upload failed, network dropRetry. The upload is independent of pack completion; you can pack first and add photos after.
Multi-bin order: completed all bins but order still shows PACKINGThe plural endpoint may have failed partway, or some bins were completed via the singular endpoint and miscountManually update Order.status: PACKED via DB if the bins are all COMPLETED and the items are all packed. Or reach to IT for transaction recovery.
Pack queue shows an order that's already shippedCache hasn't refreshed (30s auto-refresh, but stale cache possible if recently shipped)Wait 30s, refresh manually. If still showing, escalate to IT.
Pack-station bin scan keeps trying bin lookup, but the barcode is an order numberCustom barcode that doesn't start with BIN- will fall through to bin-lookup. Order-number match runs firstThe page falls back to bin lookup for unknown barcodes. If you wanted order-by-number, type it explicitly into the search field rather than scanning.

8. Escalation

  • Wire scan validation into direct-mode verifyPackItem. Today, the scanned barcode isn't even passed to the API. Adding scannedItemBarcode to the request body and comparing against the variant in verifyPackItem is a small ticket with significant audit benefit. Mirrors the bin-mode behavior. Highest-leverage packing fix.
  • Always create the WorkTask in PENDING state, never retroactively in COMPLETED. Bin-mode completePackingFromBin creates the task already-completed (per packing.repo.ts:490-523). This makes "show me a list of pack tasks in flight" impossible for bin-mode orders. Refactor to create the task at bin-scan time and progress it normally.
  • Multi-bin order status semantics. The single-bin endpoint sets Order.status: PACKED on the first call regardless of remaining bins. Either deprecate the single-bin endpoint, or make it check bins[].status first and only mark PACKED on last completion.
  • Photo enforcement. Add a requirePackingPhoto flag on the variant or order class, and have completePacking / completePackingFromBin/s reject completion if the order has the flag and no PackingImage.
  • Bin reassignment for items in the wrong bin. If a packer scans an item that's not in the current bin but is in another bin on the order, the system rejects with Item with barcode "{barcode}" not in this bin. A friendlier flow would say "this item is in Bin 2, switch?" — small UX ticket, big impact for multi-bin packers.
  • Stuck pack task investigation. WMS-AUD-002 (when written) — pull stuck tasks weekly, walk them physically, decide reassign / cancel / complete. Until that SOP exists, the warehouse manager handles per Manager weekly review queries above.
  • Suspected pack-time fraud or repeated wrong-pack patterns. Manager spot-checks + cross-reference with returns. Without server-side scan validation in direct mode, audit trail is weak; bin mode + photos is stronger evidence.

9. Revision history

VersionDateAuthorChanges
1.0[DATE][NAME]Initial release. Documents the two packing modes — direct (generatePackListverifyPackItem × N → completePacking with weight + dimensions) and bin (getOrderByBinBarcodeverifyBinItem × N per bin → completePackingFromBin or completePackingFromBins). Documents the pack queue at /pack with the dual-purpose bin/order scan input. Documents the PackingImage model for optional photo capture. Documents three real findings: (a) direct-mode does not validate scanned barcodes server-side — the taskItemId is the only thing on the verify request, no barcode is captured; bin-mode does validate (compares against pv.upc || pv.barcode || pv.sku). (b) completePackingFromBin (singular) sets Order.status: PACKED on first call regardless of remaining bins (per packing.repo.ts:486-489); multi-bin orders should use /pack/complete-from-bins (plural). (c) Bin-mode completePackingFromBin retroactively creates the WorkTask in COMPLETED status (per packing.repo.ts:490-523), which makes "in-flight pack tasks" queries miss bin-mode orders. (d) Direct-mode overwrites OrderItem.quantityPicked from the bin's quantity rather than summing — wrong for multi-bin orders. Cross-references WMS-PICK-001 (upstream), WMS-PICK-002 (bin barcodes), WMS-PICK-003 (pre-pack disposition), WMS-INV-006 (box recommendation), WMS-SHIP-001 (downstream). Code references: packing.service.ts:112-490, packing.repo.ts:323-525, fulfillment.routes.ts:103-141, 169-213, 694-728, pages/pack/index.tsx, pages/fulfillment/PackStation.tsx, pages/fulfillment/[id].tsx (the multi-mode fulfillment page), orders.prisma:228-260 (PickBin), orders.prisma:105-128 (PackingImage).