refactor: migrate templates and static files to self-contained modules

Templates Migration:
- Migrate admin templates to modules (tenancy, billing, monitoring, marketplace, etc.)
- Migrate vendor templates to modules (tenancy, billing, orders, messaging, etc.)
- Migrate storefront templates to modules (catalog, customers, orders, cart, checkout, cms)
- Migrate public templates to modules (billing, marketplace, cms)
- Keep shared templates in app/templates/ (base.html, errors/, partials/, macros/)
- Migrate letzshop partials to marketplace module

Static Files Migration:
- Migrate admin JS to modules: tenancy (23 files), core (5 files), monitoring (1 file)
- Migrate vendor JS to modules: tenancy (4 files), core (2 files)
- Migrate shared JS: vendor-selector.js to core, media-picker.js to cms
- Migrate storefront JS: storefront-layout.js to core
- Keep framework JS in static/ (api-client, utils, money, icons, log-config, lib/)
- Update all template references to use module_static paths

Naming Consistency:
- Rename static/platform/ to static/public/
- Rename app/templates/platform/ to app/templates/public/
- Update all extends and static references

Documentation:
- Update module-system.md with shared templates documentation
- Update frontend-structure.md with new module JS organization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 14:34:16 +01:00
parent 843703258f
commit 4e28d91a78
542 changed files with 11603 additions and 9037 deletions

View File

@@ -0,0 +1,129 @@
# Documentation Consolidation Plan
## Overview
Many working documents (session notes, migration plans, proposals) now represent **implemented features**. This plan consolidates them into proper platform documentation.
## Document Categories
### 1. Implemented → Merge into Architecture/Feature Docs
These documents describe features that are now implemented and should be merged into proper documentation.
| Working Document | Target Location | Action |
|------------------|-----------------|--------|
| `proposals/SESSION_NOTE_2026-01-25_modular-platform-architecture.md` | `architecture/module-system.md` | Merge relevant content, archive original |
| `proposals/SESSION_NOTE_2026-01-26_self-contained-modules.md` | `architecture/module-system.md` | Merge relevant content, archive original |
| `proposals/SESSION_NOTE_2026-01-27_module-reclassification.md` | `architecture/module-system.md` | Merge relevant content, archive original |
| `proposals/SESSION_NOTE_2026-01-28_module-config-migrations.md` | `architecture/module-system.md` | Merge relevant content, archive original |
| `proposals/SESSION_NOTE_2026-01-30_self-contained-module-routes.md` | `architecture/module-system.md` | Merge relevant content, archive original |
| `proposals/SESSION_NOTE_2026-01-31_tenancy-module-consolidation.md` | `architecture/tenancy-module-migration.md` | Merge, archive original |
| `proposals/multi-platform-cms-architecture.md` | `features/content-management-system.md` | Merge as "How CMS Works" section |
| `proposals/multi-platform-cms-architecture-implementation-plan.md` | Archive | Implementation complete |
| `proposals/section-based-homepage-plan.md` | `features/platform-homepage.md` | Merge as technical details |
| `proposals/module-migration-plan.md` | Archive | Migration complete |
| `migration/language-i18n-implementation.md` | `architecture/language-i18n.md` | Merge (may already exist) |
| `migration/multi-marketplace-product-architecture.md` | `architecture/marketplace-integration.md` | Merge product sync details |
| `migration/vendor-operations-expansion.md` | `architecture/company-vendor-management.md` | Merge operations info |
| `migration/vendor-contact-inheritance.md` | `architecture/company-vendor-management.md` | Merge contact inheritance |
| `migration/product-migration-database-changes.md` | Archive | Migration complete |
| `migration/makefile-refactoring-complete.md` | Archive | Refactoring complete |
| `migration/tailwind-migration-plan.md` | Archive or `development/tailwind-css.md` | Verify if complete |
### 2. Keep as Development Guides
These should remain as development reference but may need cleanup.
| Document | Location | Action |
|----------|----------|--------|
| `migration/database-migrations.md` | Keep | Update if needed |
| `migration/module-autodiscovery-migration.md` | Keep | Already updated, serves as history |
| `migration/svc-006-migration-plan.md` | `development/coding-standards.md` | Merge db.commit pattern |
### 3. Future/Unimplemented → Keep in Proposals
| Document | Status | Action |
|----------|--------|--------|
| `proposals/loyalty-program-analysis.md` | Future feature | Keep as proposal |
| `proposals/loyalty-phase2-interfaces-plan.md` | Future feature | Keep as proposal |
| `proposals/PLAN_storefront-module-restructure.md` | Evaluate status | Keep or archive |
| `proposals/humble-orbiting-otter.md` | Claude plan file | Archive |
## Target Documentation Structure
After consolidation, the docs should have:
```
docs/
├── architecture/
│ ├── module-system.md ← Comprehensive module docs (merged SESSION_NOTEs)
│ ├── multi-tenant.md ← Platform/Company/Vendor architecture
│ ├── language-i18n.md ← i18n implementation
│ ├── marketplace-integration.md ← Letzshop sync, product architecture
│ ├── company-vendor-management.md ← Vendor operations, contact inheritance
│ └── ...
├── features/
│ ├── content-management-system.md ← CMS with multi-platform details
│ ├── platform-homepage.md ← Section-based homepage
│ ├── subscription-billing.md ← Billing features
│ └── ...
├── development/
│ ├── creating-modules.md ← How to create new modules
│ ├── coding-standards.md ← db.commit pattern, etc.
│ ├── database-migrations.md ← Migration guide
│ └── migration/ ← Historical migration docs (reference)
│ └── module-autodiscovery-migration.md
├── proposals/ ← Future features only
│ ├── loyalty-program-analysis.md
│ └── loyalty-phase2-interfaces-plan.md
└── archive/ ← Completed plans (optional)
├── SESSION_NOTE_*.md
└── *-implementation-plan.md
```
## Consolidation Steps
### Phase 1: Module System Documentation
1. Review all SESSION_NOTE files for module system
2. Extract key decisions and final architecture
3. Update `architecture/module-system.md` with:
- Clear module classification (core/optional/internal)
- Route auto-discovery pattern
- Entity locations (routes, services, models, etc.)
4. Archive SESSION_NOTE files
### Phase 2: CMS & Homepage Documentation
1. Merge CMS architecture into `features/content-management-system.md`
2. Merge homepage sections into `features/platform-homepage.md`
3. Archive implementation plans
### Phase 3: Vendor & Marketplace Documentation
1. Update `architecture/company-vendor-management.md` with:
- Contact inheritance
- Vendor operations
2. Update `architecture/marketplace-integration.md` with:
- Multi-marketplace product architecture
- Sync patterns
### Phase 4: Development Guides
1. Create/update `development/coding-standards.md` with db.commit pattern
2. Verify Tailwind migration status, archive if complete
3. Clean up migration folder
### Phase 5: Archive
1. Create `docs/archive/` folder (or just delete completed plans)
2. Move completed implementation plans
3. Move SESSION_NOTE files
## Priority Order
1. **High**: Module system (most referenced, core architecture)
2. **High**: CMS/Homepage (user-facing features)
3. **Medium**: Vendor/Marketplace (operational)
4. **Low**: Archive cleanup
## Notes
- Keep `migration/module-autodiscovery-migration.md` as historical reference
- SESSION_NOTEs contain valuable context but are verbose for reference docs
- Focus on "what is" not "what was planned" in final docs

View File

@@ -1,344 +0,0 @@
# Implementation Plan: Storefront Module Restructure
## Overview
Restructure the platform to properly separate **core platform concerns** (customer auth/identity) from **e-commerce concerns** (cart, checkout, catalog), and rename "shop" to "storefront" throughout.
## Current Issues
1. **API routes import database models directly** - violates layered architecture
2. **No architecture rule enforces this** - validator doesn't catch it
3. **"shop" naming is misleading** - not all platforms sell items
4. **Customer auth is bundled with e-commerce** - should be core/platform concern
---
## Phase 1: Add Missing Architecture Rule
### Task 1.1: Add API-007 rule to prevent direct model imports
**File:** `.architecture-rules/api.yaml`
Add new rule:
```yaml
- id: "API-007"
name: "API endpoints must NOT import database models directly"
severity: "error"
description: |
API endpoints should import from services, not directly from database models.
This enforces the layered architecture: Routes → Services → Models.
The only exception is for type hints in FastAPI dependency injection,
which should use schemas or services that return the appropriate types.
WRONG:
from models.database.customer import Customer
from models.database.order import Order
RIGHT:
from app.modules.customers.services import customer_service
from models.schema.customer import CustomerResponse
pattern:
file_pattern: "app/api/**/*.py"
anti_patterns:
- "from models\\.database\\."
- "from app\\.modules\\..*\\.models\\."
```
### Task 1.2: Update validator to check this rule
**File:** `scripts/validate_architecture.py`
Add validation for API-007.
---
## Phase 2: Rename "shop" to "storefront"
### Task 2.1: Rename API directory
```
app/api/v1/shop/ → app/api/v1/storefront/
```
### Task 2.2: Update all imports referencing shop
- Update router registrations in `app/api/v1/__init__.py`
- Update any references in middleware
- Update documentation
### Task 2.3: Rename routes/pages if applicable
```
app/routes/shop_pages.py → app/routes/storefront_pages.py
```
---
## Phase 3: Create New E-commerce Modules
### Task 3.1: Create `cart` module
```
app/modules/cart/
├── __init__.py
├── definition.py
├── config.py
├── exceptions.py
├── models/
│ ├── __init__.py
│ └── cart.py # Cart, CartItem models (if persistent)
├── schemas/
│ ├── __init__.py
│ └── cart.py # Move from models/schema/cart.py
├── services/
│ ├── __init__.py
│ └── cart_service.py # Move from app/services/cart_service.py
└── routes/
└── api/
├── __init__.py
└── storefront.py # Cart endpoints for customers
```
### Task 3.2: Create `checkout` module
```
app/modules/checkout/
├── __init__.py
├── definition.py
├── config.py
├── exceptions.py
├── schemas/
│ ├── __init__.py
│ └── checkout.py # CheckoutRequest, CheckoutResponse
├── services/
│ ├── __init__.py
│ └── checkout_service.py # Orchestrates cart → order conversion
└── routes/
└── api/
├── __init__.py
└── storefront.py # Place order endpoint
```
### Task 3.3: Create `catalog` module
```
app/modules/catalog/
├── __init__.py
├── definition.py
├── config.py
├── schemas/
│ ├── __init__.py
│ └── catalog.py # ProductListResponse, ProductDetailResponse
├── services/
│ ├── __init__.py
│ └── catalog_service.py # Customer-facing product queries
└── routes/
└── api/
├── __init__.py
└── storefront.py # Product browsing endpoints
```
---
## Phase 4: Move Storefront Routes to Modules
### Task 4.1: Move customer auth routes to `customers` module
**From:** `app/api/v1/storefront/auth.py`
**To:** `app/modules/customers/routes/api/storefront.py`
Endpoints:
- `POST /auth/register` - Customer registration
- `POST /auth/login` - Customer login
- `POST /auth/logout` - Customer logout
- `POST /auth/password-reset/request` - Request password reset
- `POST /auth/password-reset/confirm` - Confirm password reset
### Task 4.2: Move customer profile routes to `customers` module
**From:** `app/api/v1/storefront/profile.py`
**To:** `app/modules/customers/routes/api/storefront.py` (append)
Endpoints:
- `GET /profile` - Get customer profile
- `PUT /profile` - Update customer profile
### Task 4.3: Move customer address routes to `customers` module
**From:** `app/api/v1/storefront/addresses.py`
**To:** `app/modules/customers/routes/api/storefront.py` (append)
Endpoints:
- `GET /addresses` - List customer addresses
- `POST /addresses` - Create address
- `PUT /addresses/{id}` - Update address
- `DELETE /addresses/{id}` - Delete address
### Task 4.4: Move cart routes to `cart` module
**From:** `app/api/v1/storefront/carts.py`
**To:** `app/modules/cart/routes/api/storefront.py`
### Task 4.5: Move order placement to `checkout` module
**From:** `app/api/v1/storefront/orders.py` (POST /orders only)
**To:** `app/modules/checkout/routes/api/storefront.py`
### Task 4.6: Move order viewing to `orders` module
**From:** `app/api/v1/storefront/orders.py` (GET endpoints)
**To:** `app/modules/orders/routes/api/storefront.py`
### Task 4.7: Move product browsing to `catalog` module
**From:** `app/api/v1/storefront/products.py`
**To:** `app/modules/catalog/routes/api/storefront.py`
### Task 4.8: Move messaging routes to `messaging` module
**From:** `app/api/v1/storefront/messages.py`
**To:** `app/modules/messaging/routes/api/storefront.py`
---
## Phase 5: Fix Direct Model Imports
### Task 5.1: Update dependency injection pattern
**Current pattern (violates layered architecture):**
```python
from models.database.customer import Customer
@router.get("/orders")
def get_orders(customer: Customer = Depends(get_current_customer_api)):
...
```
**New pattern (proper layered architecture):**
```python
from app.modules.customers.schemas import CustomerContext
@router.get("/orders")
def get_orders(customer: CustomerContext = Depends(get_current_customer_api)):
...
```
### Task 5.2: Create `CustomerContext` schema
**File:** `app/modules/customers/schemas/context.py`
```python
class CustomerContext(BaseModel):
"""Customer context for dependency injection in storefront routes."""
id: int
vendor_id: int
email: str
first_name: str | None
last_name: str | None
model_config = ConfigDict(from_attributes=True)
```
### Task 5.3: Update `get_current_customer_api` dependency
**File:** `app/api/deps.py`
Change return type from `Customer` (database model) to `CustomerContext` (schema).
### Task 5.4: Update all storefront routes
Replace all `Customer` type hints with `CustomerContext`.
---
## Phase 6: Delete Legacy Files
### Task 6.1: Delete `models/database/customer.py`
After all imports are updated to use `app.modules.customers.models.customer`.
### Task 6.2: Delete `app/api/v1/storefront/` directory
After all routes are moved to their respective modules.
### Task 6.3: Delete `app/services/cart_service.py`
After migrated to `app/modules/cart/services/cart_service.py`.
---
## Phase 7: Update Documentation
### Task 7.1: Update API documentation
- Rename all "shop" references to "storefront"
- Update endpoint paths
### Task 7.2: Update architecture documentation
- Document the layered architecture rule
- Document module structure
---
## Module Dependency Graph (Final State)
```
┌─────────────┐
│ core │
│ (tenancy) │
└──────┬──────┘
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│customers │ │ payments │ │messaging │
│ (auth) │ │ │ │ │
└────┬─────┘ └────┬─────┘ └──────────┘
│ │
│ ┌────────┴────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐
│ cart │ │ orders │
│ │ │ │
└────┬─────┘ └────┬─────┘
│ │
└──────────┬──────────┘
┌──────────┐
│ checkout │
│ │
└──────────┘
```
---
## Execution Order
1. **Phase 1** - Add architecture rule (enables detection) ✅ COMPLETE
2. **Phase 2** - Rename shop → storefront (terminology) ✅ COMPLETE
3. **Phase 3** - Create new modules (cart, checkout, catalog) ✅ COMPLETE
4. **Phase 4** - Move routes to modules ✅ COMPLETE
5. **Phase 5** - Fix direct model imports ✅ COMPLETE
6. **Phase 6** - Delete legacy files ✅ COMPLETE
7. **Phase 7** - Update documentation ✅ COMPLETE
---
## Files Modified Summary
### New Files
- `.architecture-rules/api.yaml` (modified - add API-007)
- `app/modules/cart/**` (new module)
- `app/modules/checkout/**` (new module)
- `app/modules/catalog/**` (new module)
- `app/modules/customers/routes/api/storefront.py`
- `app/modules/customers/schemas/context.py`
- `app/modules/orders/routes/api/storefront.py`
- `app/modules/messaging/routes/api/storefront.py`
### Deleted Files
- `app/api/v1/shop/` (entire directory)
- `app/routes/shop_pages.py`
- `models/database/customer.py`
- `app/services/cart_service.py`
### Modified Files
- `app/api/deps.py` (CustomerContext return type)
- `scripts/validate_architecture.py` (add API-007 check)
- All files currently importing from legacy locations

View File

@@ -1,235 +0,0 @@
# Session Note: Modular Platform Architecture Implementation
**Date:** 2026-01-25
**Plan Reference:** `docs/proposals/humble-orbiting-otter.md`
---
## Summary
Implemented a complete modular platform architecture allowing platforms (OMS, Loyalty, etc.) to enable/disable feature modules. This creates a flexible system where different business products can have different feature sets.
---
## Completed Phases
### Phase 1: Module Foundation (Commit: `5be42c5`)
Created the core module system:
| File | Purpose |
|------|---------|
| `app/modules/__init__.py` | Package init |
| `app/modules/base.py` | `ModuleDefinition` dataclass |
| `app/modules/registry.py` | `MODULES` dict with 12 module definitions |
| `app/modules/service.py` | `ModuleService` class for enablement checks |
**Key concepts:**
- Modules stored in `Platform.settings["enabled_modules"]` (JSON)
- Core modules (`core`, `platform-admin`) cannot be disabled
- Dependencies auto-resolved (e.g., marketplace requires inventory)
### Phase 2: Menu Integration & Route Protection (Commit: `cd65a59`)
Added access control:
| File | Changes |
|------|---------|
| `app/api/deps.py` | Added `require_module_access()` dependency |
| `app/services/menu_service.py` | Filter menu items by enabled modules |
| `app/routes/admin_pages.py` | Updated routes to use `require_menu_access()` |
**31 unit tests** in `tests/unit/services/test_module_service.py`
### Phase 3: Billing Module Extraction (Commit: `c614b7d`)
First module extraction as template:
```
app/modules/billing/
├── __init__.py
├── definition.py # ModuleDefinition with lazy router loading
└── routes/
├── __init__.py
├── admin.py # Router with require_module_access("billing")
└── vendor.py
```
### Phase 4: Additional Module Extractions (Commit: `9d0dc51`)
Extracted three more modules following the billing pattern:
- `app/modules/inventory/` - Stock management
- `app/modules/orders/` - Order management
- `app/modules/marketplace/` - Letzshop integration (depends on inventory)
Updated `app/api/v1/admin/__init__.py` to use module routers.
### Phase 5: Admin UI for Module Management (Commit: `7ecd554`)
Created complete Admin UI:
| File | Purpose |
|------|---------|
| `app/api/v1/admin/modules.py` | API endpoints for module CRUD |
| `app/templates/admin/platform-modules.html` | Module configuration page |
| `static/admin/js/platform-modules.js` | Alpine.js component |
| `app/routes/admin_pages.py` | Added `/admin/platforms/{code}/modules` route |
| `app/templates/admin/platform-detail.html` | Added link in Super Admin section |
**API Endpoints:**
- `GET /api/v1/admin/modules` - List all modules
- `GET /api/v1/admin/modules/platforms/{id}` - Get platform modules
- `PUT /api/v1/admin/modules/platforms/{id}` - Update modules
- `POST /api/v1/admin/modules/platforms/{id}/enable` - Enable module
- `POST /api/v1/admin/modules/platforms/{id}/disable` - Disable module
- `POST /api/v1/admin/modules/platforms/{id}/enable-all` - Enable all
- `POST /api/v1/admin/modules/platforms/{id}/disable-optional` - Core only
---
## Current Module Registry
| Module | Type | Description | Dependencies |
|--------|------|-------------|--------------|
| `core` | Core | Dashboard, settings, profile | - |
| `platform-admin` | Core | Companies, vendors, admin users | - |
| `billing` | Optional | Subscriptions, tiers, billing | - |
| `inventory` | Optional | Stock management, locations | - |
| `orders` | Optional | Order management, fulfillment | - |
| `marketplace` | Optional | Letzshop integration | inventory |
| `customers` | Optional | Customer management, CRM | - |
| `cms` | Optional | Content pages, media library | - |
| `analytics` | Optional | Dashboard, reports, exports | - |
| `messaging` | Optional | Messages, notifications | - |
| `dev-tools` | Optional | Components, icons | - |
| `monitoring` | Optional | Logs, background tasks | - |
---
## How It Works
### Module Enablement Flow
1. Super admin visits `/admin/platforms/{code}/modules`
2. Toggles modules on/off
3. API calls `module_service.enable_module()` or `disable_module()`
4. Dependencies auto-resolved
5. `Platform.settings["enabled_modules"]` updated
### Access Control Flow
1. User accesses route (e.g., `/api/v1/admin/billing/subscriptions`)
2. `require_module_access("billing")` dependency checks:
- Gets platform from request context
- Calls `module_service.is_module_enabled()`
- Returns 403 if disabled
3. Menu items filtered by `module_service.filter_menu_items_by_modules()`
---
## Pending/Optional Next Steps
### 1. Vendor Router Integration
Wire up vendor module routers to `app/api/v1/vendor/__init__.py`:
```python
from app.modules.billing.routes import vendor_router as billing_vendor_router
# ... etc
```
### 2. Extract Remaining Modules
Move inline modules to their own directories:
- `customers``app/modules/customers/`
- `cms``app/modules/cms/`
- `analytics``app/modules/analytics/`
- `messaging``app/modules/messaging/`
- `dev-tools``app/modules/dev-tools/`
- `monitoring``app/modules/monitoring/`
### 3. Database Table Migration (Optional)
Create `PlatformModule` junction table for better auditability:
```python
class PlatformModule(Base):
platform_id = Column(Integer, ForeignKey("platforms.id"))
module_code = Column(String(50))
is_enabled = Column(Boolean)
enabled_at = Column(DateTime)
enabled_by_user_id = Column(Integer, ForeignKey("users.id"))
```
### 4. Module-Specific Configuration UI
Add per-module config (stored in `Platform.settings["module_config"]`):
```json
{
"billing": {"stripe_mode": "live"},
"inventory": {"low_stock_threshold": 10}
}
```
### 5. Integration Tests
Add tests for `/api/v1/admin/modules/*` endpoints.
---
## Key Files Reference
### Core Module System
- `app/modules/base.py` - ModuleDefinition class
- `app/modules/registry.py` - MODULES dict
- `app/modules/service.py` - ModuleService
### Extracted Modules
- `app/modules/billing/` - Billing module
- `app/modules/inventory/` - Inventory module
- `app/modules/orders/` - Orders module
- `app/modules/marketplace/` - Marketplace module
### Access Control
- `app/api/deps.py` - `require_module_access()` dependency
- `app/services/menu_service.py` - Menu filtering
### Admin UI
- `app/api/v1/admin/modules.py` - API endpoints
- `app/templates/admin/platform-modules.html` - Template
- `static/admin/js/platform-modules.js` - JavaScript
### Tests
- `tests/unit/services/test_module_service.py` - 31 tests
---
## Git Commits (in order)
```
5be42c5 feat: implement modular platform architecture (Phase 1)
cd65a59 feat: implement module-based access control (Phase 2)
c614b7d feat: extract billing module with routes (Phase 3)
9d0dc51 feat: extract inventory, orders, and marketplace modules (Phase 4)
7ecd554 feat: add Admin UI for platform module management (Phase 5)
```
---
## Testing
Run module service tests:
```bash
python -m pytest tests/unit/services/test_module_service.py -v
```
Verify app starts:
```bash
python -c "from main import app; print('OK')"
```
---
## Access the Module UI
1. Login as super admin
2. Go to `/admin/platforms`
3. Click on a platform (e.g., OMS)
4. In "Super Admin" section, click "Module Configuration"
5. Toggle modules on/off
Or directly: `/admin/platforms/oms/modules`

View File

@@ -1,300 +0,0 @@
# Session Note: Self-Contained Module Architecture
**Date:** 2026-01-26
**Plan Reference:** `docs/proposals/TEMP.md` (now this file)
**Previous Session:** `docs/proposals/SESSION_NOTE_2026-01-25_modular-platform-architecture.md`
---
## Summary
Transformed thin module wrappers into fully self-contained modules, using CMS as the pilot. Each self-contained module is an autonomous unit with its own services, models, schemas, templates, exceptions, and locales.
---
## Completed Phases
### Phase 1: Foundation
Created infrastructure for self-contained modules:
| File | Purpose |
|------|---------|
| `app/modules/contracts/` | Protocol definitions for cross-module dependencies |
| `app/templates_config.py` | Multi-directory template loader for module templates |
| `app/modules/base.py` | Enhanced ModuleDefinition with self-contained flags |
**Git tag:** `pre-modular-architecture`
### Phase 2: CMS Pilot (Full Self-Contained Module)
Migrated CMS to be the first fully self-contained module:
| Component | Location | Status |
|-----------|----------|--------|
| Services | `app/modules/cms/services/content_page_service.py` | ✅ |
| Models | `app/modules/cms/models/content_page.py` | ✅ |
| Schemas | `app/modules/cms/schemas/content_page.py` | ✅ |
| Exceptions | `app/modules/cms/exceptions.py` | ✅ |
| Locales | `app/modules/cms/locales/{en,fr,de,lb}.json` | ✅ |
| Templates | `app/modules/cms/templates/cms/{admin,vendor}/` | ✅ |
| Static | `app/modules/cms/static/{admin,vendor}/js/` | ✅ |
| Routes | `app/modules/cms/routes/{api,pages}/` | ✅ |
---
## CMS Module Structure
```
app/modules/cms/
├── __init__.py # Lazy getter to avoid circular imports
├── definition.py # ModuleDefinition with self-contained config
├── exceptions.py # CMSException, ContentPageNotFoundError
├── locales/
│ ├── en.json
│ ├── fr.json
│ ├── de.json
│ └── lb.json
├── models/
│ ├── __init__.py # Exports: ContentPage, MediaFile, ProductMedia
│ └── content_page.py # ContentPage model (canonical location)
├── routes/
│ ├── __init__.py
│ ├── admin.py # Admin router wrapper
│ ├── vendor.py # Vendor router wrapper
│ ├── api/
│ │ ├── admin.py # Admin API endpoints
│ │ ├── vendor.py # Vendor API endpoints
│ │ └── shop.py # Shop/public API endpoints
│ └── pages/
│ ├── admin.py # Admin page routes
│ └── vendor.py # Vendor page routes
├── schemas/
│ ├── __init__.py
│ └── content_page.py # Pydantic schemas
├── services/
│ ├── __init__.py
│ └── content_page_service.py
├── static/
│ ├── admin/js/
│ │ ├── content-pages.js
│ │ └── content-page-edit.js
│ └── vendor/js/
│ ├── content-pages.js
│ └── content-page-edit.js
└── templates/
└── cms/
├── admin/
│ ├── content-pages.html
│ └── content-page-edit.html
└── vendor/
├── content-pages.html
└── content-page-edit.html
```
---
## Key Patterns Established
### 1. Module-First Models
Models live in module folders and are dynamically loaded at startup:
```python
# app/modules/cms/models/content_page.py (canonical location)
from app.core.database import Base
class ContentPage(Base):
__tablename__ = "content_pages"
...
# models/database/__init__.py (dynamic loader)
def _discover_module_models():
for module_dir in sorted(modules_dir.iterdir()):
models_init = module_dir / "models" / "__init__.py"
if models_init.exists():
importlib.import_module(f"app.modules.{module_dir.name}.models")
_discover_module_models()
```
### 2. Shared Templates Instance
Route files must import from `app.templates_config`:
```python
# CORRECT
from app.templates_config import templates
# WRONG - creates local instance without module loaders
templates = Jinja2Templates(directory="app/templates")
```
### 3. Template Namespacing
Module templates use namespace prefix to avoid collisions:
```python
# Module templates at: app/modules/cms/templates/cms/admin/content-pages.html
# Rendered as:
templates.TemplateResponse("cms/admin/content-pages.html", ...)
```
### 4. Import Pattern
All code should import from module:
```python
# Models
from app.modules.cms.models import ContentPage
# Services
from app.modules.cms.services import content_page_service
# Exceptions
from app.modules.cms.exceptions import ContentPageNotFoundException
```
### 5. Lazy Imports for Circular Import Prevention
```python
# app/modules/cms/__init__.py
def get_cms_module():
"""Lazy getter for cms_module to avoid circular imports."""
from app.modules.cms.definition import cms_module
return cms_module
```
---
## Decisions Made
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Pilot Module | CMS | Simplest, minimal dependencies |
| Cross-Module Pattern | Protocol pattern | Type-safe interfaces |
| Timeline | Incremental | Alongside feature work |
| Backwards Compatibility | No shims | Pre-launch, can delete old files |
| Template Namespace | `{module}/admin/`, `{module}/vendor/` | Prevent collisions |
---
## Verification Completed
- [x] `python -c "from main import app"` succeeds
- [x] ContentPage model in `app/modules/cms/models/content_page.py`
- [x] Dynamic model loader in `models/database/__init__.py`
- [x] `content_pages` table in Base.metadata (67 total tables)
- [x] Template files in correct locations
- [x] Route files use shared templates instance
- [x] Admin CMS pages render correctly
- [x] Vendor CMS pages render correctly
---
## What Stays in Core vs Moves to Modules
### Core (Stays in Place)
| Component | Location | Reason |
|-----------|----------|--------|
| User, Vendor, Company, Platform models | `models/database/` | Foundational entities |
| Auth service | `app/services/` | Cross-cutting concern |
| Storage, Cache, Email services | `app/services/` | Infrastructure utilities |
| Base exceptions | `app/exceptions/` | Shared error types |
| Shared macros, partials | `app/templates/shared/` | Reusable UI components |
| API dependencies | `app/api/deps.py` | Auth, module access checks |
### Modules (Move to Self-Contained)
| Module | Services | Models | Status |
|--------|----------|--------|--------|
| cms | content_page, media | content_page, media | ✅ Complete |
| billing | billing, stripe, invoice, subscription | subscription, invoice, payment | Pending |
| inventory | inventory, inventory_transaction | inventory, inventory_transaction | Pending |
| orders | order, cart, order_item_exception | order, order_item_exception | Pending |
| marketplace | marketplace, marketplace_product, letzshop_export | marketplace_product, import_job | Pending |
| customers | customer, customer_address | customer | Pending |
| messaging | messaging, notification | message, notification | Pending |
| analytics | stats, capacity_forecast | (uses other models) | Pending |
| monitoring | background_tasks, test_runner, log | test_run, architecture_scan | Pending |
---
## Pending/Next Steps
### Phase 3: Simple Modules Migration
- [ ] Migrate analytics module
- [ ] Migrate monitoring module
- [ ] Migrate messaging module
- [ ] Migrate customers module
### Phase 4: Complex Modules Migration
- [ ] Migrate billing (with Stripe integration)
- [ ] Migrate inventory
- [ ] Migrate orders
- [ ] Migrate marketplace
### Phase 5: Cleanup
- [ ] Remove deprecated shims (if any created)
- [ ] Update all imports across codebase
- [ ] Delete `app/platforms/` directory
- [ ] Update architecture documentation
### Other Pending Items
- [ ] Wire up vendor module routers to `app/api/v1/vendor/__init__.py`
- [ ] PlatformModule database table (optional - for audit trail)
- [ ] Module-specific configuration UI
- [ ] Integration tests for `/api/v1/admin/modules/*` endpoints
---
## Git Commits
```
ec4ec04 feat: complete CMS as fully autonomous self-contained module
0b65864 fix: resolve circular import in CMS module
3ffa890 fix: correct static file mount order and update architecture validator
3307205 feat: add module info and configuration pages to admin panel
```
---
## Key Files Reference
### Self-Contained Module Infrastructure
- `app/modules/base.py` - ModuleDefinition with self-contained flags
- `app/modules/contracts/` - Protocol definitions for cross-module deps
- `app/templates_config.py` - Multi-directory template loader
- `models/database/__init__.py` - Dynamic module model discovery
### CMS Module (Pilot)
- `app/modules/cms/definition.py` - Module metadata
- `app/modules/cms/models/content_page.py` - ContentPage model
- `app/modules/cms/services/content_page_service.py` - Business logic
- `app/modules/cms/exceptions.py` - Module-specific exceptions
- `app/modules/cms/templates/cms/` - Namespaced templates
---
## Testing
```bash
# Verify app starts
python -c "from main import app; print('OK')"
# Run module service tests
python -m pytest tests/unit/services/test_module_service.py -v
# Verify CMS model is loaded
python -c "from app.modules.cms.models import ContentPage; print(ContentPage.__tablename__)"
# Verify template loading
python -c "from app.templates_config import templates; print(templates.env.loader)"
```

View File

@@ -1,280 +0,0 @@
# Session Note: Module Reclassification & Framework Layer
**Date:** 2026-01-27
**Previous Session:** SESSION_NOTE_2026-01-26_self-contained-modules.md
**Tag:** v0.9.0-pre-framework-refactor
## Summary
Implemented a three-tier module classification system and added core framework infrastructure:
1. **Three-Tier Classification**
- Core modules (4): Always enabled, cannot be disabled
- Optional modules (7): Can be enabled/disabled per platform
- Internal modules (2): Admin-only tools, not customer-facing
2. **Module Renaming**
- Renamed `platform-admin``tenancy`
3. **Core Module Promotion**
- Promoted CMS and Customers to core (is_core=True)
4. **New Payments Module**
- Extracted payment gateway logic into standalone module
- Billing and Orders now depend on Payments
5. **Framework Layer Infrastructure**
- Module events system (events.py)
- Module-specific migrations support (migrations.py)
- Observability framework (observability.py)
---
## Final Module Classification
### Core Modules (4)
| Module | Description |
|--------|-------------|
| `core` | Dashboard, settings, profile |
| `tenancy` | Platform, company, vendor, admin user management |
| `cms` | Content pages, media library, themes |
| `customers` | Customer database, profiles, segmentation |
### Optional Modules (7)
| Module | Dependencies | Description |
|--------|--------------|-------------|
| `payments` | - | Payment gateway integrations (Stripe, PayPal, etc.) |
| `billing` | payments | Platform subscriptions, vendor invoices |
| `inventory` | - | Stock management, locations |
| `orders` | payments | Order management, customer checkout |
| `marketplace` | inventory | Letzshop integration |
| `analytics` | - | Reports, dashboards |
| `messaging` | - | Messages, notifications |
### Internal Modules (2)
| Module | Description |
|--------|-------------|
| `dev-tools` | Component library, icons |
| `monitoring` | Logs, background tasks, Flower, Grafana |
---
## Key Files Modified/Created
### Modified
- `app/modules/registry.py` - Three-tier classification with CORE_MODULES, OPTIONAL_MODULES, INTERNAL_MODULES
- `app/modules/base.py` - Enhanced ModuleDefinition with new fields:
- `version` - Semantic versioning
- `is_internal` - Internal module flag
- `permissions` - Module-specific permissions
- `config_schema` - Pydantic config validation
- `default_config` - Default configuration values
- `migrations_path` - Module-specific migrations
- Lifecycle hooks: `on_enable`, `on_disable`, `on_startup`, `health_check`
- `app/modules/service.py` - No changes needed (uses registry functions)
- `app/modules/cms/definition.py` - Set is_core=True
- `app/modules/customers/definition.py` - Set is_core=True
- `app/modules/billing/definition.py` - Added requires=["payments"]
- `app/modules/orders/definition.py` - Added requires=["payments"]
- `app/modules/dev_tools/definition.py` - Added is_internal=True
- `app/modules/monitoring/definition.py` - Added is_internal=True
- `alembic/env.py` - Added module migration discovery
### Created
- `app/modules/events.py` - Module event bus
- ModuleEvent enum (ENABLED, DISABLED, STARTUP, SHUTDOWN, CONFIG_CHANGED)
- ModuleEventData dataclass
- ModuleEventBus class with subscribe/emit
- `app/modules/migrations.py` - Module migration utilities
- discover_module_migrations()
- get_all_migration_paths()
- get_migration_order()
- validate_migration_names()
- `app/core/observability.py` - Observability framework
- HealthCheckRegistry
- MetricsRegistry (Prometheus placeholder)
- SentryIntegration
- health_router (/health, /metrics, /health/live, /health/ready)
- `app/modules/payments/` - New payments module
- definition.py
- services/payment_service.py
- services/gateway_service.py
- routes/admin.py, vendor.py
- schemas/__init__.py
- `alembic/versions/zc2m3n4o5p6q7_rename_platform_admin_to_tenancy.py`
- `alembic/versions/zd3n4o5p6q7r8_promote_cms_customers_to_core.py`
---
## Dependency Graph
```
CORE MODULES
┌────────────────────────────────────────┐
│ core ← tenancy ← cms ← customers │
└────────────────────────────────────────┘
OPTIONAL MODULES
┌─────────────────────────────────────────┐
│ payments │
│ ↙ ↘ │
│ billing orders │
│ │
│ inventory │
│ ↓ │
│ marketplace │
│ │
│ analytics messaging │
└─────────────────────────────────────────┘
INTERNAL MODULES
┌─────────────────────────────────────────┐
│ dev-tools monitoring │
└─────────────────────────────────────────┘
```
---
## New Registry Functions
```python
# Module dictionaries
CORE_MODULES: dict[str, ModuleDefinition]
OPTIONAL_MODULES: dict[str, ModuleDefinition]
INTERNAL_MODULES: dict[str, ModuleDefinition]
MODULES: dict[str, ModuleDefinition] # All combined
# New helper functions
get_optional_module_codes() -> set[str]
get_internal_modules() -> list[ModuleDefinition]
get_internal_module_codes() -> set[str]
is_core_module(code: str) -> bool
is_internal_module(code: str) -> bool
get_modules_by_tier() -> dict[str, list[ModuleDefinition]]
get_module_tier(code: str) -> str | None # 'core', 'optional', 'internal', None
```
---
## Module Event System
```python
from app.modules.events import module_event_bus, ModuleEvent
# Subscribe to events
@module_event_bus.subscribe(ModuleEvent.ENABLED)
def on_module_enabled(data: ModuleEventData):
print(f"Module {data.module_code} enabled for platform {data.platform_id}")
# Emit events (done by ModuleService)
module_event_bus.emit_enabled("billing", platform_id=1, user_id=42)
```
---
## Module Migrations
Modules can now have their own migrations in `app/modules/<code>/migrations/versions/`.
Migration naming convention:
```
{module_code}_{sequence}_{description}.py
Example: cms_001_create_content_pages.py
```
Alembic automatically discovers module migrations via `get_all_migration_paths()`.
---
## Observability Framework
```python
from app.core.observability import (
health_registry,
metrics_registry,
sentry,
init_observability,
)
# Register health check
@health_registry.register("database")
def check_db() -> HealthCheckResult:
return HealthCheckResult(name="database", status=HealthStatus.HEALTHY)
# Initialize in lifespan
init_observability(
enable_metrics=True,
sentry_dsn="...",
flower_url="http://flower:5555",
)
```
Endpoints:
- `GET /health` - Aggregated health from all checks
- `GET /health/live` - Kubernetes liveness probe
- `GET /health/ready` - Kubernetes readiness probe
- `GET /metrics` - Prometheus metrics
- `GET /health/tools` - External tool URLs (Flower, Grafana)
---
## Payments Module
New standalone module for payment gateway abstractions:
```python
from app.modules.payments.services import PaymentService, GatewayService
payment_service = PaymentService()
result = await payment_service.process_payment(
amount=1000, # cents
currency="EUR",
payment_method_id="pm_xxx",
)
gateway_service = GatewayService()
gateways = gateway_service.get_available_gateways()
```
**Separation from Billing:**
- **Payments**: Gateway abstractions, payment processing, refunds
- **Billing**: Subscriptions, invoices, tier management (uses Payments)
- **Orders**: Checkout, order payments (uses Payments)
---
## Verification Completed
✅ App starts successfully
✅ Core modules: core, tenancy, cms, customers
✅ Optional modules: payments, billing, inventory, orders, marketplace, analytics, messaging
✅ Internal modules: dev-tools, monitoring
✅ Dependencies validated (billing→payments, orders→payments, marketplace→inventory)
✅ Module event bus working
✅ Observability framework working
✅ Migration discovery working
---
## Next Steps
1. **Phase 6: Make Customers Self-Contained** (deferred)
- Move models, services to app/modules/customers/
- Create module-specific migrations
2. **Database Migrations**
- Run: `alembic upgrade head`
- This will rename platform-admin → tenancy in platform_modules
3. **Integrate Observability**
- Add health_router to main.py
- Initialize observability in lifespan
4. **Add Module Health Checks**
- Implement health_check on modules that need monitoring
- Call register_module_health_checks() on startup
5. **Payments Module Implementation**
- Implement actual Stripe/PayPal gateway logic
- Add Payment, Transaction models
- Create module migrations

