2025-08-11 07:01:08 +02:00
|
|
|
"""
|
|
|
|
|
User onboarding progress API routes
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
from typing import Dict, Any, List, Optional
|
|
|
|
|
import structlog
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
|
|
|
|
from app.core.database import get_db
|
|
|
|
|
from app.services.user_service import UserService
|
|
|
|
|
from app.repositories.onboarding_repository import OnboardingRepository
|
|
|
|
|
from shared.auth.decorators import get_current_user_dep
|
|
|
|
|
|
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
router = APIRouter(tags=["onboarding"])
|
|
|
|
|
|
|
|
|
|
# Request/Response Models
|
|
|
|
|
class OnboardingStepStatus(BaseModel):
|
|
|
|
|
step_name: str
|
|
|
|
|
completed: bool
|
|
|
|
|
completed_at: Optional[datetime] = None
|
|
|
|
|
data: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
class WizardContextState(BaseModel):
|
|
|
|
|
"""
|
|
|
|
|
Wizard context state extracted from completed step data.
|
|
|
|
|
This is used to restore the frontend WizardContext on session restore.
|
|
|
|
|
"""
|
|
|
|
|
bakery_type: Optional[str] = None
|
|
|
|
|
tenant_id: Optional[str] = None
|
|
|
|
|
subscription_tier: Optional[str] = None
|
|
|
|
|
ai_analysis_complete: bool = False
|
|
|
|
|
inventory_review_completed: bool = False
|
|
|
|
|
stock_entry_completed: bool = False
|
|
|
|
|
categorization_completed: bool = False
|
|
|
|
|
suppliers_completed: bool = False
|
|
|
|
|
inventory_setup_completed: bool = False
|
|
|
|
|
recipes_completed: bool = False
|
|
|
|
|
quality_completed: bool = False
|
|
|
|
|
team_completed: bool = False
|
|
|
|
|
child_tenants_completed: bool = False
|
|
|
|
|
ml_training_complete: bool = False
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
class UserProgress(BaseModel):
|
|
|
|
|
user_id: str
|
|
|
|
|
steps: List[OnboardingStepStatus]
|
|
|
|
|
current_step: str
|
|
|
|
|
next_step: Optional[str] = None
|
|
|
|
|
completion_percentage: float
|
|
|
|
|
fully_completed: bool
|
|
|
|
|
last_updated: datetime
|
2026-01-04 21:37:44 +01:00
|
|
|
context_state: Optional[WizardContextState] = None
|
2025-08-11 07:01:08 +02:00
|
|
|
|
|
|
|
|
class UpdateStepRequest(BaseModel):
|
|
|
|
|
step_name: str
|
|
|
|
|
completed: bool
|
|
|
|
|
data: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
class SaveStepDraftRequest(BaseModel):
|
|
|
|
|
step_name: str
|
|
|
|
|
draft_data: Dict[str, Any]
|
|
|
|
|
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
# Define the onboarding steps and their order - matching frontend UnifiedOnboardingWizard step IDs
|
2025-08-11 07:01:08 +02:00
|
|
|
ONBOARDING_STEPS = [
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
# Phase 0: System Steps
|
|
|
|
|
"user_registered", # Auto-completed: User account created
|
|
|
|
|
|
|
|
|
|
# Phase 1: Discovery
|
2025-12-18 13:26:32 +01:00
|
|
|
"bakery-type-selection", # Choose bakery type: production/retail/mixed (skipped for enterprise)
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
|
|
|
|
# Phase 2: Core Setup
|
|
|
|
|
"setup", # Basic bakery setup and tenant creation
|
2025-11-12 14:48:46 +00:00
|
|
|
# NOTE: POI detection now happens automatically in background during tenant registration
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
2025-12-18 13:26:32 +01:00
|
|
|
# Phase 2-Enterprise: Child Tenants Setup (enterprise tier only)
|
|
|
|
|
"child-tenants-setup", # Configure child tenants/branches for enterprise tier
|
|
|
|
|
|
2025-11-12 14:48:46 +00:00
|
|
|
# Phase 2a: AI-Assisted Inventory Setup (REFACTORED - split into 3 focused steps)
|
2025-11-09 09:22:08 +01:00
|
|
|
"upload-sales-data", # File upload, validation, and AI classification
|
|
|
|
|
"inventory-review", # Review and confirm AI-detected products with type selection
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
"initial-stock-entry", # Capture initial stock levels
|
|
|
|
|
|
2025-11-12 14:48:46 +00:00
|
|
|
# Phase 2b: Product Categorization (optional advanced categorization)
|
2025-11-09 09:22:08 +01:00
|
|
|
"product-categorization", # Advanced categorization (may be deprecated)
|
|
|
|
|
|
2025-11-12 14:48:46 +00:00
|
|
|
# Phase 2c: Suppliers (shared by all paths)
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
"suppliers-setup", # Suppliers configuration
|
2025-11-07 08:18:39 +00:00
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Phase 2d: Manual Inventory Setup (alternative to AI-assisted path)
|
|
|
|
|
"inventory-setup", # Manual ingredient/inventory management
|
|
|
|
|
|
2025-11-07 08:18:39 +00:00
|
|
|
# Phase 3: Advanced Configuration (all optional)
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
"recipes-setup", # Production recipes (conditional: production/mixed bakery)
|
|
|
|
|
"quality-setup", # Quality standards and templates
|
|
|
|
|
"team-setup", # Team members and permissions
|
|
|
|
|
|
|
|
|
|
# Phase 4: ML & Finalization
|
|
|
|
|
"ml-training", # AI model training
|
2025-11-12 14:48:46 +00:00
|
|
|
# "setup-review" removed - not useful for user, completion step is final
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
"completion" # Onboarding completed
|
2025-08-11 07:01:08 +02:00
|
|
|
]
|
|
|
|
|
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
# Step dependencies - defines which steps must be completed before others
|
|
|
|
|
# Steps not listed here have no dependencies (can be completed anytime after user_registered)
|
2025-08-11 07:01:08 +02:00
|
|
|
STEP_DEPENDENCIES = {
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
# Discovery phase
|
2025-11-07 08:18:39 +00:00
|
|
|
"bakery-type-selection": ["user_registered"],
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
2025-12-18 13:26:32 +01:00
|
|
|
# Core setup - NOTE: bakery-type-selection dependency is conditionally required
|
|
|
|
|
# Enterprise users skip bakery-type-selection, so setup only requires user_registered for them
|
2025-11-07 08:18:39 +00:00
|
|
|
"setup": ["user_registered", "bakery-type-selection"],
|
2025-11-12 14:48:46 +00:00
|
|
|
# NOTE: POI detection removed from steps - now happens automatically in background
|
2025-11-12 15:34:10 +01:00
|
|
|
|
2025-12-18 13:26:32 +01:00
|
|
|
# Enterprise child tenants setup - requires setup (parent tenant) to be completed first
|
|
|
|
|
"child-tenants-setup": ["user_registered", "setup"],
|
|
|
|
|
|
2025-11-09 09:22:08 +01:00
|
|
|
# AI-Assisted Inventory Setup - REFACTORED into 3 sequential steps
|
|
|
|
|
"upload-sales-data": ["user_registered", "setup"],
|
|
|
|
|
"inventory-review": ["user_registered", "setup", "upload-sales-data"],
|
|
|
|
|
"initial-stock-entry": ["user_registered", "setup", "upload-sales-data", "inventory-review"],
|
|
|
|
|
|
|
|
|
|
# Advanced product categorization (optional, may be deprecated)
|
|
|
|
|
"product-categorization": ["user_registered", "setup", "upload-sales-data"],
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
2025-11-09 09:22:08 +01:00
|
|
|
# Suppliers (after inventory review)
|
|
|
|
|
"suppliers-setup": ["user_registered", "setup", "inventory-review"],
|
2025-11-07 08:18:39 +00:00
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Manual inventory setup (alternative to AI-assisted path)
|
|
|
|
|
"inventory-setup": ["user_registered", "setup"],
|
|
|
|
|
|
2025-11-07 08:18:39 +00:00
|
|
|
# Advanced configuration (optional, minimal dependencies)
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
"recipes-setup": ["user_registered", "setup"],
|
|
|
|
|
"quality-setup": ["user_registered", "setup"],
|
|
|
|
|
"team-setup": ["user_registered", "setup"],
|
|
|
|
|
|
2025-11-13 16:01:08 +01:00
|
|
|
# ML Training - requires AI path completion
|
|
|
|
|
# NOTE: POI detection happens automatically in background, not required as dependency
|
|
|
|
|
"ml-training": ["user_registered", "setup", "upload-sales-data", "inventory-review"],
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
|
|
|
|
# Review and completion
|
|
|
|
|
"setup-review": ["user_registered", "setup"],
|
|
|
|
|
"completion": ["user_registered", "setup"] # Minimal requirements for completion
|
2025-08-11 07:01:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OnboardingService:
|
|
|
|
|
"""Service for managing user onboarding progress"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, db: AsyncSession):
|
|
|
|
|
self.db = db
|
|
|
|
|
self.user_service = UserService(db)
|
|
|
|
|
self.onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
|
|
|
|
|
async def get_user_progress(self, user_id: str) -> UserProgress:
|
|
|
|
|
"""Get current onboarding progress for user"""
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Get user's onboarding data from user preferences or separate table
|
|
|
|
|
user_progress_data = await self._get_user_onboarding_data(user_id)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Calculate current status for each step
|
|
|
|
|
steps = []
|
|
|
|
|
completed_steps = []
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
for step_name in ONBOARDING_STEPS:
|
|
|
|
|
step_data = user_progress_data.get(step_name, {})
|
|
|
|
|
is_completed = step_data.get("completed", False)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
if is_completed:
|
|
|
|
|
completed_steps.append(step_name)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
steps.append(OnboardingStepStatus(
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=is_completed,
|
|
|
|
|
completed_at=step_data.get("completed_at"),
|
|
|
|
|
data=step_data.get("data", {})
|
|
|
|
|
))
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Determine current and next step
|
|
|
|
|
current_step = self._get_current_step(completed_steps)
|
|
|
|
|
next_step = self._get_next_step(completed_steps)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Calculate completion percentage
|
|
|
|
|
completion_percentage = (len(completed_steps) / len(ONBOARDING_STEPS)) * 100
|
2025-12-19 13:10:24 +01:00
|
|
|
|
|
|
|
|
# Check if fully completed - based on REQUIRED steps only
|
|
|
|
|
# Define required steps
|
|
|
|
|
REQUIRED_STEPS = [
|
|
|
|
|
"user_registered",
|
|
|
|
|
"setup",
|
|
|
|
|
"suppliers-setup",
|
|
|
|
|
"ml-training",
|
|
|
|
|
"completion"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Get user's subscription tier to determine if bakery-type-selection is required
|
|
|
|
|
user_registered_data = user_progress_data.get("user_registered", {}).get("data", {})
|
|
|
|
|
subscription_tier = user_registered_data.get("subscription_tier", "professional")
|
|
|
|
|
|
|
|
|
|
# Add bakery-type-selection to required steps for non-enterprise users
|
|
|
|
|
if subscription_tier != "enterprise":
|
|
|
|
|
required_steps_for_user = REQUIRED_STEPS + ["bakery-type-selection"]
|
|
|
|
|
else:
|
|
|
|
|
required_steps_for_user = REQUIRED_STEPS
|
|
|
|
|
|
|
|
|
|
# Check if all required steps are completed
|
|
|
|
|
required_completed = all(
|
|
|
|
|
user_progress_data.get(step, {}).get("completed", False)
|
|
|
|
|
for step in required_steps_for_user
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
fully_completed = required_completed
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Extract wizard context state for frontend restoration
|
|
|
|
|
context_state = self._extract_context_state(user_progress_data)
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
return UserProgress(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
steps=steps,
|
|
|
|
|
current_step=current_step,
|
|
|
|
|
next_step=next_step,
|
|
|
|
|
completion_percentage=completion_percentage,
|
|
|
|
|
fully_completed=fully_completed,
|
2026-01-04 21:37:44 +01:00
|
|
|
last_updated=datetime.now(timezone.utc),
|
|
|
|
|
context_state=context_state
|
2025-08-11 07:01:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def update_step(self, user_id: str, update_request: UpdateStepRequest) -> UserProgress:
|
|
|
|
|
"""Update a specific onboarding step"""
|
|
|
|
|
|
|
|
|
|
step_name = update_request.step_name
|
|
|
|
|
|
|
|
|
|
# Validate step name
|
|
|
|
|
if step_name not in ONBOARDING_STEPS:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=f"Invalid step name: {step_name}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Check dependencies if marking as completed
|
|
|
|
|
if update_request.completed:
|
|
|
|
|
can_complete = await self._can_complete_step(user_id, step_name)
|
|
|
|
|
if not can_complete:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=f"Cannot complete step {step_name}: dependencies not met"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Update the step
|
|
|
|
|
await self._update_user_onboarding_data(
|
2025-09-29 07:54:25 +02:00
|
|
|
user_id,
|
|
|
|
|
step_name,
|
2025-08-11 07:01:08 +02:00
|
|
|
{
|
|
|
|
|
"completed": update_request.completed,
|
|
|
|
|
"completed_at": datetime.now(timezone.utc).isoformat() if update_request.completed else None,
|
|
|
|
|
"data": update_request.data or {}
|
|
|
|
|
}
|
|
|
|
|
)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
|
|
|
|
# Try to update summary and handle partial failures gracefully
|
|
|
|
|
try:
|
|
|
|
|
# Update the user's onboarding summary
|
|
|
|
|
await self._update_user_summary(user_id)
|
|
|
|
|
except HTTPException as he:
|
|
|
|
|
# If it's a 207 Multi-Status (partial success), log warning but continue
|
|
|
|
|
if he.status_code == status.HTTP_207_MULTI_STATUS:
|
|
|
|
|
logger.warning(f"Summary update failed for user {user_id}, step {step_name}: {he.detail}")
|
|
|
|
|
# Continue execution - the step update was successful
|
|
|
|
|
else:
|
|
|
|
|
# Re-raise other HTTP exceptions
|
|
|
|
|
raise
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Return updated progress
|
|
|
|
|
return await self.get_user_progress(user_id)
|
|
|
|
|
|
|
|
|
|
async def get_next_step(self, user_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""Get the next required step for user"""
|
|
|
|
|
progress = await self.get_user_progress(user_id)
|
|
|
|
|
|
|
|
|
|
if progress.fully_completed:
|
|
|
|
|
return {"step": "dashboard_accessible", "completed": True}
|
|
|
|
|
|
|
|
|
|
return {"step": progress.next_step or progress.current_step}
|
|
|
|
|
|
|
|
|
|
async def can_access_step(self, user_id: str, step_name: str) -> Dict[str, Any]:
|
|
|
|
|
"""Check if user can access a specific step"""
|
|
|
|
|
|
|
|
|
|
if step_name not in ONBOARDING_STEPS:
|
|
|
|
|
return {"can_access": False, "reason": "Invalid step name"}
|
|
|
|
|
|
|
|
|
|
can_access = await self._can_complete_step(user_id, step_name)
|
|
|
|
|
return {"can_access": can_access}
|
|
|
|
|
|
|
|
|
|
async def complete_onboarding(self, user_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""Mark entire onboarding as complete"""
|
2025-12-19 13:10:24 +01:00
|
|
|
|
|
|
|
|
# Get user's progress
|
2025-08-11 07:01:08 +02:00
|
|
|
progress = await self.get_user_progress(user_id)
|
2025-12-19 13:10:24 +01:00
|
|
|
user_progress_data = await self._get_user_onboarding_data(user_id)
|
|
|
|
|
|
|
|
|
|
# Define REQUIRED steps (excluding optional/conditional steps)
|
|
|
|
|
# These are the minimum steps needed to complete onboarding
|
|
|
|
|
REQUIRED_STEPS = [
|
|
|
|
|
"user_registered",
|
|
|
|
|
"setup", # bakery-type-selection is conditional for enterprise
|
|
|
|
|
"suppliers-setup",
|
|
|
|
|
"ml-training",
|
|
|
|
|
"completion"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Define CONDITIONAL steps that are only required for certain tiers/flows
|
|
|
|
|
CONDITIONAL_STEPS = {
|
|
|
|
|
"child-tenants-setup": "enterprise", # Only for enterprise tier
|
|
|
|
|
"product-categorization": None, # Optional for all
|
|
|
|
|
"bakery-type-selection": "non-enterprise", # Only for non-enterprise
|
|
|
|
|
"upload-sales-data": None, # Optional (manual inventory setup is alternative)
|
|
|
|
|
"inventory-review": None, # Optional (manual inventory setup is alternative)
|
|
|
|
|
"initial-stock-entry": None, # Optional
|
|
|
|
|
"recipes-setup": None, # Optional
|
|
|
|
|
"quality-setup": None, # Optional
|
|
|
|
|
"team-setup": None, # Optional
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Get user's subscription tier
|
|
|
|
|
user_registered_data = user_progress_data.get("user_registered", {}).get("data", {})
|
|
|
|
|
subscription_tier = user_registered_data.get("subscription_tier", "professional")
|
|
|
|
|
|
|
|
|
|
# Check if all REQUIRED steps are completed
|
|
|
|
|
incomplete_required_steps = []
|
|
|
|
|
for step_name in REQUIRED_STEPS:
|
|
|
|
|
if not user_progress_data.get(step_name, {}).get("completed", False):
|
|
|
|
|
# Special case: bakery-type-selection is not required for enterprise
|
|
|
|
|
if step_name == "bakery-type-selection" and subscription_tier == "enterprise":
|
|
|
|
|
continue
|
|
|
|
|
incomplete_required_steps.append(step_name)
|
|
|
|
|
|
|
|
|
|
# If there are incomplete required steps, reject completion
|
|
|
|
|
if incomplete_required_steps:
|
2025-08-11 07:01:08 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
2025-12-19 13:10:24 +01:00
|
|
|
detail=f"Cannot complete onboarding: incomplete required steps: {incomplete_required_steps}"
|
2025-08-11 07:01:08 +02:00
|
|
|
)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
|
|
|
|
# Log conditional steps that are incomplete (warning only, not blocking)
|
|
|
|
|
incomplete_conditional_steps = [
|
|
|
|
|
step.step_name for step in progress.steps
|
|
|
|
|
if not step.completed and step.step_name in CONDITIONAL_STEPS
|
|
|
|
|
]
|
|
|
|
|
if incomplete_conditional_steps:
|
|
|
|
|
logger.info(
|
|
|
|
|
f"User {user_id} completing onboarding with incomplete optional steps: {incomplete_conditional_steps}",
|
|
|
|
|
extra={"user_id": user_id, "subscription_tier": subscription_tier}
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Update user's isOnboardingComplete flag
|
|
|
|
|
await self.user_service.update_user_field(
|
2025-12-19 13:10:24 +01:00
|
|
|
user_id,
|
|
|
|
|
"is_onboarding_complete",
|
2025-08-11 07:01:08 +02:00
|
|
|
True
|
|
|
|
|
)
|
2025-12-19 13:10:24 +01:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "Onboarding completed successfully",
|
|
|
|
|
"optional_steps_skipped": incomplete_conditional_steps
|
|
|
|
|
}
|
2025-08-11 07:01:08 +02:00
|
|
|
|
|
|
|
|
def _get_current_step(self, completed_steps: List[str]) -> str:
|
|
|
|
|
"""Determine current step based on completed steps"""
|
|
|
|
|
for step in ONBOARDING_STEPS:
|
|
|
|
|
if step not in completed_steps:
|
|
|
|
|
return step
|
|
|
|
|
return ONBOARDING_STEPS[-1] # All completed
|
|
|
|
|
|
|
|
|
|
def _get_next_step(self, completed_steps: List[str]) -> Optional[str]:
|
|
|
|
|
"""Determine next step based on completed steps"""
|
|
|
|
|
current_step = self._get_current_step(completed_steps)
|
|
|
|
|
current_index = ONBOARDING_STEPS.index(current_step)
|
2026-01-04 21:37:44 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
if current_index < len(ONBOARDING_STEPS) - 1:
|
|
|
|
|
return ONBOARDING_STEPS[current_index + 1]
|
2026-01-04 21:37:44 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
return None # No next step
|
2026-01-04 21:37:44 +01:00
|
|
|
|
|
|
|
|
def _extract_context_state(self, user_progress_data: Dict[str, Any]) -> WizardContextState:
|
|
|
|
|
"""
|
|
|
|
|
Extract wizard context state from completed step data.
|
|
|
|
|
This allows the frontend to restore WizardContext state on session restore,
|
|
|
|
|
ensuring conditional steps remain visible based on previous progress.
|
|
|
|
|
"""
|
|
|
|
|
# Extract bakeryType from bakery-type-selection step
|
|
|
|
|
bakery_step = user_progress_data.get("bakery-type-selection", {})
|
|
|
|
|
bakery_type = None
|
|
|
|
|
if bakery_step.get("completed"):
|
|
|
|
|
bakery_type = bakery_step.get("data", {}).get("bakeryType")
|
|
|
|
|
|
|
|
|
|
# Extract tenantId from setup step
|
|
|
|
|
setup_step = user_progress_data.get("setup", {})
|
|
|
|
|
tenant_id = None
|
|
|
|
|
if setup_step.get("completed"):
|
|
|
|
|
setup_data = setup_step.get("data", {})
|
|
|
|
|
tenant_id = setup_data.get("tenantId") or setup_data.get("tenant", {}).get("id")
|
|
|
|
|
|
|
|
|
|
# Extract subscription tier from user_registered step
|
|
|
|
|
user_registered_step = user_progress_data.get("user_registered", {})
|
|
|
|
|
subscription_tier = user_registered_step.get("data", {}).get("subscription_tier")
|
|
|
|
|
|
|
|
|
|
# Derive completion flags from step completion status
|
|
|
|
|
upload_step = user_progress_data.get("upload-sales-data", {})
|
|
|
|
|
inventory_review_step = user_progress_data.get("inventory-review", {})
|
|
|
|
|
stock_entry_step = user_progress_data.get("initial-stock-entry", {})
|
|
|
|
|
categorization_step = user_progress_data.get("product-categorization", {})
|
|
|
|
|
suppliers_step = user_progress_data.get("suppliers-setup", {})
|
|
|
|
|
inventory_setup_step = user_progress_data.get("inventory-setup", {})
|
|
|
|
|
recipes_step = user_progress_data.get("recipes-setup", {})
|
|
|
|
|
quality_step = user_progress_data.get("quality-setup", {})
|
|
|
|
|
team_step = user_progress_data.get("team-setup", {})
|
|
|
|
|
child_tenants_step = user_progress_data.get("child-tenants-setup", {})
|
|
|
|
|
ml_training_step = user_progress_data.get("ml-training", {})
|
|
|
|
|
|
|
|
|
|
return WizardContextState(
|
|
|
|
|
bakery_type=bakery_type,
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
|
subscription_tier=subscription_tier,
|
|
|
|
|
ai_analysis_complete=upload_step.get("completed", False),
|
|
|
|
|
inventory_review_completed=inventory_review_step.get("completed", False),
|
|
|
|
|
stock_entry_completed=stock_entry_step.get("completed", False),
|
|
|
|
|
categorization_completed=categorization_step.get("completed", False),
|
|
|
|
|
suppliers_completed=suppliers_step.get("completed", False),
|
|
|
|
|
inventory_setup_completed=inventory_setup_step.get("completed", False),
|
|
|
|
|
recipes_completed=recipes_step.get("completed", False),
|
|
|
|
|
quality_completed=quality_step.get("completed", False),
|
|
|
|
|
team_completed=team_step.get("completed", False),
|
|
|
|
|
child_tenants_completed=child_tenants_step.get("completed", False),
|
|
|
|
|
ml_training_complete=ml_training_step.get("completed", False),
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
async def _can_complete_step(self, user_id: str, step_name: str) -> bool:
|
|
|
|
|
"""Check if user can complete a specific step"""
|
2025-12-18 13:26:32 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Get required dependencies for this step
|
2025-12-18 13:26:32 +01:00
|
|
|
required_steps = STEP_DEPENDENCIES.get(step_name, []).copy() # Copy to avoid modifying original
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
if not required_steps:
|
|
|
|
|
return True # No dependencies
|
2025-12-18 13:26:32 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Check if all required steps are completed
|
|
|
|
|
user_progress_data = await self._get_user_onboarding_data(user_id)
|
2025-12-18 13:26:32 +01:00
|
|
|
|
|
|
|
|
# SPECIAL HANDLING FOR ENTERPRISE ONBOARDING
|
|
|
|
|
# Enterprise users skip bakery-type-selection step, so don't require it for setup
|
|
|
|
|
if step_name == "setup" and "bakery-type-selection" in required_steps:
|
|
|
|
|
# Check if user's tenant has enterprise subscription tier
|
|
|
|
|
# We do this by checking if the user has any data indicating enterprise tier
|
|
|
|
|
# This could be stored in user_registered step data or we can infer from context
|
|
|
|
|
user_registered_data = user_progress_data.get("user_registered", {}).get("data", {})
|
|
|
|
|
subscription_tier = user_registered_data.get("subscription_tier")
|
|
|
|
|
|
|
|
|
|
if subscription_tier == "enterprise":
|
|
|
|
|
# Enterprise users don't need bakery-type-selection
|
|
|
|
|
logger.info(f"Enterprise user {user_id}: Skipping bakery-type-selection requirement for setup step")
|
|
|
|
|
required_steps.remove("bakery-type-selection")
|
|
|
|
|
elif not user_progress_data.get("bakery-type-selection", {}).get("completed", False):
|
|
|
|
|
# Non-enterprise user hasn't completed bakery-type-selection
|
|
|
|
|
# But allow setup anyway if user_registered is complete (frontend will handle it)
|
|
|
|
|
# This is a fallback for when subscription_tier is not stored in user_registered data
|
|
|
|
|
logger.info(f"User {user_id}: Allowing setup without bakery-type-selection (will be auto-set for enterprise)")
|
|
|
|
|
required_steps.remove("bakery-type-selection")
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
for required_step in required_steps:
|
2026-01-04 21:37:44 +01:00
|
|
|
is_completed = user_progress_data.get(required_step, {}).get("completed", False)
|
|
|
|
|
if not is_completed:
|
|
|
|
|
logger.warning(
|
|
|
|
|
f"Step {step_name} blocked for user {user_id}: missing dependency {required_step}",
|
|
|
|
|
extra={
|
|
|
|
|
"user_id": user_id,
|
|
|
|
|
"step_name": step_name,
|
|
|
|
|
"missing_dependency": required_step,
|
|
|
|
|
"all_required_steps": required_steps,
|
|
|
|
|
"user_progress_keys": list(user_progress_data.keys()),
|
|
|
|
|
"dependency_data": user_progress_data.get(required_step, {})
|
|
|
|
|
}
|
|
|
|
|
)
|
2025-08-11 07:01:08 +02:00
|
|
|
return False
|
2025-12-18 13:26:32 +01:00
|
|
|
|
2025-09-07 17:25:30 +02:00
|
|
|
# SPECIAL VALIDATION FOR ML TRAINING STEP
|
|
|
|
|
if step_name == "ml-training":
|
2025-11-09 09:22:08 +01:00
|
|
|
# ML training requires AI-assisted path completion
|
|
|
|
|
# Check if upload-sales-data and inventory-review are completed
|
|
|
|
|
upload_complete = user_progress_data.get("upload-sales-data", {}).get("completed", False)
|
|
|
|
|
inventory_complete = user_progress_data.get("inventory-review", {}).get("completed", False)
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
|
2025-11-09 09:22:08 +01:00
|
|
|
if upload_complete and inventory_complete:
|
2025-11-07 08:18:39 +00:00
|
|
|
# Validate sales data was imported
|
2025-11-09 09:22:08 +01:00
|
|
|
upload_data = user_progress_data.get("upload-sales-data", {}).get("data", {})
|
|
|
|
|
inventory_data = user_progress_data.get("inventory-review", {}).get("data", {})
|
|
|
|
|
|
|
|
|
|
# Check if sales data was processed
|
|
|
|
|
has_sales_data = (
|
|
|
|
|
upload_data.get("validationResult", {}).get("is_valid", False) or
|
|
|
|
|
upload_data.get("aiSuggestions", []) or
|
|
|
|
|
inventory_data.get("inventoryItemsCreated", 0) > 0
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
)
|
|
|
|
|
|
2025-11-09 09:22:08 +01:00
|
|
|
if has_sales_data:
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
logger.info(f"ML training allowed for user {user_id}: AI path with sales data")
|
|
|
|
|
return True
|
|
|
|
|
|
2025-11-07 08:18:39 +00:00
|
|
|
# AI path not complete or no sales data
|
|
|
|
|
logger.warning(f"ML training blocked for user {user_id}: No inventory data from AI path")
|
Make backend robust with comprehensive onboarding steps
Backend Changes (services/auth/app/api/onboarding_progress.py):
- Expanded ONBOARDING_STEPS to include all 19 frontend steps
- Phase 0: user_registered (system)
- Phase 1: bakery-type-selection, data-source-choice (discovery)
- Phase 2: setup, smart-inventory-setup, product-categorization, initial-stock-entry (core setup & AI path)
- Phase 2b: suppliers-setup, inventory-setup, recipes-setup, production-processes (manual path)
- Phase 3: quality-setup, team-setup (advanced config)
- Phase 4: ml-training, setup-review, completion (finalization)
- Updated STEP_DEPENDENCIES with granular requirements
- AI path: smart-inventory-setup → product-categorization → initial-stock-entry
- Manual path: Independent setup for suppliers, inventory, recipes, processes
- Flexible ML training: accepts either AI or manual inventory path
- Enhanced ML training validation
- Supports both AI-assisted path (sales data) and manual inventory path
- More flexible validation logic for multi-path onboarding
Frontend Changes (UnifiedOnboardingWizard.tsx):
- Fixed auto-complete step name: 'suppliers' → 'suppliers-setup'
- All step IDs now match backend ONBOARDING_STEPS exactly
- Removed temporary step mapping workarounds
Frontend Changes (apiClient.ts):
- Fixed tenant ID requirement warnings for onboarding endpoints
- Added noTenantEndpoints list for user-level endpoints:
- /auth/me/onboarding (tenant created during onboarding)
- /auth/me (user profile)
- /auth/register, /auth/login
- Eliminated false warnings during onboarding flow
This makes the onboarding system fully functional with:
✅ Backend validates all 19 onboarding steps
✅ Proper dependency tracking for multi-path onboarding
✅ No more "Invalid step name" errors
✅ No more tenant ID warnings for onboarding
✅ Robust state tracking for complete user journey
2025-11-06 13:38:06 +00:00
|
|
|
return False
|
2025-09-07 17:25:30 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
async def _get_user_onboarding_data(self, user_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""Get user's onboarding progress data from storage"""
|
|
|
|
|
try:
|
|
|
|
|
# Get all onboarding steps for the user from database
|
|
|
|
|
steps = await self.onboarding_repo.get_user_progress_steps(user_id)
|
|
|
|
|
|
|
|
|
|
# Convert to the expected dictionary format
|
|
|
|
|
progress_data = {}
|
|
|
|
|
for step in steps:
|
|
|
|
|
progress_data[step.step_name] = {
|
|
|
|
|
"completed": step.completed,
|
|
|
|
|
"completed_at": step.completed_at,
|
|
|
|
|
"data": step.step_data or {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress_data
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error getting onboarding data for user {user_id}: {e}")
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
async def _update_user_onboarding_data(
|
|
|
|
|
self,
|
|
|
|
|
user_id: str,
|
|
|
|
|
step_name: str,
|
|
|
|
|
step_data: Dict[str, Any]
|
|
|
|
|
):
|
|
|
|
|
"""Update user's onboarding step data"""
|
|
|
|
|
try:
|
|
|
|
|
# Extract the completion status and other data
|
|
|
|
|
completed = step_data.get("completed", False)
|
|
|
|
|
data_payload = step_data.get("data", {})
|
|
|
|
|
|
|
|
|
|
# Update the step in database
|
|
|
|
|
updated_step = await self.onboarding_repo.upsert_user_step(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=completed,
|
|
|
|
|
step_data=data_payload
|
|
|
|
|
)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
logger.info(f"Successfully updated onboarding step for user {user_id}: {step_name} = {step_data}")
|
|
|
|
|
return updated_step
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error updating onboarding data for user {user_id}, step {step_name}: {e}")
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
async def _update_user_summary(self, user_id: str):
|
|
|
|
|
"""Update user's onboarding summary after step changes"""
|
|
|
|
|
try:
|
|
|
|
|
# Get updated progress
|
|
|
|
|
user_progress_data = await self._get_user_onboarding_data(user_id)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Calculate current status
|
|
|
|
|
completed_steps = []
|
|
|
|
|
for step_name in ONBOARDING_STEPS:
|
|
|
|
|
if user_progress_data.get(step_name, {}).get("completed", False):
|
|
|
|
|
completed_steps.append(step_name)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Determine current and next step
|
|
|
|
|
current_step = self._get_current_step(completed_steps)
|
|
|
|
|
next_step = self._get_next_step(completed_steps)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Calculate completion percentage
|
|
|
|
|
completion_percentage = (len(completed_steps) / len(ONBOARDING_STEPS)) * 100
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Check if fully completed
|
|
|
|
|
fully_completed = len(completed_steps) == len(ONBOARDING_STEPS)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Format steps count
|
|
|
|
|
steps_completed_count = f"{len(completed_steps)}/{len(ONBOARDING_STEPS)}"
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
# Update summary in database
|
|
|
|
|
await self.onboarding_repo.upsert_user_summary(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
current_step=current_step,
|
|
|
|
|
next_step=next_step,
|
|
|
|
|
completion_percentage=completion_percentage,
|
|
|
|
|
fully_completed=fully_completed,
|
|
|
|
|
steps_completed_count=steps_completed_count
|
|
|
|
|
)
|
2025-09-29 07:54:25 +02:00
|
|
|
|
|
|
|
|
logger.debug(f"Successfully updated onboarding summary for user {user_id}")
|
|
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
except Exception as e:
|
2025-09-29 07:54:25 +02:00
|
|
|
logger.error(f"Error updating onboarding summary for user {user_id}: {e}",
|
|
|
|
|
extra={"user_id": user_id, "error_type": type(e).__name__})
|
|
|
|
|
# Raise a warning-level HTTPException to inform frontend without breaking the flow
|
|
|
|
|
# This allows the step update to succeed while alerting about summary issues
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_207_MULTI_STATUS,
|
|
|
|
|
detail=f"Step updated successfully, but summary update failed: {str(e)}"
|
|
|
|
|
)
|
2025-08-11 07:01:08 +02:00
|
|
|
|
|
|
|
|
# API Routes
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.get("/api/v1/auth/me/onboarding/progress", response_model=UserProgress)
|
2025-08-11 07:01:08 +02:00
|
|
|
async def get_user_progress(
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get current user's onboarding progress"""
|
2025-10-01 21:56:38 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
try:
|
2025-12-25 20:20:16 +01:00
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# DEMO FIX: Demo users don't need onboarding - return fully completed progress
|
|
|
|
|
# Demo tenants are pre-configured through cloning, so onboarding is not required
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Demo user {user_id} accessing onboarding progress - returning completed state")
|
|
|
|
|
|
|
|
|
|
# Create all steps as completed for demo users
|
|
|
|
|
demo_steps = []
|
|
|
|
|
for step_name in ONBOARDING_STEPS:
|
|
|
|
|
demo_steps.append(OnboardingStepStatus(
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=True,
|
|
|
|
|
completed_at=datetime.now(timezone.utc),
|
|
|
|
|
data={"auto_completed": True, "demo_mode": True}
|
|
|
|
|
))
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Create a fully completed context state for demo users
|
|
|
|
|
demo_context_state = WizardContextState(
|
|
|
|
|
bakery_type="mixed",
|
|
|
|
|
tenant_id="demo-tenant",
|
|
|
|
|
subscription_tier="professional",
|
|
|
|
|
ai_analysis_complete=True,
|
|
|
|
|
inventory_review_completed=True,
|
|
|
|
|
stock_entry_completed=True,
|
|
|
|
|
categorization_completed=True,
|
|
|
|
|
suppliers_completed=True,
|
|
|
|
|
recipes_completed=True,
|
|
|
|
|
quality_completed=True,
|
|
|
|
|
team_completed=True,
|
|
|
|
|
child_tenants_completed=False,
|
|
|
|
|
ml_training_complete=True,
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-25 20:20:16 +01:00
|
|
|
return UserProgress(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
steps=demo_steps,
|
|
|
|
|
current_step="completion",
|
|
|
|
|
next_step=None,
|
|
|
|
|
completion_percentage=100.0,
|
|
|
|
|
fully_completed=True,
|
2026-01-04 21:37:44 +01:00
|
|
|
last_updated=datetime.now(timezone.utc),
|
|
|
|
|
context_state=demo_context_state
|
2025-12-25 20:20:16 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Regular user flow
|
2025-08-11 07:01:08 +02:00
|
|
|
onboarding_service = OnboardingService(db)
|
2025-12-25 20:20:16 +01:00
|
|
|
progress = await onboarding_service.get_user_progress(user_id)
|
2025-08-11 07:01:08 +02:00
|
|
|
return progress
|
2025-10-01 21:56:38 +02:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Get onboarding progress error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get onboarding progress"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.get("/api/v1/auth/users/{user_id}/onboarding/progress", response_model=UserProgress)
|
2025-10-01 21:56:38 +02:00
|
|
|
async def get_user_progress_by_id(
|
|
|
|
|
user_id: str,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Get onboarding progress for a specific user
|
|
|
|
|
Available for service-to-service calls and admin users
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# Allow service tokens or admin users
|
|
|
|
|
user_type = current_user.get("type", "user")
|
|
|
|
|
user_role = current_user.get("role", "user")
|
|
|
|
|
|
|
|
|
|
if user_type != "service" and user_role not in ["admin", "super_admin"]:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Insufficient permissions to access other users' onboarding progress"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
try:
|
2025-12-25 20:20:16 +01:00
|
|
|
# DEMO FIX: Handle demo users - return fully completed progress
|
|
|
|
|
if user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Service requesting demo user {user_id} onboarding progress - returning completed state")
|
|
|
|
|
|
|
|
|
|
# Create all steps as completed for demo users
|
|
|
|
|
demo_steps = []
|
|
|
|
|
for step_name in ONBOARDING_STEPS:
|
|
|
|
|
demo_steps.append(OnboardingStepStatus(
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=True,
|
|
|
|
|
completed_at=datetime.now(timezone.utc),
|
|
|
|
|
data={"auto_completed": True, "demo_mode": True}
|
|
|
|
|
))
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Create a fully completed context state for demo users
|
|
|
|
|
demo_context_state = WizardContextState(
|
|
|
|
|
bakery_type="mixed",
|
|
|
|
|
tenant_id="demo-tenant",
|
|
|
|
|
subscription_tier="professional",
|
|
|
|
|
ai_analysis_complete=True,
|
|
|
|
|
inventory_review_completed=True,
|
|
|
|
|
stock_entry_completed=True,
|
|
|
|
|
categorization_completed=True,
|
|
|
|
|
suppliers_completed=True,
|
|
|
|
|
recipes_completed=True,
|
|
|
|
|
quality_completed=True,
|
|
|
|
|
team_completed=True,
|
|
|
|
|
child_tenants_completed=False,
|
|
|
|
|
ml_training_complete=True,
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-25 20:20:16 +01:00
|
|
|
return UserProgress(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
steps=demo_steps,
|
|
|
|
|
current_step="completion",
|
|
|
|
|
next_step=None,
|
|
|
|
|
completion_percentage=100.0,
|
|
|
|
|
fully_completed=True,
|
2026-01-04 21:37:44 +01:00
|
|
|
last_updated=datetime.now(timezone.utc),
|
|
|
|
|
context_state=demo_context_state
|
2025-12-25 20:20:16 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Regular user flow
|
2025-10-01 21:56:38 +02:00
|
|
|
onboarding_service = OnboardingService(db)
|
|
|
|
|
progress = await onboarding_service.get_user_progress(user_id)
|
|
|
|
|
return progress
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Get onboarding progress error for user {user_id}: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get onboarding progress"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.put("/api/v1/auth/me/onboarding/step", response_model=UserProgress)
|
2025-08-11 07:01:08 +02:00
|
|
|
async def update_onboarding_step(
|
|
|
|
|
update_request: UpdateStepRequest,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Update a specific onboarding step"""
|
2025-12-25 20:20:16 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
try:
|
2025-12-25 20:20:16 +01:00
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# DEMO FIX: Demo users don't update onboarding - return completed progress
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Demo user {user_id} attempting to update onboarding step - returning completed state (no-op)")
|
|
|
|
|
|
|
|
|
|
# Create all steps as completed for demo users
|
|
|
|
|
demo_steps = []
|
|
|
|
|
for step_name in ONBOARDING_STEPS:
|
|
|
|
|
demo_steps.append(OnboardingStepStatus(
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=True,
|
|
|
|
|
completed_at=datetime.now(timezone.utc),
|
|
|
|
|
data={"auto_completed": True, "demo_mode": True}
|
|
|
|
|
))
|
|
|
|
|
|
2026-01-04 21:37:44 +01:00
|
|
|
# Create a fully completed context state for demo users
|
|
|
|
|
demo_context_state = WizardContextState(
|
|
|
|
|
bakery_type="mixed",
|
|
|
|
|
tenant_id="demo-tenant",
|
|
|
|
|
subscription_tier="professional",
|
|
|
|
|
ai_analysis_complete=True,
|
|
|
|
|
inventory_review_completed=True,
|
|
|
|
|
stock_entry_completed=True,
|
|
|
|
|
categorization_completed=True,
|
|
|
|
|
suppliers_completed=True,
|
|
|
|
|
recipes_completed=True,
|
|
|
|
|
quality_completed=True,
|
|
|
|
|
team_completed=True,
|
|
|
|
|
child_tenants_completed=False,
|
|
|
|
|
ml_training_complete=True,
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-25 20:20:16 +01:00
|
|
|
return UserProgress(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
steps=demo_steps,
|
|
|
|
|
current_step="completion",
|
|
|
|
|
next_step=None,
|
|
|
|
|
completion_percentage=100.0,
|
|
|
|
|
fully_completed=True,
|
2026-01-04 21:37:44 +01:00
|
|
|
last_updated=datetime.now(timezone.utc),
|
|
|
|
|
context_state=demo_context_state
|
2025-12-25 20:20:16 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Regular user flow
|
2025-08-11 07:01:08 +02:00
|
|
|
onboarding_service = OnboardingService(db)
|
|
|
|
|
progress = await onboarding_service.update_step(
|
2025-12-25 20:20:16 +01:00
|
|
|
user_id,
|
2025-08-11 07:01:08 +02:00
|
|
|
update_request
|
|
|
|
|
)
|
|
|
|
|
return progress
|
2025-12-25 20:20:16 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Update onboarding step error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to update onboarding step"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.get("/api/v1/auth/me/onboarding/next-step")
|
2025-08-11 07:01:08 +02:00
|
|
|
async def get_next_step(
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get next required step for user"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
onboarding_service = OnboardingService(db)
|
|
|
|
|
result = await onboarding_service.get_next_step(current_user["user_id"])
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Get next step error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get next step"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.get("/api/v1/auth/me/onboarding/can-access/{step_name}")
|
2025-08-11 07:01:08 +02:00
|
|
|
async def can_access_step(
|
|
|
|
|
step_name: str,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Check if user can access a specific step"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
onboarding_service = OnboardingService(db)
|
|
|
|
|
result = await onboarding_service.can_access_step(
|
|
|
|
|
current_user["user_id"],
|
|
|
|
|
step_name
|
|
|
|
|
)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Can access step error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to check step access"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-27 16:33:26 +01:00
|
|
|
@router.post("/api/v1/auth/me/onboarding/complete")
|
2025-08-11 07:01:08 +02:00
|
|
|
async def complete_onboarding(
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Complete entire onboarding process"""
|
2025-12-25 20:20:16 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
try:
|
2025-12-25 20:20:16 +01:00
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# DEMO FIX: Demo users don't need to complete onboarding - return success
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Demo user {user_id} attempting to complete onboarding - returning success (no-op)")
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "Onboarding already completed for demo account",
|
|
|
|
|
"optional_steps_skipped": [],
|
|
|
|
|
"demo_mode": True
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Regular user flow
|
2025-08-11 07:01:08 +02:00
|
|
|
onboarding_service = OnboardingService(db)
|
2025-12-25 20:20:16 +01:00
|
|
|
result = await onboarding_service.complete_onboarding(user_id)
|
2025-08-11 07:01:08 +02:00
|
|
|
return result
|
2025-12-25 20:20:16 +01:00
|
|
|
|
2025-08-11 07:01:08 +02:00
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Complete onboarding error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to complete onboarding"
|
2026-01-04 21:37:44 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.put("/api/v1/auth/me/onboarding/step-draft")
|
|
|
|
|
async def save_step_draft(
|
|
|
|
|
request: SaveStepDraftRequest,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Save in-progress step data without marking the step as complete.
|
|
|
|
|
This allows users to save their work and resume later.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# Demo users don't save drafts
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Demo user {user_id} attempting to save draft - returning success (no-op)")
|
|
|
|
|
return {"success": True, "demo_mode": True}
|
|
|
|
|
|
|
|
|
|
# Validate step name
|
|
|
|
|
if request.step_name not in ONBOARDING_STEPS:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=f"Invalid step name: {request.step_name}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Save the draft data
|
|
|
|
|
onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
await onboarding_repo.upsert_user_step(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
step_name=request.step_name,
|
|
|
|
|
completed=False,
|
|
|
|
|
step_data={**request.draft_data, "is_draft": True, "draft_saved_at": datetime.now(timezone.utc).isoformat()}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger.info(f"Saved draft for step {request.step_name} for user {user_id}")
|
|
|
|
|
return {"success": True, "step_name": request.step_name}
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Save step draft error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to save step draft"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.get("/api/v1/auth/me/onboarding/step-draft/{step_name}")
|
|
|
|
|
async def get_step_draft(
|
|
|
|
|
step_name: str,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Get saved draft data for a specific step.
|
|
|
|
|
Returns null if no draft exists or the step is already completed.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# Demo users don't have drafts
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
return {"step_name": step_name, "draft_data": None, "demo_mode": True}
|
|
|
|
|
|
|
|
|
|
# Validate step name
|
|
|
|
|
if step_name not in ONBOARDING_STEPS:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=f"Invalid step name: {step_name}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Get the step data
|
|
|
|
|
onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
step = await onboarding_repo.get_user_step(user_id, step_name)
|
|
|
|
|
|
|
|
|
|
# Return draft data only if step exists, is not completed, and has draft flag
|
|
|
|
|
if step and not step.completed and step.step_data and step.step_data.get("is_draft"):
|
|
|
|
|
return {
|
|
|
|
|
"step_name": step_name,
|
|
|
|
|
"draft_data": step.step_data,
|
|
|
|
|
"draft_saved_at": step.step_data.get("draft_saved_at")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {"step_name": step_name, "draft_data": None}
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Get step draft error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get step draft"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.delete("/api/v1/auth/me/onboarding/step-draft/{step_name}")
|
|
|
|
|
async def delete_step_draft(
|
|
|
|
|
step_name: str,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Delete saved draft data for a specific step.
|
|
|
|
|
Called after step is completed to clean up draft data.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# Demo users don't have drafts
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
return {"success": True, "demo_mode": True}
|
|
|
|
|
|
|
|
|
|
# Validate step name
|
|
|
|
|
if step_name not in ONBOARDING_STEPS:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=f"Invalid step name: {step_name}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Get the step data and remove draft flag
|
|
|
|
|
onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
step = await onboarding_repo.get_user_step(user_id, step_name)
|
|
|
|
|
|
|
|
|
|
if step and step.step_data and step.step_data.get("is_draft"):
|
|
|
|
|
# Remove draft-related fields but keep other data
|
|
|
|
|
updated_data = {k: v for k, v in step.step_data.items() if k not in ["is_draft", "draft_saved_at"]}
|
|
|
|
|
await onboarding_repo.upsert_user_step(
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
step_name=step_name,
|
|
|
|
|
completed=step.completed,
|
|
|
|
|
step_data=updated_data
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger.info(f"Deleted draft for step {step_name} for user {user_id}")
|
|
|
|
|
return {"success": True, "step_name": step_name}
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Delete step draft error: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to delete step draft"
|
2026-01-13 22:22:38 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.get("/api/v1/auth/me/onboarding/subscription-parameters", response_model=Dict[str, Any])
|
|
|
|
|
async def get_subscription_parameters(
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Get subscription parameters saved during onboarding for tenant creation
|
|
|
|
|
Returns all parameters needed for subscription processing: plan, billing cycle, coupon, etc.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
user_id = current_user["user_id"]
|
|
|
|
|
is_demo = current_user.get("is_demo", False)
|
|
|
|
|
|
|
|
|
|
# DEMO FIX: Demo users get default subscription parameters
|
|
|
|
|
if is_demo or user_id.startswith("demo-user-"):
|
|
|
|
|
logger.info(f"Demo user {user_id} requesting subscription parameters - returning demo defaults")
|
|
|
|
|
return {
|
|
|
|
|
"subscription_plan": "professional",
|
|
|
|
|
"billing_cycle": "monthly",
|
|
|
|
|
"coupon_code": "DEMO2025",
|
|
|
|
|
"payment_method_id": "pm_demo_test_123",
|
|
|
|
|
"payment_customer_id": "cus_demo_test_123", # Demo payment customer ID
|
|
|
|
|
"saved_at": datetime.now(timezone.utc).isoformat(),
|
|
|
|
|
"demo_mode": True
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Get subscription parameters from onboarding progress
|
|
|
|
|
from app.repositories.onboarding_repository import OnboardingRepository
|
|
|
|
|
onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
subscription_params = await onboarding_repo.get_subscription_parameters(user_id)
|
|
|
|
|
|
|
|
|
|
if not subscription_params:
|
|
|
|
|
logger.warning(f"No subscription parameters found for user {user_id} - returning defaults")
|
|
|
|
|
return {
|
|
|
|
|
"subscription_plan": "starter",
|
|
|
|
|
"billing_cycle": "monthly",
|
|
|
|
|
"coupon_code": None,
|
|
|
|
|
"payment_method_id": None,
|
|
|
|
|
"payment_customer_id": None,
|
|
|
|
|
"saved_at": datetime.now(timezone.utc).isoformat()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.info(f"Retrieved subscription parameters for user {user_id}",
|
|
|
|
|
subscription_plan=subscription_params["subscription_plan"],
|
|
|
|
|
billing_cycle=subscription_params["billing_cycle"],
|
|
|
|
|
coupon_code=subscription_params["coupon_code"])
|
|
|
|
|
|
|
|
|
|
return subscription_params
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error getting subscription parameters for user {current_user.get('user_id')}: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve subscription parameters"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.get("/api/v1/auth/users/{user_id}/onboarding/subscription-parameters", response_model=Dict[str, Any])
|
|
|
|
|
async def get_user_subscription_parameters(
|
|
|
|
|
user_id: str,
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
Get subscription parameters for a specific user (admin/service access)
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# Check permissions - only admins and services can access other users' data
|
|
|
|
|
requester_id = current_user["user_id"]
|
|
|
|
|
requester_roles = current_user.get("roles", [])
|
|
|
|
|
is_service = current_user.get("is_service", False)
|
|
|
|
|
|
|
|
|
|
if not is_service and "super_admin" not in requester_roles and requester_id != user_id:
|
|
|
|
|
logger.warning(f"Unauthorized access attempt to user {user_id} subscription parameters by {requester_id}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Insufficient permissions to access other users' subscription parameters"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Get subscription parameters from onboarding progress
|
|
|
|
|
from app.repositories.onboarding_repository import OnboardingRepository
|
|
|
|
|
onboarding_repo = OnboardingRepository(db)
|
|
|
|
|
subscription_params = await onboarding_repo.get_subscription_parameters(user_id)
|
|
|
|
|
|
|
|
|
|
if not subscription_params:
|
|
|
|
|
logger.warning(f"No subscription parameters found for user {user_id} - returning defaults")
|
|
|
|
|
return {
|
|
|
|
|
"subscription_plan": "starter",
|
|
|
|
|
"billing_cycle": "monthly",
|
|
|
|
|
"coupon_code": None,
|
|
|
|
|
"payment_method_id": None,
|
|
|
|
|
"payment_customer_id": None,
|
|
|
|
|
"saved_at": datetime.now(timezone.utc).isoformat()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.info(f"Retrieved subscription parameters for user {user_id} by {requester_id}",
|
|
|
|
|
subscription_plan=subscription_params["subscription_plan"])
|
|
|
|
|
|
|
|
|
|
return subscription_params
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error getting subscription parameters for user {user_id}: {e}")
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve subscription parameters"
|
2025-08-11 07:01:08 +02:00
|
|
|
)
|