Fix live Purchase and Sales historical Tally sync

This commit is contained in:
A R R R Associates
2026-08-23 11:48:09 +05:30
parent 395f4eed1a
commit 6910b5b6b5
8 changed files with 293 additions and 68 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import io
from pathlib import Path
import zipfile
ERP_LOCAL_AGENT_VERSION = "1.15.0"
ERP_LOCAL_AGENT_VERSION = "1.15.1"
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,2 +1,2 @@
__version__ = "1.15.0"
__version__ = "1.15.1"
AGENT_NAME = "ERP Local Agent"
@@ -216,14 +216,110 @@ class AgentCommandProcessor:
return {"synced": True, "sync": sync, "accounting": self.store.snapshot(client_id), "tally": tally_status, "agent": self._agent_info()}
def _historical_evidence(self, payload: dict[str, Any]) -> dict[str, Any]:
"""Refresh Tally transactions read-only, then aggregate historical evidence.
Earlier builds only aggregated transactions already present in the local
.act database. That made Purchase/Sales learning appear to "not sync"
unless the user had separately run transaction sync first.
"""
from datetime import date
client_id = int(payload.get("client_id"))
return self.store.historical_learning_evidence(
client_id,
tally_guid=str(payload.get("tally_guid") or ""),
date_from=str(payload.get("date_from") or ""),
date_to=str(payload.get("date_to") or ""),
voucher_scope=str(payload.get("voucher_scope") or "purchase"),
requested_guid = str(payload.get("tally_guid") or "").strip()
date_from = str(payload.get("date_from") or "").strip()
date_to = str(payload.get("date_to") or "").strip()
voucher_scope = str(payload.get("voucher_scope") or "purchase").strip().lower()
requested_by_user_id = payload.get("requested_by_user_id")
if voucher_scope not in {"purchase", "sales", "all", "any"}:
raise ValueError("Historical evidence voucher scope must be purchase, sales or all.")
if not requested_guid:
raise ValueError("Select a currently open mapped Tally company.")
try:
start = date.fromisoformat(date_from)
end = date.fromisoformat(date_to)
except Exception as exc:
raise ValueError("Select valid From Date and To Date for historical learning.") from exc
if start > end:
raise ValueError("Historical learning From Date cannot be after To Date.")
if (end - start).days > 1826:
raise ValueError("Historical learning is limited to five years per collection run.")
if not self.store.exists(client_id):
raise ValueError(
"Accounting storage is not initialized for this client. "
"Open Tally Accounting for the client and initialize/map the company first."
)
mapping = self.store.get_active_mapping_by_guid(client_id, requested_guid)
tally_status = self.tally.status()
if not tally_status.get("connected"):
raise ValueError(str(tally_status.get("error") or "TallyPrime is not connected."))
company = next(
(
row
for row in (tally_status.get("companies") or [])
if str(row.get("guid") or "").strip() == requested_guid
),
None,
)
if not company:
raise ValueError(
"The selected mapped Tally company is not currently open in TallyPrime. "
"Open it in Tally and retry."
)
company_name = str(
company.get("name")
or payload.get("company_name")
or mapping.get("company_name")
or ""
).strip()
# Read-only live refresh. No voucher is created or altered.
transactions = self.tally.fetch_accounting_transactions(
company_name,
date_from,
date_to,
)
self.store.record_tally_status(client_id, tally_status)
sync = self.store.replace_transaction_snapshot(
client_id,
mapping={**mapping, "company_name": company_name},
transactions=transactions,
requested_by_user_id=(
int(requested_by_user_id)
if requested_by_user_id not in (None, "")
else None
),
)
evidence = self.store.historical_learning_evidence(
client_id,
tally_guid=requested_guid,
date_from=date_from,
date_to=date_to,
voucher_scope=voucher_scope,
)
evidence["transaction_sync"] = sync
evidence["tally_company_name"] = company_name
evidence["refreshed_from_tally"] = True
self.logger.info(
"Historical %s evidence refreshed from Tally client_id=%s company=%s "
"period=%s..%s vouchers=%s evidence_rows=%s",
voucher_scope,
client_id,
company_name,
date_from,
date_to,
(sync.get("counts") or {}).get("vouchers", sync.get("rows_processed")),
evidence.get("evidence_count"),
)
return evidence
def _resolve_open_company(self, payload: dict[str, Any]) -> tuple[dict[str, Any], str]: