diff --git a/api/app/database.py b/api/app/database.py index 0129293..80eb869 100644 --- a/api/app/database.py +++ b/api/app/database.py @@ -1,6 +1,9 @@ import oracledb import aiosqlite import sqlite3 +import json +import re +import unicodedata import logging import os from pathlib import Path @@ -359,12 +362,69 @@ def init_sqlite(): logger.info(f"Migrated orders: added column {col}") conn.commit() + + # Backfill address_mismatch from stored address JSON + _backfill_address_mismatch(conn) + except Exception as e: logger.warning(f"Migration check failed: {e}") conn.close() logger.info(f"SQLite initialized: {_sqlite_db_path}") + +def _backfill_address_mismatch(conn): + """Recompute address_mismatch from stored address JSON for all orders.""" + _ADDR_WORDS = re.compile( + r'\b(STR|STRADA|NR|NUMAR|NUMARUL|BL|BLOC|SC|SCARA|AP|APART|APARTAMENT|' + r'ET|ETAJ|COM|COMUNA|SAT|MUN|MUNICIPIUL|JUD|JUDETUL|CARTIER|PARTER|SECTOR|ORAS)\b' + ) + + def norm(s): + s = unicodedata.normalize('NFD', s or '') + s = re.sub(r'[\u0300-\u036f]', '', s).upper() + s = _ADDR_WORDS.sub('', s) + return re.sub(r'[^A-Z0-9]', '', s) + + def addr_match(gomag_json, roa_json): + if not gomag_json or not roa_json: + return True + try: + g = json.loads(gomag_json) if isinstance(gomag_json, str) else gomag_json + r = json.loads(roa_json) if isinstance(roa_json, str) else roa_json + except (json.JSONDecodeError, TypeError): + return True + g_street = norm(g.get('address') or g.get('strada') or '') + r_street = norm((r.get('strada') or '') + (r.get('numar') or '')) + g_city = norm(g.get('city') or g.get('localitate') or '') + r_city = norm(r.get('localitate') or '') + g_region = norm(g.get('region') or g.get('judet') or '') + r_region = norm(r.get('judet') or '') + return g_street == r_street and g_city == r_city and g_region == r_region + + try: + rows = conn.execute(""" + SELECT order_number, adresa_livrare_gomag, adresa_livrare_roa, + adresa_facturare_gomag, adresa_facturare_roa + FROM orders + WHERE adresa_livrare_roa IS NOT NULL OR adresa_facturare_roa IS NOT NULL + """).fetchall() + updated = 0 + for r in rows: + livr_ok = addr_match(r[1], r[2]) + fact_ok = addr_match(r[3], r[4]) + new_val = 1 if (not livr_ok or not fact_ok) else 0 + conn.execute( + "UPDATE orders SET address_mismatch = ? WHERE order_number = ?", + (new_val, r[0]) + ) + updated += 1 + if updated: + conn.commit() + logger.info(f"Backfill address_mismatch: {updated} orders recomputed") + except Exception as e: + logger.warning(f"Backfill address_mismatch failed: {e}") + async def get_sqlite(): """Get async SQLite connection.""" if _sqlite_db_path is None: diff --git a/api/app/services/sqlite_service.py b/api/app/services/sqlite_service.py index 879a08e..bb5f179 100644 --- a/api/app/services/sqlite_service.py +++ b/api/app/services/sqlite_service.py @@ -1172,6 +1172,48 @@ async def update_order_partner_data(order_number: str, partner_data: dict): await db.close() +async def update_gomag_addresses_batch(updates: list[dict]): + """Update GoMag addresses and recompute address_mismatch for a batch of orders. + + Each dict: {order_number, adresa_livrare_gomag, adresa_facturare_gomag} + """ + if not updates: + return + from ..services.sync_service import _addr_match + db = await get_sqlite() + try: + for u in updates: + order_number = u["order_number"] + livr_gomag = u.get("adresa_livrare_gomag") + fact_gomag = u.get("adresa_facturare_gomag") + # Update GoMag addresses + await db.execute(""" + UPDATE orders SET + adresa_livrare_gomag = COALESCE(?, adresa_livrare_gomag), + adresa_facturare_gomag = COALESCE(?, adresa_facturare_gomag), + updated_at = datetime('now') + WHERE order_number = ? + """, (livr_gomag, fact_gomag, order_number)) + # Recompute address_mismatch from stored addresses + cursor = await db.execute( + "SELECT adresa_livrare_gomag, adresa_livrare_roa, " + "adresa_facturare_gomag, adresa_facturare_roa FROM orders WHERE order_number = ?", + (order_number,) + ) + row = await cursor.fetchone() + if row and (row[1] or row[3]): # has at least one ROA address + livr_ok = _addr_match(row[0], row[1]) + fact_ok = _addr_match(row[2], row[3]) + new_val = 1 if (not livr_ok or not fact_ok) else 0 + await db.execute( + "UPDATE orders SET address_mismatch = ? WHERE order_number = ?", + (new_val, order_number) + ) + await db.commit() + finally: + await db.close() + + async def get_orders_missing_anaf() -> list[dict]: """Get orders with cod_fiscal_roa set but no ANAF data (for backfill).""" db = await get_sqlite() diff --git a/api/app/services/sync_service.py b/api/app/services/sync_service.py index aa15898..f9773b5 100644 --- a/api/app/services/sync_service.py +++ b/api/app/services/sync_service.py @@ -612,6 +612,16 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None _log_line(run_id, f"#{order.number} [{order.date or '?'}] {customer} → DEJA IMPORTAT (ID: {id_comanda_roa})") await sqlite_service.save_orders_batch(already_batch) + # Update GoMag addresses + recompute address_mismatch for already-imported orders + addr_updates = [] + for order in already_in_roa: + addr_updates.append({ + "order_number": order.number, + "adresa_livrare_gomag": json.dumps({"address": order.shipping.address, "city": order.shipping.city, "region": order.shipping.region}) if order.shipping else None, + "adresa_facturare_gomag": json.dumps({"address": order.billing.address, "city": order.billing.city, "region": order.billing.region}), + }) + await sqlite_service.update_gomag_addresses_batch(addr_updates) + # Step 3b: Record skipped orders + store items (batch) skipped_count = len(skipped) skipped_batch = []