feat(sync): already_imported tracking, invoice cache, path fixes, remove vfp

- Track already_imported/new_imported counts separately in sync_runs
  and surface them in status API + dashboard last-run card
- Cache invoice data in SQLite orders table (factura_* columns);
  dashboard falls back to Oracle only for uncached imported orders
- Resolve JSON_OUTPUT_DIR and SQLITE_DB_PATH relative to known
  anchored roots in config.py, independent of CWD (fixes WSL2 start)
- Use single Oracle connection for entire validation phase (perf)
- Batch upsert web_products instead of one-by-one
- Remove stale VFP scripts (replaced by gomag-vending.prg workflow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 00:15:37 +02:00
parent 8681a92eec
commit 2e65855fe2
24 changed files with 485 additions and 3458 deletions

View File

@@ -12,6 +12,7 @@ let qmAcTimeout = null;
let _pollInterval = null;
let _lastSyncStatus = null;
let _lastRunId = null;
let _currentRunId = null;
// ── Init ──────────────────────────────────────────
@@ -66,6 +67,13 @@ function updateSyncPanel(data) {
if (txt) txt.textContent = statusLabels[data.status] || data.status || 'Inactiv';
if (startBtn) startBtn.disabled = data.status === 'running';
// Track current running sync run_id
if (data.status === 'running' && data.run_id) {
_currentRunId = data.run_id;
} else {
_currentRunId = null;
}
// Live progress area
if (progressArea) {
progressArea.style.display = data.status === 'running' ? 'flex' : 'none';
@@ -84,7 +92,16 @@ function updateSyncPanel(data) {
const st = document.getElementById('lastSyncStatus');
if (d) d.textContent = lr.started_at ? lr.started_at.replace('T', ' ').slice(0, 16) : '\u2014';
if (dur) dur.textContent = lr.duration_seconds ? Math.round(lr.duration_seconds) + 's' : '\u2014';
if (cnt) cnt.textContent = '\u2191' + (lr.imported || 0) + ' \u2298' + (lr.skipped || 0) + ' \u2715' + (lr.errors || 0);
// Updated counts: ↑new =already ⊘skipped ✕errors
if (cnt) {
const newImp = lr.new_imported || 0;
const already = lr.already_imported || 0;
if (already > 0) {
cnt.textContent = '\u2191' + newImp + ' =' + already + ' \u2298' + (lr.skipped || 0) + ' \u2715' + (lr.errors || 0);
} else {
cnt.textContent = '\u2191' + (lr.imported || 0) + ' \u2298' + (lr.skipped || 0) + ' \u2715' + (lr.errors || 0);
}
}
if (st) {
st.textContent = lr.status === 'completed' ? '\u2713' : '\u2715';
st.style.color = lr.status === 'completed' ? '#10b981' : '#ef4444';
@@ -92,14 +109,16 @@ function updateSyncPanel(data) {
}
}
// Wire last-sync-row click → journal
// Wire last-sync-row click → journal (use current running sync if active)
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('lastSyncRow')?.addEventListener('click', () => {
if (_lastRunId) window.location = '/logs?run=' + _lastRunId;
const targetId = _currentRunId || _lastRunId;
if (targetId) window.location = '/logs?run=' + targetId;
});
document.getElementById('lastSyncRow')?.addEventListener('keydown', (e) => {
if ((e.key === 'Enter' || e.key === ' ') && _lastRunId) {
window.location = '/logs?run=' + _lastRunId;
const targetId = _currentRunId || _lastRunId;
if ((e.key === 'Enter' || e.key === ' ') && targetId) {
window.location = '/logs?run=' + targetId;
}
});
});
@@ -391,10 +410,11 @@ function fmtDate(dateStr) {
function orderStatusBadge(status) {
switch ((status || '').toUpperCase()) {
case 'IMPORTED': return '<span class="badge bg-success">Importat</span>';
case 'SKIPPED': return '<span class="badge bg-warning text-dark">Omis</span>';
case 'ERROR': return '<span class="badge bg-danger">Eroare</span>';
default: return `<span class="badge bg-secondary">${esc(status)}</span>`;
case 'IMPORTED': return '<span class="badge bg-success">Importat</span>';
case 'ALREADY_IMPORTED': return '<span class="badge bg-info">Deja importat</span>';
case 'SKIPPED': return '<span class="badge bg-warning text-dark">Omis</span>';
case 'ERROR': return '<span class="badge bg-danger">Eroare</span>';
default: return `<span class="badge bg-secondary">${esc(status)}</span>`;
}
}