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>
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 |