Files
orion/docs/guides/inventory-management.md
Samir Boulahtit e9253fbd84 refactor: rename Wizamart to Orion across entire codebase
Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:46:56 +01:00

9.4 KiB

Inventory Management

Overview

The Orion platform provides comprehensive inventory management with support for:

  • Multi-location tracking - Track stock across warehouses, stores, and storage bins
  • Reservation system - Reserve items for pending orders
  • Digital products - Automatic unlimited inventory for digital goods
  • Admin operations - Manage inventory on behalf of stores

Key Concepts

Storage Locations

Inventory is tracked at the storage location level. Each product can have stock in multiple locations:

Product: "Wireless Headphones"
├── WAREHOUSE_MAIN: 100 units (10 reserved)
├── WAREHOUSE_WEST: 50 units (0 reserved)
└── STORE_FRONT: 25 units (5 reserved)

Total: 175 units | Reserved: 15 | Available: 160

Location naming: Locations are text strings, normalized to UPPERCASE (e.g., WAREHOUSE_A, STORE_01).

Inventory States

Field Description
quantity Total physical stock at location
reserved_quantity Items reserved for pending orders
available_quantity quantity - reserved_quantity (can be sold)

Product Types & Inventory

Product Type Inventory Behavior
Physical Requires inventory tracking, orders check available stock
Digital Unlimited inventory - no stock constraints
Service Treated as digital (unlimited)
Subscription Treated as digital (unlimited)

Digital Products

Digital products have unlimited inventory by default. This means:

  • Orders for digital products never fail due to "insufficient inventory"
  • No need to create inventory entries for digital products
  • The available_inventory property returns 999999 (effectively unlimited)

How It Works

# In Product model
@property
def has_unlimited_inventory(self) -> bool:
    """Digital products have unlimited inventory."""
    return self.is_digital

@property
def available_inventory(self) -> int:
    """Calculate available inventory."""
    if self.has_unlimited_inventory:
        return 999999  # Unlimited
    return sum(inv.available_quantity for inv in self.inventory_entries)

Setting a Product as Digital

Digital products are identified by the is_digital flag on the MarketplaceProduct:

marketplace_product.is_digital = True
marketplace_product.product_type_enum = "digital"
marketplace_product.digital_delivery_method = "license_key"  # or "download", "email"

Inventory Operations

Set Inventory

Replace the exact quantity at a location:

POST /api/v1/store/inventory/set
{
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 100
}

Adjust Inventory

Add or remove stock (positive = add, negative = remove):

POST /api/v1/store/inventory/adjust
{
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": -10  // Remove 10 units
}

Reserve Inventory

Mark items as reserved for an order:

POST /api/v1/store/inventory/reserve
{
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 5
}

Release Reservation

Cancel a reservation (order cancelled):

POST /api/v1/store/inventory/release
{
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 5
}

Fulfill Reservation

Complete an order (items shipped):

POST /api/v1/store/inventory/fulfill
{
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 5
}

This decreases both quantity and reserved_quantity.


Reservation Workflow

┌─────────────────┐
│  Order Created  │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Reserve Items   │  reserved_quantity += order_qty
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────┐  ┌──────────┐
│Cancel │  │  Ship    │
└───┬───┘  └────┬─────┘
    │           │
    ▼           ▼
┌─────────┐  ┌──────────────┐
│ Release │  │   Fulfill    │
│reserved │  │ quantity -=  │
│  -= qty │  │ reserved -=  │
└─────────┘  └──────────────┘

Admin Inventory Management

Administrators can manage inventory on behalf of any store through the admin UI at /admin/inventory or via the API.

Admin UI Features

The admin inventory page provides:

  • Overview Statistics - Total entries, stock quantities, reserved items, and low stock alerts
  • Filtering - Filter by store, location, and low stock threshold
  • Search - Search by product title or SKU
  • Stock Adjustment - Add or remove stock with optional reason tracking
  • Set Quantity - Set exact stock quantity at any location
  • Delete Entries - Remove inventory entries