View File

@@ -1,161 +0,0 @@
# Session Note: Module Config & Migrations Infrastructure
**Date:** 2026-01-28
**Focus:** Self-contained module configuration and migrations auto-discovery
## Summary
Completed the infrastructure for fully self-contained modules with config and migrations auto-discovery. All modules now have placeholder files ready for the final migration phase.
## What Was Done
### 1. Module Config Auto-Discovery
Created `app/modules/config.py` that auto-discovers module configurations:
```python
from app.modules.config import get_module_config
marketplace_config = get_module_config("marketplace")
```
Each module now has a `config.py` placeholder:
```python
# app/modules/marketplace/config.py
class MarketplaceConfig(BaseSettings):
model_config = {"env_prefix": "MARKETPLACE_"}
config = MarketplaceConfig()
```
### 2. Module Migrations Directories
Created `migrations/versions/` directories for all 11 self-contained modules:
- analytics, billing, cms, customers, dev_tools
- inventory, marketplace, messaging, monitoring, orders, payments
Each with required `__init__.py` files for Alembic discovery.
### 3. New Architecture Rules
Added rules MOD-013 to MOD-015:
| Rule | Description |
|------|-------------|
| MOD-013 | config.py should export `config` or `config_class` |
| MOD-014 | Migrations must follow naming convention `{module}_{seq}_{desc}.py` |
| MOD-015 | Migrations directory must have `__init__.py` files |
### 4. Module Migration Commits
Committed all pending module migration work:
| Commit | Description |
|--------|-------------|
| `2466dfd` | feat: add module config and migrations auto-discovery infrastructure |
| `bd2c99a` | feat: complete analytics module self-containment |
| `f79e67d` | feat: complete billing module self-containment |
| `b74d134` | feat: complete marketplace module self-containment |
| `705d336` | feat: add self-contained structure to remaining modules |
| `d987274` | feat: complete dev_tools module self-containment |
| `37cf74c` | refactor: update registry and main.py for module auto-discovery |
| `3ffa337` | refactor: convert legacy models/schemas to re-exports |
| `bf871dc` | refactor: convert legacy services/tasks to re-exports |
| `fbcf079` | chore: update API routers, validation, and docs |
### 5. Documentation Updates
- `docs/architecture/module-system.md` - Added Module Configuration and Module Migrations sections
- `docs/development/creating-modules.md` - Added config.py pattern, updated migrations docs
## Current Module Status
| Module | definition.py | config.py | migrations/ | routes/api/ | routes/pages/ | locales/ | Status |
|--------|--------------|-----------|-------------|-------------|---------------|----------|--------|
| analytics | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Complete** |
| billing | ✅ | ✅ | ✅ | ✅ | ⚠️ | ✅ | Needs pages |
| cms | ✅ | ✅ | ✅ | ✅ | ✅ | - | **Complete** |
| customers | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| dev_tools | ✅ | ✅ | ✅ | ✅ | - | - | **Complete** |
| inventory | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| marketplace | ✅ | ✅ | ✅ | ✅ | ⚠️ | ✅ | Needs pages |
| messaging | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| monitoring | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| orders | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| payments | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | - | Needs routes |
| core | ✅ | - | - | - | - | - | Minimal |
| tenancy | ✅ | - | - | - | - | - | Minimal |
## Decisions Made
1. **Config is environment-based**: Each module uses Pydantic Settings with `{MODULE}_` prefix
2. **Migrations stay central for now**: Existing migrations remain in `alembic/versions/`, module directories are for future changes
3. **Migration reorganization deferred**: Will move existing migrations to modules before production
4. **Legacy files become re-exports**: Original files in `models/database/`, `models/schema/`, `app/services/` re-export from modules
## Tomorrow's Tasks
### Phase 1: Module Alignment Audit
Run architecture validator and fix all modules to comply with rules:
```bash
python scripts/validate_architecture.py
```
Check each module for:
- [ ] MOD-001: Required directories exist
- [ ] MOD-002: Services contain actual code (not re-exports)
- [ ] MOD-003: Schemas contain actual code (not re-exports)
- [ ] MOD-004: Routes import from module, not legacy
- [ ] MOD-005: Templates and static exist for UI modules
- [ ] MOD-008: exceptions.py exists
- [ ] MOD-010: Routes export `router` variable
- [ ] MOD-011: Tasks have `__init__.py`
### Phase 2: Complete Module Routes Migration
Move remaining routes to self-contained structure:
1. **customers** - Move routes from `app/api/v1/*/customers.py`
2. **inventory** - Move routes from `app/api/v1/*/inventory.py`
3. **messaging** - Move routes from `app/api/v1/*/messages.py`
4. **monitoring** - Move routes from `app/api/v1/admin/monitoring.py`
5. **orders** - Move routes from `app/api/v1/*/orders.py`
6. **payments** - Move routes from `app/api/v1/*/payments.py`
### Phase 3: Migration Reorganization (Pre-Production)
Move existing migrations to module-specific directories:
1. Identify which tables belong to which module
2. Create baseline migrations in module directories
3. Test migration chain works correctly
4. Remove/reorganize central migrations
### Phase 4: Validation & Testing
1. Run full architecture validation
2. Test all routes are accessible
3. Test module enable/disable functionality
4. Verify Alembic discovers all migrations
## Files Changed
### New Files
- `app/modules/config.py` - Config auto-discovery
- `app/modules/*/config.py` - Module config placeholders (11 files)
- `app/modules/*/migrations/__init__.py` - Migration package markers (11 modules)
- `app/modules/*/migrations/versions/__init__.py` - Version package markers (11 modules)
- `.architecture-rules/module.yaml` - Module validation rules
### Modified Files
- `docs/architecture/module-system.md` - Added config/migrations sections
- `docs/development/creating-modules.md` - Added config pattern, updated migrations
## Related Documentation
- [Module System Architecture](../architecture/module-system.md)
- [Creating Modules Guide](../development/creating-modules.md)
- [Module Migration Plan](module-migration-plan.md)

View File

@@ -1,257 +0,0 @@
# Self-Contained Module Routes Migration Plan
**Date:** 2026-01-30
**Goal:** Migrate all routes to self-contained modules, remove all legacy route files
## Current State
### Modules (17 total)
```
analytics, billing, cart, catalog, checkout, cms, customers, dev_tools,
inventory, loyalty, marketplace, messaging, monitoring, orders, payments
```
### Legacy Route Files to Migrate
#### Admin API Routes (34 files in `app/api/v1/admin/`)
| File | Target Module | Status |
|------|---------------|--------|
| `admin_users.py` | **PLATFORM CORE** | Keep in legacy |
| `audit.py` | **PLATFORM CORE** | Keep in legacy |
| `auth.py` | **PLATFORM CORE** | Keep in legacy |
| `background_tasks.py` | monitoring | Migrate |
| `code_quality.py` | dev_tools | Migrate |
| `companies.py` | **PLATFORM CORE** | Keep in legacy |
| `dashboard.py` | **PLATFORM CORE** | Keep in legacy |
| `email_templates.py` | messaging | Migrate |
| `features.py` | billing | Migrate |
| `images.py` | cms | Migrate |
| `inventory.py` | inventory | Already migrated (re-export) |
| `letzshop.py` | marketplace | Already migrated (re-export) |
| `logs.py` | monitoring | Migrate |
| `marketplace.py` | marketplace | Already migrated (re-export) |
| `media.py` | cms | Migrate |
| `menu_config.py` | **PLATFORM CORE** | Keep in legacy |
| `messages.py` | messaging | Migrate |
| `module_config.py` | **PLATFORM CORE** | Keep in legacy |
| `modules.py` | **PLATFORM CORE** | Keep in legacy |
| `monitoring.py` | monitoring | Migrate |
| `notifications.py` | messaging | Migrate |
| `order_item_exceptions.py` | orders | Already migrated (re-export) |
| `orders.py` | orders | Already migrated (re-export) |
| `platform_health.py` | monitoring | Migrate |
| `platforms.py` | **PLATFORM CORE** | Keep in legacy |
| `products.py` | catalog | Migrate |
| `settings.py` | **PLATFORM CORE** | Keep in legacy |
| `subscriptions.py` | billing | Already migrated (re-export) |
| `tests.py` | dev_tools | Migrate |
| `users.py` | **PLATFORM CORE** | Keep in legacy |
| `vendor_domains.py` | **PLATFORM CORE** | Keep in legacy |
| `vendor_products.py` | catalog | Migrate |
| `vendors.py` | **PLATFORM CORE** | Keep in legacy |
| `vendor_themes.py` | cms | Migrate |
#### Vendor API Routes (24 files in `app/api/v1/vendor/`)
| File | Target Module | Status |
|------|---------------|--------|
| `analytics.py` | analytics | Migrate |
| `auth.py` | **PLATFORM CORE** | Keep in legacy |
| `billing.py` | billing | Already migrated (re-export) |
| `dashboard.py` | **PLATFORM CORE** | Keep in legacy |
| `email_settings.py` | messaging | Migrate |
| `email_templates.py` | messaging | Migrate |
| `features.py` | billing | Migrate |
| `info.py` | **PLATFORM CORE** | Keep in legacy |
| `inventory.py` | inventory | Already migrated (re-export) |
| `invoices.py` | billing | Migrate |
| `letzshop.py` | marketplace | Already migrated (re-export) |
| `marketplace.py` | marketplace | Already migrated (re-export) |
| `media.py` | cms | Migrate |
| `messages.py` | messaging | Migrate |
| `notifications.py` | messaging | Migrate |
| `onboarding.py` | marketplace | Migrate |
| `order_item_exceptions.py` | orders | Already migrated (re-export) |
| `orders.py` | orders | Already migrated (re-export) |
| `payments.py` | payments | Migrate |
| `products.py` | catalog | Migrate |
| `profile.py` | **PLATFORM CORE** | Keep in legacy |
| `settings.py` | **PLATFORM CORE** | Keep in legacy |
| `team.py` | **PLATFORM CORE** | Keep in legacy |
| `usage.py` | billing | Migrate |
#### Page Routes (4 files in `app/routes/`)
| File | Target Module | Status |
|------|---------------|--------|
| `admin_pages.py` | Split across modules | Migrate page by page |
| `vendor_pages.py` | Split across modules | Migrate page by page |
| `storefront_pages.py` | Split across modules | Migrate page by page |
| `platform_pages.py` | **PLATFORM CORE** | Keep in legacy |
## Migration Summary
### Files to Migrate (by module)
#### 1. billing (5 routes)
- `app/api/v1/admin/features.py`
- `app/api/v1/vendor/features.py`
- `app/api/v1/vendor/invoices.py`
- `app/api/v1/vendor/usage.py`
- Remove re-exports: `subscriptions.py`, `billing.py`
#### 2. catalog (3 routes)
- `app/api/v1/admin/products.py`
- `app/api/v1/admin/vendor_products.py`
- `app/api/v1/vendor/products.py`
#### 3. cms (4 routes)
- `app/api/v1/admin/images.py`
- `app/api/v1/admin/media.py`
- `app/api/v1/admin/vendor_themes.py`
- `app/api/v1/vendor/media.py`
#### 4. messaging (7 routes)
- `app/api/v1/admin/email_templates.py`
- `app/api/v1/admin/messages.py`
- `app/api/v1/admin/notifications.py`
- `app/api/v1/vendor/email_settings.py`
- `app/api/v1/vendor/email_templates.py`
- `app/api/v1/vendor/messages.py`
- `app/api/v1/vendor/notifications.py`
#### 5. monitoring (4 routes)
- `app/api/v1/admin/background_tasks.py`
- `app/api/v1/admin/logs.py`
- `app/api/v1/admin/monitoring.py`
- `app/api/v1/admin/platform_health.py`
#### 6. dev_tools (2 routes)
- `app/api/v1/admin/code_quality.py`
- `app/api/v1/admin/tests.py`
#### 7. marketplace (1 route)
- `app/api/v1/vendor/onboarding.py`
- Remove re-exports: `letzshop.py`, `marketplace.py`
#### 8. analytics (1 route)
- `app/api/v1/vendor/analytics.py`
#### 9. payments (1 route)
- `app/api/v1/vendor/payments.py`
#### 10. orders (cleanup)
- Remove re-exports: `orders.py`, `order_item_exceptions.py`
#### 11. inventory (cleanup)
- Remove re-exports: `inventory.py`
### Files to Keep in Platform Core (12 admin + 6 vendor = 18 files)
**Admin:**
- `admin_users.py` - Admin user management (super admin only)
- `audit.py` - Platform audit logs
- `auth.py` - Admin authentication
- `companies.py` - Company management
- `dashboard.py` - Admin dashboard
- `menu_config.py` - Menu configuration
- `module_config.py` - Module configuration
- `modules.py` - Module management
- `platforms.py` - Platform management
- `settings.py` - Platform settings
- `users.py` - Platform user management
- `vendor_domains.py` - Vendor domain management
- `vendors.py` - Vendor management
**Vendor:**
- `auth.py` - Vendor authentication
- `dashboard.py` - Vendor dashboard
- `info.py` - Vendor info endpoint
- `profile.py` - Vendor profile
- `settings.py` - Vendor settings
- `team.py` - Vendor team management
## Execution Plan
### Phase 1: API Routes (Priority Order)
1. **messaging** (7 files) - Largest module, get it done first
2. **billing** (5 files) - Already partially migrated
3. **monitoring** (4 files) - Self-contained
4. **cms** (4 files) - Already partially migrated
5. **catalog** (3 files) - Product management
6. **dev_tools** (2 files) - Testing tools
7. **marketplace** (1 file) - Just onboarding
8. **analytics** (1 file) - Simple
9. **payments** (1 file) - Simple
10. **Cleanup** - Remove all re-export files for orders, inventory
### Phase 2: Page Routes
Each module that has admin/vendor pages needs:
1. Create `app/modules/{module}/routes/pages/admin.py`
2. Create `app/modules/{module}/routes/pages/vendor.py`
3. Move relevant page handlers from legacy files
4. Update `admin_pages.py` / `vendor_pages.py` to import from modules
### Phase 3: Storefront Routes
1. cart module - Cart pages
2. catalog module - Product listing, detail pages
3. checkout module - Checkout flow
4. customers module - Customer account pages
## Pattern Reference (from customers module)
### Module Route Structure
```
app/modules/{module}/
├── routes/
│ ├── __init__.py # Lazy exports
│ ├── admin.py # Admin API routes with admin_router
│ ├── vendor.py # Vendor API routes with vendor_router
│ ├── api/ # Optional: Split API routes
│ │ ├── admin.py
│ │ ├── vendor.py
│ │ └── storefront.py
│ └── pages/ # HTML page routes
│ ├── admin.py
│ └── vendor.py
└── definition.py # Module definition with router getters
```
### Module Definition Pattern
```python
def _get_admin_router():
from app.modules.{module}.routes.admin import admin_router
return admin_router
def _get_vendor_router():
from app.modules.{module}.routes.vendor import vendor_router
return vendor_router
# In get_{module}_module_with_routers():
{module}_module.admin_router = _get_admin_router()
{module}_module.vendor_router = _get_vendor_router()
```
### Router Registration in `app/api/v1/{admin,vendor}/__init__.py`
```python
# Import module router
from app.modules.{module}.routes.admin import admin_router as {module}_admin_router
# Include with module access control
router.include_router({module}_admin_router, tags=["admin-{module}"])
# Legacy: router.include_router({module}.router, tags=["admin-{module}"])
```
## Validation
After each module migration:
1. Run `python scripts/validate_architecture.py`
2. Test affected endpoints manually
3. Verify imports work: `python -c "from app.api.v1.admin import router"`
## Estimated Effort
- **API Routes:** ~28 files to migrate across 9 modules
- **Page Routes:** ~15-20 page handlers to move
- **Total:** 1-2 sessions depending on complexity

