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:
129
docs/proposals/PLAN_documentation-consolidation.md
Normal file
129
docs/proposals/PLAN_documentation-consolidation.md
Normal 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
|
||||
@@ -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
|
||||
@@ -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`
|
||||
@@ -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)"
|
||||
```
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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`
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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.*
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user