- 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>
258 lines
6.3 KiB
Python
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]
|