Move 39 documentation files from top-level docs/ into each module's docs/ folder, accessible via symlinks from docs/modules/. Create data-model.md files for 10 modules with full schema documentation. Replace originals with redirect stubs. Remove empty guide stubs. Modules migrated: tenancy, billing, loyalty, marketplace, orders, messaging, cms, catalog, inventory, hosting, prospecting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.1 KiB
Catalog Data Model
Entity relationships and database schema for the catalog module.
Entity Relationship Overview
Store 1──* Product 1──* ProductTranslation
│
├──* ProductMedia *──1 MediaFile
│
├──? MarketplaceProduct (source)
│
└──* Inventory (from inventory module)
Models
Product
Store-specific product with independent copy pattern from marketplace imports. All monetary values stored as integer cents.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
Integer | PK | Primary key |
store_id |
Integer | FK, not null | Store ownership |
marketplace_product_id |
Integer | FK, nullable | Optional marketplace source |
store_sku |
String | indexed | Store's internal SKU |
gtin |
String(50) | indexed | EAN/UPC barcode |
gtin_type |
String(20) | nullable | gtin13, gtin14, gtin12, gtin8, isbn13, isbn10 |
price_cents |
Integer | nullable | Gross price in cents |
sale_price_cents |
Integer | nullable | Sale price in cents |
currency |
String(3) | default "EUR" | Currency code |
brand |
String | nullable | Product brand |
condition |
String | nullable | Product condition |
availability |
String | nullable | Availability status |
primary_image_url |
String | nullable | Main product image URL |
additional_images |
JSON | nullable | Array of additional image URLs |
download_url |
String | nullable | Digital product download URL |
license_type |
String(50) | nullable | Digital product license |
tax_rate_percent |
Integer | not null, default 17 | VAT rate (LU: 0, 3, 8, 14, 17) |
supplier |
String(50) | nullable | codeswholesale, internal, etc. |
supplier_product_id |
String | nullable | Supplier's product reference |
cost_cents |
Integer | nullable | Cost to acquire in cents |
margin_percent_x100 |
Integer | nullable | Markup × 100 (2550 = 25.5%) |
is_digital |
Boolean | default False, indexed | Digital vs physical |
product_type |
String(20) | default "physical" | physical, digital, service, subscription |
is_featured |
Boolean | default False | Featured flag |
is_active |
Boolean | default True | Active flag |
display_order |
Integer | default 0 | Sort order |
min_quantity |
Integer | default 1 | Min purchase quantity |
max_quantity |
Integer | nullable | Max purchase quantity |
fulfillment_email_template |
String | nullable | Template for digital delivery |
created_at |
DateTime | tz-aware | Record creation time |
updated_at |
DateTime | tz-aware | Record update time |
Unique Constraint: (store_id, marketplace_product_id)
Composite Indexes: (store_id, is_active), (store_id, is_featured), (store_id, store_sku), (supplier, supplier_product_id)
Key Properties: price, sale_price, cost (euro converters), net_price_cents (gross minus VAT), vat_amount_cents, profit_cents, profit_margin_percent, total_inventory, available_inventory
ProductTranslation
Store-specific multilingual content with SEO fields. Independent copy from marketplace translations.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
Integer | PK | Primary key |
product_id |
Integer | FK, not null, cascade | Parent product |
language |
String(5) | not null | en, fr, de, lb |
title |
String | nullable | Product title |
description |
Text | nullable | Full description |
short_description |
String(500) | nullable | Abbreviated description |
meta_title |
String(70) | nullable | SEO title |
meta_description |
String(160) | nullable | SEO description |
url_slug |
String(255) | nullable | URL-friendly slug |
created_at |
DateTime | tz-aware | Record creation time |
updated_at |
DateTime | tz-aware | Record update time |
Unique Constraint: (product_id, language)
ProductMedia
Association between products and media files with usage tracking.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
Integer | PK | Primary key |
product_id |
Integer | FK, not null, cascade | Product reference |
media_id |
Integer | FK, not null, cascade | Media file reference |
usage_type |
String(50) | default "gallery" | main_image, gallery, variant, thumbnail, swatch |
display_order |
Integer | default 0 | Sort order |
variant_id |
Integer | nullable | Variant reference |
created_at |
DateTime | tz-aware | Record creation time |
updated_at |
DateTime | tz-aware | Record update time |
Unique Constraint: (product_id, media_id, usage_type)
Design Patterns
- Independent copy pattern: Products are copied from marketplace sources, not linked. Store-specific data diverges independently.
- Money as cents: All prices, costs, margins stored as integer cents
- Luxembourg VAT: Supports all LU rates (0%, 3%, 8%, 14%, 17%)
- Multi-type products: Physical, digital, service, subscription with type-specific fields
- SEO per language: Meta title and description in each translation