enhanced generic navigation tree

This commit is contained in:
patrick-motsch 2026-02-10 00:10:07 +01:00
parent d98c31a4d1
commit ab48e2e853
9 changed files with 44 additions and 15 deletions

View file

@ -28,7 +28,7 @@ class UserMandate(BaseModel):
)
mandateId: str = Field(
description="FK → Mandate.id (CASCADE DELETE)",
json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_fk_source": "/api/mandates/", "frontend_fk_display_field": "name"}
json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_fk_source": "/api/mandates/", "frontend_fk_display_field": "label"}
)
enabled: bool = Field(
default=True,

View file

@ -55,7 +55,7 @@ class Role(BaseModel):
mandateId: Optional[str] = Field(
default=None,
description="FK → Mandate.id (CASCADE DELETE). Null = Global/Template role.",
json_schema_extra={"frontend_type": "select", "frontend_readonly": True, "frontend_visible": True, "frontend_required": False, "frontend_fk_source": "/api/mandates/", "frontend_fk_display_field": "name"}
json_schema_extra={"frontend_type": "select", "frontend_readonly": True, "frontend_visible": True, "frontend_required": False, "frontend_fk_source": "/api/mandates/", "frontend_fk_display_field": "label"}
)
featureInstanceId: Optional[str] = Field(
default=None,

View file

@ -73,10 +73,10 @@ class Mandate(BaseModel):
description="Name of the mandate",
json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True}
)
description: Optional[str] = Field(
label: Optional[str] = Field(
default=None,
description="Description of the mandate",
json_schema_extra={"frontend_type": "textarea", "frontend_readonly": False, "frontend_required": False}
description="Display label of the mandate",
json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False}
)
enabled: bool = Field(
default=True,
@ -104,7 +104,7 @@ registerModelLabels(
{
"id": {"en": "ID", "de": "ID", "fr": "ID"},
"name": {"en": "Name", "de": "Name", "fr": "Nom"},
"description": {"en": "Description", "de": "Beschreibung", "fr": "Description"},
"label": {"en": "Label", "de": "Label", "fr": "Libellé"},
"enabled": {"en": "Enabled", "de": "Aktiviert", "fr": "Activé"},
"isSystem": {"en": "System Mandate", "de": "System-Mandant", "fr": "Mandat système"},
},

View file

@ -51,6 +51,9 @@ def initBootstrap(db: DatabaseConnector) -> None:
# Initialize root mandate
mandateId = initRootMandate(db)
# Migrate existing mandate records: description -> label
_migrateMandateDescriptionToLabel(db)
# Initialize system role TEMPLATES (mandateId=None, isSystemRole=True)
initRoles(db)
@ -276,6 +279,32 @@ def initRootMandate(db: DatabaseConnector) -> Optional[str]:
return mandateId
def _migrateMandateDescriptionToLabel(db: DatabaseConnector) -> None:
"""
Migration: Rename 'description' field to 'label' in all Mandate records.
Copies existing 'description' values to 'label' and removes the old field.
Safe to run multiple times (idempotent).
"""
allMandates = db.getRecordset(Mandate)
migratedCount = 0
for mandateRecord in allMandates:
mandateId = mandateRecord.get("id")
hasDescription = "description" in mandateRecord and mandateRecord.get("description") is not None
hasLabel = "label" in mandateRecord and mandateRecord.get("label") is not None
if hasDescription and not hasLabel:
# Copy description to label
updateData = {"label": mandateRecord["description"]}
db.recordModify(Mandate, mandateId, updateData)
migratedCount += 1
logger.info(f"Migrated mandate {mandateId}: description -> label")
if migratedCount > 0:
logger.info(f"Migrated {migratedCount} mandate(s) from description to label")
else:
logger.debug("No mandate description->label migration needed")
def initAdminUser(db: DatabaseConnector, mandateId: Optional[str]) -> Optional[str]:
"""
Creates the Admin user if it doesn't exist.

View file

@ -1444,7 +1444,7 @@ class AppObjects:
return Mandate(**filteredMandates[0])
def createMandate(self, name: str, description: str = None, enabled: bool = True) -> Mandate:
def createMandate(self, name: str, label: str = None, enabled: bool = True) -> Mandate:
"""
Creates a new mandate if user has permission.
Automatically copies system template roles (admin, user, viewer) to the new mandate.
@ -1453,7 +1453,7 @@ class AppObjects:
raise PermissionError("No permission to create mandates")
# Create mandate data using model
mandateData = Mandate(name=name, description=description, enabled=enabled)
mandateData = Mandate(name=name, label=label, enabled=enabled)
# Create mandate record
createdRecord = self.db.recordCreate(Mandate, mandateData)

View file

@ -355,7 +355,7 @@ def getBalanceForMandate(
from modules.interfaces.interfaceDbApp import getInterface as getAppInterface
appInterface = getAppInterface(ctx.user, mandateId=targetMandateId)
mandate = appInterface.getMandate(targetMandateId)
mandateName = mandate.get("name", "") if mandate else ""
mandateName = (mandate.get("label") or mandate.get("name", "")) if mandate else ""
return BillingBalanceResponse(
mandateId=targetMandateId,

View file

@ -192,7 +192,7 @@ def create_mandate(
)
# Get optional fields with defaults
description = mandateData.get('description')
label = mandateData.get('label')
enabled = mandateData.get('enabled', True)
appInterface = interfaceDbApp.getRootInterface()
@ -200,7 +200,7 @@ def create_mandate(
# Create mandate
newMandate = appInterface.createMandate(
name=name,
description=description,
label=label,
enabled=enabled
)

View file

@ -190,7 +190,7 @@ def create_invitation(
from modules.connectors.connectorMessagingEmail import ConnectorMessagingEmail
# Get mandate name for the email
mandate = rootInterface.getMandate(str(context.mandateId))
mandateName = mandate.name if mandate else "PowerOn"
mandateName = (mandate.label or mandate.name) if mandate else "PowerOn"
emailConnector = ConnectorMessagingEmail()
emailSubject = f"Einladung zu {mandateName}"
@ -249,7 +249,7 @@ def create_invitation(
# Get mandate name for notification
mandate = rootInterface.getMandate(str(context.mandateId))
mandateName = mandate.mandateLabel if mandate and mandate.mandateLabel else "PowerOn"
mandateName = (mandate.label or mandate.name) if mandate else "PowerOn"
inviterName = context.user.fullName or context.user.username
createInvitationNotification(
@ -529,7 +529,7 @@ def validate_invitation(
# Get mandate name
mandate = rootInterface.getMandate(str(mandateId)) if mandateId else None
if mandate:
mandateName = mandate.name
mandateName = mandate.label or mandate.name
# Get role names
roleIds = invitation.roleIds or []

View file

@ -153,7 +153,7 @@ def _buildDynamicBlock(
mandateId = str(instance.mandateId)
if mandateId not in mandatesMap:
mandate = rootInterface.getMandate(mandateId)
mandateName = mandate.name if mandate and hasattr(mandate, 'name') else mandateId
mandateName = (mandate.label or mandate.name) if mandate else mandateId
mandatesMap[mandateId] = {
"id": mandateId,
"uiLabel": mandateName,