Fix sundry creditors FIFO ageing from mirror voucher dates
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
__version__ = "1.26.11"
|
||||
__version__ = "1.26.12"
|
||||
AGENT_NAME = "ERP Local Agent"
|
||||
|
||||
@@ -4224,24 +4224,76 @@ class AgentCommandProcessor:
|
||||
|
||||
names = [str(x.get("ledger_name") or "").strip() for x in ledgers if str(x.get("ledger_name") or "").strip()]
|
||||
rows = []
|
||||
date_parse_errors = []
|
||||
truncated = False
|
||||
if names:
|
||||
from datetime import datetime as _dt
|
||||
|
||||
def _iso_voucher_date(value):
|
||||
text = str(value or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
try:
|
||||
return _dt.fromisoformat(text[:10]).date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
for fmt in (
|
||||
"%Y%m%d", "%d-%m-%Y", "%d/%m/%Y", "%d-%b-%Y",
|
||||
"%d-%b-%y", "%m/%d/%Y", "%m/%d/%Y %H:%M:%S",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
):
|
||||
try:
|
||||
return _dt.strptime(text, fmt).date().isoformat()
|
||||
except ValueError:
|
||||
continue
|
||||
return ""
|
||||
|
||||
placeholders = ",".join("?" for _ in names)
|
||||
clauses = [f"LOWER(ledger_name) IN ({placeholders})"]
|
||||
params = [x.casefold() for x in names]
|
||||
if from_date:
|
||||
clauses.append("voucher_date>=?")
|
||||
params.append(from_date)
|
||||
if to_date:
|
||||
clauses.append("voucher_date<=?")
|
||||
params.append(to_date)
|
||||
# Do not filter dates in SQL here. Older .act files can contain
|
||||
# Tally/ODBC date representations that are valid dates but are not
|
||||
# lexically comparable with YYYY-MM-DD. Normalize in Python first,
|
||||
# then apply the requested date window.
|
||||
scan_limit = max(100000, min(250000, creditor_limit * 2))
|
||||
sql = (
|
||||
"SELECT voucher_guid,line_no,voucher_date,voucher_type,voucher_number,party_ledger,"
|
||||
"ledger_name,dr_cr,amount,raw_amount FROM voucher_ledger_entry WHERE "
|
||||
+ " AND ".join(clauses)
|
||||
+ " ORDER BY ledger_name,voucher_date,line_no LIMIT ?"
|
||||
f"LOWER(ledger_name) IN ({placeholders}) "
|
||||
"ORDER BY ledger_name,line_no LIMIT ?"
|
||||
)
|
||||
params.append(creditor_limit)
|
||||
rows = [dict(r) for r in db.execute(sql, tuple(params)).fetchall()]
|
||||
raw_rows = [dict(r) for r in db.execute(
|
||||
sql,
|
||||
tuple([x.casefold() for x in names] + [scan_limit + 1]),
|
||||
).fetchall()]
|
||||
if len(raw_rows) > scan_limit:
|
||||
truncated = True
|
||||
raw_rows = raw_rows[:scan_limit]
|
||||
|
||||
for row in raw_rows:
|
||||
original_date = str(row.get("voucher_date") or "").strip()
|
||||
normalized_date = _iso_voucher_date(original_date)
|
||||
if not normalized_date:
|
||||
date_parse_errors.append({
|
||||
"ledger_name": str(row.get("ledger_name") or ""),
|
||||
"voucher_number": str(row.get("voucher_number") or ""),
|
||||
"voucher_date": original_date,
|
||||
})
|
||||
continue
|
||||
if from_date and normalized_date < from_date:
|
||||
continue
|
||||
if to_date and normalized_date > to_date:
|
||||
continue
|
||||
row["voucher_date"] = normalized_date
|
||||
rows.append(row)
|
||||
|
||||
rows.sort(key=lambda r: (
|
||||
str(r.get("ledger_name") or "").casefold(),
|
||||
str(r.get("voucher_date") or ""),
|
||||
int(r.get("line_no") or 0),
|
||||
))
|
||||
if len(rows) > creditor_limit:
|
||||
truncated = True
|
||||
rows = rows[:creditor_limit]
|
||||
|
||||
# Voucher reference/narration are intentionally joined from the
|
||||
# voucher header so the exported audit trail remains readable.
|
||||
if rows:
|
||||
@@ -4263,7 +4315,9 @@ class AgentCommandProcessor:
|
||||
"ledgers": ledgers,
|
||||
"rows": rows,
|
||||
"count": len(rows),
|
||||
"truncated": len(rows) >= creditor_limit,
|
||||
"truncated": truncated,
|
||||
"date_parse_errors": date_parse_errors[:25],
|
||||
"date_parse_error_count": len(date_parse_errors),
|
||||
"mirror": self.tally.mirror.status(client_id).get("mirror") or {},
|
||||
}
|
||||
if query == "voucher":
|
||||
|
||||
Reference in New Issue
Block a user