View File

@@ -1,145 +0,0 @@
# Session Note: Tenancy Module Consolidation Plan
**Date:** 2026-01-31
**Topic:** Complete migration plan for tenancy module and remaining legacy code
## Summary
This session established a comprehensive plan for migrating all identity and organizational management code to the tenancy module, plus identified where other legacy code should go.
## Key Decisions
### 1. Tenancy Module Scope
The tenancy module owns **identity and organizational hierarchy**:
- Platforms (top-level SaaS instances)
- Companies (business entities)
- Vendors (merchant accounts)
- Users (admin users, team members)
- Authentication (login, tokens, sessions)
- Teams (vendor team management)
- Domains (custom domain configuration)
### 2. Completed Migrations (This Session)
- Migrated `app/api/v1/vendor/info.py``tenancy/routes/api/vendor.py`
- Changed path from `/{vendor_code}` to `/info/{vendor_code}` (removes catch-all)
- Set up tenancy module as `is_self_contained=True`
- Updated frontend JS to use new endpoint path
### 3. Files Identified for Tenancy Migration
#### Routes to Migrate
```
Admin API:
- admin_users.py → tenancy/routes/api/admin_users.py
- companies.py → tenancy/routes/api/admin_companies.py
- platforms.py → tenancy/routes/api/admin_platforms.py
- vendors.py → tenancy/routes/api/admin_vendors.py
- vendor_domains.py → tenancy/routes/api/admin_vendor_domains.py
- users.py → tenancy/routes/api/admin_users.py
- auth.py → tenancy/routes/api/admin_auth.py
Vendor API:
- auth.py → tenancy/routes/api/vendor_auth.py
- profile.py → tenancy/routes/api/vendor_profile.py
- team.py → tenancy/routes/api/vendor_team.py
```
#### Services to Migrate
```
- vendor_service.py
- company_service.py
- platform_service.py
- admin_service.py
- admin_platform_service.py
- vendor_domain_service.py
- vendor_team_service.py
- team_service.py
- auth_service.py
- platform_signup_service.py
```
#### Models to Migrate
```
- vendor.py
- company.py
- platform.py
- admin.py
- admin_platform.py
- vendor_domain.py
- vendor_platform.py
- user.py
```
### 4. Other Module Assignments
| Module | Files to Receive |
|--------|-----------------|
| **monitoring** | background_tasks, logs, monitoring, platform_health, audit |
| **dev_tools** | code_quality, tests |
| **messaging** | messages, notifications, email_templates, email_settings |
| **cms** | media, images, vendor_themes |
| **core** (new) | dashboard, settings |
### 5. Framework-Level (Stay in ROOT)
- Module system (`modules.py`, `module_config.py`)
- Menu configuration (`menu_config.py`)
- System settings (`settings.py`)
- Base models (`base.py`, `platform_module.py`, `admin_menu_config.py`)
## Architecture Validation Updates
Added new rules to enforce module-first architecture:
- **MOD-016**: Routes must be in modules, not `app/api/v1/`
- **MOD-017**: Services must be in modules, not `app/services/`
- **MOD-018**: Tasks must be in modules, not `app/tasks/`
- **MOD-019**: Schemas must be in modules, not `models/schema/`
## Documentation Created
1. `docs/architecture/tenancy-module-migration.md` - Full migration plan
2. `docs/development/migration/module-autodiscovery-migration.md` - Migration history
3. Updated `docs/architecture/module-system.md` - Entity auto-discovery reference
## Next Actions
### Immediate (High Priority)
1. Migrate vendor auth routes to tenancy
2. Migrate admin auth routes to tenancy
3. Migrate vendor profile and team routes
### Short-term
4. Migrate company/vendor/platform admin routes
5. Move services to tenancy module
6. Create re-exports for backwards compatibility
### Medium-term
7. Migrate models to tenancy (careful - many dependencies)
8. Migrate schemas
9. Move messaging routes to messaging module
10. Move media routes to cms module
### Long-term
11. Create core module for dashboard/settings
12. Remove all re-exports once migration complete
13. Archive legacy directories
## Commits This Session
1. `401db56` - refactor: migrate remaining routes to modules and enforce auto-discovery
2. `23d5949` - refactor: move vendor info endpoint to tenancy module
## Key Principle
> **Tenancy owns identity and organizational hierarchy.**
> Everything else belongs to feature modules.
## Related Files
- `/docs/architecture/tenancy-module-migration.md`
- `/docs/architecture/module-system.md`
- `/docs/development/migration/module-autodiscovery-migration.md`
- `/.architecture-rules/module.yaml`
- `/scripts/validate_architecture.py`

View File

@@ -1,433 +0,0 @@
# Modular Platform Architecture - Design Plan
## Executive Summary
Design a modular architecture where platforms can enable/disable feature modules. This creates a hierarchy:
```
Global (SaaS Provider)
└── Platform (Business Product - OMS, Loyalty, etc.)
├── Modules (Enabled features - Billing, Marketplace, Inventory, etc.)
│ ├── Routes (API + Page routes)
│ ├── Services (Business logic)
│ ├── Menu Items (Sidebar entries)
│ └── Templates (UI components)
└── Frontends
├── Admin (Platform management)
├── Vendor (Vendor dashboard)
└── Customer (Storefront) - future
```
---
## Current State Analysis
### What Exists
| Component | Status | Location |
|-----------|--------|----------|
| Platform Model | ✅ Complete | `models/database/platform.py` |
| Platform Configs | ⚠️ Partial | `app/platforms/{oms,loyalty}/config.py` (routes/templates empty) |
| Feature Registry | ✅ Complete | `models/database/feature.py` (50+ features) |
| Feature Gating | ✅ Complete | `app/core/feature_gate.py` + `app/services/feature_service.py` |
| Subscription Tiers | ✅ Complete | `models/database/subscription.py` (tier→features mapping) |
| Menu System | ✅ Complete | `app/config/menu_registry.py` + `AdminMenuConfig` model |
| Platform Context | ✅ Complete | `middleware/platform_context.py` (domain/path detection) |
### Key Insight: Features vs Modules
**Current "Features"** = granular capabilities (e.g., `analytics_dashboard`, `letzshop_sync`)
- Assigned to subscription tiers
- Gated at API route level
- 50+ individual features
**Proposed "Modules"** = cohesive feature bundles (e.g., `billing`, `marketplace`, `inventory`)
- Enabled/disabled per platform
- Contains multiple features, routes, menu items
- ~10-15 modules total
---
## Proposed Architecture
### Module Definition
A **Module** is a self-contained unit of functionality:
```python
# app/modules/base.py
class ModuleDefinition:
"""Base class for all modules."""
# Identity
code: str # "billing", "marketplace", "inventory"
name: str # "Billing & Subscriptions"
description: str
# Dependencies
requires: list[str] = [] # Other module codes required
# Components
features: list[str] = [] # Feature codes this module provides
menu_items: dict[FrontendType, list[str]] = {} # Menu items per frontend
# Routes (registered dynamically)
admin_router: APIRouter | None = None
vendor_router: APIRouter | None = None
# Status
is_core: bool = False # Core modules cannot be disabled
```
### Module Registry
```python
# app/modules/registry.py
MODULES = {
# Core modules (always enabled)
"core": ModuleDefinition(
code="core",
name="Core Platform",
is_core=True,
features=["dashboard", "settings", "profile"],
menu_items={
FrontendType.ADMIN: ["dashboard", "settings"],
FrontendType.VENDOR: ["dashboard", "settings"],
},
),
# Optional modules
"billing": ModuleDefinition(
code="billing",
name="Billing & Subscriptions",
features=["subscription_management", "billing_history", "stripe_integration"],
menu_items={
FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"],
FrontendType.VENDOR: ["billing"],
},
admin_router=billing_admin_router,
vendor_router=billing_vendor_router,
),
"marketplace": ModuleDefinition(
code="marketplace",
name="Marketplace (Letzshop)",
requires=["inventory"], # Depends on inventory module
features=["letzshop_sync", "marketplace_import"],
menu_items={
FrontendType.ADMIN: ["marketplace-letzshop"],
FrontendType.VENDOR: ["letzshop", "marketplace"],
},
),
"inventory": ModuleDefinition(
code="inventory",
name="Inventory Management",
features=["inventory_basic", "inventory_locations", "low_stock_alerts"],
menu_items={
FrontendType.ADMIN: ["inventory"],
FrontendType.VENDOR: ["inventory"],
},
),
# ... more modules
}
```
### Proposed Modules
| Module | Description | Features | Core? |
|--------|-------------|----------|-------|
| `core` | Dashboard, Settings, Profile | 3 | Yes |
| `platform-admin` | Companies, Vendors, Admin Users | 5 | Yes |
| `billing` | Subscriptions, Tiers, Billing History | 4 | No |
| `inventory` | Stock management, locations, alerts | 5 | No |
| `orders` | Order management, fulfillment | 6 | No |
| `marketplace` | Letzshop integration, import | 3 | No |
| `customers` | Customer management, CRM | 4 | No |
| `cms` | Content pages, media library | 6 | No |
| `analytics` | Dashboard, reports, exports | 4 | No |
| `messaging` | Internal messages, notifications | 3 | No |
| `dev-tools` | Components, icons (internal) | 2 | No |
| `monitoring` | Logs, background tasks, imports | 4 | No |
---
## Database Changes
### Option A: JSON Field (Simpler)
Use existing `Platform.settings` JSON field:
```python
# Platform.settings example
{
"enabled_modules": ["core", "billing", "inventory", "orders"],
"module_config": {
"billing": {"stripe_mode": "live"},
"inventory": {"low_stock_threshold": 10}
}
}
```
**Pros:** No migration needed, flexible
**Cons:** No referential integrity, harder to query
### Option B: Junction Table (Recommended)
New `PlatformModule` model:
```python
# models/database/platform_module.py
class PlatformModule(Base, TimestampMixin):
"""Module enablement per platform."""
__tablename__ = "platform_modules"
id = Column(Integer, primary_key=True)
platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
module_code = Column(String(50), nullable=False)
is_enabled = Column(Boolean, default=True)
config = Column(JSON, default={}) # Module-specific config
enabled_at = Column(DateTime)
enabled_by_user_id = Column(Integer, ForeignKey("users.id"))
__table_args__ = (
UniqueConstraint("platform_id", "module_code"),
)
```
**Pros:** Proper normalization, audit trail, queryable
**Cons:** Requires migration
---
## Implementation Phases
### Phase 1: Module Registry (No DB Changes)
1. Create `app/modules/` directory structure:
```
app/modules/
├── __init__.py
├── base.py # ModuleDefinition class
├── registry.py # MODULES dict
└── service.py # ModuleService
```
2. Define all modules in registry (data only, no behavior change)
3. Create `ModuleService`:
```python
class ModuleService:
def get_platform_modules(platform_id: int) -> list[str]
def is_module_enabled(platform_id: int, module_code: str) -> bool
def get_module_menu_items(platform_id: int, frontend_type: FrontendType) -> list[str]
```
4. Initially read from `Platform.settings["enabled_modules"]` (Option A)
### Phase 2: Integrate with Menu System
1. Update `MenuService.get_menu_for_rendering()`:
- Filter menu items based on enabled modules
- Module-disabled items don't appear (not just hidden)
2. Update `AdminMenuConfig` logic:
- Can only configure visibility for module-enabled items
- Module-disabled items are completely removed
### Phase 3: Database Model (Optional)
1. Create `PlatformModule` model
2. Migration to create table
3. Migrate data from `Platform.settings["enabled_modules"]`
4. Update `ModuleService` to use new table
### Phase 4: Dynamic Route Registration
1. Modify `app/api/v1/admin/__init__.py`:
```python
def register_module_routes(app: FastAPI, platform_code: str):
enabled_modules = module_service.get_enabled_modules(platform_code)
for module in enabled_modules:
if module.admin_router:
app.include_router(module.admin_router)
```
2. Add module check middleware for routes
### Phase 5: Admin UI for Module Management
1. Create `/admin/platforms/{code}/modules` page
2. Toggle modules on/off per platform
3. Show module dependencies
4. Module-specific configuration
---
## Directory Structure Evolution
### Current
```
app/
├── api/v1/
│ ├── admin/ # All admin routes mixed
│ └── vendor/ # All vendor routes mixed
├── platforms/
│ ├── oms/config.py # Platform config only
│ └── loyalty/config.py
└── services/ # All services mixed
```
### Proposed (Gradual Migration)
```
app/
├── modules/
│ ├── base.py
│ ├── registry.py
│ ├── service.py
│ ├── core/ # Core module
│ │ ├── __init__.py
│ │ └── definition.py
│ ├── billing/ # Billing module
│ │ ├── __init__.py
│ │ ├── definition.py
│ │ ├── routes/
│ │ │ ├── admin.py
│ │ │ └── vendor.py
│ │ └── services/
│ │ └── subscription_service.py
│ ├── marketplace/ # Marketplace module
│ │ └── ...
│ └── inventory/ # Inventory module
│ └── ...
├── api/v1/ # Legacy routes (gradually migrate)
└── platforms/ # Platform-specific overrides
├── oms/
└── loyalty/
```
---
## Key Design Decisions Needed
### 1. Migration Strategy
| Option | Description |
|--------|-------------|
| **A: Big Bang** | Move all code to modules at once |
| **B: Gradual** | Keep existing structure, modules are metadata only initially |
| **C: Hybrid** | New features in modules, migrate existing over time |
**Recommendation:** Option C (Hybrid) - Start with module definitions as metadata, then gradually move code.
### 2. Module Granularity
| Option | Example |
|--------|---------|
| **Coarse** | 5-8 large modules (billing, operations, content) |
| **Medium** | 10-15 medium modules (billing, inventory, orders, cms) |
| **Fine** | 20+ small modules (subscription-tiers, invoices, stock-levels) |
**Recommendation:** Medium granularity - matches current menu sections.
### 3. Core vs Optional
Which modules should be mandatory (cannot be disabled)?
**Proposed Core:**
- `core` (dashboard, settings)
- `platform-admin` (companies, vendors, admin-users)
**Everything else optional** (including billing - some platforms may not charge).
---
## Relationship to Existing Systems
### Modules → Features
- Module contains multiple features
- Enabling module makes its features available for tier assignment
- Features still gated by subscription tier
### Modules → Menu Items
- Module specifies which menu items it provides
- Menu items only visible if module enabled AND menu visibility allows
### Modules → Routes
- Module can provide admin and vendor routers
- Routes only registered if module enabled
- Existing `require_menu_access()` still applies
### Platform Config → Modules
- `app/platforms/oms/config.py` can specify default modules
- Database `PlatformModule` or `Platform.settings` overrides defaults
---
## Verification Plan
1. **Module definition only (Phase 1)**
- Define all modules in registry
- Add `enabled_modules` to Platform.settings
- Verify ModuleService returns correct modules
2. **Menu integration (Phase 2)**
- Disable "billing" module for Loyalty platform
- Verify billing menu items don't appear in sidebar
- Verify `/admin/subscriptions` returns 404 or redirect
3. **Full module isolation (Phase 4)**
- Create new platform with minimal modules
- Verify only enabled module routes are accessible
- Verify module dependencies are enforced
---
## Decisions Made
| Decision | Choice | Rationale |
|----------|--------|-----------|
| **Storage** | JSON field (`Platform.settings`) | No migration needed, can upgrade to table later |
| **Migration** | Gradual | Module definitions as metadata first, migrate code over time |
| **Billing** | Optional module | Some platforms may not charge (e.g., internal loyalty) |
| **First module** | `billing` | Self-contained, clear routes/services, good isolation test |
---
## Implementation Roadmap
### Immediate (Phase 1): Module Foundation
1. Create `app/modules/` directory with base classes
2. Define module registry with all ~12 modules
3. Create `ModuleService` reading from `Platform.settings`
4. Add `enabled_modules` to OMS and Loyalty platform settings
### Next (Phase 2): Menu Integration
1. Update `MenuService` to filter by enabled modules
2. Test: Disable billing module → billing menu items disappear
### Then (Phase 3): Billing Module Extraction
1. Create `app/modules/billing/` structure
2. Move billing routes and services into module
3. Register billing routes dynamically based on module status
### Future: Additional Modules
- Extract marketplace, inventory, orders, etc.
- Consider junction table if audit trail becomes important
---
## Files to Create/Modify
| File | Action | Purpose |
|------|--------|---------|
| `app/modules/__init__.py` | CREATE | Module package init |
| `app/modules/base.py` | CREATE | ModuleDefinition dataclass |
| `app/modules/registry.py` | CREATE | MODULES dict with all definitions |
| `app/modules/service.py` | CREATE | ModuleService class |
| `app/services/menu_service.py` | MODIFY | Filter by enabled modules |
| `app/platforms/oms/config.py` | MODIFY | Add enabled_modules |
| `app/platforms/loyalty/config.py` | MODIFY | Add enabled_modules |

View File

