diff --git a/app/modules/documents/agent_package.py b/app/modules/documents/agent_package.py index 26e38f7..d79f096 100644 --- a/app/modules/documents/agent_package.py +++ b/app/modules/documents/agent_package.py @@ -4,7 +4,7 @@ import io from pathlib import Path import zipfile -ERP_LOCAL_AGENT_VERSION = "1.26.7" +ERP_LOCAL_AGENT_VERSION = "1.26.8" 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) diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/__init__.py b/app/modules/documents/local_agent_runtime/erp_local_agent/__init__.py index 50fd0d1..e0f55b2 100644 --- a/app/modules/documents/local_agent_runtime/erp_local_agent/__init__.py +++ b/app/modules/documents/local_agent_runtime/erp_local_agent/__init__.py @@ -1,2 +1,2 @@ -__version__ = "1.26.7" +__version__ = "1.26.8" AGENT_NAME = "ERP Local Agent" diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/accounting_mirror.py b/app/modules/documents/local_agent_runtime/erp_local_agent/accounting_mirror.py index 8cfc2f2..47894db 100644 --- a/app/modules/documents/local_agent_runtime/erp_local_agent/accounting_mirror.py +++ b/app/modules/documents/local_agent_runtime/erp_local_agent/accounting_mirror.py @@ -245,6 +245,53 @@ class AccountingMirrorEngine: value = _text(value) values.append(value) db.execute(f"INSERT INTO {table} ({','.join(names)}) VALUES ({placeholders})", values) + # Common flat accounting master table. Keep the original + # ledger_master and group_master physical tables intact. + db.execute("DROP TABLE IF EXISTS accounting_master_flat") + db.execute(""" + CREATE TABLE accounting_master_flat ( + object_type TEXT NOT NULL, + guid TEXT, + name TEXT NOT NULL, + parent TEXT, + opening_balance REAL, + closing_balance REAL + ) + """) + db.execute(""" + INSERT INTO accounting_master_flat( + object_type,guid,name,parent,opening_balance,closing_balance + ) + SELECT + 'LEDGER', + COALESCE(ledger_guid,''), + COALESCE(ledger_name,''), + COALESCE(parent_group,''), + opening_balance, + closing_balance + FROM ledger_master + """) + db.execute(""" + INSERT INTO accounting_master_flat( + object_type,guid,name,parent,opening_balance,closing_balance + ) + SELECT + 'GROUP', + COALESCE(master_guid,''), + COALESCE(name,''), + COALESCE(parent,''), + NULL, + NULL + FROM group_master + """) + db.execute( + "CREATE INDEX IF NOT EXISTS idx_accounting_master_flat_type_name " + "ON accounting_master_flat(object_type,name)" + ) + db.execute( + "CREATE INDEX IF NOT EXISTS idx_accounting_master_flat_parent " + "ON accounting_master_flat(parent)" + ) db.commit() finally: db.close() diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_0_1.tdl b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_0_1.tdl index 81d5237..27a4d70 100644 --- a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_0_1.tdl +++ b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_0_1.tdl @@ -19,6 +19,11 @@ Fetch : Name, Parent, GUID, AlterID, OpeningBalance, ClosingBalance, IsRevenue, PartyGSTIN, GSTRegistrationType Is ODBC Table : Yes +[Collection : ERPAMFlatGroupMaster] + Type : Group + Fetch : Name, Parent, GUID + Is ODBC Table : Yes + [Collection : ERPAMStockMaster] Type : StockItem Fetch : Name, Parent, GUID, AlterID, BaseUnits, OpeningBalance, OpeningValue, GSTApplicable, GSTRepUoM, GSTTypeofSupply, GSTItemHSNCodeEx diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV301.cs b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV301.cs index d4f49cd..1803ca6 100644 --- a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV301.cs +++ b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV301.cs @@ -95,31 +95,29 @@ class TallyAccountingMirrorV301 groupSec = 0; string path = Path.Combine(outDir, "group_master.csv"); - // Tally's built-in ODBC Groups table is queried on its own connection. - // This deliberately avoids a custom Group TDL collection and protects - // the proven Ledger/Voucher extraction connection from being disabled. + // Flat Group Master uses the same TDL/ODBC pattern as the proven + // Ledger Master. It is deliberately queried only AFTER the core mirror + // has finished and on a fresh connection, so a Group-side Tally error + // can never disable the Ledger/Voucher/Stock extraction connection. if (TryExtractFresh( dsn, "GROUP MASTER", + "SELECT $GUID,$Name,$Parent FROM ERPAMFlatGroupMaster", + path, out groupRows, out groupSec) && groupRows > 0) + return true; + + // Compatibility fallback for builds that expose Groups directly. + if (TryExtractFresh( + dsn, "GROUP MASTER COMPAT", "SELECT $GUID,$Name,$Parent FROM Groups", path, out groupRows, out groupSec) && groupRows > 0) return true; - // Some builds expose the table but not GUID. Name + Parent are all the - // ERP needs for recursive Fixed Assets / Capital hierarchy traversal. if (TryExtractFresh( dsn, "GROUP MASTER NAME/PARENT", "SELECT $Name,$Parent FROM Groups", path, out groupRows, out groupSec) && groupRows > 0) return true; - // Last compatibility attempt: singular built-in collection/table name, - // again on a NEW connection so a rejected query cannot poison the core. - if (TryExtractFresh( - dsn, "GROUP MASTER COMPAT", - "SELECT $GUID,$Name,$Parent FROM Group", - path, out groupRows, out groupSec) && groupRows > 0) - return true; - WriteEmptyGroupCsv(path); Console.WriteLine("GROUP MASTER - NO GROUP ROWS EXTRACTED; CORE MIRROR DATA REMAINS VALID"); Console.Out.Flush();