Fix cash ledger SQLite result transport to VPS
This commit is contained in:
@@ -56,30 +56,35 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold uppercase text-slate-500">Cash Ledger</label>
|
||||
<label class="text-xs font-semibold uppercase text-slate-500">Ledger Scope</label>
|
||||
<select id="cash-ledger-scope" name="ledger_scope" class="mt-1 w-full rounded-xl border-slate-300 text-xs">
|
||||
<option value="cash" selected>Ledgers under Cash-in-Hand</option>
|
||||
<option value="all">All Tally ledgers — manual selection</option>
|
||||
</select>
|
||||
<div class="mt-2 flex gap-2">
|
||||
<select id="cash-ledger-select" name="cash_ledger_name" class="min-w-0 flex-1 rounded-xl border-slate-300 text-sm" required>
|
||||
<option value="">Select Cash ledger</option>
|
||||
{% if selected_cash_ledger_name %}<option value="{{ selected_cash_ledger_name }}" selected>{{ selected_cash_ledger_name }}</option>{% endif %}
|
||||
</select>
|
||||
|
||||
<label class="mt-2 block text-xs font-semibold uppercase text-slate-500">Cash Ledger Selection</label>
|
||||
<select id="cash-ledger-select" name="cash_ledger_name" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required>
|
||||
<option value="">Select Cash ledger</option>
|
||||
{% if selected_cash_ledger_name %}<option value="{{ selected_cash_ledger_name }}" selected>{{ selected_cash_ledger_name }}</option>{% endif %}
|
||||
</select>
|
||||
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<button type="button" id="cash-load-ledgers" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs font-semibold text-slate-700">Load Cash Ledgers</button>
|
||||
<button type="button" id="cash-refresh-ledgers" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs font-semibold text-slate-700">Refresh Master Data</button>
|
||||
</div>
|
||||
<p id="cash-ledger-help" class="mt-1 text-xs text-slate-500">Reads the common ledger master already stored in the client .act SQLite database. TallyPrime is contacted only when you click “Refresh Master Data” or when no local master snapshot exists yet.</p>
|
||||
|
||||
<p id="cash-ledger-help" class="mt-1 text-xs text-slate-500">Load Cash Ledgers reads the common master from the client .act SQLite database only. Refresh Master Data explicitly refreshes that common master from TallyPrime.</p>
|
||||
<div id="cash-ledger-progress" class="mt-2 hidden rounded-xl border border-sky-200 bg-sky-50 p-3">
|
||||
<div class="flex items-center justify-between text-xs font-semibold text-sky-900"><span id="cash-ledger-progress-stage">Preparing Cash ledger list…</span><span id="cash-ledger-progress-pct">0%</span></div>
|
||||
<div class="mt-2 h-2 overflow-hidden rounded-full bg-sky-100"><div id="cash-ledger-progress-bar" class="h-full rounded-full bg-sky-600 transition-all duration-500" style="width:0%"></div></div>
|
||||
</div>
|
||||
<div class="mt-3 grid grid-cols-3 gap-2 text-[11px]">
|
||||
<div class="mt-3 grid grid-cols-2 gap-2 text-[11px] sm:grid-cols-4">
|
||||
<div id="cash-master-step-1" class="rounded-lg border border-slate-200 bg-slate-50 px-2 py-2 text-center"><div class="font-semibold">1. Local Master</div><div class="mt-1 text-slate-500">.act SQLite</div></div>
|
||||
<div id="cash-master-step-2" class="rounded-lg border border-slate-200 bg-slate-50 px-2 py-2 text-center"><div class="font-semibold">2. Cash Filter</div><div class="mt-1 text-slate-500">Cash-in-Hand</div></div>
|
||||
<div id="cash-master-step-3" class="rounded-lg border border-slate-200 bg-slate-50 px-2 py-2 text-center"><div class="font-semibold">3. Ledger Ready</div><div class="mt-1 text-slate-500">No Tally query</div></div>
|
||||
<div id="cash-master-step-3" class="rounded-lg border border-slate-200 bg-slate-50 px-2 py-2 text-center"><div class="font-semibold">3. Transfer to VPS</div><div class="mt-1 text-slate-500">Tunnel result</div></div>
|
||||
<div id="cash-master-step-4" class="rounded-lg border border-slate-200 bg-slate-50 px-2 py-2 text-center"><div class="font-semibold">4. Ledger Ready</div><div class="mt-1 text-slate-500">Dropdown</div></div>
|
||||
</div>
|
||||
<div id="cash-master-meta" class="mt-2 text-[11px] text-slate-500">Master source: local .act SQLite. Use Refresh Master Data only when Tally masters have changed.</div>
|
||||
<div id="cash-master-meta" class="mt-2 text-[11px] text-slate-500">Master source: local .act SQLite. Normal loading never queries TallyPrime.</div>
|
||||
</div>
|
||||
<div><label class="text-xs font-semibold uppercase text-slate-500">From</label><input type="date" name="date_from" value="{{ date_from }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
|
||||
<div><label class="text-xs font-semibold uppercase text-slate-500">To</label><input type="date" name="date_to" value="{{ date_to }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
|
||||
@@ -241,11 +246,12 @@
|
||||
const masterStep1 = document.getElementById('cash-master-step-1');
|
||||
const masterStep2 = document.getElementById('cash-master-step-2');
|
||||
const masterStep3 = document.getElementById('cash-master-step-3');
|
||||
const masterStep4 = document.getElementById('cash-master-step-4');
|
||||
const masterMeta = document.getElementById('cash-master-meta');
|
||||
let ledgerPollTimer = null;
|
||||
|
||||
function paintMasterSteps(step) {
|
||||
[masterStep1, masterStep2, masterStep3].forEach(function(el, idx) {
|
||||
[masterStep1, masterStep2, masterStep3, masterStep4].forEach(function(el, idx) {
|
||||
if (!el) return;
|
||||
el.className = 'rounded-lg border px-2 py-2 text-center ' + (idx < step
|
||||
? 'border-emerald-200 bg-emerald-50 text-emerald-900'
|
||||
@@ -286,7 +292,7 @@
|
||||
from_cache: Boolean(data.from_cache)
|
||||
};
|
||||
renderLedgerOptions();
|
||||
paintMasterSteps(3);
|
||||
paintMasterSteps(4);
|
||||
if (masterMeta) {
|
||||
const synced = data.master_synced_at_utc ? new Date(data.master_synced_at_utc).toLocaleString() : 'time not available';
|
||||
const source = data.master_refreshed ? 'TallyPrime → local .act SQLite (refreshed now)' : 'local .act SQLite';
|
||||
@@ -300,27 +306,51 @@
|
||||
ledgerProgressBar.style.width=p+'%';
|
||||
ledgerProgressPct.textContent=Math.round(p)+'%';
|
||||
ledgerProgressStage.textContent=job.stage||job.status||'Working…';
|
||||
if (p >= 90) paintMasterSteps(3);
|
||||
if (p >= 100) paintMasterSteps(4);
|
||||
else if (p >= 90) paintMasterSteps(3);
|
||||
else if (p >= 60) paintMasterSteps(2);
|
||||
else if (p >= 15) paintMasterSteps(1);
|
||||
}
|
||||
|
||||
async function pollLedgers(jobId, clientId, dateFrom) {
|
||||
async function pollLedgers(jobId, clientId, dateFrom, tallyGuid, companyName, selectedScope, forceRefresh) {
|
||||
try {
|
||||
const url=new URL('/tools/accounting/cash-payments/ledgers/progress',window.location.origin);
|
||||
url.searchParams.set('client_id',clientId); url.searchParams.set('job_id',jobId); url.searchParams.set('date_from',dateFrom);
|
||||
url.searchParams.set('client_id',clientId);
|
||||
url.searchParams.set('job_id',jobId);
|
||||
url.searchParams.set('date_from',dateFrom);
|
||||
url.searchParams.set('tally_guid',tallyGuid || '');
|
||||
url.searchParams.set('company_name',companyName || '');
|
||||
url.searchParams.set('ledger_scope',selectedScope || 'cash');
|
||||
url.searchParams.set('force_refresh',forceRefresh ? '1' : '0');
|
||||
const response=await fetch(url.toString(),{headers:{'Accept':'application/json'},cache:'no-store'});
|
||||
const data=await response.json();
|
||||
if(!response.ok||!data.ok) throw new Error(data.error||'Could not read Tally ledger progress.');
|
||||
const job=data.job||{}; updateLedgerProgress(job);
|
||||
if(job.status==='completed'){
|
||||
populateLedgers(data); ledgerButton.disabled=false; if(ledgerRefreshButton) ledgerRefreshButton.disabled=false;
|
||||
window.setTimeout(()=>ledgerProgress.classList.add('hidden'),1800); return;
|
||||
if(!response.ok||!data.ok) throw new Error(data.error||'Could not read Cash ledger transfer progress.');
|
||||
const job=data.job||{};
|
||||
updateLedgerProgress(job);
|
||||
if(data.transport_pending){
|
||||
ledgerHelp.textContent='Local Agent work is queued/running. Waiting for the tunnel to deliver the ledger result to the VPS…';
|
||||
}
|
||||
if(job.status==='failed') throw new Error(job.error||'Tally ledger sync failed.');
|
||||
ledgerPollTimer=window.setTimeout(()=>pollLedgers(jobId,clientId,dateFrom),1500);
|
||||
if(job.status==='completed' && Array.isArray(data.ledgers) && data.ledgers.length >= 0){
|
||||
populateLedgers(data);
|
||||
ledgerButton.disabled=false;
|
||||
if(ledgerRefreshButton) ledgerRefreshButton.disabled=false;
|
||||
ledgerProgress.classList.remove('hidden');
|
||||
ledgerProgressBar.style.width='100%';
|
||||
ledgerProgressPct.textContent='100%';
|
||||
ledgerProgressStage.textContent=String(data.cash_ledger_count || 0)+' Cash-in-Hand ledger(s) transmitted to VPS';
|
||||
paintMasterSteps(4);
|
||||
ledgerHelp.textContent=String(data.cash_ledger_count || 0)+' Cash-in-Hand ledger(s) received by VPS and ready for selection.';
|
||||
window.setTimeout(()=>ledgerProgress.classList.add('hidden'),1800);
|
||||
return;
|
||||
}
|
||||
if(job.status==='failed') throw new Error(job.error||'Cash ledger preparation failed.');
|
||||
ledgerPollTimer=window.setTimeout(
|
||||
()=>pollLedgers(jobId,clientId,dateFrom,tallyGuid,companyName,selectedScope,forceRefresh),
|
||||
1500
|
||||
);
|
||||
} catch(err) {
|
||||
ledgerHelp.textContent=err&&err.message?err.message:String(err); ledgerButton.disabled=false;
|
||||
ledgerHelp.textContent=err&&err.message?err.message:String(err);
|
||||
ledgerButton.disabled=false;
|
||||
if(ledgerRefreshButton) ledgerRefreshButton.disabled=false;
|
||||
ledgerProgressStage.textContent=ledgerHelp.textContent;
|
||||
}
|
||||
@@ -364,14 +394,23 @@
|
||||
ledgerProgressStage.textContent = data.cash_only
|
||||
? String(data.ledger_count || 0)+' Cash-in-Hand ledger(s) ready from SQLite'
|
||||
: String(data.ledger_count || 0)+' ledger(s) ready from SQLite';
|
||||
paintMasterSteps(3);
|
||||
paintMasterSteps(4);
|
||||
window.setTimeout(()=>ledgerProgress.classList.add('hidden'),1200);
|
||||
return;
|
||||
}
|
||||
const job=data.job||{};
|
||||
if(!job.job_id) throw new Error('Local Agent did not return a ledger-sync job id.');
|
||||
updateLedgerProgress(job);
|
||||
pollLedgers(job.job_id,String(fd.get('client_id')||''),String(fd.get('date_from')||''));
|
||||
if(data.transport_pending){ ledgerHelp.textContent='Command sent. Waiting for the Local Agent result to cross the tunnel back to VPS…'; }
|
||||
pollLedgers(
|
||||
job.job_id,
|
||||
String(fd.get('client_id')||''),
|
||||
String(fd.get('date_from')||''),
|
||||
String(fd.get('tally_guid')||''),
|
||||
companyName,
|
||||
selectedScope,
|
||||
Boolean(forceRefresh)
|
||||
);
|
||||
} catch(err) {
|
||||
ledgerHelp.textContent = err && err.message ? err.message : String(err);
|
||||
ledgerButton.disabled=false; if(ledgerRefreshButton) ledgerRefreshButton.disabled=false;
|
||||
|
||||
Reference in New Issue
Block a user