Tally is checked locally at 127.0.0.1:9000. Port 9000 is not exposed to the internet.
+
Tally Accounting Mirror TDL
Link Tally once to the stable local path below. Local Agent updates keep this same file path and refresh its contents, so future TDL repairs do not require a new Tally link.
Stable Local TDL Path
Local TDL URL
For Tally's Local TDL setting, use the stable file path if your TallyPrime build does not accept an HTTP URL. The URL is also kept stable for tools that support URL loading.
Cash Payment Compliance
Background Tally job
0%
Waiting…
Days0 / 0
Cash vouchers0
Tally requests0
Current date—
1. Connect to Tally
2. Confirm Cash ledger
3. Extract transactions
4. Analyse threshold
5. Complete
Recent History
Loading…
@@ -171,13 +236,14 @@ function gb(n){return (n/1073741824).toFixed(1)+' GB'}
function setProgress(title,pct,text,active=true){let b=document.getElementById('progressBox');b.style.display='block';document.getElementById('progressTitle').textContent=title;let p=Math.max(0,Math.min(100,Number(pct)||0));lastServerPercent=Math.max(lastServerPercent,p);document.getElementById('progressPct').textContent=Math.round(p)+'%';document.getElementById('progressFill').style.width=p+'%';document.getElementById('progressMsg').textContent=text||'';document.getElementById('progressFill').className='progressfill'+(active&&p<100?' pulse':'')}
function applyProgress(u){let p=(u&&u.progress)||{};if(!p.operation)return;let title=p.operation==='check'?'Checking for Updates':p.operation==='download'?'Downloading Update':'Applying Update';setProgress(title,p.percent||0,p.message||p.phase||'',!['updated','downloaded','current','available','error'].includes(p.status));if(p.operation==='install'&&p.target_version){installTarget=p.target_version}}
function setCashStep(id,state){let e=document.getElementById(id);e.className='cashstep'+(state?' '+state:'')}
-async function refreshCashProgress(){try{const r=await fetch('/api/cash-progress',{cache:'no-store'});const d=await r.json();const j=d.job||{};if(!j.job_id){document.getElementById('cashJobSection').style.display='none';return}document.getElementById('cashJobSection').style.display='block';let p=Math.max(0,Math.min(100,Number(j.percent)||0));document.getElementById('cashJobPct').textContent=Math.round(p)+'%';document.getElementById('cashJobFill').style.width=p+'%';document.getElementById('cashJobStage').textContent=j.stage||j.status||'Working…';let kind=String(j.job_kind||'');let isLedger=(kind==='ledger_master'||kind==='cash_ledger_discovery');document.querySelector('#cashJobSection h2').textContent=isLedger?'Cash Payment — Tally Ledger Sync':'Cash Payment Compliance';document.getElementById('cashJobSub').textContent=(j.company_name||'Tally company')+(j.cash_ledgers&&j.cash_ledgers.length?' • Cash ledger: '+j.cash_ledgers.join(', '):'');if(isLedger){document.getElementById('cashDays').parentElement.querySelector('.label').textContent='Ledgers';document.getElementById('cashDays').textContent=String(j.ledger_count||0);document.getElementById('cashVouchers').parentElement.querySelector('.label').textContent='Status';document.getElementById('cashVouchers').textContent=j.status||'working';document.getElementById('cashRequests').parentElement.querySelector('.label').textContent='Progress';document.getElementById('cashRequests').textContent=Math.round(p)+'%';document.getElementById('cashDate').parentElement.querySelector('.label').textContent='Source';document.getElementById('cashDate').textContent=(kind==='cash_ledger_discovery'?'Cash-in-Hand':'Tally ledger master');}else{document.getElementById('cashDays').parentElement.querySelector('.label').textContent='Days';document.getElementById('cashDays').textContent=String(j.completed_days||0)+' / '+String(j.total_days||0);document.getElementById('cashVouchers').parentElement.querySelector('.label').textContent='Cash vouchers';document.getElementById('cashVouchers').textContent=String(j.vouchers_cached||0);document.getElementById('cashRequests').parentElement.querySelector('.label').textContent='Tally requests';document.getElementById('cashRequests').textContent=String(j.tally_requests||0);document.getElementById('cashDate').parentElement.querySelector('.label').textContent='Current date';document.getElementById('cashDate').textContent=j.current_date||'—';}let st=String(j.stage||'').toLowerCase(),status=String(j.status||'').toLowerCase();if(isLedger){setCashStep('cs1',(p>=15?'done':(p>0?'run':'')));setCashStep('cs2','');setCashStep('cs3','');setCashStep('cs4','');setCashStep('cs5','');}else{setCashStep('cs1',(p>=10?'done':(p>0?'run':'')));setCashStep('cs2',(p>=20?'done':(st.includes('ledger')?'run':'')));setCashStep('cs3',(p>=93?'done':(p>=20?'run':'')));setCashStep('cs4',(status==='completed'?'done':(st.includes('analys')?'run':'')));setCashStep('cs5',(status==='completed'?'done':''));}let er=document.getElementById('cashError');if(j.error){er.style.display='block';er.textContent=j.error}else{er.style.display='none'}}catch(e){}}
+async function refreshCashProgress(){try{const r=await fetch('/api/cash-progress',{cache:'no-store'});const d=await r.json();const j=d.job||{};if(!j.job_id){document.getElementById('cashJobSection').style.display='none';return}document.getElementById('cashJobSection').style.display='block';let p=Math.max(0,Math.min(100,Number(j.percent)||0));document.getElementById('cashJobPct').textContent=Math.round(p)+'%';document.getElementById('cashJobFill').style.width=p+'%';document.getElementById('cashJobStage').textContent=j.stage||j.status||'Working…';document.getElementById('cashJobSub').textContent=(j.company_name||'Tally company')+(j.cash_ledgers&&j.cash_ledgers.length?' • Cash ledger: '+j.cash_ledgers.join(', '):'');document.getElementById('cashDays').textContent=String(j.completed_days||0)+' / '+String(j.total_days||0);document.getElementById('cashVouchers').textContent=String(j.vouchers_cached||0);document.getElementById('cashRequests').textContent=String(j.tally_requests||0);document.getElementById('cashDate').textContent=j.current_date||'—';let st=String(j.stage||'').toLowerCase(),status=String(j.status||'').toLowerCase();setCashStep('cs1',(p>=10?'done':(p>0?'run':'')));setCashStep('cs2',(p>=20?'done':(st.includes('ledger')?'run':'')));setCashStep('cs3',(p>=93?'done':(p>=20?'run':'')));setCashStep('cs4',(status==='completed'?'done':(st.includes('analys')?'run':'')));setCashStep('cs5',(status==='completed'?'done':''));let er=document.getElementById('cashError');if(j.error){er.style.display='block';er.textContent=j.error}else{er.style.display='none'}}catch(e){}}
async function refresh(){try{const r=await fetch('/api/status',{cache:'no-store'});if(!r.ok)throw new Error('status unavailable');const s=await r.json();offlineTicks=0;
let c=s.agent.connection_state||'unknown';let ce=document.getElementById('connection');ce.textContent=c.charAt(0).toUpperCase()+c.slice(1);ce.className='value '+(c==='connected'?'ok':'bad');
document.getElementById('heartbeat').textContent=s.agent.last_heartbeat_utc?'Last heartbeat: '+s.agent.last_heartbeat_utc:'No heartbeat recorded';
document.getElementById('version').textContent=s.agent.version;document.getElementById('latest').textContent='Latest: '+s.update.latest_version+(s.update.update_available?' • update available':' • current');
let te=document.getElementById('tally');let known=s.tally.connected!==null;te.textContent=!known?'Not checked':(s.tally.connected?'Connected':'Offline');te.className='value '+(!known?'warn':(s.tally.connected?'ok':'bad'));document.getElementById('companies').textContent=!known?'Use Test Tally Connection':(s.tally.connected?(s.tally.company_count+' loaded compan'+(s.tally.company_count===1?'y':'ies')):(s.tally.error||''));
document.getElementById('storage').textContent=gb(s.storage.free_bytes);document.getElementById('storageRoot').textContent=s.storage.root;
+if(s.tdl){document.getElementById('tdlPath').value=s.tdl.path||'';document.getElementById('tdlUrl').value=s.tdl.url||'';document.getElementById('tdlMeta').textContent='Mirror '+(s.tdl.version||'')+' • SHA256 '+String(s.tdl.sha256||'').slice(0,16)+'…'+(s.tdl.repaired?' • refreshed now':'');}
document.getElementById('check').disabled=!!s.update.busy;document.getElementById('download').disabled=!s.update.update_available||!!s.update.busy;document.getElementById('install').disabled=!s.update.downloaded||!!s.update.busy;applyProgress(s.update);
if(installWatching&&installTarget&&s.agent.version===installTarget){setProgress('Update Complete',100,'ERP Local Agent '+installTarget+' is running. Updated dashboard loaded.',false);msg('Update completed successfully.',true);installWatching=false;setTimeout(()=>location.reload(),1200)}
await refreshHistory();await refreshCashProgress();}catch(e){if(installWatching){offlineTicks++;let estimated=Math.min(92,Math.max(lastServerPercent,35+offlineTicks*3));setProgress('Applying Update',estimated,'Local Agent is restarting. Waiting for the updated dashboard to come back online…',true)}else{console.log(e)}}}
@@ -187,6 +253,8 @@ async function checkUpdate(){setProgress('Checking for Updates',10,'Checking ERP
async function downloadUpdate(){setProgress('Downloading Update',5,'Starting download…',true);msg('Downloading update…',true);try{const r=await fetch('/api/update/download',{method:'POST'});const j=await r.json();if(!r.ok||!j.ok)throw new Error(j.error||'Download failed');msg('Download started. You can keep this dashboard open.',true);await refresh()}catch(e){setProgress('Download Update',100,e.message,false);msg(e.message,false)}}
async function action(url){msg('Working…',true);try{const r=await fetch(url,{method:'POST'});const j=await r.json();if(!r.ok||!j.ok)throw new Error(j.error||'Operation failed');msg('Completed successfully.',true);await refresh()}catch(e){msg(e.message,false)}}
async function installUpdate(){if(!confirm('Install the downloaded update now? The Local Agent worker will restart safely and this dashboard will show progress until the updated dashboard returns.'))return;let latest=document.getElementById('latest').textContent.replace('Latest: ','').split(' •')[0];installTarget=latest;installWatching=true;offlineTicks=0;lastServerPercent=5;setProgress('Applying Update',5,'Queuing update with the Local Agent supervisor…',true);msg('Starting safe update installation…',true);try{const r=await fetch('/api/update/install',{method:'POST'});const j=await r.json();if(!r.ok||!j.ok)throw new Error(j.error||'Install failed');if(j.target_version)installTarget=j.target_version;msg(j.message||'Update queued. Keep this dashboard open.',true);setProgress('Applying Update',8,'Update queued. Waiting for supervisor…',true)}catch(e){installWatching=false;setProgress('Apply Update',100,e.message,false);msg(e.message,false)}}
+async function copyText(id){let e=document.getElementById(id);let v=e.value||'';try{await navigator.clipboard.writeText(v);msg('Copied to clipboard.',true)}catch(err){e.focus();e.select();document.execCommand('copy');msg('Copied to clipboard.',true)}}
+async function repairTdl(){msg('Refreshing the managed TDL…',true);try{const r=await fetch('/api/tally/tdl/repair',{method:'POST'});const j=await r.json();if(!r.ok||!j.ok)throw new Error(j.error||'TDL repair failed');msg('TDL refreshed successfully. Restart/reload TDL in TallyPrime if it is already open.',true);await refresh()}catch(e){msg(e.message,false)}}
function msg(t,ok){let e=document.getElementById('message');e.textContent=t;e.className=ok?'msgok':'msgbad'}
refresh();setInterval(refresh,1000);
'''
diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_1.tdl b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_1.tdl
index 16838b7..81d5237 100644
--- a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_1.tdl
+++ b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/ERP_Accounting_Mirror_v3_1.tdl
@@ -163,45 +163,3 @@
;; ============================================================
;; END
;; ============================================================
-
-;; ================================================================
-;; ACCOUNTING MIRROR v3.1 - ADDITIONAL MASTER COLLECTIONS
-;; These are flat read-only ODBC master tables used to replace the
-;; former XML master synchronization path.
-;; ================================================================
-
-[Collection : ERPAMGroupMaster]
- Type : Group
- Fetch : Name, GUID, Parent, ReservedName, IsRevenue, IsDeemedPositive, IsActive
- Is ODBC Table : Yes
-
-[Collection : ERPAMVoucherTypeMaster]
- Type : VoucherType
- Fetch : Name, GUID, Parent, NumberingMethod, IsActive
- Is ODBC Table : Yes
-
-[Collection : ERPAMStockGroupMaster]
- Type : StockGroup
- Fetch : Name, GUID, Parent, BaseUnits, GSTApplicable, GSTTypeofSupply, IsActive
- Is ODBC Table : Yes
-
-[Collection : ERPAMStockCategoryMaster]
- Type : StockCategory
- Fetch : Name, GUID, Parent, IsActive
- Is ODBC Table : Yes
-
-[Collection : ERPAMUnitMaster]
- Type : Unit
- Fetch : Name, GUID, OriginalName, IsSimpleUnit, BaseUnits, AdditionalUnits, Conversion
- Is ODBC Table : Yes
-
-[Collection : ERPAMCostCentreMaster]
- Type : CostCentre
- Fetch : Name, GUID, Parent, IsActive
- Is ODBC Table : Yes
-
-[Collection : ERPAMCostCategoryMaster]
- Type : CostCategory
- Fetch : Name, GUID, Parent, IsActive
- Is ODBC Table : Yes
-
diff --git a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV31.cs b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV31.cs
index 0f35c98..e5b27d8 100644
--- a/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV31.cs
+++ b/app/modules/documents/local_agent_runtime/erp_local_agent/mirror_runtime/TallyAccountingMirrorV31.cs
@@ -6,7 +6,7 @@ using System.Globalization;
using System.IO;
using System.Text;
-class TallyAccountingMirrorV31
+class TallyAccountingMirrorV301
{
static string Safe(object v) {
if (v == null || v == DBNull.Value) return "";
@@ -58,29 +58,9 @@ class TallyAccountingMirrorV31
Console.Out.Flush();
}
-
- static void TryExtract(OdbcConnection cn, string label, string sql, string path,
- out int rows, out double sec)
- {
- try
- {
- Extract(cn, label, sql, path, out rows, out sec);
- }
- catch (Exception ex)
- {
- rows = 0;
- sec = 0;
- Console.WriteLine(label + " - OPTIONAL QUERY FAILED: " + ex.Message);
- using (var w = new StreamWriter(path, false, new UTF8Encoding(true)))
- {
- w.WriteLine("");
- }
- }
- }
-
static int Main(string[] args)
{
- string dsn="TallyODBC64_9000", outDir="mirror_output_v3_1";
+ string dsn="TallyODBC64_9000", outDir="mirror_output_v3_0_1";
for(int i=0;i