Skip to main content

Inventory Management

Switchyard provides a hierarchical inventory location system with FEFO/FIFO picking support, designed for warehouse and retail operations.

Location Hierarchy

Inventory locations follow a 5-level hierarchy:
Zone > Aisle > Bay > Shelf > Slot
LevelDescriptionExample
ZoneMajor area of the warehouseA (Ambient), C (Chilled), F (Frozen)
AisleNumbered pathway1, 2, 3
BaySection within an aisle1, 2, 3
ShelfVertical position in bay1 (bottom) to 5 (top)
SlotHorizontal position on shelf1 to 4

Location Code Format

Location codes follow the pattern: {Zone}{Aisle}-{Bay}-{Shelf}-{Slot} Examples:
  • A1-1-1-1 - Zone A (Ambient), Aisle 1, Bay 1, Shelf 1, Slot 1
  • F2-3-4-2 - Zone F (Frozen), Aisle 2, Bay 3, Shelf 4, Slot 2
  • C1-2-1-1 - Zone C (Chilled), Aisle 1, Bay 2, Shelf 1, Slot 1

Inventory Items

The inventory_items table tracks physical inventory in the RFC warehouse with support for FEFO/FIFO picking.

Key Fields

FieldDescription
sellable_product_idReference to the sellable product
location_idPhysical location in warehouse
quantityTotal quantity in this item
reserved_quantityQuantity reserved for orders
received_atWhen received (for FIFO)
expiration_dateExpiration date (for FEFO, nullable)
lot_numberLot tracking
source_sweep_idWhich sweep brought this in
unit_costAcquisition cost

FEFO/FIFO Picking

When picking inventory for orders, items are selected in this order:
  1. FEFO (First Expired, First Out) - Items expiring soonest are picked first
  2. FIFO (First In, First Out) - For non-perishables, oldest received items are picked first
-- Picking order
SELECT * FROM inventory_items
WHERE sellable_product_id = $1
  AND is_available = true
  AND quantity > reserved_quantity
ORDER BY 
    expiration_date ASC NULLS LAST,  -- FEFO: expiring soonest first
    received_at ASC                   -- FIFO: oldest received first
LIMIT 1;
Items without an expiration date (non-perishables) are picked last among items with the same received date.

API Endpoints

Lookup by Barcode

GET /scanner/inventory/lookup?barcode=012345678901
Response:
{
  "success": true,
  "barcode": "012345678901",
  "product": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Organic Bananas",
    "brand": "Dole",
    "selling_price": 1.99,
    "is_perishable": true,
    "warehouse_zone": "C"
  },
  "inventory": {
    "total_quantity": 48,
    "total_reserved": 12,
    "available_quantity": 36,
    "items": [
      {
        "id": "inv-item-uuid",
        "location_id": "loc-uuid",
        "quantity": 24,
        "reserved_quantity": 6,
        "available": 18,
        "expiration_date": "2025-01-15",
        "received_at": "2024-12-15T10:00:00Z",
        "lot_number": "LOT-2024-001"
      }
    ]
  }
}

Process Inventory Scan

POST /scanner/inventory/scan
Content-Type: application/json

{
  "barcode": "012345678901",
  "location_id": "loc-uuid",
  "quantity": 24,
  "action": "receive",
  "expiration_date": "2025-01-15",
  "lot_number": "LOT-2024-001"
}
Actions:
ActionDescription
lookupJust look up product and inventory
adjustAdd or subtract from current quantity
countSet exact count (inventory audit)
receiveReceive new inventory (e.g., from sweep)

Receive Response

{
  "success": true,
  "barcode": "012345678901",
  "action": "receive",
  "product": {
    "id": "prod-uuid",
    "name": "Organic Bananas",
    "is_perishable": true
  },
  "inventory": {
    "item_id": "new-inv-item-uuid",
    "quantity": 24,
    "reserved_quantity": 0,
    "available": 24,
    "location_id": "loc-uuid",
    "expiration_date": "2025-01-15"
  },
  "totals": {
    "total_quantity": 48,
    "total_reserved": 12,
    "available": 36
  },
  "scanned_at": "2024-12-18T15:30:00Z",
  "scanned_by": "staff-uuid"
}

Inventory Replenishment

When inventory falls below reorder points, the system can automatically create inventory sweeps.

Sourcing Decision

The system compares individual vs bulk pricing:
  1. Check if a bulk SKU relationship exists
  2. Compare per-unit cost for individual vs bulk
  3. Select the best retailer based on price

List Inventory Groups

GET /admin/inventory-groups
Query parameters:
  • type - Filter by level type (zone, aisle, bay, shelf, slot)
  • parent_group_id - Filter by parent location

Permissions Required

ActionPermission
View inventoryinventory.read
Update quantitiesinventory.write
Scan operationsinventory.scan
Receive inventoryinventory.receive
Manage locationsinventory.locations