from pydantic import BaseModel, Field, EmailStr, validator from typing import List, Optional from datetime import datetime from bson import ObjectId class PyObjectId(ObjectId): @classmethod def __get_validators__(cls): yield cls.validate @classmethod def validate(cls, v): if not ObjectId.is_valid(v): raise ValueError("Invalid objectid") return ObjectId(v) @classmethod def __modify_schema__(cls, field_schema): field_schema.update(type="string") class MongoBaseModel(BaseModel): id: Optional[PyObjectId] = Field(default_factory=PyObjectId, alias="_id") class Config: allow_population_by_field_name = True arbitrary_types_allowed = True json_encoders = {ObjectId: str} class Item(MongoBaseModel): name: str price: float quantity: int unit: str related_items: List[str] = [] class OrderItem(BaseModel): item_id: PyObjectId quantity: int price_at_order: float class Order(MongoBaseModel): user_id: PyObjectId items: List[OrderItem] total_amount: float payment_method: Optional[str] = None payment_status: str = "pending" order_status: str = "created" created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: Optional[datetime] = None discount_applied: Optional[float] = None notes: Optional[str] = None @validator('order_status') def valid_order_status(cls, v): allowed_statuses = ["created", "processing", "shipped", "delivered", "cancelled"] if v not in allowed_statuses: raise ValueError(f"Invalid order status. Must be one of: { ', '.join(allowed_statuses)}") return v @validator('payment_status') def valid_payment_status(cls, v): allowed_statuses = ["pending", "paid", "refunded", "failed"] if v not in allowed_statuses: raise ValueError(f"Invalid payment status. Must be one of: { ', '.join(allowed_statuses)}") return v class UserBase(BaseModel): username: str email: EmailStr full_name: str is_active: bool = True is_superuser: bool = False class UserCreate(UserBase): password: str class UserInDB(UserBase, MongoBaseModel): hashed_password: str class User(UserBase, MongoBaseModel): pass class Token(BaseModel): access_token: str token_type: str class TokenData(BaseModel): username: Optional[str] = None