refactor: modernize code quality tooling with Ruff

- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter)
- Add comprehensive pyproject.toml configuration
- Simplify Makefile code quality targets
- Configure exclusions for venv/.venv in pyproject.toml
- Auto-fix 1,359 linting issues across codebase

Benefits:
- Much faster builds (Ruff is written in Rust)
- Single tool replaces multiple tools
- More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q)
- All configuration centralized in pyproject.toml
- Better import sorting and formatting consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-28 19:37:38 +01:00
parent 21c13ca39b
commit 238c1ec9b8
169 changed files with 2183 additions and 1784 deletions

View File

@@ -3,6 +3,7 @@
# API models (Pydantic) - import the modules, not all classes
from . import schema
# Database models (SQLAlchemy)
from .database.base import Base
from .database.inventory import Inventory

View File

@@ -1,7 +1,13 @@
# models/database/__init__.py
"""Database models package."""
from .admin import (AdminAuditLog, AdminNotification, AdminSession,
AdminSetting, PlatformAlert)
from .admin import (
AdminAuditLog,
AdminNotification,
AdminSession,
AdminSetting,
PlatformAlert,
)
from .base import Base
from .customer import Customer, CustomerAddress
from .inventory import Inventory

View File

@@ -10,8 +10,16 @@ This module provides models for:
- Platform alerts (system-wide issues)
"""
from sqlalchemy import (JSON, Boolean, Column, DateTime, ForeignKey, Integer,
String, Text)
from sqlalchemy import (
JSON,
Boolean,
Column,
DateTime,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -1,17 +1,15 @@
from datetime import datetime, timezone
from datetime import UTC, datetime
from sqlalchemy import Column, DateTime
from app.core.database import Base
class TimestampMixin:
"""Mixin to add created_at and updated_at timestamps to models"""
created_at = Column(DateTime, default=datetime.now(timezone.utc), nullable=False)
created_at = Column(DateTime, default=datetime.now(UTC), nullable=False)
updated_at = Column(
DateTime,
default=datetime.now(timezone.utc),
onupdate=datetime.now(timezone.utc),
default=datetime.now(UTC),
onupdate=datetime.now(UTC),
nullable=False,
)

View File

@@ -1,9 +1,16 @@
# models/database/cart.py
"""Cart item database model."""
from datetime import datetime
from sqlalchemy import (Column, Float, ForeignKey, Index, Integer, String,
UniqueConstraint)
from sqlalchemy import (
Column,
Float,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -14,10 +14,19 @@ Features:
- Version history support
"""
from datetime import datetime, timezone
from datetime import UTC, datetime
from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Index, Integer,
String, Text, UniqueConstraint)
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -77,13 +86,13 @@ class ContentPage(Base):
# Timestamps
created_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
default=lambda: datetime.now(UTC),
nullable=False,
)
updated_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
default=lambda: datetime.now(UTC),
onupdate=lambda: datetime.now(UTC),
nullable=False,
)

View File

@@ -1,8 +1,14 @@
from datetime import datetime
from decimal import Decimal
from sqlalchemy import (JSON, Boolean, Column, DateTime, ForeignKey, Integer,
Numeric, String, Text)
from sqlalchemy import (
JSON,
Boolean,
Column,
DateTime,
ForeignKey,
Integer,
Numeric,
String,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -1,8 +1,6 @@
# models/database/inventory.py
from datetime import datetime
from sqlalchemy import (Column, ForeignKey, Index, Integer, String,
UniqueConstraint)
from sqlalchemy import Column, ForeignKey, Index, Integer, String, UniqueConstraint
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -1,7 +1,5 @@
from datetime import datetime, timezone
from sqlalchemy import (Column, DateTime, ForeignKey, Index, Integer, String,
Text)
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Text
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -1,7 +1,10 @@
from datetime import datetime, timezone
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Index,
Integer, String, Text, UniqueConstraint)
from sqlalchemy import (
Column,
Index,
Integer,
String,
)
from sqlalchemy.orm import relationship
# Import Base from the central database module instead of creating a new one

View File