@@ -1,609 +0,0 @@
# Module Migration Plan
**Status:** In Progress
**Started:** 2026-01-25
**Last Updated:** 2026-01-28
**Target:** v1.0.0
This is the unified migration plan for transforming Wizamart into a fully modular architecture.
---
## Executive Summary
Transform the platform from a monolithic structure to self-contained modules where each module owns its:
- Services (business logic)
- Models (database entities)
- Schemas (Pydantic DTOs)
- Tasks (Celery background jobs)
- Templates (UI if applicable)
- Migrations (database changes)
---
## Current State Assessment
### Module Migration Status
| Module | Classification | Current State | Services | Models | Tasks | Target |
|--------|---------------|---------------|----------|--------|-------|--------|
| `cms` | Core | ✅ **Complete** | ✅ | ✅ | - | Done |
| `payments` | Optional | 🟡 Partial | ✅ | ✅ | - | Done |
| `billing` | Optional | ✅ **Complete** | ✅ | ✅ | ✅ | Done |
| `marketplace` | Optional | ✅ **Complete** | ✅ | ✅ | ✅ | Done |
| `orders` | Optional | ✅ **Complete** | ✅ | ✅ | - | Done |
| `inventory` | Optional | ✅ **Complete** | ✅ | ✅ | - | Done |
| `customers` | Core | ✅ **Complete** | ✅ | ✅ | - | Done |
| `analytics` | Optional | ✅ **Complete** | ✅ | - | - | Done |
| `messaging` | Optional | ✅ **Complete** | ✅ | ✅ | - | Done |
| `monitoring` | Internal | ✅ **Complete** | ✅ | ✅ | - | Done |
| `dev-tools` | Internal | ✅ **Complete** | ✅ | ✅ | ✅ | Done |
| `tenancy` | Core | 🔴 Shell | ❌ | ❌ | - | Full |
| `core` | Core | 🔴 Shell | ❌ | ❌ | - | Minimal |
### Current Code Locations (To Be Migrated)
```
app/services/ # → Move to respective modules
├── subscription_service.py # → billing/services/
├── stripe_service.py # → billing/services/ (or payments)
├── customer_service.py # → customers/services/
├── order_service.py # → orders/services/
├── inventory_service.py # → inventory/services/
├── letzshop_service.py # → marketplace/services/
├── marketplace_import_service.py # → marketplace/services/
├── messaging_service.py # → messaging/services/
└── ...
models/database/ # → Move to respective modules
├── subscription.py # → billing/models/
├── customer.py # → customers/models/
├── order.py # → orders/models/
├── inventory.py # → inventory/models/
├── letzshop_*.py # → marketplace/models/
└── ...
app/tasks/celery_tasks/ # → Move to respective modules
├── subscription.py # → billing/tasks/
├── marketplace.py # → marketplace/tasks/
├── letzshop.py # → marketplace/tasks/
├── export.py # → marketplace/tasks/
├── code_quality.py # → dev_tools/tasks/
└── test_runner.py # → dev_tools/tasks/
```
### Celery Task Mapping
| Current Task | Current Location | Target Module |
|--------------|------------------|---------------|
| `reset_period_counters` | subscription.py | `billing` |
| `check_trial_expirations` | subscription.py | `billing` |
| `sync_stripe_status` | subscription.py | `billing` |
| `cleanup_stale_subscriptions` | subscription.py | `billing` |
| `capture_capacity_snapshot` | subscription.py | `monitoring` |
| `process_marketplace_import` | marketplace.py | `marketplace` |
| `process_historical_import` | letzshop.py | `marketplace` |
| `sync_vendor_directory` | letzshop.py | `marketplace` |
| `export_vendor_products_to_folder` | export.py | `marketplace` |
| `export_marketplace_products` | export.py | `marketplace` |
| `execute_code_quality_scan` | code_quality.py | `dev-tools` |
| `execute_test_run` | test_runner.py | `dev-tools` |
---
## Infrastructure Work (Completed)
### ✅ Phase 1: Module Classification
- Three-tier system: Core, Optional, Internal
- Renamed `platform-admin``tenancy`
- Module registry with CORE_MODULES, OPTIONAL_MODULES, INTERNAL_MODULES
### ✅ Phase 2: Module Infrastructure
- `ModuleDefinition` with lifecycle hooks
- Module events system (`events.py`)
- Module migration discovery (`migrations.py`)
- Observability framework (`observability.py`)
### ✅ Phase 3: Documentation
- Module system architecture docs
- Menu management docs
- Observability docs
- Creating modules developer guide
### ✅ Phase 4: Celery Task Infrastructure
- Added `ScheduledTask` dataclass to `ModuleDefinition`
- Added `tasks_path` and `scheduled_tasks` fields
- Created `app/modules/task_base.py` with `ModuleTask` base class
- Created `app/modules/tasks.py` with discovery utilities
- Updated `celery_config.py` to use module discovery
### ✅ Phase 5: Billing Module Migration
- Created `app/modules/billing/services/` with subscription, stripe, admin services
- Created `app/modules/billing/models/` re-exporting from central location
- Created `app/modules/billing/schemas/` re-exporting from central location
- Created `app/modules/billing/tasks/` with 4 scheduled tasks
- Created `app/modules/billing/exceptions.py`
- Updated `definition.py` with self-contained configuration
- Created backward-compatible re-exports in `app/services/`
- Updated legacy celery_config.py to not duplicate scheduled tasks
### ✅ Phase 6: Marketplace Module Migration
- Created `app/modules/marketplace/services/` re-exporting from existing locations
- Created `app/modules/marketplace/models/` re-exporting marketplace & letzshop models
- Created `app/modules/marketplace/schemas/` re-exporting marketplace schemas
- Created `app/modules/marketplace/tasks/` with:
- `import_tasks.py` - process_marketplace_import, process_historical_import
- `sync_tasks.py` - sync_vendor_directory (scheduled daily)
- `export_tasks.py` - export_vendor_products_to_folder, export_marketplace_products
- Created `app/modules/marketplace/exceptions.py`
- Updated `definition.py` with self-contained configuration
- Updated legacy task files to re-export from new location
- Removed marketplace/letzshop/export from LEGACY_TASK_MODULES
### ✅ Phase 7: Dev-Tools Module Migration
- Created `app/modules/dev_tools/models/` with:
- `architecture_scan.py` - ArchitectureScan, ArchitectureViolation, ArchitectureRule, etc.
- `test_run.py` - TestRun, TestResult, TestCollection
- Created `app/modules/dev_tools/services/` re-exporting code_quality_service, test_runner_service
- Created `app/modules/dev_tools/schemas/` (placeholder for future schemas)
- Created `app/modules/dev_tools/tasks/` with:
- `code_quality.py` - execute_code_quality_scan
- `test_runner.py` - execute_test_run
- Created `app/modules/dev_tools/exceptions.py` with test runner exceptions
- Created `app/modules/dev_tools/routes/api/` with admin router
- Updated `definition.py` with self-contained configuration
- Updated legacy model files to re-export from module location
- Updated legacy task files to re-export from module location
- Used lazy imports in `__init__.py` to avoid circular import issues
### ✅ Phase 8: Monitoring Module Migration
- Created `app/modules/monitoring/models/` re-exporting CapacitySnapshot, AdminNotification
- Created `app/modules/monitoring/services/` re-exporting BackgroundTasksService
- Created `app/modules/monitoring/schemas/` (placeholder)
- Created `app/modules/monitoring/exceptions.py` with monitoring-specific exceptions
- Updated `definition.py` with self-contained configuration
- Used lazy imports in `__init__.py` to avoid circular import issues
### ✅ Phase 9: Customers Module Migration
- Created `app/modules/customers/models/` re-exporting Customer, CustomerAddress, PasswordResetToken
- Created `app/modules/customers/services/` re-exporting customer services
- Created `app/modules/customers/schemas/` re-exporting customer schemas
- Created `app/modules/customers/exceptions.py` re-exporting customer exceptions
- Updated `definition.py` with self-contained configuration
- Used lazy imports in `__init__.py` to avoid circular import issues
### ✅ Phase 10: Orders Module Migration
- Created `app/modules/orders/models/` re-exporting Order, OrderItem, Invoice, etc.
- Created `app/modules/orders/services/` re-exporting order and invoice services
- Created `app/modules/orders/schemas/` re-exporting order and invoice schemas
- Created `app/modules/orders/exceptions.py` re-exporting order exceptions
- Updated `definition.py` with self-contained configuration
- Used lazy imports in `__init__.py` to avoid circular import issues
### ✅ Phase 11: Inventory Module Migration
- Created `app/modules/inventory/models/` re-exporting Inventory, InventoryTransaction
- Created `app/modules/inventory/services/` re-exporting inventory services
- Created `app/modules/inventory/schemas/` re-exporting inventory schemas
- Created `app/modules/inventory/exceptions.py` re-exporting inventory exceptions
- Updated `definition.py` with self-contained configuration
- Used lazy imports in `__init__.py` to avoid circular import issues
### ✅ Phase 12: Analytics & Messaging Module Migration
- **Analytics Module:**
- Created `app/modules/analytics/models/` (empty - uses data from other modules)
- Created `app/modules/analytics/services/` re-exporting StatsService, UsageService
- Created `app/modules/analytics/schemas/` re-exporting stats schemas
- Created `app/modules/analytics/exceptions.py` with reporting exceptions
- Updated `definition.py` with self-contained configuration
- **Messaging Module:**
- Created `app/modules/messaging/models/` re-exporting Conversation, Message, etc.
- Created `app/modules/messaging/services/` re-exporting messaging services
- Created `app/modules/messaging/schemas/` re-exporting messaging schemas
- Created `app/modules/messaging/exceptions.py` re-exporting message exceptions
- Updated `definition.py` with self-contained configuration
---
## Module Migration Phases
Each module migration includes: services, models, schemas, tasks, templates (if any).
### Phase 5: Billing Module (High Priority)
**Why first:** Has most Celery tasks, critical business logic.
#### Components to Move
| From | To | Type |
|------|-----|------|
| `app/services/subscription_service.py` | `app/modules/billing/services/` | Service |
| `app/services/stripe_service.py` | `app/modules/billing/services/` | Service |
| `models/database/subscription.py` | `app/modules/billing/models/` | Model |
| `models/database/subscription_tier.py` | `app/modules/billing/models/` | Model |
| `models/schema/subscription.py` | `app/modules/billing/schemas/` | Schema |
| `app/tasks/celery_tasks/subscription.py` | `app/modules/billing/tasks/` | Tasks |
#### Target Structure
```
app/modules/billing/
├── __init__.py
├── definition.py # Update with tasks_path, scheduled_tasks
├── config.py # BillingConfig schema
├── exceptions.py # BillingException, SubscriptionError, etc.
├── services/
│ ├── __init__.py
│ ├── subscription_service.py
│ └── stripe_service.py
├── models/
│ ├── __init__.py
│ ├── subscription.py
│ └── subscription_tier.py
├── schemas/
│ ├── __init__.py
│ └── subscription.py
├── tasks/
│ ├── __init__.py
│ └── subscription.py # 4 scheduled tasks
├── routes/
│ ├── admin.py
│ └── vendor.py
└── migrations/
└── versions/
```
#### Tasks
| Task | Schedule | Queue |
|------|----------|-------|
| `reset_period_counters` | Daily 00:05 | scheduled |
| `check_trial_expirations` | Daily 01:00 | scheduled |
| `sync_stripe_status` | Hourly :30 | scheduled |
| `cleanup_stale_subscriptions` | Weekly Sun 03:00 | scheduled |
#### Migration Checklist
- [ ] Create `billing/services/` directory
- [ ] Move `subscription_service.py` → update imports
- [ ] Move `stripe_service.py` → update imports
- [ ] Create `billing/models/` directory
- [ ] Move subscription models → update imports
- [ ] Create `billing/schemas/` directory
- [ ] Move subscription schemas → update imports
- [ ] Create `billing/tasks/` directory
- [ ] Move subscription tasks → update task paths
- [ ] Create `billing/exceptions.py`
- [ ] Update `definition.py` with `tasks_path`, `scheduled_tasks`
- [ ] Create module migrations
- [ ] Update all imports across codebase
- [ ] Test subscription flows
- [ ] Test scheduled tasks
---
### Phase 6: Marketplace Module (High Priority)
**Why second:** Has most tasks, complex import logic.
#### Components to Move
| From | To | Type |
|------|-----|------|
| `app/services/letzshop_service.py` | `app/modules/marketplace/services/` | Service |
| `app/services/marketplace_import_service.py` | `app/modules/marketplace/services/` | Service |
| `app/services/letzshop_credentials_service.py` | `app/modules/marketplace/services/` | Service |
| `models/database/letzshop_*.py` | `app/modules/marketplace/models/` | Models |
| `models/database/marketplace_import_job.py` | `app/modules/marketplace/models/` | Model |
| `app/tasks/celery_tasks/marketplace.py` | `app/modules/marketplace/tasks/` | Tasks |
| `app/tasks/celery_tasks/letzshop.py` | `app/modules/marketplace/tasks/` | Tasks |
| `app/tasks/celery_tasks/export.py` | `app/modules/marketplace/tasks/` | Tasks |
#### Target Structure
```
app/modules/marketplace/
├── __init__.py
├── definition.py
├── exceptions.py
├── services/
│ ├── __init__.py
│ ├── letzshop_service.py
│ ├── letzshop_credentials_service.py
│ ├── import_service.py
│ └── export_service.py
├── models/
│ ├── __init__.py
│ ├── letzshop_order.py
│ ├── letzshop_credentials.py
│ └── import_job.py
├── schemas/
│ └── ...
├── tasks/
│ ├── __init__.py
│ ├── import_tasks.py # process_marketplace_import
│ ├── letzshop.py # process_historical_import, sync_vendor_directory
│ └── export.py # export tasks
└── routes/
```
#### Tasks
| Task | Type | Queue |
|------|------|-------|
| `process_marketplace_import` | On-demand | long_running |
| `process_historical_import` | On-demand | long_running |
| `sync_vendor_directory` | Scheduled (optional) | long_running |
| `export_vendor_products_to_folder` | On-demand | default |
| `export_marketplace_products` | On-demand | default |
---
### Phase 7: Dev-Tools Module (Medium Priority)
**Why:** Internal module with code quality and test runner tasks.
#### Components to Move
| From | To | Type |
|------|-----|------|
| `app/services/code_quality_service.py` | `app/modules/dev_tools/services/` | Service |
| `app/services/test_runner_service.py` | `app/modules/dev_tools/services/` | Service |
| `models/database/architecture_scan.py` | `app/modules/dev_tools/models/` | Model |
| `models/database/test_run.py` | `app/modules/dev_tools/models/` | Model |
| `app/tasks/celery_tasks/code_quality.py` | `app/modules/dev_tools/tasks/` | Tasks |
| `app/tasks/celery_tasks/test_runner.py` | `app/modules/dev_tools/tasks/` | Tasks |
#### Target Structure
```
app/modules/dev_tools/
├── __init__.py
├── definition.py
├── services/
│ ├── code_quality_service.py
│ └── test_runner_service.py
├── models/
│ ├── architecture_scan.py
│ ├── architecture_violation.py
│ └── test_run.py
├── tasks/
│ ├── code_quality.py
│ └── test_runner.py
└── routes/
```
---
### Phase 8: Monitoring Module (Medium Priority)
#### Components to Move
| From | To | Type |
|------|-----|------|
| `app/services/background_tasks_service.py` | `app/modules/monitoring/services/` | Service |
| `capture_capacity_snapshot` task | `app/modules/monitoring/tasks/` | Task |
| `models/database/capacity_snapshot.py` | `app/modules/monitoring/models/` | Model |
#### Target Structure
```
app/modules/monitoring/
├── __init__.py
├── definition.py
├── services/
│ └── background_tasks_service.py
├── models/
│ └── capacity_snapshot.py
├── tasks/
│ └── capacity.py # capture_capacity_snapshot
└── routes/
```
---
### Phase 9: Customers Module (Core)
#### Components to Move
| From | To |
|------|-----|
| `app/services/customer_service.py` | `app/modules/customers/services/` |
| `models/database/customer.py` | `app/modules/customers/models/` |
| Customer-related schemas | `app/modules/customers/schemas/` |
---
### Phase 10: Orders Module
#### Components to Move
| From | To |
|------|-----|
| `app/services/order_service.py` | `app/modules/orders/services/` |
| `models/database/order.py` | `app/modules/orders/models/` |
| Order-related schemas | `app/modules/orders/schemas/` |
---
### Phase 11: Inventory Module
#### Components to Move
| From | To |
|------|-----|
| `app/services/inventory_service.py` | `app/modules/inventory/services/` |
| `models/database/inventory*.py` | `app/modules/inventory/models/` |
---
### Phase 12: Remaining Modules
- **Analytics** - Move analytics services
- **Messaging** - Move messaging service, notification models
- **Tenancy** - Move platform, company, vendor services
---
## Phase 13: Cleanup & Full Celery Migration
After all modules are migrated:
### 13.1 Remove Fallback Tasks
Delete:
```
app/tasks/background_tasks.py
app/tasks/letzshop_tasks.py
app/tasks/subscription_tasks.py
app/tasks/code_quality_tasks.py
app/tasks/test_runner_tasks.py
```
### 13.2 Remove USE_CELERY Flag
```python
# app/core/config.py
# DELETE: USE_CELERY: bool = False
```
### 13.3 Simplify Dispatcher
Rewrite `app/tasks/dispatcher.py` to Celery-only.
### 13.4 Delete Old Task Directory
```
app/tasks/celery_tasks/ # DELETE - moved to modules
```
### 13.5 Clean Up Old Services/Models
Remove files from:
- `app/services/` (moved to modules)
- `models/database/` (moved to modules)
Leave re-exports for backward compatibility if needed.
---
## Queue Configuration
### Final Queue Structure
| Queue | Purpose | Modules |
|-------|---------|---------|
| `default` | Fast tasks | marketplace (exports) |
| `long_running` | CPU-intensive | marketplace (imports), dev-tools |
| `scheduled` | Celery Beat | billing, monitoring |
### Task Routing
```python
task_routes = {
"app.modules.billing.tasks.*": {"queue": "scheduled"},
"app.modules.marketplace.tasks.import_tasks.*": {"queue": "long_running"},
"app.modules.marketplace.tasks.letzshop.*": {"queue": "long_running"},
"app.modules.marketplace.tasks.export.*": {"queue": "default"},
"app.modules.monitoring.tasks.*": {"queue": "scheduled"},
"app.modules.dev_tools.tasks.*": {"queue": "long_running"},
}
```
---
## Migration Strategy
### For Each Module
1. **Create directory structure**
2. **Move models first** (fewest dependencies)
3. **Move services** (depend on models)
4. **Move schemas** (depend on models)
5. **Move tasks** (depend on services)
6. **Update all imports** across codebase
7. **Create re-exports** in old locations (temporary)
8. **Test thoroughly**
9. **Remove re-exports** in next phase
### Import Update Strategy
Use IDE refactoring or script:
```bash
# Example: Update subscription_service imports
find . -name "*.py" -exec sed -i 's/from app.services.subscription_service/from app.modules.billing.services.subscription_service/g' {} \;
```
---
## Verification Checklist
### Per-Module Verification
- [ ] Module directory has all components
- [ ] All imports updated
- [ ] Services work
- [ ] API routes work
- [ ] Tasks execute correctly
- [ ] Scheduled tasks run (Celery Beat)
- [ ] No circular imports
- [ ] Tests pass
### Final Verification
- [ ] All 12 tasks in modules
- [ ] `USE_CELERY` flag removed
- [ ] Fallback tasks deleted
- [ ] Old service files removed
- [ ] Old model files removed
- [ ] All tests pass
- [ ] Production smoke test
---
## Priority Order
| Priority | Phase | Module | Reason |
|----------|-------|--------|--------|
| 1 | 4 | Infrastructure | Required for task migration |
| 2 | 5 | `billing` | Most tasks, critical business logic |
| 3 | 6 | `marketplace` | Most complex, many tasks |
| 4 | 7 | `dev-tools` | Internal, low risk |
| 5 | 8 | `monitoring` | Internal, low risk |
| 6 | 9-12 | Others | Lower complexity |
| 7 | 13 | Cleanup | Final step |
---
## Estimated Effort
| Phase | Sessions | Notes |
|-------|----------|-------|
| 4 (Infrastructure) | 1 | Task base class, discovery |
| 5 (Billing) | 2-3 | Complex, many components |
| 6 (Marketplace) | 3-4 | Most complex module |
| 7-8 (Internal) | 1-2 | Simpler modules |
| 9-12 (Others) | 4-6 | Medium complexity each |
| 13 (Cleanup) | 1 | Removing old code |
**Total:** ~12-17 sessions
---
## Rollback Plan
Each phase can be rolled back independently:
1. Restore moved files to original locations
2. Revert import changes
3. Restore celery_config.py
No database schema changes during code migration = no DB rollback needed.
---
## Related Documents
- [Module System Architecture](../architecture/module-system.md)
- [Creating Modules](../development/creating-modules.md)
- [Observability](../architecture/observability.md)
- [SESSION_NOTE_2026-01-27](SESSION_NOTE_2026-01-27_module-reclassification.md)

