153 lines
4.4 KiB
Python
153 lines
4.4 KiB
Python
"""Pydantic v2 request/response schemas."""
|
|
from datetime import datetime
|
|
from typing import Any, List, Optional
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
class ORMModel(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# ── auth ────────────────────────────────────────────────────────────────
|
|
class LoginRequest(BaseModel):
|
|
email: str
|
|
password: str
|
|
|
|
|
|
class TokenResponse(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
class CustomerRegister(BaseModel):
|
|
email: str
|
|
name: Optional[str] = None
|
|
shipping_addr: Optional[dict] = None
|
|
# NOTE: customers table has no password column — password (if sent) is not
|
|
# stored. Customer identity for MVP is email-only; TODO: add password column + hash.
|
|
password: Optional[str] = None
|
|
|
|
|
|
# ── products ────────────────────────────────────────────────────────────
|
|
class ProductOut(ORMModel):
|
|
id: UUID
|
|
sku: str
|
|
title: str
|
|
description: Optional[str] = None
|
|
category: Optional[str] = None
|
|
brand: Optional[str] = None
|
|
manufacturer: Optional[str] = None
|
|
images: Any = None
|
|
weight_grams: Optional[float] = None
|
|
cost: float
|
|
retail_price: Optional[float] = None
|
|
min_price: Optional[float] = None
|
|
max_price: Optional[float] = None
|
|
status: str
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
|
|
class ImportRequest(BaseModel):
|
|
supplier_id: UUID
|
|
feed_type: str = "csv"
|
|
data_or_url: str = ""
|
|
|
|
|
|
class PriceHistoryOut(ORMModel):
|
|
price: float
|
|
reason: Optional[str] = None
|
|
created_at: Optional[datetime] = None
|
|
|
|
|
|
# ── suppliers ───────────────────────────────────────────────────────────
|
|
class SupplierCreate(BaseModel):
|
|
name: str
|
|
adapter_type: str = "csv"
|
|
api_endpoint: Optional[str] = None
|
|
account_id: Optional[str] = None
|
|
fulfillment_caps: dict = {}
|
|
shipping_regions: List[str] = []
|
|
reliability_score: float = 50.0
|
|
status: str = "active"
|
|
|
|
|
|
class SupplierOut(ORMModel):
|
|
id: UUID
|
|
name: str
|
|
adapter_type: str
|
|
api_endpoint: Optional[str] = None
|
|
fulfillment_caps: Any = None
|
|
shipping_regions: Any = None
|
|
reliability_score: float
|
|
status: str
|
|
created_at: Optional[datetime] = None
|
|
|
|
|
|
class SupplierPerformanceOut(BaseModel):
|
|
supplier_id: UUID
|
|
supplier_name: str
|
|
fulfillment_rate: float
|
|
avg_shipping_days: Optional[float] = None
|
|
cancellation_rate: float
|
|
stock_accuracy: float
|
|
return_rate: float
|
|
updated_at: Optional[datetime] = None
|
|
|
|
|
|
# ── customers ───────────────────────────────────────────────────────────
|
|
class CustomerOut(ORMModel):
|
|
id: UUID
|
|
email: str
|
|
name: Optional[str] = None
|
|
shipping_addr: Any = None
|
|
created_at: Optional[datetime] = None
|
|
|
|
|
|
# ── orders ──────────────────────────────────────────────────────────────
|
|
class OrderItemIn(BaseModel):
|
|
product_id: UUID
|
|
qty: int = Field(ge=1)
|
|
|
|
|
|
class OrderCreate(BaseModel):
|
|
customer_email: str
|
|
items: List[OrderItemIn] = Field(min_length=1)
|
|
|
|
|
|
class OrderOut(ORMModel):
|
|
id: UUID
|
|
order_number: str
|
|
customer_id: Optional[UUID] = None
|
|
items: Any
|
|
retail_total: float
|
|
supplier_id: Optional[UUID] = None
|
|
supplier_cost: float
|
|
shipping_cost: float
|
|
fees: float
|
|
profit: float
|
|
status: str
|
|
tracking: Optional[str] = None
|
|
carrier: Optional[str] = None
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
|
|
class CustomerOrderOut(ORMModel):
|
|
id: UUID
|
|
order_number: str
|
|
items: Any
|
|
retail_total: float
|
|
status: str
|
|
tracking: Optional[str] = None
|
|
carrier: Optional[str] = None
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
|
|
class TrackingIn(BaseModel):
|
|
tracking: str
|
|
carrier: Optional[str] = None
|