Files
orion/docs/implementation/unified-order-view.md
Samir Boulahtit 2e6f8fdb8a feat: update frontend for unified order model
Update all frontend templates and JavaScript to use new unified Order model:
- Orders tab: use status field, processing/cancelled values, items array
- Order detail: use snapshot fields, items array, tracking_provider
- JavaScript: update API params (status vs sync_status), orderStats fields
- Tracking modal: use tracking_provider instead of tracking_carrier
- Order items modal: use items array with item_state field

All status mappings:
- pending → pending (unconfirmed)
- processing → confirmed (at least one item available)
- cancelled → declined (all items unavailable)
- shipped → shipped (with tracking)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 21:38:02 +01:00

8.7 KiB

Unified Order Schema Implementation

Overview

This document describes the unified order schema that consolidates all order types (direct and marketplace) into a single orders table with snapshotted customer and address data.

Design Decision: Option B - Single Unified Table

After analyzing the gap between internal orders and Letzshop orders, we chose Option B: Full Import to Order Table with the following key principles:

  1. Single orders table for all channels (direct, letzshop, future marketplaces)
  2. Customer/address snapshots preserved at order time (not just FK references)
  3. Products must exist in catalog - GTIN lookup errors trigger investigation
  4. Inactive customers created for marketplace imports until they register on storefront
  5. No separate letzshop_orders table - eliminates sync issues

Schema Design

Order Table

The orders table now includes:

orders
├── Identity
│   ├── id (PK)
│   ├── vendor_id (FK → vendors)
│   ├── customer_id (FK → customers)
│   └── order_number (unique)
│
├── Channel/Source
│   ├── channel (direct | letzshop)
│   ├── external_order_id
│   ├── external_shipment_id
│   ├── external_order_number
│   └── external_data (JSON - raw marketplace data)
│
├── Status
│   └── status (pending | processing | shipped | delivered | cancelled | refunded)
│
├── Financials
│   ├── subtotal (nullable for marketplace)
│   ├── tax_amount
│   ├── shipping_amount
│   ├── discount_amount
│   ├── total_amount
│   └── currency
│
├── Customer Snapshot
│   ├── customer_first_name
│   ├── customer_last_name
│   ├── customer_email
│   ├── customer_phone
│   └── customer_locale
│
├── Shipping Address Snapshot
│   ├── ship_first_name
│   ├── ship_last_name
│   ├── ship_company
│   ├── ship_address_line_1
│   ├── ship_address_line_2
│   ├── ship_city
│   ├── ship_postal_code
│   └── ship_country_iso
│
├── Billing Address Snapshot
│   ├── bill_first_name
│   ├── bill_last_name
│   ├── bill_company
│   ├── bill_address_line_1
│   ├── bill_address_line_2
│   ├── bill_city
│   ├── bill_postal_code
│   └── bill_country_iso
│
├── Tracking
│   ├── shipping_method
│   ├── tracking_number
│   └── tracking_provider
│
├── Notes
│   ├── customer_notes
│   └── internal_notes
│
└── Timestamps
    ├── order_date (when customer placed order)
    ├── confirmed_at
    ├── shipped_at
    ├── delivered_at
    ├── cancelled_at
    ├── created_at
    └── updated_at

OrderItem Table

The order_items table includes:

order_items
├── Identity
│   ├── id (PK)
│   ├── order_id (FK → orders)
│   └── product_id (FK → products, NOT NULL)
│
├── Product Snapshot
│   ├── product_name
│   ├── product_sku
│   ├── gtin
│   └── gtin_type (ean13, upc, isbn, etc.)
│
├── Pricing
│   ├── quantity
│   ├── unit_price
│   └── total_price
│
├── External References
│   ├── external_item_id (Letzshop inventory unit ID)
│   └── external_variant_id
│
├── Item State (marketplace confirmation)
│   └── item_state (confirmed_available | confirmed_unavailable)
│
└── Inventory
    ├── inventory_reserved
    └── inventory_fulfilled

Status Mapping

Letzshop State Order Status Description
unconfirmed pending Order received, awaiting confirmation
confirmed processing Items confirmed, being prepared
confirmed + tracking shipped Shipped with tracking info
declined cancelled All items declined

Customer Handling

