diff --git a/api/app/services/anaf_service.py b/api/app/services/anaf_service.py
index d0c37f5..993bc08 100644
--- a/api/app/services/anaf_service.py
+++ b/api/app/services/anaf_service.py
@@ -82,8 +82,8 @@ async def _call_anaf_api(body: list[dict], retry: int = 0) -> dict[str, dict]:
# Parse ANAF response
found_list = data.get("found", [])
for item in found_list:
- cui_str = str(item.get("cui", ""))
date_generals = item.get("date_generale", {})
+ cui_str = str(date_generals.get("cui", ""))
results[cui_str] = {
"scpTVA": item.get("inregistrare_scop_Tva", {}).get("scpTVA"),
"denumire_anaf": date_generals.get("denumire", ""),
@@ -91,9 +91,10 @@ async def _call_anaf_api(body: list[dict], retry: int = 0) -> dict[str, dict]:
}
# Not found CUIs
- notfound_list = data.get("notfound", [])
+ notfound_list = data.get("notFound", [])
for item in notfound_list:
- cui_str = str(item.get("cui", ""))
+ date_gen = item.get("date_generale", {})
+ cui_str = str(date_gen.get("cui", item.get("cui", "")))
results[cui_str] = {
"scpTVA": None,
"denumire_anaf": "",
diff --git a/api/app/services/import_service.py b/api/app/services/import_service.py
index a8e132a..58afdef 100644
--- a/api/app/services/import_service.py
+++ b/api/app/services/import_service.py
@@ -365,6 +365,8 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se
cur.execute("SELECT strada, numar, localitate, judet FROM vadrese_parteneri WHERE id_adresa = :1", [int(addr_fact_id)])
row = cur.fetchone()
result["adresa_facturare_roa"] = {"strada": row[0], "numar": row[1], "localitate": row[2], "judet": row[3]} if row else None
+ elif addr_fact_id and addr_fact_id == addr_livr_id:
+ result["adresa_facturare_roa"] = result.get("adresa_livrare_roa")
# Step 4: Build articles JSON and import order
articles_json = build_articles_json(order.items, order, app_settings)
diff --git a/api/app/services/sqlite_service.py b/api/app/services/sqlite_service.py
index 059e216..f04a23d 100644
--- a/api/app/services/sqlite_service.py
+++ b/api/app/services/sqlite_service.py
@@ -696,6 +696,8 @@ async def get_orders(page: int = 1, per_page: int = 50,
if status_filter and status_filter not in ("all", "UNINVOICED"):
if status_filter.upper() == "IMPORTED":
data_clauses.append("UPPER(status) IN ('IMPORTED', 'ALREADY_IMPORTED')")
+ elif status_filter.upper() == "DIFFS":
+ data_clauses.append("(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1)")
else:
data_clauses.append("UPPER(status) = ?")
data_params.append(status_filter.upper())
@@ -749,6 +751,14 @@ async def get_orders(page: int = 1, per_page: int = 50,
cursor = await db.execute(f"SELECT COUNT(*) FROM orders {uninv_old_where}", base_params)
uninvoiced_old = (await cursor.fetchone())[0]
+ # Diffs count: orders with ANAF adjustments
+ diffs_clauses = list(base_clauses) + [
+ "(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1)"
+ ]
+ diffs_where = "WHERE " + " AND ".join(diffs_clauses)
+ cursor = await db.execute(f"SELECT COUNT(*) FROM orders {diffs_where}", base_params)
+ diffs_count = (await cursor.fetchone())[0]
+
return {
"orders": [dict(r) for r in rows],
"total": total,
@@ -765,6 +775,7 @@ async def get_orders(page: int = 1, per_page: int = 50,
"total": sum(status_counts.values()),
"uninvoiced_sqlite": uninvoiced_sqlite,
"uninvoiced_old": uninvoiced_old,
+ "diffs": diffs_count,
}
}
finally:
@@ -1072,6 +1083,35 @@ async def bulk_populate_anaf_cache(results: dict[str, dict]):
await db.close()
+async def get_expired_cuis_for_prepopulate() -> list[str]:
+ """Get CUIs from recent orders that need ANAF cache refresh."""
+ from ..services import anaf_service
+ db = await get_sqlite()
+ try:
+ cursor = await db.execute("""
+ SELECT DISTINCT cod_fiscal_gomag FROM orders
+ WHERE cod_fiscal_gomag IS NOT NULL
+ AND cod_fiscal_gomag != ''
+ AND order_date >= date('now', '-3 months')
+ """)
+ rows = await cursor.fetchall()
+
+ cuis_to_check = []
+ for row in rows:
+ raw = row["cod_fiscal_gomag"]
+ bare = anaf_service.strip_ro_prefix(raw)
+ if not anaf_service.validate_cui(bare):
+ continue
+ # Check if cache is valid
+ cached = await get_anaf_cache(bare)
+ if cached is None:
+ cuis_to_check.append(bare)
+
+ return cuis_to_check
+ finally:
+ await db.close()
+
+
# ── Partner/Address Data on Orders ─────────────────
async def update_order_partner_data(order_number: str, partner_data: dict):
diff --git a/api/app/services/sync_service.py b/api/app/services/sync_service.py
index 7f2eac4..c7a15f2 100644
--- a/api/app/services/sync_service.py
+++ b/api/app/services/sync_service.py
@@ -638,20 +638,20 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
0, len(truly_importable),
{"imported": 0, "skipped": skipped_count, "errors": 0, "already_imported": already_imported_count})
- # ANAF cache pre-population check
+ # ANAF cache pre-population: CUIs from last 3 months with expired/missing cache
try:
- db_check = await sqlite_service.get_sqlite()
- try:
- cursor = await db_check.execute("SELECT COUNT(*) FROM anaf_cache WHERE checked_at > datetime('now', '-7 days')")
- row = await cursor.fetchone()
- cache_count = row[0] if row else 0
- finally:
- await db_check.close()
-
- if cache_count < 10:
- _log_line(run_id, "ANAF pre-populare cache...")
+ prepop_cuis = await sqlite_service.get_expired_cuis_for_prepopulate()
+ if prepop_cuis:
+ _log_line(run_id, f"ANAF pre-populare: {len(prepop_cuis)} CUI-uri cu cache expirat")
+ prepop_results = await anaf_service.check_vat_status_batch(prepop_cuis)
+ if prepop_results:
+ await sqlite_service.bulk_populate_anaf_cache(prepop_results)
+ _log_line(run_id, f"ANAF pre-populare: {len(prepop_results)} rezultate stocate")
+ else:
+ _log_line(run_id, "ANAF pre-populare: cache complet")
except Exception as e:
- logger.warning(f"ANAF cache pre-population check failed: {e}")
+ _log_line(run_id, f"ANAF pre-populare eroare: {e}")
+ logger.warning(f"ANAF cache pre-population failed: {e}")
# Step 4: ANAF batch verification for company CUIs
company_cuis = set()
diff --git a/api/app/static/css/style.css b/api/app/static/css/style.css
index 1478e9c..958c000 100644
--- a/api/app/static/css/style.css
+++ b/api/app/static/css/style.css
@@ -451,6 +451,7 @@ input[type="checkbox"] {
.fc-neutral { color: var(--text-muted); }
.fc-blue { color: var(--info); }
.fc-dark { color: var(--text-secondary); }
+.fc-orange { color: var(--accent); }
/* ── Log viewer (dark theme — keep as-is) ────────── */
.log-viewer {
@@ -1155,6 +1156,66 @@ tr.mapping-deleted td {
.anaf-badge-ok { background: var(--success-light); color: var(--success-text); }
.anaf-badge-warn { background: var(--warning-light); color: var(--warning-text); }
.anaf-badge-gray { background: var(--cancelled-light); color: var(--text-muted); }
+
+/* ── Compact order detail layout ──────────────── */
+.detail-col-label {
+ font-family: var(--font-display);
+ font-size: 12px;
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ color: var(--text-muted);
+ margin-bottom: 4px;
+}
+.detail-client-name {
+ font-family: var(--font-body);
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin-bottom: 2px;
+}
+.detail-cui-line {
+ font-size: 13px;
+ margin-bottom: 2px;
+}
+.detail-roa-id {
+ font-size: 13px;
+ color: var(--text-secondary);
+}
+
+/* Address compact lines */
+.addr-line {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 2px 0;
+ font-size: 13px;
+ font-family: var(--font-body);
+}
+.addr-line-label {
+ flex-shrink: 0;
+ width: 120px;
+ font-size: 12px;
+ color: var(--text-muted);
+ font-family: var(--font-body);
+}
+.addr-line-text {
+ flex: 1;
+ min-width: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: var(--text-primary);
+}
+.addr-line .bi-check-lg {
+ color: var(--success);
+ flex-shrink: 0;
+}
+.addr-line .bi-exclamation-triangle {
+ color: var(--warning);
+ flex-shrink: 0;
+ filter: drop-shadow(0 0 3px rgba(202,138,4,0.3));
+}
.addr-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.addr-table th {
font-family: var(--font-display);
diff --git a/api/app/static/js/dashboard.js b/api/app/static/js/dashboard.js
index c98ec26..9bb7a1a 100644
--- a/api/app/static/js/dashboard.js
+++ b/api/app/static/js/dashboard.js
@@ -329,6 +329,7 @@ async function loadDashOrders() {
if (el('cntFact')) el('cntFact').textContent = c.facturate || 0;
if (el('cntNef')) el('cntNef').textContent = c.nefacturate || c.uninvoiced || 0;
if (el('cntCanc')) el('cntCanc').textContent = c.cancelled || 0;
+ if (el('cntDiff')) el('cntDiff').textContent = c.diffs || 0;
// Attention card
const attnEl = document.getElementById('attentionCard');
@@ -336,10 +337,11 @@ async function loadDashOrders() {
const errors = c.error || 0;
const unmapped = c.unresolved_skus || 0;
const nefact = c.nefacturate || 0;
+ const diffs = c.diffs || 0;
const incompleteAddr = c.incomplete_addresses || 0;
- if (errors === 0 && unmapped === 0 && nefact === 0 && incompleteAddr === 0) {
+ if (errors === 0 && unmapped === 0 && nefact === 0 && incompleteAddr === 0 && diffs === 0) {
attnEl.innerHTML = '
Totul in ordine
';
} else {
let items = [];
@@ -347,6 +349,7 @@ async function loadDashOrders() {
if (unmapped > 0) items.push(` ${unmapped} SKU-uri nemapate`);
if (nefact > 0) items.push(` ${nefact} nefacturate`);
if (c.incomplete_addresses > 0) items.push(` ${c.incomplete_addresses} adrese incomplete`);
+ if (diffs > 0) items.push(` ${diffs} diferente ANAF`);
attnEl.innerHTML = '' + items.join('') + '
';
}
}
@@ -410,7 +413,8 @@ async function loadDashOrders() {
{ label: 'Erori', count: c.error || c.errors || 0, value: 'ERROR', active: activeStatus === 'ERROR', colorClass: 'fc-red' },
{ label: 'Fact.', count: c.facturate || 0, value: 'INVOICED', active: activeStatus === 'INVOICED', colorClass: 'fc-green' },
{ label: 'Nefact.', count: c.nefacturate || c.uninvoiced || 0, value: 'UNINVOICED', active: activeStatus === 'UNINVOICED', colorClass: 'fc-red' },
- { label: 'Anulate', count: c.cancelled || 0, value: 'CANCELLED', active: activeStatus === 'CANCELLED', colorClass: 'fc-dark' }
+ { label: 'Anulate', count: c.cancelled || 0, value: 'CANCELLED', active: activeStatus === 'CANCELLED', colorClass: 'fc-dark' },
+ { label: 'Dif.', count: c.diffs || 0, value: 'DIFFS', active: activeStatus === 'DIFFS', colorClass: 'fc-orange' }
], (val) => {
document.querySelectorAll('.filter-pill[data-status]').forEach(b => b.classList.remove('active'));
const pill = document.querySelector(`.filter-pill[data-status="${val}"]`);
diff --git a/api/app/static/js/shared.js b/api/app/static/js/shared.js
index 76d041b..460cb7f 100644
--- a/api/app/static/js/shared.js
+++ b/api/app/static/js/shared.js
@@ -501,8 +501,6 @@ async function renderOrderDetailModal(orderNumber, opts) {
document.getElementById('detailStatus').innerHTML = '';
document.getElementById('detailIdComanda').textContent = '-';
document.getElementById('detailIdPartener').textContent = '-';
- document.getElementById('detailIdAdresaFact').textContent = '-';
- document.getElementById('detailIdAdresaLivr').textContent = '-';
document.getElementById('detailItemsBody').innerHTML = '| Se incarca... |
';
document.getElementById('detailError').style.display = 'none';
const retryBtn = document.getElementById('detailRetryBtn');
@@ -519,15 +517,21 @@ async function renderOrderDetailModal(orderNumber, opts) {
if (priceCheckEl) priceCheckEl.innerHTML = '';
const reconEl = document.getElementById('detailInvoiceRecon');
if (reconEl) { reconEl.innerHTML = ''; reconEl.style.display = 'none'; }
- // Reset partner/address sections
- const partnerSection = document.getElementById('detailPartnerSection');
- if (partnerSection) partnerSection.style.display = 'none';
- const addressSection = document.getElementById('detailAddressSection');
- if (addressSection) addressSection.style.display = 'none';
- const partnerBody = document.getElementById('partnerInfoBody');
- if (partnerBody) partnerBody.innerHTML = '';
- const addressBody = document.getElementById('addressInfoBody');
- if (addressBody) addressBody.innerHTML = '';
+ // Reset compact header elements
+ const partenerRoa = document.getElementById('detailPartenerRoa');
+ if (partenerRoa) { partenerRoa.style.display = 'none'; partenerRoa.textContent = ''; }
+ const cuiGomag = document.getElementById('detailCuiGomag');
+ if (cuiGomag) cuiGomag.style.display = 'none';
+ const cuiRoa = document.getElementById('detailCuiRoa');
+ if (cuiRoa) cuiRoa.style.display = 'none';
+ const anafArea = document.getElementById('detailPartnerAnafArea');
+ if (anafArea) anafArea.innerHTML = '';
+ const denomMismatch = document.getElementById('detailDenomMismatch');
+ if (denomMismatch) { denomMismatch.style.display = 'none'; denomMismatch.innerHTML = ''; }
+ const addressBlock = document.getElementById('detailAddressBlock');
+ if (addressBlock) addressBlock.style.display = 'none';
+ const addressLines = document.getElementById('detailAddressLines');
+ if (addressLines) addressLines.innerHTML = '';
const modalEl = document.getElementById('orderDetailModal');
const existing = bootstrap.Modal.getInstance(modalEl);
@@ -563,8 +567,6 @@ async function renderOrderDetailModal(orderNumber, opts) {
document.getElementById('detailIdComanda').textContent = order.id_comanda || '-';
document.getElementById('detailIdPartener').textContent = order.id_partener || '-';
- document.getElementById('detailIdAdresaFact').textContent = order.id_adresa_facturare || '-';
- document.getElementById('detailIdAdresaLivr').textContent = order.id_adresa_livrare || '-';
// Invoice info
const inv = order.invoice;
@@ -592,9 +594,8 @@ async function renderOrderDetailModal(orderNumber, opts) {
reconEl.style.display = 'none';
}
- // Render partner + address sections
- _renderPartnerSection(order);
- _renderAddressSection(order);
+ // Render compact header info (partner + addresses)
+ _renderHeaderInfo(order);
if (order.error_message) {
document.getElementById('detailError').textContent = order.error_message;
@@ -831,197 +832,161 @@ function statusDot(status) {
}
}
-// ── Partner & Address Section Rendering ──────────
+// ── Address helpers (module scope) ───────────────
-function _renderPartnerSection(order) {
- const section = document.getElementById('detailPartnerSection');
- const body = document.getElementById('partnerInfoBody');
- const alertEl = document.getElementById('partnerAlertCount');
- if (!section || !body) return;
+function fmtAddr(a) {
+ if (!a) return '\u2014';
+ if (typeof a === 'string') return a;
+ const parts = [a.address || a.strada || '', a.numar || ''].filter(Boolean);
+ const line1 = parts.join(' ').trim();
+ const line2 = [a.city || a.localitate || '', a.region || a.judet || ''].filter(Boolean).join(', ');
+ return [line1, line2].filter(Boolean).join(', ');
+}
+function addrMatch(gomag, roa) {
+ if (!gomag || !roa) return true; // can't compare
+ function norm(s) {
+ return (s || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '')
+ .toUpperCase()
+ .replace(/\b(STR|NR|BL|SC|AP|ET|ETAJ|APART)\b/g, '')
+ .replace(/[^A-Z0-9]/g, '');
+ }
+ const gStreet = norm(gomag.address || gomag.strada || '');
+ const rStreet = norm((roa.strada || '') + (roa.numar || ''));
+ const gCity = norm(gomag.city || gomag.localitate || '');
+ const rCity = norm(roa.localitate || '');
+ const gRegion = norm(gomag.region || gomag.judet || '');
+ const rRegion = norm(roa.judet || '');
+ return gStreet === rStreet && gCity === rCity && gRegion === rRegion;
+}
+
+function hasEfacturaRisk(roa) {
+ if (!roa || typeof roa === 'string') return false;
+ return !roa.judet || !roa.localitate;
+}
+
+// ── Compact Header Info Rendering ────────────────
+
+function _renderHeaderInfo(order) {
const pi = order.partner_info;
- if (!pi || !pi.cod_fiscal_gomag) {
- section.style.display = 'none';
- return;
- }
+ const isPJ = pi && pi.cod_fiscal_gomag;
- section.style.display = '';
- let alertCount = 0;
- if (pi.anaf_cod_fiscal_adjusted) alertCount++;
- if (pi.anaf_denumire_mismatch) alertCount++;
-
- if (alertEl) {
- if (alertCount > 0) {
- alertEl.textContent = alertCount + (alertCount === 1 ? ' alerta' : ' alerte');
- alertEl.style.display = '';
- } else {
- alertEl.style.display = 'none';
+ // GoMag CUI (PJ only)
+ if (isPJ) {
+ const cuiGomagEl = document.getElementById('detailCuiGomag');
+ if (cuiGomagEl) {
+ document.getElementById('detailCuiGomagVal').textContent = pi.cod_fiscal_gomag;
+ cuiGomagEl.style.display = '';
}
}
- // ANAF badge
- let anafBadge;
- if (pi.anaf_platitor_tva === 1) {
- anafBadge = 'Platitor TVA';
- } else if (pi.anaf_platitor_tva === 0) {
- anafBadge = 'Neplatitor TVA';
- } else {
- anafBadge = 'Neverificat';
- }
-
- // CUI correction badge
- let cuiCorrBadge = '';
- if (pi.anaf_cod_fiscal_adjusted) {
- cuiCorrBadge = ' Corectat ANAF';
- }
-
- // Denomination mismatch
- let denomHtml = '';
- if (pi.anaf_denumire_mismatch && pi.denumire_anaf) {
- denomHtml = `
- Denumire diferita
- GoMag: ${esc(order.customer_name || '')}
- ANAF: ${esc(pi.denumire_anaf)}
-
`;
- }
-
- body.innerHTML = `
-
-
-
CUI GoMag
-
${esc(pi.cod_fiscal_gomag)}
-
-
-
CUI ROA
-
${esc(pi.cod_fiscal_roa || '-')}${cuiCorrBadge}
-
-
-
Partener ROA
-
${esc(pi.denumire_roa || '-')}
-
-
-
- ${denomHtml}`;
-
- // Auto-expand on mismatch
- if (alertCount > 0) {
- const collapseEl = document.getElementById('detailPartnerInfo');
- if (collapseEl && !collapseEl.classList.contains('show')) {
- new bootstrap.Collapse(collapseEl, { show: true });
+ // ROA column
+ if (isPJ && pi.denumire_roa) {
+ const partenerRoa = document.getElementById('detailPartenerRoa');
+ if (partenerRoa) {
+ partenerRoa.textContent = pi.denumire_roa;
+ partenerRoa.style.display = '';
}
}
-}
-function _renderAddressSection(order) {
- const section = document.getElementById('detailAddressSection');
- const body = document.getElementById('addressInfoBody');
- const alertEl = document.getElementById('addressAlertCount');
- if (!section || !body) return;
+ if (isPJ) {
+ const cuiRoaEl = document.getElementById('detailCuiRoa');
+ if (cuiRoaEl) {
+ document.getElementById('detailCuiRoaVal').textContent = pi.cod_fiscal_roa || '\u2014';
+ cuiRoaEl.style.display = '';
+ // CUI correction badge
+ let cuiCorrHtml = '';
+ if (pi.anaf_cod_fiscal_adjusted) {
+ cuiCorrHtml = ' Corectat';
+ }
+
+ // ANAF badge
+ const anafArea = document.getElementById('detailPartnerAnafArea');
+ if (anafArea) {
+ let anafBadge;
+ if (pi.anaf_platitor_tva === 1) {
+ anafBadge = 'Platitor TVA';
+ } else if (pi.anaf_platitor_tva === 0) {
+ anafBadge = 'Neplatitor TVA';
+ } else {
+ anafBadge = '?';
+ }
+ anafArea.innerHTML = cuiCorrHtml + ' ' + anafBadge;
+ }
+ }
+ }
+
+ // ERROR orders: muted dashes for ROA fields
+ if (order.status === 'ERROR' && !order.id_comanda) {
+ document.getElementById('detailIdComanda').innerHTML = '\u2014';
+ document.getElementById('detailIdPartener').innerHTML = '\u2014';
+ }
+
+ // Denomination mismatch alert
+ if (isPJ && pi.anaf_denumire_mismatch && pi.denumire_anaf) {
+ const denomEl = document.getElementById('detailDenomMismatch');
+ if (denomEl) {
+ denomEl.innerHTML = `
+ Denumire diferita
+ GoMag: ${esc(order.customer_name || '')}
+ ANAF: ${esc(pi.denumire_anaf)}
+
`;
+ denomEl.style.display = '';
+ }
+ }
+
+ // Compact address lines
const addr = order.addresses;
- if (!addr || (!addr.livrare_gomag && !addr.facturare_gomag)) {
- section.style.display = 'none';
- return;
- }
+ if (!addr || (!addr.livrare_gomag && !addr.facturare_gomag)) return;
- section.style.display = '';
- let mismatchCount = 0;
+ const addressBlock = document.getElementById('detailAddressBlock');
+ const addressLines = document.getElementById('detailAddressLines');
+ if (!addressBlock || !addressLines) return;
- function fmtAddr(a) {
- if (!a) return '-';
- if (typeof a === 'string') return esc(a);
- const parts = [a.address || a.strada || '', a.numar || ''].filter(Boolean);
- const line1 = parts.join(' ').trim();
- const line2 = [a.city || a.localitate || '', a.region || a.judet || ''].filter(Boolean).join(', ');
- return esc(line1) + (line2 ? '
' + esc(line2) : '');
- }
+ addressBlock.style.display = '';
+ let html = '';
- function addrMatch(gomag, roa) {
- if (!gomag || !roa) return true; // can't compare
- const g = JSON.stringify(gomag).toUpperCase().replace(/[^A-Z0-9]/g, '');
- const r = JSON.stringify(roa).toUpperCase().replace(/[^A-Z0-9]/g, '');
- return g === r;
- }
-
- function hasEfacturaRisk(roa) {
- if (!roa || typeof roa === 'string') return false;
- return !roa.judet || !roa.localitate;
- }
-
- const livrMatch = addrMatch(addr.livrare_gomag, addr.livrare_roa);
- const factMatch = addrMatch(addr.facturare_gomag, addr.facturare_roa);
- if (!livrMatch) mismatchCount++;
- if (!factMatch) mismatchCount++;
-
- const livrRisk = hasEfacturaRisk(addr.livrare_roa);
- const factRisk = hasEfacturaRisk(addr.facturare_roa);
-
- if (alertEl) {
- if (mismatchCount > 0) {
- alertEl.textContent = mismatchCount + (mismatchCount === 1 ? ' diferenta' : ' diferente');
- alertEl.style.display = '';
- } else {
- alertEl.style.display = 'none';
+ function addrLine(label, addrObj, matchIcon) {
+ const text = fmtAddr(addrObj);
+ const escaped = esc(text);
+ let icon = '';
+ if (matchIcon === 'match') {
+ icon = ' ';
+ } else if (matchIcon === 'mismatch') {
+ icon = ' ';
+ } else if (matchIcon === 'risk') {
+ icon = ' ';
}
- }
-
- // Desktop: 2-column table
- const livrClass = livrRisk ? 'addr-efactura-risk' : (!livrMatch ? 'addr-mismatch' : '');
- const factClass = factRisk ? 'addr-efactura-risk' : (!factMatch ? 'addr-mismatch' : '');
-
- const desktopHtml = `
-
- | GOMAG | ROA |
-
-
- LIVRARE${livrRisk ? ' ⚠ Risc eFactura' : ''} |
- ${fmtAddr(addr.livrare_gomag)} |
- ${fmtAddr(addr.livrare_roa)} |
-
-
- FACTURARE${factRisk ? ' ⚠ Risc eFactura' : ''} |
- ${fmtAddr(addr.facturare_gomag)} |
- ${fmtAddr(addr.facturare_roa)} |
-
-
-
`;
-
- // Mobile: stacked cards
- function mobileCard(label, gomag, roa, isMatch, isRisk) {
- const cls = isRisk ? ' addr-efactura-risk' : (!isMatch ? ' mismatch' : ' match');
- const matchLabel = isMatch ? '✓ Adrese identice
' : '';
- const riskLabel = isRisk ? '⚠ Risc eFactura
' : '';
- return `
-
-
-
GoMag:
-
${fmtAddr(gomag)}
-
-
-
ROA:
-
${fmtAddr(roa)}
-
- ${matchLabel}${riskLabel}
+ return `
+ ${label}
+ ${escaped}${icon}
`;
}
- const mobileHtml = `
- ${mobileCard('LIVRARE', addr.livrare_gomag, addr.livrare_roa, livrMatch, livrRisk)}
- ${mobileCard('FACTURARE', addr.facturare_gomag, addr.facturare_roa, factMatch, factRisk)}
-
`;
-
- body.innerHTML = desktopHtml + mobileHtml;
-
- // Auto-expand on mismatch
- if (mismatchCount > 0) {
- const collapseEl = document.getElementById('detailAddressInfo');
- if (collapseEl && !collapseEl.classList.contains('show')) {
- new bootstrap.Collapse(collapseEl, { show: true });
+ // Livrare
+ if (addr.livrare_gomag || addr.livrare_roa) {
+ html += addrLine('Livrare GoMag:', addr.livrare_gomag, null);
+ const livrRisk = hasEfacturaRisk(addr.livrare_roa);
+ const livrMatch = addrMatch(addr.livrare_gomag, addr.livrare_roa);
+ let matchType = null;
+ if (addr.livrare_roa) {
+ matchType = livrRisk ? 'risk' : (livrMatch ? 'match' : 'mismatch');
}
+ html += addrLine('Livrare ROA:', addr.livrare_roa, matchType);
}
+
+ // Facturare
+ if (addr.facturare_gomag || addr.facturare_roa) {
+ html += addrLine('Facturare GoMag:', addr.facturare_gomag, null);
+ const factRisk = hasEfacturaRisk(addr.facturare_roa);
+ const factMatch = addrMatch(addr.facturare_gomag, addr.facturare_roa);
+ let matchType = null;
+ if (addr.facturare_roa) {
+ matchType = factRisk ? 'risk' : (factMatch ? 'match' : 'mismatch');
+ }
+ html += addrLine('Facturare ROA:', addr.facturare_roa, matchType);
+ }
+
+ addressLines.innerHTML = html;
}
diff --git a/api/app/templates/base.html b/api/app/templates/base.html
index 1c3037b..39c7ff8 100644
--- a/api/app/templates/base.html
+++ b/api/app/templates/base.html
@@ -19,7 +19,7 @@
{% set rp = request.scope.get('root_path', '') %}
-
+
@@ -93,16 +93,26 @@
+
-
Client:
-
Data comanda:
-
Status:
+
GOMAG
+
...
+
+ CUI:
+
+
Data:
+
Status:
+
-
ID Comanda ROA: -
-
ID Partener: -
-
ID Adr. Facturare: -
-
ID Adr. Livrare: -
+
ROA
+
+
+ CUI:
+
+
+
ID Comanda: -
+
ID Partener: -
Factura:
din
@@ -110,31 +120,12 @@
-
-
-
-
-
-
+
+
+
+
@@ -170,7 +161,7 @@
-
+
+
{% endblock %}
diff --git a/api/tests/e2e/test_order_detail.py b/api/tests/e2e/test_order_detail.py
index c1421d9..068b99d 100644
--- a/api/tests/e2e/test_order_detail.py
+++ b/api/tests/e2e/test_order_detail.py
@@ -14,10 +14,10 @@ def test_order_detail_modal_has_roa_ids(page: Page, app_url: str):
expect(modal).to_be_attached()
modal_html = modal.inner_html()
- assert "ID Comanda ROA" in modal_html, "Missing 'ID Comanda ROA' label in order detail modal"
+ assert "ID Comanda" in modal_html, "Missing 'ID Comanda' label in order detail modal"
assert "ID Partener" in modal_html, "Missing 'ID Partener' label in order detail modal"
- assert "ID Adr. Facturare" in modal_html, "Missing 'ID Adr. Facturare' label in order detail modal"
- assert "ID Adr. Livrare" in modal_html, "Missing 'ID Adr. Livrare' label in order detail modal"
+ assert "GOMAG" in modal_html, "Missing 'GOMAG' column label in order detail modal"
+ assert "ROA" in modal_html, "Missing 'ROA' column label in order detail modal"
def test_order_detail_items_table_columns(page: Page, app_url: str):