refactor: migrate schemas to canonical module locations
Migrate remaining legacy schemas to their respective modules: Marketplace module (app/modules/marketplace/schemas/): - letzshop.py: Letzshop credentials, orders, fulfillment, sync - onboarding.py: Vendor onboarding wizard schemas Catalog module (app/modules/catalog/schemas/): - product.py: ProductCreate, ProductUpdate, ProductResponse Payments module (app/modules/payments/schemas/): - payment.py: PaymentConfig, Stripe, transactions, balance Delete legacy files: - models/schema/letzshop.py - models/schema/onboarding.py - models/schema/product.py - models/schema/payment.py - models/schema/marketplace_product.py (re-export) - models/schema/marketplace_import_job.py (re-export) - models/schema/search.py (empty) Update imports across 19 files to use canonical locations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,9 @@ Note: Many schemas have been migrated to their respective modules:
|
||||
- Inventory schemas: app.modules.inventory.schemas
|
||||
- Message schemas: app.modules.messaging.schemas
|
||||
- Cart schemas: app.modules.cart.schemas
|
||||
- Marketplace schemas: app.modules.marketplace.schemas
|
||||
- Catalog/Product schemas: app.modules.catalog.schemas
|
||||
- Payment schemas: app.modules.payments.schemas
|
||||
"""
|
||||
|
||||
# Import API model modules that remain in legacy location
|
||||
@@ -14,9 +17,6 @@ from . import (
|
||||
auth,
|
||||
base,
|
||||
email,
|
||||
marketplace_import_job,
|
||||
marketplace_product,
|
||||
onboarding,
|
||||
vendor,
|
||||
)
|
||||
|
||||
@@ -27,8 +27,5 @@ __all__ = [
|
||||
"base",
|
||||
"auth",
|
||||
"email",
|
||||
"marketplace_product",
|
||||
"onboarding",
|
||||
"vendor",
|
||||
"marketplace_import_job",
|
||||
]
|
||||
|
||||
@@ -1,626 +0,0 @@
|
||||
# models/schema/letzshop.py
|
||||
"""
|
||||
Pydantic schemas for Letzshop marketplace integration.
|
||||
|
||||
Covers:
|
||||
- Vendor credentials management
|
||||
- Letzshop order import/sync
|
||||
- Fulfillment queue operations
|
||||
- Sync logs
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
# ============================================================================
|
||||
# Credentials Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopCredentialsCreate(BaseModel):
|
||||
"""Schema for creating/updating Letzshop credentials."""
|
||||
|
||||
api_key: str = Field(..., min_length=1, description="Letzshop API key")
|
||||
api_endpoint: str | None = Field(
|
||||
None,
|
||||
description="Custom API endpoint (defaults to https://letzshop.lu/graphql)",
|
||||
)
|
||||
auto_sync_enabled: bool = Field(False, description="Enable automatic order sync")
|
||||
sync_interval_minutes: int = Field(
|
||||
15, ge=5, le=1440, description="Sync interval in minutes (5-1440)"
|
||||
)
|
||||
test_mode_enabled: bool = Field(
|
||||
False, description="Test mode - disables API mutations"
|
||||
)
|
||||
default_carrier: str | None = Field(
|
||||
None, description="Default carrier (greco, colissimo, xpresslogistics)"
|
||||
)
|
||||
carrier_greco_label_url: str | None = Field(
|
||||
"https://dispatchweb.fr/Tracky/Home/", description="Greco label URL prefix"
|
||||
)
|
||||
carrier_colissimo_label_url: str | None = Field(
|
||||
None, description="Colissimo label URL prefix"
|
||||
)
|
||||
carrier_xpresslogistics_label_url: str | None = Field(
|
||||
None, description="XpressLogistics label URL prefix"
|
||||
)
|
||||
|
||||
|
||||
class LetzshopCredentialsUpdate(BaseModel):
|
||||
"""Schema for updating Letzshop credentials (partial update)."""
|
||||
|
||||
api_key: str | None = Field(None, min_length=1)
|
||||
api_endpoint: str | None = None
|
||||
auto_sync_enabled: bool | None = None
|
||||
sync_interval_minutes: int | None = Field(None, ge=5, le=1440)
|
||||
test_mode_enabled: bool | None = None
|
||||
default_carrier: str | None = None
|
||||
carrier_greco_label_url: str | None = None
|
||||
carrier_colissimo_label_url: str | None = None
|
||||
carrier_xpresslogistics_label_url: str | None = None
|
||||
|
||||
|
||||
class LetzshopCredentialsResponse(BaseModel):
|
||||
"""Schema for Letzshop credentials response (API key is masked)."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
api_key_masked: str = Field(..., description="Masked API key for display")
|
||||
api_endpoint: str
|
||||
auto_sync_enabled: bool
|
||||
sync_interval_minutes: int
|
||||
test_mode_enabled: bool = False
|
||||
default_carrier: str | None = None
|
||||
carrier_greco_label_url: str | None = None
|
||||
carrier_colissimo_label_url: str | None = None
|
||||
carrier_xpresslogistics_label_url: str | None = None
|
||||
last_sync_at: datetime | None
|
||||
last_sync_status: str | None
|
||||
last_sync_error: str | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class LetzshopCredentialsStatus(BaseModel):
|
||||
"""Schema for Letzshop connection status."""
|
||||
|
||||
is_configured: bool
|
||||
is_connected: bool
|
||||
last_sync_at: datetime | None
|
||||
last_sync_status: str | None
|
||||
auto_sync_enabled: bool
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Letzshop Order Schemas (using unified Order model)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopOrderItemResponse(BaseModel):
|
||||
"""Schema for order item in Letzshop order response."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
product_id: int
|
||||
product_name: str
|
||||
product_sku: str | None = None
|
||||
gtin: str | None = None
|
||||
gtin_type: str | None = None
|
||||
quantity: int
|
||||
unit_price: float
|
||||
total_price: float
|
||||
external_item_id: str | None = None # Letzshop inventory unit ID
|
||||
external_variant_id: str | None = None
|
||||
item_state: str | None = None # confirmed_available, confirmed_unavailable
|
||||
|
||||
|
||||
class LetzshopOrderResponse(BaseModel):
|
||||
"""Schema for Letzshop order response (from unified Order model)."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
vendor_name: str | None = None # For cross-vendor views
|
||||
order_number: str
|
||||
|
||||
# External references
|
||||
external_order_id: str | None = None
|
||||
external_shipment_id: str | None = None
|
||||
external_order_number: str | None = None
|
||||
|
||||
# Status
|
||||
status: str # pending, processing, shipped, delivered, cancelled
|
||||
|
||||
# Customer info
|
||||
customer_email: str
|
||||
customer_name: str # computed: customer_first_name + customer_last_name
|
||||
customer_locale: str | None = None
|
||||
|
||||
# Address info
|
||||
ship_country_iso: str
|
||||
bill_country_iso: str
|
||||
|
||||
# Financial
|
||||
total_amount: float
|
||||
currency: str = "EUR"
|
||||
|
||||
# Tracking
|
||||
tracking_number: str | None = None
|
||||
tracking_provider: str | None = None
|
||||
|
||||
# Timestamps
|
||||
order_date: datetime
|
||||
confirmed_at: datetime | None = None
|
||||
shipped_at: datetime | None = None
|
||||
cancelled_at: datetime | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
# Items (for list view, may be empty)
|
||||
items: list[LetzshopOrderItemResponse] = Field(default_factory=list)
|
||||
|
||||
|
||||
class LetzshopOrderDetailResponse(LetzshopOrderResponse):
|
||||
"""Schema for detailed Letzshop order response with all data."""
|
||||
|
||||
# Full customer snapshot
|
||||
customer_first_name: str
|
||||
customer_last_name: str
|
||||
customer_phone: str | None = None
|
||||
|
||||
# Full shipping address
|
||||
ship_first_name: str
|
||||
ship_last_name: str
|
||||
ship_company: str | None = None
|
||||
ship_address_line_1: str
|
||||
ship_address_line_2: str | None = None
|
||||
ship_city: str
|
||||
ship_postal_code: str
|
||||
|
||||
# Full billing address
|
||||
bill_first_name: str
|
||||
bill_last_name: str
|
||||
bill_company: str | None = None
|
||||
bill_address_line_1: str
|
||||
bill_address_line_2: str | None = None
|
||||
bill_city: str
|
||||
bill_postal_code: str
|
||||
|
||||
# Raw marketplace data
|
||||
external_data: dict[str, Any] | None = None
|
||||
|
||||
# Notes
|
||||
customer_notes: str | None = None
|
||||
internal_notes: str | None = None
|
||||
|
||||
|
||||
class LetzshopOrderStats(BaseModel):
|
||||
"""Schema for order statistics by status."""
|
||||
|
||||
pending: int = 0
|
||||
processing: int = 0
|
||||
shipped: int = 0
|
||||
delivered: int = 0
|
||||
cancelled: int = 0
|
||||
total: int = 0
|
||||
has_declined_items: int = 0 # Orders with at least one declined item
|
||||
|
||||
|
||||
class LetzshopOrderListResponse(BaseModel):
|
||||
"""Schema for paginated Letzshop order list."""
|
||||
|
||||
orders: list[LetzshopOrderResponse]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
stats: LetzshopOrderStats | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Fulfillment Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class FulfillmentConfirmRequest(BaseModel):
|
||||
"""Schema for confirming order fulfillment."""
|
||||
|
||||
inventory_unit_ids: list[str] = Field(
|
||||
..., min_length=1, description="List of inventory unit IDs to confirm"
|
||||
)
|
||||
|
||||
|
||||
class FulfillmentRejectRequest(BaseModel):
|
||||
"""Schema for rejecting order fulfillment."""
|
||||
|
||||
inventory_unit_ids: list[str] = Field(
|
||||
..., min_length=1, description="List of inventory unit IDs to reject"
|
||||
)
|
||||
reason: str | None = Field(None, max_length=500, description="Rejection reason")
|
||||
|
||||
|
||||
class FulfillmentTrackingRequest(BaseModel):
|
||||
"""Schema for setting tracking information."""
|
||||
|
||||
tracking_number: str = Field(..., min_length=1, max_length=100)
|
||||
tracking_carrier: str = Field(
|
||||
..., min_length=1, max_length=100, description="Carrier code (e.g., dhl, ups)"
|
||||
)
|
||||
|
||||
|
||||
class FulfillmentQueueItemResponse(BaseModel):
|
||||
"""Schema for fulfillment queue item response."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
order_id: int # FK to unified orders table
|
||||
operation: str
|
||||
payload: dict[str, Any]
|
||||
status: str
|
||||
attempts: int
|
||||
max_attempts: int
|
||||
last_attempt_at: datetime | None
|
||||
next_retry_at: datetime | None
|
||||
error_message: str | None
|
||||
completed_at: datetime | None
|
||||
response_data: dict[str, Any] | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class FulfillmentQueueListResponse(BaseModel):
|
||||
"""Schema for paginated fulfillment queue list."""
|
||||
|
||||
items: list[FulfillmentQueueItemResponse]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Sync Log Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopSyncLogResponse(BaseModel):
|
||||
"""Schema for Letzshop sync log response."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
operation_type: str
|
||||
direction: str
|
||||
status: str
|
||||
records_processed: int
|
||||
records_succeeded: int
|
||||
records_failed: int
|
||||
error_details: dict[str, Any] | None
|
||||
started_at: datetime
|
||||
completed_at: datetime | None
|
||||
duration_seconds: int | None
|
||||
triggered_by: str | None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class LetzshopSyncLogListResponse(BaseModel):
|
||||
"""Schema for paginated sync log list."""
|
||||
|
||||
logs: list[LetzshopSyncLogResponse]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Sync Trigger Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopSyncTriggerRequest(BaseModel):
|
||||
"""Schema for triggering a sync operation."""
|
||||
|
||||
operation: str = Field(
|
||||
"order_import",
|
||||
pattern="^(order_import|full_sync)$",
|
||||
description="Type of sync operation",
|
||||
)
|
||||
|
||||
|
||||
class LetzshopSyncTriggerResponse(BaseModel):
|
||||
"""Schema for sync trigger response."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
sync_log_id: int | None = None
|
||||
orders_imported: int = 0
|
||||
orders_updated: int = 0
|
||||
errors: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Connection Test Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopConnectionTestRequest(BaseModel):
|
||||
"""Schema for testing Letzshop connection."""
|
||||
|
||||
api_key: str = Field(..., min_length=1, description="API key to test")
|
||||
api_endpoint: str | None = Field(None, description="Custom endpoint to test")
|
||||
|
||||
|
||||
class LetzshopConnectionTestResponse(BaseModel):
|
||||
"""Schema for connection test response."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
response_time_ms: float | None = None
|
||||
error_details: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Generic Response Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopSuccessResponse(BaseModel):
|
||||
"""Generic success response for Letzshop operations."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
|
||||
|
||||
class FulfillmentOperationResponse(BaseModel):
|
||||
"""Response for fulfillment operations (confirm, reject, tracking)."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
confirmed_units: list[str] | None = None
|
||||
tracking_number: str | None = None
|
||||
tracking_carrier: str | None = None
|
||||
errors: list[str] | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Admin Overview Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopVendorOverview(BaseModel):
|
||||
"""Schema for vendor Letzshop integration overview (admin view)."""
|
||||
|
||||
vendor_id: int
|
||||
vendor_name: str
|
||||
vendor_code: str
|
||||
is_configured: bool
|
||||
auto_sync_enabled: bool
|
||||
last_sync_at: datetime | None
|
||||
last_sync_status: str | None
|
||||
pending_orders: int
|
||||
total_orders: int
|
||||
|
||||
|
||||
class LetzshopVendorListResponse(BaseModel):
|
||||
"""Schema for paginated vendor Letzshop overview list."""
|
||||
|
||||
vendors: list[LetzshopVendorOverview]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Jobs Schemas (Unified view of imports, exports, and syncs)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopJobItem(BaseModel):
|
||||
"""Schema for a unified job item (import, export, order sync, or historical import)."""
|
||||
|
||||
id: int
|
||||
type: str = Field(
|
||||
..., description="Job type: import, export, order_sync, or historical_import"
|
||||
)
|
||||
status: str = Field(..., description="Job status")
|
||||
created_at: datetime
|
||||
started_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
records_processed: int = 0
|
||||
records_succeeded: int = 0
|
||||
records_failed: int = 0
|
||||
# Vendor info
|
||||
vendor_id: int | None = Field(None, description="Vendor ID")
|
||||
vendor_name: str | None = Field(None, description="Vendor name")
|
||||
vendor_code: str | None = Field(None, description="Vendor code")
|
||||
# Historical import specific fields
|
||||
current_phase: str | None = Field(
|
||||
None, description="Current phase for historical imports"
|
||||
)
|
||||
error_message: str | None = Field(None, description="Error message if failed")
|
||||
error_details: dict[str, Any] | None = Field(
|
||||
None, description="Error details or export file info"
|
||||
)
|
||||
|
||||
|
||||
class LetzshopJobsListResponse(BaseModel):
|
||||
"""Schema for paginated jobs list."""
|
||||
|
||||
jobs: list[LetzshopJobItem]
|
||||
total: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Historical Import Job Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopHistoricalImportJobResponse(BaseModel):
|
||||
"""Schema for historical import job status (polling endpoint)."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
status: str # pending, fetching, processing, completed, failed
|
||||
current_phase: str | None = None # "confirmed" or "declined"
|
||||
|
||||
# Fetch progress
|
||||
current_page: int = 0
|
||||
total_pages: int | None = None
|
||||
shipments_fetched: int = 0
|
||||
|
||||
# Processing progress
|
||||
orders_processed: int = 0
|
||||
orders_imported: int = 0
|
||||
orders_updated: int = 0
|
||||
orders_skipped: int = 0
|
||||
|
||||
# EAN matching stats
|
||||
products_matched: int = 0
|
||||
products_not_found: int = 0
|
||||
|
||||
# Phase-specific stats (when complete)
|
||||
confirmed_stats: dict[str, Any] | None = None
|
||||
declined_stats: dict[str, Any] | None = None
|
||||
|
||||
# Error handling
|
||||
error_message: str | None = None
|
||||
|
||||
# Timing
|
||||
started_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class LetzshopHistoricalImportStartResponse(BaseModel):
|
||||
"""Schema for starting a historical import job."""
|
||||
|
||||
job_id: int
|
||||
status: str = "pending"
|
||||
message: str = "Historical import job started"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Vendor Directory Schemas (Letzshop Marketplace Cache)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class LetzshopCachedVendorItem(BaseModel):
|
||||
"""Schema for a cached Letzshop vendor in list view."""
|
||||
|
||||
id: int
|
||||
letzshop_id: str
|
||||
slug: str
|
||||
name: str
|
||||
company_name: str | None = None
|
||||
email: str | None = None
|
||||
phone: str | None = None
|
||||
website: str | None = None
|
||||
city: str | None = None
|
||||
categories: list[str] = []
|
||||
is_active: bool = True
|
||||
is_claimed: bool = False
|
||||
claimed_by_vendor_id: int | None = None
|
||||
last_synced_at: datetime | None = None
|
||||
letzshop_url: str
|
||||
|
||||
|
||||
class LetzshopCachedVendorDetail(BaseModel):
|
||||
"""Schema for detailed cached Letzshop vendor."""
|
||||
|
||||
id: int
|
||||
letzshop_id: str
|
||||
slug: str
|
||||
name: str
|
||||
company_name: str | None = None
|
||||
description_en: str | None = None
|
||||
description_fr: str | None = None
|
||||
description_de: str | None = None
|
||||
email: str | None = None
|
||||
phone: str | None = None
|
||||
fax: str | None = None
|
||||
website: str | None = None
|
||||
street: str | None = None
|
||||
street_number: str | None = None
|
||||
city: str | None = None
|
||||
zipcode: str | None = None
|
||||
country_iso: str | None = None
|
||||
latitude: str | None = None
|
||||
longitude: str | None = None
|
||||
categories: list[str] = []
|
||||
background_image_url: str | None = None
|
||||
social_media_links: list[str] = []
|
||||
opening_hours_en: str | None = None
|
||||
opening_hours_fr: str | None = None
|
||||
opening_hours_de: str | None = None
|
||||
representative_name: str | None = None
|
||||
representative_title: str | None = None
|
||||
is_active: bool = True
|
||||
is_claimed: bool = False
|
||||
claimed_by_vendor_id: int | None = None
|
||||
claimed_at: datetime | None = None
|
||||
last_synced_at: datetime | None = None
|
||||
letzshop_url: str
|
||||
|
||||
|
||||
class LetzshopVendorDirectoryStats(BaseModel):
|
||||
"""Schema for vendor directory cache statistics."""
|
||||
|
||||
total_vendors: int = 0
|
||||
active_vendors: int = 0
|
||||
claimed_vendors: int = 0
|
||||
unclaimed_vendors: int = 0
|
||||
unique_cities: int = 0
|
||||
last_synced_at: str | None = None
|
||||
|
||||
|
||||
class LetzshopVendorDirectoryStatsResponse(BaseModel):
|
||||
"""Response schema for vendor directory stats endpoint."""
|
||||
|
||||
success: bool = True
|
||||
stats: LetzshopVendorDirectoryStats
|
||||
|
||||
|
||||
class LetzshopCachedVendorListResponse(BaseModel):
|
||||
"""Response schema for vendor directory list endpoint."""
|
||||
|
||||
success: bool = True
|
||||
vendors: list[LetzshopCachedVendorItem]
|
||||
total: int
|
||||
page: int
|
||||
limit: int
|
||||
has_more: bool
|
||||
|
||||
|
||||
class LetzshopCachedVendorDetailResponse(BaseModel):
|
||||
"""Response schema for vendor directory detail endpoint."""
|
||||
|
||||
success: bool = True
|
||||
vendor: LetzshopCachedVendorDetail
|
||||
|
||||
|
||||
class LetzshopVendorDirectorySyncResponse(BaseModel):
|
||||
"""Response schema for vendor directory sync trigger."""
|
||||
|
||||
success: bool = True
|
||||
message: str
|
||||
task_id: str | None = None
|
||||
mode: str = "celery"
|
||||
|
||||
|
||||
class LetzshopCreateVendorFromCacheResponse(BaseModel):
|
||||
"""Response schema for creating vendor from Letzshop cache."""
|
||||
|
||||
success: bool = True
|
||||
message: str
|
||||
vendor: dict[str, Any] | None = None
|
||||
letzshop_vendor_slug: str
|
||||
@@ -1,39 +0,0 @@
|
||||
# models/schema/marketplace_import_job.py
|
||||
"""
|
||||
Legacy location for marketplace import job schemas.
|
||||
|
||||
MIGRATED: All schemas have been moved to app.modules.marketplace.schemas.marketplace_import_job.
|
||||
|
||||
New location:
|
||||
from app.modules.marketplace.schemas import (
|
||||
MarketplaceImportJobRequest,
|
||||
MarketplaceImportJobResponse,
|
||||
)
|
||||
|
||||
This file re-exports from the new location for backward compatibility.
|
||||
"""
|
||||
|
||||
# Re-export everything from the new canonical location
|
||||
from app.modules.marketplace.schemas.marketplace_import_job import (
|
||||
MarketplaceImportJobRequest,
|
||||
AdminMarketplaceImportJobRequest,
|
||||
MarketplaceImportJobResponse,
|
||||
MarketplaceImportJobListResponse,
|
||||
MarketplaceImportErrorResponse,
|
||||
MarketplaceImportErrorListResponse,
|
||||
AdminMarketplaceImportJobResponse,
|
||||
AdminMarketplaceImportJobListResponse,
|
||||
MarketplaceImportJobStatusUpdate,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"MarketplaceImportJobRequest",
|
||||
"AdminMarketplaceImportJobRequest",
|
||||
"MarketplaceImportJobResponse",
|
||||
"MarketplaceImportJobListResponse",
|
||||
"MarketplaceImportErrorResponse",
|
||||
"MarketplaceImportErrorListResponse",
|
||||
"AdminMarketplaceImportJobResponse",
|
||||
"AdminMarketplaceImportJobListResponse",
|
||||
"MarketplaceImportJobStatusUpdate",
|
||||
]
|
||||
@@ -1,45 +0,0 @@
|
||||
# models/schema/marketplace_product.py
|
||||
"""
|
||||
Legacy location for marketplace product schemas.
|
||||
|
||||
MIGRATED: All schemas have been moved to app.modules.marketplace.schemas.marketplace_product.
|
||||
|
||||
New location:
|
||||
from app.modules.marketplace.schemas import (
|
||||
MarketplaceProductCreate,
|
||||
MarketplaceProductResponse,
|
||||
MarketplaceProductTranslationSchema,
|
||||
)
|
||||
|
||||
This file re-exports from the new location for backward compatibility.
|
||||
"""
|
||||
|
||||
# Re-export everything from the new canonical location
|
||||
from app.modules.marketplace.schemas.marketplace_product import (
|
||||
# Translation schemas
|
||||
MarketplaceProductTranslationSchema,
|
||||
# Base schemas
|
||||
MarketplaceProductBase,
|
||||
# CRUD schemas
|
||||
MarketplaceProductCreate,
|
||||
MarketplaceProductUpdate,
|
||||
# Response schemas
|
||||
MarketplaceProductResponse,
|
||||
MarketplaceProductListResponse,
|
||||
MarketplaceProductDetailResponse,
|
||||
# Import schemas
|
||||
MarketplaceImportRequest,
|
||||
MarketplaceImportResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"MarketplaceProductTranslationSchema",
|
||||
"MarketplaceProductBase",
|
||||
"MarketplaceProductCreate",
|
||||
"MarketplaceProductUpdate",
|
||||
"MarketplaceProductResponse",
|
||||
"MarketplaceProductListResponse",
|
||||
"MarketplaceProductDetailResponse",
|
||||
"MarketplaceImportRequest",
|
||||
"MarketplaceImportResponse",
|
||||
]
|
||||
@@ -1,291 +0,0 @@
|
||||
# models/schema/onboarding.py
|
||||
"""
|
||||
Pydantic schemas for Vendor Onboarding operations.
|
||||
|
||||
Schemas include:
|
||||
- OnboardingStatusResponse: Current onboarding status with all step states
|
||||
- CompanyProfileRequest/Response: Step 1 - Company profile data
|
||||
- LetzshopApiConfigRequest/Response: Step 2 - API configuration
|
||||
- ProductImportConfigRequest/Response: Step 3 - CSV URL configuration
|
||||
- OrderSyncTriggerResponse: Step 4 - Job trigger response
|
||||
- OrderSyncProgressResponse: Step 4 - Progress polling response
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# STEP STATUS MODELS
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class StepStatus(BaseModel):
|
||||
"""Status for a single onboarding step."""
|
||||
|
||||
completed: bool = False
|
||||
completed_at: datetime | None = None
|
||||
|
||||
|
||||
class CompanyProfileStepStatus(StepStatus):
|
||||
"""Step 1 status with saved data."""
|
||||
|
||||
data: dict | None = None
|
||||
|
||||
|
||||
class LetzshopApiStepStatus(StepStatus):
|
||||
"""Step 2 status with connection verification."""
|
||||
|
||||
connection_verified: bool = False
|
||||
|
||||
|
||||
class ProductImportStepStatus(StepStatus):
|
||||
"""Step 3 status with CSV URL flag."""
|
||||
|
||||
csv_url_set: bool = False
|
||||
|
||||
|
||||
class OrderSyncStepStatus(StepStatus):
|
||||
"""Step 4 status with job tracking."""
|
||||
|
||||
job_id: int | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ONBOARDING STATUS RESPONSE
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class OnboardingStatusResponse(BaseModel):
|
||||
"""Full onboarding status with all step information."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
status: str # not_started, in_progress, completed, skipped
|
||||
current_step: str # company_profile, letzshop_api, product_import, order_sync
|
||||
|
||||
# Step statuses
|
||||
company_profile: CompanyProfileStepStatus
|
||||
letzshop_api: LetzshopApiStepStatus
|
||||
product_import: ProductImportStepStatus
|
||||
order_sync: OrderSyncStepStatus
|
||||
|
||||
# Progress tracking
|
||||
completion_percentage: int
|
||||
completed_steps_count: int
|
||||
total_steps: int = 4
|
||||
|
||||
# Completion info
|
||||
is_completed: bool
|
||||
started_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
|
||||
# Admin override info
|
||||
skipped_by_admin: bool = False
|
||||
skipped_at: datetime | None = None
|
||||
skipped_reason: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# STEP 1: COMPANY PROFILE
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class CompanyProfileRequest(BaseModel):
|
||||
"""Request to save company profile during onboarding Step 1."""
|
||||
|
||||
# Company name is already set during signup, but can be updated
|
||||
company_name: str | None = Field(None, min_length=2, max_length=255)
|
||||
|
||||
# Vendor/brand name
|
||||
brand_name: str | None = Field(None, min_length=2, max_length=255)
|
||||
description: str | None = Field(None, max_length=2000)
|
||||
|
||||
# Contact information
|
||||
contact_email: str | None = Field(None, max_length=255)
|
||||
contact_phone: str | None = Field(None, max_length=50)
|
||||
website: str | None = Field(None, max_length=255)
|
||||
business_address: str | None = Field(None, max_length=500)
|
||||
tax_number: str | None = Field(None, max_length=100)
|
||||
|
||||
# Language preferences
|
||||
default_language: str = Field("fr", pattern="^(en|fr|de|lb)$")
|
||||
dashboard_language: str = Field("fr", pattern="^(en|fr|de|lb)$")
|
||||
|
||||
|
||||
class CompanyProfileResponse(BaseModel):
|
||||
"""Response after saving company profile."""
|
||||
|
||||
success: bool
|
||||
step_completed: bool
|
||||
next_step: str | None = None
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# STEP 2: LETZSHOP API CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class LetzshopApiConfigRequest(BaseModel):
|
||||
"""Request to configure Letzshop API credentials."""
|
||||
|
||||
api_key: str = Field(..., min_length=10, description="Letzshop API key")
|
||||
shop_slug: str = Field(
|
||||
...,
|
||||
min_length=2,
|
||||
max_length=100,
|
||||
description="Letzshop shop URL slug (e.g., 'my-shop')",
|
||||
)
|
||||
vendor_id: str | None = Field(
|
||||
None,
|
||||
max_length=100,
|
||||
description="Letzshop vendor ID (optional, auto-detected if not provided)",
|
||||
)
|
||||
|
||||
|
||||
class LetzshopApiTestRequest(BaseModel):
|
||||
"""Request to test Letzshop API connection."""
|
||||
|
||||
api_key: str = Field(..., min_length=10)
|
||||
shop_slug: str = Field(..., min_length=2, max_length=100)
|
||||
|
||||
|
||||
class LetzshopApiTestResponse(BaseModel):
|
||||
"""Response from Letzshop API connection test."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
vendor_name: str | None = None
|
||||
vendor_id: str | None = None
|
||||
shop_slug: str | None = None
|
||||
|
||||
|
||||
class LetzshopApiConfigResponse(BaseModel):
|
||||
"""Response after saving Letzshop API configuration."""
|
||||
|
||||
success: bool
|
||||
step_completed: bool
|
||||
next_step: str | None = None
|
||||
message: str | None = None
|
||||
connection_verified: bool = False
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# STEP 3: PRODUCT & ORDER IMPORT CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ProductImportConfigRequest(BaseModel):
|
||||
"""Request to configure product import settings."""
|
||||
|
||||
# CSV feed URLs for each language
|
||||
csv_url_fr: str | None = Field(None, max_length=500)
|
||||
csv_url_en: str | None = Field(None, max_length=500)
|
||||
csv_url_de: str | None = Field(None, max_length=500)
|
||||
|
||||
# Letzshop feed settings
|
||||
default_tax_rate: int = Field(
|
||||
17,
|
||||
ge=0,
|
||||
le=17,
|
||||
description="Default VAT rate: 0, 3, 8, 14, or 17",
|
||||
)
|
||||
delivery_method: str = Field(
|
||||
"package_delivery",
|
||||
description="Delivery method: nationwide, package_delivery, self_collect",
|
||||
)
|
||||
preorder_days: int = Field(1, ge=0, le=30)
|
||||
|
||||
|
||||
class ProductImportConfigResponse(BaseModel):
|
||||
"""Response after saving product import configuration."""
|
||||
|
||||
success: bool
|
||||
step_completed: bool
|
||||
next_step: str | None = None
|
||||
message: str | None = None
|
||||
csv_urls_configured: int = 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# STEP 4: ORDER SYNC
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class OrderSyncTriggerRequest(BaseModel):
|
||||
"""Request to trigger historical order import."""
|
||||
|
||||
# How far back to import orders (days)
|
||||
days_back: int = Field(90, ge=1, le=365, description="Days of order history to import")
|
||||
include_products: bool = Field(True, description="Also import products from Letzshop")
|
||||
|
||||
|
||||
class OrderSyncTriggerResponse(BaseModel):
|
||||
"""Response after triggering order sync job."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
job_id: int | None = None
|
||||
estimated_duration_minutes: int | None = None
|
||||
|
||||
|
||||
class OrderSyncProgressResponse(BaseModel):
|
||||
"""Response for order sync progress polling."""
|
||||
|
||||
job_id: int
|
||||
status: str # pending, running, completed, failed
|
||||
progress_percentage: int = 0
|
||||
current_phase: str | None = None # products, orders, finalizing
|
||||
|
||||
# Counts
|
||||
orders_imported: int = 0
|
||||
orders_total: int | None = None
|
||||
products_imported: int = 0
|
||||
|
||||
# Timing
|
||||
started_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
estimated_remaining_seconds: int | None = None
|
||||
|
||||
# Error info if failed
|
||||
error_message: str | None = None
|
||||
|
||||
|
||||
class OrderSyncCompleteRequest(BaseModel):
|
||||
"""Request to mark order sync as complete."""
|
||||
|
||||
job_id: int
|
||||
|
||||
|
||||
class OrderSyncCompleteResponse(BaseModel):
|
||||
"""Response after completing order sync step."""
|
||||
|
||||
success: bool
|
||||
step_completed: bool
|
||||
onboarding_completed: bool = False
|
||||
message: str | None = None
|
||||
redirect_url: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ADMIN SKIP
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class OnboardingSkipRequest(BaseModel):
|
||||
"""Request to skip onboarding (admin only)."""
|
||||
|
||||
reason: str = Field(..., min_length=10, max_length=500)
|
||||
|
||||
|
||||
class OnboardingSkipResponse(BaseModel):
|
||||
"""Response after skipping onboarding."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
vendor_id: int
|
||||
skipped_at: datetime
|
||||
@@ -1,167 +0,0 @@
|
||||
# models/schema/payment.py
|
||||
"""
|
||||
Payment Pydantic schemas for API validation and responses.
|
||||
|
||||
This module provides schemas for:
|
||||
- Payment configuration
|
||||
- Stripe integration
|
||||
- Payment methods
|
||||
- Transactions and balance
|
||||
- Refunds
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# ============================================================================
|
||||
# PAYMENT CONFIGURATION SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class PaymentConfigResponse(BaseModel):
|
||||
"""Response for payment configuration."""
|
||||
|
||||
payment_gateway: str | None = None
|
||||
accepted_methods: list[str] = []
|
||||
currency: str = "EUR"
|
||||
stripe_connected: bool = False
|
||||
stripe_account_id: str | None = None
|
||||
paypal_connected: bool = False
|
||||
message: str | None = None
|
||||
|
||||
|
||||
class PaymentConfigUpdate(BaseModel):
|
||||
"""Request model for updating payment configuration."""
|
||||
|
||||
payment_gateway: str | None = Field(None, max_length=50)
|
||||
accepted_methods: list[str] | None = None
|
||||
currency: str | None = Field(None, max_length=3)
|
||||
|
||||
|
||||
class PaymentConfigUpdateResponse(BaseModel):
|
||||
"""Response for payment configuration update."""
|
||||
|
||||
success: bool = False
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# STRIPE INTEGRATION SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class StripeConnectRequest(BaseModel):
|
||||
"""Request model for connecting Stripe account."""
|
||||
|
||||
authorization_code: str | None = None
|
||||
state: str | None = None
|
||||
|
||||
|
||||
class StripeConnectResponse(BaseModel):
|
||||
"""Response for Stripe connection."""
|
||||
|
||||
connected: bool = False
|
||||
stripe_account_id: str | None = None
|
||||
message: str | None = None
|
||||
|
||||
|
||||
class StripeDisconnectResponse(BaseModel):
|
||||
"""Response for Stripe disconnection."""
|
||||
|
||||
disconnected: bool = False
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# PAYMENT METHODS SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class PaymentMethodInfo(BaseModel):
|
||||
"""Information about a payment method."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
type: str # credit_card, paypal, bank_transfer, etc.
|
||||
enabled: bool = True
|
||||
icon: str | None = None
|
||||
|
||||
|
||||
class PaymentMethodsResponse(BaseModel):
|
||||
"""Response for payment methods listing."""
|
||||
|
||||
methods: list[PaymentMethodInfo] = []
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# TRANSACTION SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class TransactionInfo(BaseModel):
|
||||
"""Information about a payment transaction."""
|
||||
|
||||
id: int
|
||||
order_id: int | None = None
|
||||
amount: float
|
||||
currency: str = "EUR"
|
||||
status: str # pending, completed, failed, refunded
|
||||
payment_method: str | None = None
|
||||
customer_email: str | None = None
|
||||
created_at: datetime
|
||||
completed_at: datetime | None = None
|
||||
metadata: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class TransactionsResponse(BaseModel):
|
||||
"""Response for payment transactions listing."""
|
||||
|
||||
transactions: list[TransactionInfo] = []
|
||||
total: int = 0
|
||||
skip: int = 0
|
||||
limit: int = 50
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# BALANCE SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class PaymentBalanceResponse(BaseModel):
|
||||
"""Response for payment balance information."""
|
||||
|
||||
available_balance: float = 0.0
|
||||
pending_balance: float = 0.0
|
||||
currency: str = "EUR"
|
||||
next_payout_date: datetime | None = None
|
||||
last_payout_date: datetime | None = None
|
||||
last_payout_amount: float | None = None
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# REFUND SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class RefundRequest(BaseModel):
|
||||
"""Request model for processing a refund."""
|
||||
|
||||
amount: float | None = Field(
|
||||
None, gt=0, description="Partial refund amount, or None for full refund"
|
||||
)
|
||||
reason: str | None = Field(None, max_length=500)
|
||||
|
||||
|
||||
class RefundResponse(BaseModel):
|
||||
"""Response for refund operation."""
|
||||
|
||||
refund_id: int | None = None
|
||||
payment_id: int | None = None
|
||||
amount: float | None = None
|
||||
status: str | None = None # pending, completed, failed
|
||||
message: str | None = None
|
||||
@@ -1,87 +0,0 @@
|
||||
# models/schema/product.py
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.modules.inventory.schemas import InventoryLocationResponse
|
||||
from models.schema.marketplace_product import MarketplaceProductResponse
|
||||
|
||||
|
||||
class ProductCreate(BaseModel):
|
||||
marketplace_product_id: int = Field(
|
||||
..., description="MarketplaceProduct ID to add to vendor catalog"
|
||||
)
|
||||
vendor_sku: str | None = Field(None, description="Vendor's internal SKU")
|
||||
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: int | None = Field(None, ge=1)
|
||||
|
||||
|
||||
class ProductUpdate(BaseModel):
|
||||
vendor_sku: 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):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
vendor_id: int
|
||||
marketplace_product: MarketplaceProductResponse
|
||||
vendor_sku: 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: int | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
# Include inventory summary
|
||||
total_inventory: int | None = None
|
||||
available_inventory: int | None = None
|
||||
|
||||
|
||||
class ProductDetailResponse(ProductResponse):
|
||||
"""Product with full inventory details."""
|
||||
|
||||
inventory_locations: list[InventoryLocationResponse] = []
|
||||
|
||||
|
||||
class ProductListResponse(BaseModel):
|
||||
products: list[ProductResponse]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
class ProductDeleteResponse(BaseModel):
|
||||
"""Response for product deletion."""
|
||||
|
||||
message: str
|
||||
|
||||
|
||||
class ProductToggleResponse(BaseModel):
|
||||
"""Response for product toggle operations (active/featured)."""
|
||||
|
||||
message: str
|
||||
is_active: bool | None = None
|
||||
is_featured: bool | None = None
|
||||
@@ -1 +0,0 @@
|
||||
# Search models
|
||||
Reference in New Issue
Block a user