feat(anaf-dedup): ANAF partner dedup + address fix + UI enrichment
Prevent partner duplicates via ANAF CUI verification and dual PL/SQL search. Fix address matching with street-level comparison and diacritics normalization. Show partner/address comparison in order detail modal. - New anaf_service.py: batch ANAF API client with chunking, retry, cache - PL/SQL: dual CUI search (bare/RO+bare/RO space+bare), 3-tier address search (street+city+id_loc → city+id_loc → create), strip_diacritics at storage for addresses and partner names - SQLite: anaf_cache table, 12 new order columns for partner/address data - import_service: cod_fiscal_override param, return partner/address from Oracle - sync_service: ANAF batch integration, denomination mismatch detection, cache pre-population trigger - Router: enriched order_detail with partner_info + addresses JSON - UI: collapsible Detalii Partener + Adrese Comparativ sections in modal, auto-expand on mismatch, ANAF badges, mobile address cards - Dashboard: address quality attention indicator - New scan_duplicate_partners.py script for one-time duplicate audit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -529,6 +529,33 @@ async def order_detail(order_number: str):
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
# Partner info
|
||||
order["partner_info"] = {
|
||||
"cod_fiscal_gomag": order.get("cod_fiscal_gomag"),
|
||||
"cod_fiscal_roa": order.get("cod_fiscal_roa"),
|
||||
"denumire_roa": order.get("denumire_roa"),
|
||||
"anaf_platitor_tva": order.get("anaf_platitor_tva"),
|
||||
"anaf_checked_at": order.get("anaf_checked_at"),
|
||||
"anaf_cod_fiscal_adjusted": order.get("anaf_cod_fiscal_adjusted") == 1,
|
||||
"anaf_denumire_mismatch": order.get("anaf_denumire_mismatch") == 1,
|
||||
"denumire_anaf": order.get("denumire_anaf"),
|
||||
}
|
||||
# Parse JSON address strings
|
||||
for key in ("adresa_livrare_gomag", "adresa_facturare_gomag",
|
||||
"adresa_livrare_roa", "adresa_facturare_roa"):
|
||||
val = order.get(key)
|
||||
if val and isinstance(val, str):
|
||||
try:
|
||||
order[key] = json.loads(val)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
order["addresses"] = {
|
||||
"livrare_gomag": order.get("adresa_livrare_gomag"),
|
||||
"facturare_gomag": order.get("adresa_facturare_gomag"),
|
||||
"livrare_roa": order.get("adresa_livrare_roa"),
|
||||
"facturare_roa": order.get("adresa_facturare_roa"),
|
||||
}
|
||||
|
||||
# Add settings for receipt display (app_settings already fetched above)
|
||||
order["transport_vat"] = app_settings.get("transport_vat") or "21"
|
||||
order["transport_codmat"] = app_settings.get("transport_codmat") or ""
|
||||
@@ -684,6 +711,16 @@ async def dashboard_orders(page: int = 1, per_page: int = 50,
|
||||
except Exception:
|
||||
counts["unresolved_skus"] = 0
|
||||
|
||||
# Address quality: count orders with incomplete ROA addresses
|
||||
try:
|
||||
addr_count = await sqlite_service.get_incomplete_addresses_count()
|
||||
if addr_count == -1: # stale cache — skip
|
||||
counts["incomplete_addresses"] = 0
|
||||
else:
|
||||
counts["incomplete_addresses"] = addr_count
|
||||
except Exception:
|
||||
counts["incomplete_addresses"] = 0
|
||||
|
||||
# For UNINVOICED filter: apply server-side filtering + pagination
|
||||
if is_uninvoiced_filter:
|
||||
filtered = [o for o in all_orders if o.get("status") in ("IMPORTED", "ALREADY_IMPORTED") and not o.get("invoice")]
|
||||
|
||||
Reference in New Issue
Block a user