Isolate Tally group hierarchy extraction from proven mirror reads

This commit is contained in:
A R R R Associates
2026-09-08 21:16:33 +05:30
parent f927b50ef1
commit f334d25ed4
5 changed files with 83 additions and 16 deletions
@@ -1,2 +1,2 @@
__version__ = "1.26.6"
__version__ = "1.26.7"
AGENT_NAME = "ERP Local Agent"
@@ -317,6 +317,10 @@ class AccountingMirrorEngine:
"COMPANY - ROWS READ:": (14, "Company read"),
"LEDGER MASTER - BEFORE QUERY": (15, "Reading ledgers"),
"LEDGER MASTER - ROWS READ:": (25, "Ledgers read"),
"GROUP MASTER - BEFORE QUERY": (91, "Reading group/subgroup hierarchy"),
"GROUP MASTER NAME/PARENT - BEFORE QUERY": (91, "Reading group/subgroup hierarchy"),
"GROUP MASTER COMPAT - BEFORE QUERY": (91, "Reading group/subgroup hierarchy"),
"GROUP SUMMARY": (93, "Group/subgroup hierarchy completed"),
"STOCK MASTER - BEFORE QUERY": (26, "Reading stock items"),
"STOCK MASTER - ROWS READ:": (34, "Stock items read"),
"STOCK HSN MASTER - BEFORE QUERY": (35, "Reading HSN details"),
@@ -19,11 +19,6 @@
Fetch : Name, Parent, GUID, AlterID, OpeningBalance, ClosingBalance, IsRevenue, PartyGSTIN, GSTRegistrationType
Is ODBC Table : Yes
[Collection : ERPAMGroupMaster]
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
@@ -58,6 +58,74 @@ class TallyAccountingMirrorV301
Console.Out.Flush();
}
static bool TryExtractFresh(string dsn, string label, string sql, string path,
out int rows, out double sec)
{
rows = 0;
sec = 0;
try {
using (var fresh = new OdbcConnection("DSN=" + dsn + ";")) {
fresh.Open();
Extract(fresh, label, sql, path, out rows, out sec);
}
return true;
}
catch(Exception ex) {
Console.WriteLine(label + " - OPTIONAL QUERY FAILED");
Console.WriteLine(label + " - ERROR TYPE: " + ex.GetType().FullName);
Console.WriteLine(label + " - ERROR: " + ex.Message);
if(ex.InnerException != null)
Console.WriteLine(label + " - INNER: " + ex.InnerException.Message);
Console.Out.Flush();
return false;
}
}
static void WriteEmptyGroupCsv(string path)
{
using (var w = new StreamWriter(path, false, new UTF8Encoding(true))) {
w.WriteLine("GUID,Name,Parent");
}
}
static bool ExtractGroupHierarchy(string dsn, string outDir,
out int groupRows, out double groupSec)
{
groupRows = 0;
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.
if (TryExtractFresh(
dsn, "GROUP MASTER",
"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();
return false;
}
static int Main(string[] args)
{
string dsn="TallyODBC64_9000", outDir="mirror_output_v3_0_1";
@@ -69,6 +137,8 @@ class TallyAccountingMirrorV301
try {
var total=Stopwatch.StartNew();
int groupRows = 0;
double groupSec = 0;
Console.WriteLine("ERP ACCOUNTING MIRROR v3.0.1 - ERP INTEGRATION CANDIDATE");
Console.WriteLine("READ ONLY | ODBC | NO XML | NO TALLY WRITES");
Console.WriteLine("BEFORE CONNECT");
@@ -88,13 +158,6 @@ class TallyAccountingMirrorV301
"SELECT $GUID,$AlterID,$Name,$Parent,$OpeningBalance,$ClosingBalance,$IsRevenue,$PartyGSTIN,$GSTRegistrationType FROM ERPAMLedgerMaster",
Path.Combine(outDir,"ledger_master.csv"),out l,out sl);
// Group hierarchy only. Ledger extraction above is intentionally
// the same proven ERPAMLedgerMaster path used before.
int groupRows; double groupSec;
Extract(cn,"GROUP MASTER",
"SELECT $GUID,$Name,$Parent FROM ERPAMGroupMaster",
Path.Combine(outDir,"group_master.csv"),out groupRows,out groupSec);
Extract(cn,"STOCK MASTER",
"SELECT $GUID,$AlterID,$Name,$Parent,$BaseUnits,$OpeningBalance,$OpeningValue,$GSTApplicable,$GSTRepUoM,$GSTTypeofSupply,$GSTItemHSNCodeEx FROM ERPAMStockMaster",
Path.Combine(outDir,"stock_master.csv"),out s,out ss);
@@ -132,9 +195,8 @@ class TallyAccountingMirrorV301
"SELECT $VoucherGUID,$VoucherAlterID,$VoucherMasterID,$VoucherDate,$VoucherType,$VoucherNumber,$PartyLedger,$StockItemName,$HSNCode,$BilledQty,$ActualQty,$Rate,$IsDeemedPositive,$InventoryAmount FROM ERPAMInventoryMovement",
Path.Combine(outDir,"inventory_movement.csv"),out im,out sim);
Console.WriteLine("SUMMARY | Company="+c+
Console.WriteLine("CORE SUMMARY | Company="+c+
" | Ledger="+l+
" | Group="+groupRows+
" | Stock="+s+
" | StockHSN="+tax+" | StockGST="+gstRows+
" | Voucher="+v+
@@ -143,6 +205,12 @@ class TallyAccountingMirrorV301
" | TotalSeconds="+total.Elapsed.TotalSeconds.ToString("0.000"));
}
// Read only Group -> Parent hierarchy after all proven core reads have
// completed and the original connection has been disposed.
ExtractGroupHierarchy(dsn, outDir, out groupRows, out groupSec);
Console.WriteLine("GROUP SUMMARY | Rows="+groupRows+
" | Seconds="+groupSec.ToString("0.000"));
Console.WriteLine("SUCCESS");
return 0;
}