fix(address+ui): remove TIER 2 reuse, typed diff badges, false positive reduction
- Remove TIER 2 address lookup (county+city without street) from PL/SQL — creates new address when street differs instead of reusing wrong one - Replace generic "N diferente" badge with typed micro-badges (CUI, Denumire, TVA, Adr. livr., Adr. fact., Preturi) with red/amber semantic colors - Extend addrMatch() regex to strip full Romanian address words (STRADA, NUMAR, BLOC, COMUNA, SAT, MUNICIPIUL, etc.) — fixes "Strada X" vs "X" false positives - Extend normalize_company_name() for II, PFA, INTREPRINDERE INDIVIDUALA legal forms - Persist address_mismatch to SQLite so "Dif." filter includes address-only diffs - Add red/amber indicator dots to desktop table and mobile list rows - 12 unit tests for normalization and server-side address matching Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -183,8 +183,10 @@ def normalize_company_name(name: str) -> str:
|
||||
result = name.strip().upper()
|
||||
# Strip diacritics
|
||||
result = result.translate(_DIACRITICS)
|
||||
# Remove common suffixes
|
||||
result = re.sub(r'\b(S\.?R\.?L\.?|S\.?A\.?|S\.?C\.?|S\.?N\.?C\.?|S\.?C\.?S\.?)\b', '', result)
|
||||
# Remove common suffixes and legal forms
|
||||
result = re.sub(r'\b(S\.?R\.?L\.?|S\.?A\.?|S\.?C\.?|S\.?N\.?C\.?|S\.?C\.?S\.?|P\.?F\.?A\.?|INTREPRINDERE\s+INDIVIDUALA)\b', '', result)
|
||||
# Strip II only at start of name (avoid matching Roman numeral II in "TEHNICA II SRL")
|
||||
result = re.sub(r'^I\.?I\.?\s+', '', result)
|
||||
# Remove punctuation and extra spaces
|
||||
result = re.sub(r'[^\w\s]', '', result)
|
||||
result = re.sub(r'\s+', ' ', result).strip()
|
||||
|
||||
@@ -697,7 +697,7 @@ async def get_orders(page: int = 1, per_page: int = 50,
|
||||
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)")
|
||||
data_clauses.append("(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1 OR address_mismatch = 1)")
|
||||
else:
|
||||
data_clauses.append("UPPER(status) = ?")
|
||||
data_params.append(status_filter.upper())
|
||||
@@ -751,9 +751,9 @@ 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 count: orders with ANAF adjustments or address mismatches
|
||||
diffs_clauses = list(base_clauses) + [
|
||||
"(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1)"
|
||||
"(anaf_cod_fiscal_adjusted = 1 OR anaf_denumire_mismatch = 1 OR address_mismatch = 1)"
|
||||
]
|
||||
diffs_where = "WHERE " + " AND ".join(diffs_clauses)
|
||||
cursor = await db.execute(f"SELECT COUNT(*) FROM orders {diffs_where}", base_params)
|
||||
@@ -1138,6 +1138,7 @@ async def update_order_partner_data(order_number: str, partner_data: dict):
|
||||
adresa_facturare_roa = ?,
|
||||
anaf_denumire_mismatch = ?,
|
||||
denumire_anaf = ?,
|
||||
address_mismatch = ?,
|
||||
updated_at = datetime('now')
|
||||
WHERE order_number = ?
|
||||
""", (
|
||||
@@ -1153,6 +1154,7 @@ async def update_order_partner_data(order_number: str, partner_data: dict):
|
||||
partner_data.get("adresa_facturare_roa"),
|
||||
partner_data.get("anaf_denumire_mismatch", 0),
|
||||
partner_data.get("denumire_anaf"),
|
||||
partner_data.get("address_mismatch", 0),
|
||||
order_number,
|
||||
))
|
||||
await db.commit()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import unicodedata
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
@@ -18,6 +20,34 @@ from .. import database
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _addr_match(gomag_json, roa_json):
|
||||
"""Server-side address comparison matching JS addrMatch()."""
|
||||
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
|
||||
_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)
|
||||
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
|
||||
|
||||
|
||||
# Sync state
|
||||
_sync_lock = asyncio.Lock()
|
||||
_current_sync = None # dict with run_id, status, progress info
|
||||
@@ -802,6 +832,11 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
partner_data["anaf_denumire_mismatch"] = 1
|
||||
partner_data["denumire_anaf"] = anaf_data_for_order["denumire_anaf"]
|
||||
|
||||
# Address mismatch check (server-side, mirrors JS addrMatch)
|
||||
livr_match = _addr_match(partner_data.get("adresa_livrare_gomag"), partner_data.get("adresa_livrare_roa"))
|
||||
fact_match = _addr_match(partner_data.get("adresa_facturare_gomag"), partner_data.get("adresa_facturare_roa"))
|
||||
partner_data["address_mismatch"] = 1 if (not livr_match or not fact_match) else 0
|
||||
|
||||
await sqlite_service.update_order_partner_data(order.number, partner_data)
|
||||
|
||||
if not result["success"]:
|
||||
|
||||
Reference in New Issue
Block a user