# 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 ```python # 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`: ```python 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: ```http POST /api/v1/store/inventory/set { "product_id": 123, "location": "WAREHOUSE_A", "quantity": 100 } ``` ### Adjust Inventory Add or remove stock (positive = add, negative = remove): ```http 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: ```http POST /api/v1/store/inventory/reserve { "product_id": 123, "location": "WAREHOUSE_A", "quantity": 5 } ``` ### Release Reservation Cancel a reservation (order cancelled): ```http POST /api/v1/store/inventory/release { "product_id": 123, "location": "WAREHOUSE_A", "quantity": 5 } ``` ### Fulfill Reservation Complete an order (items shipped): ```http 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 ```http GET /api/v1/admin/inventory GET /api/v1/admin/inventory?store_id=1 GET /api/v1/admin/inventory?low_stock=10 ``` ### Get Inventory Statistics ```http 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 ```http 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) ```http POST /api/v1/admin/inventory/set { "store_id": 1, "product_id": 123, "location": "WAREHOUSE_A", "quantity": 100 } ``` ### Adjust Inventory (Admin) ```http 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 ```sql 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 | --- ## Related Documentation - [Product Management](../catalog/architecture.md) - [Admin Inventory Migration Plan](../../implementation/inventory-admin-migration.md) - [Store Operations Expansion](../../development/migration/store-operations-expansion.md)