from datetime import date, datetime from sqlalchemy import select from app.db.session import Base, SessionLocal, engine from app.models.assumption import FreightCostRule, PackagingCostRule, ProcessCostRule from app.models.client_access import ClientAccessAuditEvent, ClientAccount, ClientFeatureAccess, ClientUser, ClientUserModulePermission from app.models.mix import Mix, MixIngredient from app.models.product import Product from app.models.raw_material import RawMaterial, RawMaterialPriceVersion from app.services.client_access_service import MODULE_CATALOG, default_access_level_for_role def seed_client_access(db): existing = db.scalar(select(ClientAccount.id)) if existing is not None: return specialty = ClientAccount( tenant_id="hunter-premium-produce", name="Hunter Premium Produce", client_code="HPP", status="active", powerbi_workspace="hunter-premium-produce-prod", notes="Primary production client for the Lean 101 admin and access workflows", ) loft = ClientAccount( tenant_id="loft-grains", name="Loft Grains", client_code="LOFT", status="onboarding", powerbi_workspace="farm-ops-sandbox", notes="Onboarding workspace used to test staged user enablement", ) db.add_all([specialty, loft]) db.flush() specialty.users.extend( [ ClientUser( tenant_id=specialty.tenant_id, full_name="Amelia Hart", email="operator@example.com", role="superadmin", status="active", is_new_user=False, last_login_at=datetime(2026, 4, 24, 11, 30), ), ClientUser( tenant_id=specialty.tenant_id, full_name="Ethan Cole", email="ethan.cole@hunterpremiumproduce.example", role="operator", status="invited", is_new_user=True, ), ] ) loft.users.extend( [ ClientUser( tenant_id=loft.tenant_id, full_name="Ruby Singh", email="ruby.singh@loftgrains.example", role="viewer", status="active", is_new_user=False, last_login_at=datetime(2026, 4, 22, 9, 10), ) ] ) enabled_feature_map = { "hunter-premium-produce": {"dashboard", "raw_materials", "mix_master", "mix_calculator", "products", "scenarios", "powerbi_export", "client_access"}, "loft-grains": {"dashboard", "mix_calculator", "products", "powerbi_export"}, } for client in (specialty, loft): enabled_keys = enabled_feature_map[client.tenant_id] for feature_key, feature_name, feature_group, description in MODULE_CATALOG: client.features.append( ClientFeatureAccess( tenant_id=client.tenant_id, feature_key=feature_key, feature_name=feature_name, feature_group=feature_group, description=description, enabled=feature_key in enabled_keys, ) ) for user in client.users: for module_key, _, _, _ in MODULE_CATALOG: user.module_permissions.append( ClientUserModulePermission( tenant_id=client.tenant_id, client_account_id=client.id, module_key=module_key, access_level=default_access_level_for_role(user.role, module_key), ) ) specialty.audit_events.append( ClientAccessAuditEvent( tenant_id=specialty.tenant_id, actor_type="seed", actor_name="Lean 101 Seeder", actor_email="system@lean101.local", actor_role="system", action="client_access.seeded", target_type="client_account", target_id=specialty.id, module_key="client_access", summary="Initial client access controls, module permissions, and feature flags were seeded.", ) ) def seed_costing_workspace(db): existing = db.scalar(select(RawMaterial.id)) if existing is not None: return tenant_id = "hunter-premium-produce" maize = RawMaterial(tenant_id=tenant_id, name="Maize", supplier="Example Supplier", unit_of_measure="tonne", kg_per_unit=1000, status="active") barley = RawMaterial(tenant_id=tenant_id, name="Barley", supplier="Example Supplier", unit_of_measure="tonne", kg_per_unit=1000, status="active") acid_buf = RawMaterial(tenant_id=tenant_id, name="Acid Buf", supplier="Example Supplier", unit_of_measure="bag", kg_per_unit=25, status="active") maize.price_versions.append(RawMaterialPriceVersion(tenant_id=tenant_id, market_value=520, waste_percentage=0.02, effective_date=date(2026, 4, 1))) barley.price_versions.append(RawMaterialPriceVersion(tenant_id=tenant_id, market_value=470, waste_percentage=0.015, effective_date=date(2026, 4, 1))) acid_buf.price_versions.append(RawMaterialPriceVersion(tenant_id=tenant_id, market_value=39, waste_percentage=0.0, effective_date=date(2026, 4, 1))) db.add_all([maize, barley, acid_buf]) db.flush() db.add_all( [ ProcessCostRule(tenant_id=tenant_id, process_name="standard_bagging", grading_cost=0.055, bagging_cost=0.04, cracking_cost=0.0), ProcessCostRule(tenant_id=tenant_id, process_name="bulk_loadout", grading_cost=0.03, bagging_cost=0.0, cracking_cost=0.0), PackagingCostRule(tenant_id=tenant_id, sale_type="standard", unit_of_measure="20kg bag", own_bag=False, bag_cost=0.63), PackagingCostRule(tenant_id=tenant_id, sale_type="bulka", unit_of_measure="550kg bulka", own_bag=False, bag_cost=7.5), FreightCostRule(tenant_id=tenant_id, sale_type="standard", unit_of_measure="20kg bag", cost_per_unit=1.45), FreightCostRule(tenant_id=tenant_id, sale_type="bulka", unit_of_measure="550kg bulka", cost_per_unit=18.0), ] ) db.flush() mix = Mix(tenant_id=tenant_id, client_name="Hunter Premium Produce", name="Hunter Orchard Blend", status="active", version=1, notes="Seed recipe for MVP") db.add(mix) db.flush() db.add_all( [ MixIngredient(tenant_id=tenant_id, mix_id=mix.id, raw_material_id=maize.id, quantity_kg=180), MixIngredient(tenant_id=tenant_id, mix_id=mix.id, raw_material_id=barley.id, quantity_kg=95), MixIngredient(tenant_id=tenant_id, mix_id=mix.id, raw_material_id=acid_buf.id, quantity_kg=5), ] ) db.flush() db.add( Product( tenant_id=tenant_id, client_name="Hunter Premium Produce", item_id="SKU-001", name="Hunter Orchard Blend 20kg", mix_id=mix.id, sale_type="standard", own_bag=False, unit_of_measure="20kg bag", items_per_pallet=50, bagging_process="standard_bagging", distributor_margin=0.225, wholesale_margin=0.18, notes="Reference product for formula parity work", ) ) def seed_if_empty(): Base.metadata.create_all(bind=engine) with SessionLocal() as db: seed_costing_workspace(db) seed_client_access(db) db.commit() if __name__ == "__main__": seed_if_empty()