gateway/modules/workflows/methods/methodTrustee/actions/refreshAccountingData.py
2026-04-12 18:32:21 +02:00

139 lines
5.3 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)
try:
from modules.serviceCenter.services.serviceAgent.coreTools._featureSubAgentTools import clearFeatureQueryCache
clearFeatureQueryCache(featureInstanceId)
logger.info("Cleared feature query cache for instance %s after accounting import", featureInstanceId)
except Exception as cacheErr:
logger.warning("Could not clear feature query cache: %s", cacheErr)
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