@@ -1,8 +1,15 @@
# models/database/order.py
from datetime import datetime
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Integer,
String, Text)
from sqlalchemy import (
Boolean,
Column,
DateTime,
Float,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -1,8 +1,15 @@
# models/database/product.py
from datetime import datetime
from sqlalchemy import (Boolean, Column, Float, ForeignKey, Index, Integer,
String, UniqueConstraint)
from sqlalchemy import (
Boolean,
Column,
Float,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -10,9 +10,10 @@ ROLE CLARIFICATION:
- Vendor-specific roles (manager, staff, etc.) are stored in VendorUser.role
- Customers are NOT in the User table - they use the Customer model
"""
import enum
from sqlalchemy import Boolean, Column, DateTime, Enum, Integer, String
from sqlalchemy import Boolean, Column, DateTime, Integer, String
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -5,13 +5,23 @@ Vendor model representing entities that sell products or services.
This module defines the Vendor model along with its relationships to
other models such as User (owner), Product, Customer, Order, and MarketplaceImportJob.
"""
import enum
from sqlalchemy import (JSON, Boolean, Column, DateTime, ForeignKey, Integer,
String, Text)
from sqlalchemy import (
JSON,
Boolean,
Column,
DateTime,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.orm import relationship
from app.core.config import settings
# Import Base from the central database module instead of creating a new one
from app.core.database import Base
from models.database.base import TimestampMixin

View File

@@ -2,10 +2,18 @@
"""
Vendor Domain Model - Maps custom domains to vendors
"""
from datetime import datetime, timezone
from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Index, Integer,
String, UniqueConstraint)
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base

View File

@@ -3,6 +3,7 @@
Vendor Theme Configuration Model
Allows each vendor to customize their shop's appearance
"""
from sqlalchemy import JSON, Boolean, Column, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship

View File

@@ -2,8 +2,16 @@
"""API models package - Pydantic models for request/response validation."""
# Import API model modules
from . import (auth, base, inventory, marketplace_import_job,
marketplace_product, stats, vendor)
from . import (
auth,
base,
inventory,
marketplace_import_job,
marketplace_product,
stats,
vendor,
)
# Common imports for convenience
from .base import * # Base Pydantic models

View File

@@ -12,7 +12,7 @@ This module provides schemas for:
"""
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any
from pydantic import BaseModel, Field, field_validator
@@ -26,14 +26,14 @@ class AdminAuditLogResponse(BaseModel):
id: int
admin_user_id: int
admin_username: Optional[str] = None
admin_username: str | None = None
action: str
target_type: str
target_id: str
details: Optional[Dict[str, Any]] = None
ip_address: Optional[str] = None
user_agent: Optional[str] = None
request_id: Optional[str] = None
details: dict[str, Any] | None = None
ip_address: str | None = None
user_agent: str | None = None
request_id: str | None = None
created_at: datetime
model_config = {"from_attributes": True}
@@ -42,11 +42,11 @@ class AdminAuditLogResponse(BaseModel):
class AdminAuditLogFilters(BaseModel):
"""Filters for querying audit logs."""
admin_user_id: Optional[int] = None
action: Optional[str] = None
target_type: Optional[str] = None
date_from: Optional[datetime] = None
date_to: Optional[datetime] = None
admin_user_id: int | None = None
action: str | None = None
target_type: str | None = None
date_from: datetime | None = None
date_to: datetime | None = None
skip: int = Field(0, ge=0)
limit: int = Field(100, ge=1, le=1000)
@@ -54,7 +54,7 @@ class AdminAuditLogFilters(BaseModel):
class AdminAuditLogListResponse(BaseModel):
"""Paginated list of audit logs."""
logs: List[AdminAuditLogResponse]
logs: list[AdminAuditLogResponse]
total: int
skip: int
limit: int
@@ -73,8 +73,8 @@ class AdminNotificationCreate(BaseModel):
title: str = Field(..., max_length=200)
message: str = Field(..., description="Notification message")
action_required: bool = Field(default=False)
action_url: Optional[str] = Field(None, max_length=500)
metadata: Optional[Dict[str, Any]] = None
action_url: str | None = Field(None, max_length=500)
metadata: dict[str, Any] | None = None
@field_validator("priority")
@classmethod
@@ -94,11 +94,11 @@ class AdminNotificationResponse(BaseModel):
title: str
message: str
is_read: bool
read_at: Optional[datetime] = None
read_by_user_id: Optional[int] = None
read_at: datetime | None = None
read_by_user_id: int | None = None
action_required: bool
action_url: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
action_url: str | None = None
metadata: dict[str, Any] | None = None
created_at: datetime
model_config = {"from_attributes": True}
@@ -113,7 +113,7 @@ class AdminNotificationUpdate(BaseModel):
class AdminNotificationListResponse(BaseModel):
"""Paginated list of notifications."""
notifications: List[AdminNotificationResponse]
notifications: list[AdminNotificationResponse]
total: int
unread_count: int
skip: int
@@ -131,8 +131,8 @@ class AdminSettingCreate(BaseModel):
key: str = Field(..., max_length=100, description="Unique setting key")
value: str = Field(..., description="Setting value")
value_type: str = Field(default="string", description="Data type")
category: Optional[str] = Field(None, max_length=50)
description: Optional[str] = None
category: str | None = Field(None, max_length=50)
description: str | None = None
is_encrypted: bool = Field(default=False)
is_public: bool = Field(default=False, description="Can be exposed to frontend")
@@ -162,11 +162,11 @@ class AdminSettingResponse(BaseModel):
key: str
value: str
value_type: str
category: Optional[str] = None
description: Optional[str] = None
category: str | None = None
description: str | None = None
is_encrypted: bool
is_public: bool
last_modified_by_user_id: Optional[int] = None
last_modified_by_user_id: int | None = None
updated_at: datetime
model_config = {"from_attributes": True}
@@ -176,15 +176,15 @@ class AdminSettingUpdate(BaseModel):
"""Update admin setting value."""
value: str
description: Optional[str] = None
description: str | None = None
class AdminSettingListResponse(BaseModel):
"""List of settings by category."""
settings: List[AdminSettingResponse]
settings: list[AdminSettingResponse]
total: int
category: Optional[str] = None
category: str | None = None
# ============================================================================
@@ -198,9 +198,9 @@ class PlatformAlertCreate(BaseModel):
alert_type: str = Field(..., max_length=50)
severity: str = Field(..., description="Alert severity")
title: str = Field(..., max_length=200)
description: Optional[str] = None
affected_vendors: Optional[List[int]] = None
affected_systems: Optional[List[str]] = None
description: str | None = None
affected_vendors: list[int] | None = None
affected_systems: list[str] | None = None
auto_generated: bool = Field(default=True)
@field_validator("severity")
@@ -234,13 +234,13 @@ class PlatformAlertResponse(BaseModel):
alert_type: str
severity: str
title: str
description: Optional[str] = None
affected_vendors: Optional[List[int]] = None
affected_systems: Optional[List[str]] = None
description: str | None = None
affected_vendors: list[int] | None = None
affected_systems: list[str] | None = None
is_resolved: bool
resolved_at: Optional[datetime] = None
resolved_by_user_id: Optional[int] = None
resolution_notes: Optional[str] = None
resolved_at: datetime | None = None
resolved_by_user_id: int | None = None
resolution_notes: str | None = None
auto_generated: bool
occurrence_count: int
first_occurred_at: datetime
@@ -254,13 +254,13 @@ class PlatformAlertResolve(BaseModel):
"""Resolve platform alert."""
is_resolved: bool = True
resolution_notes: Optional[str] = None
resolution_notes: str | None = None
class PlatformAlertListResponse(BaseModel):
"""Paginated list of platform alerts."""
alerts: List[PlatformAlertResponse]
alerts: list[PlatformAlertResponse]
total: int
active_count: int
critical_count: int
@@ -276,10 +276,10 @@ class PlatformAlertListResponse(BaseModel):
class BulkVendorAction(BaseModel):
"""Bulk actions on vendors."""
vendor_ids: List[int] = Field(..., min_length=1, max_length=100)
vendor_ids: list[int] = Field(..., min_length=1, max_length=100)
action: str = Field(..., description="Action to perform")
confirm: bool = Field(default=False, description="Required for destructive actions")
reason: Optional[str] = Field(None, description="Reason for bulk action")
reason: str | None = Field(None, description="Reason for bulk action")
@field_validator("action")
@classmethod
@@ -293,8 +293,8 @@ class BulkVendorAction(BaseModel):
class BulkVendorActionResponse(BaseModel):
"""Response for bulk vendor actions."""
successful: List[int]
failed: Dict[int, str] # vendor_id -> error_message
successful: list[int]
failed: dict[int, str] # vendor_id -> error_message
total_processed: int
action_performed: str
message: str
@@ -303,10 +303,10 @@ class BulkVendorActionResponse(BaseModel):
class BulkUserAction(BaseModel):
"""Bulk actions on users."""
user_ids: List[int] = Field(..., min_length=1, max_length=100)
user_ids: list[int] = Field(..., min_length=1, max_length=100)
action: str = Field(..., description="Action to perform")
confirm: bool = Field(default=False)
reason: Optional[str] = None
reason: str | None = None
@field_validator("action")
@classmethod
@@ -320,8 +320,8 @@ class BulkUserAction(BaseModel):
class BulkUserActionResponse(BaseModel):
"""Response for bulk user actions."""
successful: List[int]
failed: Dict[int, str]
successful: list[int]
failed: dict[int, str]
total_processed: int
action_performed: str
message: str
@@ -335,14 +335,14 @@ class BulkUserActionResponse(BaseModel):
class AdminDashboardStats(BaseModel):
"""Comprehensive admin dashboard statistics."""
platform: Dict[str, Any]
users: Dict[str, Any]
vendors: Dict[str, Any]
products: Dict[str, Any]
orders: Dict[str, Any]
imports: Dict[str, Any]
recent_vendors: List[Dict[str, Any]]
recent_imports: List[Dict[str, Any]]
platform: dict[str, Any]
users: dict[str, Any]
vendors: dict[str, Any]
products: dict[str, Any]
orders: dict[str, Any]
imports: dict[str, Any]
recent_vendors: list[dict[str, Any]]
recent_imports: list[dict[str, Any]]
unread_notifications: int
active_alerts: int
critical_alerts: int
@@ -357,10 +357,10 @@ class ComponentHealthStatus(BaseModel):
"""Health status for a system component."""
status: str # healthy, degraded, unhealthy
response_time_ms: Optional[float] = None
error_message: Optional[str] = None
response_time_ms: float | None = None
error_message: str | None = None
last_checked: datetime
details: Optional[Dict[str, Any]] = None
details: dict[str, Any] | None = None
class SystemHealthResponse(BaseModel):
@@ -386,14 +386,14 @@ class AdminSessionResponse(BaseModel):
id: int
admin_user_id: int
admin_username: Optional[str] = None
admin_username: str | None = None
ip_address: str
user_agent: Optional[str] = None
user_agent: str | None = None
login_at: datetime
last_activity_at: datetime
logout_at: Optional[datetime] = None
logout_at: datetime | None = None
is_active: bool
logout_reason: Optional[str] = None
logout_reason: str | None = None
model_config = {"from_attributes": True}
@@ -401,6 +401,6 @@ class AdminSessionResponse(BaseModel):
class AdminSessionListResponse(BaseModel):
"""List of admin sessions."""
sessions: List[AdminSessionResponse]
sessions: list[AdminSessionResponse]
total: int
active_count: int

View File

@@ -1,7 +1,6 @@
# auth.py - Keep security-critical validation
import re
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
@@ -33,7 +32,7 @@ class UserRegister(BaseModel):
class UserLogin(BaseModel):
email_or_username: str = Field(..., description="Username or email address")
password: str = Field(..., description="Password")
vendor_code: Optional[str] = Field(
vendor_code: str | None = Field(
None, description="Optional vendor code for context"
)
@@ -50,7 +49,7 @@ class UserResponse(BaseModel):
username: str
role: str
is_active: bool
last_login: Optional[datetime] = None
last_login: datetime | None = None
created_at: datetime
updated_at: datetime

View File

@@ -1,4 +1,4 @@
from typing import Generic, List, TypeVar
from typing import Generic, TypeVar
from pydantic import BaseModel
@@ -8,7 +8,7 @@ T = TypeVar("T")
class ListResponse(BaseModel, Generic[T]):
"""Generic list response model"""
items: List[T]
items: list[T]
total: int
skip: int
limit: int

View File

@@ -3,8 +3,6 @@
Pydantic schemas for shopping cart operations.
"""
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -43,7 +41,7 @@ class CartItemResponse(BaseModel):
line_total: float = Field(
..., description="Total price for this line (price * quantity)"
)
image_url: Optional[str] = Field(None, description="Product image URL")
image_url: str | None = Field(None, description="Product image URL")
class CartResponse(BaseModel):
@@ -51,7 +49,7 @@ class CartResponse(BaseModel):
vendor_id: int = Field(..., description="Vendor ID")
session_id: str = Field(..., description="Shopping session ID")
items: List[CartItemResponse] = Field(
items: list[CartItemResponse] = Field(
default_factory=list, description="Cart items"
)
subtotal: float = Field(..., description="Subtotal of all items")
@@ -82,7 +80,7 @@ class CartOperationResponse(BaseModel):
message: str = Field(..., description="Operation result message")
product_id: int = Field(..., description="Product ID affected")
quantity: Optional[int] = Field(
quantity: int | None = Field(
None, description="New quantity (for add/update operations)"
)

View File

@@ -5,7 +5,6 @@ Pydantic schema for customer-related operations.
from datetime import datetime
from decimal import Decimal
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, EmailStr, Field, field_validator
@@ -23,7 +22,7 @@ class CustomerRegister(BaseModel):
)
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
phone: Optional[str] = Field(None, max_length=50)
phone: str | None = Field(None, max_length=50)
marketing_consent: bool = Field(default=False)
@field_validator("email")
@@ -48,15 +47,15 @@ class CustomerRegister(BaseModel):
class CustomerUpdate(BaseModel):
"""Schema for updating customer profile."""
email: Optional[EmailStr] = None
first_name: Optional[str] = Field(None, min_length=1, max_length=100)
last_name: Optional[str] = Field(None, min_length=1, max_length=100)
phone: Optional[str] = Field(None, max_length=50)
marketing_consent: Optional[bool] = None
email: EmailStr | None = None
first_name: str | None = Field(None, min_length=1, max_length=100)
last_name: str | None = Field(None, min_length=1, max_length=100)
phone: str | None = Field(None, max_length=50)
marketing_consent: bool | None = None
@field_validator("email")
@classmethod
def email_lowercase(cls, v: Optional[str]) -> Optional[str]:
def email_lowercase(cls, v: str | None) -> str | None:
"""Convert email to lowercase."""
return v.lower() if v else None
@@ -72,12 +71,12 @@ class CustomerResponse(BaseModel):
id: int
vendor_id: int
email: str
first_name: Optional[str]
last_name: Optional[str]
phone: Optional[str]
first_name: str | None
last_name: str | None
phone: str | None
customer_number: str
marketing_consent: bool
last_order_date: Optional[datetime]
last_order_date: datetime | None
total_orders: int
total_spent: Decimal
is_active: bool
@@ -97,7 +96,7 @@ class CustomerResponse(BaseModel):
class CustomerListResponse(BaseModel):
"""Schema for paginated customer list."""
customers: List[CustomerResponse]
customers: list[CustomerResponse]
total: int
page: int
per_page: int
@@ -115,9 +114,9 @@ class CustomerAddressCreate(BaseModel):
address_type: str = Field(..., pattern="^(billing|shipping)$")
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
company: Optional[str] = Field(None, max_length=200)
company: str | None = Field(None, max_length=200)
address_line_1: str = Field(..., min_length=1, max_length=255)
address_line_2: Optional[str] = Field(None, 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)
@@ -127,16 +126,16 @@ class CustomerAddressCreate(BaseModel):
class CustomerAddressUpdate(BaseModel):
"""Schema for updating customer address."""
address_type: Optional[str] = Field(None, pattern="^(billing|shipping)$")
first_name: Optional[str] = Field(None, min_length=1, max_length=100)
last_name: Optional[str] = Field(None, min_length=1, max_length=100)
company: Optional[str] = Field(None, max_length=200)
address_line_1: Optional[str] = Field(None, min_length=1, max_length=255)
address_line_2: Optional[str] = Field(None, max_length=255)
city: Optional[str] = Field(None, min_length=1, max_length=100)
postal_code: Optional[str] = Field(None, min_length=1, max_length=20)
country: Optional[str] = Field(None, min_length=2, max_length=100)
is_default: Optional[bool] = None
address_type: str | None = Field(None, pattern="^(billing|shipping)$")
first_name: str | None = Field(None, min_length=1, max_length=100)
last_name: str | None = Field(None, min_length=1, max_length=100)
company: str | None = Field(None, max_length=200)
address_line_1: str | None = Field(None, min_length=1, max_length=255)
address_line_2: str | None = Field(None, max_length=255)
city: str | None = Field(None, min_length=1, max_length=100)
postal_code: str | None = Field(None, min_length=1, max_length=20)
country: str | None = Field(None, min_length=2, max_length=100)
is_default: bool | None = None
class CustomerAddressResponse(BaseModel):
@@ -148,9 +147,9 @@ class CustomerAddressResponse(BaseModel):
address_type: str
first_name: str
last_name: str
company: Optional[str]
company: str | None
address_line_1: str
address_line_2: Optional[str]
address_line_2: str | None
city: str
postal_code: str
country: str
@@ -169,7 +168,7 @@ class CustomerAddressResponse(BaseModel):
class CustomerPreferencesUpdate(BaseModel):
"""Schema for updating customer preferences."""
marketing_consent: Optional[bool] = None
language: Optional[str] = Field(None, max_length=10)
currency: Optional[str] = Field(None, max_length=3)
notification_preferences: Optional[Dict[str, bool]] = None
marketing_consent: bool | None = None
language: str | None = Field(None, max_length=10)
currency: str | None = Field(None, max_length=3)
notification_preferences: dict[str, bool] | None = None

View File

@@ -1,6 +1,5 @@
# models/schema/inventory.py
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -27,9 +26,9 @@ class InventoryAdjust(InventoryBase):
class InventoryUpdate(BaseModel):
"""Update inventory fields."""
quantity: Optional[int] = Field(None, ge=0)
reserved_quantity: Optional[int] = Field(None, ge=0)
location: Optional[str] = None
quantity: int | None = Field(None, ge=0)
reserved_quantity: int | None = Field(None, ge=0)
location: str | None = None
class InventoryReserve(BaseModel):
@@ -49,7 +48,7 @@ class InventoryResponse(BaseModel):
location: str
quantity: int
reserved_quantity: int
gtin: Optional[str]
gtin: str | None
created_at: datetime
updated_at: datetime
@@ -70,16 +69,16 @@ class ProductInventorySummary(BaseModel):
product_id: int
vendor_id: int
product_sku: Optional[str]
product_sku: str | None
product_title: str
total_quantity: int
total_reserved: int
total_available: int
locations: List[InventoryLocationResponse]
locations: list[InventoryLocationResponse]
class InventoryListResponse(BaseModel):
inventories: List[InventoryResponse]
inventories: list[InventoryResponse]
total: int
skip: int
limit: int

View File

@@ -1,5 +1,4 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
@@ -12,7 +11,7 @@ class MarketplaceImportJobRequest(BaseModel):
source_url: str = Field(..., description="URL to CSV file from marketplace")
marketplace: str = Field(default="Letzshop", description="Marketplace name")
batch_size: Optional[int] = Field(
batch_size: int | None = Field(
1000, description="Processing batch size", ge=100, le=10000
)
@@ -50,12 +49,12 @@ class MarketplaceImportJobResponse(BaseModel):
error_count: int = 0
# Error details
error_message: Optional[str] = None
error_message: str | None = None
# Timestamps
created_at: datetime
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
started_at: datetime | None = None
completed_at: datetime | None = None
class MarketplaceImportJobListResponse(BaseModel):
@@ -71,8 +70,8 @@ class MarketplaceImportJobStatusUpdate(BaseModel):
"""Schema for updating import job status (internal use)."""
status: str
imported_count: Optional[int] = None
updated_count: Optional[int] = None
error_count: Optional[int] = None
total_processed: Optional[int] = None
error_message: Optional[str] = None
imported_count: int | None = None
updated_count: int | None = None
error_count: int | None = None
total_processed: int | None = None
error_message: str | None = None

View File

@@ -1,6 +1,5 @@
# models/schema/marketplace_products.py - Simplified validation
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -8,45 +7,45 @@ from models.schema.inventory import ProductInventorySummary
class MarketplaceProductBase(BaseModel):
marketplace_product_id: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
link: Optional[str] = None
image_link: Optional[str] = None
availability: Optional[str] = None
price: Optional[str] = None
brand: Optional[str] = None
gtin: Optional[str] = None
mpn: Optional[str] = None
condition: Optional[str] = None
adult: Optional[str] = None
multipack: Optional[int] = None
is_bundle: Optional[str] = None
age_group: Optional[str] = None
color: Optional[str] = None
gender: Optional[str] = None
material: Optional[str] = None
pattern: Optional[str] = None
size: Optional[str] = None
size_type: Optional[str] = None
size_system: Optional[str] = None
item_group_id: Optional[str] = None
google_product_category: Optional[str] = None
product_type: Optional[str] = None
custom_label_0: Optional[str] = None
custom_label_1: Optional[str] = None
custom_label_2: Optional[str] = None
custom_label_3: Optional[str] = None
custom_label_4: Optional[str] = None
additional_image_link: Optional[str] = None
sale_price: Optional[str] = None
unit_pricing_measure: Optional[str] = None
unit_pricing_base_measure: Optional[str] = None
identifier_exists: Optional[str] = None
shipping: Optional[str] = None
currency: Optional[str] = None
marketplace: Optional[str] = None
vendor_name: Optional[str] = None
marketplace_product_id: str | None = None
title: str | None = None
description: str | None = None
link: str | None = None
image_link: str | None = None
availability: str | None = None
price: str | None = None
brand: str | None = None
gtin: str | None = None
mpn: str | None = None
condition: str | None = None
adult: str | None = None
multipack: int | None = None
is_bundle: str | None = None
age_group: str | None = None
color: str | None = None
gender: str | None = None
material: str | None = None
pattern: str | None = None
size: str | None = None
size_type: str | None = None
size_system: str | None = None
item_group_id: str | None = None
google_product_category: str | None = None
product_type: str | None = None
custom_label_0: str | None = None
custom_label_1: str | None = None
custom_label_2: str | None = None
custom_label_3: str | None = None
custom_label_4: str | None = None
additional_image_link: str | None = None
sale_price: str | None = None
unit_pricing_measure: str | None = None
unit_pricing_base_measure: str | None = None
identifier_exists: str | None = None
shipping: str | None = None
currency: str | None = None
marketplace: str | None = None
vendor_name: str | None = None
class MarketplaceProductCreate(MarketplaceProductBase):
@@ -70,7 +69,7 @@ class MarketplaceProductResponse(MarketplaceProductBase):
class MarketplaceProductListResponse(BaseModel):
products: List[MarketplaceProductResponse]
products: list[MarketplaceProductResponse]
total: int
skip: int
limit: int
@@ -78,4 +77,4 @@ class MarketplaceProductListResponse(BaseModel):
class MarketplaceProductDetailResponse(BaseModel):
product: MarketplaceProductResponse
inventory_info: Optional[ProductInventorySummary] = None
inventory_info: ProductInventorySummary | None = None

View File

@@ -4,8 +4,6 @@ Pydantic schema for order operations.
"""
from datetime import datetime
from decimal import Decimal
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -30,7 +28,7 @@ class OrderItemResponse(BaseModel):
order_id: int
product_id: int
product_name: str
product_sku: Optional[str]
product_sku: str | None
quantity: int
unit_price: float
total_price: float
@@ -50,9 +48,9 @@ class OrderAddressCreate(BaseModel):
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
company: Optional[str] = Field(None, max_length=200)
company: str | None = Field(None, max_length=200)
address_line_1: str = Field(..., min_length=1, max_length=255)
address_line_2: Optional[str] = Field(None, 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)
@@ -67,9 +65,9 @@ class OrderAddressResponse(BaseModel):
address_type: str
first_name: str
last_name: str
company: Optional[str]
company: str | None
address_line_1: str
address_line_2: Optional[str]
address_line_2: str | None
city: str
postal_code: str
country: str
@@ -83,29 +81,29 @@ class OrderAddressResponse(BaseModel):
class OrderCreate(BaseModel):
"""Schema for creating an order."""
customer_id: Optional[int] = None # Optional for guest checkout
items: List[OrderItemCreate] = Field(..., min_length=1)
customer_id: int | None = None # Optional for guest checkout
items: list[OrderItemCreate] = Field(..., min_length=1)
# Addresses
shipping_address: OrderAddressCreate
billing_address: Optional[OrderAddressCreate] = None # Use shipping if not provided
billing_address: OrderAddressCreate | None = None # Use shipping if not provided
# Optional fields
shipping_method: Optional[str] = None
customer_notes: Optional[str] = Field(None, max_length=1000)
shipping_method: str | None = None
customer_notes: str | None = Field(None, max_length=1000)
# Cart/session info
session_id: Optional[str] = None
session_id: str | None = None
class OrderUpdate(BaseModel):
"""Schema for updating order status."""
status: Optional[str] = Field(
status: str | None = Field(
None, pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
)
tracking_number: Optional[str] = None
internal_notes: Optional[str] = None
tracking_number: str | None = None
internal_notes: str | None = None
# ============================================================================
@@ -133,26 +131,26 @@ class OrderResponse(BaseModel):
currency: str
# Shipping
shipping_method: Optional[str]
tracking_number: Optional[str]
shipping_method: str | None
tracking_number: str | None
# Notes
customer_notes: Optional[str]
internal_notes: Optional[str]
customer_notes: str | None
internal_notes: str | None
# Timestamps
created_at: datetime
updated_at: datetime
paid_at: Optional[datetime]
shipped_at: Optional[datetime]
delivered_at: Optional[datetime]
cancelled_at: Optional[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]
items: list[OrderItemResponse]
shipping_address: OrderAddressResponse
billing_address: OrderAddressResponse
@@ -160,7 +158,7 @@ class OrderDetailResponse(OrderResponse):
class OrderListResponse(BaseModel):
"""Schema for paginated order list."""
orders: List[OrderResponse]
orders: list[OrderResponse]
total: int
skip: int
limit: int

View File

@@ -1,6 +1,5 @@
# models/schema/product.py
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -12,30 +11,30 @@ class ProductCreate(BaseModel):
marketplace_product_id: int = Field(
..., description="MarketplaceProduct ID to add to vendor catalog"
)
product_id: Optional[str] = Field(
product_id: str | None = Field(
None, description="Vendor's internal SKU/product ID"
)
price: Optional[float] = Field(None, ge=0)
sale_price: Optional[float] = Field(None, ge=0)
currency: Optional[str] = None
availability: Optional[str] = None
condition: Optional[str] = None
price: float | None = Field(None, ge=0)
sale_price: float | None = Field(None, ge=0)
currency: str | None = None
availability: str | None = None
condition: str | None = None
is_featured: bool = False
min_quantity: int = Field(1, ge=1)
max_quantity: Optional[int] = Field(None, ge=1)
max_quantity: int | None = Field(None, ge=1)
class ProductUpdate(BaseModel):
product_id: Optional[str] = None
price: Optional[float] = Field(None, ge=0)
sale_price: Optional[float] = Field(None, ge=0)
currency: Optional[str] = None
availability: Optional[str] = None
condition: Optional[str] = None
is_featured: Optional[bool] = None
is_active: Optional[bool] = None
min_quantity: Optional[int] = Field(None, ge=1)
max_quantity: Optional[int] = Field(None, ge=1)
product_id: str | None = None
price: float | None = Field(None, ge=0)
sale_price: float | None = Field(None, ge=0)
currency: str | None = None
availability: str | None = None
condition: str | None = None
is_featured: bool | None = None
is_active: bool | None = None
min_quantity: int | None = Field(None, ge=1)
max_quantity: int | None = Field(None, ge=1)
class ProductResponse(BaseModel):
@@ -44,33 +43,33 @@ class ProductResponse(BaseModel):
id: int
vendor_id: int
marketplace_product: MarketplaceProductResponse
product_id: Optional[str]
price: Optional[float]
sale_price: Optional[float]
currency: Optional[str]
availability: Optional[str]
condition: Optional[str]
product_id: str | None
price: float | None
sale_price: float | None
currency: str | None
availability: str | None
condition: str | None
is_featured: bool
is_active: bool
display_order: int
min_quantity: int
max_quantity: Optional[int]
max_quantity: int | None
created_at: datetime
updated_at: datetime
# Include inventory summary
total_inventory: Optional[int] = None
available_inventory: Optional[int] = None
total_inventory: int | None = None
available_inventory: int | None = None
class ProductDetailResponse(ProductResponse):
"""Product with full inventory details."""
inventory_locations: List[InventoryLocationResponse] = []
inventory_locations: list[InventoryLocationResponse] = []
class ProductListResponse(BaseModel):
products: List[ProductResponse]
products: list[ProductResponse]
total: int
skip: int
limit: int

View File

@@ -1,9 +1,7 @@
import re
from datetime import datetime
from decimal import Decimal
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
from pydantic import BaseModel, Field
class StatsResponse(BaseModel):
@@ -35,8 +33,8 @@ class CustomerStatsResponse(BaseModel):
total_orders: int
total_spent: Decimal
average_order_value: Decimal
last_order_date: Optional[datetime]
first_order_date: Optional[datetime]
last_order_date: datetime | None
first_order_date: datetime | None
lifetime_value: Decimal

View File

@@ -10,7 +10,6 @@ This module defines request/response schemas for:
"""
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, EmailStr, Field, field_validator
@@ -23,7 +22,7 @@ class RoleBase(BaseModel):
"""Base role schema."""
name: str = Field(..., min_length=1, max_length=100, description="Role name")
permissions: List[str] = Field(
permissions: list[str] = Field(
default_factory=list, description="List of permission strings"
)
@@ -31,14 +30,13 @@ class RoleBase(BaseModel):
class RoleCreate(RoleBase):
"""Schema for creating a role."""
pass
class RoleUpdate(BaseModel):
"""Schema for updating a role."""
name: Optional[str] = Field(None, min_length=1, max_length=100)
permissions: Optional[List[str]] = None
name: str | None = Field(None, min_length=1, max_length=100)
permissions: list[str] | None = None
class RoleResponse(RoleBase):
@@ -56,7 +54,7 @@ class RoleResponse(RoleBase):
class RoleListResponse(BaseModel):
"""Schema for role list response."""
roles: List[RoleResponse]
roles: list[RoleResponse]
total: int
@@ -69,20 +67,20 @@ class TeamMemberBase(BaseModel):
"""Base team member schema."""
email: EmailStr = Field(..., description="Team member email address")
first_name: Optional[str] = Field(None, max_length=100)
last_name: Optional[str] = Field(None, max_length=100)
first_name: str | None = Field(None, max_length=100)
last_name: str | None = Field(None, max_length=100)
class TeamMemberInvite(TeamMemberBase):
"""Schema for inviting a team member."""
role_id: Optional[int] = Field(
role_id: int | None = Field(
None, description="Role ID to assign (for preset roles)"
)
role_name: Optional[str] = Field(
role_name: str | None = Field(
None, description="Role name (manager, staff, support, etc.)"
)
custom_permissions: Optional[List[str]] = Field(
custom_permissions: list[str] | None = Field(
None, description="Custom permissions (overrides role preset)"
)
@@ -112,8 +110,8 @@ class TeamMemberInvite(TeamMemberBase):
class TeamMemberUpdate(BaseModel):
"""Schema for updating a team member."""
role_id: Optional[int] = Field(None, description="New role ID")
is_active: Optional[bool] = Field(None, description="Active status")
role_id: int | None = Field(None, description="New role ID")
is_active: bool | None = Field(None, description="Active status")
class TeamMemberResponse(BaseModel):
@@ -122,13 +120,13 @@ class TeamMemberResponse(BaseModel):
id: int = Field(..., description="User ID")
email: EmailStr
username: str
first_name: Optional[str]
last_name: Optional[str]
first_name: str | None
last_name: str | None
full_name: str
user_type: str = Field(..., description="'owner' or 'member'")
role_name: str = Field(..., description="Role name")
role_id: Optional[int]
permissions: List[str] = Field(
role_id: int | None
permissions: list[str] = Field(
default_factory=list, description="User's permissions"
)
is_active: bool
@@ -136,8 +134,8 @@ class TeamMemberResponse(BaseModel):
invitation_pending: bool = Field(
default=False, description="True if invitation not yet accepted"
)
invited_at: Optional[datetime] = Field(None, description="When invitation was sent")
accepted_at: Optional[datetime] = Field(
invited_at: datetime | None = Field(None, description="When invitation was sent")
accepted_at: datetime | None = Field(
None, description="When invitation was accepted"
)
joined_at: datetime = Field(..., description="When user joined vendor")
@@ -149,7 +147,7 @@ class TeamMemberResponse(BaseModel):
class TeamMemberListResponse(BaseModel):
"""Schema for team member list response."""
members: List[TeamMemberResponse]
members: list[TeamMemberResponse]
total: int
active_count: int
pending_invitations: int
@@ -197,7 +195,7 @@ class InvitationResponse(BaseModel):
message: str
email: EmailStr
role: str
invitation_token: Optional[str] = Field(
invitation_token: str | None = Field(
None, description="Token (only returned in dev/test environments)"
)
invitation_sent: bool = Field(default=True)
@@ -239,7 +237,7 @@ class TeamStatistics(BaseModel):
class BulkRemoveRequest(BaseModel):
"""Schema for bulk removing team members."""
user_ids: List[int] = Field(
user_ids: list[int] = Field(
..., min_items=1, description="List of user IDs to remove"
)
@@ -249,7 +247,7 @@ class BulkRemoveResponse(BaseModel):
success_count: int
failed_count: int
errors: List[dict] = Field(default_factory=list)
errors: list[dict] = Field(default_factory=list)
# ============================================================================
@@ -260,7 +258,7 @@ class BulkRemoveResponse(BaseModel):
class PermissionCheckRequest(BaseModel):
"""Schema for checking permissions."""
permissions: List[str] = Field(..., min_items=1, description="Permissions to check")
permissions: list[str] = Field(..., min_items=1, description="Permissions to check")
class PermissionCheckResponse(BaseModel):
@@ -268,8 +266,8 @@ class PermissionCheckResponse(BaseModel):
has_all: bool = Field(..., description="True if user has all permissions")
has_any: bool = Field(..., description="True if user has any permission")
granted: List[str] = Field(default_factory=list, description="Permissions user has")
denied: List[str] = Field(
granted: list[str] = Field(default_factory=list, description="Permissions user has")
denied: list[str] = Field(
default_factory=list, description="Permissions user lacks"
)
@@ -277,10 +275,10 @@ class PermissionCheckResponse(BaseModel):
class UserPermissionsResponse(BaseModel):
"""Schema for user's permissions response."""
permissions: List[str] = Field(default_factory=list)
permissions: list[str] = Field(default_factory=list)
permission_count: int
is_owner: bool
role_name: Optional[str] = None
role_name: str | None = None
# ============================================================================
@@ -293,4 +291,4 @@ class TeamErrorResponse(BaseModel):
error_code: str
message: str
details: Optional[dict] = None
details: dict | None = None

View File

@@ -15,7 +15,7 @@ Schemas include:
import re
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any
from pydantic import BaseModel, ConfigDict, Field, field_validator
@@ -36,7 +36,7 @@ class VendorCreate(BaseModel):
name: str = Field(
..., description="Display name of the vendor", min_length=2, max_length=255
)
description: Optional[str] = Field(None, description="Vendor description")
description: str | None = Field(None, description="Vendor description")
# Owner Information (Creates User Account)
owner_email: str = Field(
@@ -45,21 +45,21 @@ class VendorCreate(BaseModel):
)
# Business Contact Information (Vendor Fields)
contact_email: Optional[str] = Field(
contact_email: str | None = Field(
None,
description="Public business contact email (defaults to owner_email if not provided)",
)
contact_phone: Optional[str] = Field(None, description="Contact phone number")
website: Optional[str] = Field(None, description="Website URL")
contact_phone: str | None = Field(None, description="Contact phone number")
website: str | None = Field(None, description="Website URL")
# Business Details
business_address: Optional[str] = Field(None, description="Business address")
tax_number: Optional[str] = Field(None, description="Tax/VAT number")
business_address: str | None = Field(None, description="Business address")
tax_number: str | None = Field(None, description="Tax/VAT number")
# Marketplace URLs (multi-language support)
letzshop_csv_url_fr: Optional[str] = Field(None, description="French CSV URL")
letzshop_csv_url_en: Optional[str] = Field(None, description="English CSV URL")
letzshop_csv_url_de: Optional[str] = Field(None, description="German CSV URL")
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("owner_email", "contact_email")
@classmethod
@@ -95,29 +95,29 @@ class VendorUpdate(BaseModel):
"""
# Basic Information
name: Optional[str] = Field(None, min_length=2, max_length=255)
description: Optional[str] = None
subdomain: Optional[str] = Field(None, min_length=2, max_length=100)
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)
# Business Contact Information (Vendor Fields)
contact_email: Optional[str] = Field(
contact_email: str | None = Field(
None, description="Public business contact email"
)
contact_phone: Optional[str] = None
website: Optional[str] = None
contact_phone: str | None = None
website: str | None = None
# Business Details
business_address: Optional[str] = None
tax_number: Optional[str] = None
business_address: str | None = None
tax_number: str | None = None
# Marketplace URLs
letzshop_csv_url_fr: Optional[str] = None
letzshop_csv_url_en: Optional[str] = None
letzshop_csv_url_de: Optional[str] = None
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: Optional[bool] = None
is_verified: Optional[bool] = None
is_active: bool | None = None
is_verified: bool | None = None
@field_validator("subdomain")
@classmethod
@@ -145,22 +145,22 @@ class VendorResponse(BaseModel):
vendor_code: str
subdomain: str
name: str
description: Optional[str]
description: str | None
owner_user_id: int
# Contact Information (Business)
contact_email: Optional[str]
contact_phone: Optional[str]
website: Optional[str]
contact_email: str | None
contact_phone: str | None
website: str | None
# Business Information
business_address: Optional[str]
tax_number: Optional[str]
business_address: str | None
tax_number: str | None
# Marketplace URLs
letzshop_csv_url_fr: Optional[str]
letzshop_csv_url_en: Optional[str]
letzshop_csv_url_de: Optional[str]
letzshop_csv_url_fr: str | None
letzshop_csv_url_en: str | None
letzshop_csv_url_de: str | None
# Status Flags
is_active: bool
@@ -196,13 +196,13 @@ class VendorCreateResponse(VendorDetailResponse):
temporary_password: str = Field(
..., description="Temporary password for owner (SHOWN ONLY ONCE)"
)
login_url: Optional[str] = Field(None, description="URL for vendor owner to login")
login_url: str | None = Field(None, description="URL for vendor owner to login")
class VendorListResponse(BaseModel):
"""Schema for paginated vendor list."""
vendors: List[VendorResponse]
vendors: list[VendorResponse]
total: int
skip: int
limit: int
@@ -237,7 +237,7 @@ class VendorTransferOwnership(BaseModel):
..., description="Must be true to confirm ownership transfer"
)
transfer_reason: Optional[str] = Field(
transfer_reason: str | None = Field(
None,
max_length=500,
description="Reason for ownership transfer (for audit logs)",
@@ -260,12 +260,12 @@ class VendorTransferOwnershipResponse(BaseModel):
vendor_code: str
vendor_name: str
old_owner: Dict[str, Any] = Field(
old_owner: dict[str, Any] = Field(
..., description="Information about the previous owner"
)
new_owner: Dict[str, Any] = Field(
new_owner: dict[str, Any] = Field(
..., description="Information about the new owner"
)
transferred_at: datetime
transfer_reason: Optional[str]
transfer_reason: str | None

View File

@@ -12,7 +12,6 @@ Schemas include:
import re
from datetime import datetime
from typing import Dict, List, Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
@@ -69,8 +68,8 @@ class VendorDomainCreate(BaseModel):
class VendorDomainUpdate(BaseModel):
"""Schema for updating vendor domain settings."""
is_primary: Optional[bool] = Field(None, description="Set as primary domain")
is_active: Optional[bool] = Field(None, description="Activate or deactivate domain")
is_primary: bool | None = Field(None, description="Set as primary domain")
is_active: bool | None = Field(None, description="Activate or deactivate domain")
model_config = ConfigDict(from_attributes=True)
@@ -87,9 +86,9 @@ class VendorDomainResponse(BaseModel):
is_active: bool
is_verified: bool
ssl_status: str
verification_token: Optional[str] = None
verified_at: Optional[datetime] = None
ssl_verified_at: Optional[datetime] = None
verification_token: str | None = None
verified_at: datetime | None = None
ssl_verified_at: datetime | None = None
created_at: datetime
updated_at: datetime
@@ -97,7 +96,7 @@ class VendorDomainResponse(BaseModel):
class VendorDomainListResponse(BaseModel):
"""Schema for paginated vendor domain list."""
domains: List[VendorDomainResponse]
domains: list[VendorDomainResponse]
total: int
@@ -106,9 +105,9 @@ class DomainVerificationInstructions(BaseModel):
domain: str
verification_token: str
instructions: Dict[str, str]
txt_record: Dict[str, str]
common_registrars: Dict[str, str]
instructions: dict[str, str]
txt_record: dict[str, str]
common_registrars: dict[str, str]
model_config = ConfigDict(from_attributes=True)

View File

@@ -3,7 +3,6 @@
Pydantic schemas for vendor theme operations.
"""
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
@@ -11,40 +10,40 @@ from pydantic import BaseModel, Field
class VendorThemeColors(BaseModel):
"""Color scheme for vendor theme."""
primary: Optional[str] = Field(None, description="Primary brand color")
secondary: Optional[str] = Field(None, description="Secondary color")
accent: Optional[str] = Field(None, description="Accent/CTA color")
background: Optional[str] = Field(None, description="Background color")
text: Optional[str] = Field(None, description="Text color")
border: Optional[str] = Field(None, description="Border color")
primary: str | None = Field(None, description="Primary brand color")
secondary: str | None = Field(None, description="Secondary color")
accent: str | None = Field(None, description="Accent/CTA color")
background: str | None = Field(None, description="Background color")
text: str | None = Field(None, description="Text color")
border: str | None = Field(None, description="Border color")
class VendorThemeFonts(BaseModel):
"""Typography settings for vendor theme."""
heading: Optional[str] = Field(None, description="Font for headings")
body: Optional[str] = Field(None, description="Font for body text")
heading: str | None = Field(None, description="Font for headings")
body: str | None = Field(None, description="Font for body text")
class VendorThemeBranding(BaseModel):
"""Branding assets for vendor theme."""
logo: Optional[str] = Field(None, description="Logo URL")
logo_dark: Optional[str] = Field(None, description="Dark mode logo URL")
favicon: Optional[str] = Field(None, description="Favicon URL")
banner: Optional[str] = Field(None, description="Banner image URL")
logo: str | None = Field(None, description="Logo URL")
logo_dark: str | None = Field(None, description="Dark mode logo URL")
favicon: str | None = Field(None, description="Favicon URL")
banner: str | None = Field(None, description="Banner image URL")
class VendorThemeLayout(BaseModel):
"""Layout settings for vendor theme."""
style: Optional[str] = Field(
style: str | None = Field(
None, description="Product layout style (grid, list, masonry)"
)
header: Optional[str] = Field(
header: str | None = Field(
None, description="Header style (fixed, static, transparent)"
)
product_card: Optional[str] = Field(
product_card: str | None = Field(
None, description="Product card style (modern, classic, minimal)"
)
@@ -52,15 +51,15 @@ class VendorThemeLayout(BaseModel):
class VendorThemeUpdate(BaseModel):
"""Schema for updating vendor theme (partial updates allowed)."""
theme_name: Optional[str] = Field(None, description="Theme preset name")
colors: Optional[Dict[str, str]] = Field(None, description="Color scheme")
fonts: Optional[Dict[str, str]] = Field(None, description="Font settings")
branding: Optional[Dict[str, Optional[str]]] = Field(
theme_name: str | None = Field(None, description="Theme preset name")
colors: dict[str, str] | None = Field(None, description="Color scheme")
fonts: dict[str, str] | None = Field(None, description="Font settings")
branding: dict[str, str | None] | None = Field(
None, description="Branding assets"
)
layout: Optional[Dict[str, str]] = Field(None, description="Layout settings")
custom_css: Optional[str] = Field(None, description="Custom CSS rules")
social_links: Optional[Dict[str, str]] = Field(
layout: dict[str, str] | None = Field(None, description="Layout settings")
custom_css: str | None = Field(None, description="Custom CSS rules")
social_links: dict[str, str] | None = Field(
None, description="Social media links"
)
@@ -69,15 +68,15 @@ class VendorThemeResponse(BaseModel):
"""Schema for vendor theme response."""
theme_name: str = Field(..., description="Theme name")
colors: Dict[str, str] = Field(..., description="Color scheme")
fonts: Dict[str, str] = Field(..., description="Font settings")
branding: Dict[str, Optional[str]] = Field(..., description="Branding assets")
layout: Dict[str, str] = Field(..., description="Layout settings")
social_links: Optional[Dict[str, str]] = Field(
colors: dict[str, str] = Field(..., description="Color scheme")
fonts: dict[str, str] = Field(..., description="Font settings")
branding: dict[str, str | None] = Field(..., description="Branding assets")
layout: dict[str, str] = Field(..., description="Layout settings")
social_links: dict[str, str] | None = Field(
default_factory=dict, description="Social links"
)
custom_css: Optional[str] = Field(None, description="Custom CSS")
css_variables: Optional[Dict[str, str]] = Field(
custom_css: str | None = Field(None, description="Custom CSS")
css_variables: dict[str, str] | None = Field(
None, description="CSS custom properties"
)
@@ -105,4 +104,4 @@ class ThemePresetResponse(BaseModel):
class ThemePresetListResponse(BaseModel):
"""List of available theme presets."""
presets: List[ThemePresetPreview] = Field(..., description="Available presets")
presets: list[ThemePresetPreview] = Field(..., description="Available presets")