Add module system for enabling/disabling feature bundles per platform. Module System: - ModuleDefinition dataclass for defining modules - 12 modules: core, platform-admin, billing, inventory, orders, marketplace, customers, cms, analytics, messaging, dev-tools, monitoring - Core modules (core, platform-admin) cannot be disabled - Module dependencies (e.g., marketplace requires inventory) MenuService Integration: - Menu items filtered by module enablement - MenuItemConfig includes is_module_enabled and module_code fields - Module-disabled items hidden from sidebar Platform Configuration: - BasePlatformConfig.enabled_modules property - OMS: all modules enabled (full commerce) - Loyalty: focused subset (no billing/inventory/orders/marketplace) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
113 lines
3.7 KiB
Python
113 lines
3.7 KiB
Python
# app/modules/base.py
|
|
"""
|
|
Base module definition class.
|
|
|
|
A Module is a self-contained unit of functionality that can be enabled/disabled
|
|
per platform. Each module contains:
|
|
- Features: Granular capabilities for tier-based access control
|
|
- Menu items: Sidebar entries per frontend type
|
|
- Routes: API and page routes (future: dynamically registered)
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from fastapi import APIRouter
|
|
|
|
from models.database.admin_menu_config import FrontendType
|
|
|
|
|
|
@dataclass
|
|
class ModuleDefinition:
|
|
"""
|
|
Definition of a platform module.
|
|
|
|
A module groups related functionality that can be enabled/disabled per platform.
|
|
Core modules cannot be disabled and are always available.
|
|
|
|
Attributes:
|
|
code: Unique identifier (e.g., "billing", "marketplace")
|
|
name: Display name (e.g., "Billing & Subscriptions")
|
|
description: Description of what this module provides
|
|
requires: List of module codes this module depends on
|
|
features: List of feature codes this module provides
|
|
menu_items: Dict mapping FrontendType to list of menu item IDs
|
|
is_core: Core modules cannot be disabled
|
|
admin_router: FastAPI router for admin routes (future)
|
|
vendor_router: FastAPI router for vendor routes (future)
|
|
|
|
Example:
|
|
billing_module = ModuleDefinition(
|
|
code="billing",
|
|
name="Billing & Subscriptions",
|
|
description="Subscription tiers, billing history, and payment processing",
|
|
features=["subscription_management", "billing_history", "stripe_integration"],
|
|
menu_items={
|
|
FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"],
|
|
FrontendType.VENDOR: ["billing"],
|
|
},
|
|
)
|
|
"""
|
|
|
|
# Identity
|
|
code: str
|
|
name: str
|
|
description: str = ""
|
|
|
|
# Dependencies
|
|
requires: list[str] = field(default_factory=list)
|
|
|
|
# Components
|
|
features: list[str] = field(default_factory=list)
|
|
menu_items: dict[FrontendType, list[str]] = field(default_factory=dict)
|
|
|
|
# Status
|
|
is_core: bool = False
|
|
|
|
# Routes (registered dynamically) - Future implementation
|
|
admin_router: "APIRouter | None" = None
|
|
vendor_router: "APIRouter | None" = None
|
|
|
|
def get_menu_items(self, frontend_type: FrontendType) -> list[str]:
|
|
"""Get menu item IDs for a specific frontend type."""
|
|
return self.menu_items.get(frontend_type, [])
|
|
|
|
def get_all_menu_items(self) -> set[str]:
|
|
"""Get all menu item IDs across all frontend types."""
|
|
all_items = set()
|
|
for items in self.menu_items.values():
|
|
all_items.update(items)
|
|
return all_items
|
|
|
|
def has_feature(self, feature_code: str) -> bool:
|
|
"""Check if this module provides a specific feature."""
|
|
return feature_code in self.features
|
|
|
|
def has_menu_item(self, menu_item_id: str) -> bool:
|
|
"""Check if this module provides a specific menu item."""
|
|
return menu_item_id in self.get_all_menu_items()
|
|
|
|
def check_dependencies(self, enabled_modules: set[str]) -> list[str]:
|
|
"""
|
|
Check if all required modules are enabled.
|
|
|
|
Args:
|
|
enabled_modules: Set of enabled module codes
|
|
|
|
Returns:
|
|
List of missing required module codes
|
|
"""
|
|
return [req for req in self.requires if req not in enabled_modules]
|
|
|
|
def __hash__(self) -> int:
|
|
return hash(self.code)
|
|
|
|
def __eq__(self, other: object) -> bool:
|
|
if isinstance(other, ModuleDefinition):
|
|
return self.code == other.code
|
|
return False
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<Module({self.code}, core={self.is_core})>"
|