Fix live Purchase and Sales historical Tally sync
This commit is contained in:
@@ -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]:
|
||||
|
||||
Reference in New Issue
Block a user