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:
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
Reference in New Issue
Block a user