Files
bakery-ia/services/orders/app/schemas/order_schemas.py
2025-08-21 20:28:14 +02:00

367 lines
13 KiB
Python

# ================================================================
# services/orders/app/schemas/order_schemas.py
# ================================================================
"""
Order-related Pydantic schemas for Orders Service
"""
from datetime import datetime, date
from decimal import Decimal
from typing import Optional, List, Dict, Any
from uuid import UUID
from pydantic import BaseModel, Field, validator
# ===== Customer Schemas =====
class CustomerBase(BaseModel):
name: str = Field(..., min_length=1, max_length=200)
business_name: Optional[str] = Field(None, max_length=200)
customer_type: str = Field(default="individual", pattern="^(individual|business|central_bakery)$")
email: Optional[str] = Field(None, max_length=255)
phone: Optional[str] = Field(None, max_length=50)
address_line1: Optional[str] = Field(None, max_length=255)
address_line2: Optional[str] = Field(None, max_length=255)
city: Optional[str] = Field(None, max_length=100)
state: Optional[str] = Field(None, max_length=100)
postal_code: Optional[str] = Field(None, max_length=20)
country: str = Field(default="US", max_length=100)
is_active: bool = Field(default=True)
preferred_delivery_method: str = Field(default="delivery", pattern="^(delivery|pickup)$")
payment_terms: str = Field(default="immediate", pattern="^(immediate|net_30|net_60)$")
credit_limit: Optional[Decimal] = Field(None, ge=0)
discount_percentage: Decimal = Field(default=Decimal("0.00"), ge=0, le=100)
customer_segment: str = Field(default="regular", pattern="^(vip|regular|wholesale)$")
priority_level: str = Field(default="normal", pattern="^(high|normal|low)$")
special_instructions: Optional[str] = None
delivery_preferences: Optional[Dict[str, Any]] = None
product_preferences: Optional[Dict[str, Any]] = None
class CustomerCreate(CustomerBase):
customer_code: str = Field(..., min_length=1, max_length=50)
tenant_id: UUID
class CustomerUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=200)
business_name: Optional[str] = Field(None, max_length=200)
customer_type: Optional[str] = Field(None, pattern="^(individual|business|central_bakery)$")
email: Optional[str] = Field(None, max_length=255)
phone: Optional[str] = Field(None, max_length=50)
address_line1: Optional[str] = Field(None, max_length=255)
address_line2: Optional[str] = Field(None, max_length=255)
city: Optional[str] = Field(None, max_length=100)
state: Optional[str] = Field(None, max_length=100)
postal_code: Optional[str] = Field(None, max_length=20)
country: Optional[str] = Field(None, max_length=100)
is_active: Optional[bool] = None
preferred_delivery_method: Optional[str] = Field(None, pattern="^(delivery|pickup)$")
payment_terms: Optional[str] = Field(None, pattern="^(immediate|net_30|net_60)$")
credit_limit: Optional[Decimal] = Field(None, ge=0)
discount_percentage: Optional[Decimal] = Field(None, ge=0, le=100)
customer_segment: Optional[str] = Field(None, pattern="^(vip|regular|wholesale)$")
priority_level: Optional[str] = Field(None, pattern="^(high|normal|low)$")
special_instructions: Optional[str] = None
delivery_preferences: Optional[Dict[str, Any]] = None
product_preferences: Optional[Dict[str, Any]] = None
class CustomerResponse(CustomerBase):
id: UUID
tenant_id: UUID
customer_code: str
total_orders: int
total_spent: Decimal
average_order_value: Decimal
last_order_date: Optional[datetime]
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# ===== Order Item Schemas =====
class OrderItemBase(BaseModel):
product_id: UUID
product_name: str = Field(..., min_length=1, max_length=200)
product_sku: Optional[str] = Field(None, max_length=100)
product_category: Optional[str] = Field(None, max_length=100)
quantity: Decimal = Field(..., gt=0)
unit_of_measure: str = Field(default="each", max_length=50)
weight: Optional[Decimal] = Field(None, ge=0)
unit_price: Decimal = Field(..., ge=0)
line_discount: Decimal = Field(default=Decimal("0.00"), ge=0)
product_specifications: Optional[Dict[str, Any]] = None
customization_details: Optional[str] = None
special_instructions: Optional[str] = None
recipe_id: Optional[UUID] = None
class OrderItemCreate(OrderItemBase):
pass
class OrderItemUpdate(BaseModel):
quantity: Optional[Decimal] = Field(None, gt=0)
unit_price: Optional[Decimal] = Field(None, ge=0)
line_discount: Optional[Decimal] = Field(None, ge=0)
product_specifications: Optional[Dict[str, Any]] = None
customization_details: Optional[str] = None
special_instructions: Optional[str] = None
class OrderItemResponse(OrderItemBase):
id: UUID
order_id: UUID
line_total: Decimal
status: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# ===== Order Schemas =====
class OrderBase(BaseModel):
customer_id: UUID
order_type: str = Field(default="standard", pattern="^(standard|rush|recurring|special)$")
priority: str = Field(default="normal", pattern="^(high|normal|low)$")
requested_delivery_date: datetime
delivery_method: str = Field(default="delivery", pattern="^(delivery|pickup)$")
delivery_address: Optional[Dict[str, Any]] = None
delivery_instructions: Optional[str] = None
delivery_window_start: Optional[datetime] = None
delivery_window_end: Optional[datetime] = None
discount_percentage: Decimal = Field(default=Decimal("0.00"), ge=0, le=100)
delivery_fee: Decimal = Field(default=Decimal("0.00"), ge=0)
payment_method: Optional[str] = Field(None, pattern="^(cash|card|bank_transfer|account)$")
payment_terms: str = Field(default="immediate", pattern="^(immediate|net_30|net_60)$")
special_instructions: Optional[str] = None
custom_requirements: Optional[Dict[str, Any]] = None
allergen_warnings: Optional[Dict[str, Any]] = None
order_source: str = Field(default="manual", pattern="^(manual|online|phone|app|api)$")
sales_channel: str = Field(default="direct", pattern="^(direct|wholesale|retail)$")
order_origin: Optional[str] = Field(None, max_length=100)
communication_preferences: Optional[Dict[str, Any]] = None
class OrderCreate(OrderBase):
tenant_id: UUID
items: List[OrderItemCreate] = Field(..., min_items=1)
class OrderUpdate(BaseModel):
status: Optional[str] = Field(None, pattern="^(pending|confirmed|in_production|ready|out_for_delivery|delivered|cancelled|failed)$")
priority: Optional[str] = Field(None, pattern="^(high|normal|low)$")
requested_delivery_date: Optional[datetime] = None
confirmed_delivery_date: Optional[datetime] = None
delivery_method: Optional[str] = Field(None, pattern="^(delivery|pickup)$")
delivery_address: Optional[Dict[str, Any]] = None
delivery_instructions: Optional[str] = None
delivery_window_start: Optional[datetime] = None
delivery_window_end: Optional[datetime] = None
payment_method: Optional[str] = Field(None, pattern="^(cash|card|bank_transfer|account)$")
payment_status: Optional[str] = Field(None, pattern="^(pending|partial|paid|failed|refunded)$")
special_instructions: Optional[str] = None
custom_requirements: Optional[Dict[str, Any]] = None
allergen_warnings: Optional[Dict[str, Any]] = None
class OrderResponse(OrderBase):
id: UUID
tenant_id: UUID
order_number: str
status: str
order_date: datetime
confirmed_delivery_date: Optional[datetime]
actual_delivery_date: Optional[datetime]
subtotal: Decimal
discount_amount: Decimal
tax_amount: Decimal
total_amount: Decimal
payment_status: str
business_model: Optional[str]
estimated_business_model: Optional[str]
production_batch_id: Optional[UUID]
quality_score: Optional[Decimal]
customer_rating: Optional[int]
created_at: datetime
updated_at: datetime
items: List[OrderItemResponse] = []
class Config:
from_attributes = True
# ===== Procurement Schemas =====
class ProcurementRequirementBase(BaseModel):
product_id: UUID
product_name: str = Field(..., min_length=1, max_length=200)
product_sku: Optional[str] = Field(None, max_length=100)
product_category: Optional[str] = Field(None, max_length=100)
product_type: str = Field(default="ingredient", pattern="^(ingredient|packaging|supplies)$")
required_quantity: Decimal = Field(..., gt=0)
unit_of_measure: str = Field(..., min_length=1, max_length=50)
safety_stock_quantity: Decimal = Field(default=Decimal("0.000"), ge=0)
required_by_date: date
priority: str = Field(default="normal", pattern="^(critical|high|normal|low)$")
preferred_supplier_id: Optional[UUID] = None
quality_specifications: Optional[Dict[str, Any]] = None
special_requirements: Optional[str] = None
storage_requirements: Optional[str] = Field(None, max_length=200)
class ProcurementRequirementCreate(ProcurementRequirementBase):
pass
class ProcurementRequirementResponse(ProcurementRequirementBase):
id: UUID
plan_id: UUID
requirement_number: str
total_quantity_needed: Decimal
current_stock_level: Decimal
available_stock: Decimal
net_requirement: Decimal
order_demand: Decimal
production_demand: Decimal
forecast_demand: Decimal
status: str
estimated_unit_cost: Optional[Decimal]
estimated_total_cost: Optional[Decimal]
supplier_name: Optional[str]
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class ProcurementPlanBase(BaseModel):
plan_date: date
plan_period_start: date
plan_period_end: date
planning_horizon_days: int = Field(default=14, ge=1, le=365)
plan_type: str = Field(default="regular", pattern="^(regular|emergency|seasonal)$")
priority: str = Field(default="normal", pattern="^(high|normal|low)$")
business_model: Optional[str] = Field(None, pattern="^(individual_bakery|central_bakery)$")
procurement_strategy: str = Field(default="just_in_time", pattern="^(just_in_time|bulk|mixed)$")
safety_stock_buffer: Decimal = Field(default=Decimal("20.00"), ge=0, le=100)
special_requirements: Optional[str] = None
class ProcurementPlanCreate(ProcurementPlanBase):
tenant_id: UUID
requirements: List[ProcurementRequirementCreate] = Field(..., min_items=1)
class ProcurementPlanResponse(ProcurementPlanBase):
id: UUID
tenant_id: UUID
plan_number: str
status: str
total_requirements: int
total_estimated_cost: Decimal
total_approved_cost: Decimal
total_demand_orders: int
supply_risk_level: str
approved_at: Optional[datetime]
created_at: datetime
updated_at: datetime
requirements: List[ProcurementRequirementResponse] = []
class Config:
from_attributes = True
# ===== Dashboard and Analytics Schemas =====
class OrdersDashboardSummary(BaseModel):
"""Summary data for orders dashboard"""
# Current period metrics
total_orders_today: int
total_orders_this_week: int
total_orders_this_month: int
# Revenue metrics
revenue_today: Decimal
revenue_this_week: Decimal
revenue_this_month: Decimal
# Order status breakdown
pending_orders: int
confirmed_orders: int
in_production_orders: int
ready_orders: int
delivered_orders: int
# Customer metrics
total_customers: int
new_customers_this_month: int
repeat_customers_rate: Decimal
# Performance metrics
average_order_value: Decimal
order_fulfillment_rate: Decimal
on_time_delivery_rate: Decimal
# Business model detection
business_model: Optional[str]
business_model_confidence: Optional[Decimal]
# Recent activity
recent_orders: List[OrderResponse]
high_priority_orders: List[OrderResponse]
class DemandRequirements(BaseModel):
"""Demand requirements for production planning"""
date: date
tenant_id: UUID
# Product demand breakdown
product_demands: List[Dict[str, Any]]
# Aggregate metrics
total_orders: int
total_quantity: Decimal
total_value: Decimal
# Business context
business_model: Optional[str]
rush_orders_count: int
special_requirements: List[str]
# Timing requirements
earliest_delivery: datetime
latest_delivery: datetime
average_lead_time_hours: int
class ProcurementPlanningData(BaseModel):
"""Data for procurement planning decisions"""
planning_date: date
planning_horizon_days: int
# Demand forecast
demand_forecast: List[Dict[str, Any]]
# Current inventory status
inventory_levels: Dict[str, Any]
# Supplier information
supplier_performance: Dict[str, Any]
# Risk factors
supply_risks: List[str]
demand_volatility: Decimal
# Recommendations
recommended_purchases: List[Dict[str, Any]]
critical_shortages: List[Dict[str, Any]]