846 lines
36 KiB
Python
846 lines
36 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.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"},
|
|
"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"},
|
|
"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"},
|
|
"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"},
|
|
"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"},
|
|
}
|
|
)
|
|
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"},
|
|
}
|
|
)
|
|
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": "User"},
|
|
}
|
|
)
|
|
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"},
|
|
}
|
|
)
|
|
mandateId: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate ID",
|
|
json_schema_extra={
|
|
"label": "Mandat",
|
|
"fk_target": {"db": "poweron_app", "table": "Mandate"},
|
|
"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"},
|
|
"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"},
|
|
}
|
|
)
|
|
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"},
|
|
"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"},
|
|
"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"},
|
|
}
|
|
)
|
|
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"},
|
|
"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"},
|
|
"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"},
|
|
}
|
|
)
|
|
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"},
|
|
}
|
|
)
|
|
valuta: Optional[str] = Field(
|
|
default=None,
|
|
description="Value date (ISO format: YYYY-MM-DD)",
|
|
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
|
|
}
|
|
)
|
|
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
|
|
}
|
|
)
|
|
vatPercentage: float = Field(
|
|
default=0.0,
|
|
description="VAT percentage",
|
|
json_schema_extra={
|
|
"label": "MwSt-Prozentsatz",
|
|
"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={
|
|
"label": "MwSt-Betrag",
|
|
"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={
|
|
"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[str] = Field(
|
|
default=None,
|
|
description="Payment due date (ISO format: YYYY-MM-DD)",
|
|
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"},
|
|
"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"},
|
|
"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"}})
|
|
featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
|
|
@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[str] = Field(default=None, description="Booking date (YYYY-MM-DD)", json_schema_extra={"label": "Datum"})
|
|
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"})
|
|
mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate"}})
|
|
featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
|
|
@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"}})
|
|
accountNumber: str = Field(description="Account number", json_schema_extra={"label": "Konto"})
|
|
debitAmount: float = Field(default=0.0, json_schema_extra={"label": "Soll"})
|
|
creditAmount: float = Field(default=0.0, json_schema_extra={"label": "Haben"})
|
|
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"}})
|
|
featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
|
|
@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"}})
|
|
featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
|
|
@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"})
|
|
debitTotal: float = Field(default=0.0, json_schema_extra={"label": "Soll-Umsatz"})
|
|
creditTotal: float = Field(default=0.0, json_schema_extra={"label": "Haben-Umsatz"})
|
|
closingBalance: float = Field(default=0.0, json_schema_extra={"label": "Schlusssaldo"})
|
|
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"}})
|
|
featureInstanceId: Optional[str] = Field(default=None, json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
|
|
@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"}})
|
|
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"})
|
|
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"})
|
|
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"})
|
|
mandateId: Optional[str] = Field(default=None, json_schema_extra={"label": "Mandat", "fk_target": {"db": "poweron_app", "table": "Mandate"}})
|
|
|
|
@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"}},
|
|
)
|
|
featureInstanceId: str = Field(description="FK -> FeatureInstance.id", json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}})
|
|
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"})
|
|
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"}})
|
|
|