feat: enhance Letzshop order import with EAN matching and stats
- Add historical order import with pagination support - Add customer_locale, shipping_country_iso, billing_country_iso columns - Add gtin/gtin_type columns to Product table for EAN matching - Fix order stats to count all orders server-side (not just visible page) - Add GraphQL introspection script with tracking workaround tests - Enrich inventory units with EAN, MPN, SKU, product name - Add LetzshopOrderStats schema for proper status counts Migrations: - a9a86cef6cca: Add locale and country fields to letzshop_orders - cb88bc9b5f86: Add gtin columns to products table 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -361,6 +361,9 @@ def list_vendor_letzshop_orders(
|
||||
sync_status=sync_status,
|
||||
)
|
||||
|
||||
# Get order stats for all statuses
|
||||
stats = order_service.get_order_stats(vendor_id)
|
||||
|
||||
return LetzshopOrderListResponse(
|
||||
orders=[
|
||||
LetzshopOrderResponse(
|
||||
@@ -392,6 +395,7 @@ def list_vendor_letzshop_orders(
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
stats=stats,
|
||||
)
|
||||
|
||||
|
||||
@@ -430,6 +434,10 @@ def trigger_vendor_sync(
|
||||
try:
|
||||
with creds_service.create_client(vendor_id) as client:
|
||||
shipments = client.get_unconfirmed_shipments()
|
||||
logger.info(
|
||||
f"Letzshop sync for vendor {vendor_id}: "
|
||||
f"fetched {len(shipments)} unconfirmed shipments from API"
|
||||
)
|
||||
|
||||
orders_imported = 0
|
||||
orders_updated = 0
|
||||
@@ -524,3 +532,118 @@ def list_vendor_letzshop_jobs(
|
||||
jobs = [LetzshopJobItem(**job) for job in jobs_data]
|
||||
|
||||
return LetzshopJobsListResponse(jobs=jobs, total=total)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Historical Import
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.post(
|
||||
"/vendors/{vendor_id}/import-history",
|
||||
)
|
||||
def import_historical_orders(
|
||||
vendor_id: int = Path(..., description="Vendor ID"),
|
||||
state: str = Query("confirmed", description="Shipment state to import"),
|
||||
max_pages: int | None = Query(None, ge=1, le=100, description="Max pages to fetch"),
|
||||
match_products: bool = Query(True, description="Match EANs to local products"),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Import historical orders from Letzshop.
|
||||
|
||||
Fetches all shipments with the specified state (default: confirmed)
|
||||
and imports them into the database. Supports pagination and EAN matching.
|
||||
|
||||
Returns statistics on imported/updated/skipped orders and product matching.
|
||||
"""
|
||||
order_service = get_order_service(db)
|
||||
creds_service = get_credentials_service(db)
|
||||
|
||||
try:
|
||||
vendor = order_service.get_vendor_or_raise(vendor_id)
|
||||
except VendorNotFoundError:
|
||||
raise ResourceNotFoundException("Vendor", str(vendor_id))
|
||||
|
||||
# Verify credentials exist
|
||||
try:
|
||||
creds_service.get_credentials_or_raise(vendor_id)
|
||||
except CredentialsNotFoundError:
|
||||
raise ValidationException(
|
||||
f"Letzshop credentials not configured for vendor {vendor.name}"
|
||||
)
|
||||
|
||||
# Fetch all shipments with pagination
|
||||
try:
|
||||
with creds_service.create_client(vendor_id) as client:
|
||||
logger.info(
|
||||
f"Starting historical import for vendor {vendor_id}, state={state}, max_pages={max_pages}"
|
||||
)
|
||||
|
||||
shipments = client.get_all_shipments_paginated(
|
||||
state=state,
|
||||
page_size=50,
|
||||
max_pages=max_pages,
|
||||
)
|
||||
|
||||
logger.info(f"Fetched {len(shipments)} {state} shipments from Letzshop")
|
||||
|
||||
# Import shipments
|
||||
stats = order_service.import_historical_shipments(
|
||||
vendor_id=vendor_id,
|
||||
shipments=shipments,
|
||||
match_products=match_products,
|
||||
)
|
||||
|
||||
db.commit()
|
||||
|
||||
# Update sync status
|
||||
creds_service.update_sync_status(
|
||||
vendor_id,
|
||||
"success",
|
||||
None,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Historical import completed: {stats['imported']} imported, "
|
||||
f"{stats['updated']} updated, {stats['skipped']} skipped"
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Historical import completed: {stats['imported']} imported, {stats['updated']} updated",
|
||||
"statistics": stats,
|
||||
}
|
||||
|
||||
except LetzshopClientError as e:
|
||||
creds_service.update_sync_status(vendor_id, "failed", str(e))
|
||||
raise ValidationException(f"Letzshop API error: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/vendors/{vendor_id}/import-summary",
|
||||
)
|
||||
def get_import_summary(
|
||||
vendor_id: int = Path(..., description="Vendor ID"),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Get summary statistics for imported Letzshop orders.
|
||||
|
||||
Returns total orders, unique customers, and breakdowns by state/locale/country.
|
||||
"""
|
||||
order_service = get_order_service(db)
|
||||
|
||||
try:
|
||||
order_service.get_vendor_or_raise(vendor_id)
|
||||
except VendorNotFoundError:
|
||||
raise ResourceNotFoundException("Vendor", str(vendor_id))
|
||||
|
||||
summary = order_service.get_historical_import_summary(vendor_id)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"summary": summary,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user