Add Phase 16 Chart of Accounts and ledger group intelligence

This commit is contained in:
A R R R Associates
2026-08-23 13:35:37 +05:30
parent 10b12a3dea
commit 7d11236eed
12 changed files with 1462 additions and 4 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import io
from pathlib import Path
import zipfile
ERP_LOCAL_AGENT_VERSION = "1.15.1"
ERP_LOCAL_AGENT_VERSION = "1.16.0"
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.1"
__version__ = "1.16.0"
AGENT_NAME = "ERP Local Agent"
@@ -1077,6 +1077,61 @@ class LocalAccountingStore:
).fetchall()
return [dict(row) for row in rows]
def chart_of_accounts_snapshot(self, client_id: int, *, tally_guid: str) -> dict:
if not self.exists(client_id):
raise ValueError("Accounting storage is not initialized for this client.")
guid = str(tally_guid or "").strip()
if not guid:
raise ValueError("Tally company GUID is required.")
mapping = self.get_active_mapping_by_guid(client_id, guid)
with self.connect(client_id) as db:
groups = [
dict(row)
for row in db.execute(
"""SELECT master_guid AS guid, name, parent, reserved_name,
is_revenue, is_deemed_positive, payload_json
FROM tally_groups
WHERE tally_guid=?
ORDER BY name COLLATE NOCASE""",
(guid,),
).fetchall()
]
ledgers = [
dict(row)
for row in db.execute(
"""SELECT master_guid AS guid, name, parent,
opening_balance, closing_balance, is_billwise_on,
tax_type, gst_applicable, gst_registration_type,
gst_type_of_supply, hsn_code, is_revenue,
is_deemed_positive, payload_json
FROM tally_ledgers
WHERE tally_guid=?
ORDER BY name COLLATE NOCASE""",
(guid,),
).fetchall()
]
import json as _json
for row in ledgers:
try:
payload = _json.loads(row.get("payload_json") or "{}")
except Exception:
payload = {}
row["party_gstin"] = str(payload.get("party_gstin") or "")
row.pop("payload_json", None)
for row in groups:
row.pop("payload_json", None)
return {
"tally_guid": guid,
"company_name": str(mapping.get("company_name") or ""),
"groups": groups,
"ledgers": ledgers,
}
def historical_learning_evidence(self, client_id: int, *, tally_guid: str, date_from: str = "", date_to: str = "", voucher_scope: str = "purchase") -> dict:
"""Return neutral aggregated historical ledger facts from the local .act store.
@@ -35,6 +35,8 @@ class AgentCommandProcessor:
result = self._unmap_company(payload)
elif action == "accounting_sync_masters":
result = self._sync_masters(payload)
elif action == "accounting_chart_of_accounts":
result = self._chart_of_accounts(payload)
elif action == "accounting_sync_transactions":
result = self._sync_transactions(payload)
elif action == "accounting_historical_evidence":
@@ -76,7 +78,7 @@ class AgentCommandProcessor:
return {
"name": "ERP Local Agent", "version": __version__,
"tally_capability": True, "accounting_act_capability": True,
"tally_mapping_capability": True, "tally_master_sync_capability": True,
"tally_mapping_capability": True, "tally_master_sync_capability": True, "chart_of_accounts_capability": True,
"tally_transaction_sync_capability": True,
"historical_learning_read_capability": True,
"purchase_posting_preflight_capability": True,
@@ -322,6 +324,21 @@ class AgentCommandProcessor:
return evidence
def _chart_of_accounts(self, payload: dict[str, Any]) -> dict[str, Any]:
# Read-only: refresh the existing master snapshot, then return Groups/Ledgers.
sync_result = self._sync_masters(payload)
client_id = int(payload.get("client_id"))
tally_guid = str(payload.get("tally_guid") or "").strip()
chart = self.store.chart_of_accounts_snapshot(
client_id,
tally_guid=tally_guid,
)
return {
**sync_result,
"chart_of_accounts": chart,
}
def _resolve_open_company(self, payload: dict[str, Any]) -> tuple[dict[str, Any], str]:
requested_guid = str(payload.get("tally_guid") or "").strip()
if not requested_guid:
@@ -114,7 +114,7 @@ class TallyLiveConnector:
"collection": "ARRRAccountingLedgers",
"type": "Ledger",
"tag": "LEDGER",
"fetch": "Name,GUID,Parent,OpeningBalance,ClosingBalance,IsBillWiseOn,TaxType,GSTApplicable,GSTRegistrationType",
"fetch": "Name,GUID,Parent,OpeningBalance,ClosingBalance,IsBillWiseOn,TaxType,GSTApplicable,GSTRegistrationType,GSTTypeOfSupply,HSNCode,PartyGSTIN,GSTIN,GSTRegistrationNumber,IsRevenue,IsDeemedPositive,IsActive",
},
"voucher_types": {
"collection": "ARRRAccountingVoucherTypes",
@@ -1095,6 +1095,10 @@ class TallyLiveConnector:
"gst_registration_type": _child_text(element, "GSTREGISTRATIONTYPE"),
"gst_type_of_supply": _child_text(element, "GSTTYPEOFSUPPLY"),
"hsn_code": _first_text(element, ("HSNCODE", "GSTHSNCODE")),
"party_gstin": _first_text(
element,
("PARTYGSTIN", "GSTIN", "GSTREGISTRATIONNUMBER", "GSTNO", "GSTNUMBER"),
),
"is_revenue": _child_text(element, "ISREVENUE"),
"is_deemed_positive": _child_text(element, "ISDEEMEDPOSITIVE"),
"is_billwise_on": _child_text(element, "ISBILLWISEON"),