Admin API Endpoints

List All Inventory

GET /api/v1/admin/inventory
GET /api/v1/admin/inventory?store_id=1
GET /api/v1/admin/inventory?low_stock=10

Get Inventory Statistics

GET /api/v1/admin/inventory/stats

Response:
{
    "total_entries": 150,
    "total_quantity": 5000,
    "total_reserved": 200,
    "total_available": 4800,
    "low_stock_count": 12,
    "stores_with_inventory": 5,
    "unique_locations": 8
}

Low Stock Alerts

GET /api/v1/admin/inventory/low-stock?threshold=10

Response:
[
    {
        "product_id": 123,
        "store_name": "TechStore",
        "product_title": "USB Cable",
        "location": "WAREHOUSE_A",
        "quantity": 3,
        "available_quantity": 2
    }
]

Set Inventory (Admin)

POST /api/v1/admin/inventory/set
{
    "store_id": 1,
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 100
}

Adjust Inventory (Admin)

POST /api/v1/admin/inventory/adjust
{
    "store_id": 1,
    "product_id": 123,
    "location": "WAREHOUSE_A",
    "quantity": 25,
    "reason": "Restocking from supplier"
}

Database Schema

Inventory Table

CREATE TABLE inventory (
    id SERIAL PRIMARY KEY,
    product_id INTEGER NOT NULL REFERENCES products(id),
    store_id INTEGER NOT NULL REFERENCES stores(id),
    location VARCHAR NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 0,
    reserved_quantity INTEGER DEFAULT 0,
    gtin VARCHAR,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),

    UNIQUE(product_id, location)
);

CREATE INDEX idx_inventory_store_product ON inventory(store_id, product_id);
CREATE INDEX idx_inventory_product_location ON inventory(product_id, location);

Constraints

  • Unique constraint: (product_id, location) - One entry per product/location
  • Foreign keys: References products and stores tables
  • Non-negative: quantity and reserved_quantity must be >= 0

Best Practices

Physical Products

  1. Create inventory entries before accepting orders
  2. Use meaningful location names (e.g., WAREHOUSE_MAIN, STORE_NYC)
  3. Monitor low stock using the admin dashboard or API
  4. Reserve on order creation to prevent overselling

Digital Products

  1. No inventory setup needed - unlimited by default
  2. Optional: Create entries for license key tracking
  3. Focus on fulfillment - digital delivery mechanism

Multi-Location

  1. Aggregate queries use Product.total_inventory and Product.available_inventory
  2. Location-specific operations use the Inventory model directly
  3. Transfers between locations: adjust down at source, adjust up at destination

API Reference

Store Endpoints

Method Endpoint Description
POST /api/v1/store/inventory/set Set exact quantity
POST /api/v1/store/inventory/adjust Add/remove quantity
POST /api/v1/store/inventory/reserve Reserve for order
POST /api/v1/store/inventory/release Cancel reservation
POST /api/v1/store/inventory/fulfill Complete order
GET /api/v1/store/inventory/product/{id} Product summary
GET /api/v1/store/inventory List with filters
PUT /api/v1/store/inventory/{id} Update entry
DELETE /api/v1/store/inventory/{id} Delete entry

Admin Endpoints

Method Endpoint Description
GET /api/v1/admin/inventory List all (cross-store)
GET /api/v1/admin/inventory/stats Platform statistics
GET /api/v1/admin/inventory/low-stock Low stock alerts
GET /api/v1/admin/inventory/stores Stores with inventory
GET /api/v1/admin/inventory/locations Unique locations
GET /api/v1/admin/inventory/stores/{id} Store inventory
GET /api/v1/admin/inventory/products/{id} Product summary
POST /api/v1/admin/inventory/set Set (requires store_id)
POST /api/v1/admin/inventory/adjust Adjust (requires store_id)
PUT /api/v1/admin/inventory/{id} Update entry
DELETE /api/v1/admin/inventory/{id} Delete entry