# Admin Inventory Management Migration Plan ## Overview **Objective:** Add inventory management capabilities to the admin "Store Operations" section, allowing administrators to view and manage store inventory on their behalf. **Status:** Phase 1 Complete --- ## Current State Analysis ### What Exists The inventory system is **fully implemented at the store API level** with comprehensive functionality: | Component | Status | Location | |-----------|--------|----------| | Database Model | ✅ Complete | `models/database/inventory.py` | | Pydantic Schemas | ✅ Complete | `models/schema/inventory.py` | | Service Layer | ✅ Complete | `app/services/inventory_service.py` | | Store API | ✅ Complete | `app/api/v1/store/inventory.py` | | Exceptions | ✅ Complete | `app/exceptions/inventory.py` | | Unit Tests | ✅ Complete | `tests/unit/services/test_inventory_service.py` | | Integration Tests | ✅ Complete | `tests/integration/api/v1/store/test_inventory.py` | | Store UI | 🔲 Placeholder | `app/templates/store/inventory.html` | | Admin API | 🔲 Not Started | - | | Admin UI | 🔲 Not Started | - | | Audit Trail | 🔲 Not Started | Logs only, no dedicated table | ### Storage/Location Architecture The inventory system tracks stock at the **storage location level**: ``` Product A ├── 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 ``` **Key design decisions:** - One product can have inventory across multiple locations - Unique constraint: `(product_id, location)` - one entry per product/location - Locations are text strings, normalized to UPPERCASE - Available quantity = `quantity - reserved_quantity` ### Existing Operations | Operation | Description | Service Method | |-----------|-------------|----------------| | **Set** | Replace exact quantity at location | `set_inventory()` | | **Adjust** | Add/remove quantity (positive/negative) | `adjust_inventory()` | | **Reserve** | Mark items for pending order | `reserve_inventory()` | | **Release** | Cancel reservation | `release_reservation()` | | **Fulfill** | Complete order (reduces both qty & reserved) | `fulfill_reservation()` | | **Get Product** | Summary across all locations | `get_product_inventory()` | | **Get Store** | List with filters | `get_store_inventory()` | | **Update** | Partial field update | `update_inventory()` | | **Delete** | Remove inventory entry | `delete_inventory()` | ### Store API Endpoints All endpoints at `/api/v1/store/inventory/*`: | Method | Endpoint | Operation | |--------|----------|-----------| | POST | `/inventory/set` | Set exact quantity | | POST | `/inventory/adjust` | Add/remove quantity | | POST | `/inventory/reserve` | Reserve for order | | POST | `/inventory/release` | Cancel reservation | | POST | `/inventory/fulfill` | Complete order | | GET | `/inventory/product/{id}` | Product summary | | GET | `/inventory` | List with filters | | PUT | `/inventory/{id}` | Update entry | | DELETE | `/inventory/{id}` | Delete entry | --- ## Gap Analysis ### What's Missing for Admin Store Operations 1. **Admin API Endpoints** - ✅ Implemented in Phase 1 2. **Admin UI Page** - No inventory management interface in admin panel 3. **Store Selector** - Admin needs to select which store to manage 4. **Cross-Store View** - ✅ Implemented in Phase 1 5. **Audit Trail** - Only application logs, no queryable audit history 6. **Bulk Operations** - No bulk adjust/import capabilities 7. **Low Stock Alerts** - Basic filter exists, no alert configuration ### Digital Products - Infinite Inventory ✅ **Implementation Complete** Digital products now have unlimited inventory by default: ```python # In Product model (models/database/product.py) UNLIMITED_INVENTORY = 999999 @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 (total - reserved). Digital products return unlimited inventory. """ if self.has_unlimited_inventory: return self.UNLIMITED_INVENTORY return sum(inv.available_quantity for inv in self.inventory_entries) ``` **Behavior:** - Physical products: Sum of inventory entries (0 if no entries) - Digital products: Returns `999999` (unlimited) regardless of entries - Orders for digital products never fail due to "insufficient inventory" **Tests:** `tests/unit/models/database/test_product.py::TestProductInventoryProperties` **Documentation:** [Inventory Management Guide](../guides/inventory-management.md) --- ## Migration Plan ### Phase 1: Admin API Endpoints **Goal:** Expose inventory management to admin users with store selection #### 1.1 New File: `app/api/v1/admin/inventory.py` Admin endpoints that mirror store functionality with store_id as parameter: | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/admin/inventory` | List all inventory (cross-store) | | GET | `/admin/inventory/stores/{store_id}` | Store-specific inventory | | GET | `/admin/inventory/products/{product_id}` | Product inventory summary | | POST | `/admin/inventory/set` | Set inventory (requires store_id) | | POST | `/admin/inventory/adjust` | Adjust inventory | | PUT | `/admin/inventory/{id}` | Update inventory entry | | DELETE | `/admin/inventory/{id}` | Delete inventory entry | | GET | `/admin/inventory/low-stock` | Low stock report | #### 1.2 Schema Extensions Add admin-specific request schemas in `models/schema/inventory.py`: ```python class AdminInventoryCreate(InventoryCreate): """Admin version - requires explicit store_id.""" store_id: int = Field(..., description="Target store ID") class AdminInventoryAdjust(InventoryAdjust): """Admin version - requires explicit store_id.""" store_id: int = Field(..., description="Target store ID") class AdminInventoryListResponse(BaseModel): """Cross-store inventory list.""" inventories: list[InventoryResponse] total: int skip: int limit: int store_filter: int | None = None ``` #### 1.3 Service Layer Reuse The existing `InventoryService` already accepts `store_id` as a parameter - **no service changes needed**. Admin endpoints simply pass the store_id from the request instead of from the JWT token. ### Phase 2: Admin UI **Goal:** Create admin inventory management page #### 2.1 New Files | File | Description | |------|-------------| | `app/templates/admin/inventory.html` | Main inventory page | | `static/admin/js/inventory.js` | Alpine.js controller | #### 2.2 UI Features 1. **Store Selector Dropdown** - Filter by store (or show all) 2. **Inventory Table** - Product, Location, Quantity, Reserved, Available 3. **Search/Filter** - By product name, location, low stock 4. **Adjust Modal** - Quick add/remove with reason 5. **Pagination** - Handle large inventories 6. **Export** - CSV download (future) #### 2.3 Page Layout ``` ┌─────────────────────────────────────────────────────────┐ │ Inventory Management [Store: All ▼] │ ├─────────────────────────────────────────────────────────┤ │ [Search...] [Location ▼] [Low Stock Only ☐] │ ├─────────────────────────────────────────────────────────┤ │ Product │ Location │ Qty │ Reserved │ Available │ │──────────────┼─────────────┼─────┼──────────┼───────────│ │ Widget A │ WAREHOUSE_A │ 100 │ 10 │ 90 [⚙️] │ │ Widget A │ WAREHOUSE_B │ 50 │ 0 │ 50 [⚙️] │ │ Gadget B │ WAREHOUSE_A │ 25 │ 5 │ 20 [⚙️] │ ├─────────────────────────────────────────────────────────┤ │ Showing 1-20 of 150 [< 1 2 3 ... >] │ └─────────────────────────────────────────────────────────┘ ``` #### 2.4 Sidebar Integration Add to `app/templates/admin/partials/sidebar.html`: ```html