2025-08-21 20:28:14 +02:00
|
|
|
# ================================================================
|
|
|
|
|
# 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
|
|
|
|
|
|
2025-09-19 11:44:38 +02:00
|
|
|
from app.models.enums import (
|
|
|
|
|
CustomerType, DeliveryMethod, PaymentTerms, PaymentMethod, PaymentStatus,
|
|
|
|
|
CustomerSegment, PriorityLevel, OrderType, OrderStatus, OrderSource,
|
2025-10-30 21:08:07 +01:00
|
|
|
SalesChannel, BusinessModel, DeliveryStatus
|
2025-09-19 11:44:38 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
# ===== Customer Schemas =====
|
|
|
|
|
|
|
|
|
|
class CustomerBase(BaseModel):
|
|
|
|
|
name: str = Field(..., min_length=1, max_length=200)
|
|
|
|
|
business_name: Optional[str] = Field(None, max_length=200)
|
2025-09-19 11:44:38 +02:00
|
|
|
customer_type: CustomerType = Field(default=CustomerType.INDIVIDUAL)
|
2025-08-21 20:28:14 +02:00
|
|
|
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)
|
2025-09-19 11:44:38 +02:00
|
|
|
preferred_delivery_method: DeliveryMethod = Field(default=DeliveryMethod.DELIVERY)
|
|
|
|
|
payment_terms: PaymentTerms = Field(default=PaymentTerms.IMMEDIATE)
|
2025-08-21 20:28:14 +02:00
|
|
|
credit_limit: Optional[Decimal] = Field(None, ge=0)
|
|
|
|
|
discount_percentage: Decimal = Field(default=Decimal("0.00"), ge=0, le=100)
|
2025-09-19 11:44:38 +02:00
|
|
|
customer_segment: CustomerSegment = Field(default=CustomerSegment.REGULAR)
|
|
|
|
|
priority_level: PriorityLevel = Field(default=PriorityLevel.NORMAL)
|
2025-08-21 20:28:14 +02:00
|
|
|
special_instructions: Optional[str] = None
|
|
|
|
|
delivery_preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
product_preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2025-09-19 11:44:38 +02:00
|
|
|
class Config:
|
|
|
|
|
from_attributes = True
|
|
|
|
|
use_enum_values = True
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
class CustomerCreate(CustomerBase):
|
|
|
|
|
customer_code: str = Field(..., min_length=1, max_length=50)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CustomerUpdate(BaseModel):
|
|
|
|
|
name: Optional[str] = Field(None, min_length=1, max_length=200)
|
|
|
|
|
business_name: Optional[str] = Field(None, max_length=200)
|
2025-09-19 11:44:38 +02:00
|
|
|
customer_type: Optional[CustomerType] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
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
|
2025-09-19 11:44:38 +02:00
|
|
|
preferred_delivery_method: Optional[DeliveryMethod] = None
|
|
|
|
|
payment_terms: Optional[PaymentTerms] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
credit_limit: Optional[Decimal] = Field(None, ge=0)
|
|
|
|
|
discount_percentage: Optional[Decimal] = Field(None, ge=0, le=100)
|
2025-09-19 11:44:38 +02:00
|
|
|
customer_segment: Optional[CustomerSegment] = None
|
|
|
|
|
priority_level: Optional[PriorityLevel] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
special_instructions: Optional[str] = None
|
|
|
|
|
delivery_preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
product_preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2025-09-19 11:44:38 +02:00
|
|
|
class Config:
|
|
|
|
|
from_attributes = True
|
|
|
|
|
use_enum_values = True
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
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
|
2025-09-19 11:44:38 +02:00
|
|
|
order_type: OrderType = Field(default=OrderType.STANDARD)
|
|
|
|
|
priority: PriorityLevel = Field(default=PriorityLevel.NORMAL)
|
2025-08-21 20:28:14 +02:00
|
|
|
requested_delivery_date: datetime
|
2025-09-19 11:44:38 +02:00
|
|
|
delivery_method: DeliveryMethod = Field(default=DeliveryMethod.DELIVERY)
|
2025-08-21 20:28:14 +02:00
|
|
|
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)
|
2025-09-19 11:44:38 +02:00
|
|
|
payment_method: Optional[PaymentMethod] = None
|
|
|
|
|
payment_terms: PaymentTerms = Field(default=PaymentTerms.IMMEDIATE)
|
2025-08-21 20:28:14 +02:00
|
|
|
special_instructions: Optional[str] = None
|
|
|
|
|
custom_requirements: Optional[Dict[str, Any]] = None
|
|
|
|
|
allergen_warnings: Optional[Dict[str, Any]] = None
|
2025-09-19 11:44:38 +02:00
|
|
|
order_source: OrderSource = Field(default=OrderSource.MANUAL)
|
|
|
|
|
sales_channel: SalesChannel = Field(default=SalesChannel.DIRECT)
|
2025-08-21 20:28:14 +02:00
|
|
|
order_origin: Optional[str] = Field(None, max_length=100)
|
|
|
|
|
communication_preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2025-09-19 11:44:38 +02:00
|
|
|
class Config:
|
|
|
|
|
from_attributes = True
|
|
|
|
|
use_enum_values = True
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
class OrderCreate(OrderBase):
|
|
|
|
|
tenant_id: UUID
|
|
|
|
|
items: List[OrderItemCreate] = Field(..., min_items=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OrderUpdate(BaseModel):
|
2025-09-19 11:44:38 +02:00
|
|
|
status: Optional[OrderStatus] = None
|
|
|
|
|
priority: Optional[PriorityLevel] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
requested_delivery_date: Optional[datetime] = None
|
|
|
|
|
confirmed_delivery_date: Optional[datetime] = None
|
2025-09-19 11:44:38 +02:00
|
|
|
delivery_method: Optional[DeliveryMethod] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
delivery_address: Optional[Dict[str, Any]] = None
|
|
|
|
|
delivery_instructions: Optional[str] = None
|
|
|
|
|
delivery_window_start: Optional[datetime] = None
|
|
|
|
|
delivery_window_end: Optional[datetime] = None
|
2025-09-19 11:44:38 +02:00
|
|
|
payment_method: Optional[PaymentMethod] = None
|
|
|
|
|
payment_status: Optional[PaymentStatus] = None
|
2025-08-21 20:28:14 +02:00
|
|
|
special_instructions: Optional[str] = None
|
|
|
|
|
custom_requirements: Optional[Dict[str, Any]] = None
|
|
|
|
|
allergen_warnings: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2025-09-19 11:44:38 +02:00
|
|
|
class Config:
|
|
|
|
|
from_attributes = True
|
|
|
|
|
use_enum_values = True
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ===== 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
|