feat(partner): detect and resync partner mismatches on already-imported orders
Detects PF↔PJ transitions and CUI changes after import; auto-resyncs uninvoiced orders (max 5/cycle) and shows visual alert for invoiced ones. - SQLite: partner_mismatch column + batch helpers - sync_service: detection loop + _resync_partner_for_order - dashboard: red dot + attention card indicator - modal: alert with contextual message and resync button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -531,6 +531,8 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
||||
// Restore original structure (may have been replaced by PF indicator)
|
||||
cuiRoa.innerHTML = '<small class="text-muted">CUI:</small> <span class="font-data" id="detailCuiRoaVal"></span><span id="detailPartnerAnafArea"></span>';
|
||||
}
|
||||
const partnerMismatchEl = document.getElementById('detailPartnerMismatch');
|
||||
if (partnerMismatchEl) { partnerMismatchEl.style.display = 'none'; partnerMismatchEl.innerHTML = ''; }
|
||||
const denomMismatch = document.getElementById('detailDenomMismatch');
|
||||
if (denomMismatch) { denomMismatch.style.display = 'none'; denomMismatch.innerHTML = ''; }
|
||||
const addressBlock = document.getElementById('detailAddressBlock');
|
||||
@@ -940,6 +942,33 @@ function _renderHeaderInfo(order) {
|
||||
document.getElementById('detailIdPartener').innerHTML = '<span class="text-muted">\u2014</span>';
|
||||
}
|
||||
|
||||
// Partner mismatch alert
|
||||
if (pi && pi.partner_mismatch) {
|
||||
const pmEl = document.getElementById('detailPartnerMismatch');
|
||||
if (pmEl) {
|
||||
const isInvoiced = !!(order.invoice && order.invoice.facturat);
|
||||
let mismatchType = '';
|
||||
if (pi.cod_fiscal_gomag && !pi.cod_fiscal_roa) {
|
||||
mismatchType = 'PF → PJ: comanda importata ca persoana fizica, acum are CUI in GoMag.';
|
||||
} else if (!pi.cod_fiscal_gomag && pi.cod_fiscal_roa) {
|
||||
mismatchType = 'PJ → PF: comanda importata cu CUI, acum GoMag nu mai are companie.';
|
||||
} else if (pi.cod_fiscal_gomag && pi.cod_fiscal_roa && pi.cod_fiscal_gomag !== pi.cod_fiscal_roa) {
|
||||
mismatchType = `CUI schimbat: GoMag are ${esc(pi.cod_fiscal_gomag)}, ROA are ${esc(pi.cod_fiscal_roa)}.`;
|
||||
} else {
|
||||
mismatchType = 'Date partener diferite fata de momentul importului.';
|
||||
}
|
||||
const resyncBtn = isInvoiced
|
||||
? `<button class="btn btn-sm btn-outline-warning mt-1" onclick="resyncPartner('${esc(order.order_number)}', this)"><i class="bi bi-person-check"></i> Actualizeaza partener in ROA</button>`
|
||||
: '';
|
||||
pmEl.innerHTML = `<div class="denom-mismatch" style="border-color:var(--error)">
|
||||
<span class="denom-mismatch-title" style="color:var(--error-text)"><i class="bi bi-people"></i> Partener schimbat in GoMag</span><br>
|
||||
<span style="font-size:13px">${mismatchType}</span>
|
||||
${resyncBtn}
|
||||
</div>`;
|
||||
pmEl.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Denomination mismatch alert
|
||||
if (isPJ && pi.anaf_denumire_mismatch && pi.denumire_anaf) {
|
||||
const denomEl = document.getElementById('detailDenomMismatch');
|
||||
@@ -1022,6 +1051,7 @@ function _renderHeaderInfo(order) {
|
||||
if (addr && addr.livrare_roa && !addrMatch(addr.livrare_gomag, addr.livrare_roa)) badges.push({label:'Adr. livr.', cls:'diff-badge-addr', aria:'Adresa livrare diferita'});
|
||||
if (addr && addr.facturare_roa && !addrMatch(addr.facturare_gomag, addr.facturare_roa)) badges.push({label:'Adr. fact.', cls:'diff-badge-addr', aria:'Adresa facturare diferita'});
|
||||
if (order.price_check && order.price_check.mismatches > 0) badges.push({label:'Preturi (' + order.price_check.mismatches + ')', cls:'diff-badge-price', aria:'Preturi diferite: ' + order.price_check.mismatches});
|
||||
if (pi && pi.partner_mismatch) badges.push({label:'Partener', cls:'diff-badge-anaf', aria:'Partener schimbat in GoMag'});
|
||||
let insertAfter = orderNumEl;
|
||||
badges.forEach(b => {
|
||||
const el = document.createElement('span');
|
||||
@@ -1033,3 +1063,24 @@ function _renderHeaderInfo(order) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Partner Resync ────────────────────────────────
|
||||
|
||||
async function resyncPartner(orderNumber, btnEl) {
|
||||
if (!confirm('Actualizeaza partenerul acestei comenzi in ROA Oracle?\n\nAtentie: Comanda este facturata. Verificati manual dupa actualizare.')) return;
|
||||
if (btnEl) { btnEl.disabled = true; btnEl.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Se actualizeaza...'; }
|
||||
try {
|
||||
const res = await fetch(`${window.ROOT_PATH || ''}/api/orders/${encodeURIComponent(orderNumber)}/resync-partner`, { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
if (btnEl) { btnEl.innerHTML = '<i class="bi bi-check-circle"></i> Actualizat'; btnEl.className = 'btn btn-sm btn-success mt-1'; }
|
||||
setTimeout(() => renderOrderDetailModal(orderNumber, {}), 1500);
|
||||
} else {
|
||||
if (btnEl) { btnEl.disabled = false; btnEl.innerHTML = '<i class="bi bi-person-check"></i> Actualizeaza partener in ROA'; }
|
||||
alert('Eroare: ' + (data.message || 'Resync esuat'));
|
||||
}
|
||||
} catch(e) {
|
||||
if (btnEl) { btnEl.disabled = false; btnEl.innerHTML = '<i class="bi bi-person-check"></i> Actualizeaza partener in ROA'; }
|
||||
alert('Eroare de retea: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user