feat: update Pydantic schemas for unified order model
- Add AddressSnapshot and CustomerSnapshot schemas - Update OrderItemResponse with gtin fields and item_state - Update OrderResponse with all snapshot fields - Add OrderListItem for simplified list views - Add Letzshop-specific schemas (LetzshopOrderImport, LetzshopShippingInfo) - Update AdminOrderItem with new fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,9 @@ class LetzshopOrderBase(BaseModel):
|
|||||||
letzshop_state: str | None = None
|
letzshop_state: str | None = None
|
||||||
customer_email: str | None = None
|
customer_email: str | None = None
|
||||||
customer_name: str | None = None
|
customer_name: str | None = None
|
||||||
|
customer_locale: str | None = None
|
||||||
|
shipping_country_iso: str | None = None
|
||||||
|
billing_country_iso: str | None = None
|
||||||
total_amount: str | None = None
|
total_amount: str | None = None
|
||||||
currency: str = "EUR"
|
currency: str = "EUR"
|
||||||
|
|
||||||
@@ -120,6 +123,7 @@ class LetzshopOrderResponse(LetzshopOrderBase):
|
|||||||
tracking_number: str | None
|
tracking_number: str | None
|
||||||
tracking_carrier: str | None
|
tracking_carrier: str | None
|
||||||
inventory_units: list[dict[str, Any]] | None
|
inventory_units: list[dict[str, Any]] | None
|
||||||
|
order_date: datetime | None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
|
|
||||||
@@ -368,3 +372,55 @@ class LetzshopJobsListResponse(BaseModel):
|
|||||||
|
|
||||||
jobs: list[LetzshopJobItem]
|
jobs: list[LetzshopJobItem]
|
||||||
total: int
|
total: int
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Historical Import Job Schemas
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopHistoricalImportJobResponse(BaseModel):
|
||||||
|
"""Schema for historical import job status (polling endpoint)."""
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
id: int
|
||||||
|
vendor_id: int
|
||||||
|
status: str # pending, fetching, processing, completed, failed
|
||||||
|
current_phase: str | None = None # "confirmed" or "declined"
|
||||||
|
|
||||||
|
# Fetch progress
|
||||||
|
current_page: int = 0
|
||||||
|
total_pages: int | None = None
|
||||||
|
shipments_fetched: int = 0
|
||||||
|
|
||||||
|
# Processing progress
|
||||||
|
orders_processed: int = 0
|
||||||
|
orders_imported: int = 0
|
||||||
|
orders_updated: int = 0
|
||||||
|
orders_skipped: int = 0
|
||||||
|
|
||||||
|
# EAN matching stats
|
||||||
|
products_matched: int = 0
|
||||||
|
products_not_found: int = 0
|
||||||
|
|
||||||
|
# Phase-specific stats (when complete)
|
||||||
|
confirmed_stats: dict[str, Any] | None = None
|
||||||
|
declined_stats: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
# Error handling
|
||||||
|
error_message: str | None = None
|
||||||
|
|
||||||
|
# Timing
|
||||||
|
started_at: datetime | None = None
|
||||||
|
completed_at: datetime | None = None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopHistoricalImportStartResponse(BaseModel):
|
||||||
|
"""Schema for starting a historical import job."""
|
||||||
|
|
||||||
|
job_id: int
|
||||||
|
status: str = "pending"
|
||||||
|
message: str = "Historical import job started"
|
||||||
|
|||||||
@@ -1,12 +1,50 @@
|
|||||||
# models/schema/order.py
|
# models/schema/order.py
|
||||||
"""
|
"""
|
||||||
Pydantic schema for order operations.
|
Pydantic schemas for unified order operations.
|
||||||
|
|
||||||
|
Supports both direct orders and marketplace orders (Letzshop, etc.)
|
||||||
|
with snapshotted customer and address data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Address Snapshot Schemas
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
class AddressSnapshot(BaseModel):
|
||||||
|
"""Address snapshot for order creation."""
|
||||||
|
|
||||||
|
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_iso: str = Field(..., min_length=2, max_length=5)
|
||||||
|
|
||||||
|
|
||||||
|
class AddressSnapshotResponse(BaseModel):
|
||||||
|
"""Address snapshot in order response."""
|
||||||
|
|
||||||
|
first_name: str
|
||||||
|
last_name: str
|
||||||
|
company: str | None
|
||||||
|
address_line_1: str
|
||||||
|
address_line_2: str | None
|
||||||
|
city: str
|
||||||
|
postal_code: str
|
||||||
|
country_iso: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_name(self) -> str:
|
||||||
|
return f"{self.first_name} {self.last_name}".strip()
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Order Item Schemas
|
# Order Item Schemas
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -29,48 +67,69 @@ class OrderItemResponse(BaseModel):
|
|||||||
product_id: int
|
product_id: int
|
||||||
product_name: str
|
product_name: str
|
||||||
product_sku: str | None
|
product_sku: str | None
|
||||||
|
gtin: str | None
|
||||||
|
gtin_type: str | None
|
||||||
quantity: int
|
quantity: int
|
||||||
unit_price: float
|
unit_price: float
|
||||||
total_price: float
|
total_price: float
|
||||||
|
|
||||||
|
# External references (for marketplace items)
|
||||||
|
external_item_id: str | None = None
|
||||||
|
external_variant_id: str | None = None
|
||||||
|
|
||||||
|
# Item state (for marketplace confirmation flow)
|
||||||
|
item_state: str | None = None
|
||||||
|
|
||||||
|
# Inventory tracking
|
||||||
inventory_reserved: bool
|
inventory_reserved: bool
|
||||||
inventory_fulfilled: bool
|
inventory_fulfilled: bool
|
||||||
|
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_confirmed(self) -> bool:
|
||||||
|
"""Check if item has been confirmed (available or unavailable)."""
|
||||||
|
return self.item_state in ("confirmed_available", "confirmed_unavailable")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_available(self) -> bool:
|
||||||
|
"""Check if item is confirmed as available."""
|
||||||
|
return self.item_state == "confirmed_available"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_declined(self) -> bool:
|
||||||
|
"""Check if item was declined (unavailable)."""
|
||||||
|
return self.item_state == "confirmed_unavailable"
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Order Address Schemas
|
# Customer Snapshot Schemas
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
class OrderAddressCreate(BaseModel):
|
class CustomerSnapshot(BaseModel):
|
||||||
"""Schema for order address (shipping/billing)."""
|
"""Customer snapshot for order creation."""
|
||||||
|
|
||||||
first_name: str = Field(..., min_length=1, max_length=100)
|
first_name: str = Field(..., min_length=1, max_length=100)
|
||||||
last_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)
|
email: str = Field(..., max_length=255)
|
||||||
address_line_1: str = Field(..., min_length=1, max_length=255)
|
phone: str | None = Field(None, max_length=50)
|
||||||
address_line_2: str | None = Field(None, max_length=255)
|
locale: str | None = Field(None, max_length=10)
|
||||||
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):
|
class CustomerSnapshotResponse(BaseModel):
|
||||||
"""Schema for order address response."""
|
"""Customer snapshot in order response."""
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
|
||||||
|
|
||||||
id: int
|
|
||||||
address_type: str
|
|
||||||
first_name: str
|
first_name: str
|
||||||
last_name: str
|
last_name: str
|
||||||
company: str | None
|
email: str
|
||||||
address_line_1: str
|
phone: str | None
|
||||||
address_line_2: str | None
|
locale: str | None
|
||||||
city: str
|
|
||||||
postal_code: str
|
@property
|
||||||
country: str
|
def full_name(self) -> str:
|
||||||
|
return f"{self.first_name} {self.last_name}".strip()
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -79,14 +138,17 @@ class OrderAddressResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class OrderCreate(BaseModel):
|
class OrderCreate(BaseModel):
|
||||||
"""Schema for creating an order."""
|
"""Schema for creating an order (direct channel)."""
|
||||||
|
|
||||||
customer_id: int | None = None # Optional for guest checkout
|
customer_id: int | None = None # Optional for guest checkout
|
||||||
items: list[OrderItemCreate] = Field(..., min_length=1)
|
items: list[OrderItemCreate] = Field(..., min_length=1)
|
||||||
|
|
||||||
# Addresses
|
# Customer info snapshot
|
||||||
shipping_address: OrderAddressCreate
|
customer: CustomerSnapshot
|
||||||
billing_address: OrderAddressCreate | None = None # Use shipping if not provided
|
|
||||||
|
# Addresses (snapshots)
|
||||||
|
shipping_address: AddressSnapshot
|
||||||
|
billing_address: AddressSnapshot | None = None # Use shipping if not provided
|
||||||
|
|
||||||
# Optional fields
|
# Optional fields
|
||||||
shipping_method: str | None = None
|
shipping_method: str | None = None
|
||||||
@@ -103,9 +165,24 @@ class OrderUpdate(BaseModel):
|
|||||||
None, pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
|
None, pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
|
||||||
)
|
)
|
||||||
tracking_number: str | None = None
|
tracking_number: str | None = None
|
||||||
|
tracking_provider: str | None = None
|
||||||
internal_notes: str | None = None
|
internal_notes: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class OrderTrackingUpdate(BaseModel):
|
||||||
|
"""Schema for setting tracking information."""
|
||||||
|
|
||||||
|
tracking_number: str = Field(..., min_length=1, max_length=100)
|
||||||
|
tracking_provider: str = Field(..., min_length=1, max_length=100)
|
||||||
|
|
||||||
|
|
||||||
|
class OrderItemStateUpdate(BaseModel):
|
||||||
|
"""Schema for updating item state (marketplace confirmation)."""
|
||||||
|
|
||||||
|
item_id: int
|
||||||
|
state: str = Field(..., pattern="^(confirmed_available|confirmed_unavailable)$")
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Order Response Schemas
|
# Order Response Schemas
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -120,39 +197,86 @@ class OrderResponse(BaseModel):
|
|||||||
vendor_id: int
|
vendor_id: int
|
||||||
customer_id: int
|
customer_id: int
|
||||||
order_number: str
|
order_number: str
|
||||||
|
|
||||||
|
# Channel/Source
|
||||||
|
channel: str
|
||||||
|
external_order_id: str | None = None
|
||||||
|
external_shipment_id: str | None = None
|
||||||
|
external_order_number: str | None = None
|
||||||
|
|
||||||
|
# Status
|
||||||
status: str
|
status: str
|
||||||
|
|
||||||
# Financial
|
# Financial
|
||||||
subtotal: float
|
subtotal: float | None
|
||||||
tax_amount: float
|
tax_amount: float | None
|
||||||
shipping_amount: float
|
shipping_amount: float | None
|
||||||
discount_amount: float
|
discount_amount: float | None
|
||||||
total_amount: float
|
total_amount: float
|
||||||
currency: str
|
currency: str
|
||||||
|
|
||||||
# Shipping
|
# Customer snapshot
|
||||||
|
customer_first_name: str
|
||||||
|
customer_last_name: str
|
||||||
|
customer_email: str
|
||||||
|
customer_phone: str | None
|
||||||
|
customer_locale: str | None
|
||||||
|
|
||||||
|
# Shipping address snapshot
|
||||||
|
ship_first_name: str
|
||||||
|
ship_last_name: str
|
||||||
|
ship_company: str | None
|
||||||
|
ship_address_line_1: str
|
||||||
|
ship_address_line_2: str | None
|
||||||
|
ship_city: str
|
||||||
|
ship_postal_code: str
|
||||||
|
ship_country_iso: str
|
||||||
|
|
||||||
|
# Billing address snapshot
|
||||||
|
bill_first_name: str
|
||||||
|
bill_last_name: str
|
||||||
|
bill_company: str | None
|
||||||
|
bill_address_line_1: str
|
||||||
|
bill_address_line_2: str | None
|
||||||
|
bill_city: str
|
||||||
|
bill_postal_code: str
|
||||||
|
bill_country_iso: str
|
||||||
|
|
||||||
|
# Tracking
|
||||||
shipping_method: str | None
|
shipping_method: str | None
|
||||||
tracking_number: str | None
|
tracking_number: str | None
|
||||||
|
tracking_provider: str | None
|
||||||
|
|
||||||
# Notes
|
# Notes
|
||||||
customer_notes: str | None
|
customer_notes: str | None
|
||||||
internal_notes: str | None
|
internal_notes: str | None
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
created_at: datetime
|
order_date: datetime
|
||||||
updated_at: datetime
|
confirmed_at: datetime | None
|
||||||
paid_at: datetime | None
|
|
||||||
shipped_at: datetime | None
|
shipped_at: datetime | None
|
||||||
delivered_at: datetime | None
|
delivered_at: datetime | None
|
||||||
cancelled_at: datetime | None
|
cancelled_at: datetime | None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
@property
|
||||||
|
def customer_full_name(self) -> str:
|
||||||
|
return f"{self.customer_first_name} {self.customer_last_name}".strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ship_full_name(self) -> str:
|
||||||
|
return f"{self.ship_first_name} {self.ship_last_name}".strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_marketplace_order(self) -> bool:
|
||||||
|
return self.channel != "direct"
|
||||||
|
|
||||||
|
|
||||||
class OrderDetailResponse(OrderResponse):
|
class OrderDetailResponse(OrderResponse):
|
||||||
"""Schema for detailed order response with items and addresses."""
|
"""Schema for detailed order response with items."""
|
||||||
|
|
||||||
items: list[OrderItemResponse]
|
items: list[OrderItemResponse] = []
|
||||||
shipping_address: OrderAddressResponse
|
|
||||||
billing_address: OrderAddressResponse
|
|
||||||
|
|
||||||
|
|
||||||
class OrderListResponse(BaseModel):
|
class OrderListResponse(BaseModel):
|
||||||
@@ -164,6 +288,49 @@ class OrderListResponse(BaseModel):
|
|||||||
limit: int
|
limit: int
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Order List Item (Simplified for list views)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
class OrderListItem(BaseModel):
|
||||||
|
"""Simplified order item for list views."""
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
id: int
|
||||||
|
vendor_id: int
|
||||||
|
order_number: str
|
||||||
|
channel: str
|
||||||
|
status: str
|
||||||
|
|
||||||
|
# External references
|
||||||
|
external_order_number: str | None = None
|
||||||
|
|
||||||
|
# Customer
|
||||||
|
customer_full_name: str
|
||||||
|
customer_email: str
|
||||||
|
|
||||||
|
# Financial
|
||||||
|
total_amount: float
|
||||||
|
currency: str
|
||||||
|
|
||||||
|
# Shipping
|
||||||
|
ship_country_iso: str
|
||||||
|
|
||||||
|
# Tracking
|
||||||
|
tracking_number: str | None
|
||||||
|
tracking_provider: str | None
|
||||||
|
|
||||||
|
# Item count
|
||||||
|
item_count: int = 0
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
order_date: datetime
|
||||||
|
confirmed_at: datetime | None
|
||||||
|
shipped_at: datetime | None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Admin Order Schemas
|
# Admin Order Schemas
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -179,34 +346,42 @@ class AdminOrderItem(BaseModel):
|
|||||||
vendor_name: str | None = None
|
vendor_name: str | None = None
|
||||||
vendor_code: str | None = None
|
vendor_code: str | None = None
|
||||||
customer_id: int
|
customer_id: int
|
||||||
customer_name: str | None = None
|
|
||||||
customer_email: str | None = None
|
|
||||||
order_number: str
|
order_number: str
|
||||||
channel: str
|
channel: str
|
||||||
status: str
|
status: str
|
||||||
|
|
||||||
|
# External references
|
||||||
|
external_order_number: str | None = None
|
||||||
|
external_shipment_id: str | None = None
|
||||||
|
|
||||||
|
# Customer snapshot
|
||||||
|
customer_full_name: str
|
||||||
|
customer_email: str
|
||||||
|
|
||||||
# Financial
|
# Financial
|
||||||
subtotal: float
|
subtotal: float | None
|
||||||
tax_amount: float
|
tax_amount: float | None
|
||||||
shipping_amount: float
|
shipping_amount: float | None
|
||||||
discount_amount: float
|
discount_amount: float | None
|
||||||
total_amount: float
|
total_amount: float
|
||||||
currency: str
|
currency: str
|
||||||
|
|
||||||
# Shipping
|
# Shipping
|
||||||
shipping_method: str | None
|
ship_country_iso: str
|
||||||
tracking_number: str | None
|
tracking_number: str | None
|
||||||
|
tracking_provider: str | None
|
||||||
|
|
||||||
# Item count
|
# Item count
|
||||||
item_count: int = 0
|
item_count: int = 0
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
created_at: datetime
|
order_date: datetime
|
||||||
updated_at: datetime
|
confirmed_at: datetime | None
|
||||||
paid_at: datetime | None
|
|
||||||
shipped_at: datetime | None
|
shipped_at: datetime | None
|
||||||
delivered_at: datetime | None
|
delivered_at: datetime | None
|
||||||
cancelled_at: datetime | None
|
cancelled_at: datetime | None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
|
||||||
class AdminOrderListResponse(BaseModel):
|
class AdminOrderListResponse(BaseModel):
|
||||||
@@ -221,15 +396,21 @@ class AdminOrderListResponse(BaseModel):
|
|||||||
class AdminOrderStats(BaseModel):
|
class AdminOrderStats(BaseModel):
|
||||||
"""Order statistics for admin dashboard."""
|
"""Order statistics for admin dashboard."""
|
||||||
|
|
||||||
total_orders: int
|
total_orders: int = 0
|
||||||
pending_orders: int
|
pending_orders: int = 0
|
||||||
processing_orders: int
|
processing_orders: int = 0
|
||||||
shipped_orders: int
|
shipped_orders: int = 0
|
||||||
delivered_orders: int
|
delivered_orders: int = 0
|
||||||
cancelled_orders: int
|
cancelled_orders: int = 0
|
||||||
refunded_orders: int
|
refunded_orders: int = 0
|
||||||
total_revenue: float
|
total_revenue: float = 0.0
|
||||||
vendors_with_orders: int
|
|
||||||
|
# By channel
|
||||||
|
direct_orders: int = 0
|
||||||
|
letzshop_orders: int = 0
|
||||||
|
|
||||||
|
# Vendors
|
||||||
|
vendors_with_orders: int = 0
|
||||||
|
|
||||||
|
|
||||||
class AdminOrderStatusUpdate(BaseModel):
|
class AdminOrderStatusUpdate(BaseModel):
|
||||||
@@ -239,6 +420,7 @@ class AdminOrderStatusUpdate(BaseModel):
|
|||||||
..., pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
|
..., pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
|
||||||
)
|
)
|
||||||
tracking_number: str | None = None
|
tracking_number: str | None = None
|
||||||
|
tracking_provider: str | None = None
|
||||||
reason: str | None = Field(None, description="Reason for status change")
|
reason: str | None = Field(None, description="Reason for status change")
|
||||||
|
|
||||||
|
|
||||||
@@ -255,3 +437,76 @@ class AdminVendorsWithOrdersResponse(BaseModel):
|
|||||||
"""Response for vendors with orders list."""
|
"""Response for vendors with orders list."""
|
||||||
|
|
||||||
vendors: list[AdminVendorWithOrders]
|
vendors: list[AdminVendorWithOrders]
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Letzshop-specific Schemas
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopOrderImport(BaseModel):
|
||||||
|
"""Schema for importing a Letzshop order from shipment data."""
|
||||||
|
|
||||||
|
shipment_id: str
|
||||||
|
order_id: str
|
||||||
|
order_number: str
|
||||||
|
order_date: datetime
|
||||||
|
|
||||||
|
# Customer
|
||||||
|
customer_email: str
|
||||||
|
customer_locale: str | None = None
|
||||||
|
|
||||||
|
# Shipping address
|
||||||
|
ship_first_name: str
|
||||||
|
ship_last_name: str
|
||||||
|
ship_company: str | None = None
|
||||||
|
ship_address_line_1: str
|
||||||
|
ship_address_line_2: str | None = None
|
||||||
|
ship_city: str
|
||||||
|
ship_postal_code: str
|
||||||
|
ship_country_iso: str
|
||||||
|
|
||||||
|
# Billing address
|
||||||
|
bill_first_name: str
|
||||||
|
bill_last_name: str
|
||||||
|
bill_company: str | None = None
|
||||||
|
bill_address_line_1: str
|
||||||
|
bill_address_line_2: str | None = None
|
||||||
|
bill_city: str
|
||||||
|
bill_postal_code: str
|
||||||
|
bill_country_iso: str
|
||||||
|
|
||||||
|
# Totals
|
||||||
|
total_amount: float
|
||||||
|
currency: str = "EUR"
|
||||||
|
|
||||||
|
# State
|
||||||
|
letzshop_state: str # unconfirmed, confirmed, declined
|
||||||
|
|
||||||
|
# Items
|
||||||
|
inventory_units: list[dict]
|
||||||
|
|
||||||
|
# Raw data
|
||||||
|
raw_data: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopShippingInfo(BaseModel):
|
||||||
|
"""Shipping info retrieved from Letzshop."""
|
||||||
|
|
||||||
|
tracking_number: str
|
||||||
|
tracking_provider: str
|
||||||
|
shipment_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopOrderConfirmItem(BaseModel):
|
||||||
|
"""Schema for confirming/declining a single item."""
|
||||||
|
|
||||||
|
item_id: int
|
||||||
|
external_item_id: str
|
||||||
|
action: str = Field(..., pattern="^(confirm|decline)$")
|
||||||
|
|
||||||
|
|
||||||
|
class LetzshopOrderConfirmRequest(BaseModel):
|
||||||
|
"""Schema for confirming/declining order items."""
|
||||||
|
|
||||||
|
items: list[LetzshopOrderConfirmItem]
|
||||||
|
|||||||
Reference in New Issue
Block a user