feat(partner): detect and resync partner mismatches on already-imported orders
Detects PF↔PJ transitions and CUI changes after import; auto-resyncs uninvoiced orders (max 5/cycle) and shows visual alert for invoiced ones. - SQLite: partner_mismatch column + batch helpers - sync_service: detection loop + _resync_partner_for_order - dashboard: red dot + attention card indicator - modal: alert with contextual message and resync button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user