Skip to content

Pick & Pack Module

Enterprise WMS-style picking and packing with optimal route calculation and real-time progress tracking.

Overview

The Pick & Pack module automates the picking and packing workflow, generating optimized pick lists based on zone coordinates and routing algorithms. It integrates seamlessly with inventory management and provides real-time tracking of picking progress.

Features

Core Capabilities

  • Automated Pick List Generation: Create pick lists from orders/shipments
  • Optimal Route Calculation: Uses routing engine for zone-ordered picking
  • Real-Time Progress Tracking: Live updates of picking status
  • Pack Session Management: Track packing operations
  • Inventory Integration: Automatic stock movement creation
  • Multi-Item Support: Handle multiple items per pick list
  • Assignment Management: Assign pick lists to workers

Architecture

mermaid
graph TB
    A[Orders/Shipments] --> B[Create Pick List]
    B --> C[Routing Engine]
    C --> D[Optimal Zone Sequence]
    D --> E[Pick List Items]
    E --> F[Worker Picks Items]
    F --> G[Inventory Outbound]
    F --> H[Pack Session]
    H --> I[Completed]
    
    style B fill:#3b82f6
    style C fill:#10b981
    style F fill:#f59e0b

Database Schema

pick_lists Table

ColumnTypeConstraintsDescription
iduuidPRIMARY KEYUnique pick list identifier
tenant_iduuidNOT NULL, FK → tenantsTenant isolation
created_byuuidNOT NULL, FK → profilesCreator user
assigned_touuidFK → profiles, NULLAssigned worker
statustextNOT NULL, CHECKStatus: 'pending', 'picking', 'completed'
created_attimestamptzNOT NULL, DEFAULT now()Creation timestamp
updated_attimestamptzNOT NULL, DEFAULT now()Last update timestamp

Indexes:

  • idx_pick_lists_tenant on tenant_id
  • idx_pick_lists_status on status
  • idx_pick_lists_assigned on assigned_to

pick_list_items Table

ColumnTypeConstraintsDescription
iduuidPRIMARY KEYUnique item identifier
tenant_iduuidNOT NULL, FK → tenantsTenant isolation
pick_list_iduuidNOT NULL, FK → pick_listsParent pick list
item_iduuidNOT NULL, FK → inventory_itemsInventory item
qty_requiredintegerNOT NULL, > 0Required quantity
qty_pickedintegerNOT NULL, >= 0, DEFAULT 0Picked quantity
statustextNOT NULL, CHECKStatus: 'pending', 'picked'
zone_iduuidFK → zones, NULLItem zone location
sequenceintegerNOT NULLPick order (from routing)
created_attimestamptzNOT NULL, DEFAULT now()Creation timestamp

Indexes:

  • idx_pick_list_items_pick_list on pick_list_id
  • idx_pick_list_items_item on item_id
  • idx_pick_list_items_sequence on (pick_list_id, sequence)

pack_sessions Table

ColumnTypeConstraintsDescription
iduuidPRIMARY KEYUnique session identifier
tenant_iduuidNOT NULL, FK → tenantsTenant isolation
pick_list_iduuidNOT NULL, FK → pick_listsRelated pick list
packed_byuuidNOT NULL, FK → profilesPacker user
packed_attimestamptzNOT NULL, DEFAULT now()Pack timestamp

Indexes:

  • idx_pack_sessions_pick_list on pick_list_id
  • idx_pack_sessions_tenant on tenant_id

API Functions

Create Pick List

typescript
import { createPickList } from '@/lib/pickpack';

const { data, error } = await createPickList({
  items: [
    { item_id: 'item-1-uuid', qty_required: 5 },
    { item_id: 'item-2-uuid', qty_required: 10 },
    { item_id: 'item-3-uuid', qty_required: 3 },
  ],
  created_by: 'user-uuid',
  assigned_to: 'worker-uuid', // Optional
  tenant_id: 'tenant-uuid', // Optional, auto-set from context
});

Parameters:

  • items (array, required): Array of { item_id, qty_required, zone_id? }
  • created_by (uuid, required): Creator user ID
  • assigned_to (uuid, optional): Assigned worker ID
  • tenant_id (uuid, optional): Tenant ID (auto-set from auth context)

Returns: { data: PickList | null, error: any }

Behavior:

  1. Fetches zone information for each item
  2. Calculates optimal zone sequence using routing engine
  3. Creates pick list with items ordered by optimal route
  4. Sets sequence numbers based on route order

Get Pick List with Items

typescript
import { getPickListWithItems } from '@/lib/pickpack';

const { data, error } = await getPickListWithItems('pick-list-uuid');
// Returns: PickListWithItems (includes items array)

Update Pick Item Status

