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:
2026-01-30 15:16:49 +01:00
parent 0c63f387aa
commit 1ef50893a1
31 changed files with 255 additions and 125 deletions

View File

@@ -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",
]

View File

@@ -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

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
# Search models