Fix Accounting Mirror trial balance reconciliation v1.26.22
This commit is contained in:
@@ -4,7 +4,7 @@ import io
|
||||
from pathlib import Path
|
||||
import zipfile
|
||||
|
||||
ERP_LOCAL_AGENT_VERSION = "1.26.21"
|
||||
ERP_LOCAL_AGENT_VERSION = "1.26.22"
|
||||
ERP_LOCAL_AGENT_NAME = "ERP Local Agent"
|
||||
RUNTIME_ROOT = Path(__file__).resolve().parent / "local_agent_runtime"
|
||||
_DETERMINISTIC_ZIP_TIMESTAMP = (2026, 1, 1, 0, 0, 0)
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.26.21"
|
||||
__version__ = "1.26.22"
|
||||
|
||||
@@ -4558,14 +4558,87 @@ class AgentCommandProcessor:
|
||||
(*params,limit)).fetchall()]
|
||||
return {"rows":rows,"count":len(rows)}
|
||||
if query == "trial_balance":
|
||||
# Tally Trial Balance excludes cancelled and optional vouchers. Older
|
||||
# mirror queries summed voucher_ledger_entry directly, so those
|
||||
# non-accounting vouchers could make mirror debit/credit movement
|
||||
# differ from Tally even though Tally itself was perfectly balanced.
|
||||
#
|
||||
# Keep Tally's master opening/closing balances as the authoritative
|
||||
# control figures and independently reconstruct closing from booked
|
||||
# voucher movement. The extra calculated/difference columns are
|
||||
# backward compatible: existing callers continue using the original
|
||||
# opening_balance, closing_balance, debit and credit fields.
|
||||
rows=[dict(r) for r in db.execute(
|
||||
"""SELECT lm.ledger_name,lm.parent_group,lm.opening_balance,lm.closing_balance,
|
||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE 0 END),0),2) debit,
|
||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='CR' THEN vle.amount ELSE 0 END),0),2) credit
|
||||
FROM ledger_master lm LEFT JOIN voucher_ledger_entry vle ON vle.ledger_name=lm.ledger_name
|
||||
GROUP BY lm.ledger_guid,lm.ledger_name,lm.parent_group,lm.opening_balance,lm.closing_balance
|
||||
"""WITH booked_movement AS (
|
||||
SELECT lower(trim(COALESCE(vle.ledger_name,''))) AS ledger_key,
|
||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE 0 END),0),2) AS debit,
|
||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='CR' THEN vle.amount ELSE 0 END),0),2) AS credit
|
||||
FROM voucher_ledger_entry vle
|
||||
JOIN voucher v ON v.voucher_guid=vle.voucher_guid
|
||||
WHERE COALESCE(v.is_cancelled,0)=0
|
||||
AND COALESCE(v.is_optional,0)=0
|
||||
GROUP BY lower(trim(COALESCE(vle.ledger_name,'')))
|
||||
)
|
||||
SELECT lm.ledger_name,lm.parent_group,lm.opening_balance,lm.closing_balance,
|
||||
ROUND(COALESCE(bm.debit,0),2) AS debit,
|
||||
ROUND(COALESCE(bm.credit,0),2) AS credit,
|
||||
ROUND(COALESCE(lm.opening_balance,0)
|
||||
+ COALESCE(bm.credit,0) - COALESCE(bm.debit,0),2) AS calculated_closing_balance,
|
||||
ROUND(COALESCE(lm.closing_balance,0)
|
||||
- (COALESCE(lm.opening_balance,0)
|
||||
+ COALESCE(bm.credit,0) - COALESCE(bm.debit,0)),2) AS reconciliation_difference
|
||||
FROM ledger_master lm
|
||||
LEFT JOIN booked_movement bm
|
||||
ON bm.ledger_key=lower(trim(COALESCE(lm.ledger_name,'')))
|
||||
ORDER BY lm.parent_group,lm.ledger_name LIMIT ?""",(limit,)).fetchall()]
|
||||
return {"rows":rows,"count":len(rows)}
|
||||
|
||||
# Diagnostics are deliberately returned alongside the existing rows
|
||||
# instead of changing/removing any existing Trial Balance behaviour.
|
||||
# This makes extraction defects visible without ever inserting a
|
||||
# balancing ledger or silently changing Tally control balances.
|
||||
difference_rows = [
|
||||
r for r in rows
|
||||
if abs(float(r.get("reconciliation_difference") or 0)) > 0.01
|
||||
]
|
||||
totals = {
|
||||
"opening_debit": round(sum(abs(float(r.get("opening_balance") or 0)) for r in rows if float(r.get("opening_balance") or 0) < 0), 2),
|
||||
"opening_credit": round(sum(float(r.get("opening_balance") or 0) for r in rows if float(r.get("opening_balance") or 0) > 0), 2),
|
||||
"debit": round(sum(float(r.get("debit") or 0) for r in rows), 2),
|
||||
"credit": round(sum(float(r.get("credit") or 0) for r in rows), 2),
|
||||
"closing_debit": round(sum(abs(float(r.get("closing_balance") or 0)) for r in rows if float(r.get("closing_balance") or 0) < 0), 2),
|
||||
"closing_credit": round(sum(float(r.get("closing_balance") or 0) for r in rows if float(r.get("closing_balance") or 0) > 0), 2),
|
||||
}
|
||||
totals["movement_difference"] = round(totals["debit"] - totals["credit"], 2)
|
||||
totals["closing_difference"] = round(totals["closing_debit"] - totals["closing_credit"], 2)
|
||||
|
||||
unmatched = [dict(r) for r in db.execute(
|
||||
"""SELECT vle.ledger_name, COUNT(*) AS entry_count,
|
||||
ROUND(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE 0 END),2) AS debit,
|
||||
ROUND(SUM(CASE WHEN vle.dr_cr='CR' THEN vle.amount ELSE 0 END),2) AS credit
|
||||
FROM voucher_ledger_entry vle
|
||||
JOIN voucher v ON v.voucher_guid=vle.voucher_guid
|
||||
LEFT JOIN ledger_master lm
|
||||
ON lower(trim(COALESCE(lm.ledger_name,'')))=lower(trim(COALESCE(vle.ledger_name,'')))
|
||||
WHERE COALESCE(v.is_cancelled,0)=0
|
||||
AND COALESCE(v.is_optional,0)=0
|
||||
AND lm.ledger_name IS NULL
|
||||
GROUP BY vle.ledger_name
|
||||
ORDER BY ABS(COALESCE(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE -vle.amount END),0)) DESC
|
||||
LIMIT 100"""
|
||||
).fetchall()]
|
||||
return {
|
||||
"rows": rows,
|
||||
"count": len(rows),
|
||||
"reconciliation": {
|
||||
"ok": not difference_rows and not unmatched and abs(totals["movement_difference"]) <= 0.01,
|
||||
"difference_count": len(difference_rows),
|
||||
"differences": difference_rows[:100],
|
||||
"unmatched_ledger_count": len(unmatched),
|
||||
"unmatched_ledgers": unmatched,
|
||||
"totals": totals,
|
||||
"basis": "Booked vouchers only; cancelled and optional vouchers excluded; Tally ledger closing balance retained as control figure.",
|
||||
},
|
||||
}
|
||||
if query == "group_master":
|
||||
rows=[dict(r) for r in db.execute(
|
||||
"""SELECT master_guid,name,parent,reserved_name,is_revenue,is_deemed_positive,is_active
|
||||
|
||||
Reference in New Issue
Block a user