fix(sync): backfill address_mismatch for orders missing blue dot

Orders synced before address_mismatch was deployed had stale 0 values,
causing missing blue dots in the dashboard. Adds startup backfill from
stored address JSON + recomputes on each sync for ALREADY_IMPORTED orders.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-06 16:05:32 +00:00
parent 9977ec28cf
commit 86e8d54d5e
3 changed files with 112 additions and 0 deletions

View File

@@ -1,6 +1,9 @@
import oracledb import oracledb
import aiosqlite import aiosqlite
import sqlite3 import sqlite3
import json
import re
import unicodedata
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
@@ -359,12 +362,69 @@ def init_sqlite():
logger.info(f"Migrated orders: added column {col}") logger.info(f"Migrated orders: added column {col}")
conn.commit() conn.commit()
# Backfill address_mismatch from stored address JSON
_backfill_address_mismatch(conn)
except Exception as e: except Exception as e:
logger.warning(f"Migration check failed: {e}") logger.warning(f"Migration check failed: {e}")
conn.close() conn.close()
logger.info(f"SQLite initialized: {_sqlite_db_path}") 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(): async def get_sqlite():
"""Get async SQLite connection.""" """Get async SQLite connection."""
if _sqlite_db_path is None: if _sqlite_db_path is None:

View File

@@ -1172,6 +1172,48 @@ async def update_order_partner_data(order_number: str, partner_data: dict):
await db.close() 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]: async def get_orders_missing_anaf() -> list[dict]:
"""Get orders with cod_fiscal_roa set but no ANAF data (for backfill).""" """Get orders with cod_fiscal_roa set but no ANAF data (for backfill)."""
db = await get_sqlite() db = await get_sqlite()

View File

@@ -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})") _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) 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) # Step 3b: Record skipped orders + store items (batch)
skipped_count = len(skipped) skipped_count = len(skipped)
skipped_batch = [] skipped_batch = []