Files
orion/models/schema/order.py
Samir Boulahtit 6c6b5d259d feat: add admin orders management
- Add admin API endpoints for order management
- Add orders page with vendor selector and filtering
- Add order schemas for admin operations
- Support order status tracking and management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 21:05:27 +01:00

258 lines
6.3 KiB
Python

# models/schema/order.py
"""
Pydantic schema for order operations.
"""
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
# ============================================================================
# Order Item Schemas
# ============================================================================
class OrderItemCreate(BaseModel):
"""Schema for creating an order item."""
product_id: int
quantity: int = Field(..., ge=1)
class OrderItemResponse(BaseModel):
"""Schema for order item response."""
model_config = ConfigDict(from_attributes=True)
id: int
order_id: int
product_id: int
product_name: str
product_sku: str | None
quantity: int
unit_price: float
total_price: float
inventory_reserved: bool
inventory_fulfilled: bool
created_at: datetime
updated_at: datetime
# ============================================================================
# Order Address Schemas
# ============================================================================
class OrderAddressCreate(BaseModel):
"""Schema for order address (shipping/billing)."""
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
company: str | None = Field(None, max_length=200)
address_line_1: str = Field(..., min_length=1, max_length=255)
address_line_2: str | None = Field(None, max_length=255)
city: str = Field(..., min_length=1, max_length=100)
postal_code: str = Field(..., min_length=1, max_length=20)
country: str = Field(..., min_length=2, max_length=100)
class OrderAddressResponse(BaseModel):
"""Schema for order address response."""
model_config = ConfigDict(from_attributes=True)
id: int
address_type: str
first_name: str
last_name: str
company: str | None
address_line_1: str
address_line_2: str | None
city: str
postal_code: str
country: str
# ============================================================================
# Order Create/Update Schemas
# ============================================================================
class OrderCreate(BaseModel):
"""Schema for creating an order."""
customer_id: int | None = None # Optional for guest checkout
items: list[OrderItemCreate] = Field(..., min_length=1)
# Addresses
shipping_address: OrderAddressCreate
billing_address: OrderAddressCreate | None = None # Use shipping if not provided
# Optional fields
shipping_method: str | None = None
customer_notes: str | None = Field(None, max_length=1000)
# Cart/session info
session_id: str | None = None
class OrderUpdate(BaseModel):
"""Schema for updating order status."""
status: str | None = Field(
None, pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
)
tracking_number: str | None = None
internal_notes: str | None = None
# ============================================================================
# Order Response Schemas
# ============================================================================
class OrderResponse(BaseModel):
"""Schema for order response."""
model_config = ConfigDict(from_attributes=True)
id: int
vendor_id: int
customer_id: int
order_number: str
status: str
# Financial
subtotal: float
tax_amount: float
shipping_amount: float
discount_amount: float
total_amount: float
currency: str
# Shipping
shipping_method: str | None
tracking_number: str | None
# Notes
customer_notes: str | None
internal_notes: str | None
# Timestamps
created_at: datetime
updated_at: datetime
paid_at: datetime | None
shipped_at: datetime | None
delivered_at: datetime | None
cancelled_at: datetime | None
class OrderDetailResponse(OrderResponse):
"""Schema for detailed order response with items and addresses."""
items: list[OrderItemResponse]
shipping_address: OrderAddressResponse
billing_address: OrderAddressResponse
class OrderListResponse(BaseModel):
"""Schema for paginated order list."""
orders: list[OrderResponse]
total: int
skip: int
limit: int
# ============================================================================
# Admin Order Schemas
# ============================================================================
class AdminOrderItem(BaseModel):
"""Order item with vendor info for admin list view."""
model_config = ConfigDict(from_attributes=True)
id: int
vendor_id: int
vendor_name: str | None = None
vendor_code: str | None = None
customer_id: int
customer_name: str | None = None
customer_email: str | None = None
order_number: str
channel: str
status: str
# Financial
subtotal: float
tax_amount: float
shipping_amount: float
discount_amount: float
total_amount: float
currency: str
# Shipping
shipping_method: str | None
tracking_number: str | None
# Item count
item_count: int = 0
# Timestamps
created_at: datetime
updated_at: datetime
paid_at: datetime | None
shipped_at: datetime | None
delivered_at: datetime | None
cancelled_at: datetime | None
class AdminOrderListResponse(BaseModel):
"""Cross-vendor order list for admin."""
orders: list[AdminOrderItem]
total: int
skip: int
limit: int
class AdminOrderStats(BaseModel):
"""Order statistics for admin dashboard."""
total_orders: int
pending_orders: int
processing_orders: int
shipped_orders: int
delivered_orders: int
cancelled_orders: int
refunded_orders: int
total_revenue: float
vendors_with_orders: int
class AdminOrderStatusUpdate(BaseModel):
"""Admin version of status update with reason."""
status: str = Field(
..., pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
)
tracking_number: str | None = None
reason: str | None = Field(None, description="Reason for status change")
class AdminVendorWithOrders(BaseModel):
"""Vendor with order count."""
id: int
name: str
vendor_code: str
order_count: int = 0
class AdminVendorsWithOrdersResponse(BaseModel):
"""Response for vendors with orders list."""
vendors: list[AdminVendorWithOrders]