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 "
|
||||
"mit den 3 groessten Abweichungen (>10%) und einer fachlichen "
|
||||
"Einschaetzung.\n\n"
|
||||
"Verwende die uebergebene Budget-Datei als Soll-Quelle und die im "
|
||||
"Kontext bereitgestellten Buchhaltungsdaten als Ist-Quelle.\n"
|
||||
"DATENQUELLEN:\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 "
|
||||
"Uebersichts-Chart ueber alle Konten ist gewuenscht.\n\n"
|
||||
"Hinweis: Das documentTheme ist 'finance'. Wenn du ein Dokument erstellst, "
|
||||
|
|
|
|||
|
|
@ -38,6 +38,52 @@ def _tsToIso(ts) -> Optional[str]:
|
|||
_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:
|
||||
"""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:
|
||||
"""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 (
|
||||
TrusteeDataAccount,
|
||||
TrusteeDataJournalEntry,
|
||||
|
|
@ -155,17 +207,9 @@ def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: st
|
|||
}
|
||||
|
||||
balances = trusteeInterface.db.getRecordset(TrusteeDataAccountBalance, recordFilter=baseFilter) or []
|
||||
balanceList = []
|
||||
for b in balances:
|
||||
balanceList.append({
|
||||
"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),
|
||||
})
|
||||
|
||||
currentYear = _dt.now(tz=_tz.utc).year
|
||||
accountSummary = _buildAccountSummary(accountMap, balances, currentYear)
|
||||
|
||||
entries = trusteeInterface.db.getRecordset(TrusteeDataJournalEntry, recordFilter=baseFilter) or []
|
||||
fromTs = _isoToTs(dateFrom)
|
||||
|
|
@ -205,21 +249,26 @@ def _exportAccountingData(trusteeInterface, featureInstanceId: str, dateFrom: st
|
|||
})
|
||||
|
||||
export = {
|
||||
"accounts": list(accountMap.values()),
|
||||
"balances": balanceList,
|
||||
"accountSummary": accountSummary,
|
||||
"journalLines": lineList,
|
||||
"meta": {
|
||||
"accountCount": len(accountMap),
|
||||
"entryCount": len(entryMap),
|
||||
"lineCount": len(lineList),
|
||||
"balanceCount": len(balanceList),
|
||||
"summaryYear": currentYear,
|
||||
"dateFrom": dateFrom,
|
||||
"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)
|
||||
logger.info("Exported accounting data: %d accounts, %d entries, %d lines, %d balances (%d bytes)",
|
||||
len(accountMap), len(entryMap), len(lineList), len(balanceList), len(result))
|
||||
logger.info("Exported accounting data: %d accounts (summary), %d entries, %d lines (%d bytes)",
|
||||
len(accountSummary), len(entryMap), len(lineList), len(result))
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.warning("Could not export accounting data: %s", e)
|
||||
|
|
|
|||
Loading…
Reference in a new issue