579 lines
20 KiB
Python
579 lines
20 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""Trustee models: TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition, TrusteePositionDocument."""
|
|
|
|
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
|
|
}
|
|
)
|
|
# 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"},
|
|
},
|
|
)
|
|
|
|
|
|
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
|
|
}
|
|
)
|
|
# 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"},
|
|
},
|
|
)
|
|
|
|
|
|
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": "TrusteeOrganisation"
|
|
}
|
|
)
|
|
roleId: str = Field(
|
|
description="Reference to TrusteeRole.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeRole"
|
|
}
|
|
)
|
|
userId: str = Field(
|
|
description="User ID assigned to this role",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "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={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": False,
|
|
"frontend_options": "TrusteeContract",
|
|
"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
|
|
}
|
|
)
|
|
# 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"},
|
|
},
|
|
)
|
|
|
|
|
|
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": "TrusteeOrganisation"
|
|
}
|
|
)
|
|
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
|
|
}
|
|
)
|
|
# 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"},
|
|
},
|
|
)
|
|
|
|
|
|
class TrusteeDocument(BaseModel):
|
|
"""Contains document references and receipts for bookings."""
|
|
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
|
|
}
|
|
)
|
|
organisationId: str = Field(
|
|
description="Reference to TrusteeOrganisation.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeOrganisation"
|
|
}
|
|
)
|
|
contractId: str = Field(
|
|
description="Reference to TrusteeContract.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeContract",
|
|
"frontend_depends_on": "organisationId"
|
|
}
|
|
)
|
|
documentData: Optional[bytes] = Field(
|
|
default=None,
|
|
description="The file content (binary)",
|
|
json_schema_extra={
|
|
"frontend_type": "file",
|
|
"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": [
|
|
{"value": "application/pdf", "label": {"en": "PDF", "fr": "PDF", "de": "PDF"}},
|
|
{"value": "image/jpeg", "label": {"en": "JPEG", "fr": "JPEG", "de": "JPEG"}},
|
|
{"value": "image/png", "label": {"en": "PNG", "fr": "PNG", "de": "PNG"}},
|
|
{"value": "application/octet-stream", "label": {"en": "Other", "fr": "Autre", "de": "Andere"}},
|
|
]
|
|
}
|
|
)
|
|
mandateId: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate ID",
|
|
json_schema_extra={
|
|
"frontend_type": "text",
|
|
"frontend_readonly": True,
|
|
"frontend_required": False
|
|
}
|
|
)
|
|
# System attributes are automatically set by DatabaseConnector
|
|
|
|
|
|
registerModelLabels(
|
|
"TrusteeDocument",
|
|
{"en": "Document", "fr": "Document", "de": "Dokument"},
|
|
{
|
|
"id": {"en": "ID", "fr": "ID", "de": "ID"},
|
|
"organisationId": {"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
|
|
"contractId": {"en": "Contract", "fr": "Contrat", "de": "Vertrag"},
|
|
"documentData": {"en": "Document Data", "fr": "Données du document", "de": "Dokumentdaten"},
|
|
"documentName": {"en": "Document Name", "fr": "Nom du document", "de": "Dokumentname"},
|
|
"documentMimeType": {"en": "MIME Type", "fr": "Type MIME", "de": "MIME-Typ"},
|
|
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
|
|
},
|
|
)
|
|
|
|
|
|
class TrusteePosition(BaseModel):
|
|
"""Contains booking positions (expense entries)."""
|
|
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
|
|
}
|
|
)
|
|
organisationId: str = Field(
|
|
description="Reference to TrusteeOrganisation.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeOrganisation"
|
|
}
|
|
)
|
|
contractId: str = Field(
|
|
description="Reference to TrusteeContract.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeContract",
|
|
"frontend_depends_on": "organisationId"
|
|
}
|
|
)
|
|
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
|
|
}
|
|
)
|
|
mandateId: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate ID",
|
|
json_schema_extra={
|
|
"frontend_type": "text",
|
|
"frontend_readonly": True,
|
|
"frontend_required": False
|
|
}
|
|
)
|
|
# System attributes are automatically set by DatabaseConnector
|
|
|
|
|
|
registerModelLabels(
|
|
"TrusteePosition",
|
|
{"en": "Position", "fr": "Position", "de": "Position"},
|
|
{
|
|
"id": {"en": "ID", "fr": "ID", "de": "ID"},
|
|
"organisationId": {"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
|
|
"contractId": {"en": "Contract", "fr": "Contrat", "de": "Vertrag"},
|
|
"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"},
|
|
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
|
|
},
|
|
)
|
|
|
|
|
|
class TrusteePositionDocument(BaseModel):
|
|
"""Cross-reference table linking positions to documents (many-to-many)."""
|
|
id: str = Field(
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
description="Unique link 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": "TrusteeOrganisation"
|
|
}
|
|
)
|
|
contractId: str = Field(
|
|
description="Reference to TrusteeContract.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeContract",
|
|
"frontend_depends_on": "organisationId"
|
|
}
|
|
)
|
|
documentId: str = Field(
|
|
description="Reference to TrusteeDocument.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteeDocument",
|
|
"frontend_depends_on": "contractId"
|
|
}
|
|
)
|
|
positionId: str = Field(
|
|
description="Reference to TrusteePosition.id",
|
|
json_schema_extra={
|
|
"frontend_type": "select",
|
|
"frontend_readonly": False,
|
|
"frontend_required": True,
|
|
"frontend_options": "TrusteePosition",
|
|
"frontend_depends_on": "contractId"
|
|
}
|
|
)
|
|
mandateId: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate ID",
|
|
json_schema_extra={
|
|
"frontend_type": "text",
|
|
"frontend_readonly": True,
|
|
"frontend_required": False
|
|
}
|
|
)
|
|
# System attributes are automatically set by DatabaseConnector
|
|
|
|
|
|
registerModelLabels(
|
|
"TrusteePositionDocument",
|
|
{"en": "Position-Document Link", "fr": "Lien Position-Document", "de": "Position-Dokument-Verknüpfung"},
|
|
{
|
|
"id": {"en": "ID", "fr": "ID", "de": "ID"},
|
|
"organisationId": {"en": "Organisation", "fr": "Organisation", "de": "Organisation"},
|
|
"contractId": {"en": "Contract", "fr": "Contrat", "de": "Vertrag"},
|
|
"documentId": {"en": "Document", "fr": "Document", "de": "Dokument"},
|
|
"positionId": {"en": "Position", "fr": "Position", "de": "Position"},
|
|
"mandateId": {"en": "Mandate", "fr": "Mandat", "de": "Mandat"},
|
|
},
|
|
)
|