# Copyright (c) 2025 Patrick Motsch # All rights reserved. """Trustee models: TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition.""" from enum import Enum from typing import Optional, Dict, Any from pydantic import BaseModel, Field from modules.datamodels.datamodelBase import PowerOnModel from modules.shared.i18nRegistry import i18nModel import uuid @i18nModel("Organisation") class TrusteeOrganisation(PowerOnModel): """Represents trustee organisations (companies) within the Trustee feature.""" id: str = Field( # Unique string label (PK), not UUID description="Unique organisation identifier (label)", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": False, # Editable at creation, then readonly "frontend_required": True } ) label: str = Field( description="Company name", json_schema_extra={ "label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True } ) enabled: bool = Field( default=True, description="Whether the organisation is enabled", json_schema_extra={ "label": "Aktiviert", "frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID (system-level organisation)", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) # System attributes are automatically set by DatabaseConnector: # sysCreatedAt, sysModifiedAt, sysCreatedBy, sysModifiedBy (PowerOnModel) @i18nModel("Rolle") class TrusteeRole(PowerOnModel): """Defines roles within the Trustee feature.""" id: str = Field( # Unique string label (PK), not UUID description="Unique role identifier (label)", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True } ) desc: str = Field( description="Role description", json_schema_extra={ "label": "Beschreibung", "frontend_type": "textarea", "frontend_readonly": False, "frontend_required": True } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) # System attributes are automatically set by DatabaseConnector @i18nModel("Zugriff") class TrusteeAccess(PowerOnModel): """Defines user access to organisations with specific roles.""" id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Unique access ID", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) organisationId: str = Field( description="Reference to TrusteeOrganisation.id", json_schema_extra={ "label": "Organisation", "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": "/api/trustee/{instanceId}/organisations/options", "fk_target": {"db": "poweron_trustee", "table": "TrusteeOrganisation", "labelField": "label"}, } ) roleId: str = Field( description="Reference to TrusteeRole.id", json_schema_extra={ "label": "Rolle", "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": "/api/trustee/{instanceId}/roles/options", "fk_target": {"db": "poweron_trustee", "table": "TrusteeRole", "labelField": "desc"}, } ) userId: str = Field( description="User ID assigned to this role", json_schema_extra={ "label": "Benutzer", "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": "/api/users/options", "fk_target": {"db": "poweron_app", "table": "UserInDB", "labelField": "username"}, } ) contractId: Optional[str] = Field( default=None, description="Optional reference to TrusteeContract.id. If None, access is for full organisation. If set, access is limited to this specific contract.", json_schema_extra={ "label": "Vertrag (optional)", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": "/api/trustee/{instanceId}/contracts/options", "frontend_depends_on": "organisationId", "fk_target": {"db": "poweron_trustee", "table": "TrusteeContract", "labelField": "label"}, } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) # System attributes are automatically set by DatabaseConnector @i18nModel("Vertrag") class TrusteeContract(PowerOnModel): """Defines customer contracts within organisations.""" id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Unique contract ID", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) organisationId: str = Field( description="Reference to TrusteeOrganisation.id (immutable after creation)", json_schema_extra={ "label": "Organisation", "frontend_type": "select", "frontend_readonly": False, # Editable at creation, then readonly "frontend_required": True, "frontend_options": "/api/trustee/{instanceId}/organisations/options", "fk_target": {"db": "poweron_trustee", "table": "TrusteeOrganisation", "labelField": "label"}, } ) label: str = Field( description="Label for the customer contract (e.g., 'Muster AG 2026')", json_schema_extra={ "label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True } ) enabled: bool = Field( default=True, description="Whether the contract is enabled", json_schema_extra={ "label": "Aktiviert", "frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) # System attributes are automatically set by DatabaseConnector class TrusteeDocumentTypeEnum(str, Enum): """Document type for trustee documents (expense extraction, ingest, sync).""" INVOICE = "invoice" EXPENSE_RECEIPT = "expense_receipt" BANK_DOCUMENT = "bank_document" CONTRACT = "contract" UNKNOWN = "unknown" AUTO = "auto" @i18nModel("Dokument") class TrusteeDocument(PowerOnModel): """Contains document references for bookings. Documents reference files in the central Files table via fileId. This allows file content to be stored once and referenced by multiple features. Note: organisationId and contractId removed as per architecture decision: - The feature instance IS the organisation - Contracts are eliminated from the model """ sysCreatedAt: Optional[float] = Field( default=None, description="Record creation timestamp (UTC, set by system)", json_schema_extra={ "label": "Erstellt am", "frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False, "frontend_visible": True, "system": True, }, ) id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Unique document ID", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) fileId: Optional[str] = Field( default=None, description="Reference to central Files table (Files.id)", json_schema_extra={ "label": "Datei-Referenz", "frontend_type": "file_reference", "frontend_readonly": False, "frontend_required": False, "fk_target": {"db": "poweron_management", "table": "FileItem", "labelField": "fileName"}, } ) documentName: str = Field( description="File name (e.g., 'Beleg.pdf')", json_schema_extra={ "label": "Dokumentname", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True } ) documentMimeType: str = Field( default="application/octet-stream", description="MIME type of the document", json_schema_extra={ "label": "MIME-Typ", "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": "/api/trustee/mime-types/options" } ) sourceType: Optional[str] = Field( default=None, description="Source type (e.g., 'sharepoint', 'upload', 'email')", json_schema_extra={ "label": "Quelltyp", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) sourceLocation: Optional[str] = Field( default=None, description="Original source location (e.g., SharePoint path)", json_schema_extra={ "label": "Quellort", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID (auto-set from context)", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation (auto-set from context)", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) documentType: Optional[str] = Field( default=None, description="Document type (e.g. invoice, expense_receipt, bank_document, contract); use TrusteeDocumentTypeEnum values", json_schema_extra={ "label": "Dokumenttyp", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) externalBelegId: Optional[str] = Field( default=None, description="External Beleg-ID in accounting system (e.g. RMA); set on first successful upload, reused on re-sync", json_schema_extra={ "label": "Beleg-ID (Buchhaltung)", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) # System attributes are automatically set by DatabaseConnector @i18nModel("Position") class TrusteePosition(PowerOnModel): """Contains booking positions (expense entries). A position can have up to two document references: documentId (Beleg) and bankDocumentId (Bank-Referenz). One document (e.g. bank statement) can generate many positions. """ sysCreatedAt: Optional[float] = Field( default=None, description="Record creation timestamp (UTC, set by system)", json_schema_extra={ "label": "Erstellt am", "frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False, "frontend_visible": True, "system": True, }, ) id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Unique position ID", json_schema_extra={ "label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False } ) documentId: Optional[str] = Field( default=None, description="Reference to TrusteeDocument.id (Beleg / primary document)", json_schema_extra={ "label": "Dokument", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": "/api/trustee/{instanceId}/documents/options", "fk_target": {"db": "poweron_trustee", "table": "TrusteeDocument", "labelField": "documentName"}, } ) bankDocumentId: Optional[str] = Field( default=None, description="Reference to TrusteeDocument.id (Bank-Referenz / second document)", json_schema_extra={ "label": "Bank-Referenz", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": "/api/trustee/{instanceId}/documents/options", "fk_target": {"db": "poweron_trustee", "table": "TrusteeDocument", "labelField": "documentName"}, } ) valuta: Optional[float] = Field( default=None, description="Value date (UTC midnight unix timestamp)", json_schema_extra={ "label": "Valutadatum", "frontend_type": "date", "frontend_readonly": False, "frontend_required": True } ) transactionDateTime: Optional[float] = Field( default=None, description="Transaction timestamp (UTC timestamp in seconds)", json_schema_extra={ "label": "Transaktionszeitpunkt", "frontend_type": "timestamp", "frontend_readonly": False, "frontend_required": True } ) company: str = Field( default="", description="Company name", json_schema_extra={ "label": "Firma", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) desc: str = Field( default="", description="Description", json_schema_extra={ "label": "Beschreibung", "frontend_type": "textarea", "frontend_readonly": False, "frontend_required": False } ) tags: str = Field( default="", description="Tags (comma-separated)", json_schema_extra={ "label": "Tags", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) bookingCurrency: str = Field( default="CHF", description="Booking currency code", json_schema_extra={ "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": [ {"value": "CHF", "label": "CHF"}, {"value": "EUR", "label": "EUR"}, {"value": "USD", "label": "USD"}, {"value": "GBP", "label": "GBP"}, ] } ) bookingAmount: float = Field( default=0.0, description="Booking amount", json_schema_extra={ "label": "Buchungsbetrag", "frontend_type": "number", "frontend_readonly": False, "frontend_required": True, "frontend_format": "R:#'###.00", } ) originalCurrency: str = Field( default="CHF", description="Original currency code", json_schema_extra={ "frontend_type": "select", "frontend_readonly": False, "frontend_required": True, "frontend_options": [ {"value": "CHF", "label": "CHF"}, {"value": "EUR", "label": "EUR"}, {"value": "USD", "label": "USD"}, {"value": "GBP", "label": "GBP"}, ] } ) originalAmount: float = Field( default=0.0, description="Original amount (manual input, no automatic currency conversion)", json_schema_extra={ "label": "Originalbetrag", "frontend_type": "number", "frontend_readonly": False, "frontend_required": True, "frontend_format": "R:#'###.00", } ) vatPercentage: float = Field( default=0.0, description="VAT percentage", json_schema_extra={ "label": "MwSt-Prozentsatz", "frontend_type": "number", "frontend_readonly": False, "frontend_required": False, "frontend_format": "R:0.00", } ) vatAmount: float = Field( default=0.0, description="VAT amount (calculated: bookingAmount * vatPercentage / 100, can be manually overridden)", json_schema_extra={ "label": "MwSt-Betrag", "frontend_type": "number", "frontend_readonly": False, "frontend_required": False, "frontend_format": "R:#'###.00", } ) debitAccountNumber: Optional[str] = Field( default=None, description="Debit account number (e.g. '4200' for expenses)", json_schema_extra={ "label": "Soll-Konto", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) creditAccountNumber: Optional[str] = Field( default=None, description="Credit account number (e.g. '1020' for bank)", json_schema_extra={ "label": "Haben-Konto", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) taxCode: Optional[str] = Field( default=None, description="Tax code for the accounting system", json_schema_extra={ "label": "Steuercode", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) costCenter: Optional[str] = Field( default=None, description="Cost center identifier", json_schema_extra={ "label": "Kostenstelle", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) bookingReference: Optional[str] = Field( default=None, description="Booking reference (e.g. voucher number)", json_schema_extra={ "label": "Buchungsreferenz", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) documentType: Optional[str] = Field( default=None, description="Document type that generated this position (invoice, expense_receipt, bank_document, contract, unknown)", json_schema_extra={ "frontend_type": "select", "frontend_readonly": True, "frontend_required": False, "frontend_options": [ {"value": "invoice", "label": "Rechnung"}, {"value": "expense_receipt", "label": "Beleg"}, {"value": "bank_document", "label": "Bankauszug"}, {"value": "contract", "label": "Vertrag"}, {"value": "unknown", "label": "Sonstige"}, ] } ) payeeIban: Optional[str] = Field( default=None, description="IBAN of the payment recipient (from invoice / QR code)", json_schema_extra={ "label": "Empfänger-IBAN", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) payeeName: Optional[str] = Field( default=None, description="Bank or account holder name of the payment recipient", json_schema_extra={ "label": "Empfänger-Name", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) payeeBic: Optional[str] = Field( default=None, description="BIC / SWIFT code of the recipient bank", json_schema_extra={ "label": "Empfänger-BIC", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) paymentReference: Optional[str] = Field( default=None, description="Structured payment reference (QR-Referenz, ESR, SCOR, Mitteilung)", json_schema_extra={ "label": "Zahlungsreferenz", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False } ) dueDate: Optional[float] = Field( default=None, description="Payment due date (UTC midnight unix timestamp)", json_schema_extra={ "label": "Fälligkeitsdatum", "frontend_type": "date", "frontend_readonly": False, "frontend_required": False } ) mandateId: Optional[str] = Field( default=None, description="Mandate ID (auto-set from context)", json_schema_extra={ "label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) featureInstanceId: Optional[str] = Field( default=None, description="Feature Instance ID for instance-level isolation (auto-set from context)", json_schema_extra={ "label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}, "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) accountingSyncId: Optional[str] = Field( default=None, description="External ID (UUID) of the synced record in the accounting system; set by sync, used for duplicate check", json_schema_extra={ "label": "Buha-Sync-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_hidden": True } ) # ── TrusteeData* tables (synced from external accounting apps for analysis) ── @i18nModel("Konto (Sync)") class TrusteeDataAccount(PowerOnModel): """Chart of accounts synced from external accounting system.""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) accountNumber: str = Field(description="Account number (e.g. '1020')", json_schema_extra={"label": "Kontonummer"}) label: str = Field(default="", description="Account name", json_schema_extra={"label": "Bezeichnung"}) accountType: Optional[str] = Field(default=None, description="asset / liability / equity / revenue / expense", json_schema_extra={"label": "Typ"}) accountGroup: Optional[str] = Field(default=None, description="Account group/category", json_schema_extra={"label": "Gruppe"}) currency: str = Field(default="CHF", description="Account currency", json_schema_extra={"label": "Währung"}) isActive: bool = Field(default=True, json_schema_extra={"label": "Aktiv"}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) @i18nModel("Buchung (Sync)") class TrusteeDataJournalEntry(PowerOnModel): """Journal entry header synced from external accounting system.""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) externalId: Optional[str] = Field(default=None, description="ID in the source system", json_schema_extra={"label": "Externe ID"}) bookingDate: Optional[float] = Field(default=None, description="Booking date (UTC unix timestamp)", json_schema_extra={"label": "Datum", "frontend_type": "timestamp"}) reference: Optional[str] = Field(default=None, description="Booking reference / voucher number", json_schema_extra={"label": "Referenz"}) description: str = Field(default="", description="Booking text", json_schema_extra={"label": "Beschreibung"}) currency: str = Field(default="CHF", json_schema_extra={"label": "Währung"}) totalAmount: float = Field( default=0.0, description="Total amount of entry", json_schema_extra={ "label": "Betrag", # Right-aligned amount with Swiss thousands separator and 2 decimals. "frontend_format": "R:#'###.00", }, ) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) @i18nModel("Buchungszeile (Sync)") class TrusteeDataJournalLine(PowerOnModel): """Journal entry line (debit/credit) synced from external accounting system.""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) journalEntryId: str = Field(description="FK → TrusteeDataJournalEntry.id", json_schema_extra={"label": "Buchung", "fk_target": {"db": "poweron_trustee", "table": "TrusteeDataJournalEntry", "labelField": "reference"}}) accountNumber: str = Field(description="Account number", json_schema_extra={"label": "Konto"}) debitAmount: float = Field(default=0.0, json_schema_extra={"label": "Soll", "frontend_format": "R:#'###.00"}) creditAmount: float = Field(default=0.0, json_schema_extra={"label": "Haben", "frontend_format": "R:#'###.00"}) currency: str = Field(default="CHF", json_schema_extra={"label": "Währung"}) taxCode: Optional[str] = Field(default=None, json_schema_extra={"label": "Steuercode"}) costCenter: Optional[str] = Field(default=None, json_schema_extra={"label": "Kostenstelle"}) description: str = Field(default="", json_schema_extra={"label": "Beschreibung"}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) @i18nModel("Kontakt (Sync)") class TrusteeDataContact(PowerOnModel): """Customer or vendor synced from external accounting system.""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) externalId: Optional[str] = Field(default=None, description="ID in the source system", json_schema_extra={"label": "Externe ID"}) contactType: str = Field(default="customer", description="customer / vendor / both", json_schema_extra={"label": "Typ"}) contactNumber: Optional[str] = Field(default=None, description="Customer/vendor number", json_schema_extra={"label": "Nummer"}) name: str = Field(default="", description="Name / company", json_schema_extra={"label": "Name"}) address: Optional[str] = Field(default=None, json_schema_extra={"label": "Adresse"}) zip: Optional[str] = Field(default=None, json_schema_extra={"label": "PLZ"}) city: Optional[str] = Field(default=None, json_schema_extra={"label": "Ort"}) country: Optional[str] = Field(default=None, json_schema_extra={"label": "Land"}) email: Optional[str] = Field(default=None, json_schema_extra={"label": "E-Mail"}) phone: Optional[str] = Field(default=None, json_schema_extra={"label": "Telefon"}) vatNumber: Optional[str] = Field(default=None, json_schema_extra={"label": "MWST-Nr."}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) @i18nModel("Kontosaldo (Sync)") class TrusteeDataAccountBalance(PowerOnModel): """Account balance per period, derived from journal lines or directly from accounting system.""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) accountNumber: str = Field(description="Account number", json_schema_extra={"label": "Konto"}) periodYear: int = Field(description="Fiscal year", json_schema_extra={"label": "Jahr"}) periodMonth: int = Field(default=0, description="Month (1-12); 0 = annual total", json_schema_extra={"label": "Monat"}) openingBalance: float = Field(default=0.0, json_schema_extra={"label": "Eröffnungssaldo", "frontend_format": "R:#'###.00"}) debitTotal: float = Field(default=0.0, json_schema_extra={"label": "Soll-Umsatz", "frontend_format": "R:#'###.00"}) creditTotal: float = Field(default=0.0, json_schema_extra={"label": "Haben-Umsatz", "frontend_format": "R:#'###.00"}) closingBalance: float = Field(default=0.0, json_schema_extra={"label": "Schlusssaldo", "frontend_format": "R:#'###.00"}) currency: str = Field(default="CHF", json_schema_extra={"label": "Währung"}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) @i18nModel("Buchhaltungs-Konfiguration") class TrusteeAccountingConfig(PowerOnModel): """Per-instance accounting system configuration with encrypted credentials. Each feature instance can connect to exactly one accounting system. Credentials are stored encrypted (decrypted at runtime by the AccountingBridge). """ id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) featureInstanceId: str = Field(description="FK -> FeatureInstance.id (1:1)", json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) connectorType: str = Field(description="Connector type key, e.g. 'rma', 'bexio', 'abacus'", json_schema_extra={"label": "System"}) displayLabel: str = Field(default="", description="User-visible label for this integration", json_schema_extra={"label": "Bezeichnung"}) encryptedConfig: str = Field(default="", description="Encrypted JSON blob with connector credentials", json_schema_extra={"label": "Verschlüsselte Konfiguration"}) isActive: bool = Field(default=True, json_schema_extra={"label": "Aktiv"}) lastSyncAt: Optional[float] = Field(default=None, description="Timestamp of last sync attempt", json_schema_extra={"label": "Letzte Synchronisation", "frontend_type": "timestamp"}) lastSyncStatus: Optional[str] = Field(default=None, description="Last sync result: success, error, partial", json_schema_extra={"label": "Status"}) lastSyncErrorMessage: Optional[str] = Field(default=None, description="Error message when lastSyncStatus is error", json_schema_extra={"label": "Fehlermeldung"}) lastSyncDateFrom: Optional[float] = Field(default=None, description="dateFrom (UTC midnight unix timestamp) of the last data import window", json_schema_extra={"label": "Letztes Import-Fenster von", "frontend_type": "date"}) lastSyncDateTo: Optional[float] = Field(default=None, description="dateTo (UTC midnight unix timestamp) of the last data import window", json_schema_extra={"label": "Letztes Import-Fenster bis", "frontend_type": "date"}) lastSyncCounts: Optional[Dict[str, Any]] = Field(default=None, description="Last import summary: per-entity counts (accounts, journalEntries, journalLines, contacts, accountBalances) plus oldestBookingDate / newestBookingDate (ISO YYYY-MM-DD) for completeness verification", json_schema_extra={"label": "Letzte Import-Zaehler"}) cachedChartOfAccounts: Optional[str] = Field(default=None, description="JSON-serialised chart of accounts cache (list of {accountNumber, label, accountType})", json_schema_extra={"label": "Cached Kontoplan"}) chartCachedAt: Optional[float] = Field(default=None, description="Timestamp when cachedChartOfAccounts was last refreshed", json_schema_extra={"label": "Kontoplan-Cache-Zeitpunkt", "frontend_type": "timestamp"}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}}) @i18nModel("Buchhaltungs-Synchronisation") class TrusteeAccountingSync(PowerOnModel): """Tracks which position was synced to which external system and when. Used for duplicate prevention, audit trail, and retry logic. """ id: str = Field(default_factory=lambda: str(uuid.uuid4()), json_schema_extra={"label": "ID"}) positionId: str = Field( description="FK -> TrusteePosition.id", json_schema_extra={"label": "Position", "fk_target": {"db": "poweron_trustee", "table": "TrusteePosition", "labelField": None}}, ) featureInstanceId: str = Field(description="FK -> FeatureInstance.id", json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}}) connectorType: str = Field(description="Connector type at time of sync", json_schema_extra={"label": "System"}) externalId: Optional[str] = Field(default=None, description="ID assigned by the external system", json_schema_extra={"label": "Externe ID"}) externalReference: Optional[str] = Field(default=None, description="Reference in the external system", json_schema_extra={"label": "Externe Referenz"}) syncStatus: str = Field(default="pending", description="pending | synced | error | cancelled", json_schema_extra={"label": "Status"}) syncDirection: str = Field(default="push", description="push (local->ext) or pull (ext->local)", json_schema_extra={"label": "Richtung"}) syncedAt: Optional[float] = Field(default=None, description="Timestamp of successful sync", json_schema_extra={"label": "Synchronisiert am", "frontend_type": "timestamp"}) errorMessage: Optional[str] = Field(default=None, json_schema_extra={"label": "Fehler"}) bookingPayload: Optional[dict] = Field(default=None, description="Payload sent to the external system (audit)", json_schema_extra={"label": "Buchungs-Payload"}) mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}})