# app/modules/inventory/schemas/inventory.py from datetime import datetime from pydantic import BaseModel, ConfigDict, Field class InventoryBase(BaseModel): product_id: int = Field(..., description="Product ID in store catalog") location: str = Field(..., description="Storage location") class InventoryCreate(InventoryBase): """Set exact inventory quantity (replaces existing).""" quantity: int = Field(..., description="Exact inventory quantity", ge=0) class InventoryAdjust(InventoryBase): """Add or remove inventory quantity.""" quantity: int = Field( ..., description="Quantity to add (positive) or remove (negative)" ) class InventoryUpdate(BaseModel): """Update inventory fields.""" quantity: int | None = Field(None, ge=0) reserved_quantity: int | None = Field(None, ge=0) location: str | None = None class InventoryReserve(BaseModel): """Reserve inventory for orders.""" product_id: int location: str quantity: int = Field(..., gt=0) class InventoryResponse(BaseModel): model_config = ConfigDict(from_attributes=True) id: int product_id: int store_id: int bin_location: str quantity: int reserved_quantity: int gtin: str | None created_at: datetime updated_at: datetime @property def available_quantity(self): return max(0, self.quantity - self.reserved_quantity) class InventoryLocationResponse(BaseModel): location: str quantity: int reserved_quantity: int available_quantity: int class ProductInventorySummary(BaseModel): """Inventory summary for a product.""" product_id: int store_id: int product_sku: str | None product_title: str total_quantity: int total_reserved: int total_available: int locations: list[InventoryLocationResponse] class InventoryListResponse(BaseModel): inventories: list[InventoryResponse] total: int skip: int limit: int class InventoryMessageResponse(BaseModel): """Simple message response for inventory operations.""" message: str class InventorySummaryResponse(BaseModel): """Inventory summary response for marketplace product service.""" gtin: str total_quantity: int locations: list[InventoryLocationResponse] # ============================================================================ # Admin Inventory Schemas # ============================================================================ class AdminInventoryCreate(BaseModel): """Admin version of inventory create - requires explicit store_id.""" store_id: int = Field(..., description="Target store ID") product_id: int = Field(..., description="Product ID in store catalog") location: str = Field(..., description="Storage location") quantity: int = Field(..., description="Exact inventory quantity", ge=0) class AdminInventoryAdjust(BaseModel): """Admin version of inventory adjust - requires explicit store_id.""" store_id: int = Field(..., description="Target store ID") product_id: int = Field(..., description="Product ID in store catalog") location: str = Field(..., description="Storage location") quantity: int = Field( ..., description="Quantity to add (positive) or remove (negative)" ) reason: str | None = Field(None, description="Reason for adjustment") class AdminInventoryItem(BaseModel): """Inventory item with store info for admin list view.""" model_config = ConfigDict(from_attributes=True) id: int product_id: int store_id: int store_name: str | None = None store_code: str | None = None product_title: str | None = None product_sku: str | None = None location: str quantity: int reserved_quantity: int available_quantity: int gtin: str | None = None created_at: datetime updated_at: datetime class AdminInventoryListResponse(BaseModel): """Cross-store inventory list for admin.""" inventories: list[AdminInventoryItem] total: int skip: int limit: int store_filter: int | None = None location_filter: str | None = None class AdminInventoryStats(BaseModel): """Inventory statistics for admin dashboard.""" total_entries: int total_quantity: int total_reserved: int total_available: int low_stock_count: int stores_with_inventory: int unique_locations: int class AdminLowStockItem(BaseModel): """Low stock item for admin alerts.""" id: int product_id: int store_id: int store_name: str | None = None product_title: str | None = None location: str quantity: int reserved_quantity: int available_quantity: int class AdminStoreWithInventory(BaseModel): """Store with inventory entries.""" id: int name: str store_code: str class AdminStoresWithInventoryResponse(BaseModel): """Response for stores with inventory list.""" stores: list[AdminStoreWithInventory] class AdminInventoryLocationsResponse(BaseModel): """Response for unique inventory locations.""" locations: list[str] # ============================================================================ # Inventory Transaction Schemas # ============================================================================ class InventoryTransactionResponse(BaseModel): """Single inventory transaction record.""" model_config = ConfigDict(from_attributes=True) id: int store_id: int product_id: int inventory_id: int | None = None transaction_type: str quantity_change: int quantity_after: int reserved_after: int location: str | None = None warehouse: str | None = None order_id: int | None = None order_number: str | None = None reason: str | None = None created_by: str | None = None created_at: datetime class InventoryTransactionWithProduct(InventoryTransactionResponse): """Transaction with product details for list views.""" product_title: str | None = None product_sku: str | None = None class InventoryTransactionListResponse(BaseModel): """Paginated list of inventory transactions.""" transactions: list[InventoryTransactionWithProduct] total: int skip: int limit: int class ProductTransactionHistoryResponse(BaseModel): """Transaction history for a specific product.""" product_id: int product_title: str | None = None product_sku: str | None = None current_quantity: int current_reserved: int transactions: list[InventoryTransactionResponse] total: int class OrderTransactionHistoryResponse(BaseModel): """Transaction history for a specific order.""" order_id: int order_number: str transactions: list[InventoryTransactionWithProduct] # ============================================================================ # Admin Inventory Transaction Schemas # ============================================================================ class AdminInventoryTransactionItem(InventoryTransactionWithProduct): """Transaction with store details for admin views.""" store_name: str | None = None store_code: str | None = None class AdminInventoryTransactionListResponse(BaseModel): """Paginated list of transactions for admin.""" transactions: list[AdminInventoryTransactionItem] total: int skip: int limit: int class AdminTransactionStatsResponse(BaseModel): """Transaction statistics for admin dashboard.""" total_transactions: int transactions_today: int by_type: dict[str, int]