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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user