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>
367 lines
9.4 KiB
Markdown
367 lines
9.4 KiB
Markdown
# 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](product-management.md)
|
|
- [Admin Inventory Migration Plan](../implementation/inventory-admin-migration.md)
|
|
- [Store Operations Expansion](../development/migration/store-operations-expansion.md)
|