feat(modules): create cart, catalog, and checkout e-commerce modules

Phase 3 of storefront restructure plan - create dedicated modules for
e-commerce functionality:

- cart: Shopping cart management with storefront API routes
  - CartItem model with cents-based pricing
  - CartService for cart operations
  - Storefront routes for cart CRUD operations

- catalog: Product catalog browsing for customers
  - CatalogService for public product queries
  - Storefront routes for product listing/search/details

- checkout: Order creation from cart (placeholder)
  - CheckoutService stub for future cart-to-order conversion
  - Schemas for checkout flow

These modules separate e-commerce concerns from core platform
concerns (customer auth), enabling non-commerce platforms.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 22:53:35 +01:00
parent 3e86d4b58b
commit 0845555413
32 changed files with 1748 additions and 3 deletions

View File

@@ -0,0 +1,14 @@
# app/modules/checkout/schemas/__init__.py
"""Checkout module schemas."""
from app.modules.checkout.schemas.checkout import (
CheckoutRequest,
CheckoutResponse,
CheckoutSessionResponse,
)
__all__ = [
"CheckoutRequest",
"CheckoutResponse",
"CheckoutSessionResponse",
]

View File

@@ -0,0 +1,54 @@
# app/modules/checkout/schemas/checkout.py
"""
Pydantic schemas for checkout operations.
These schemas handle the conversion of a shopping cart into an order.
"""
from pydantic import BaseModel, Field
class ShippingAddress(BaseModel):
"""Shipping address for checkout."""
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
address_line_1: str = Field(..., min_length=1, max_length=255)
address_line_2: str | None = Field(None, max_length=255)
city: str = Field(..., min_length=1, max_length=100)
postal_code: str = Field(..., min_length=1, max_length=20)
country: str = Field(..., min_length=2, max_length=2) # ISO 3166-1 alpha-2
phone: str | None = Field(None, max_length=20)
class CheckoutRequest(BaseModel):
"""Request to initiate checkout."""
session_id: str = Field(..., description="Cart session ID")
shipping_address: ShippingAddress = Field(..., description="Shipping address")
billing_same_as_shipping: bool = Field(
True, description="Use shipping address for billing"
)
billing_address: ShippingAddress | None = Field(
None, description="Billing address (if different from shipping)"
)
customer_email: str = Field(..., description="Customer email for order confirmation")
customer_note: str | None = Field(None, description="Optional customer note")
class CheckoutSessionResponse(BaseModel):
"""Response for checkout session creation."""
checkout_session_id: str = Field(..., description="Checkout session ID")
cart_total: float = Field(..., description="Cart total in euros")
item_count: int = Field(..., description="Number of items in cart")
requires_payment: bool = Field(..., description="Whether payment is required")
class CheckoutResponse(BaseModel):
"""Response for completed checkout."""
order_id: int = Field(..., description="Created order ID")
order_number: str = Field(..., description="Human-readable order number")
total: float = Field(..., description="Order total in euros")
message: str = Field(..., description="Confirmation message")