View File

@@ -1,435 +0,0 @@
# Multi-Platform CMS Architecture - Implementation Plan
> **Status:** All Phases Complete (1-7)
> **Last Updated:** 2026-01-19
> **Commits:**
> - `408019d` (Phase 1: Database & Models)
> - `9680026` (Phases 2-5: Migration, Admin, Docs, Vendor Dashboard)
> - `32bcbc8` (Phase 6: Loyalty Platform Setup)
> - `a2407ae` (Phase 7: Platform URL Routing Strategy)
---
## Executive Summary
Transform the single-platform OMS into a multi-platform system supporting independent business offerings (OMS, Loyalty, Site Builder), each with its own CMS, vendor defaults, and marketing pages.
---
## Phase 1: Database & Models ✅ COMPLETE
### Completed Work
| Task | File | Status |
|------|------|--------|
| Platform model | `models/database/platform.py` | ✅ |
| VendorPlatform junction table | `models/database/vendor_platform.py` | ✅ |
| SubscriptionTier updates | `models/database/subscription.py` | ✅ |
| ContentPage updates | `models/database/content_page.py` | ✅ |
| CMS feature codes | `models/database/feature.py` | ✅ |
| Model exports | `models/database/__init__.py` | ✅ |
| PlatformContextMiddleware | `middleware/platform_context.py` | ✅ |
| Middleware exports | `middleware/__init__.py` | ✅ |
| ContentPageService updates | `app/services/content_page_service.py` | ✅ |
| Alembic migration | `alembic/versions/z4e5f6a7b8c9_...py` | ✅ |
| Platform folder structure | `app/platforms/` | ✅ |
---
## Phase 2: OMS Migration & Integration ✅ COMPLETE
### 2.1 Run Database Migration
```bash
# 1. Backup database first
pg_dump wizamart > wizamart_backup_$(date +%Y%m%d).sql
# 2. Run migration
alembic upgrade head
# 3. Verify migration
psql -d wizamart -c "SELECT * FROM platforms;"
psql -d wizamart -c "SELECT COUNT(*) FROM vendor_platforms;"
psql -d wizamart -c "SELECT platform_id, is_platform_page, COUNT(*) FROM content_pages GROUP BY 1, 2;"
```
### 2.2 Register PlatformContextMiddleware in main.py
```python
# In main.py, add BEFORE VendorContextMiddleware
from middleware import PlatformContextMiddleware
# Order matters: Platform detection must run first
app.add_middleware(VendorContextMiddleware) # Runs second (existing)
app.add_middleware(PlatformContextMiddleware) # Runs first (NEW)
```
### 2.3 Update VendorContextMiddleware ✅ COMPLETE
File: `middleware/vendor_context.py`
Changes completed:
- [x] Use `request.state.platform_clean_path` instead of `request.url.path` for path-based vendor detection (line 52)
- [x] Skip vendor detection if no platform found (platform marketing pages like /oms/pricing)
- [x] Pass platform context to vendor lookup for multi-platform vendor support
### 2.4 Fix Platform Homepage Route
File: `app/routes/platform/homepage.py`
Current state (BROKEN):
- Uses hardcoded `homepage-wizamart.html` template
- Admin CMS changes are saved but ignored by route
Fix required:
```python
# Get platform from middleware
platform = request.state.platform
# Query CMS for platform homepage
page = content_page_service.get_platform_page(
db,
platform_id=platform.id,
slug="home"
)
if page:
# Render with selected template
return templates.TemplateResponse(
f"platform/homepage-{page.template}.html",
{"request": request, "page": page}
)
else:
# Fallback to hardcoded (temporary)
return templates.TemplateResponse("platform/homepage-wizamart.html", {...})
```
### 2.5 Update Content Page Routes
Files to update:
- [ ] `app/routes/platform/content_pages.py` - Add platform_id from request.state.platform
- [ ] `app/routes/vendor/content_pages.py` - Add platform_id to queries
- [ ] `app/routes/admin/content_pages.py` - Add platform filtering
---
## Phase 3: Admin Interface ✅ COMPLETE
### 3.1 Platform Management UI
| Task | File | Status |
|------|------|--------|
| Platform list page route | `app/routes/admin_pages.py` | ✅ |
| Platform detail/edit routes | `app/routes/admin_pages.py` | ✅ |
| Platform API endpoints | `app/api/v1/admin/platforms.py` | ✅ |
| Register API router | `app/api/v1/admin/__init__.py` | ✅ |
| Platforms template | `app/templates/admin/platforms.html` | ✅ |
| Platforms JS component | `static/admin/js/platforms.js` | ✅ |
| Sidebar menu item | `app/templates/admin/partials/sidebar.html` | ✅ |
### 3.2 Update Content Pages Admin
| Task | File | Status |
|------|------|--------|
| Platform dropdown filter | `app/templates/admin/content-pages.html` | ✅ |
| Four-tab tier view | `app/templates/admin/content-pages.html` | ✅ |
| Tier badges (Blue/Teal/Purple) | `static/admin/js/content-pages.js` | ✅ |
| Platform filter in JS | `static/admin/js/content-pages.js` | ✅ |
| API schema with platform fields | `app/api/v1/admin/content_pages.py` | ✅ |
| Model to_dict with platform_name | `models/database/content_page.py` | ✅ |
---
## Phase 4: Vendor Dashboard ✅ COMPLETE
### 4.1 Content Pages List Updates
| Task | File | Status |
|------|------|--------|
| Source indicators (Default/Override/Custom) | `app/templates/vendor/content-pages.html` | ✅ Already existed |
| Override Default button | `app/templates/vendor/content-pages.html` | ✅ Already existed |
| Revert to Default (delete override) | `static/vendor/js/content-pages.js` | ✅ Already existed |
| CMS usage API endpoint | `app/api/v1/vendor/content_pages.py` | ✅ New |
| CMS usage progress bar | `app/templates/vendor/content-pages.html` | ✅ New |
| Upgrade prompt at 80% limit | `app/templates/vendor/content-pages.html` | ✅ New |
| Load usage in JS | `static/vendor/js/content-pages.js` | ✅ New |
### 4.2 Page Editor Updates
| Task | File | Status |
|------|------|--------|
| Override info banner | `app/templates/vendor/content-page-edit.html` | ✅ Already existed |
| View Default button | `app/templates/vendor/content-page-edit.html` | ✅ New |
| Default preview modal | `app/templates/vendor/content-page-edit.html` | ✅ New |
| Platform default API | `app/api/v1/vendor/content_pages.py` | ✅ New |
| Show default preview JS | `static/vendor/js/content-page-edit.js` | ✅ New |
| Revert button (styled) | `app/templates/vendor/content-page-edit.html` | ✅ New |
---
## Phase 5: Routes & Templates ✅ COMPLETE
### 5.1 Platform-Prefixed Routes (Development)
Register in `main.py`:
```python
# Development mode: path-based routing
if settings.environment == "development":
app.mount("/oms", oms_router)
app.mount("/loyalty", loyalty_router)
```
### 5.2 Update Shop Routes ✅ COMPLETE
- [x] Add platform context to shop routes (`shop_pages.py` line 117)
- [x] Use `request.state.platform` for template selection
- [x] Pass platform to content page lookups (`platform_id` used in CMS queries)
### 5.3 Test All URL Patterns ✅ COMPLETE
Development (using /platforms/ prefix):
- [x] `localhost:9999/platforms/oms/` → OMS homepage
- [x] `localhost:9999/platforms/oms/pricing` → OMS pricing page
- [x] `localhost:9999/platforms/oms/vendors/{code}/` → Vendor storefront
- [x] `localhost:9999/platforms/loyalty/` → Loyalty homepage
---
## Phase 6: Loyalty Platform Setup ✅ COMPLETE
### 6.1 Database Setup ✅
Migration: `alembic/versions/z5f6g7h8i9j0_add_loyalty_platform.py`
Inserts Loyalty platform with:
- code: `loyalty`
- name: `Loyalty+`
- domain: `loyalty.lu`
- path_prefix: `loyalty`
- theme_config: purple color scheme
### 6.2 Platform Marketing Pages ✅
| Page | Slug | Status |
|------|------|--------|
| Homepage | `home` | ✅ |
| Pricing | `pricing` | ✅ |
| Features | `features` | ✅ |
| How It Works | `how-it-works` | ✅ |
### 6.3 Vendor Default Pages ✅
| Page | Slug | Status |
|------|------|--------|
| About | `about` | ✅ |
| Rewards Catalog | `rewards-catalog` | ✅ |
| Terms | `terms` | ✅ |
| Privacy | `privacy` | ✅ |
---
## Phase 7: Platform URL Routing Strategy ✅ COMPLETE
### 7.1 URL Structure Changes
**Previous approach (DEPRECATED):**
- Development: `/oms/`, `/loyalty/` (first path segment = platform)
- Ambiguity: `/faq` could be platform or page
**New approach:**
- Development: `/platforms/oms/`, `/platforms/loyalty/` (explicit prefix)
- Main marketing site: `/` (no prefix) → `main` platform
- Clear separation: `/faq` = main site, `/platforms/oms/faq` = OMS
### 7.2 Implementation Details
| Task | File | Status |
|------|------|--------|
| Update middleware URL detection | `middleware/platform_context.py` | ✅ |
| Change DEFAULT_PLATFORM_CODE | `middleware/platform_context.py` | ✅ |
| Remove hardcoded /oms, /loyalty routes | `main.py` | ✅ |
| Update platform_pages.py homepage | `app/routes/platform_pages.py` | ✅ |
| Add 'main' platform migration | `alembic/versions/z6g7h8i9j0k1_...py` | ✅ |
### 7.3 URL Routing Summary
| Platform | Code | Dev URL | Prod URL |
|----------|------|---------|----------|
| Main Marketing | `main` | `localhost:9999/` | `wizamart.lu/` |
| OMS | `oms` | `localhost:9999/platforms/oms/` | `oms.lu/` |
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `loyalty.lu/` |
### 7.4 Middleware Detection Logic
```
Request arrives
┌─────────────────────────────────────┐
│ Production domain? (oms.lu, etc.) │
└─────────────────────────────────────┘
│ YES → Route to that platform
▼ NO (localhost)
┌─────────────────────────────────────┐
│ Path starts with /platforms/{code}? │
└─────────────────────────────────────┘
│ YES → Strip prefix, route to platform
│ /platforms/oms/pricing → /pricing
▼ NO
┌─────────────────────────────────────┐
│ Use 'main' platform (DEFAULT) │
│ /faq → Main site FAQ page │
└─────────────────────────────────────┘
```
---
## Documentation Requirements ✅ COMPLETE
### Architecture Documentation ✅ COMPLETE
Updated documentation for Phase 7:
- [x] `docs/architecture/url-routing/overview.md` - Multi-platform URL routing section
- [x] `docs/architecture/multi-platform-cms.md` - Request flow with path rewriting
- [x] `docs/architecture/middleware.md` - PlatformContextMiddleware documentation
- [x] Three-tier content hierarchy explanation
- [x] Platform vs Vendor Default vs Vendor Override
- [x] Database schema diagrams
- [x] Request flow diagrams
- [x] API endpoints reference
- [x] Key files reference
- [x] Adding new platform guide with URL summary
### API Documentation
OpenAPI specs auto-generated from FastAPI:
- [x] Platform endpoints (`/api/v1/admin/platforms`)
- [x] Content page endpoints with platform fields
- [ ] Vendor platform membership endpoints (future)
### Developer Guide
Included in `docs/architecture/multi-platform-cms.md`:
- [x] Step-by-step platform creation
- [x] Required database records
- [x] Key files reference
- [x] Platform URL summary table
---
## Quick Reference
### Three-Tier Content Resolution
```
Customer visits: oms.lu/vendors/wizamart/about
┌─────────────────────────────────────────────────────────────┐
│ Tier 1: Vendor Override │
│ WHERE platform_id=1 AND vendor_id=123 AND slug='about' │
│ Found? → Return vendor's custom page │
└─────────────────────────────────────────────────────────────┘
│ Not found
┌─────────────────────────────────────────────────────────────┐
│ Tier 2: Vendor Default │
│ WHERE platform_id=1 AND vendor_id IS NULL │
│ AND is_platform_page=FALSE AND slug='about' │
│ Found? → Return platform default │
└─────────────────────────────────────────────────────────────┘
│ Not found
Return 404
```
### CMS Tier Limits
| Tier | Total Pages | Custom Pages |
|------|-------------|--------------|
| Essential | 3 | 0 |
| Professional | 10 | 5 |
| Business | 30 | 20 |
| Enterprise | Unlimited | Unlimited |
### Platform Marketing Page Slugs
`home`, `platform_homepage`, `pricing`, `about`, `contact`, `faq`, `terms`, `privacy`, `features`, `integrations`
---
## Verification Checklist
All phases are complete. Use these commands to verify:
```bash
# 1. Check platforms in database
psql -d wizamart -c "SELECT code, name, domain FROM platforms;"
# Expected: main, oms, loyalty
# 2. Test main marketing site
curl -s localhost:9999/ | grep -o "<title>.*</title>"
# Expected: Main marketing site title
# 3. Test OMS platform
curl -s localhost:9999/platforms/oms/ | grep -o "<title>.*</title>"
# Expected: OMS platform title
# 4. Test Loyalty platform
curl -s localhost:9999/platforms/loyalty/ | grep -o "<title>.*</title>"
# Expected: Loyalty platform title
# 5. Verify middleware detection
python -c "
from middleware.platform_context import PlatformContextMiddleware, DEFAULT_PLATFORM_CODE
print(f'DEFAULT_PLATFORM_CODE: {DEFAULT_PLATFORM_CODE}')
# Expected: main
"
```
---
## Files Created/Modified in Phase 1
### New Files (17)
```
models/database/platform.py
models/database/vendor_platform.py
middleware/platform_context.py
alembic/versions/z4e5f6a7b8c9_add_multi_platform_support.py
app/platforms/__init__.py
app/platforms/oms/__init__.py
app/platforms/oms/config.py
app/platforms/oms/routes/__init__.py
app/platforms/oms/templates/__init__.py
app/platforms/loyalty/__init__.py
app/platforms/loyalty/config.py
app/platforms/loyalty/routes/__init__.py
app/platforms/loyalty/templates/__init__.py
app/platforms/shared/__init__.py
app/platforms/shared/base_platform.py
app/platforms/shared/routes/__init__.py
app/platforms/shared/templates/__init__.py
```
### Modified Files (7)
```
models/database/__init__.py
models/database/content_page.py
models/database/subscription.py
models/database/feature.py
models/database/vendor.py
middleware/__init__.py
app/services/content_page_service.py
```
---
## Notes
- Git tag `v1.0.0-pre-multiplatform` was created before starting
- All existing `content_pages` will be backfilled to OMS platform
- All existing vendors will be linked to OMS via `vendor_platforms`
- Migration is reversible (see downgrade function in migration file)

View File

