refactor: clean up legacy models and migrate remaining schemas
Delete empty stub files from models/database/: - audit.py, backup.py, configuration.py, monitoring.py - notification.py, payment.py, search.py, task.py Delete re-export files: - models/database/subscription.py → app.modules.billing.models - models/database/architecture_scan.py → app.modules.dev_tools.models - models/database/test_run.py → app.modules.dev_tools.models - models/schema/subscription.py → app.modules.billing.schemas - models/schema/marketplace.py (empty) - models/schema/monitoring.py (empty) Migrate schemas to canonical module locations: - billing.py → app/modules/billing/schemas/ - vendor_product.py → app/modules/catalog/schemas/ - homepage_sections.py → app/modules/cms/schemas/ Keep as CORE (framework-level, used everywhere): - models/schema/: admin, auth, base, company, email, image, media, team, vendor* - models/database/: admin*, base, company, email, feature, media, platform*, user, vendor* Update 30+ files to use canonical import locations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,23 @@ from app.modules.catalog.schemas.product import (
|
||||
ProductDeleteResponse,
|
||||
ProductToggleResponse,
|
||||
)
|
||||
from app.modules.catalog.schemas.vendor_product import (
|
||||
# List/Detail schemas
|
||||
VendorProductListItem,
|
||||
VendorProductListResponse,
|
||||
VendorProductStats,
|
||||
VendorProductDetail,
|
||||
# Catalog vendor schemas
|
||||
CatalogVendor,
|
||||
CatalogVendorsResponse,
|
||||
# CRUD schemas
|
||||
TranslationUpdate,
|
||||
VendorProductCreate,
|
||||
VendorDirectProductCreate,
|
||||
VendorProductUpdate,
|
||||
VendorProductCreateResponse,
|
||||
RemoveProductResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Catalog browsing schemas (storefront)
|
||||
@@ -29,4 +46,17 @@ __all__ = [
|
||||
"ProductListResponse",
|
||||
"ProductDeleteResponse",
|
||||
"ProductToggleResponse",
|
||||
# Vendor Product schemas (admin)
|
||||
"VendorProductListItem",
|
||||
"VendorProductListResponse",
|
||||
"VendorProductStats",
|
||||
"VendorProductDetail",
|
||||
"CatalogVendor",
|
||||
"CatalogVendorsResponse",
|
||||
"TranslationUpdate",
|
||||
"VendorProductCreate",
|
||||
"VendorDirectProductCreate",
|
||||
"VendorProductUpdate",
|
||||
"VendorProductCreateResponse",
|
||||
"RemoveProductResponse",
|
||||
]
|
||||
|
||||
247
app/modules/catalog/schemas/vendor_product.py
Normal file
247
app/modules/catalog/schemas/vendor_product.py
Normal file
@@ -0,0 +1,247 @@
|
||||
# app/modules/catalog/schemas/vendor_product.py
|
||||
"""
|
||||
Pydantic schemas for vendor product catalog operations.
|
||||
|
||||
Used by admin vendor product endpoints for:
|
||||
- Product listing and filtering
|
||||
- Product statistics
|
||||
- Product detail views
|
||||
- Catalog vendor listings
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class VendorProductListItem(BaseModel):
|
||||
"""Product item for vendor catalog list view."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
vendor_name: str | None = None
|
||||
vendor_code: str | None = None
|
||||
marketplace_product_id: int | None = None
|
||||
vendor_sku: str | None = None
|
||||
title: str | None = None
|
||||
brand: str | None = None
|
||||
price: float | None = None
|
||||
currency: str | None = None
|
||||
effective_price: float | None = None
|
||||
effective_currency: str | None = None
|
||||
is_active: bool | None = None
|
||||
is_featured: bool | None = None
|
||||
is_digital: bool | None = None
|
||||
image_url: str | None = None
|
||||
source_marketplace: str | None = None
|
||||
source_vendor: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class VendorProductListResponse(BaseModel):
|
||||
"""Paginated vendor product list response."""
|
||||
|
||||
products: list[VendorProductListItem]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
class VendorProductStats(BaseModel):
|
||||
"""Vendor product statistics."""
|
||||
|
||||
total: int
|
||||
active: int
|
||||
inactive: int
|
||||
featured: int
|
||||
digital: int
|
||||
physical: int
|
||||
by_vendor: dict[str, int]
|
||||
|
||||
|
||||
class CatalogVendor(BaseModel):
|
||||
"""Vendor with products in catalog."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
vendor_code: str
|
||||
|
||||
|
||||
class CatalogVendorsResponse(BaseModel):
|
||||
"""Response for catalog vendors list."""
|
||||
|
||||
vendors: list[CatalogVendor]
|
||||
|
||||
|
||||
class VendorProductDetail(BaseModel):
|
||||
"""Detailed vendor product information.
|
||||
|
||||
Products are independent entities - all fields are populated at creation.
|
||||
Source values are kept for "view original source" comparison only.
|
||||
"""
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
vendor_name: str | None = None
|
||||
vendor_code: str | None = None
|
||||
marketplace_product_id: int | None = None # Optional for direct product creation
|
||||
vendor_sku: str | None = None
|
||||
# Product identifiers
|
||||
gtin: str | None = None
|
||||
gtin_type: str | None = None # ean13, ean8, upc, isbn, etc.
|
||||
# Product fields with source comparison
|
||||
price: float | None = None
|
||||
price_cents: int | None = None
|
||||
price_source: float | None = None # For "view original source" feature
|
||||
sale_price: float | None = None
|
||||
sale_price_cents: int | None = None
|
||||
sale_price_source: float | None = None
|
||||
currency: str | None = None
|
||||
currency_source: str | None = None
|
||||
brand: str | None = None
|
||||
brand_source: str | None = None
|
||||
condition: str | None = None
|
||||
condition_source: str | None = None
|
||||
availability: str | None = None
|
||||
availability_source: str | None = None
|
||||
primary_image_url: str | None = None
|
||||
primary_image_url_source: str | None = None
|
||||
additional_images: list[str] | None = None
|
||||
is_digital: bool | None = None
|
||||
product_type: str | None = None
|
||||
# Vendor-specific fields
|
||||
is_featured: bool | None = None
|
||||
is_active: bool | None = None
|
||||
display_order: int | None = None
|
||||
min_quantity: int | None = None
|
||||
max_quantity: int | None = None
|
||||
# Supplier tracking
|
||||
supplier: str | None = None
|
||||
supplier_product_id: str | None = None
|
||||
cost: float | None = None # What vendor pays to acquire product
|
||||
margin_percent: float | None = None
|
||||
# Tax/profit info
|
||||
tax_rate_percent: int | None = None
|
||||
net_price: float | None = None
|
||||
vat_amount: float | None = None
|
||||
profit: float | None = None
|
||||
profit_margin_percent: float | None = None
|
||||
# Digital fulfillment
|
||||
download_url: str | None = None
|
||||
license_type: str | None = None
|
||||
fulfillment_email_template: str | None = None
|
||||
# Source info
|
||||
source_marketplace: str | None = None
|
||||
source_vendor: str | None = None
|
||||
source_gtin: str | None = None
|
||||
source_sku: str | None = None
|
||||
# Translations
|
||||
marketplace_translations: dict | None = None
|
||||
vendor_translations: dict | None = None
|
||||
# Convenience fields for UI display
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
image_url: str | None = None # Alias for primary_image_url
|
||||
# Timestamps
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class RemoveProductResponse(BaseModel):
|
||||
"""Response from product removal."""
|
||||
|
||||
message: str
|
||||
|
||||
|
||||
class TranslationUpdate(BaseModel):
|
||||
"""Translation data for a single language."""
|
||||
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class VendorProductCreate(BaseModel):
|
||||
"""Schema for creating a vendor product (admin use - includes vendor_id)."""
|
||||
|
||||
vendor_id: int
|
||||
|
||||
# Translations by language code (en, fr, de, lu)
|
||||
translations: dict[str, TranslationUpdate] | None = None
|
||||
|
||||
# Product identifiers
|
||||
brand: str | None = None
|
||||
vendor_sku: str | None = None
|
||||
gtin: str | None = None
|
||||
gtin_type: str | None = None # ean13, ean8, upc, isbn
|
||||
|
||||
# Pricing
|
||||
price: float | None = None
|
||||
sale_price: float | None = None
|
||||
currency: str = "EUR"
|
||||
tax_rate_percent: int | None = 17 # Default Luxembourg VAT
|
||||
availability: str | None = None
|
||||
|
||||
# Images
|
||||
primary_image_url: str | None = None
|
||||
additional_images: list[str] | None = None
|
||||
|
||||
# Status
|
||||
is_active: bool = True
|
||||
is_featured: bool = False
|
||||
is_digital: bool = False
|
||||
|
||||
|
||||
class VendorDirectProductCreate(BaseModel):
|
||||
"""Schema for vendor direct product creation (vendor_id from JWT token)."""
|
||||
|
||||
title: str
|
||||
brand: str | None = None
|
||||
vendor_sku: str | None = None
|
||||
gtin: str | None = None
|
||||
price: float | None = None
|
||||
currency: str = "EUR"
|
||||
availability: str | None = None
|
||||
is_active: bool = True
|
||||
is_featured: bool = False
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class VendorProductUpdate(BaseModel):
|
||||
"""Schema for updating a vendor product."""
|
||||
|
||||
# Translations by language code (en, fr, de, lu)
|
||||
translations: dict[str, TranslationUpdate] | None = None
|
||||
|
||||
# Product identifiers
|
||||
brand: str | None = None
|
||||
vendor_sku: str | None = None
|
||||
gtin: str | None = None
|
||||
gtin_type: str | None = None # ean13, ean8, upc, isbn, etc.
|
||||
|
||||
# Pricing
|
||||
price: float | None = None # Price incl. VAT in euros
|
||||
sale_price: float | None = None # Optional sale price
|
||||
currency: str | None = None
|
||||
tax_rate_percent: int | None = None # 3, 8, 14, 17
|
||||
availability: str | None = None # in_stock, out_of_stock, preorder, backorder
|
||||
|
||||
# Status
|
||||
is_digital: bool | None = None
|
||||
is_active: bool | None = None
|
||||
is_featured: bool | None = None
|
||||
|
||||
# Images
|
||||
primary_image_url: str | None = None
|
||||
additional_images: list[str] | None = None
|
||||
|
||||
# Optional supplier info
|
||||
supplier: str | None = None
|
||||
cost: float | None = None # Cost in euros
|
||||
|
||||
|
||||
class VendorProductCreateResponse(BaseModel):
|
||||
"""Response from product creation."""
|
||||
|
||||
id: int
|
||||
message: str
|
||||
Reference in New Issue
Block a user