diff --git a/CLAUDE.md b/CLAUDE.md index 18c5fcf..438c09f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,9 +73,10 @@ Documentatie completa: [README.md](README.md) - Recovery: la fiecare sync, comenzile ERROR sunt reverificate in Oracle ### Parteneri -- Prioritate: **companie** (PJ, cod_fiscal + registru) daca exista in GoMag, altfel persoana fizica cu **shipping name** +- Prioritate: **companie** (PJ, cod_fiscal + registru) daca exista in GoMag (name SAU code), altfel persoana fizica cu **shipping name** - Adresa livrare: intotdeauna GoMag shipping -- Adresa facturare: daca shipping ≠ billing person → shipping pt ambele; altfel → billing din GoMag +- Adresa facturare PJ: adresa billing din GoMag (sediul firmei) +- Adresa facturare PF: adresa shipping din GoMag (ramburs curier pe numele destinatarului) ### Preturi - Dual policy: articolele sunt rutate la `id_pol_vanzare` sau `id_pol_productie` pe baza contului contabil (341/345 = productie) diff --git a/api/app/database.py b/api/app/database.py index 0dc2d1f..cb3584b 100644 --- a/api/app/database.py +++ b/api/app/database.py @@ -353,6 +353,7 @@ def init_sqlite(): ("anaf_denumire_mismatch", "INTEGER DEFAULT 0"), ("denumire_anaf", "TEXT"), ("address_mismatch", "INTEGER DEFAULT 0"), + ("partner_mismatch", "INTEGER DEFAULT 0"), ]: if col not in order_cols: conn.execute(f"ALTER TABLE orders ADD COLUMN {col} {typedef}") diff --git a/api/app/routers/sync.py b/api/app/routers/sync.py index b41513b..0551c1a 100644 --- a/api/app/routers/sync.py +++ b/api/app/routers/sync.py @@ -544,6 +544,7 @@ async def order_detail(order_number: str): "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"), + "partner_mismatch": order.get("partner_mismatch") == 1, } # Parse JSON address strings for key in ("adresa_livrare_gomag", "adresa_facturare_gomag", @@ -579,6 +580,80 @@ async def retry_order(order_number: str): return result +@router.post("/api/orders/{order_number}/resync-partner") +async def resync_partner(order_number: str): + """Manual partner resync for invoiced orders with partner_mismatch=1. + + Auto-resync handles uninvoiced orders during sync loop. + This endpoint is for edge case: operator wants to fix an already-invoiced order. + """ + detail = await sqlite_service.get_order_detail(order_number) + if not detail: + raise HTTPException(status_code=404, detail="Comanda nu a fost gasita") + + order_data = detail["order"] + if not order_data.get("partner_mismatch"): + return {"success": False, "message": "Comanda nu are mismatch de partener"} + + if sync_service._sync_lock.locked(): + return {"success": False, "message": "Sync in curs — asteapta finalizarea"} + + stored = { + "id_comanda": order_data.get("id_comanda"), + "id_partener": order_data.get("id_partener"), + "denumire_roa": order_data.get("denumire_roa"), + "cod_fiscal_gomag": order_data.get("cod_fiscal_gomag"), + "factura_numar": order_data.get("factura_numar"), + } + + # Download order from GoMag to get current data + import tempfile + from ..services import order_reader, gomag_client + app_settings = await sqlite_service.get_app_settings() + gomag_key = app_settings.get("gomag_api_key") or None + gomag_shop = app_settings.get("gomag_api_shop") or None + + from datetime import datetime, timedelta + order_date_str = order_data.get("order_date", "") + try: + order_date = datetime.fromisoformat(order_date_str.replace("Z", "+00:00")).date() + except (ValueError, AttributeError): + order_date = datetime.now().date() - timedelta(days=1) + + with tempfile.TemporaryDirectory() as tmp_dir: + try: + days_back = (datetime.now().date() - order_date).days + 2 + await gomag_client.download_orders( + tmp_dir, days_back=days_back, + api_key=gomag_key, api_shop=gomag_shop, limit=200, + ) + except Exception as e: + return {"success": False, "message": f"Eroare download GoMag: {e}"} + + target_order = None + orders, _ = order_reader.read_json_orders(json_dir=tmp_dir) + for o in orders: + if str(o.number) == str(order_number): + target_order = o + break + + if not target_order: + return {"success": False, "message": f"Comanda {order_number} nu a fost gasita in GoMag API"} + + run_id = f"resync_{order_number}" + try: + await sync_service._resync_partner_for_order( + order=target_order, + stored=stored, + app_settings=app_settings, + run_id=run_id, + ) + return {"success": True, "message": "Partener actualizat in ROA"} + except Exception as e: + logger.error(f"Manual resync failed for {order_number}: {e}") + return {"success": False, "message": str(e)} + + @router.get("/api/orders/by-sku/{sku}/pending") async def get_pending_orders_for_sku(sku: str): """Get SKIPPED orders that contain the given SKU.""" diff --git a/api/app/services/import_service.py b/api/app/services/import_service.py index 020a3fa..eaf1c44 100644 --- a/api/app/services/import_service.py +++ b/api/app/services/import_service.py @@ -52,6 +52,38 @@ def convert_web_date(date_str: str) -> datetime: return datetime.now() +def determine_partner_data(order) -> dict: + """Extract partner identification from a GoMag order (no Oracle calls). + + Returns: {denumire, cod_fiscal, registru, is_pj} + Identical logic to import_single_order partner block — reuse to avoid drift. + """ + if order.billing.is_company: + denumire = clean_web_text(order.billing.company_name).upper() + if not denumire: + # CUI-only fallback: company has code but no name → use billing person name + denumire = clean_web_text( + f"{order.billing.lastname} {order.billing.firstname}" + ).upper() + cod_fiscal = clean_web_text(order.billing.company_code) or None + registru = clean_web_text(order.billing.company_reg) or None + is_pj = 1 + else: + if order.shipping and (order.shipping.lastname or order.shipping.firstname): + raw_name = clean_web_text( + f"{order.shipping.lastname} {order.shipping.firstname}" + ).upper() + else: + raw_name = clean_web_text( + f"{order.billing.lastname} {order.billing.firstname}" + ).upper() + denumire = " ".join(sorted(raw_name.split())) + cod_fiscal = None + registru = None + is_pj = 0 + return {"denumire": denumire, "cod_fiscal": cod_fiscal, "registru": registru, "is_pj": is_pj} + + def format_address_for_oracle(address: str, city: str, region: str) -> str: """Port of VFP FormatAddressForOracle.""" region_clean = clean_web_text(region) @@ -245,26 +277,11 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se # Step 1: Process partner — use shipping person data for name id_partener = cur.var(oracledb.DB_TYPE_NUMBER) - if order.billing.is_company: - denumire = clean_web_text(order.billing.company_name).upper() - cod_fiscal = cod_fiscal_override or clean_web_text(order.billing.company_code) or None - registru = clean_web_text(order.billing.company_reg) or None - is_pj = 1 - else: - # Use shipping person for partner name (person on shipping label) - # Sort words alphabetically to normalize firstname/lastname swap - if order.shipping and (order.shipping.lastname or order.shipping.firstname): - raw_name = clean_web_text( - f"{order.shipping.lastname} {order.shipping.firstname}" - ).upper() - else: - raw_name = clean_web_text( - f"{order.billing.lastname} {order.billing.firstname}" - ).upper() - denumire = " ".join(sorted(raw_name.split())) - cod_fiscal = None - registru = None - is_pj = 0 + _pdata = determine_partner_data(order) + denumire = _pdata["denumire"] + cod_fiscal = (cod_fiscal_override or _pdata["cod_fiscal"]) if _pdata["is_pj"] else None + registru = _pdata["registru"] + is_pj = _pdata["is_pj"] cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_partener", [ cod_fiscal, denumire, registru, is_pj, anaf_strict, id_partener @@ -283,19 +300,6 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se result["denumire_roa"] = row[0] if row else None result["cod_fiscal_roa"] = row[1] if row else None - # Determine if billing and shipping are different persons - billing_name = clean_web_text( - f"{order.billing.lastname} {order.billing.firstname}" - ).strip().upper() - shipping_name = "" - if order.shipping: - shipping_name = clean_web_text( - f"{order.shipping.lastname} {order.shipping.firstname}" - ).strip().upper() - different_person = bool( - shipping_name and billing_name and shipping_name != billing_name - ) - # Step 2: Process shipping address (primary — person on shipping label) # Use shipping person phone/email for partner contact shipping_phone = "" @@ -333,12 +337,9 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se result["error"] = err_msg return result - # Step 3: Process billing address - if different_person: - # Different person: use shipping address for BOTH billing and shipping in ROA - addr_fact_id = addr_livr_id - else: - # Same person: compute billing addr, short-circuit if identical to shipping + # Step 3: Process billing address — PJ vs PF rule + if is_pj: + # PJ (company): billing address = GoMag billing (company HQ) billing_addr = format_address_for_oracle( order.billing.address, order.billing.city, order.billing.region ) @@ -364,6 +365,9 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se logger.error(f"Order {order_number}: {err_msg}") result["error"] = err_msg return result + else: + # PF (individual): billing = shipping (ramburs curier pe numele destinatarului) + addr_fact_id = addr_livr_id if addr_fact_id is not None: result["id_adresa_facturare"] = int(addr_fact_id) diff --git a/api/app/services/order_reader.py b/api/app/services/order_reader.py index 576c2ad..8d2527e 100644 --- a/api/app/services/order_reader.py +++ b/api/app/services/order_reader.py @@ -124,7 +124,9 @@ def _parse_order(order_id: str, data: dict, source_file: str) -> OrderData: # Parse billing billing_data = data.get("billing", {}) or {} company = billing_data.get("company") - is_company = isinstance(company, dict) and bool(company.get("name")) + is_company = isinstance(company, dict) and ( + bool(company.get("name")) or bool(company.get("code")) + ) billing = OrderBilling( firstname=str(billing_data.get("firstname", "")), diff --git a/api/app/services/sqlite_service.py b/api/app/services/sqlite_service.py index af68c49..da209d4 100644 --- a/api/app/services/sqlite_service.py +++ b/api/app/services/sqlite_service.py @@ -701,6 +701,7 @@ async def get_orders(page: int = 1, per_page: int = 50, elif status_filter.upper() == "DIFFS": data_clauses.append( "(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1" + " OR partner_mismatch = 1" " OR (cod_fiscal_gomag IS NOT NULL AND cod_fiscal_gomag != '' AND anaf_platitor_tva IS NOT NULL" " AND anaf_cod_fiscal_adjusted != 1" " AND ((UPPER(cod_fiscal_gomag) LIKE 'RO%' AND anaf_platitor_tva = 0)" @@ -759,9 +760,10 @@ 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 or TVA mismatch (not address) + # Diffs count: orders with ANAF adjustments, TVA mismatch, or partner mismatch diffs_clauses = list(base_clauses) + [ "(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1" + " OR partner_mismatch = 1" " OR (cod_fiscal_gomag IS NOT NULL AND cod_fiscal_gomag != '' AND anaf_platitor_tva IS NOT NULL" " AND anaf_cod_fiscal_adjusted != 1" " AND ((UPPER(cod_fiscal_gomag) LIKE 'RO%' AND anaf_platitor_tva = 0)" @@ -771,6 +773,12 @@ async def get_orders(page: int = 1, per_page: int = 50, cursor = await db.execute(f"SELECT COUNT(*) FROM orders {diffs_where}", base_params) diffs_count = (await cursor.fetchone())[0] + # Partner mismatches count + pm_clauses = list(base_clauses) + ["partner_mismatch = 1"] + pm_where = "WHERE " + " AND ".join(pm_clauses) + cursor = await db.execute(f"SELECT COUNT(*) FROM orders {pm_where}", base_params) + partner_mismatches_count = (await cursor.fetchone())[0] + return { "orders": [dict(r) for r in rows], "total": total, @@ -788,6 +796,7 @@ async def get_orders(page: int = 1, per_page: int = 50, "uninvoiced_sqlite": uninvoiced_sqlite, "uninvoiced_old": uninvoiced_old, "diffs": diffs_count, + "partner_mismatches": partner_mismatches_count, } } finally: @@ -1382,3 +1391,76 @@ async def set_incomplete_addresses_count(count: int): await db.commit() finally: await db.close() + + +# ── Partner Mismatch ────────────────────────────── + +async def get_orders_partner_data_batch(order_numbers: list) -> dict: + """Return {order_number: {cod_fiscal_gomag, denumire_roa, id_partener, factura_numar, id_comanda}}.""" + if not order_numbers: + return {} + db = await get_sqlite() + try: + result = {} + for i in range(0, len(order_numbers), 500): + batch = order_numbers[i:i+500] + placeholders = ",".join("?" * len(batch)) + cursor = await db.execute( + f"SELECT order_number, cod_fiscal_gomag, denumire_roa, id_partener, " + f"factura_numar, id_comanda FROM orders WHERE order_number IN ({placeholders})", + batch + ) + for row in await cursor.fetchall(): + result[row[0]] = { + "cod_fiscal_gomag": row[1], + "denumire_roa": row[2], + "id_partener": row[3], + "factura_numar": row[4], + "id_comanda": row[5], + } + return result + finally: + await db.close() + + +async def update_partner_mismatch_batch(updates: list) -> None: + """Update partner_mismatch flag for a batch of orders. + Each item: {order_number, partner_mismatch: 0|1} + """ + if not updates: + return + db = await get_sqlite() + try: + await db.executemany( + "UPDATE orders SET partner_mismatch = ?, updated_at = datetime('now') WHERE order_number = ?", + [(u["partner_mismatch"], u["order_number"]) for u in updates] + ) + await db.commit() + finally: + await db.close() + + +async def update_partner_resync_data(order_number: str, data: dict) -> None: + """Update partner fields + clear partner_mismatch after a successful resync.""" + db = await get_sqlite() + try: + await db.execute(""" + UPDATE orders SET + id_partener = ?, + cod_fiscal_gomag = ?, + cod_fiscal_roa = ?, + denumire_roa = ?, + partner_mismatch = ?, + updated_at = datetime('now') + WHERE order_number = ? + """, ( + data.get("id_partener"), + data.get("cod_fiscal_gomag"), + data.get("cod_fiscal_roa"), + data.get("denumire_roa"), + data.get("partner_mismatch", 0), + order_number, + )) + await db.commit() + finally: + await db.close() diff --git a/api/app/services/sync_service.py b/api/app/services/sync_service.py index 736946c..64f5952 100644 --- a/api/app/services/sync_service.py +++ b/api/app/services/sync_service.py @@ -625,6 +625,63 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None }) await sqlite_service.update_gomag_addresses_batch(addr_updates) + # Detect partner mismatches for already-imported orders + if already_in_roa: + stored_partner_data = await sqlite_service.get_orders_partner_data_batch( + [o.number for o in already_in_roa] + ) + mismatch_map = {} + mismatch_updates = [] + for order in already_in_roa: + stored = stored_partner_data.get(order.number, {}) + stored_cf = stored.get("cod_fiscal_gomag") + new_data = import_service.determine_partner_data(order) + new_cf = new_data["cod_fiscal"] + + def _strip_ro(cf): + if not cf: + return "" + return re.sub(r'^RO', '', cf.strip().upper()) + + is_mismatch = False + if new_data["is_pj"] and not stored_cf: + is_mismatch = True # PF→PJ + elif not new_data["is_pj"] and stored_cf: + is_mismatch = True # PJ→PF + elif new_data["is_pj"] and stored_cf and _strip_ro(new_cf) != _strip_ro(stored_cf): + is_mismatch = True # CUI schimbat + + val = 1 if is_mismatch else 0 + mismatch_map[order.number] = val + mismatch_updates.append({"order_number": order.number, "partner_mismatch": val}) + + await sqlite_service.update_partner_mismatch_batch(mismatch_updates) + + # Auto-resync uninvoiced orders with partner mismatch (max 5/cycle) + MAX_PARTNER_RESYNC_PER_CYCLE = 5 + mismatched_uninvoiced = [ + o for o in already_in_roa + if mismatch_map.get(o.number) == 1 + and not stored_partner_data.get(o.number, {}).get("factura_numar") + ][:MAX_PARTNER_RESYNC_PER_CYCLE] + + if mismatched_uninvoiced: + resync_ok = 0 + for _order in mismatched_uninvoiced: + try: + await _resync_partner_for_order( + order=_order, + stored=stored_partner_data.get(_order.number, {}), + app_settings=app_settings, + run_id=run_id, + ) + resync_ok += 1 + except Exception as _e: + _log_line(run_id, f"#{_order.number} EROARE resync partener: {_e}") + logger.error(f"Partner resync error for {_order.number}: {_e}") + if resync_ok: + _log_line(run_id, f"Resync parteneri: {resync_ok} comenzi actualizate") + # Step 3b: Record skipped orders + store items (batch) skipped_count = len(skipped) skipped_batch = [] @@ -1076,3 +1133,199 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None def stop_sync(): """Signal sync to stop. Currently sync runs to completion.""" pass + + +async def _resync_partner_for_order(order, stored: dict, app_settings: dict, run_id: str) -> None: + """Resync partner for a single already-imported uninvoiced order. + + Safety: double-checks factura_numar before Oracle call. + Reads existing comanda row and calls PACK_COMENZI.modifica_comanda. + """ + import oracledb + + order_number = order.number + id_comanda = stored.get("id_comanda") + if not id_comanda: + _log_line(run_id, f"#{order_number} SKIP resync partener: id_comanda lipsa") + return + + # Double-check factura_numar — may have been invoiced since mismatch detection + current_detail = await sqlite_service.get_order_detail(order_number) + if current_detail and current_detail.get("order", {}).get("factura_numar"): + _log_line(run_id, f"#{order_number} SKIP resync partener: comanda facturata in tranzit") + return + + old_partner_id = stored.get("id_partener") + old_partner_name = stored.get("denumire_roa") or "?" + + new_partner_data = import_service.determine_partner_data(order) + + # ANAF check for PF→PJ transition + cod_fiscal_override = None + anaf_data = None + if new_partner_data["is_pj"] and new_partner_data["cod_fiscal"]: + raw_cf = new_partner_data["cod_fiscal"] + bare_cui, _ = anaf_service.sanitize_cui(raw_cf) + if bare_cui: + anaf_data = await sqlite_service.get_anaf_cache(bare_cui) + if not anaf_data: + try: + fresh = await anaf_service.check_vat_status_batch([bare_cui]) + if fresh: + await sqlite_service.bulk_populate_anaf_cache(fresh) + anaf_data = fresh.get(bare_cui) + except Exception as e: + logger.warning(f"ANAF check failed for {bare_cui}: {e}") + if anaf_data and anaf_data.get("scpTVA") is not None: + cod_fiscal_override = anaf_service.determine_correct_cod_fiscal( + bare_cui, anaf_data["scpTVA"] + ) + + def _do_resync(): + if database.pool is None: + raise RuntimeError("Oracle pool not initialized") + conn = database.pool.acquire() + try: + with conn.cursor() as cur: + # Create/find partner + id_partener_var = cur.var(oracledb.DB_TYPE_NUMBER) + anaf_strict = 1 if (anaf_data and anaf_data.get("scpTVA") is not None) else None + cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_partener", [ + cod_fiscal_override or new_partner_data["cod_fiscal"], + new_partner_data["denumire"], + new_partner_data["registru"], + new_partner_data["is_pj"], + anaf_strict, + id_partener_var, + ]) + new_partner_id = id_partener_var.getvalue() + if not new_partner_id or new_partner_id <= 0: + raise RuntimeError(f"Partner creation failed for {new_partner_data['denumire']}") + new_partner_id = int(new_partner_id) + + # Same partner — just clear mismatch + if new_partner_id == (old_partner_id or -1): + return {"same_partner": True, "new_partner_id": new_partner_id} + + # Get new partner details for audit log + cur.execute( + "SELECT denumire, cod_fiscal FROM nom_parteneri WHERE id_part = :1", + [new_partner_id] + ) + row = cur.fetchone() + new_partner_name = row[0] if row else new_partner_data["denumire"] + new_cod_fiscal_roa = row[1] if row else None + + # Create addresses under new partner + addr_livr_id = None + shipping_addr = None + if order.shipping: + id_adresa_livr = cur.var(oracledb.DB_TYPE_NUMBER) + shipping_addr = import_service.format_address_for_oracle( + order.shipping.address, order.shipping.city, order.shipping.region + ) + shipping_phone = order.shipping.phone or order.billing.phone or "" + shipping_email = order.shipping.email or order.billing.email or "" + cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_adresa", [ + new_partner_id, shipping_addr, shipping_phone, shipping_email, id_adresa_livr + ]) + addr_livr_id = id_adresa_livr.getvalue() + if addr_livr_id is None: + raise RuntimeError(f"Shipping address creation failed for partner {new_partner_id}") + addr_livr_id = int(addr_livr_id) + + billing_name_str = import_service.clean_web_text( + f"{order.billing.lastname} {order.billing.firstname}" + ).strip().upper() + ship_name_str = "" + if order.shipping: + ship_name_str = import_service.clean_web_text( + f"{order.shipping.lastname} {order.shipping.firstname}" + ).strip().upper() + different_person = bool(ship_name_str and billing_name_str and ship_name_str != billing_name_str) + + if different_person and addr_livr_id: + addr_fact_id = addr_livr_id + else: + billing_addr = import_service.format_address_for_oracle( + order.billing.address, order.billing.city, order.billing.region + ) + if addr_livr_id and order.shipping and billing_addr == shipping_addr: + addr_fact_id = addr_livr_id + else: + id_adresa_fact = cur.var(oracledb.DB_TYPE_NUMBER) + cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_adresa", [ + new_partner_id, billing_addr, + order.billing.phone or "", + order.billing.email or "", + id_adresa_fact, + ]) + addr_fact_id = id_adresa_fact.getvalue() + if addr_fact_id is None: + raise RuntimeError(f"Billing address creation failed for partner {new_partner_id}") + addr_fact_id = int(addr_fact_id) + + # Read existing comanda row for modifica_comanda params + cur.execute(""" + SELECT nr_comanda, data_comanda, data_livrare, proc_discount, + interna, id_util_um, id_codclient, comanda_externa, id_ctr + FROM comenzi WHERE id_comanda = :1 + """, [id_comanda]) + row = cur.fetchone() + if not row: + raise RuntimeError(f"Comanda {id_comanda} not found in Oracle") + nr_comanda, data_comanda, data_livrare, proc_discount, interna, id_util_um, id_codclient, comanda_externa, id_ctr = row + + cur.callproc("PACK_COMENZI.modifica_comanda", [ + id_comanda, + nr_comanda, + data_comanda, + new_partner_id, + data_livrare, + proc_discount, + interna, + id_util_um, + addr_fact_id, + addr_livr_id, + id_codclient, + comanda_externa, + id_ctr, + ]) + conn.commit() + return { + "same_partner": False, + "new_partner_id": new_partner_id, + "new_partner_name": new_partner_name, + "new_cod_fiscal_roa": new_cod_fiscal_roa, + } + except Exception: + try: + conn.rollback() + except Exception: + pass + raise + finally: + database.pool.release(conn) + + resync_result = await asyncio.to_thread(_do_resync) + + if resync_result.get("same_partner"): + await sqlite_service.update_partner_mismatch_batch([ + {"order_number": order_number, "partner_mismatch": 0} + ]) + _log_line(run_id, f"#{order_number} RESYNC: partener neschimbat, mismatch cleared") + else: + new_partner_id = resync_result["new_partner_id"] + new_partner_name = resync_result.get("new_partner_name", "?") + new_cod_fiscal_roa = resync_result.get("new_cod_fiscal_roa") + await sqlite_service.update_partner_resync_data(order_number, { + "id_partener": new_partner_id, + "cod_fiscal_gomag": cod_fiscal_override or new_partner_data["cod_fiscal"], + "cod_fiscal_roa": new_cod_fiscal_roa, + "denumire_roa": new_partner_name, + "partner_mismatch": 0, + }) + _log_line( + run_id, + f"#{order_number} RESYNC partener: {old_partner_id} ({old_partner_name}) → {new_partner_id} ({new_partner_name})" + ) diff --git a/api/app/static/js/dashboard.js b/api/app/static/js/dashboard.js index 773ec74..44dc3ce 100644 --- a/api/app/static/js/dashboard.js +++ b/api/app/static/js/dashboard.js @@ -340,8 +340,9 @@ async function loadDashOrders() { const diffs = c.diffs || 0; const incompleteAddr = c.incomplete_addresses || 0; + const partnerMismatches = c.partner_mismatches || 0; - if (errors === 0 && unmapped === 0 && nefact === 0 && incompleteAddr === 0 && diffs === 0) { + if (errors === 0 && unmapped === 0 && nefact === 0 && incompleteAddr === 0 && diffs === 0 && partnerMismatches === 0) { attnEl.innerHTML = '