gateway/modules/features/trustee/datamodelFeatureTrustee.py
2026-02-22 22:34:07 +01:00

744 lines
29 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""Trustee models: TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition."""
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field
from modules.shared.attributeUtils import registerModelLabels
import uuid
class TrusteeOrganisation(BaseModel):
"""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={
"frontend_type": "text",
"frontend_readonly": False, # Editable at creation, then readonly
"frontend_required": True
}
)
label: str = Field(
description="Company name",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": True
}
)
enabled: bool = Field(
default=True,
description="Whether the organisation is enabled",
json_schema_extra={
"frontend_type": "checkbox",
"frontend_readonly": False,
"frontend_required": False
}
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate ID (system-level organisation)",
json_schema_extra={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
# System attributes are automatically set by DatabaseConnector:
# _createdAt, _modifiedAt, _createdBy, _modifiedBy
registerModelLabels(
"TrusteeOrganisation",
{"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"label": {"en": "Label", "fr": "Libellé", "de": "Bezeichnung"},
"enabled": {"en": "Enabled", "fr": "Activé", "de": "Aktiviert"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
},
)
class TrusteeRole(BaseModel):
"""Defines roles within the Trustee feature."""
id: str = Field( # Unique string label (PK), not UUID
description="Unique role identifier (label)",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": True
}
)
desc: str = Field(
description="Role description",
json_schema_extra={
"frontend_type": "textarea",
"frontend_readonly": False,
"frontend_required": True
}
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate ID",
json_schema_extra={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
# System attributes are automatically set by DatabaseConnector
registerModelLabels(
"TrusteeRole",
{"en": "Role", "fr": "Rôle", "de": "Rolle"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"desc": {"en": "Description", "fr": "Description", "de": "Beschreibung"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
},
)
class TrusteeAccess(BaseModel):
"""Defines user access to organisations with specific roles."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Unique access ID",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
organisationId: str = Field(
description="Reference to TrusteeOrganisation.id",
json_schema_extra={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": True,
"frontend_options": "/api/trustee/{instanceId}/organisations/options"
}
)
roleId: str = Field(
description="Reference to TrusteeRole.id",
json_schema_extra={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": True,
"frontend_options": "/api/trustee/{instanceId}/roles/options"
}
)
userId: str = Field(
description="User ID assigned to this role",
json_schema_extra={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": True,
"frontend_options": "/api/users/options"
}
)
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={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": False,
"frontend_options": "/api/trustee/{instanceId}/contracts/options",
"frontend_depends_on": "organisationId"
}
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate ID",
json_schema_extra={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
# System attributes are automatically set by DatabaseConnector
registerModelLabels(
"TrusteeAccess",
{"en": "Access", "fr": "Accès", "de": "Zugriff"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"organisationId": {"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
"roleId": {"en": "Role", "fr": "Rôle", "de": "Rolle"},
"userId": {"en": "User", "fr": "Utilisateur", "de": "Benutzer"},
"contractId": {"en": "Contract (optional)", "fr": "Contrat (optionnel)", "de": "Vertrag (optional)"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
},
)
class TrusteeContract(BaseModel):
"""Defines customer contracts within organisations."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Unique contract ID",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
organisationId: str = Field(
description="Reference to TrusteeOrganisation.id (immutable after creation)",
json_schema_extra={
"frontend_type": "select",
"frontend_readonly": False, # Editable at creation, then readonly
"frontend_required": True,
"frontend_options": "/api/trustee/{instanceId}/organisations/options"
}
)
label: str = Field(
description="Label for the customer contract (e.g., 'Muster AG 2026')",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": True
}
)
enabled: bool = Field(
default=True,
description="Whether the contract is enabled",
json_schema_extra={
"frontend_type": "checkbox",
"frontend_readonly": False,
"frontend_required": False
}
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate ID",
json_schema_extra={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False
}
)
# System attributes are automatically set by DatabaseConnector
registerModelLabels(
"TrusteeContract",
{"en": "Contract", "fr": "Contrat", "de": "Vertrag"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"organisationId": {"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
"label": {"en": "Label", "fr": "Libellé", "de": "Bezeichnung"},
"enabled": {"en": "Enabled", "fr": "Activé", "de": "Aktiviert"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
},
)
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"
class TrusteeDocument(BaseModel):
"""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
"""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Unique document ID",
json_schema_extra={
"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={
"frontend_type": "file_reference",
"frontend_readonly": False,
"frontend_required": False
}
)
documentName: str = Field(
description="File name (e.g., 'Beleg.pdf')",
json_schema_extra={
"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={
"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={
"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={
"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={
"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={
"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={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False,
"frontend_hidden": True
}
)
# System attributes are automatically set by DatabaseConnector
registerModelLabels(
"TrusteeDocument",
{"en": "Document", "fr": "Document", "de": "Dokument"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"fileId": {"en": "File Reference", "fr": "Référence du fichier", "de": "Datei-Referenz"},
"documentName": {"en": "Document Name", "fr": "Nom du document", "de": "Dokumentname"},
"documentMimeType": {"en": "MIME Type", "fr": "Type MIME", "de": "MIME-Typ"},
"sourceType": {"en": "Source Type", "fr": "Type de source", "de": "Quelltyp"},
"sourceLocation": {"en": "Source Location", "fr": "Emplacement source", "de": "Quellort"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
"documentType": {"en": "Document Type", "fr": "Type de document", "de": "Dokumenttyp"},
"externalBelegId": {"en": "Beleg ID (Accounting)", "fr": "ID Beleg (Comptabilité)", "de": "Beleg-ID (Buchhaltung)"},
},
)
class TrusteePosition(BaseModel):
"""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.
"""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Unique position ID",
json_schema_extra={
"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={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": False,
"frontend_options": "/api/trustee/{instanceId}/documents/options"
}
)
bankDocumentId: Optional[str] = Field(
default=None,
description="Reference to TrusteeDocument.id (Bank-Referenz / second document)",
json_schema_extra={
"frontend_type": "select",
"frontend_readonly": False,
"frontend_required": False,
"frontend_options": "/api/trustee/{instanceId}/documents/options"
}
)
valuta: Optional[str] = Field(
default=None,
description="Value date (ISO format: YYYY-MM-DD)",
json_schema_extra={
"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={
"frontend_type": "timestamp",
"frontend_readonly": False,
"frontend_required": True
}
)
company: str = Field(
default="",
description="Company name",
json_schema_extra={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": False
}
)
desc: str = Field(
default="",
description="Description",
json_schema_extra={
"frontend_type": "textarea",
"frontend_readonly": False,
"frontend_required": False
}
)
tags: str = Field(
default="",
description="Tags (comma-separated)",
json_schema_extra={
"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": {"en": "CHF", "fr": "CHF", "de": "CHF"}},
{"value": "EUR", "label": {"en": "EUR", "fr": "EUR", "de": "EUR"}},
{"value": "USD", "label": {"en": "USD", "fr": "USD", "de": "USD"}},
{"value": "GBP", "label": {"en": "GBP", "fr": "GBP", "de": "GBP"}},
]
}
)
bookingAmount: float = Field(
default=0.0,
description="Booking amount",
json_schema_extra={
"frontend_type": "number",
"frontend_readonly": False,
"frontend_required": True
}
)
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": {"en": "CHF", "fr": "CHF", "de": "CHF"}},
{"value": "EUR", "label": {"en": "EUR", "fr": "EUR", "de": "EUR"}},
{"value": "USD", "label": {"en": "USD", "fr": "USD", "de": "USD"}},
{"value": "GBP", "label": {"en": "GBP", "fr": "GBP", "de": "GBP"}},
]
}
)
originalAmount: float = Field(
default=0.0,
description="Original amount (manual input, no automatic currency conversion)",
json_schema_extra={
"frontend_type": "number",
"frontend_readonly": False,
"frontend_required": True
}
)
vatPercentage: float = Field(
default=0.0,
description="VAT percentage",
json_schema_extra={
"frontend_type": "number",
"frontend_readonly": False,
"frontend_required": False
}
)
vatAmount: float = Field(
default=0.0,
description="VAT amount (calculated: bookingAmount * vatPercentage / 100, can be manually overridden)",
json_schema_extra={
"frontend_type": "number",
"frontend_readonly": False,
"frontend_required": False
}
)
debitAccountNumber: Optional[str] = Field(
default=None,
description="Debit account number (e.g. '4200' for expenses)",
json_schema_extra={
"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={
"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={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": False
}
)
costCenter: Optional[str] = Field(
default=None,
description="Cost center identifier",
json_schema_extra={
"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={
"frontend_type": "text",
"frontend_readonly": False,
"frontend_required": False
}
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate ID (auto-set from context)",
json_schema_extra={
"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={
"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={
"frontend_type": "text",
"frontend_readonly": True,
"frontend_required": False,
"frontend_hidden": True
}
)
# Allow extra fields like _createdAt from database
model_config = {"extra": "allow"}
registerModelLabels(
"TrusteePosition",
{"en": "Position", "fr": "Position", "de": "Position"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"documentId": {"en": "Document", "fr": "Document", "de": "Dokument"},
"bankDocumentId": {"en": "Bank Reference", "fr": "Référence bancaire", "de": "Bank-Referenz"},
"valuta": {"en": "Value Date", "fr": "Date de valeur", "de": "Valutadatum"},
"transactionDateTime": {"en": "Transaction Date/Time", "fr": "Date/Heure de transaction", "de": "Transaktionszeitpunkt"},
"company": {"en": "Company", "fr": "Entreprise", "de": "Firma"},
"desc": {"en": "Description", "fr": "Description", "de": "Beschreibung"},
"tags": {"en": "Tags", "fr": "Tags", "de": "Tags"},
"bookingCurrency": {"en": "Booking Currency", "fr": "Devise de comptabilisation", "de": "Buchungswährung"},
"bookingAmount": {"en": "Booking Amount", "fr": "Montant de comptabilisation", "de": "Buchungsbetrag"},
"originalCurrency": {"en": "Original Currency", "fr": "Devise d'origine", "de": "Originalwährung"},
"originalAmount": {"en": "Original Amount", "fr": "Montant d'origine", "de": "Originalbetrag"},
"vatPercentage": {"en": "VAT Percentage", "fr": "Pourcentage TVA", "de": "MwSt-Prozentsatz"},
"vatAmount": {"en": "VAT Amount", "fr": "Montant TVA", "de": "MwSt-Betrag"},
"debitAccountNumber": {"en": "Debit Account", "fr": "Compte débit", "de": "Soll-Konto"},
"creditAccountNumber": {"en": "Credit Account", "fr": "Compte crédit", "de": "Haben-Konto"},
"taxCode": {"en": "Tax Code", "fr": "Code TVA", "de": "Steuercode"},
"costCenter": {"en": "Cost Center", "fr": "Centre de coûts", "de": "Kostenstelle"},
"bookingReference": {"en": "Booking Reference", "fr": "Référence de réservation", "de": "Buchungsreferenz"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité", "de": "Feature-Instanz"},
"accountingSyncId": {"en": "Accounting Sync ID", "fr": "ID sync comptabilité", "de": "Buha-Sync-ID"},
},
)
class TrusteeAccountingConfig(BaseModel):
"""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()))
featureInstanceId: str = Field(description="FK -> FeatureInstance.id (1:1)")
connectorType: str = Field(description="Connector type key, e.g. 'rma', 'bexio', 'abacus'")
displayLabel: str = Field(default="", description="User-visible label for this integration")
encryptedConfig: str = Field(default="", description="Encrypted JSON blob with connector credentials")
isActive: bool = Field(default=True)
lastSyncAt: Optional[float] = Field(default=None, description="Timestamp of last sync attempt")
lastSyncStatus: Optional[str] = Field(default=None, description="Last sync result: success, error, partial")
lastSyncErrorMessage: Optional[str] = Field(default=None, description="Error message when lastSyncStatus is error")
cachedChartOfAccounts: Optional[str] = Field(default=None, description="JSON-serialised chart of accounts cache (list of {accountNumber, label, accountType})")
chartCachedAt: Optional[float] = Field(default=None, description="Timestamp when cachedChartOfAccounts was last refreshed")
mandateId: Optional[str] = Field(default=None)
registerModelLabels(
"TrusteeAccountingConfig",
{"en": "Accounting Configuration", "de": "Buchhaltungs-Konfiguration", "fr": "Configuration comptable"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"featureInstanceId": {"en": "Feature Instance", "fr": "Instance", "de": "Feature-Instanz"},
"connectorType": {"en": "System", "fr": "Système", "de": "System"},
"displayLabel": {"en": "Label", "fr": "Libellé", "de": "Bezeichnung"},
"isActive": {"en": "Active", "fr": "Actif", "de": "Aktiv"},
"lastSyncAt": {"en": "Last Sync", "fr": "Dernière sync.", "de": "Letzte Synchronisation"},
"lastSyncStatus": {"en": "Status", "fr": "Statut", "de": "Status"},
"lastSyncErrorMessage": {"en": "Error", "fr": "Erreur", "de": "Fehlermeldung"},
"cachedChartOfAccounts": {"en": "Cached Chart", "de": "Cached Kontoplan", "fr": "Plan comptable en cache"},
"chartCachedAt": {"en": "Chart Cached At", "de": "Kontoplan-Cache-Zeitpunkt", "fr": "Horodatage cache plan comptable"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
},
)
class TrusteeAccountingSync(BaseModel):
"""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()))
positionId: str = Field(description="FK -> TrusteePosition.id")
featureInstanceId: str = Field(description="FK -> FeatureInstance.id")
connectorType: str = Field(description="Connector type at time of sync")
externalId: Optional[str] = Field(default=None, description="ID assigned by the external system")
externalReference: Optional[str] = Field(default=None, description="Reference in the external system")
syncStatus: str = Field(default="pending", description="pending | synced | error | cancelled")
syncDirection: str = Field(default="push", description="push (local->ext) or pull (ext->local)")
syncedAt: Optional[float] = Field(default=None, description="Timestamp of successful sync")
errorMessage: Optional[str] = Field(default=None)
bookingPayload: Optional[dict] = Field(default=None, description="Payload sent to the external system (audit)")
mandateId: Optional[str] = Field(default=None)
registerModelLabels(
"TrusteeAccountingSync",
{"en": "Accounting Sync", "de": "Buchhaltungs-Synchronisation", "fr": "Synchronisation comptable"},
{
"id": {"en": "ID", "fr": "ID", "de": "ID"},
"positionId": {"en": "Position", "fr": "Position", "de": "Position"},
"connectorType": {"en": "System", "fr": "Système", "de": "System"},
"externalId": {"en": "External ID", "fr": "ID Externe", "de": "Externe ID"},
"syncStatus": {"en": "Status", "fr": "Statut", "de": "Status"},
"syncDirection": {"en": "Direction", "fr": "Direction", "de": "Richtung"},
"syncedAt": {"en": "Synced At", "fr": "Synchronisé à", "de": "Synchronisiert am"},
"errorMessage": {"en": "Error", "fr": "Erreur", "de": "Fehler"},
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
},
)