When importing marketplace orders:

  1. Look up customer by (vendor_id, email)
  2. If not found, create with is_active=False
  3. Customer becomes active when they register on storefront
  4. Customer info is always snapshotted in order (regardless of customer record)

This ensures:

  • Customer history is preserved even if customer info changes
  • Marketplace customers can later claim their order history
  • No data loss if customer record is modified

Shipping Workflows

Scenario 1: Letzshop Auto-Shipping

When using Letzshop's shipping service:

  1. Order confirmed → status = processing
  2. Letzshop auto-creates shipment with their carrier
  3. Operator picks & packs
  4. Operator clicks "Retrieve Shipping Info"
  5. App fetches tracking from Letzshop API
  6. Order updated → status = shipped

Scenario 2: Vendor Own Shipping

When vendor uses their own carrier:

  1. Order confirmed → status = processing
  2. Operator picks & packs with own carrier
  3. Operator enters tracking info in app
  4. App sends tracking to Letzshop API
  5. Order updated → status = shipped

Removed: LetzshopOrder Table

The letzshop_orders table has been removed. All data now goes directly into the unified orders table with channel = 'letzshop'.

Migration of Existing References

  • LetzshopFulfillmentQueue.letzshop_order_idorder_id (FK to orders)
  • LetzshopSyncLog - unchanged (no order reference)
  • LetzshopHistoricalImportJob - unchanged (no order reference)

Files Modified

File Changes
models/database/order.py Complete rewrite with snapshots
models/database/letzshop.py Removed LetzshopOrder, updated LetzshopFulfillmentQueue
models/schema/order.py Updated schemas for new structure
models/schema/letzshop.py Updated schemas for unified Order model
app/services/order_service.py Unified service with create_letzshop_order()
app/services/letzshop/order_service.py Updated to use unified Order model
app/api/v1/admin/letzshop.py Updated endpoints for unified model
alembic/versions/c1d2e3f4a5b6_unified_order_schema.py Migration

API Endpoints

All Letzshop order endpoints now use the unified Order model:

Endpoint Description
GET /admin/letzshop/vendors/{id}/orders List orders with channel='letzshop' filter
GET /admin/letzshop/orders/{id} Get order detail with items
POST /admin/letzshop/vendors/{id}/orders/{id}/confirm Confirm items via external_item_id
POST /admin/letzshop/vendors/{id}/orders/{id}/reject Decline items via external_item_id
POST /admin/letzshop/vendors/{id}/orders/{id}/items/{item_id}/confirm Confirm single item
POST /admin/letzshop/vendors/{id}/orders/{id}/items/{item_id}/decline Decline single item

Order Number Format

Channel Format Example
Direct ORD-{vendor_id}-{date}-{random} ORD-1-20251219-A1B2C3
Letzshop LS-{vendor_id}-{letzshop_order_number} LS-1-ORD-123456

Error Handling

Product Not Found by GTIN

When importing a Letzshop order, if a product cannot be found by its GTIN:

raise ValidationException(
    f"Product not found for GTIN {gtin}. "
    f"Please ensure the product catalog is in sync."
)

This is intentional - the Letzshop catalog is sourced from the vendor catalog, so missing products indicate a sync issue that must be investigated.

Future Considerations

Performance at Scale

As the orders table grows, consider:

  1. Partitioning by order_date or vendor_id
  2. Archiving old orders to separate tables
  3. Read replicas for reporting queries
  4. Materialized views for dashboard statistics

Additional Marketplaces

The schema supports additional channels:

channel = Column(String(50))  # direct, letzshop, amazon, ebay, etc.

Each marketplace would use:

  • external_order_id - Marketplace order ID
  • external_shipment_id - Marketplace shipment ID
  • external_order_number - Display order number
  • external_data - Raw marketplace data (JSON)

Implementation Status

  • Order model with snapshots
  • OrderItem model with GTIN fields
  • LetzshopFulfillmentQueue updated
  • LetzshopOrder removed
  • Database migration created
  • Order schemas updated
  • Unified order service created
  • Letzshop order service updated
  • Letzshop schemas updated
  • API endpoints updated
  • Frontend updated
    • Orders tab template (status badges, filters, table)
    • Order detail page (snapshots, items, tracking)
    • JavaScript (API params, response handling)
    • Tracking modal (tracking_provider field)
    • Order items modal (items array, item_state)