wiki/concepts/Plan-SystemFields-Architecture.md
ValueOn AG dc0f458f38 BREAKING CHANGE
API and persisted records use PowerOnModel system fields:
- sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy
Removed legacy JSON/DB field names:
- _createdAt, _createdBy, _modifiedAt, _modifiedBy
Frontend (frontend_nyla) and gateway call sites were updated accordingly.
Database:
- Bootstrap runs idempotent backfill (_migrateSystemFieldColumns) from old
  underscore columns and selected business duplicates into sys* where sys* IS NULL.
- Re-run app bootstrap against each PostgreSQL database after deploy.
- Optional: DROP INDEX IF EXISTS "idx_invitation_createdby" if an old index remains;
  new index: idx_invitation_syscreatedby on Invitation(sysCreatedBy).
Tests:
- RBAC integration tests aligned with current GROUP mandate filter and UserMandate-based
  UserConnection GROUP clause; buildRbacWhereClause(..., mandateId=...) must be passed
  explicitly (same as production request context).
2026-03-28 18:12:32 +01:00

23 KiB

Plan: System-Felder Architektur (inkl. Review-Findings)

Entscheidung: Echte Pydantic-Felder (KEIN extra='allow')

Pydantic v2 erlaubt keine _-prefixed Felder als regulaere model_fields. Deshalb: Felder OHNE Underscore-Prefix in einer Base-Class, DB-Spalten werden umbenannt.

Feldnamen: sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy

Vorteile gegenueber extra='allow':

  • Felder sind in model_fields (type-safe, sichtbar fuer getModelAttributeDefinitions)
  • model_dump() inkludiert sie natuerlich
  • Kein extra='allow' noetig - wird global entfernt
  • Kein Spezial-Code in attributeUtils noetig

Deploy-Reihenfolge (Boot-Sequenz)

  1. Deploy neuer Code
  2. Boot: _ensureTableExists fuegt neue sysCreatedAt etc. Spalten automatisch hinzu (da jetzt in model_fields)
  3. Boot: Bootstrap _migrateSystemFieldColumns kopiert Daten von alten Spalten in neue
  4. Applikation laeuft — referenziert nur noch sysCreatedAt etc.

Alte _createdAt-Spalten bleiben in DB bestehen (kein DROP COLUMN), werden aber ignoriert.


Ziel-Architektur (Datenfluss)

PostgreSQL (sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy)
    |
connectorDbPostgre.py (_saveRecord setzt die 4 Felder automatisch)
    |
PowerOnModel (echte Felder: sysCreatedAt etc., readonly, visible=false)
    |
Alle persistenten Models erben von PowerOnModel
    |
interfaceDbApp.py (KEIN _-Filter mehr, Konstruktor akzeptiert alle Felder)
    |
Route Endpoints (model_dump() inkludiert system fields)
    |
Frontend (API liefert sysCreatedAt etc., FormGenerator zeigt sie nur wenn visible=true)

Schritt 1: PowerOnModel Basisklasse erstellen

Neue Datei: gateway/modules/datamodels/datamodelBase.py

from typing import Optional
from pydantic import BaseModel, Field
from modules.shared.attributeUtils import registerModelLabels

class PowerOnModel(BaseModel):
    sysCreatedAt: Optional[float] = Field(
        default=None,
        description="Record creation timestamp (UTC, set by system)",
        json_schema_extra={
            "frontend_type": "timestamp",
            "frontend_readonly": True,
            "frontend_required": False,
            "frontend_visible": False,
            "system": True,
        }
    )
    sysCreatedBy: Optional[str] = Field(
        default=None,
        description="User ID who created this record (set by system)",
        json_schema_extra={
            "frontend_type": "text",
            "frontend_readonly": True,
            "frontend_required": False,
            "frontend_visible": False,
            "system": True,
        }
    )
    sysModifiedAt: Optional[float] = Field(
        default=None,
        description="Record last modification timestamp (UTC, set by system)",
        json_schema_extra={
            "frontend_type": "timestamp",
            "frontend_readonly": True,
            "frontend_required": False,
            "frontend_visible": False,
            "system": True,
        }
    )
    sysModifiedBy: Optional[str] = Field(
        default=None,
        description="User ID who last modified this record (set by system)",
        json_schema_extra={
            "frontend_type": "text",
            "frontend_readonly": True,
            "frontend_required": False,
            "frontend_visible": False,
            "system": True,
        }
    )

