""" Investor Demo April 2026 Creates a complete demo environment with two mandates, one user, and all feature instances needed for the investor live demo. Mandates: - HappyLife AG (happylife) — workspace, trustee(RMA), graphEditor, chatbot, neutralization - Alpina Treuhand AG (alpina) — workspace, trustee(RMA), graphEditor, neutralization User: - Patrick Helvetia (p.motsch@poweron.swiss) — SysAdmin, member of both mandates """ import json import logging import uuid from typing import Dict, Any, Optional, List from modules.demoConfigs._baseDemoConfig import _BaseDemoConfig logger = logging.getLogger(__name__) _DEMO_PREFIX = "demo-inv2026" _MANDATE_HAPPYLIFE = { "name": "happylife", "label": "HappyLife AG", } _MANDATE_ALPINA = { "name": "alpina-treuhand", "label": "Alpina Treuhand AG", } _USER = { "username": "patrick.helvetia", "email": "p.motsch@poweron.swiss", "fullName": "Patrick Helvetia", "password": "patrick.helvetia", "language": "en", } _FEATURES_HAPPYLIFE = ["workspace", "trustee", "graphicalEditor", "chatbot", "neutralization"] _FEATURES_ALPINA = ["workspace", "trustee", "graphicalEditor", "neutralization"] class InvestorDemo2026(_BaseDemoConfig): code = "investor-demo-2026" label = "Investor Demo April 2026" description = ( "Two mandates (HappyLife AG + Alpina Treuhand AG), one SysAdmin user, " "trustee with RMA, workspace, graph editor, chatbot, and neutralization." ) # ------------------------------------------------------------------ # load # ------------------------------------------------------------------ def load(self, db) -> Dict[str, Any]: summary: Dict[str, Any] = {"created": [], "skipped": [], "errors": []} try: mandateIdHappy = self._ensureMandate(db, _MANDATE_HAPPYLIFE, summary) mandateIdAlpina = self._ensureMandate(db, _MANDATE_ALPINA, summary) userId = self._ensureUser(db, summary) if mandateIdHappy: self._ensureMembership(db, userId, mandateIdHappy, _MANDATE_HAPPYLIFE["label"], summary) self._ensureFeatures(db, mandateIdHappy, _MANDATE_HAPPYLIFE["label"], _FEATURES_HAPPYLIFE, summary) if mandateIdAlpina: self._ensureMembership(db, userId, mandateIdAlpina, _MANDATE_ALPINA["label"], summary) self._ensureFeatures(db, mandateIdAlpina, _MANDATE_ALPINA["label"], _FEATURES_ALPINA, summary) self._ensureTrusteeRmaConfig(db, mandateIdHappy, _MANDATE_HAPPYLIFE["label"], summary) self._ensureTrusteeRmaConfig(db, mandateIdAlpina, _MANDATE_ALPINA["label"], summary) self._ensureNeutralizationConfig(db, mandateIdHappy, summary) self._ensureNeutralizationConfig(db, mandateIdAlpina, summary) self._ensureBilling(db, mandateIdHappy, _MANDATE_HAPPYLIFE["label"], summary) self._ensureBilling(db, mandateIdAlpina, _MANDATE_ALPINA["label"], summary) except Exception as e: logger.error(f"Demo load failed: {e}", exc_info=True) summary["errors"].append(str(e)) return summary # ------------------------------------------------------------------ # remove # ------------------------------------------------------------------ def remove(self, db) -> Dict[str, Any]: summary: Dict[str, Any] = {"removed": [], "errors": []} from modules.datamodels.datamodelUam import Mandate, UserInDB from modules.datamodels.datamodelMembership import UserMandate for mandateDef in [_MANDATE_HAPPYLIFE, _MANDATE_ALPINA]: try: existing = db.getRecordset(Mandate, recordFilter={"name": mandateDef["name"]}) for m in existing: mid = m.get("id") db.recordDelete(Mandate, mid) summary["removed"].append(f"Mandate {mandateDef['label']} ({mid})") logger.info(f"Removed mandate {mandateDef['label']} ({mid})") except Exception as e: summary["errors"].append(f"Remove mandate {mandateDef['label']}: {e}") try: existing = db.getRecordset(UserInDB, recordFilter={"username": _USER["username"]}) for u in existing: uid = u.get("id") memberships = db.getRecordset(UserMandate, recordFilter={"userId": uid}) for mem in memberships: try: db.recordDelete(UserMandate, mem.get("id")) except Exception: pass db.recordDelete(UserInDB, uid) summary["removed"].append(f"User {_USER['username']} ({uid})") logger.info(f"Removed user {_USER['username']} ({uid})") except Exception as e: summary["errors"].append(f"Remove user: {e}") self._removeLanguageSet(db, "es", summary) return summary # ------------------------------------------------------------------ # helpers # ------------------------------------------------------------------ def _ensureMandate(self, db, mandateDef: Dict, summary: Dict) -> Optional[str]: from modules.datamodels.datamodelUam import Mandate from modules.interfaces.interfaceBootstrap import copySystemRolesToMandate existing = db.getRecordset(Mandate, recordFilter={"name": mandateDef["name"]}) if existing: mid = existing[0].get("id") summary["skipped"].append(f"Mandate {mandateDef['label']} exists ({mid})") return mid mandate = Mandate(name=mandateDef["name"], label=mandateDef["label"], enabled=True) created = db.recordCreate(Mandate, mandate) mid = created.get("id") logger.info(f"Created mandate {mandateDef['label']} ({mid})") summary["created"].append(f"Mandate {mandateDef['label']}") copySystemRolesToMandate(db, mid) return mid def _ensureUser(self, db, summary: Dict) -> Optional[str]: from modules.datamodels.datamodelUam import UserInDB, AuthAuthority from passlib.context import CryptContext existing = db.getRecordset(UserInDB, recordFilter={"username": _USER["username"]}) if existing: uid = existing[0].get("id") summary["skipped"].append(f"User {_USER['username']} exists ({uid})") return uid pwdContext = CryptContext(schemes=["argon2"], deprecated="auto") user = UserInDB( username=_USER["username"], email=_USER["email"], fullName=_USER["fullName"], enabled=True, language=_USER["language"], isSysAdmin=True, authenticationAuthority=AuthAuthority.LOCAL, hashedPassword=pwdContext.hash(_USER["password"]), ) created = db.recordCreate(UserInDB, user) uid = created.get("id") logger.info(f"Created user {_USER['username']} ({uid})") summary["created"].append(f"User {_USER['fullName']}") return uid def _ensureMembership(self, db, userId: str, mandateId: str, mandateLabel: str, summary: Dict): from modules.datamodels.datamodelMembership import UserMandate, UserMandateRole, Role existing = db.getRecordset(UserMandate, recordFilter={"userId": userId, "mandateId": mandateId}) if existing: userMandateId = existing[0].get("id") summary["skipped"].append(f"Membership {_USER['username']} -> {mandateLabel} exists") else: um = UserMandate(userId=userId, mandateId=mandateId, enabled=True) created = db.recordCreate(UserMandate, um) userMandateId = created.get("id") summary["created"].append(f"Membership {_USER['username']} -> {mandateLabel}") logger.info(f"Created membership {_USER['username']} -> {mandateLabel}") adminRoles = db.getRecordset(Role, recordFilter={"mandateId": mandateId, "label": "admin"}) if adminRoles: adminRoleId = adminRoles[0].get("id") existingRole = db.getRecordset(UserMandateRole, recordFilter={"userMandateId": userMandateId, "roleId": adminRoleId}) if not existingRole: umr = UserMandateRole(userMandateId=userMandateId, roleId=adminRoleId) db.recordCreate(UserMandateRole, umr) logger.info(f"Assigned admin role in {mandateLabel}") def _ensureFeatures(self, db, mandateId: str, mandateLabel: str, featureCodes: List[str], summary: Dict): from modules.interfaces.interfaceFeatures import getFeatureInterface fi = getFeatureInterface(db) existingInstances = fi.getFeatureInstances(mandateId) existingCodes = { (inst.featureCode if hasattr(inst, "featureCode") else inst.get("featureCode", "")) for inst in existingInstances } for code in featureCodes: if code in existingCodes: summary["skipped"].append(f"Feature {code} in {mandateLabel} exists") continue try: fi.createFeatureInstance( featureCode=code, mandateId=mandateId, label=f"{code} ({mandateLabel})", enabled=True, copyTemplateRoles=True, ) summary["created"].append(f"Feature {code} in {mandateLabel}") logger.info(f"Created feature instance {code} in {mandateLabel}") except Exception as e: summary["errors"].append(f"Feature {code} in {mandateLabel}: {e}") logger.error(f"Failed to create feature {code} in {mandateLabel}: {e}") def _ensureTrusteeRmaConfig(self, db, mandateId: Optional[str], mandateLabel: str, summary: Dict): if not mandateId: return from modules.datamodels.datamodelFeatures import FeatureInstance from modules.features.trustee.datamodelFeatureTrustee import TrusteeAccountingConfig from modules.shared.configuration import APP_CONFIG, encryptValue instances = db.getRecordset(FeatureInstance, recordFilter={"mandateId": mandateId, "featureCode": "trustee"}) if not instances: summary["skipped"].append(f"No trustee instance in {mandateLabel} for RMA config") return instanceId = instances[0].get("id") existing = db.getRecordset(TrusteeAccountingConfig, recordFilter={"featureInstanceId": instanceId}) if existing: summary["skipped"].append(f"RMA config for {mandateLabel} exists") return apiBaseUrl = APP_CONFIG.get("Demo_RMA_ApiBaseUrl", "") clientName = APP_CONFIG.get("Demo_RMA_ClientName", "") apiKey = APP_CONFIG.get("Demo_RMA_ApiKey", "") if not apiBaseUrl or not apiKey: summary["errors"].append( f"RMA credentials missing in config.ini (Demo_RMA_ApiBaseUrl, Demo_RMA_ClientName, Demo_RMA_ApiKey) for {mandateLabel}" ) return plainConfig = { "apiBaseUrl": apiBaseUrl, "clientName": clientName, "apiKey": apiKey, } configRecord = { "id": str(uuid.uuid4()), "featureInstanceId": instanceId, "connectorType": "rma", "displayLabel": "Run My Accounts", "encryptedConfig": encryptValue(json.dumps(plainConfig), keyName="accountingConfig"), "isActive": True, "mandateId": mandateId, } db.recordCreate(TrusteeAccountingConfig, configRecord) summary["created"].append(f"RMA accounting config for {mandateLabel}") logger.info(f"Created RMA accounting config for {mandateLabel}") def _ensureNeutralizationConfig(self, db, mandateId: Optional[str], summary: Dict): if not mandateId: return from modules.datamodels.datamodelFeatures import FeatureInstance instances = db.getRecordset(FeatureInstance, recordFilter={"mandateId": mandateId, "featureCode": "neutralization"}) if not instances: return instanceId = instances[0].get("id") try: from modules.features.neutralization.datamodelFeatureNeutralizer import DataNeutraliserConfig existing = db.getRecordset(DataNeutraliserConfig, recordFilter={"featureInstanceId": instanceId}) if existing: summary["skipped"].append(f"Neutralization config for mandate {mandateId} exists") return config = DataNeutraliserConfig( featureInstanceId=instanceId, mandateId=mandateId, enabled=True, scope="featureInstance", ) db.recordCreate(DataNeutraliserConfig, config) summary["created"].append(f"Neutralization config for mandate {mandateId}") logger.info(f"Created neutralization config for mandate {mandateId}") except Exception as e: summary["errors"].append(f"Neutralization config: {e}") def _ensureBilling(self, db, mandateId: Optional[str], mandateLabel: str, summary: Dict): if not mandateId: return try: from modules.interfaces.interfaceDbBilling import _getRootInterface from modules.datamodels.datamodelBilling import BillingSettings billingInterface = _getRootInterface() existingSettings = billingInterface.getSettings(mandateId) if existingSettings: summary["skipped"].append(f"Billing for {mandateLabel} exists") return settings = BillingSettings( mandateId=mandateId, warningThresholdPercent=10.0, notifyOnWarning=True, ) billingInterface.db.recordCreate(BillingSettings, settings) summary["created"].append(f"Billing settings for {mandateLabel}") logger.info(f"Created billing settings for {mandateLabel}") except Exception as e: summary["errors"].append(f"Billing for {mandateLabel}: {e}") def _removeLanguageSet(self, db, code: str, summary: Dict): """Remove a language set if it was created during demo (e.g. 'es' from UC4).""" try: from modules.datamodels.datamodelUiLanguage import UiLanguageSet existing = db.getRecordset(UiLanguageSet, recordFilter={"id": code}) if existing: db.recordDelete(UiLanguageSet, code) summary["removed"].append(f"Language set '{code}'") logger.info(f"Removed language set '{code}'") except Exception as e: logger.debug(f"Could not remove language set '{code}': {e}")