Add async SQLite job manager for cash payment extraction
This commit is contained in:
@@ -152,7 +152,10 @@
|
||||
</div>
|
||||
<p class="mt-4 text-xs text-slate-500">Only one day is requested from TallyPrime at a time. The agent waits about 3 seconds after every successful day. If Tally responds slowly, it cools for 20 seconds and retries only once. The tunnel heartbeat stays responsive because progress is read from SQLite, not from Tally.</p>
|
||||
<div id="cash-progress-error" class="mt-4 hidden rounded-xl border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900"></div>
|
||||
<div class="mt-4 flex justify-end"><button type="button" id="cash-progress-close" class="hidden rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700">Close</button></div>
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<button type="button" id="cash-progress-cancel" class="hidden rounded-xl border border-amber-300 bg-amber-50 px-4 py-2 text-sm font-semibold text-amber-900">Cancel Extraction</button>
|
||||
<button type="button" id="cash-progress-close" class="hidden rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -170,13 +173,18 @@
|
||||
const currentDate = document.getElementById('cash-progress-date');
|
||||
const errorBox = document.getElementById('cash-progress-error');
|
||||
const closeButton = document.getElementById('cash-progress-close');
|
||||
const cancelButton = document.getElementById('cash-progress-cancel');
|
||||
let pollTimer = null;
|
||||
let activeJobId = '';
|
||||
let activeClientId = '';
|
||||
let activeDateFrom = '';
|
||||
|
||||
function show() {
|
||||
overlay.classList.remove('hidden');
|
||||
overlay.classList.add('flex');
|
||||
errorBox.classList.add('hidden');
|
||||
closeButton.classList.add('hidden');
|
||||
cancelButton.classList.add('hidden');
|
||||
}
|
||||
function stopPolling() {
|
||||
if (pollTimer) window.clearTimeout(pollTimer);
|
||||
@@ -189,6 +197,7 @@
|
||||
errorBox.textContent = message || 'The extraction did not complete.';
|
||||
errorBox.classList.remove('hidden');
|
||||
closeButton.classList.remove('hidden');
|
||||
cancelButton.classList.add('hidden');
|
||||
}
|
||||
function update(job) {
|
||||
const value = Math.max(0, Math.min(100, Number(job.percent || 0)));
|
||||
@@ -199,6 +208,7 @@
|
||||
vouchers.textContent = String(job.vouchers_cached || 0);
|
||||
requests.textContent = String(job.tally_requests || 0);
|
||||
currentDate.textContent = job.current_date || '—';
|
||||
if (job.can_cancel) cancelButton.classList.remove('hidden'); else cancelButton.classList.add('hidden');
|
||||
}
|
||||
async function poll(jobId, clientId, dateFrom, tallyGuid, dateTo) {
|
||||
try {
|
||||
@@ -224,6 +234,10 @@
|
||||
window.location.assign(target.toString());
|
||||
return;
|
||||
}
|
||||
if (job.status === 'cancelled') {
|
||||
fail('Extraction was cancelled safely. Already cached days remain available and can be resumed later.');
|
||||
return;
|
||||
}
|
||||
if (job.status === 'paused' || job.status === 'failed') {
|
||||
fail(job.error || 'Tally extraction paused. Restart/verify TallyPrime and click Extract / Refresh & Analyze again to resume.');
|
||||
return;
|
||||
@@ -245,12 +259,36 @@
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data.ok) throw new Error(data.error || 'Could not start extraction.');
|
||||
const job = data.job || {};
|
||||
activeJobId = job.job_id || '';
|
||||
activeClientId = String(fd.get('client_id') || '');
|
||||
activeDateFrom = String(fd.get('date_from') || '');
|
||||
update(job);
|
||||
poll(job.job_id, fd.get('client_id'), fd.get('date_from'), fd.get('tally_guid'), fd.get('date_to'));
|
||||
} catch (err) {
|
||||
fail(err && err.message ? err.message : String(err));
|
||||
}
|
||||
});
|
||||
cancelButton.addEventListener('click', async function () {
|
||||
if (!activeJobId || !activeClientId) return;
|
||||
cancelButton.disabled = true;
|
||||
text.textContent = 'Requesting a safe stop after the current Tally request…';
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.set('csrf_token', document.getElementById('cash-csrf-token').value);
|
||||
fd.set('client_id', activeClientId);
|
||||
fd.set('job_id', activeJobId);
|
||||
fd.set('date_from', activeDateFrom);
|
||||
const response = await fetch('/tools/accounting/cash-payments/cache/cancel', {method: 'POST', body: fd, headers: {'Accept': 'application/json'}});
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data.ok) throw new Error(data.error || 'Could not cancel extraction.');
|
||||
if (data.job) update(data.job);
|
||||
cancelButton.classList.add('hidden');
|
||||
} catch (err) {
|
||||
errorBox.textContent = err && err.message ? err.message : String(err);
|
||||
errorBox.classList.remove('hidden');
|
||||
cancelButton.disabled = false;
|
||||
}
|
||||
});
|
||||
closeButton.addEventListener('click', function () {
|
||||
overlay.classList.add('hidden'); overlay.classList.remove('flex');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user