typescript
import { updatePickItemStatus } from '@/lib/pickpack';

// Mark item as picked
const { data, error } = await updatePickItemStatus(
  'pick-list-item-uuid',
  'picked',
  5 // qty_picked
);

Parameters:

  • pickListItemId (string, required): Pick list item ID
  • status (string, required): 'pending' or 'picked'
  • qtyPicked (number, required): Quantity picked

Effect:

  • Updates item status and quantity
  • Creates outbound movement in inventory
  • Updates pick list status if all items picked

Complete Pick List

typescript
import { completePickList } from '@/lib/pickpack';

const { data, error } = await completePickList('pick-list-uuid');

Effect:

  • Sets pick list status to 'completed'
  • Validates all items are picked
  • Updates timestamps

Create Pack Session

typescript
import { createPackSession } from '@/lib/pickpack';

const { data, error } = await createPackSession({
  pick_list_id: 'pick-list-uuid',
  packed_by: 'user-uuid',
});

Returns: { data: PackSession | null, error: any }

Routing Integration

The Pick & Pack module uses the routing engine to calculate optimal pick routes:

mermaid
sequenceDiagram
    participant PL[Pick List Creation]
    participant RE[Routing Engine]
    participant DB[Database]
    
    PL->>DB: Get item zones
    DB->>PL: Return zone IDs
    PL->>RE: findOptimalPath(zoneIds)
    RE->>DB: Get zone coordinates
    DB->>RE: Return coordinates
    RE->>RE: Calculate optimal sequence
    RE->>PL: Return zone order
    PL->>DB: Create items with sequence

Route Calculation:

  1. Extract unique zone IDs from items
  2. Call findOptimalPath() from routing engine
  3. Use returned zone order to sequence items
  4. Assign sequence numbers to pick list items

Data Flow

mermaid
sequenceDiagram
    participant Order
    participant PickList
    participant Routing
    participant Worker
    participant Inventory
    participant Pack
    
    Order->>PickList: Create pick list
    PickList->>Routing: Calculate route
    Routing->>PickList: Return sequence
    PickList->>Worker: Assign pick list
    Worker->>PickList: Pick items
    PickList->>Inventory: Create outbound
    Worker->>Pack: Create pack session
    Pack->>PickList: Mark completed

Permissions

RolePermissions
AdminFull access: create, assign, complete pick lists
Inventory WorkerCreate pick lists, pick items, create pack sessions
DriverNo access to pick & pack
Tenant AdminFull access within tenant

Integration Points

Inventory Integration

When items are picked, outbound movements are automatically created:

typescript
// Automatic when item status changes to 'picked'
// Creates: stock_movement with type='outbound'

Routing Integration

Uses routing engine for optimal zone sequencing:

typescript
import { findOptimalPath } from '@/lib/routingEngine';

const zoneIds = items.map(item => item.zone_id).filter(Boolean);
const { data: optimalPath } = await findOptimalPath(zoneIds);
// Use optimalPath.zone_order for item sequencing

Warehouse Map Integration

Pick lists are visualized on the warehouse floor plan:

typescript
// Floor plan shows:
// - Pick list zones
// - Optimal route path
// - Current picking progress

Best Practices

1. Use Optimal Routing

Always let the system calculate optimal routes:

typescript
// ✅ GOOD - System calculates route
await createPickList({ items: [...] });

// ❌ BAD - Manual sequencing
// Don't manually set sequence numbers

2. Assign to Workers

Assign pick lists to specific workers for accountability:

typescript
await createPickList({
  items: [...],
  assigned_to: 'worker-uuid',
});

3. Track Progress

Regularly check pick list status:

typescript
const { data: pickList } = await getPickListWithItems(pickListId);
const progress = pickList.items.filter(i => i.status === 'picked').length / pickList.items.length;

4. Complete Pack Sessions

Always create pack sessions when packing is complete:

typescript
await createPackSession({
  pick_list_id: pickListId,
  packed_by: userId,
});

Troubleshooting

Issue: Items Not in Optimal Order

Symptom: Pick list items not ordered by zone proximity.

Solution:

  1. Verify zones have coordinates (x, y, z)
  2. Check routing engine is working
  3. Re-run computeAllDistances() script

Issue: Outbound Movement Not Created

Symptom: Picking item doesn't create inventory movement.

Solution:

  1. Verify item status is set to 'picked'
  2. Check inventory module is integrated
  3. Verify user has inventory permissions

Issue: Pick List Stuck in "Picking" Status

Symptom: Pick list doesn't complete even when all items picked.

Solution:

  1. Verify all items have status 'picked'
  2. Check qty_picked matches qty_required
  3. Manually call completePickList() if needed

Released under Commercial License