registerModelLabels(
    "PowerOnModel",
    {"en": "Base Record", "de": "Basisdatensatz"},
    {
        "sysCreatedAt": {"en": "Created At", "de": "Erstellt am", "fr": "Cree le"},
        "sysCreatedBy": {"en": "Created By", "de": "Erstellt von", "fr": "Cree par"},
        "sysModifiedAt": {"en": "Modified At", "de": "Geaendert am", "fr": "Modifie le"},
        "sysModifiedBy": {"en": "Modified By", "de": "Geaendert von", "fr": "Modifie par"},
    },
)

Label-Vererbung in attributeUtils.py (Review #6)

getModelAttributeDefinitions liest Labels anhand des Model-Namens (z.B. "Prompt"). Die Labels der Basis-Klasse "PowerOnModel" werden NICHT automatisch vererbt.

Fix: getModelAttributeDefinitions anpassen: wenn ein Label fuer ein Feld nicht im aktuellen Model gefunden wird, in der MRO (Method Resolution Order) der Elternklassen suchen. Pseudocode:

labels = getModelLabels(model_name, userLanguage)
if not labels.get(fieldName):
    for parent in modelClass.__mro__:
        parentLabels = getModelLabels(parent.__name__, userLanguage)
        if parentLabels.get(fieldName):
            labels[fieldName] = parentLabels[fieldName]
            break

Schritt 2: connectorDbPostgre.py anpassen

DB-Spaltennamen von _createdAt auf sysCreatedAt etc. aendern:

2a: _create_table_from_model (Zeile ~598)

Vorher: "_createdAt" DOUBLE PRECISION etc. hardcoded append Nachher: ENTFERNEN — Spalten kommen jetzt automatisch aus model_fields via _get_model_fields()

2b: _save_record columns-Liste (Zeile ~629)

Vorher: columns = [...] + ["_createdAt", "_createdBy", "_modifiedAt", "_modifiedBy"] Nachher: columns = ["id"] + [field for field in fields.keys() if field != "id"] (system fields sind jetzt in model_fields, kein Append noetig)

2c: KRITISCH — _save_record ON CONFLICT Klausel (Zeile ~689-694) (Review #2)

Vorher:

updates = ", ".join(
    [f'"{col}" = EXCLUDED."{col}"'
     for col in columns[1:]
     if col not in ["_createdAt", "_createdBy"]]
)

Nachher:

updates = ", ".join(
    [f'"{col}" = EXCLUDED."{col}"'
     for col in columns[1:]
     if col not in ["sysCreatedAt", "sysCreatedBy"]]
)

Zweck: bei UPSERT werden sysCreatedAt/sysCreatedBy NICHT ueberschrieben.

2d: _save_record Timestamp-String-Parsing (Zeile ~651) (Review #3)

Vorher: if col in ["_createdAt", "_modifiedAt"] and value is not None: Nachher: if col in ["sysCreatedAt", "sysModifiedAt"] and value is not None:

2e: KRITISCH — _saveRecord Metadata-Logik (Zeile ~746) (Review #11)

Vorher:

if "_createdAt" not in record:
    record["_createdAt"] = currentTime

Problem: da sysCreatedAt ein model_field mit default=None ist, ist es bei model_dump() IMMER im Dict (als None). "sysCreatedAt" not in record waere deshalb NIE True.

Nachher:

if not record.get("sysCreatedAt"):
    record["sysCreatedAt"] = currentTime
    if effective_user_id:
        record["sysCreatedBy"] = effective_user_id
elif not record.get("sysCreatedBy"):
    if effective_user_id:
        record["sysCreatedBy"] = effective_user_id
record["sysModifiedAt"] = currentTime
if effective_user_id:
    record["sysModifiedBy"] = effective_user_id

2f: _ensureTableExists (Zeile ~521)

Vorher: desired_columns |= {"_createdAt", "_modifiedAt", "_createdBy", "_modifiedBy"} Nachher: ENTFERNEN (kommen aus model_fields)

2g: _buildPaginationClauses (Zeile ~995)

Vorher: fields["_createdAt"] = "DOUBLE PRECISION" etc. Nachher: ENTFERNEN (kommen aus model_fields)

2h: getDistinctColumnValues (Zeile ~1193)

Vorher: fields["_createdAt"] = "DOUBLE PRECISION" etc. Nachher: ENTFERNEN (kommen aus model_fields)

2i: _system Tabelle (Review #7)

Die interne _system-Tabelle hat hardcoded _createdAt/_modifiedAt in CREATE TABLE (Zeile ~326-333, ~447-454). SystemTable erbt von BaseModel.

Empfehlung: SystemTable auf PowerOnModel umstellen, hardcoded _createdAt/_modifiedAt auf sysCreatedAt/sysModifiedAt aendern. Bootstrap-Migration muss auch _system beruecksichtigen.


Schritt 3: Alle persistenten Models auf PowerOnModel umstellen

Kern-Datamodels (gateway/modules/datamodels/):

  • datamodelUam.py: User, Mandate, UserConnection, UserVoicePreferences, UserInDB
  • datamodelRbac.py: Role, AccessRule
  • datamodelMembership.py: UserMandate, FeatureAccess, UserMandateRole, FeatureAccessRole
  • datamodelFeatures.py: Feature, FeatureInstance
  • datamodelUtils.py: Prompt (ENTFERNE extra='allow')
  • datamodelFiles.py: FileItem (ENTFERNE extra='allow'), FileData
  • datamodelFileFolder.py: FileFolder
  • datamodelInvitation.py: Invitation
  • datamodelFeatureDataSource.py: FeatureDataSource
  • datamodelDataSource.py: DataSource
  • datamodelMessaging.py: MessagingSubscription
  • datamodelBilling.py: BillingAccount, BillingTransaction
  • datamodelSubscription.py: MandateSubscription
  • datamodelNotification.py: UserNotification
  • datamodelSecurity.py: Token, AuthEvent
  • datamodelKnowledge.py: persistente Knowledge-Models
  • datamodelChat.py: persistente Chat-Models

Feature-Datamodels (gateway/modules/features/):

  • commcoach/datamodelCommcoach.py: CoachingContext, CoachingSession, CoachingMessage, CoachingTask, CoachingScore, CoachingUserProfile, CoachingPersona, CoachingBadge
  • workspace/datamodelFeatureWorkspace.py: WorkspaceUserSettings
  • neutralization/datamodelFeatureNeutralizer.py: DataNeutraliserConfig
  • automation2/datamodelFeatureAutomation2.py: Automation2Workflow, Automation2WorkflowRun, Automation2HumanTask
  • trustee/datamodelFeatureTrustee.py: TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition (ENTFERNE extra='allow'), TrusteeDataAccount, TrusteeDataJournalEntry, TrusteeDataJournalLine, TrusteeDataContact, TrusteeDataAccountBalance, TrusteeAccountingConfig, TrusteeAccountingSync
  • teamsbot/datamodelTeamsbot.py: TeamsbotSession, TeamsbotTranscript, TeamsbotBotResponse, TeamsbotSystemBot, TeamsbotUserAccount, TeamsbotUserSettings, TeamsbotConfig
  • automation/datamodelFeatureAutomation.py: AutomationDefinition, AutomationTemplate
  • realEstate/datamodelFeatureRealEstate.py: Land, Kanton, Gemeinde, Parzelle, Projekt, Dokument, Kontext

Connector-intern:

  • connectorDbPostgre.py: SystemTable (Review #7)

NICHT umstellen (reine DTOs):

  • PaginationParams, PaginatedResponse, AttributeDefinition, FilePreview, TextMultilingual
  • CreateContextRequest, UpdateContextRequest, etc. (Request/Response DTOs)
  • DataNeutralizerAttributes (DTO)
  • GeoPunkt, GeoPolylinie (embedded value objects)
  • Enums

Schritt 4: Business-Feld-Duplikate entfernen

Felder die durch die neuen System-Felder ersetzt werden:

datamodelFileFolder.py:

  • ENTFERNE: createdAt: float -> ersetzt durch sysCreatedAt

datamodelFiles.py:

  • ENTFERNE: creationDate: float -> ersetzt durch sysCreatedAt

datamodelInvitation.py:

  • ENTFERNE: createdAt: float -> ersetzt durch sysCreatedAt
  • ENTFERNE: createdBy: str -> ersetzt durch sysCreatedBy

datamodelFeatureDataSource.py:

  • ENTFERNE: createdAt: float -> ersetzt durch sysCreatedAt

datamodelDataSource.py (Review #5):

  • ENTFERNE: createdAt: float -> ersetzt durch sysCreatedAt

datamodelNotification.py (Review #5):

  • ENTFERNE: createdAt: float auf UserNotification -> ersetzt durch sysCreatedAt

datamodelSecurity.py (Review #5):

  • ENTFERNE: createdAt: Optional[float] auf Token -> ersetzt durch sysCreatedAt

datamodelMessaging.py:

  • ENTFERNE: createdBy: str -> ersetzt durch sysCreatedBy
  • ENTFERNE: modifiedBy: str -> ersetzt durch sysModifiedBy

commcoach/datamodelCommcoach.py:

  • ENTFERNE: createdAt auf 8 Models -> ersetzt durch sysCreatedAt
  • ENTFERNE: updatedAt auf mehreren Models -> ersetzt durch sysModifiedAt

teamsbot/datamodelTeamsbot.py:

  • ENTFERNE: creationDate auf 6 Models -> ersetzt durch sysCreatedAt
  • ENTFERNE: lastModified auf 4 Models -> ersetzt durch sysModifiedAt

Schritt 5: _-Filter und Passthrough-Logik bereinigen

5a: 44x _-Filter in interfaceDbApp.py entfernen

Vorher (44 Stellen):

cleanedRecord = {k: v for k, v in record.items() if not k.startswith("_")}
return UserMandate(**cleanedRecord)

Nachher:

return UserMandate(**record)

Da alle Models jetzt von PowerOnModel erben und die system fields als echte model_fields haben, akzeptiert der Konstruktor sie direkt.

5b: Muster B — User.model_validate statt Denylist (Review #1)

Die 2 Stellen mit Zusatzausschluss hashedPassword/resetToken/resetTokenExpires (Zeile ~652 und ~2560-2567) ersetzen durch:

user = User.model_validate(userRecord)
if not hasattr(user, 'roleLabels') or user.roleLabels is None:
    # roleLabels Defaulting wie bisher
    pass

Pydantic v2 extra='ignore' verwirft unbekannte Keys automatisch. Die Sicherheit steckt im Schema von User (Allowlist), nicht in einer parallelen Ausschlussliste (Denylist).

5c: startswith("_") Passthrough entfernen (Review #4)

In interfaceDbApp.py Zeile ~192 und interfaceDbManagement.py Zeile ~180:

if fieldName.startswith("_"):
    simpleFields[fieldName] = value

Diese Sonderbehandlung wird OBSOLET: sysCreatedAt ist ein regulaeres model_field und landet automatisch in simpleFields. Die startswith("_")- Branch kann ENTFERNT werden.

5d: Gleiche Bereinigung in:

  • gateway/modules/interfaces/interfaceFeatures.py (9 Stellen, nur Muster A)
  • gateway/modules/interfaces/interfaceDbManagement.py

Inventar Muster A (Ziel-Model pro Block, interfaceDbApp.py):

Ziel-Model Typische Funktionen / Bereich Zeilen (ca.)
User getUsers, Listen, einzelner User ~531, ~563, ~589, ~880, ~920, ~981, ~1044
Mandate Mandanten-Filter fuer API ~1332, ~1381
UserMandate Membership ~1797, ~1820, ~1872, ~2002, ~2596
UserMandateRole Junction ~2026, ~2123, ~2131, ~2617
FeatureAccess Feature-Zugriff ~2196, ~2219, ~2243, ~2292
Invitation Einladungen ~2430, ~2450, ~2471, ~2492, ~2513, ~2534
FeatureInstance Instanz laden / nach Mandant ~2637, ~2682
Feature getFeatureByCode ~2657
UserNotification Benachrichtigungen ~2706, ~2737
AccessRule Regeln laden ~2765, ~2786, ~3366
Role Rollen nach Instanz/Code/Mandant/alle ~2807, ~2832, ~3550, ~3571
Token Tokens nach Connection/User ~3031, ~3052

Sonderfall Token:

Zeile ~3006: Token(**tokens[0]) ohne vorherigen _-Filter — nach Umbenennung konsistent, aber an andere Token-Pfade angleichen.

Sonderfall Invitation + recordFilter:

getInvitationsByCreator nutzt recordFilter={"createdBy": creatorId} (~2489). Nach Entfernung des Business-Felds auf recordFilter={"sysCreatedBy": creatorId} umstellen.

Sonderfall UserNotification + Sortierung:

getNotificationsByUser sortiert mit x.createdAt (~2740). Nach Entfernung auf x.sysCreatedAt aendern.


Schritt 6: extra='allow' global entfernen

Entfernen aus:

  • datamodelUtils.py: Prompt (ConfigDict(extra='allow'))
  • datamodelFiles.py: FileItem (ConfigDict(extra='allow'))
  • features/trustee/datamodelFeatureTrustee.py: TrusteePosition (model_config={"extra":"allow"})

Keine model_config mit extra='allow' mehr in der gesamten Applikation.


Schritt 7: Bootstrap-Migration (interfaceBootstrap.py)

Neue Funktion _migrateSystemFieldColumns(db) in interfaceBootstrap.py, aufgerufen frueh in initBootstrap() (NACH initRootMandate, VOR allem anderen):

def _migrateSystemFieldColumns(db: DatabaseConnector) -> None:
    """Migrate old _createdAt/_createdBy/_modifiedAt/_modifiedBy columns
    to new sysCreatedAt/sysCreatedBy/sysModifiedAt/sysModifiedBy columns.
    Also migrates business field duplicates (createdAt, creationDate, etc.)
    Idempotent: safe to run on every boot."""

    COLUMN_RENAMES = {
        "_createdAt": "sysCreatedAt",
        "_createdBy": "sysCreatedBy",
        "_modifiedAt": "sysModifiedAt",
        "_modifiedBy": "sysModifiedBy",
    }

    BUSINESS_FIELD_MIGRATIONS = {
        "FileFolder": {"createdAt": "sysCreatedAt"},
        "FileItem": {"creationDate": "sysCreatedAt"},
        "Invitation": {"createdAt": "sysCreatedAt", "createdBy": "sysCreatedBy"},
        "FeatureDataSource": {"createdAt": "sysCreatedAt"},
        "DataSource": {"createdAt": "sysCreatedAt"},
        "UserNotification": {"createdAt": "sysCreatedAt"},
        "Token": {"createdAt": "sysCreatedAt"},
        "MessagingSubscription": {"createdBy": "sysCreatedBy", "modifiedBy": "sysModifiedBy"},
        "CoachingContext": {"createdAt": "sysCreatedAt"},
        "CoachingSession": {"createdAt": "sysCreatedAt", "updatedAt": "sysModifiedAt"},
        "CoachingMessage": {"createdAt": "sysCreatedAt"},
        "CoachingTask": {"createdAt": "sysCreatedAt", "updatedAt": "sysModifiedAt"},
        "CoachingScore": {"createdAt": "sysCreatedAt"},
        "CoachingUserProfile": {"createdAt": "sysCreatedAt", "updatedAt": "sysModifiedAt"},
        "CoachingPersona": {"createdAt": "sysCreatedAt", "updatedAt": "sysModifiedAt"},
        "CoachingBadge": {"createdAt": "sysCreatedAt"},
        "TeamsbotSession": {"creationDate": "sysCreatedAt", "lastModified": "sysModifiedAt"},
        "TeamsbotTranscript": {"creationDate": "sysCreatedAt"},
        "TeamsbotBotResponse": {"creationDate": "sysCreatedAt"},
        "TeamsbotSystemBot": {"creationDate": "sysCreatedAt", "lastModified": "sysModifiedAt"},
        "TeamsbotUserAccount": {"creationDate": "sysCreatedAt", "lastModified": "sysModifiedAt"},
        "TeamsbotUserSettings": {"creationDate": "sysCreatedAt", "lastModified": "sysModifiedAt"},
        "_system": {"_createdAt": "sysCreatedAt", "_modifiedAt": "sysModifiedAt"},
    }

    # Fuer jede Tabelle in der DB:
    # 1. Alte _-Spalten -> neue sys-Spalten kopieren (COLUMN_RENAMES)
    # 2. Business-Felder -> sys-Felder kopieren (BUSINESS_FIELD_MIGRATIONS)
    # 3. Nur wo sysX IS NULL (idempotent)
    # 4. Neue Spalten werden automatisch von _ensureTableExists erstellt

Schritt 8: Gateway Calling References anpassen

Alle Dateien, die _createdAt/_createdBy/_modifiedAt/_modifiedBy referenzieren:

Interfaces:

  • gateway/modules/interfaces/interfaceDbApp.py (44x _-Filter + direkte Referenzen)
  • gateway/modules/interfaces/interfaceDbManagement.py
  • gateway/modules/interfaces/interfaceBootstrap.py
  • gateway/modules/interfaces/interfaceRbac.py
  • gateway/modules/interfaces/interfaceDbChat.py
  • gateway/modules/interfaces/interfaceDbBilling.py

Routes:

  • gateway/modules/routes/routeDataFiles.py
  • gateway/modules/routes/routeBilling.py
  • gateway/modules/routes/routeAdminAutomationLogs.py
  • gateway/modules/routes/routeAdminRbacRules.py
  • gateway/modules/routes/routeAdminAutomationEvents.py
  • gateway/modules/routes/routeSecurityLocal.py
  • gateway/modules/routes/routeSecurityGoogle.py

Features:

  • gateway/modules/features/trustee/interfaceFeatureTrustee.py
  • gateway/modules/features/chatbot/interfaceFeatureChatbot.py
  • gateway/modules/features/automation2/routeFeatureAutomation2.py
  • gateway/modules/features/automation/routeFeatureAutomation.py
  • gateway/modules/features/automation/interfaceFeatureAutomation.py
  • gateway/modules/features/workspace/mainWorkspace.py
  • gateway/modules/features/teamsbot/routeFeatureTeamsbot.py
  • gateway/modules/features/teamsbot/interfaceFeatureTeamsbot.py

Sonstige Gateway:

  • gateway/modules/connectors/connectorDbPostgre.py
  • gateway/modules/auth/tokenManager.py
  • gateway/modules/serviceCenter/services/serviceChat/mainServiceChat.py
  • gateway/modules/serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py
  • gateway/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
  • gateway/modules/serviceCenter/services/serviceGeneration/mainServiceGeneration.py
  • gateway/modules/workflows/automation/mainWorkflow.py
  • gateway/modules/security/rbac.py
  • gateway/modules/shared/gdprDeletion.py
  • gateway/modules/migration/migrateRootUsers.py
  • gateway/scripts/script_db_export_migration.py
  • gateway/tests/integration/rbac/test_rbac_database.py

Business-Feld Referenzen (creationDate, .createdAt, .createdBy, etc.):

  • gateway/modules/features/teamsbot/datamodelTeamsbot.py
  • gateway/modules/features/teamsbot/interfaceFeatureTeamsbot.py
  • gateway/modules/features/teamsbot/routeFeatureTeamsbot.py
  • gateway/modules/features/commcoach/datamodelCommcoach.py
  • gateway/modules/datamodels/datamodelFiles.py
  • gateway/modules/datamodels/datamodelMessaging.py
  • gateway/modules/datamodels/datamodelDataSource.py
  • gateway/modules/datamodels/datamodelNotification.py
  • gateway/modules/datamodels/datamodelSecurity.py
  • gateway/modules/interfaces/interfaceDbManagement.py
  • gateway/modules/routes/routeSecurityLocal.py
  • gateway/modules/routes/routeSecurityGoogle.py
  • gateway/modules/serviceCenter/services/serviceChat/mainServiceChat.py
  • gateway/modules/serviceCenter/services/serviceGeneration/mainServiceGeneration.py
  • gateway/modules/auth/tokenManager.py

Schritt 9: Frontend Calling References anpassen

Alle Dateien die _createdAt/_createdBy/_modifiedAt/_modifiedBy oder Business-Duplikate referenzieren:

API Types:

  • frontend_nyla/src/api/automationApi.ts _createdAt, _createdBy, _createdByUserName -> sysCreatedAt, sysCreatedBy
  • frontend_nyla/src/api/automation2Api.ts createdAt Kommentar -> sysCreatedAt

Type Definitions:

  • frontend_nyla/src/types/mandate.ts record._createdBy -> record.sysCreatedBy (RBAC owner check)

Hooks:

  • frontend_nyla/src/hooks/useAutomations.ts Omit<..., '_createdAt' | '_createdBy'> -> Omit<..., 'sysCreatedAt' | 'sysCreatedBy'>

Pages (excludedFields/hiddenColumns Listen):

  • frontend_nyla/src/pages/basedata/PromptsPage.tsx
  • frontend_nyla/src/pages/basedata/FilesPage.tsx
  • frontend_nyla/src/pages/basedata/ConnectionsPage.tsx
  • frontend_nyla/src/pages/views/trustee/TrusteePositionsView.tsx
  • frontend_nyla/src/pages/views/trustee/TrusteePositionDocumentsView.tsx
  • frontend_nyla/src/pages/views/trustee/TrusteeDocumentsView.tsx
  • frontend_nyla/src/pages/views/realestate/RealEstateProjectsView.tsx
  • frontend_nyla/src/pages/views/realestate/RealEstateParcelsView.tsx
  • frontend_nyla/src/pages/views/automation/AutomationDefinitionsView.tsx
  • frontend_nyla/src/pages/views/automation/AutomationTemplatesView.tsx
  • frontend_nyla/src/pages/views/workspace/NeutralizationPanel.tsx m.createdAt || m._createdAt -> m.sysCreatedAt

Hinweis zu den excludedFields-Listen:

Da die System-Felder jetzt mit visible=false in den Attributen kommen, werden viele dieser manuellen Ausschluss-Listen OBSOLET. Der FormGenerator filtert sie automatisch raus. Die Listen koennen vereinfacht werden.


Zusammenfassung

Bereich Dateien Aenderungsart
Neue Datei datamodelBase.py 1 neue Datei (PowerOnModel + Labels)
Connector connectorDbPostgre.py ~15 Stellen: Spaltennamen + Logik-Fix
attributeUtils attributeUtils.py Label-Vererbung via MRO
Kern-Datamodels ~17 Dateien BaseModel -> PowerOnModel
Feature-Datamodels 8 Dateien BaseModel -> PowerOnModel
Business-Felder ~13 Models Felder entfernen
extra='allow' 3 Models ConfigDict entfernen
_-Filter interfaceDbApp.py + 2 weitere ~50 Stellen vereinfachen
Muster B interfaceDbApp.py (2 Stellen) Denylist -> User.model_validate
Passthrough interfaceDbApp.py + interfaceDbManagement.py startswith("_") entfernen
Gateway Referenzen ~30 Dateien _createdAt -> sysCreatedAt
Frontend Referenzen ~13 Dateien _createdAt -> sysCreatedAt
Bootstrap interfaceBootstrap.py Migration-Funktion
registerModelLabels ~10 Dateien Labels fuer entfernte Felder bereinigen
_system Tabelle connectorDbPostgre.py Hardcoded Spalten umbenennen