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 ClientAccount, ClientFeatureAccess, ClientUser from app.models.mix import Mix, MixIngredient from app.models.product import Product from app.models.raw_material import RawMaterial, RawMaterialPriceVersion CLIENT_FEATURES = [ ("dashboard", "Dashboard", "workspace", "Top-level operational dashboard"), ("raw_materials", "Raw Materials", "costing", "Maintain live material costs and versions"), ("mix_master", "Mix Master", "costing", "Create and maintain mix worksheets"), ("products", "Products", "pricing", "Review finished product pricing"), ("scenarios", "Scenarios", "planning", "Run scenario overrides and comparisons"), ("powerbi_export", "Power BI Export", "reporting", "Expose client access data to BI consumers"), ] 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="admin", 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", "products", "scenarios", "powerbi_export"}, "loft-grains": {"dashboard", "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 CLIENT_FEATURES: 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, ) ) 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()