@@ -1,603 +0,0 @@
# Multi-Platform CMS Architecture
**Session Date:** 2026-01-18
**Status:** Initial Analysis - Requirements Captured
**Related:** [Loyalty Program Analysis](./loyalty-program-analysis.md)
---
## Executive Summary
The platform is evolving from a single OMS product to a **multi-platform business** where each platform represents a distinct business offering (OMS, Loyalty Program, Website Builder, etc.). Each platform requires its own independent CMS with a three-tier content hierarchy:
1. **Platform Pages** - Marketing site for the platform itself
2. **Vendor Default Pages** - Fallback content for vendor storefronts
3. **Vendor Override/Custom Pages** - Vendor-specific content
---
## Current State Analysis
### Problems Identified
| Issue | Description |
|-------|-------------|
| **Conflated page types** | Platform pages and vendor defaults share `vendor_id = NULL`, making them indistinguishable |
| **Hardcoded homepage** | Platform homepage uses `homepage-wizamart.html` directly, ignoring CMS |
| **Non-functional admin UI** | `/admin/platform-homepage` saves to CMS but route doesn't use it |
| **Single platform assumption** | Architecture assumes one platform, can't scale to multiple offerings |
| **No platform isolation** | No way to have separate About/FAQ/Pricing pages per platform |
### Current Architecture (Broken)
```
ContentPage (vendor_id = NULL)
↓ used by both (conflated)
├── Platform Homepage (/about, /pricing) ← Should be Platform A specific
└── Vendor Default Fallback ← Should be generic storefront pages
```
---
## Proposed Architecture
### Multi-Platform Hierarchy
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ PLATFORM LEVEL │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Platform A │ │ Platform B │ │ Platform C │ │
│ │ (Wizamart OMS) │ │ (Loyalty+) │ │ (Site Builder) │ │
│ │ │ │ │ │ │ │
│ │ • Homepage │ │ • Homepage │ │ • Homepage │ │
│ │ • About │ │ • About │ │ • About │ │
│ │ • Pricing │ │ • Pricing │ │ • Pricing │ │
│ │ • FAQ │ │ • FAQ │ │ • FAQ │ │
│ │ • Contact │ │ • Contact │ │ • Contact │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ VENDOR DEFAULT LEVEL (per platform) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Platform A Defaults │ │
│ │ • About Us (generic store template) │ │
│ │ • Shipping Policy │ │
│ │ • Return Policy │ │
│ │ • Privacy Policy │ │
│ │ • Terms of Service │ │
│ │ • FAQ (e-commerce focused) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ VENDOR LEVEL (isolated) │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Vendor 1 (WizaMart) │ │ Vendor 2 (TechStore) │ │
│ │ Platform A, Tier: Pro │ │ Platform A, Tier: Basic │ │
│ │ │ │ │ │
│ │ Override Pages: │ │ Override Pages: │ │
│ │ • About (custom) │ │ • (none - uses defaults)│ │
│ │ • Shipping (custom) │ │ │ │
│ │ │ │ Custom Pages: │ │
│ │ Custom Pages: │ │ • Size Guide │ │
│ │ • Our Story │ │ │ │
│ │ • Store Locations │ │ │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Content Resolution Flow
When a customer visits `vendor1.example.com/about`:
```
1. Identify vendor context (Vendor 1)
2. Identify platform context (Platform A)
3. Check: Does Vendor 1 have custom "about" page?
├── YES → Return vendor's custom page
└── NO → Check: Does Platform A have default "about" page?
├── YES → Return platform default
└── NO → Return 404
```
---
## Data Model Changes
### New: Platform Model
```python
class Platform(Base):
"""
Represents a business offering/product line.
Examples: Wizamart OMS, Loyalty+, Site Builder
"""
__tablename__ = "platforms"
id = Column(Integer, primary_key=True)
code = Column(String(50), unique=True, nullable=False) # "oms", "loyalty", "sites"
name = Column(String(100), nullable=False) # "Wizamart OMS"
domain = Column(String(255), nullable=True) # "wizamart.lu"
# Branding
logo = Column(String(500), nullable=True)
theme_config = Column(JSON, nullable=True) # Colors, fonts, etc.
# Status
is_active = Column(Boolean, default=True)
# Timestamps
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
# Relationships
content_pages = relationship("ContentPage", back_populates="platform")
subscription_tiers = relationship("SubscriptionTier", back_populates="platform")
vendors = relationship("Vendor", back_populates="platform")
```
### Updated: ContentPage Model
```python
class ContentPage(Base):
"""
CMS content page with three-tier hierarchy:
1. Platform pages (platform_id set, vendor_id NULL, is_platform_page=True)
2. Vendor defaults (platform_id set, vendor_id NULL, is_platform_page=False)
3. Vendor overrides (platform_id set, vendor_id set)
"""
__tablename__ = "content_pages"
id = Column(Integer, primary_key=True)
# NEW: Platform association (required)
platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
# Existing: Vendor association (NULL for platform pages and defaults)
vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=True)
# NEW: Distinguish platform marketing pages from vendor defaults
is_platform_page = Column(Boolean, default=False, nullable=False)
# True = Platform's own page (homepage, pricing, platform about)
# False = Vendor default template (when vendor_id is NULL)
# N/A = Vendor override (when vendor_id is set)
# Existing fields...
slug = Column(String(100), nullable=False)
title = Column(String(200), nullable=False)
content = Column(Text, nullable=False)
content_format = Column(String(20), default="html")
template = Column(String(50), default="default")
# SEO
meta_description = Column(String(300), nullable=True)
meta_keywords = Column(String(300), nullable=True)
# Publishing
is_published = Column(Boolean, default=False)
published_at = Column(DateTime, nullable=True)
# Navigation
display_order = Column(Integer, default=0)
show_in_header = Column(Boolean, default=False)
show_in_footer = Column(Boolean, default=True)
show_in_legal = Column(Boolean, default=False)
# Timestamps & audit
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
updated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
# Constraints
__table_args__ = (
UniqueConstraint("platform_id", "vendor_id", "slug", name="uq_platform_vendor_slug"),
Index("idx_platform_vendor_published", "platform_id", "vendor_id", "is_published"),
)
```
### Updated: Vendor Model
```python
class Vendor(Base):
# Existing fields...
# NEW: Platform association
platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
platform = relationship("Platform", back_populates="vendors")
```
---
## Page Type Matrix
| Page Type | platform_id | vendor_id | is_platform_page | Example |
|-----------|:-----------:|:---------:|:----------------:|---------|
| Platform Marketing Page | ✓ | NULL | TRUE | Platform A's homepage, pricing |
| Vendor Default Page | ✓ | NULL | FALSE | Generic "About Our Store" template |
| Vendor Override Page | ✓ | ✓ | FALSE | WizaMart's custom About page |
| Vendor Custom Page | ✓ | ✓ | FALSE | WizaMart's "Store Locations" page |
---
## User Journeys
### Journey 1: Platform Admin Sets Up New Platform
**Actor:** Super Admin
**Goal:** Create a new business platform with its marketing pages
```
1. Admin navigates to /admin/platforms
2. Admin clicks "Create Platform"
3. Admin fills in:
- Code: "loyalty"
- Name: "Loyalty+"
- Domain: "loyalty.wizamart.lu"
- Logo, theme colors
4. Admin saves platform
5. System creates platform record
6. Admin navigates to /admin/platforms/loyalty/pages
7. Admin creates platform pages:
- Homepage (is_platform_page=True)
- About Us (is_platform_page=True)
- Pricing (is_platform_page=True)
- FAQ (is_platform_page=True)
- Contact (is_platform_page=True)
8. Each page can use different templates (modern, minimal, etc.)
9. Admin publishes pages
10. Platform marketing site is now live at loyalty.wizamart.lu
```
### Journey 2: Platform Admin Creates Vendor Defaults
**Actor:** Platform Admin
**Goal:** Set up default storefront pages for all vendors on Platform A
```
1. Admin navigates to /admin/platforms/oms/vendor-defaults
2. Admin sees list of vendor default pages
3. Admin creates default pages:
- About Us (generic store template)
Content: "Welcome to our store. We're dedicated to..."
- Shipping Policy
Content: "We offer fast and reliable shipping..."
- Return Policy
Content: "30-day return policy on all items..."
- Privacy Policy
Content: "Your privacy is important to us..."
- Terms of Service
Content: "By using our store, you agree to..."
4. All pages have is_platform_page=False, vendor_id=NULL
5. These pages are now available to ALL vendors on Platform A
6. Vendors who don't customize will see these defaults
```
### Journey 3: Vendor Subscribes and Views Default Pages
**Actor:** New Vendor (TechStore)
**Goal:** Start using the platform and see what pages are available
```
1. Vendor signs up for Platform A (OMS), selects "Basic" tier
2. Vendor completes onboarding
3. Vendor logs into dashboard
4. Vendor navigates to "Content Pages" section
5. Vendor sees list of pages:
┌─────────────────────────────────────────────────────────┐
│ Content Pages │
├─────────────────────────────────────────────────────────┤
│ Page │ Source │ Status │ Actions │
├───────────────┼──────────────────┼───────────┼─────────┤
│ About Us │ Platform Default │ Published │ Override│
│ Shipping │ Platform Default │ Published │ Override│
│ Returns │ Platform Default │ Published │ Override│
│ Privacy │ Platform Default │ Published │ Override│
│ Terms │ Platform Default │ Published │ Override│
└─────────────────────────────────────────────────────────┘
[+ Create Custom Page]
6. Vendor previews storefront at techstore.example.com/about
7. Sees platform default "About Us" content
```
### Journey 4: Vendor Overrides a Default Page
**Actor:** Vendor (WizaMart)
**Goal:** Customize the About page with store-specific content
```
1. Vendor logs into dashboard
2. Navigates to Content Pages
3. Sees "About Us" with source "Platform Default"
4. Clicks "Override" button
5. System creates a copy with vendor_id set
6. Vendor edits content:
- Title: "About WizaMart"
- Content: "WizaMart was founded in 2020 in Luxembourg..."
- Adds store images
7. Vendor saves and publishes
8. Page list now shows:
┌─────────────────────────────────────────────────────────┐
│ About Us │ Custom Override │ Published │ Edit │
└─────────────────────────────────────────────────────────┘
9. Customer visits wizamart.example.com/about
10. Sees WizaMart's custom About page
```
### Journey 5: Vendor Creates a Custom Page
**Actor:** Vendor (WizaMart)
**Goal:** Add a new page that doesn't exist in defaults
```
1. Vendor logs into dashboard
2. Navigates to Content Pages
3. Clicks "+ Create Custom Page"
4. Fills in:
- Slug: "store-locations"
- Title: "Our Store Locations"
- Content: Map and addresses of physical stores
- Show in footer: Yes
5. Vendor saves and publishes
6. Page appears in storefront footer navigation
7. Accessible at wizamart.example.com/store-locations
```
### Journey 6: Vendor Reverts Override to Default
**Actor:** Vendor (WizaMart)
**Goal:** Remove customization and use platform default again
```
1. Vendor navigates to Content Pages
2. Sees "Shipping" with source "Custom Override"
3. Clicks "Revert to Default"
4. System shows confirmation:
"This will delete your custom Shipping page and show
the platform default instead. This cannot be undone."
5. Vendor confirms
6. System deletes vendor's custom page
7. Storefront now shows platform default Shipping page
```
### Journey 7: Customer Browses Vendor Storefront
**Actor:** Customer
**Goal:** Read store policies before purchasing
```
1. Customer visits wizamart.example.com
2. Browses products, adds to cart
3. Wants to check return policy
4. Clicks "Returns" in footer
5. System resolves page:
- Check: WizaMart override? NO
- Check: Platform A default? YES
- Serve: Platform A default "Return Policy" page
6. Customer reads return policy
7. Customer clicks "About Us" in footer
8. System resolves page:
- Check: WizaMart override? YES
- Serve: WizaMart's custom "About WizaMart" page
9. Customer sees store-specific About page
```
---
## Workflow Diagrams
### Content Resolution Algorithm
```
resolve_page(vendor, slug):
├─► Get vendor's platform_id
├─► Query: ContentPage WHERE
│ platform_id = vendor.platform_id
│ AND vendor_id = vendor.id
│ AND slug = slug
│ AND is_published = True
├─► Found? ──YES──► Return vendor's page
│ │
│ NO
│ ▼
├─► Query: ContentPage WHERE
│ platform_id = vendor.platform_id
│ AND vendor_id IS NULL
│ AND is_platform_page = False ← Important: exclude platform pages
│ AND slug = slug
│ AND is_published = True
├─► Found? ──YES──► Return platform default
│ │
│ NO
│ ▼
└─► Return 404
```
### Platform Page Resolution (Marketing Site)
```
resolve_platform_page(platform, slug):
├─► Query: ContentPage WHERE
│ platform_id = platform.id
│ AND vendor_id IS NULL
│ AND is_platform_page = True ← Only platform marketing pages
│ AND slug = slug
│ AND is_published = True
├─► Found? ──YES──► Return platform page
│ │
│ NO
│ ▼
└─► Return 404
```
---
## API Endpoints
### Platform Admin API
```
# Platform Management
GET /api/v1/admin/platforms # List all platforms
POST /api/v1/admin/platforms # Create platform
GET /api/v1/admin/platforms/{code} # Get platform
PUT /api/v1/admin/platforms/{code} # Update platform
DELETE /api/v1/admin/platforms/{code} # Delete platform
# Platform Pages (Marketing)
GET /api/v1/admin/platforms/{code}/pages # List platform pages
POST /api/v1/admin/platforms/{code}/pages # Create platform page
PUT /api/v1/admin/platforms/{code}/pages/{id} # Update platform page
DELETE /api/v1/admin/platforms/{code}/pages/{id} # Delete platform page
# Vendor Defaults
GET /api/v1/admin/platforms/{code}/defaults # List vendor defaults
POST /api/v1/admin/platforms/{code}/defaults # Create vendor default
PUT /api/v1/admin/platforms/{code}/defaults/{id}# Update vendor default
DELETE /api/v1/admin/platforms/{code}/defaults/{id}# Delete vendor default
```
### Vendor API
```
# View All Pages (defaults + overrides + custom)
GET /api/v1/vendor/{code}/content-pages # List all pages
# Override/Custom Page Management
POST /api/v1/vendor/{code}/content-pages # Create override/custom
PUT /api/v1/vendor/{code}/content-pages/{id} # Update page
DELETE /api/v1/vendor/{code}/content-pages/{id} # Delete/revert page
# Page Preview
GET /api/v1/vendor/{code}/content-pages/{slug}/preview # Preview with fallback
```
### Public API
```
# Storefront Pages (with fallback resolution)
GET /api/v1/shop/content-pages/{slug} # Get page (vendor context from middleware)
GET /api/v1/shop/content-pages/navigation # Get nav pages
# Platform Marketing Pages
GET /api/v1/platform/{code}/pages/{slug} # Get platform page
GET /api/v1/platform/{code}/pages/navigation # Get platform nav
```
---
## Implementation Phases
### Phase 1: Database & Model Updates
- [ ] Create `Platform` model
- [ ] Add `platform_id` to `ContentPage`
- [ ] Add `is_platform_page` to `ContentPage`
- [ ] Add `platform_id` to `Vendor`
- [ ] Create migration scripts
- [ ] Create default "oms" platform for existing data
### Phase 2: Service Layer
- [ ] Update `ContentPageService` with three-tier resolution
- [ ] Add `PlatformService` for platform CRUD
- [ ] Update page listing to show source (Platform/Default/Override)
### Phase 3: Admin Interface
- [ ] Platform management UI (`/admin/platforms`)
- [ ] Platform pages editor (`/admin/platforms/{code}/pages`)
- [ ] Vendor defaults editor (`/admin/platforms/{code}/defaults`)
- [ ] Fix platform homepage to use CMS
### Phase 4: Vendor Dashboard
- [ ] Update content pages list to show page source
- [ ] Add "Override" action for default pages
- [ ] Add "Revert to Default" action for overrides
- [ ] Visual indicator for inherited vs custom pages
### Phase 5: Public Routes
- [ ] Update shop routes with three-tier resolution
- [ ] Update platform routes to use CMS
- [ ] Ensure proper navigation loading per context
---
## Migration Strategy
### Existing Data Handling
```sql
-- 1. Create default platform
INSERT INTO platforms (code, name, domain, is_active)
VALUES ('oms', 'Wizamart OMS', 'localhost:8000', true);
-- 2. Update all existing content_pages
UPDATE content_pages
SET platform_id = (SELECT id FROM platforms WHERE code = 'oms');
-- 3. Mark platform marketing pages
UPDATE content_pages
SET is_platform_page = true
WHERE vendor_id IS NULL
AND slug IN ('platform_homepage', 'pricing');
-- 4. Remaining NULL vendor pages become vendor defaults
UPDATE content_pages
SET is_platform_page = false
WHERE vendor_id IS NULL
AND is_platform_page IS NULL;
-- 5. Update all vendors
UPDATE vendors
SET platform_id = (SELECT id FROM platforms WHERE code = 'oms');
```
---
## Open Questions
1. **Domain routing**: How to route requests to correct platform?
- Option A: Separate domains (oms.wizamart.lu, loyalty.wizamart.lu)
- Option B: Path-based (/oms/*, /loyalty/*)
- Option C: Subdomain detection
2. **Shared vendors**: Can a vendor belong to multiple platforms?
- Current assumption: NO, one vendor per platform
- If YES: Need junction table
3. **Tier restrictions**: Can page creation be restricted by tier?
- e.g., Basic tier: max 5 custom pages
- e.g., Pro tier: unlimited pages
4. **Template inheritance**: Should vendor defaults have template selection?
- Or always use a standard template?
---
## Session Notes
### 2026-01-18
- Analyzed current CMS architecture issues
- Identified homepage is hardcoded (not using CMS)
- Confirmed admin platform-homepage UI is non-functional
- Designed three-tier content hierarchy:
1. Platform pages (marketing)
2. Vendor defaults (fallback)
3. Vendor overrides/custom
- Documented user journeys for all actors
- Outlined implementation phases
- **Next**: Review proposal, clarify open questions, begin implementation
---
*Document created for session continuity. Update as discussions progress.*

View File

@@ -1,393 +0,0 @@
# Section-Based Homepage Management System
**Status:** COMPLETE
**Created:** 2026-01-20
**Updated:** 2026-01-23
**Completed:** All 7 phases implemented
## Problem Statement
Current homepage implementation has critical issues:
1. **Hardcoded platform content** - Migrations contain OMS/Loyalty/Main-specific HTML
2. **Monolithic content storage** - Entire page stored as HTML blob, can't edit sections individually
3. **No admin control** - Hero, features, pricing sections are hardcoded in templates
## Solution: JSON-Based Section Architecture
### Approach: Add `sections` JSON field to ContentPage
**Why JSON field vs separate PageSection model:**
- Simpler - no new tables, no joins, no N+1 queries
- Flexible - schema can evolve without migrations
- Atomic - save entire homepage in one transaction
- Follows existing pattern - VendorTheme already uses JSON for `colors`
---
## Multi-Language Support
### Dynamic Language Support
Languages are NOT hardcoded. The system uses the platform's `supported_languages` setting:
```python
# Platform model already has:
supported_languages = Column(JSON) # e.g., ["fr", "de", "en"]
default_language = Column(String) # e.g., "fr"
```
### Schema with Dynamic i18n
```python
class TranslatableText(BaseModel):
"""
Text field with translations stored as dict.
Keys are language codes from platform.supported_languages.
"""
translations: dict[str, str] = {} # {"fr": "...", "de": "...", "en": "..."}
def get(self, lang: str, default_lang: str = "fr") -> str:
"""Get translation with fallback to default language."""
return self.translations.get(lang) or self.translations.get(default_lang) or ""
class HeroButton(BaseModel):
text: TranslatableText
url: str
style: str = "primary"
class HeroSection(BaseModel):
enabled: bool = True
badge_text: Optional[TranslatableText] = None
title: TranslatableText
subtitle: TranslatableText
background_type: str = "gradient"
buttons: list[HeroButton] = []
```
### Template Usage with Platform Languages
```html
{# Language comes from platform settings #}
{% set lang = request.state.language or platform.default_language %}
{% set default_lang = platform.default_language %}
<h1>{{ hero.title.get(lang, default_lang) }}</h1>
<p>{{ hero.subtitle.get(lang, default_lang) }}</p>
```
### Admin UI Language Tabs
The admin editor dynamically generates language tabs from `platform.supported_languages`:
```javascript
// Fetch platform languages
const platform = await apiClient.get(`/admin/platforms/${platformCode}`);
const languages = platform.supported_languages; // ["fr", "de", "en"]
// Render language tabs dynamically
languages.forEach(lang => {
addLanguageTab(lang);
});
```
---
## Implementation Plan
### Phase 1: Database Changes
**1.1 Add `sections` column to ContentPage**
File: `models/database/content_page.py`
```python
sections = Column(JSON, nullable=True, default=None)
```
**1.2 Create migration**
File: `alembic/versions/xxx_add_sections_to_content_pages.py`
- Add `sections` JSON column (nullable)
### Phase 2: Schema Validation
**2.1 Create Pydantic schemas with dynamic i18n**
File: `models/schema/homepage_sections.py` (NEW)
```python
from pydantic import BaseModel
from typing import Optional
class TranslatableText(BaseModel):
"""
Stores translations as dict with language codes as keys.
Language codes come from platform.supported_languages.
"""
translations: dict[str, str] = {}
def get(self, lang: str, default_lang: str = "fr") -> str:
"""Get text for language with fallback."""
return self.translations.get(lang) or self.translations.get(default_lang) or ""
class HeroButton(BaseModel):
text: TranslatableText
url: str
style: str = "primary" # primary, secondary, outline
class HeroSection(BaseModel):
enabled: bool = True
badge_text: Optional[TranslatableText] = None
title: TranslatableText = TranslatableText()
subtitle: TranslatableText = TranslatableText()
background_type: str = "gradient"
buttons: list[HeroButton] = []
class FeatureCard(BaseModel):
icon: str
title: TranslatableText
description: TranslatableText
class FeaturesSection(BaseModel):
enabled: bool = True
title: TranslatableText = TranslatableText()
subtitle: Optional[TranslatableText] = None
features: list[FeatureCard] = []
layout: str = "grid"
class PricingSection(BaseModel):
enabled: bool = True
title: TranslatableText = TranslatableText()
subtitle: Optional[TranslatableText] = None
use_subscription_tiers: bool = True # Pull from DB dynamically
class CTASection(BaseModel):
enabled: bool = True
title: TranslatableText = TranslatableText()
subtitle: Optional[TranslatableText] = None
buttons: list[HeroButton] = []
class HomepageSections(BaseModel):
hero: Optional[HeroSection] = None
features: Optional[FeaturesSection] = None
pricing: Optional[PricingSection] = None
cta: Optional[CTASection] = None
```
### Phase 3: Template Changes
**3.1 Create section partials**
Directory: `app/templates/platform/sections/` (NEW)
- `_hero.html` - Renders hero with language support
- `_features.html` - Renders features grid
- `_pricing.html` - Renders pricing (uses subscription_tiers from DB)
- `_cta.html` - Renders CTA section
**3.2 Update homepage templates**
File: `app/templates/platform/homepage-default.html`
```html
{% set lang = request.state.language or platform.default_language or 'fr' %}
{% if page and page.sections %}
{{ render_hero(page.sections.hero, lang) }}
{{ render_features(page.sections.features, lang) }}
{{ render_pricing(page.sections.pricing, lang, tiers) }}
{{ render_cta(page.sections.cta, lang) }}
{% else %}
{# Placeholder for unconfigured homepage #}
{% endif %}
```
### Phase 4: Service Layer
**4.1 Add section methods to ContentPageService**
File: `app/services/content_page_service.py`
- `update_homepage_sections(db, page_id, sections, updated_by)` - Validates and saves
- `get_default_sections()` - Returns empty section structure
### Phase 5: Admin API
**5.1 Add section endpoints**
File: `app/api/v1/admin/content_pages.py`
- `GET /{page_id}/sections` - Get structured sections
- `PUT /{page_id}/sections` - Update all sections
- `PUT /{page_id}/sections/{section_name}` - Update single section
### Phase 6: Remove Hardcoded Content from Migrations
**6.1 Update OMS migration**
File: `alembic/versions/z4e5f6a7b8c9_add_multi_platform_support.py`
- Remove `oms_homepage_content` variable
- Create homepage with empty `sections` structure instead
- Set `is_published=False` (admin configures before publishing)
**6.2 Migration creates structure only**
- Migrations should ONLY create empty structure
- Content is entered via admin UI in each language
### Phase 7: Admin UI
**7.1 Add section editor to content-page-edit**
File: `app/templates/admin/content-page-edit.html`
- Add "Sections" tab for homepage pages
- Language tabs within each section (dynamically from platform.supported_languages)
- Form fields for each section type
- Enable/disable toggle per section
File: `static/admin/js/content-page-edit.js`
- Section editor logic
- Language tab switching
- Save sections via API
---
## Critical Files to Modify
1. `models/database/content_page.py` - Add `sections` column
2. `models/schema/homepage_sections.py` - NEW: Pydantic schemas with i18n
3. `app/services/content_page_service.py` - Add section methods
4. `app/api/v1/admin/content_pages.py` - Add section endpoints
5. `app/templates/platform/sections/` - NEW: Section partials
6. `app/templates/platform/homepage-default.html` - Use section partials
7. `app/routes/platform_pages.py` - Pass sections + language to context
8. `alembic/versions/z4e5f6a7b8c9_*.py` - Remove hardcoded content
9. `app/templates/admin/content-page-edit.html` - Section editor UI with language tabs
10. `static/admin/js/content-page-edit.js` - Section editor JS
---
## Section JSON Schema Example (with dynamic i18n)
Languages in `translations` dict come from `platform.supported_languages`.
```json
{
"hero": {
"enabled": true,
"badge_text": {
"translations": {
"fr": "Essai gratuit de 30 jours",
"de": "30 Tage kostenlos testen",
"en": "30-Day Free Trial"
}
},
"title": {
"translations": {
"fr": "Votre titre de plateforme ici",
"de": "Ihr Plattform-Titel hier",
"en": "Your Platform Headline Here"
}
},
"subtitle": {
"translations": {
"fr": "Une description convaincante de votre plateforme.",
"de": "Eine überzeugende Beschreibung Ihrer Plattform.",
"en": "A compelling description of your platform."
}
},
"background_type": "gradient",
"buttons": [
{
"text": {
"translations": {"fr": "Commencer", "de": "Loslegen", "en": "Get Started"}
},
"url": "/signup",
"style": "primary"
}
]
},
"features": {
"enabled": true,
"title": {
"translations": {
"fr": "Pourquoi nous choisir",
"de": "Warum uns wählen",
"en": "Why Choose Us"
}
},
"features": [
{
"icon": "lightning-bolt",
"title": {"translations": {"fr": "Rapide", "de": "Schnell", "en": "Fast"}},
"description": {"translations": {"fr": "Rapide et efficace.", "de": "Schnell und effizient.", "en": "Quick and efficient."}}
}
]
},
"pricing": {
"enabled": true,
"title": {
"translations": {
"fr": "Tarification simple",
"de": "Einfache Preise",
"en": "Simple Pricing"
}
},
"use_subscription_tiers": true
},
"cta": {
"enabled": true,
"title": {
"translations": {
"fr": "Prêt à commencer?",
"de": "Bereit anzufangen?",
"en": "Ready to Start?"
}
},
"buttons": [
{
"text": {
"translations": {"fr": "S'inscrire gratuitement", "de": "Kostenlos registrieren", "en": "Sign Up Free"}
},
"url": "/signup",
"style": "primary"
}
]
}
}
```
---
## Migration Strategy (No Hardcoded Content)
When creating a platform homepage:
```python
homepage = ContentPage(
platform_id=platform_id,
slug="home",
title="Homepage", # Generic
content="", # Empty - sections used instead
sections=get_default_sections(), # Empty structure with all languages
is_published=False, # Admin configures first
)
```
---
## Verification Steps
1. Run migration to add `sections` column
2. Create a test homepage with sections via API (all languages)
3. Verify homepage renders correct language based on request
4. Test admin UI section editor with language tabs
5. Verify pricing section pulls from subscription_tiers
6. Test enable/disable toggle for each section
7. Test language fallback when translation is missing
---
## Notes
- Languages are dynamic from `platform.supported_languages` (not hardcoded)
- Fallback uses `platform.default_language`
- Admin UI should allow partial translations (show warning indicator for missing)
## Pre-Implementation Cleanup
Before implementing, fix the broken OMS migration file:
- `alembic/versions/z4e5f6a7b8c9_add_multi_platform_support.py` has "I dn" on line 181

View File

@@ -1,229 +0,0 @@
│ Loyalty Platform & Module Implementation Plan │
│ │
│ Overview │
│ │
│ Create a Loyalty Module for Wizamart that provides stamp-based and points-based loyalty programs with Google Wallet and Apple Wallet integration. │
│ │
│ Entity Mapping │
│ ┌────────────────────┬────────────────────┬──────────────────────────────────────────────────────────┐ │
│ │ Loyalty Concept │ Wizamart Entity │ Notes │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Merchant │ Company │ Existing - legal business entity │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Store │ Vendor │ Existing - brand/location │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Customer │ Customer │ Existing - has vendor_id, total_spent, marketing_consent │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Pass Object │ LoyaltyCard │ NEW - links customer to vendor's program │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Stamp/Points Event │ LoyaltyTransaction │ NEW - records all operations │ │
│ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────────────┤ │
│ │ Staff PIN │ StaffPin │ NEW - fraud prevention │ │
│ └────────────────────┴────────────────────┴──────────────────────────────────────────────────────────┘ │
│ Database Models │
│ │
│ Core Models (5 tables) │
│ │
│ 1. loyalty_programs - Vendor's program configuration │
│ - vendor_id (unique FK) │
│ - loyalty_type: stamps | points | hybrid │
│ - Stamps config: stamps_target, stamps_reward_description │
│ - Points config: points_per_euro, points_rewards (JSON) │
│ - Anti-fraud: cooldown_minutes, max_daily_stamps, require_staff_pin │
│ - Branding: card_name, card_color, logo_url │
│ - Wallet IDs: google_issuer_id, apple_pass_type_id │
│ 2. loyalty_cards - Customer's card (PassObject) │
│ - customer_id, program_id, vendor_id │
│ - card_number (unique), qr_code_data │
│ - Stamps: stamp_count, total_stamps_earned, stamps_redeemed │
│ - Points: points_balance, total_points_earned, points_redeemed │
│ - Wallet: google_object_id, apple_serial_number, apple_auth_token │
│ - Timestamps: last_stamp_at, last_points_at │
│ 3. loyalty_transactions - All loyalty events │
│ - card_id, vendor_id, staff_pin_id │
│ - transaction_type: stamp_earned | stamp_redeemed | points_earned | points_redeemed │
│ - stamps_delta, points_delta, purchase_amount_cents │
│ - Metadata: ip_address, user_agent, notes │
│ 4. staff_pins - Fraud prevention │
│ - program_id, vendor_id │
│ - name, pin_hash (bcrypt) │
│ - failed_attempts, locked_until │
│ 5. apple_device_registrations - Apple Wallet push │
│ - card_id, device_library_identifier, push_token │
│ │
│ Module Structure │
│ │
│ app/modules/loyalty/ │
│ ├── __init__.py │
│ ├── definition.py # ModuleDefinition (requires: customers) │
│ ├── config.py # LOYALTY_ env vars │
│ ├── exceptions.py │
│ ├── models/ │
│ │ ├── loyalty_program.py │
│ │ ├── loyalty_card.py │
│ │ ├── loyalty_transaction.py │
│ │ ├── staff_pin.py │
│ │ └── apple_device.py │
│ ├── schemas/ │
│ │ ├── program.py │
│ │ ├── card.py │
│ │ ├── stamp.py │
│ │ ├── points.py │
│ │ └── pin.py │
│ ├── services/ │
│ │ ├── program_service.py # Program CRUD │
│ │ ├── card_service.py # Card enrollment, lookup │
│ │ ├── stamp_service.py # Stamp logic + anti-fraud │
│ │ ├── points_service.py # Points logic │
│ │ ├── pin_service.py # PIN validation │
│ │ ├── wallet_service.py # Unified wallet abstraction │
│ │ ├── google_wallet_service.py │
│ │ └── apple_wallet_service.py │
│ ├── routes/ │
│ │ ├── api/ │
│ │ │ ├── admin.py # Platform admin │
│ │ │ ├── vendor.py # Vendor dashboard │
│ │ │ └── public.py # Enrollment, Apple web service │
│ │ └── pages/ │
│ ├── tasks/ │
│ │ ├── point_expiration.py │
│ │ └── wallet_sync.py │
│ ├── migrations/versions/ │
│ ├── locales/ │
│ └── templates/ │
│ │
│ Key API Endpoints │
│ │
│ Public (Customer) │
│ │
│ - POST /api/v1/loyalty/enroll/{vendor_code} - Enroll in program │
│ - GET /api/v1/loyalty/passes/apple/{serial}.pkpass - Download Apple pass │
│ - Apple Web Service endpoints for device registration/updates │
│ │
│ Vendor (Staff) │
│ │
│ - POST /api/v1/vendor/loyalty/stamp - Add stamp (requires PIN) │
│ - POST /api/v1/vendor/loyalty/points - Add points from purchase │
│ - POST /api/v1/vendor/loyalty/*/redeem - Redeem for reward │
│ - GET /api/v1/vendor/loyalty/cards - List customer cards │
│ - GET /api/v1/vendor/loyalty/pins - Manage staff PINs │
│ - GET /api/v1/vendor/loyalty/stats - Dashboard analytics │
│ │
│ Admin │
│ │
│ - GET /api/v1/admin/loyalty/programs - List all programs │
│ - GET /api/v1/admin/loyalty/stats - Platform-wide stats │
│ │
│ Anti-Fraud System │
│ │
│ 1. Staff PIN - Required for all stamp/points operations │
│ 2. Cooldown - Configurable minutes between stamps (default: 15) │
│ 3. Daily Limit - Max stamps per card per day (default: 5) │
│ 4. PIN Lockout - Lock after 5 failed attempts for 30 minutes │
│ 5. Audit Trail - All transactions logged with IP/user agent │
│ │
│ Wallet Integration │
│ │
│ Google Wallet │
│ │
│ - Create LoyaltyClass when program created │
│ - Create LoyaltyObject when customer enrolls │
│ - PATCH object on stamp/points change │
│ - Generate JWT-based "Add to Wallet" URL │
│ │
│ Apple Wallet │
│ │
│ - Generate .pkpass file (pass.json + images + signature) │
│ - Implement Apple Web Service for device registration │
│ - Send push notification on updates → device fetches new pass │
│ │
│ Implementation Phases │
│ │
│ Phase 1: MVP (Target) │
│ │
│ 1. Core Infrastructure │
│ - Module structure, definition, exceptions │
│ - Database models and migrations │
│ - Program service (CRUD) │
│ - Card service (enrollment, lookup) │
│ 2. Stamp Loyalty │
│ - Staff PIN service with lockout │
│ - Stamp service with anti-fraud │
│ - Transaction logging │
│ - Vendor API routes │
│ 3. Points Loyalty │
│ - Points service │
│ - Purchase-to-points calculation │
│ - Redemption flow │
│ 4. Wallet Integration │
│ - Google Wallet service │
│ - Apple .pkpass generation │
│ - Apple Web Service endpoints │
│ 5. Dashboard │
│ - Vendor stats endpoint │
│ - Transaction history │
│ - QR code generation │
│ │
│ Phase 2: Future Enhancements │
│ │
│ - Rewards catalog with configurable tiers │
│ - Customer tiers (Bronze/Silver/Gold) │
│ - Referral program │
│ - Gamification (spin wheel, scratch cards) │
│ - POS integration │
│ │
│ Files to Create │
│ │
│ app/modules/loyalty/ │
│ ├── __init__.py │
│ ├── definition.py │
│ ├── config.py │
│ ├── exceptions.py │
│ ├── models/__init__.py │
│ ├── models/loyalty_program.py │
│ ├── models/loyalty_card.py │
│ ├── models/loyalty_transaction.py │
│ ├── models/staff_pin.py │
│ ├── models/apple_device.py │
│ ├── schemas/__init__.py │
│ ├── schemas/program.py │
│ ├── schemas/card.py │
│ ├── schemas/stamp.py │
│ ├── schemas/points.py │
│ ├── schemas/pin.py │
│ ├── services/__init__.py │
│ ├── services/program_service.py │
│ ├── services/card_service.py │
│ ├── services/stamp_service.py │
│ ├── services/points_service.py │
│ ├── services/pin_service.py │
│ ├── services/wallet_service.py │
│ ├── services/google_wallet_service.py │
│ ├── services/apple_wallet_service.py │
│ ├── routes/__init__.py │
│ ├── routes/api/__init__.py │
│ ├── routes/api/admin.py │
│ ├── routes/api/vendor.py │
│ ├── routes/api/public.py │
│ ├── routes/pages/__init__.py │
│ ├── tasks/__init__.py │
│ ├── tasks/point_expiration.py │
│ ├── migrations/__init__.py │
│ ├── migrations/versions/__init__.py │
│ └── locales/{en,fr,de,lu}.json │
│ │
│ Reference Patterns │
│ │
│ - Module definition: app/modules/billing/definition.py │
│ - Models: app/modules/billing/models/subscription.py │
│ - Services: app/modules/billing/services/subscription_service.py │
│ - Customer integration: models/database/customer.py │
│ │
│ Verification │
│ │
│ 1. Run architecture validator: python scripts/validate_architecture.py │
│ 2. Run migrations: alembic upgrade head │
│ 3. Test enrollment flow via API │
│ 4. Test stamp/points operations with PIN │
│ 5. Verify wallet pass generation │
│ 6. Check anti-fraud (cooldown, limits, lockout)