trustee budget fix
This commit is contained in:
parent
1ed462ad13
commit
9773c00bca
2 changed files with 74 additions and 19 deletions
|
|
@ -484,8 +484,14 @@ TEMPLATE_WORKFLOWS = [
|
||||||
"3. Kurzer Management-Summary-Absatz (3-5 Saetze) UNTER dem Chart "
|
"3. Kurzer Management-Summary-Absatz (3-5 Saetze) UNTER dem Chart "
|
||||||
"mit den 3 groessten Abweichungen (>10%) und einer fachlichen "
|
"mit den 3 groessten Abweichungen (>10%) und einer fachlichen "
|
||||||
"Einschaetzung.\n\n"
|
"Einschaetzung.\n\n"
|
||||||
"Verwende die uebergebene Budget-Datei als Soll-Quelle und die im "
|
"DATENQUELLEN:\n"
|
||||||
"Kontext bereitgestellten Buchhaltungsdaten als Ist-Quelle.\n"
|
"- SOLL (Budget): Aus der uebergebenen Budget-Datei (Excel).\n"
|
||||||
|
"- IST (Buchhaltung): Verwende AUSSCHLIESSLICH das Feld "
|
||||||
|
"\"closingBalance\" aus \"accountSummary\" im Kontext-JSON. "
|
||||||
|
"Dort steht pro Konto GENAU EIN Ist-Wert (Jahresabschluss-Saldo). "
|
||||||
|
"Fuer Quartals-Budgets stehen zusaetzlich Q1/Q2/Q3/Q4-Felder bereit. "
|
||||||
|
"SUMMIERE NIEMALS mehrere Zeilen oder Journal-Eintraege auf -- der "
|
||||||
|
"closingBalance in accountSummary ist bereits der korrekte Ist-Wert.\n\n"
|
||||||
"WICHTIG: Erstelle KEINEN separaten Chart pro Konto. Nur EIN "
|
"WICHTIG: Erstelle KEINEN separaten Chart pro Konto. Nur EIN "
|
||||||
"Uebersichts-Chart ueber alle Konten ist gewuenscht.\n\n"
|
"Uebersichts-Chart ueber alle Konten ist gewuenscht.\n\n"
|
||||||
"Hinweis: Das documentTheme ist 'finance'. Wenn du ein Dokument erstellst, "
|
"Hinweis: Das documentTheme ist 'finance'. Wenn du ein Dokument erstellst, "
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,52 @@ def _tsToIso(ts) -> Optional[str]:
|
||||||
_SYNC_THRESHOLD_SECONDS = 3600
|
_SYNC_THRESHOLD_SECONDS = 3600
|
||||||
|
|
||||||
|
|
||||||
|
def _buildAccountSummary(accountMap: Dict[str, dict], balances: list, year: int) -> list:
|
||||||
|
"""Aggregate balance records into one row per account for *year*.
|
||||||
|
|
||||||
|
For each account the annual balance record (``periodMonth == 0``) of
|
||||||
|
*year* is preferred. If that row is missing, we also check the
|
||||||
|
previous year's annual record so that YTD carry-forwards are visible.
|
||||||
|
Additionally, quarterly closing balances (Q1-Q4) are derived from the
|
||||||
|
monthly records so the AI can compare against quarterly budgets.
|
||||||
|
"""
|
||||||
|
bestClosing: Dict[str, float] = {}
|
||||||
|
quarterClosing: Dict[str, Dict[str, float]] = {}
|
||||||
|
|
||||||
|
for b in balances:
|
||||||
|
acct = b.get("accountNumber", "")
|
||||||
|
bYear = b.get("periodYear", 0)
|
||||||
|
bMonth = b.get("periodMonth", 0)
|
||||||
|
closing = b.get("closingBalance", 0) or 0
|
||||||
|
|
||||||
|
if bYear == year and bMonth == 0:
|
||||||
|
bestClosing[acct] = closing
|
||||||
|
|
||||||
|
if bYear == year and bMonth in (3, 6, 9, 12):
|
||||||
|
qLabel = f"Q{bMonth // 3}"
|
||||||
|
quarterClosing.setdefault(acct, {})[qLabel] = closing
|
||||||
|
|
||||||
|
if acct not in bestClosing and bYear == year - 1 and bMonth == 0:
|
||||||
|
bestClosing[acct] = closing
|
||||||
|
|
||||||
|
summary = []
|
||||||
|
for nr in sorted(accountMap.keys()):
|
||||||
|
info = accountMap[nr]
|
||||||
|
row = {
|
||||||
|
"account": nr,
|
||||||
|
"label": info.get("label", ""),
|
||||||
|
"type": info.get("type", ""),
|
||||||
|
"group": info.get("group", ""),
|
||||||
|
"closingBalance": round(bestClosing.get(nr, 0), 2),
|
||||||
|
}
|
||||||
|
qData = quarterClosing.get(nr, {})
|
||||||
|
for q in ("Q1", "Q2", "Q3", "Q4"):
|
||||||
|
if q in qData:
|
||||||
|
row[q] = round(qData[q], 2)
|
||||||
|
summary.append(row)
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
async def refreshAccountingData(self, parameters: Dict[str, Any]) -> ActionResult:
|
async def refreshAccountingData(self, parameters: Dict[str, Any]) -> ActionResult:
|
||||||
"""Import/refresh accounting data from the configured external system.
|
"""Import/refresh accounting data from the configured external system.
|
||||||
|
|
||||||
|
|
@ -133,7 +179,13 @@ async def refreshAccountingData(self, parameters: Dict[str, Any]) -> ActionResul
|
||||||
|
|
||||||
|
|
||||||
def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: str = None, dateTo: str = None) -> str:
|
def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: str = None, dateTo: str = None) -> str:
|
||||||
"""Export accounting data (accounts, balances, journal entries+lines) as compact JSON for downstream AI nodes."""
|
"""Export accounting data as compact JSON for downstream AI nodes.
|
||||||
|
|
||||||
|
Produces a pre-aggregated ``accountSummary`` (one row per account with
|
||||||
|
a single *Ist* value) so the AI does not have to navigate thousands of
|
||||||
|
raw balance records. Raw per-month balances are deliberately omitted to
|
||||||
|
avoid confusion and reduce payload size.
|
||||||
|
"""
|
||||||
from modules.features.trustee.datamodelFeatureTrustee import (
|
from modules.features.trustee.datamodelFeatureTrustee import (
|
||||||
TrusteeDataAccount,
|
TrusteeDataAccount,
|
||||||
TrusteeDataJournalEntry,
|
TrusteeDataJournalEntry,
|
||||||
|
|
@ -155,17 +207,9 @@ def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: st
|
||||||
}
|
}
|
||||||
|
|
||||||
balances = trusteeInterface.db.getRecordset(TrusteeDataAccountBalance, recordFilter=baseFilter) or []
|
balances = trusteeInterface.db.getRecordset(TrusteeDataAccountBalance, recordFilter=baseFilter) or []
|
||||||
balanceList = []
|
|
||||||
for b in balances:
|
currentYear = _dt.now(tz=_tz.utc).year
|
||||||
balanceList.append({
|
accountSummary = _buildAccountSummary(accountMap, balances, currentYear)
|
||||||
"account": b.get("accountNumber", ""),
|
|
||||||
"year": b.get("periodYear", 0),
|
|
||||||
"month": b.get("periodMonth", 0),
|
|
||||||
"opening": b.get("openingBalance", 0),
|
|
||||||
"debit": b.get("debitTotal", 0),
|
|
||||||
"credit": b.get("creditTotal", 0),
|
|
||||||
"closing": b.get("closingBalance", 0),
|
|
||||||
})
|
|
||||||
|
|
||||||
entries = trusteeInterface.db.getRecordset(TrusteeDataJournalEntry, recordFilter=baseFilter) or []
|
entries = trusteeInterface.db.getRecordset(TrusteeDataJournalEntry, recordFilter=baseFilter) or []
|
||||||
fromTs = _isoToTs(dateFrom)
|
fromTs = _isoToTs(dateFrom)
|
||||||
|
|
@ -205,21 +249,26 @@ def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: st
|
||||||
})
|
})
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
"accounts": list(accountMap.values()),
|
"accountSummary": accountSummary,
|
||||||
"balances": balanceList,
|
|
||||||
"journalLines": lineList,
|
"journalLines": lineList,
|
||||||
"meta": {
|
"meta": {
|
||||||
"accountCount": len(accountMap),
|
"accountCount": len(accountMap),
|
||||||
"entryCount": len(entryMap),
|
"entryCount": len(entryMap),
|
||||||
"lineCount": len(lineList),
|
"lineCount": len(lineList),
|
||||||
"balanceCount": len(balanceList),
|
"summaryYear": currentYear,
|
||||||
"dateFrom": dateFrom,
|
"dateFrom": dateFrom,
|
||||||
"dateTo": dateTo,
|
"dateTo": dateTo,
|
||||||
|
"hint": (
|
||||||
|
"accountSummary contains ONE row per account with the "
|
||||||
|
"current-year closing balance (Ist). Use this for "
|
||||||
|
"budget comparisons. journalLines lists individual "
|
||||||
|
"bookings for drill-down."
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result = json.dumps(export, ensure_ascii=False, default=str)
|
result = json.dumps(export, ensure_ascii=False, default=str)
|
||||||
logger.info("Exported accounting data: %d accounts, %d entries, %d lines, %d balances (%d bytes)",
|
logger.info("Exported accounting data: %d accounts (summary), %d entries, %d lines (%d bytes)",
|
||||||
len(accountMap), len(entryMap), len(lineList), len(balanceList), len(result))
|
len(accountSummary), len(entryMap), len(lineList), len(result))
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Could not export accounting data: %s", e)
|
logger.warning("Could not export accounting data: %s", e)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue