Move 39 documentation files from top-level docs/ into each module's docs/ folder, accessible via symlinks from docs/modules/. Create data-model.md files for 10 modules with full schema documentation. Replace originals with redirect stubs. Remove empty guide stubs. Modules migrated: tenancy, billing, loyalty, marketplace, orders, messaging, cms, catalog, inventory, hosting, prospecting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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_inventoryproperty returns999999(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
productsandstorestables - Non-negative:
quantityandreserved_quantitymust be >= 0
Best Practices
Physical Products
- Create inventory entries before accepting orders
- Use meaningful location names (e.g.,
WAREHOUSE_MAIN,STORE_NYC) - Monitor low stock using the admin dashboard or API
- Reserve on order creation to prevent overselling
Digital Products
- No inventory setup needed - unlimited by default
- Optional: Create entries for license key tracking
- Focus on fulfillment - digital delivery mechanism
Multi-Location
- Aggregate queries use
Product.total_inventoryandProduct.available_inventory - Location-specific operations use the Inventory model directly
- 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 |