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:
Claude Agent
2026-04-01 20:07:37 +00:00
parent 2f593c30f6
commit e8b42088e3
10 changed files with 308 additions and 243 deletions

View File

@@ -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": "",

View File

@@ -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)

View File

@@ -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):

View File

@@ -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()