- Fix 404 on /admin/loyalty/* and /vendor/loyalty/* by applying ROUTE_CONFIG custom_prefix when registering page routers in main.py (admin, vendor, and storefront registrations all updated) - Move loyalty alembic migrations from central alembic/versions/ into app/modules/loyalty/migrations/versions/ with proper naming convention - Add migrations_path="migrations" to loyalty module definition so the auto-discovery system finds them - Add unit tests for Apple/Google Wallet Code 128 barcode configuration (6 Apple tests, 4 Google tests) - Add integration tests for module migration auto-discovery (4 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
204 lines
7.2 KiB
Python
204 lines
7.2 KiB
Python
# app/modules/loyalty/definition.py
|
|
"""
|
|
Loyalty module definition.
|
|
|
|
Defines the loyalty module including its features, menu items,
|
|
route configurations, and scheduled tasks.
|
|
"""
|
|
|
|
from app.modules.base import MenuItemDefinition, MenuSectionDefinition, ModuleDefinition, PermissionDefinition, ScheduledTask
|
|
from app.modules.enums import FrontendType
|
|
|
|
|
|
def _get_admin_router():
|
|
"""Lazy import of admin router to avoid circular imports."""
|
|
from app.modules.loyalty.routes.api.admin import admin_router
|
|
|
|
return admin_router
|
|
|
|
|
|
def _get_vendor_router():
|
|
"""Lazy import of vendor router to avoid circular imports."""
|
|
from app.modules.loyalty.routes.api.vendor import vendor_router
|
|
|
|
return vendor_router
|
|
|
|
|
|
def _get_platform_router():
|
|
"""Lazy import of platform router to avoid circular imports."""
|
|
from app.modules.loyalty.routes.api.platform import platform_router
|
|
|
|
return platform_router
|
|
|
|
|
|
# Loyalty module definition
|
|
loyalty_module = ModuleDefinition(
|
|
code="loyalty",
|
|
name="Loyalty Programs",
|
|
description=(
|
|
"Stamp-based and points-based loyalty programs with Google Wallet "
|
|
"and Apple Wallet integration. Includes anti-fraud features like "
|
|
"staff PINs, cooldown periods, and daily limits."
|
|
),
|
|
version="1.0.0",
|
|
requires=["customers"], # Depends on customers module for customer data
|
|
features=[
|
|
# Core features
|
|
"loyalty_stamps", # Stamp-based loyalty
|
|
"loyalty_points", # Points-based loyalty
|
|
"loyalty_hybrid", # Both stamps and points
|
|
# Card management
|
|
"loyalty_cards", # Customer card management
|
|
"loyalty_enrollment", # Customer enrollment
|
|
# Staff/fraud prevention
|
|
"loyalty_staff_pins", # Staff PIN management
|
|
"loyalty_anti_fraud", # Cooldown, daily limits
|
|
# Wallet integration
|
|
"loyalty_google_wallet", # Google Wallet passes
|
|
"loyalty_apple_wallet", # Apple Wallet passes
|
|
# Analytics
|
|
"loyalty_stats", # Dashboard statistics
|
|
"loyalty_reports", # Transaction reports
|
|
],
|
|
# Module-driven permissions
|
|
permissions=[
|
|
PermissionDefinition(
|
|
id="loyalty.view_programs",
|
|
label_key="loyalty.permissions.view_programs",
|
|
description_key="loyalty.permissions.view_programs_desc",
|
|
category="loyalty",
|
|
),
|
|
PermissionDefinition(
|
|
id="loyalty.manage_programs",
|
|
label_key="loyalty.permissions.manage_programs",
|
|
description_key="loyalty.permissions.manage_programs_desc",
|
|
category="loyalty",
|
|
),
|
|
PermissionDefinition(
|
|
id="loyalty.view_rewards",
|
|
label_key="loyalty.permissions.view_rewards",
|
|
description_key="loyalty.permissions.view_rewards_desc",
|
|
category="loyalty",
|
|
),
|
|
PermissionDefinition(
|
|
id="loyalty.manage_rewards",
|
|
label_key="loyalty.permissions.manage_rewards",
|
|
description_key="loyalty.permissions.manage_rewards_desc",
|
|
category="loyalty",
|
|
),
|
|
],
|
|
menu_items={
|
|
FrontendType.ADMIN: [
|
|
"loyalty-programs", # View all programs
|
|
"loyalty-analytics", # Platform-wide stats
|
|
],
|
|
FrontendType.VENDOR: [
|
|
"loyalty", # Loyalty dashboard
|
|
"loyalty-cards", # Customer cards
|
|
"loyalty-stats", # Vendor stats
|
|
],
|
|
},
|
|
# New module-driven menu definitions
|
|
menus={
|
|
FrontendType.ADMIN: [
|
|
MenuSectionDefinition(
|
|
id="loyalty",
|
|
label_key="loyalty.menu.loyalty",
|
|
icon="gift",
|
|
order=55,
|
|
items=[
|
|
MenuItemDefinition(
|
|
id="loyalty-programs",
|
|
label_key="loyalty.menu.programs",
|
|
icon="gift",
|
|
route="/admin/loyalty/programs",
|
|
order=10,
|
|
),
|
|
MenuItemDefinition(
|
|
id="loyalty-analytics",
|
|
label_key="loyalty.menu.analytics",
|
|
icon="chart-bar",
|
|
route="/admin/loyalty/analytics",
|
|
order=20,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
FrontendType.VENDOR: [
|
|
MenuSectionDefinition(
|
|
id="loyalty",
|
|
label_key="loyalty.menu.loyalty_programs",
|
|
icon="gift",
|
|
order=35,
|
|
items=[
|
|
MenuItemDefinition(
|
|
id="loyalty",
|
|
label_key="loyalty.menu.dashboard",
|
|
icon="gift",
|
|
route="/vendor/{vendor_code}/loyalty",
|
|
order=10,
|
|
),
|
|
MenuItemDefinition(
|
|
id="loyalty-cards",
|
|
label_key="loyalty.menu.customer_cards",
|
|
icon="identification",
|
|
route="/vendor/{vendor_code}/loyalty/cards",
|
|
order=20,
|
|
),
|
|
MenuItemDefinition(
|
|
id="loyalty-stats",
|
|
label_key="loyalty.menu.statistics",
|
|
icon="chart-bar",
|
|
route="/vendor/{vendor_code}/loyalty/stats",
|
|
order=30,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
},
|
|
is_core=False, # Loyalty can be disabled
|
|
# =========================================================================
|
|
# Self-Contained Module Configuration
|
|
# =========================================================================
|
|
is_self_contained=True,
|
|
services_path="app.modules.loyalty.services",
|
|
models_path="app.modules.loyalty.models",
|
|
schemas_path="app.modules.loyalty.schemas",
|
|
exceptions_path="app.modules.loyalty.exceptions",
|
|
tasks_path="app.modules.loyalty.tasks",
|
|
migrations_path="migrations",
|
|
# =========================================================================
|
|
# Scheduled Tasks
|
|
# =========================================================================
|
|
scheduled_tasks=[
|
|
ScheduledTask(
|
|
name="loyalty.sync_wallet_passes",
|
|
task="app.modules.loyalty.tasks.wallet_sync.sync_wallet_passes",
|
|
schedule="0 * * * *", # Hourly
|
|
options={"queue": "scheduled"},
|
|
),
|
|
ScheduledTask(
|
|
name="loyalty.expire_points",
|
|
task="app.modules.loyalty.tasks.point_expiration.expire_points",
|
|
schedule="0 2 * * *", # Daily at 02:00
|
|
options={"queue": "scheduled"},
|
|
),
|
|
],
|
|
)
|
|
|
|
|
|
def get_loyalty_module_with_routers() -> ModuleDefinition:
|
|
"""
|
|
Get loyalty module with routers attached.
|
|
|
|
This function attaches the routers lazily to avoid circular imports
|
|
during module initialization.
|
|
"""
|
|
loyalty_module.admin_router = _get_admin_router()
|
|
loyalty_module.vendor_router = _get_vendor_router()
|
|
loyalty_module.platform_router = _get_platform_router()
|
|
return loyalty_module
|
|
|
|
|
|
__all__ = ["loyalty_module", "get_loyalty_module_with_routers"]
|