# Stock Management Integration **Created:** January 1, 2026 **Status:** Implemented ## Overview This document describes the automatic inventory synchronization between orders and stock levels. When order status changes, inventory is automatically updated to maintain accurate stock counts. ## Architecture ### Services Involved ``` OrderService OrderInventoryService │ │ ├─ update_order_status() ──────────► handle_status_change() │ │ │ ├─► reserve_for_order() │ ├─► fulfill_order() │ └─► release_order_reservation() │ │ │ ▼ │ InventoryService │ │ │ ├─► reserve_inventory() │ ├─► fulfill_reservation() │ └─► release_reservation() ``` ### Key Files | File | Purpose | |------|---------| | `app/services/order_inventory_service.py` | Orchestrates order-inventory operations | | `app/services/order_service.py` | Calls inventory hooks on status change | | `app/services/inventory_service.py` | Low-level inventory operations | ## Status Change Inventory Actions | Status Transition | Inventory Action | Description | |-------------------|------------------|-------------| | Any → `processing` | Reserve | Reserves stock for order items | | Any → `shipped` | Fulfill | Deducts from stock and releases reservation | | Any → `cancelled` | Release | Returns reserved stock to available | ## Inventory Operations ### Reserve Inventory When an order status changes to `processing`: 1. For each order item: - Find inventory record with available quantity - Increase `reserved_quantity` by item quantity - Log the reservation 2. Placeholder products (unmatched Letzshop items) are skipped ### Fulfill Inventory When an order status changes to `shipped`: 1. For each order item: - Decrease `quantity` by item quantity (stock consumed) - Decrease `reserved_quantity` accordingly - Log the fulfillment ### Release Reservation When an order is `cancelled`: 1. For each order item: - Decrease `reserved_quantity` (stock becomes available again) - Total `quantity` remains unchanged - Log the release ## Error Handling Inventory operations use **soft failure** - if inventory cannot be updated: 1. Warning is logged 2. Order status update continues 3. Inventory can be manually adjusted This ensures orders are never blocked by inventory issues while providing visibility into any problems. ## Edge Cases ### Placeholder Products Letzshop orders may contain unmatched GTINs that map to placeholder products. These are identified by: - GTIN `0000000000000` - Product linked to placeholder MarketplaceProduct Inventory operations skip placeholder products since they have no real stock. ### Missing Inventory If a product has no inventory record: - Operation is skipped with `skip_missing=True` - Item is logged in `skipped_items` list - No error is raised ### Multi-Location Inventory The service finds the first location with available stock: ```python def _find_inventory_location(db, product_id, vendor_id): return ( db.query(Inventory) .filter( Inventory.product_id == product_id, Inventory.vendor_id == vendor_id, Inventory.quantity > Inventory.reserved_quantity, ) .first() ) ``` ## Usage Example ### Automatic (Via Order Status Update) ```python from app.services.order_service import order_service from models.schema.order import OrderUpdate # Update order status - inventory is handled automatically order = order_service.update_order_status( db=db, vendor_id=vendor_id, order_id=order_id, order_update=OrderUpdate(status="processing") ) # Inventory is now reserved for this order ``` ### Direct (Manual Operations) ```python from app.services.order_inventory_service import order_inventory_service # Reserve inventory for an order result = order_inventory_service.reserve_for_order( db=db, vendor_id=vendor_id, order_id=order_id, skip_missing=True ) print(f"Reserved: {result['reserved_count']}, Skipped: {len(result['skipped_items'])}") # Fulfill when shipped result = order_inventory_service.fulfill_order( db=db, vendor_id=vendor_id, order_id=order_id ) # Release if cancelled result = order_inventory_service.release_order_reservation( db=db, vendor_id=vendor_id, order_id=order_id ) ``` ## Inventory Model ```python class Inventory: quantity: int # Total stock reserved_quantity: int # Reserved for pending orders @property def available_quantity(self): return self.quantity - self.reserved_quantity ``` ## Logging All inventory operations are logged: ``` INFO: Reserved 2 units of product 123 for order ORD-1-20260101-ABC123 INFO: Order ORD-1-20260101-ABC123: reserved 3 items, skipped 1 INFO: Fulfilled 2 units of product 123 for order ORD-1-20260101-ABC123 WARNING: Order ORD-1-20260101-ABC123 inventory operation failed: No inventory found ``` ## Future Enhancements 1. **Inventory Transaction Log** - Audit trail for all stock movements 2. **Multi-Location Selection** - Choose which location to draw from 3. **Backorder Support** - Handle orders when stock is insufficient 4. **Return Processing** - Increase stock when orders are returned