Files
orion/app/modules/loyalty/definition.py
Samir Boulahtit b3224ba13d fix(loyalty): replace broad exception handlers with specific types and rename onboarding service
- Replace `except Exception` with specific exception types in
  google_wallet_service.py (requests.RequestException, ValueError, etc.)
  and apple_wallet_service.py (httpx.HTTPError, OSError, ssl.SSLError)
- Rename loyalty_onboarding.py -> loyalty_onboarding_service.py to
  match NAM-002 naming convention (+ test file + imports)
- Add PasswordChangeResponse Pydantic model to user_account API,
  removing raw dict return and noqa suppression

Resolves 12 EXC-003 + 1 NAM-002 architecture warnings in loyalty module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 23:09:23 +01:00

302 lines
10 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 router
return router
def _get_merchant_router():
"""Lazy import of merchant router to avoid circular imports."""
from app.modules.loyalty.routes.api.merchant import router as merchant_router
return merchant_router
def _get_store_router():
"""Lazy import of store router to avoid circular imports."""
from app.modules.loyalty.routes.api.store import router
return 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
def _get_storefront_router():
"""Lazy import of storefront router to avoid circular imports."""
from app.modules.loyalty.routes.api.storefront import storefront_router
return storefront_router
def _get_feature_provider():
"""Lazy import of feature provider to avoid circular imports."""
from app.modules.loyalty.services.loyalty_features import loyalty_feature_provider
return loyalty_feature_provider
def _get_onboarding_provider():
"""Lazy import of onboarding provider to avoid circular imports."""
from app.modules.loyalty.services.loyalty_onboarding_service import (
loyalty_onboarding_provider,
)
return loyalty_onboarding_provider
# 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.STORE: [
"terminal", # Loyalty terminal
"cards", # Customer cards
"loyalty-program", # Program config
"loyalty-analytics", # Store analytics
],
FrontendType.MERCHANT: [
"loyalty-program", # Merchant loyalty program
"loyalty-analytics", # Merchant loyalty analytics
],
},
# 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.STORE: [
MenuSectionDefinition(
id="loyalty",
label_key="loyalty.menu.loyalty_programs",
icon="gift",
order=35,
items=[
MenuItemDefinition(
id="terminal",
label_key="loyalty.menu.terminal",
icon="gift",
route="/store/{store_code}/loyalty/terminal",
order=10,
requires_permission="loyalty.view_programs",
),
MenuItemDefinition(
id="cards",
label_key="loyalty.menu.customer_cards",
icon="identification",
route="/store/{store_code}/loyalty/cards",
order=20,
requires_permission="loyalty.view_programs",
),
MenuItemDefinition(
id="loyalty-program",
label_key="loyalty.menu.program",
icon="cog",
route="/store/{store_code}/loyalty/program",
order=25,
requires_permission="loyalty.view_programs",
),
MenuItemDefinition(
id="loyalty-analytics",
label_key="loyalty.menu.analytics",
icon="chart-bar",
route="/store/{store_code}/loyalty/analytics",
order=30,
requires_permission="loyalty.view_programs",
),
],
),
],
FrontendType.MERCHANT: [
MenuSectionDefinition(
id="loyalty",
label_key="loyalty.menu.loyalty",
icon="gift",
order=60,
items=[
MenuItemDefinition(
id="loyalty-program",
label_key="loyalty.menu.program",
icon="gift",
route="/merchants/loyalty/program",
order=10,
),
MenuItemDefinition(
id="loyalty-analytics",
label_key="loyalty.menu.analytics",
icon="chart-bar",
route="/merchants/loyalty/analytics",
order=20,
),
],
),
],
FrontendType.STOREFRONT: [
MenuSectionDefinition(
id="account",
label_key=None,
order=10,
items=[
MenuItemDefinition(
id="loyalty",
label_key="storefront.account.loyalty",
icon="gift",
route="account/loyalty",
order=60,
),
],
),
],
},
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"},
),
],
# Feature provider for billing feature gating
feature_provider=_get_feature_provider,
# Onboarding provider for post-signup checklist
onboarding_provider=_get_onboarding_provider,
)
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.merchant_router = _get_merchant_router()
loyalty_module.store_router = _get_store_router()
loyalty_module.platform_router = _get_platform_router()
loyalty_module.storefront_router = _get_storefront_router()
return loyalty_module
__all__ = ["loyalty_module", "get_loyalty_module_with_routers"]