Simplify cash ledger selection using Cash-in-Hand scope

This commit is contained in:
A R R R Associates
2026-09-04 17:09:12 +05:30
parent 9a6b35c19b
commit 79896d72c1
@@ -57,23 +57,27 @@
</div> </div>
<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">Cash Ledger</label>
<div class="mt-1 flex gap-2"> <select id="cash-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> <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="">Load and confirm cash ledger</option> <option value="">Select Cash ledger</option>
{% if selected_cash_ledger_name %}<option value="{{ selected_cash_ledger_name }}" selected>{{ selected_cash_ledger_name }}</option>{% endif %} {% if selected_cash_ledger_name %}<option value="{{ selected_cash_ledger_name }}" selected>{{ selected_cash_ledger_name }}</option>{% endif %}
</select> </select>
<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</button> <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 from Tally</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</button>
</div> </div>
<p id="cash-ledger-help" class="mt-1 text-xs text-slate-500">Loads cached Tally ledgers instantly when available. Refresh from Tally runs safely in the Local Agent background and shows progress here and on the Local Agent dashboard.</p> <p id="cash-ledger-help" class="mt-1 text-xs text-slate-500">By default only ledgers classified under Cash-in-Hand are shown. Choose “All Tally ledgers” only when you need to manually select a ledger that Tally has grouped differently.</p>
<div id="cash-ledger-progress" class="mt-2 hidden rounded-xl border border-sky-200 bg-sky-50 p-3"> <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 ledger sync…</span><span id="cash-ledger-progress-pct">0%</span></div> <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 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>
</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">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> <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>
<div class="md:col-span-2 xl:col-span-5"><button type="button" id="cash-review-button" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white" {% if not selected_client %}disabled{% endif %}>Extract / Refresh &amp; Analyze</button><p class="mt-2 text-xs text-slate-500">Only vouchers containing the confirmed Cash ledger are stored in the cash-review cache. Extraction runs in the Local Agent background; the same graphical progress is visible here and on the Local Agent dashboard.</p></div> <div class="md:col-span-2 xl:col-span-5"><button type="button" id="cash-review-button" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white" {% if not selected_client %}disabled{% endif %}>Review Cash Transactions</button><p class="mt-2 text-xs text-slate-500">Select the Tally company, choose a Cash-in-Hand ledger, and review its cash transactions for the selected period. The existing background extraction, threshold analysis and dual VPS/Local Agent progress are preserved.</p></div>
</form> </form>
</div> </div>
@@ -217,7 +221,11 @@
let activeDateFrom = ''; let activeDateFrom = '';
const ledgerButton = document.getElementById('cash-load-ledgers'); const ledgerButton = document.getElementById('cash-load-ledgers');
const ledgerSelect = document.getElementById('cash-ledger-select'); const ledgerSelect = document.getElementById('cash-ledger-select');
const ledgerScope = document.getElementById('cash-ledger-scope');
const ledgerHelp = document.getElementById('cash-ledger-help'); const ledgerHelp = document.getElementById('cash-ledger-help');
let allLedgerRows = [];
let cashLedgerRows = [];
let lastLedgerMeta = {ledger_count:0, company_name:'', from_cache:false};
const ledgerRefreshButton = document.getElementById('cash-refresh-ledgers'); const ledgerRefreshButton = document.getElementById('cash-refresh-ledgers');
const ledgerProgress = document.getElementById('cash-ledger-progress'); const ledgerProgress = document.getElementById('cash-ledger-progress');
@@ -226,18 +234,39 @@
const ledgerProgressStage = document.getElementById('cash-ledger-progress-stage'); const ledgerProgressStage = document.getElementById('cash-ledger-progress-stage');
let ledgerPollTimer = null; let ledgerPollTimer = null;
function populateLedgers(data) { function renderLedgerOptions() {
const previous = ledgerSelect.value; const previous = ledgerSelect.value;
ledgerSelect.innerHTML = '<option value="">Confirm Cash ledger</option>'; const rows = (ledgerScope && ledgerScope.value === 'all') ? allLedgerRows : cashLedgerRows;
(data.ledgers || []).forEach(function(row){ ledgerSelect.innerHTML = '<option value="">Select Cash ledger</option>';
rows.forEach(function(row){
const opt=document.createElement('option'); const opt=document.createElement('option');
opt.value=row.name; opt.value=row.name;
opt.textContent=row.name + (row.parent ? ' — '+row.parent : '') + (row.is_cash_candidate ? ' • Cash candidate' : ''); opt.textContent=row.name + (row.parent ? ' — '+row.parent : '') + (row.is_cash_candidate ? ' • Cash-in-Hand' : '');
if ((previous && previous===row.name) || (!previous && row.is_cash_candidate && !ledgerSelect.value)) opt.selected=true; if (previous && previous===row.name) opt.selected=true;
ledgerSelect.appendChild(opt); ledgerSelect.appendChild(opt);
}); });
if (!ledgerSelect.value && (data.cash_candidates||[]).length===1) ledgerSelect.value=data.cash_candidates[0].name; if (previous && !rows.some(row => row.name === previous)) ledgerSelect.value='';
ledgerHelp.textContent = String(data.ledger_count || 0)+' ledger(s) ready from '+(data.company_name || 'Tally')+(data.from_cache?' (local cache).':'.')+' Confirm the Cash ledger before extraction.'; const company = lastLedgerMeta.company_name || 'Tally';
if (ledgerScope && ledgerScope.value === 'all') {
ledgerHelp.textContent = String(rows.length)+' of '+String(lastLedgerMeta.ledger_count || allLedgerRows.length)+' ledger(s) available from '+company+'. Select the required ledger manually.';
} else if (rows.length) {
ledgerHelp.textContent = String(rows.length)+' Cash-in-Hand ledger(s) found in '+company+'. Select the ledger whose transactions should be reviewed.';
} else if (allLedgerRows.length) {
ledgerHelp.textContent = 'No Cash-in-Hand ledger was identified from Tally grouping. Choose “All Tally ledgers — manual selection” to select the required ledger.';
} else {
ledgerHelp.textContent = 'No ledgers are available yet for the selected Tally company.';
}
}
function populateLedgers(data) {
allLedgerRows = Array.isArray(data.ledgers) ? data.ledgers.slice() : [];
cashLedgerRows = Array.isArray(data.cash_candidates) ? data.cash_candidates.slice() : allLedgerRows.filter(row => row.is_cash_candidate);
lastLedgerMeta = {
ledger_count: Number(data.ledger_count || allLedgerRows.length || 0),
company_name: String(data.company_name || ''),
from_cache: Boolean(data.from_cache)
};
renderLedgerOptions();
} }
function updateLedgerProgress(job) { function updateLedgerProgress(job) {
@@ -299,11 +328,21 @@
if (ledgerButton) ledgerButton.addEventListener('click', function(){loadCashLedgers(false);}); if (ledgerButton) ledgerButton.addEventListener('click', function(){loadCashLedgers(false);});
if (ledgerRefreshButton) ledgerRefreshButton.addEventListener('click', function(){loadCashLedgers(true);}); if (ledgerRefreshButton) ledgerRefreshButton.addEventListener('click', function(){loadCashLedgers(true);});
const companySelect = document.getElementById('cash-tally-company'); const companySelect = document.getElementById('cash-tally-company');
if (ledgerScope) ledgerScope.addEventListener('change', function(){ renderLedgerOptions(); });
if (companySelect) companySelect.addEventListener('change', function(){ if (companySelect) companySelect.addEventListener('change', function(){
ledgerSelect.innerHTML = '<option value="">Load and confirm cash ledger</option>'; allLedgerRows = []; cashLedgerRows = []; lastLedgerMeta = {ledger_count:0, company_name:'', from_cache:false};
ledgerHelp.textContent = this.value ? 'Company changed. Click Load to use that company's cached ledgers, or Refresh from Tally to re-sync.' : 'Select an open Tally company first.'; ledgerSelect.innerHTML = '<option value="">Select Cash ledger</option>';
ledgerProgress.classList.add('hidden'); ledgerProgress.classList.add('hidden');
if (this.value) {
ledgerHelp.textContent = 'Loading Cash-in-Hand ledgers for the selected Tally company…';
loadCashLedgers(false);
} else {
ledgerHelp.textContent = 'Select an open Tally company first.';
}
}); });
if (companySelect && companySelect.value) {
window.setTimeout(function(){ loadCashLedgers(false); }, 250);
}
function show() { function show() {
overlay.classList.remove('hidden'); overlay.classList.remove('hidden');
@@ -373,7 +412,7 @@
return; return;
} }
if (job.status === 'paused' || job.status === 'failed') { if (job.status === 'paused' || job.status === 'failed') {
fail(job.error || 'Tally extraction paused. Restart/verify TallyPrime and click Extract / Refresh & Analyze again to resume.'); fail(job.error || 'Tally extraction paused. Restart/verify TallyPrime and click Review Cash Transactions again to resume.');
return; return;
} }
pollTimer = window.setTimeout(() => poll(jobId, clientId, dateFrom, tallyGuid, dateTo), 2000); pollTimer = window.setTimeout(() => poll(jobId, clientId, dateFrom, tallyGuid, dateTo), 2000);