# models/schema/order_item_exception.py """ Pydantic schemas for order item exception management. Handles unmatched products during marketplace order imports. """ from datetime import datetime from pydantic import BaseModel, ConfigDict, Field # ============================================================================ # Exception Response Schemas # ============================================================================ class OrderItemExceptionResponse(BaseModel): """Schema for order item exception response.""" model_config = ConfigDict(from_attributes=True) id: int order_item_id: int vendor_id: int vendor_name: str | None = None # For cross-vendor views # Original data from marketplace original_gtin: str | None original_product_name: str | None original_sku: str | None # Exception classification exception_type: str # product_not_found, gtin_mismatch, duplicate_gtin # Resolution status status: str # pending, resolved, ignored # Resolution details resolved_product_id: int | None resolved_at: datetime | None resolved_by: int | None resolution_notes: str | None # Timestamps created_at: datetime updated_at: datetime # Nested order info (populated by service) order_number: str | None = None order_id: int | None = None order_date: datetime | None = None order_status: str | None = None @property def is_pending(self) -> bool: """Check if exception is pending resolution.""" return self.status == "pending" @property def is_resolved(self) -> bool: """Check if exception has been resolved.""" return self.status == "resolved" @property def is_ignored(self) -> bool: """Check if exception has been ignored.""" return self.status == "ignored" class OrderItemExceptionBriefResponse(BaseModel): """Brief exception info for embedding in order item responses.""" model_config = ConfigDict(from_attributes=True) id: int original_gtin: str | None original_product_name: str | None exception_type: str status: str resolved_product_id: int | None # ============================================================================ # List/Stats Response Schemas # ============================================================================ class OrderItemExceptionListResponse(BaseModel): """Paginated list of exceptions.""" exceptions: list[OrderItemExceptionResponse] total: int skip: int limit: int class OrderItemExceptionStats(BaseModel): """Exception statistics for a vendor.""" pending: int = 0 resolved: int = 0 ignored: int = 0 total: int = 0 # Additional breakdown orders_with_exceptions: int = 0 # ============================================================================ # Request Schemas # ============================================================================ class ResolveExceptionRequest(BaseModel): """Request to resolve an exception by assigning a product.""" product_id: int = Field(..., description="Product ID to assign to this order item") notes: str | None = Field( None, max_length=1000, description="Optional notes about the resolution" ) class IgnoreExceptionRequest(BaseModel): """Request to ignore an exception (still blocks confirmation).""" notes: str = Field( ..., min_length=1, max_length=1000, description="Reason for ignoring (required)" ) class BulkResolveRequest(BaseModel): """Request to bulk resolve all pending exceptions for a GTIN.""" gtin: str = Field( ..., min_length=1, max_length=50, description="GTIN to match pending exceptions" ) product_id: int = Field(..., description="Product ID to assign") notes: str | None = Field( None, max_length=1000, description="Optional notes about the resolution" ) class BulkResolveResponse(BaseModel): """Response from bulk resolve operation.""" resolved_count: int gtin: str product_id: int # ============================================================================ # Auto-Match Response Schemas # ============================================================================ class AutoMatchResult(BaseModel): """Result of auto-matching after product import.""" gtin: str product_id: int resolved_count: int resolved_exception_ids: list[int]