fix(anaf-dedup): fix ANAF parsing, facturare addr, compact modal layout
- Fix ANAF API: extract CUI from date_generale (not top-level), fix notFound casing (capital F) - Fix missing facturare address when same ID as livrare (copy instead of skip) - Replace ANAF cache pre-population stub with real logic (3-month CUIs) - Restructure order detail modal: inline 2-col GOMAG|ROA layout with compact address lines replacing collapsed sections - Fix addrMatch() to use field-level comparison with Romanian abbreviation stripping (STR, NR, BL, SC, AP, ET, ETAJ, APART) - Add dashboard "Diferente" filter pill for ANAF-adjusted orders - Update e2e test for new modal structure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,8 +82,8 @@ async def _call_anaf_api(body: list[dict], retry: int = 0) -> dict[str, dict]:
|
|||||||
# Parse ANAF response
|
# Parse ANAF response
|
||||||
found_list = data.get("found", [])
|
found_list = data.get("found", [])
|
||||||
for item in found_list:
|
for item in found_list:
|
||||||
cui_str = str(item.get("cui", ""))
|
|
||||||
date_generals = item.get("date_generale", {})
|
date_generals = item.get("date_generale", {})
|
||||||
|
cui_str = str(date_generals.get("cui", ""))
|
||||||
results[cui_str] = {
|
results[cui_str] = {
|
||||||
"scpTVA": item.get("inregistrare_scop_Tva", {}).get("scpTVA"),
|
"scpTVA": item.get("inregistrare_scop_Tva", {}).get("scpTVA"),
|
||||||
"denumire_anaf": date_generals.get("denumire", ""),
|
"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
|
# Not found CUIs
|
||||||
notfound_list = data.get("notfound", [])
|
notfound_list = data.get("notFound", [])
|
||||||
for item in notfound_list:
|
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] = {
|
results[cui_str] = {
|
||||||
"scpTVA": None,
|
"scpTVA": None,
|
||||||
"denumire_anaf": "",
|
"denumire_anaf": "",
|
||||||
|
|||||||
@@ -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)])
|
cur.execute("SELECT strada, numar, localitate, judet FROM vadrese_parteneri WHERE id_adresa = :1", [int(addr_fact_id)])
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
result["adresa_facturare_roa"] = {"strada": row[0], "numar": row[1], "localitate": row[2], "judet": row[3]} if row else None
|
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
|
# Step 4: Build articles JSON and import order
|
||||||
articles_json = build_articles_json(order.items, order, app_settings)
|
articles_json = build_articles_json(order.items, order, app_settings)
|
||||||
|
|||||||
@@ -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 and status_filter not in ("all", "UNINVOICED"):
|
||||||
if status_filter.upper() == "IMPORTED":
|
if status_filter.upper() == "IMPORTED":
|
||||||
data_clauses.append("UPPER(status) IN ('IMPORTED', 'ALREADY_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:
|
else:
|
||||||
data_clauses.append("UPPER(status) = ?")
|
data_clauses.append("UPPER(status) = ?")
|
||||||
data_params.append(status_filter.upper())
|
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)
|
cursor = await db.execute(f"SELECT COUNT(*) FROM orders {uninv_old_where}", base_params)
|
||||||
uninvoiced_old = (await cursor.fetchone())[0]
|
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 {
|
return {
|
||||||
"orders": [dict(r) for r in rows],
|
"orders": [dict(r) for r in rows],
|
||||||
"total": total,
|
"total": total,
|
||||||
@@ -765,6 +775,7 @@ async def get_orders(page: int = 1, per_page: int = 50,
|
|||||||
"total": sum(status_counts.values()),
|
"total": sum(status_counts.values()),
|
||||||
"uninvoiced_sqlite": uninvoiced_sqlite,
|
"uninvoiced_sqlite": uninvoiced_sqlite,
|
||||||
"uninvoiced_old": uninvoiced_old,
|
"uninvoiced_old": uninvoiced_old,
|
||||||
|
"diffs": diffs_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally:
|
finally:
|
||||||
@@ -1072,6 +1083,35 @@ async def bulk_populate_anaf_cache(results: dict[str, dict]):
|
|||||||
await db.close()
|
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 ─────────────────
|
# ── Partner/Address Data on Orders ─────────────────
|
||||||
|
|
||||||
async def update_order_partner_data(order_number: str, partner_data: dict):
|
async def update_order_partner_data(order_number: str, partner_data: dict):
|
||||||
|
|||||||
@@ -638,20 +638,20 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
|||||||
0, len(truly_importable),
|
0, len(truly_importable),
|
||||||
{"imported": 0, "skipped": skipped_count, "errors": 0, "already_imported": already_imported_count})
|
{"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:
|
try:
|
||||||
db_check = await sqlite_service.get_sqlite()
|
prepop_cuis = await sqlite_service.get_expired_cuis_for_prepopulate()
|
||||||
try:
|
if prepop_cuis:
|
||||||
cursor = await db_check.execute("SELECT COUNT(*) FROM anaf_cache WHERE checked_at > datetime('now', '-7 days')")
|
_log_line(run_id, f"ANAF pre-populare: {len(prepop_cuis)} CUI-uri cu cache expirat")
|
||||||
row = await cursor.fetchone()
|
prepop_results = await anaf_service.check_vat_status_batch(prepop_cuis)
|
||||||
cache_count = row[0] if row else 0
|
if prepop_results:
|
||||||
finally:
|
await sqlite_service.bulk_populate_anaf_cache(prepop_results)
|
||||||
await db_check.close()
|
_log_line(run_id, f"ANAF pre-populare: {len(prepop_results)} rezultate stocate")
|
||||||
|
else:
|
||||||
if cache_count < 10:
|
_log_line(run_id, "ANAF pre-populare: cache complet")
|
||||||
_log_line(run_id, "ANAF pre-populare cache...")
|
|
||||||
except Exception as e:
|
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
|
# Step 4: ANAF batch verification for company CUIs
|
||||||
company_cuis = set()
|
company_cuis = set()
|
||||||
|
|||||||
@@ -451,6 +451,7 @@ input[type="checkbox"] {
|
|||||||
.fc-neutral { color: var(--text-muted); }
|
.fc-neutral { color: var(--text-muted); }
|
||||||
.fc-blue { color: var(--info); }
|
.fc-blue { color: var(--info); }
|
||||||
.fc-dark { color: var(--text-secondary); }
|
.fc-dark { color: var(--text-secondary); }
|
||||||
|
.fc-orange { color: var(--accent); }
|
||||||
|
|
||||||
/* ── Log viewer (dark theme — keep as-is) ────────── */
|
/* ── Log viewer (dark theme — keep as-is) ────────── */
|
||||||
.log-viewer {
|
.log-viewer {
|
||||||
@@ -1155,6 +1156,66 @@ tr.mapping-deleted td {
|
|||||||
.anaf-badge-ok { background: var(--success-light); color: var(--success-text); }
|
.anaf-badge-ok { background: var(--success-light); color: var(--success-text); }
|
||||||
.anaf-badge-warn { background: var(--warning-light); color: var(--warning-text); }
|
.anaf-badge-warn { background: var(--warning-light); color: var(--warning-text); }
|
||||||
.anaf-badge-gray { background: var(--cancelled-light); color: var(--text-muted); }
|
.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 { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||||
.addr-table th {
|
.addr-table th {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ async function loadDashOrders() {
|
|||||||
if (el('cntFact')) el('cntFact').textContent = c.facturate || 0;
|
if (el('cntFact')) el('cntFact').textContent = c.facturate || 0;
|
||||||
if (el('cntNef')) el('cntNef').textContent = c.nefacturate || c.uninvoiced || 0;
|
if (el('cntNef')) el('cntNef').textContent = c.nefacturate || c.uninvoiced || 0;
|
||||||
if (el('cntCanc')) el('cntCanc').textContent = c.cancelled || 0;
|
if (el('cntCanc')) el('cntCanc').textContent = c.cancelled || 0;
|
||||||
|
if (el('cntDiff')) el('cntDiff').textContent = c.diffs || 0;
|
||||||
|
|
||||||
// Attention card
|
// Attention card
|
||||||
const attnEl = document.getElementById('attentionCard');
|
const attnEl = document.getElementById('attentionCard');
|
||||||
@@ -336,10 +337,11 @@ async function loadDashOrders() {
|
|||||||
const errors = c.error || 0;
|
const errors = c.error || 0;
|
||||||
const unmapped = c.unresolved_skus || 0;
|
const unmapped = c.unresolved_skus || 0;
|
||||||
const nefact = c.nefacturate || 0;
|
const nefact = c.nefacturate || 0;
|
||||||
|
const diffs = c.diffs || 0;
|
||||||
|
|
||||||
const incompleteAddr = c.incomplete_addresses || 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 = '<div class="attention-card attention-ok"><i class="bi bi-check-circle"></i> Totul in ordine</div>';
|
attnEl.innerHTML = '<div class="attention-card attention-ok"><i class="bi bi-check-circle"></i> Totul in ordine</div>';
|
||||||
} else {
|
} else {
|
||||||
let items = [];
|
let items = [];
|
||||||
@@ -347,6 +349,7 @@ async function loadDashOrders() {
|
|||||||
if (unmapped > 0) items.push(`<span class="attention-item attention-warning" onclick="window.location='${window.ROOT_PATH||''}/missing-skus'"><i class="bi bi-puzzle"></i> ${unmapped} SKU-uri nemapate</span>`);
|
if (unmapped > 0) items.push(`<span class="attention-item attention-warning" onclick="window.location='${window.ROOT_PATH||''}/missing-skus'"><i class="bi bi-puzzle"></i> ${unmapped} SKU-uri nemapate</span>`);
|
||||||
if (nefact > 0) items.push(`<span class="attention-item attention-warning" onclick="document.querySelector('.filter-pill[data-status=UNINVOICED]')?.click()"><i class="bi bi-receipt"></i> ${nefact} nefacturate</span>`);
|
if (nefact > 0) items.push(`<span class="attention-item attention-warning" onclick="document.querySelector('.filter-pill[data-status=UNINVOICED]')?.click()"><i class="bi bi-receipt"></i> ${nefact} nefacturate</span>`);
|
||||||
if (c.incomplete_addresses > 0) items.push(`<span class="attention-item attention-warning"><i class="bi bi-geo-alt"></i> ${c.incomplete_addresses} adrese incomplete</span>`);
|
if (c.incomplete_addresses > 0) items.push(`<span class="attention-item attention-warning"><i class="bi bi-geo-alt"></i> ${c.incomplete_addresses} adrese incomplete</span>`);
|
||||||
|
if (diffs > 0) items.push(`<span class="attention-item attention-warning" onclick="document.querySelector('.filter-pill[data-status=DIFFS]')?.click()"><i class="bi bi-exclamation-diamond"></i> ${diffs} diferente ANAF</span>`);
|
||||||
attnEl.innerHTML = '<div class="attention-card attention-alert">' + items.join('') + '</div>';
|
attnEl.innerHTML = '<div class="attention-card attention-alert">' + items.join('') + '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +413,8 @@ async function loadDashOrders() {
|
|||||||
{ label: 'Erori', count: c.error || c.errors || 0, value: 'ERROR', active: activeStatus === 'ERROR', colorClass: 'fc-red' },
|
{ 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: '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: '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) => {
|
], (val) => {
|
||||||
document.querySelectorAll('.filter-pill[data-status]').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.filter-pill[data-status]').forEach(b => b.classList.remove('active'));
|
||||||
const pill = document.querySelector(`.filter-pill[data-status="${val}"]`);
|
const pill = document.querySelector(`.filter-pill[data-status="${val}"]`);
|
||||||
|
|||||||
@@ -501,8 +501,6 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
document.getElementById('detailStatus').innerHTML = '';
|
document.getElementById('detailStatus').innerHTML = '';
|
||||||
document.getElementById('detailIdComanda').textContent = '-';
|
document.getElementById('detailIdComanda').textContent = '-';
|
||||||
document.getElementById('detailIdPartener').textContent = '-';
|
document.getElementById('detailIdPartener').textContent = '-';
|
||||||
document.getElementById('detailIdAdresaFact').textContent = '-';
|
|
||||||
document.getElementById('detailIdAdresaLivr').textContent = '-';
|
|
||||||
document.getElementById('detailItemsBody').innerHTML = '<tr><td colspan="9" class="text-center">Se incarca...</td></tr>';
|
document.getElementById('detailItemsBody').innerHTML = '<tr><td colspan="9" class="text-center">Se incarca...</td></tr>';
|
||||||
document.getElementById('detailError').style.display = 'none';
|
document.getElementById('detailError').style.display = 'none';
|
||||||
const retryBtn = document.getElementById('detailRetryBtn');
|
const retryBtn = document.getElementById('detailRetryBtn');
|
||||||
@@ -519,15 +517,21 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
if (priceCheckEl) priceCheckEl.innerHTML = '';
|
if (priceCheckEl) priceCheckEl.innerHTML = '';
|
||||||
const reconEl = document.getElementById('detailInvoiceRecon');
|
const reconEl = document.getElementById('detailInvoiceRecon');
|
||||||
if (reconEl) { reconEl.innerHTML = ''; reconEl.style.display = 'none'; }
|
if (reconEl) { reconEl.innerHTML = ''; reconEl.style.display = 'none'; }
|
||||||
// Reset partner/address sections
|
// Reset compact header elements
|
||||||
const partnerSection = document.getElementById('detailPartnerSection');
|
const partenerRoa = document.getElementById('detailPartenerRoa');
|
||||||
if (partnerSection) partnerSection.style.display = 'none';
|
if (partenerRoa) { partenerRoa.style.display = 'none'; partenerRoa.textContent = ''; }
|
||||||
const addressSection = document.getElementById('detailAddressSection');
|
const cuiGomag = document.getElementById('detailCuiGomag');
|
||||||
if (addressSection) addressSection.style.display = 'none';
|
if (cuiGomag) cuiGomag.style.display = 'none';
|
||||||
const partnerBody = document.getElementById('partnerInfoBody');
|
const cuiRoa = document.getElementById('detailCuiRoa');
|
||||||
if (partnerBody) partnerBody.innerHTML = '';
|
if (cuiRoa) cuiRoa.style.display = 'none';
|
||||||
const addressBody = document.getElementById('addressInfoBody');
|
const anafArea = document.getElementById('detailPartnerAnafArea');
|
||||||
if (addressBody) addressBody.innerHTML = '';
|
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 modalEl = document.getElementById('orderDetailModal');
|
||||||
const existing = bootstrap.Modal.getInstance(modalEl);
|
const existing = bootstrap.Modal.getInstance(modalEl);
|
||||||
@@ -563,8 +567,6 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
|
|
||||||
document.getElementById('detailIdComanda').textContent = order.id_comanda || '-';
|
document.getElementById('detailIdComanda').textContent = order.id_comanda || '-';
|
||||||
document.getElementById('detailIdPartener').textContent = order.id_partener || '-';
|
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
|
// Invoice info
|
||||||
const inv = order.invoice;
|
const inv = order.invoice;
|
||||||
@@ -592,9 +594,8 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
reconEl.style.display = 'none';
|
reconEl.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render partner + address sections
|
// Render compact header info (partner + addresses)
|
||||||
_renderPartnerSection(order);
|
_renderHeaderInfo(order);
|
||||||
_renderAddressSection(order);
|
|
||||||
|
|
||||||
if (order.error_message) {
|
if (order.error_message) {
|
||||||
document.getElementById('detailError').textContent = order.error_message;
|
document.getElementById('detailError').textContent = order.error_message;
|
||||||
@@ -831,121 +832,32 @@ 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;
|
|
||||||
|
|
||||||
const pi = order.partner_info;
|
|
||||||
if (!pi || !pi.cod_fiscal_gomag) {
|
|
||||||
section.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ANAF badge
|
|
||||||
let anafBadge;
|
|
||||||
if (pi.anaf_platitor_tva === 1) {
|
|
||||||
anafBadge = '<span class="anaf-badge anaf-badge-ok">Platitor TVA</span>';
|
|
||||||
} else if (pi.anaf_platitor_tva === 0) {
|
|
||||||
anafBadge = '<span class="anaf-badge anaf-badge-warn">Neplatitor TVA</span>';
|
|
||||||
} else {
|
|
||||||
anafBadge = '<span class="anaf-badge anaf-badge-gray">Neverificat</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// CUI correction badge
|
|
||||||
let cuiCorrBadge = '';
|
|
||||||
if (pi.anaf_cod_fiscal_adjusted) {
|
|
||||||
cuiCorrBadge = ' <span class="anaf-badge anaf-badge-warn"><i class="bi bi-arrow-left-right"></i> Corectat ANAF</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Denomination mismatch
|
|
||||||
let denomHtml = '';
|
|
||||||
if (pi.anaf_denumire_mismatch && pi.denumire_anaf) {
|
|
||||||
denomHtml = `<div style="background:var(--warning-light);padding:8px 12px;border-radius:var(--card-radius);margin-top:8px">
|
|
||||||
<span class="partner-label" style="color:var(--warning-text)"><i class="bi bi-exclamation-triangle"></i> Denumire diferita</span><br>
|
|
||||||
<span style="font-size:13px">GoMag: <strong>${esc(order.customer_name || '')}</strong></span><br>
|
|
||||||
<span style="font-size:13px">ANAF: <strong>${esc(pi.denumire_anaf)}</strong></span>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.innerHTML = `
|
|
||||||
<div class="partner-row">
|
|
||||||
<div class="partner-field">
|
|
||||||
<div class="partner-label">CUI GoMag</div>
|
|
||||||
<div class="partner-value">${esc(pi.cod_fiscal_gomag)}</div>
|
|
||||||
</div>
|
|
||||||
<div class="partner-field">
|
|
||||||
<div class="partner-label">CUI ROA</div>
|
|
||||||
<div class="partner-value">${esc(pi.cod_fiscal_roa || '-')}${cuiCorrBadge}</div>
|
|
||||||
</div>
|
|
||||||
<div class="partner-field">
|
|
||||||
<div class="partner-label">Partener ROA</div>
|
|
||||||
<div style="font-family:var(--font-body);font-size:14px;font-weight:500">${esc(pi.denumire_roa || '-')}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="partner-row">
|
|
||||||
<div class="partner-field">
|
|
||||||
<div class="partner-label">ANAF</div>
|
|
||||||
<div>${anafBadge}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
${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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _renderAddressSection(order) {
|
|
||||||
const section = document.getElementById('detailAddressSection');
|
|
||||||
const body = document.getElementById('addressInfoBody');
|
|
||||||
const alertEl = document.getElementById('addressAlertCount');
|
|
||||||
if (!section || !body) return;
|
|
||||||
|
|
||||||
const addr = order.addresses;
|
|
||||||
if (!addr || (!addr.livrare_gomag && !addr.facturare_gomag)) {
|
|
||||||
section.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.style.display = '';
|
|
||||||
let mismatchCount = 0;
|
|
||||||
|
|
||||||
function fmtAddr(a) {
|
function fmtAddr(a) {
|
||||||
if (!a) return '-';
|
if (!a) return '\u2014';
|
||||||
if (typeof a === 'string') return esc(a);
|
if (typeof a === 'string') return a;
|
||||||
const parts = [a.address || a.strada || '', a.numar || ''].filter(Boolean);
|
const parts = [a.address || a.strada || '', a.numar || ''].filter(Boolean);
|
||||||
const line1 = parts.join(' ').trim();
|
const line1 = parts.join(' ').trim();
|
||||||
const line2 = [a.city || a.localitate || '', a.region || a.judet || ''].filter(Boolean).join(', ');
|
const line2 = [a.city || a.localitate || '', a.region || a.judet || ''].filter(Boolean).join(', ');
|
||||||
return esc(line1) + (line2 ? '<br>' + esc(line2) : '');
|
return [line1, line2].filter(Boolean).join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addrMatch(gomag, roa) {
|
function addrMatch(gomag, roa) {
|
||||||
if (!gomag || !roa) return true; // can't compare
|
if (!gomag || !roa) return true; // can't compare
|
||||||
const g = JSON.stringify(gomag).toUpperCase().replace(/[^A-Z0-9]/g, '');
|
function norm(s) {
|
||||||
const r = JSON.stringify(roa).toUpperCase().replace(/[^A-Z0-9]/g, '');
|
return (s || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
||||||
return g === r;
|
.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) {
|
function hasEfacturaRisk(roa) {
|
||||||
@@ -953,75 +865,128 @@ function _renderAddressSection(order) {
|
|||||||
return !roa.judet || !roa.localitate;
|
return !roa.judet || !roa.localitate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const livrMatch = addrMatch(addr.livrare_gomag, addr.livrare_roa);
|
// ── Compact Header Info Rendering ────────────────
|
||||||
const factMatch = addrMatch(addr.facturare_gomag, addr.facturare_roa);
|
|
||||||
if (!livrMatch) mismatchCount++;
|
|
||||||
if (!factMatch) mismatchCount++;
|
|
||||||
|
|
||||||
const livrRisk = hasEfacturaRisk(addr.livrare_roa);
|
function _renderHeaderInfo(order) {
|
||||||
const factRisk = hasEfacturaRisk(addr.facturare_roa);
|
const pi = order.partner_info;
|
||||||
|
const isPJ = pi && pi.cod_fiscal_gomag;
|
||||||
|
|
||||||
if (alertEl) {
|
// GoMag CUI (PJ only)
|
||||||
if (mismatchCount > 0) {
|
if (isPJ) {
|
||||||
alertEl.textContent = mismatchCount + (mismatchCount === 1 ? ' diferenta' : ' diferente');
|
const cuiGomagEl = document.getElementById('detailCuiGomag');
|
||||||
alertEl.style.display = '';
|
if (cuiGomagEl) {
|
||||||
|
document.getElementById('detailCuiGomagVal').textContent = pi.cod_fiscal_gomag;
|
||||||
|
cuiGomagEl.style.display = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROA column
|
||||||
|
if (isPJ && pi.denumire_roa) {
|
||||||
|
const partenerRoa = document.getElementById('detailPartenerRoa');
|
||||||
|
if (partenerRoa) {
|
||||||
|
partenerRoa.textContent = pi.denumire_roa;
|
||||||
|
partenerRoa.style.display = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ' <span class="anaf-badge anaf-badge-warn"><i class="bi bi-arrow-left-right"></i> Corectat</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANAF badge
|
||||||
|
const anafArea = document.getElementById('detailPartnerAnafArea');
|
||||||
|
if (anafArea) {
|
||||||
|
let anafBadge;
|
||||||
|
if (pi.anaf_platitor_tva === 1) {
|
||||||
|
anafBadge = '<span class="anaf-badge anaf-badge-ok">Platitor TVA</span>';
|
||||||
|
} else if (pi.anaf_platitor_tva === 0) {
|
||||||
|
anafBadge = '<span class="anaf-badge anaf-badge-warn">Neplatitor TVA</span>';
|
||||||
} else {
|
} else {
|
||||||
alertEl.style.display = 'none';
|
anafBadge = '<span class="anaf-badge anaf-badge-gray">?</span>';
|
||||||
|
}
|
||||||
|
anafArea.innerHTML = cuiCorrHtml + ' ' + anafBadge;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Desktop: 2-column table
|
// ERROR orders: muted dashes for ROA fields
|
||||||
const livrClass = livrRisk ? 'addr-efactura-risk' : (!livrMatch ? 'addr-mismatch' : '');
|
if (order.status === 'ERROR' && !order.id_comanda) {
|
||||||
const factClass = factRisk ? 'addr-efactura-risk' : (!factMatch ? 'addr-mismatch' : '');
|
document.getElementById('detailIdComanda').innerHTML = '<span class="text-muted">\u2014</span>';
|
||||||
|
document.getElementById('detailIdPartener').innerHTML = '<span class="text-muted">\u2014</span>';
|
||||||
|
}
|
||||||
|
|
||||||
const desktopHtml = `
|
// Denomination mismatch alert
|
||||||
<table class="addr-table d-none d-md-table">
|
if (isPJ && pi.anaf_denumire_mismatch && pi.denumire_anaf) {
|
||||||
<thead><tr><th></th><th>GOMAG</th><th>ROA</th></tr></thead>
|
const denomEl = document.getElementById('detailDenomMismatch');
|
||||||
<tbody>
|
if (denomEl) {
|
||||||
<tr class="${livrClass}">
|
denomEl.innerHTML = `<div style="background:var(--warning-light);padding:8px 12px;border-radius:var(--card-radius)">
|
||||||
<td><span class="addr-label">LIVRARE</span>${livrRisk ? '<br><small style="color:var(--error-text)">⚠ Risc eFactura</small>' : ''}</td>
|
<span style="font-size:12px;color:var(--warning-text);font-weight:500"><i class="bi bi-exclamation-triangle"></i> Denumire diferita</span><br>
|
||||||
<td>${fmtAddr(addr.livrare_gomag)}</td>
|
<span style="font-size:13px">GoMag: <strong>${esc(order.customer_name || '')}</strong></span><br>
|
||||||
<td>${fmtAddr(addr.livrare_roa)}</td>
|
<span style="font-size:13px">ANAF: <strong>${esc(pi.denumire_anaf)}</strong></span>
|
||||||
</tr>
|
</div>`;
|
||||||
<tr class="${factClass}">
|
denomEl.style.display = '';
|
||||||
<td><span class="addr-label">FACTURARE</span>${factRisk ? '<br><small style="color:var(--error-text)">⚠ Risc eFactura</small>' : ''}</td>
|
}
|
||||||
<td>${fmtAddr(addr.facturare_gomag)}</td>
|
}
|
||||||
<td>${fmtAddr(addr.facturare_roa)}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>`;
|
|
||||||
|
|
||||||
// Mobile: stacked cards
|
// Compact address lines
|
||||||
function mobileCard(label, gomag, roa, isMatch, isRisk) {
|
const addr = order.addresses;
|
||||||
const cls = isRisk ? ' addr-efactura-risk' : (!isMatch ? ' mismatch' : ' match');
|
if (!addr || (!addr.livrare_gomag && !addr.facturare_gomag)) return;
|
||||||
const matchLabel = isMatch ? '<div class="addr-match-label">✓ Adrese identice</div>' : '';
|
|
||||||
const riskLabel = isRisk ? '<div style="font-size:11px;color:var(--error-text)">⚠ Risc eFactura</div>' : '';
|
const addressBlock = document.getElementById('detailAddressBlock');
|
||||||
return `<div class="addr-card${cls}">
|
const addressLines = document.getElementById('detailAddressLines');
|
||||||
<div class="addr-card-header">${label}</div>
|
if (!addressBlock || !addressLines) return;
|
||||||
<div class="addr-card-row">
|
|
||||||
<div class="addr-card-source">GoMag:</div>
|
addressBlock.style.display = '';
|
||||||
<div class="addr-card-text">${fmtAddr(gomag)}</div>
|
let html = '';
|
||||||
</div>
|
|
||||||
<div class="addr-card-row">
|
function addrLine(label, addrObj, matchIcon) {
|
||||||
<div class="addr-card-source">ROA:</div>
|
const text = fmtAddr(addrObj);
|
||||||
<div class="addr-card-text">${fmtAddr(roa)}</div>
|
const escaped = esc(text);
|
||||||
</div>
|
let icon = '';
|
||||||
${matchLabel}${riskLabel}
|
if (matchIcon === 'match') {
|
||||||
|
icon = ' <i class="bi bi-check-lg addr-line-match" title="Adrese identice"></i>';
|
||||||
|
} else if (matchIcon === 'mismatch') {
|
||||||
|
icon = ' <i class="bi bi-exclamation-triangle" title="Adrese diferite"></i>';
|
||||||
|
} else if (matchIcon === 'risk') {
|
||||||
|
icon = ' <i class="bi bi-exclamation-triangle" title="Risc eFactura" style="color:var(--error-text)"></i>';
|
||||||
|
}
|
||||||
|
return `<div class="addr-line">
|
||||||
|
<span class="addr-line-label">${label}</span>
|
||||||
|
<span class="addr-line-text" title="${escaped}">${escaped}</span>${icon}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mobileHtml = `<div class="d-md-none">
|
// Livrare
|
||||||
${mobileCard('LIVRARE', addr.livrare_gomag, addr.livrare_roa, livrMatch, livrRisk)}
|
if (addr.livrare_gomag || addr.livrare_roa) {
|
||||||
${mobileCard('FACTURARE', addr.facturare_gomag, addr.facturare_roa, factMatch, factRisk)}
|
html += addrLine('Livrare GoMag:', addr.livrare_gomag, null);
|
||||||
</div>`;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
body.innerHTML = desktopHtml + mobileHtml;
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-expand on mismatch
|
addressLines.innerHTML = html;
|
||||||
if (mismatchCount > 0) {
|
|
||||||
const collapseEl = document.getElementById('detailAddressInfo');
|
|
||||||
if (collapseEl && !collapseEl.classList.contains('show')) {
|
|
||||||
new bootstrap.Collapse(collapseEl, { show: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
{% set rp = request.scope.get('root_path', '') %}
|
{% set rp = request.scope.get('root_path', '') %}
|
||||||
<link href="{{ rp }}/static/css/style.css?v=26" rel="stylesheet">
|
<link href="{{ rp }}/static/css/style.css?v=27" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Top Navbar (hidden on mobile via CSS) -->
|
<!-- Top Navbar (hidden on mobile via CSS) -->
|
||||||
@@ -93,16 +93,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
<!-- GOMAG Column -->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<small class="text-muted">Client:</small> <strong id="detailCustomer"></strong><br>
|
<div class="detail-col-label">GOMAG</div>
|
||||||
<small class="text-muted">Data comanda:</small> <span id="detailDate"></span><br>
|
<div class="detail-client-name" id="detailCustomer">...</div>
|
||||||
<small class="text-muted">Status:</small> <span id="detailStatus"></span><span id="detailPriceCheck" class="ms-2"></span>
|
<div class="detail-cui-line" id="detailCuiGomag" style="display:none">
|
||||||
|
<small class="text-muted">CUI:</small> <span class="font-data" id="detailCuiGomagVal"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div><small class="text-muted">Data:</small> <span id="detailDate"></span></div>
|
||||||
|
<div><small class="text-muted">Status:</small> <span id="detailStatus"></span><span id="detailPriceCheck" class="ms-2"></span></div>
|
||||||
|
</div>
|
||||||
|
<!-- ROA Column -->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<small class="text-muted">ID Comanda ROA:</small> <span id="detailIdComanda">-</span><br>
|
<div class="detail-col-label">ROA</div>
|
||||||
<small class="text-muted">ID Partener:</small> <span id="detailIdPartener">-</span><br>
|
<div class="detail-client-name" id="detailPartenerRoa" style="display:none"></div>
|
||||||
<small class="text-muted">ID Adr. Facturare:</small> <span id="detailIdAdresaFact">-</span><br>
|
<div class="detail-cui-line" id="detailCuiRoa" style="display:none">
|
||||||
<small class="text-muted">ID Adr. Livrare:</small> <span id="detailIdAdresaLivr">-</span>
|
<small class="text-muted">CUI:</small> <span class="font-data" id="detailCuiRoaVal"></span>
|
||||||
|
<span id="detailPartnerAnafArea"></span>
|
||||||
|
</div>
|
||||||
|
<div><small class="text-muted">ID Comanda:</small> <span class="font-data detail-roa-id" id="detailIdComanda">-</span></div>
|
||||||
|
<div><small class="text-muted">ID Partener:</small> <span class="font-data detail-roa-id" id="detailIdPartener">-</span></div>
|
||||||
<div id="detailInvoiceInfo" style="display:none; margin-top:4px;">
|
<div id="detailInvoiceInfo" style="display:none; margin-top:4px;">
|
||||||
<small class="text-muted">Factura:</small> <span id="detailInvoiceNumber"></span>
|
<small class="text-muted">Factura:</small> <span id="detailInvoiceNumber"></span>
|
||||||
<span class="ms-2"><small class="text-muted">din</small> <span id="detailInvoiceDate"></span></span>
|
<span class="ms-2"><small class="text-muted">din</small> <span id="detailInvoiceDate"></span></span>
|
||||||
@@ -110,31 +120,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Partner Info Section (PJ only) -->
|
<!-- Denomination mismatch alert -->
|
||||||
<div id="detailPartnerSection" style="display:none" class="mb-3">
|
<div id="detailDenomMismatch" style="display:none" class="mb-2"></div>
|
||||||
<div class="detail-section-header" role="button" tabindex="0"
|
<!-- Compact Address Lines -->
|
||||||
data-bs-toggle="collapse" data-bs-target="#detailPartnerInfo"
|
<div id="detailAddressBlock" style="display:none" class="mb-3">
|
||||||
aria-expanded="false" aria-controls="detailPartnerInfo">
|
<div class="detail-col-label" style="border-bottom:1px solid var(--border);margin-bottom:8px;padding-bottom:4px">ADRESE</div>
|
||||||
<i class="bi bi-chevron-right"></i>
|
<div id="detailAddressLines"></div>
|
||||||
<span>DETALII PARTENER</span>
|
|
||||||
<span id="partnerAlertCount" class="alert-count" style="display:none"></span>
|
|
||||||
</div>
|
|
||||||
<div class="collapse" id="detailPartnerInfo">
|
|
||||||
<div class="detail-section-body" id="partnerInfoBody"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Address Comparison Section -->
|
|
||||||
<div id="detailAddressSection" style="display:none" class="mb-3">
|
|
||||||
<div class="detail-section-header" role="button" tabindex="0"
|
|
||||||
data-bs-toggle="collapse" data-bs-target="#detailAddressInfo"
|
|
||||||
aria-expanded="false" aria-controls="detailAddressInfo">
|
|
||||||
<i class="bi bi-chevron-right"></i>
|
|
||||||
<span>ADRESE COMPARATIV</span>
|
|
||||||
<span id="addressAlertCount" class="alert-count" style="display:none"></span>
|
|
||||||
</div>
|
|
||||||
<div class="collapse" id="detailAddressInfo">
|
|
||||||
<div class="detail-section-body" id="addressInfoBody"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="table-responsive d-none d-md-block">
|
<div class="table-responsive d-none d-md-block">
|
||||||
<table class="table table-sm table-bordered mb-0">
|
<table class="table table-sm table-bordered mb-0">
|
||||||
@@ -170,7 +161,7 @@
|
|||||||
|
|
||||||
<script>window.ROOT_PATH = "{{ rp }}";</script>
|
<script>window.ROOT_PATH = "{{ rp }}";</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="{{ rp }}/static/js/shared.js?v=21"></script>
|
<script src="{{ rp }}/static/js/shared.js?v=22"></script>
|
||||||
<script>
|
<script>
|
||||||
// Dark mode toggle
|
// Dark mode toggle
|
||||||
function toggleDarkMode() {
|
function toggleDarkMode() {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
<button class="filter-pill d-none d-md-inline-flex" data-status="INVOICED">Facturate <span class="filter-count fc-green" id="cntFact">0</span></button>
|
<button class="filter-pill d-none d-md-inline-flex" data-status="INVOICED">Facturate <span class="filter-count fc-green" id="cntFact">0</span></button>
|
||||||
<button class="filter-pill d-none d-md-inline-flex" data-status="UNINVOICED">Nefacturate <span class="filter-count fc-red" id="cntNef">0</span></button>
|
<button class="filter-pill d-none d-md-inline-flex" data-status="UNINVOICED">Nefacturate <span class="filter-count fc-red" id="cntNef">0</span></button>
|
||||||
<button class="filter-pill d-none d-md-inline-flex" data-status="CANCELLED">Anulate <span class="filter-count fc-dark" id="cntCanc">0</span></button>
|
<button class="filter-pill d-none d-md-inline-flex" data-status="CANCELLED">Anulate <span class="filter-count fc-dark" id="cntCanc">0</span></button>
|
||||||
|
<button class="filter-pill d-none d-md-inline-flex" data-status="DIFFS">Diferente <span class="filter-count fc-orange" id="cntDiff">0</span></button>
|
||||||
<button class="btn btn-sm btn-outline-secondary d-none d-md-inline-flex" id="btnRefreshInvoices" onclick="refreshInvoices()" title="Actualizeaza status facturi din Oracle">↻</button>
|
<button class="btn btn-sm btn-outline-secondary d-none d-md-inline-flex" id="btnRefreshInvoices" onclick="refreshInvoices()" title="Actualizeaza status facturi din Oracle">↻</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-md-none mb-2 d-flex align-items-center gap-2" style="max-width:100%;overflow:hidden">
|
<div class="d-md-none mb-2 d-flex align-items-center gap-2" style="max-width:100%;overflow:hidden">
|
||||||
@@ -114,5 +115,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=34"></script>
|
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=35"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ def test_order_detail_modal_has_roa_ids(page: Page, app_url: str):
|
|||||||
expect(modal).to_be_attached()
|
expect(modal).to_be_attached()
|
||||||
|
|
||||||
modal_html = modal.inner_html()
|
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 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 "GOMAG" in modal_html, "Missing 'GOMAG' column label in order detail modal"
|
||||||
assert "ID Adr. Livrare" in modal_html, "Missing 'ID Adr. Livrare' 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):
|
def test_order_detail_items_table_columns(page: Page, app_url: str):
|
||||||
|
|||||||
Reference in New Issue
Block a user