132 lines
4.9 KiB
Python
132 lines
4.9 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
Refresh accounting data from external system (e.g. Abacus) into local TrusteeData* tables.
|
|
Checks lastSyncAt to avoid redundant imports unless forceRefresh is set.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
import time
|
|
from typing import Dict, Any
|
|
|
|
from modules.datamodels.datamodelChat import ActionResult, ActionDocument
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_SYNC_THRESHOLD_SECONDS = 3600
|
|
|
|
|
|
async def refreshAccountingData(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Import/refresh accounting data from the configured external system.
|
|
|
|
If data was synced within the last hour and forceRefresh is not set,
|
|
returns cached counts without triggering an external sync.
|
|
"""
|
|
featureInstanceId = parameters.get("featureInstanceId") or (
|
|
self.services.featureInstanceId if hasattr(self.services, "featureInstanceId") else None
|
|
)
|
|
forceRefresh = parameters.get("forceRefresh", False)
|
|
if isinstance(forceRefresh, str):
|
|
forceRefresh = forceRefresh.lower() in ("true", "1", "yes")
|
|
dateFrom = parameters.get("dateFrom") or None
|
|
dateTo = parameters.get("dateTo") or None
|
|
|
|
if not featureInstanceId:
|
|
return ActionResult.isFailure(error="featureInstanceId is required")
|
|
|
|
try:
|
|
from modules.features.trustee.interfaceFeatureTrustee import getInterface as getTrusteeInterface
|
|
from modules.features.trustee.datamodelFeatureTrustee import (
|
|
TrusteeAccountingConfig,
|
|
TrusteeDataAccount,
|
|
TrusteeDataJournalEntry,
|
|
TrusteeDataJournalLine,
|
|
TrusteeDataContact,
|
|
TrusteeDataAccountBalance,
|
|
)
|
|
|
|
trusteeInterface = getTrusteeInterface(
|
|
self.services.user,
|
|
mandateId=self.services.mandateId,
|
|
featureInstanceId=featureInstanceId,
|
|
)
|
|
|
|
cfgRecords = trusteeInterface.db.getRecordset(
|
|
TrusteeAccountingConfig,
|
|
recordFilter={"featureInstanceId": featureInstanceId, "isActive": True},
|
|
)
|
|
if not cfgRecords:
|
|
return ActionResult.isFailure(error="No active accounting configuration found for this Trustee instance")
|
|
|
|
cfgRecord = cfgRecords[0]
|
|
lastSyncAt = cfgRecord.get("lastSyncAt") or 0
|
|
lastSyncStatus = cfgRecord.get("lastSyncStatus") or ""
|
|
|
|
isFresh = (
|
|
lastSyncAt
|
|
and (time.time() - lastSyncAt) < _SYNC_THRESHOLD_SECONDS
|
|
and lastSyncStatus in ("success", "partial")
|
|
)
|
|
|
|
if isFresh and not forceRefresh:
|
|
counts = _getCachedCounts(trusteeInterface, featureInstanceId)
|
|
counts["synced"] = False
|
|
counts["lastSyncAt"] = lastSyncAt
|
|
counts["lastSyncStatus"] = lastSyncStatus
|
|
counts["message"] = f"Data is fresh (synced {int(time.time() - lastSyncAt)}s ago). Use forceRefresh=true to re-import."
|
|
return ActionResult.isSuccess(documents=[
|
|
ActionDocument(
|
|
documentName="refresh_result",
|
|
documentData=json.dumps(counts, ensure_ascii=False),
|
|
mimeType="application/json",
|
|
)
|
|
])
|
|
|
|
from modules.features.trustee.accounting.accountingDataSync import AccountingDataSync
|
|
|
|
sync = AccountingDataSync(trusteeInterface)
|
|
summary = await sync.importData(
|
|
featureInstanceId=featureInstanceId,
|
|
mandateId=self.services.mandateId,
|
|
dateFrom=dateFrom,
|
|
dateTo=dateTo,
|
|
)
|
|
summary["synced"] = True
|
|
summary.pop("startedAt", None)
|
|
summary.pop("finishedAt", None)
|
|
|
|
return ActionResult.isSuccess(documents=[
|
|
ActionDocument(
|
|
documentName="refresh_result",
|
|
documentData=json.dumps(summary, ensure_ascii=False),
|
|
mimeType="application/json",
|
|
)
|
|
])
|
|
except Exception as e:
|
|
logger.exception("refreshAccountingData failed")
|
|
return ActionResult.isFailure(error=str(e))
|
|
|
|
|
|
def _getCachedCounts(trusteeInterface, featureInstanceId: str) -> Dict[str, Any]:
|
|
"""Count existing records per TrusteeData* table without triggering an external sync."""
|
|
from modules.features.trustee.datamodelFeatureTrustee import (
|
|
TrusteeDataAccount,
|
|
TrusteeDataJournalEntry,
|
|
TrusteeDataJournalLine,
|
|
TrusteeDataContact,
|
|
TrusteeDataAccountBalance,
|
|
)
|
|
counts = {}
|
|
for label, model in [
|
|
("accounts", TrusteeDataAccount),
|
|
("journalEntries", TrusteeDataJournalEntry),
|
|
("journalLines", TrusteeDataJournalLine),
|
|
("contacts", TrusteeDataContact),
|
|
("accountBalances", TrusteeDataAccountBalance),
|
|
]:
|
|
records = trusteeInterface.db.getRecordset(
|
|
model, recordFilter={"featureInstanceId": featureInstanceId}
|
|
)
|
|
counts[label] = len(records) if records else 0
|
|
return counts
|