""" Startup seeder for tenant service. Seeds initial data (like pilot coupons) on service startup. All operations are idempotent - safe to run multiple times. """ import os import uuid from datetime import datetime, timedelta, timezone from typing import Optional import structlog from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.coupon import CouponModel logger = structlog.get_logger() async def ensure_pilot_coupon(session: AsyncSession) -> Optional[CouponModel]: """ Ensure the PILOT2025 coupon exists in the database. This coupon provides 3 months (90 days) free trial extension for the first 20 pilot customers. This function is idempotent - it will not create duplicates. Args: session: Database session Returns: The coupon model (existing or newly created), or None if disabled """ # Check if pilot mode is enabled via environment variable pilot_mode_enabled = os.getenv("VITE_PILOT_MODE_ENABLED", "true").lower() == "true" if not pilot_mode_enabled: logger.info("Pilot mode is disabled, skipping coupon seeding") return None coupon_code = os.getenv("VITE_PILOT_COUPON_CODE", "PILOT2025") trial_months = int(os.getenv("VITE_PILOT_TRIAL_MONTHS", "3")) max_redemptions = int(os.getenv("PILOT_MAX_REDEMPTIONS", "20")) # Check if coupon already exists result = await session.execute( select(CouponModel).where(CouponModel.code == coupon_code) ) existing_coupon = result.scalars().first() if existing_coupon: logger.info( "Pilot coupon already exists", code=coupon_code, current_redemptions=existing_coupon.current_redemptions, max_redemptions=existing_coupon.max_redemptions, active=existing_coupon.active ) return existing_coupon # Create new coupon now = datetime.now(timezone.utc) valid_until = now + timedelta(days=180) # Valid for 6 months trial_days = trial_months * 30 # Approximate days coupon = CouponModel( id=uuid.uuid4(), code=coupon_code, discount_type="trial_extension", discount_value=trial_days, max_redemptions=max_redemptions, current_redemptions=0, valid_from=now, valid_until=valid_until, active=True, created_at=now, extra_data={ "program": "pilot_launch_2025", "description": f"Programa piloto - {trial_months} meses gratis para los primeros {max_redemptions} clientes", "terms": "Válido para nuevos registros únicamente. Un cupón por cliente." } ) session.add(coupon) await session.commit() await session.refresh(coupon) logger.info( "Pilot coupon created successfully", code=coupon_code, type="Trial Extension", value=f"{trial_days} days ({trial_months} months)", max_redemptions=max_redemptions, valid_until=valid_until.isoformat(), id=str(coupon.id) ) return coupon async def run_startup_seeders(database_manager) -> None: """ Run all startup seeders. This function is called during service startup to ensure required seed data exists in the database. Args: database_manager: The database manager instance """ logger.info("Running startup seeders...") try: async with database_manager.get_session() as session: # Seed pilot coupon await ensure_pilot_coupon(session) logger.info("Startup seeders completed successfully") except Exception as e: # Log but don't fail startup - seed data is not critical logger.warning( "Startup seeder encountered an error (non-fatal)", error=str(e) )