Fix Accounting Mirror trial balance reconciliation v1.26.22
This commit is contained in:
@@ -4,7 +4,7 @@ import io
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
ERP_LOCAL_AGENT_VERSION = "1.26.21"
|
ERP_LOCAL_AGENT_VERSION = "1.26.22"
|
||||||
ERP_LOCAL_AGENT_NAME = "ERP Local Agent"
|
ERP_LOCAL_AGENT_NAME = "ERP Local Agent"
|
||||||
RUNTIME_ROOT = Path(__file__).resolve().parent / "local_agent_runtime"
|
RUNTIME_ROOT = Path(__file__).resolve().parent / "local_agent_runtime"
|
||||||
_DETERMINISTIC_ZIP_TIMESTAMP = (2026, 1, 1, 0, 0, 0)
|
_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()]
|
(*params,limit)).fetchall()]
|
||||||
return {"rows":rows,"count":len(rows)}
|
return {"rows":rows,"count":len(rows)}
|
||||||
if query == "trial_balance":
|
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(
|
rows=[dict(r) for r in db.execute(
|
||||||
"""SELECT lm.ledger_name,lm.parent_group,lm.opening_balance,lm.closing_balance,
|
"""WITH booked_movement AS (
|
||||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE 0 END),0),2) debit,
|
SELECT lower(trim(COALESCE(vle.ledger_name,''))) AS ledger_key,
|
||||||
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='CR' THEN vle.amount ELSE 0 END),0),2) credit
|
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='DR' THEN vle.amount ELSE 0 END),0),2) AS debit,
|
||||||
FROM ledger_master lm LEFT JOIN voucher_ledger_entry vle ON vle.ledger_name=lm.ledger_name
|
ROUND(COALESCE(SUM(CASE WHEN vle.dr_cr='CR' THEN vle.amount ELSE 0 END),0),2) AS credit
|
||||||
GROUP BY lm.ledger_guid,lm.ledger_name,lm.parent_group,lm.opening_balance,lm.closing_balance
|
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()]
|
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":
|
if query == "group_master":
|
||||||
rows=[dict(r) for r in db.execute(
|
rows=[dict(r) for r in db.execute(
|
||||||
"""SELECT master_guid,name,parent,reserved_name,is_revenue,is_deemed_positive,is_active
|
"""SELECT master_guid,name,parent,reserved_name,is_revenue,is_deemed_positive,is_active
|
||||||
|
|||||||
Reference in New Issue
Block a user