# models/schema/vendor.py """ Pydantic schemas for Vendor-related operations. Schemas include: - VendorCreate: For creating vendors under companies - VendorUpdate: For updating vendor information (Admin only) - VendorResponse: Standard vendor response - VendorDetailResponse: Vendor response with company/owner details - VendorCreateResponse: Response after vendor creation - VendorListResponse: Paginated vendor list - VendorSummary: Lightweight vendor info Note: Ownership transfer is handled at the Company level. See models/schema/company.py for CompanyTransferOwnership. """ import re from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, field_validator class VendorCreate(BaseModel): """ Schema for creating a new vendor (storefront/brand) under an existing company. Business contact info is inherited from the parent company. """ # Parent company company_id: int = Field(..., description="ID of the parent company", gt=0) # Basic Information vendor_code: str = Field( ..., description="Unique vendor identifier (e.g., TECHSTORE)", min_length=2, max_length=50, ) subdomain: str = Field( ..., description="Unique subdomain for the vendor", min_length=2, max_length=100 ) name: str = Field( ..., description="Display name of the vendor/brand", min_length=2, max_length=255 ) description: str | None = Field(None, description="Vendor/brand description") # Marketplace URLs (brand-specific multi-language support) letzshop_csv_url_fr: str | None = Field(None, description="French CSV URL") letzshop_csv_url_en: str | None = Field(None, description="English CSV URL") letzshop_csv_url_de: str | None = Field(None, description="German CSV URL") @field_validator("subdomain") @classmethod def validate_subdomain(cls, v): """Validate subdomain format: lowercase alphanumeric with hyphens.""" if v and not re.match(r"^[a-z0-9][a-z0-9-]*[a-z0-9]$", v): raise ValueError( "Subdomain must contain only lowercase letters, numbers, and hyphens" ) return v.lower() if v else v @field_validator("vendor_code") @classmethod def validate_vendor_code(cls, v): """Ensure vendor code is uppercase for consistency.""" return v.upper() if v else v class VendorUpdate(BaseModel): """ Schema for updating vendor information (Admin only). Note: Business contact info (contact_email, etc.) is at the Company level. Use company update endpoints to modify those fields. """ # Basic Information name: str | None = Field(None, min_length=2, max_length=255) description: str | None = None subdomain: str | None = Field(None, min_length=2, max_length=100) # Marketplace URLs (brand-specific) letzshop_csv_url_fr: str | None = None letzshop_csv_url_en: str | None = None letzshop_csv_url_de: str | None = None # Status (Admin only) is_active: bool | None = None is_verified: bool | None = None @field_validator("subdomain") @classmethod def subdomain_lowercase(cls, v): """Normalize subdomain to lowercase.""" return v.lower().strip() if v else v model_config = ConfigDict(from_attributes=True) class VendorResponse(BaseModel): """ Standard schema for vendor response data. Note: Business contact info (contact_email, contact_phone, website, business_address, tax_number) is now at the Company level. Use company_id to look up company details. """ model_config = ConfigDict(from_attributes=True) id: int vendor_code: str subdomain: str name: str description: str | None # Company relationship company_id: int # Marketplace URLs (brand-specific) letzshop_csv_url_fr: str | None letzshop_csv_url_en: str | None letzshop_csv_url_de: str | None # Status Flags is_active: bool is_verified: bool # Timestamps created_at: datetime updated_at: datetime class VendorDetailResponse(VendorResponse): """ Extended vendor response including company information. Includes company details like contact info and owner information. """ # Company info company_name: str = Field(..., description="Name of the parent company") company_contact_email: str = Field(..., description="Company business contact email") company_contact_phone: str | None = Field(None, description="Company phone number") company_website: str | None = Field(None, description="Company website URL") # Owner info (at company level) owner_email: str = Field( ..., description="Email of the company owner (for login/authentication)" ) owner_username: str = Field(..., description="Username of the company owner") class VendorCreateResponse(VendorDetailResponse): """ Response after creating vendor under an existing company. The vendor is created under a company, so no new owner credentials are generated. The company owner already has access to this vendor. """ login_url: str | None = Field(None, description="URL for vendor storefront") class VendorListResponse(BaseModel): """Schema for paginated vendor list.""" vendors: list[VendorResponse] total: int skip: int limit: int class VendorSummary(BaseModel): """Lightweight vendor summary for dropdowns and quick references.""" model_config = ConfigDict(from_attributes=True) id: int vendor_code: str subdomain: str name: str company_id: int is_active: bool # NOTE: Vendor ownership transfer schemas have been removed. # Ownership transfer is now handled at the Company level. # See models/schema/company.py for CompanyTransferOwnership and CompanyTransferOwnershipResponse.