Files
orion/models/database/order.py
Samir Boulahtit 448f01f82b feat: add Letzshop bidirectional order integration
Add complete Letzshop marketplace integration with:
- GraphQL client for order import and fulfillment operations
- Encrypted credential storage per vendor (Fernet encryption)
- Admin and vendor API endpoints for credentials management
- Order import, confirmation, rejection, and tracking
- Fulfillment queue and sync logging
- Comprehensive documentation and test coverage

New files:
- app/services/letzshop/ - GraphQL client and services
- app/utils/encryption.py - Fernet encryption utility
- models/database/letzshop.py - Database models
- models/schema/letzshop.py - Pydantic schemas
- app/api/v1/admin/letzshop.py - Admin API endpoints
- app/api/v1/vendor/letzshop.py - Vendor API endpoints
- docs/guides/letzshop-order-integration.md - Documentation

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

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

118 lines
3.7 KiB
Python

# models/database/order.py
from sqlalchemy import (
Boolean,
Column,
DateTime,
Float,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.dialects.sqlite import JSON
from sqlalchemy.orm import relationship
from app.core.database import Base
from models.database.base import TimestampMixin
class Order(Base, TimestampMixin):
"""Customer orders."""
__tablename__ = "orders"
id = Column(Integer, primary_key=True, index=True)
vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False, index=True)
customer_id = Column(
Integer, ForeignKey("customers.id"), nullable=False, index=True
)
order_number = Column(String, nullable=False, unique=True, index=True)
# Order channel/source
channel = Column(
String(50), default="direct", index=True
) # direct, letzshop, amazon, etc.
external_order_id = Column(
String(100), nullable=True, index=True
) # External order reference
external_channel_data = Column(JSON, nullable=True) # Channel-specific metadata
# Order status
status = Column(String, nullable=False, default="pending", index=True)
# pending, processing, shipped, delivered, cancelled, refunded
# Financial
subtotal = Column(Float, nullable=False)
tax_amount = Column(Float, default=0.0)
shipping_amount = Column(Float, default=0.0)
discount_amount = Column(Float, default=0.0)
total_amount = Column(Float, nullable=False)
currency = Column(String, default="EUR")
# Addresses (stored as IDs)
shipping_address_id = Column(
Integer, ForeignKey("customer_addresses.id"), nullable=False
)
billing_address_id = Column(
Integer, ForeignKey("customer_addresses.id"), nullable=False
)
# Shipping
shipping_method = Column(String, nullable=True)
tracking_number = Column(String, nullable=True)
# Notes
customer_notes = Column(Text, nullable=True)
internal_notes = Column(Text, nullable=True)
# Timestamps
paid_at = Column(DateTime, nullable=True)
shipped_at = Column(DateTime, nullable=True)
delivered_at = Column(DateTime, nullable=True)
cancelled_at = Column(DateTime, nullable=True)
# Relationships
vendor = relationship("Vendor")
customer = relationship("Customer", back_populates="orders")
items = relationship(
"OrderItem", back_populates="order", cascade="all, delete-orphan"
)
shipping_address = relationship(
"CustomerAddress", foreign_keys=[shipping_address_id]
)
billing_address = relationship("CustomerAddress", foreign_keys=[billing_address_id])
def __repr__(self):
return f"<Order(id={self.id}, order_number='{self.order_number}', status='{self.status}')>"
class OrderItem(Base, TimestampMixin):
"""Individual items in an order."""
__tablename__ = "order_items"
id = Column(Integer, primary_key=True, index=True)
order_id = Column(Integer, ForeignKey("orders.id"), nullable=False, index=True)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
# Product details at time of order (snapshot)
product_name = Column(String, nullable=False)
product_sku = Column(String, nullable=True)
quantity = Column(Integer, nullable=False)
unit_price = Column(Float, nullable=False)
total_price = Column(Float, nullable=False)
# Inventory tracking
inventory_reserved = Column(Boolean, default=False)
inventory_fulfilled = Column(Boolean, default=False)
# Relationships
order = relationship("Order", back_populates="items")
product = relationship("Product")
def __repr__(self):
return f"<OrderItem(id={self.id}, order_id={self.order_id}, product_id={self.product_id})>"