fix(partner-mismatch): fix 3 infinite-loop bugs in mismatch detection cycle
Three root causes caused partner_mismatch=1 to loop indefinitely: 1. No-CUI company orders (is_pj=1, no cod_fiscal): old code flagged as mismatch every cycle. Fixed by requiring new_cf to be non-null for PF→PJ detection. Stale flags from old code cleared via new clear_stale_partner_mismatches_no_cui() for out-of-window orders. 2. same_partner resync path did not update cod_fiscal_gomag in SQLite. On next cycle GoMag returned a CUI but stored_cf was still NULL → re-detected as mismatch. Fixed by also calling update_partner_resync_data (not just update_partner_mismatch_batch) in the same_partner branch. 3. GoMag sends CUI with space: 'RO 17922480'. The _strip_ro() regex ^RO left the space → ' 17922480' != '17922480' → false mismatch. Fixed: changed regex to ^RO\s* and added .strip(). Also adds diagnostic logger.info lines for mismatch detection/resync counts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1440,6 +1440,36 @@ async def update_partner_mismatch_batch(updates: list) -> None:
|
||||
await db.close()
|
||||
|
||||
|
||||
async def clear_stale_partner_mismatches_no_cui(exclude_numbers: set) -> int:
|
||||
"""Clear partner_mismatch=1 for orders with cod_fiscal_gomag=NULL that are NOT in the
|
||||
current sync batch. These were flagged by old code (before the no-CUI fix) and will
|
||||
never self-correct because they fall outside the active sync window.
|
||||
Returns number of rows cleared.
|
||||
"""
|
||||
db = await get_sqlite()
|
||||
try:
|
||||
if exclude_numbers:
|
||||
placeholders = ",".join("?" * len(exclude_numbers))
|
||||
sql = f"""
|
||||
UPDATE orders SET partner_mismatch = 0, updated_at = datetime('now')
|
||||
WHERE partner_mismatch = 1
|
||||
AND cod_fiscal_gomag IS NULL
|
||||
AND order_number NOT IN ({placeholders})
|
||||
"""
|
||||
await db.execute(sql, list(exclude_numbers))
|
||||
else:
|
||||
await db.execute("""
|
||||
UPDATE orders SET partner_mismatch = 0, updated_at = datetime('now')
|
||||
WHERE partner_mismatch = 1 AND cod_fiscal_gomag IS NULL
|
||||
""")
|
||||
await db.commit()
|
||||
cursor = await db.execute("SELECT changes()")
|
||||
row = await cursor.fetchone()
|
||||
return row[0] if row else 0
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
|
||||
async def update_partner_resync_data(order_number: str, data: dict) -> None:
|
||||
"""Update partner fields + clear partner_mismatch after a successful resync."""
|
||||
db = await get_sqlite()
|
||||
|
||||
@@ -641,11 +641,12 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
def _strip_ro(cf):
|
||||
if not cf:
|
||||
return ""
|
||||
return re.sub(r'^RO', '', cf.strip().upper())
|
||||
# Strip optional "RO" prefix + any surrounding whitespace
|
||||
return re.sub(r'^RO\s*', '', cf.strip().upper()).strip()
|
||||
|
||||
is_mismatch = False
|
||||
if new_data["is_pj"] and not stored_cf:
|
||||
is_mismatch = True # PF→PJ
|
||||
if new_data["is_pj"] and new_cf and not stored_cf:
|
||||
is_mismatch = True # PF→PJ (doar dacă are CUI — fără CUI nu putem confirma)
|
||||
elif not new_data["is_pj"] and stored_cf:
|
||||
is_mismatch = True # PJ→PF
|
||||
elif new_data["is_pj"] and stored_cf and _strip_ro(new_cf) != _strip_ro(stored_cf):
|
||||
@@ -657,17 +658,28 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
|
||||
await sqlite_service.update_partner_mismatch_batch(mismatch_updates)
|
||||
|
||||
# Clear stale mismatches for orders outside the current sync window
|
||||
# that have no CUI stored (flagged by old code before the no-CUI fix)
|
||||
current_batch_numbers = {o.number for o in already_in_roa}
|
||||
cleared = await sqlite_service.clear_stale_partner_mismatches_no_cui(current_batch_numbers)
|
||||
if cleared:
|
||||
logger.info(f"Partner mismatch: cleared {cleared} stale no-CUI flags from previous sync window")
|
||||
|
||||
# Auto-resync uninvoiced orders with partner mismatch (max 5/cycle)
|
||||
MAX_PARTNER_RESYNC_PER_CYCLE = 5
|
||||
total_mismatched = sum(1 for v in mismatch_map.values() if v == 1)
|
||||
logger.info(f"Partner mismatch detection: {len(already_in_roa)} orders checked, {total_mismatched} mismatches found")
|
||||
mismatched_uninvoiced = [
|
||||
o for o in already_in_roa
|
||||
if mismatch_map.get(o.number) == 1
|
||||
and not stored_partner_data.get(o.number, {}).get("factura_numar")
|
||||
][:MAX_PARTNER_RESYNC_PER_CYCLE]
|
||||
logger.info(f"Partner auto-resync: {len(mismatched_uninvoiced)} uninvoiced orders queued")
|
||||
|
||||
if mismatched_uninvoiced:
|
||||
resync_ok = 0
|
||||
for _order in mismatched_uninvoiced:
|
||||
logger.info(f"Partner resync attempt: #{_order.number}")
|
||||
try:
|
||||
await _resync_partner_for_order(
|
||||
order=_order,
|
||||
@@ -676,6 +688,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
run_id=run_id,
|
||||
)
|
||||
resync_ok += 1
|
||||
logger.info(f"Partner resync success: #{_order.number}")
|
||||
except Exception as _e:
|
||||
_log_line(run_id, f"#{_order.number} EROARE resync partener: {_e}")
|
||||
logger.error(f"Partner resync error for {_order.number}: {_e}")
|
||||
@@ -1310,9 +1323,14 @@ async def _resync_partner_for_order(order, stored: dict, app_settings: dict, run
|
||||
resync_result = await asyncio.to_thread(_do_resync)
|
||||
|
||||
if resync_result.get("same_partner"):
|
||||
await sqlite_service.update_partner_mismatch_batch([
|
||||
{"order_number": order_number, "partner_mismatch": 0}
|
||||
])
|
||||
# Update cod_fiscal_gomag so next detection doesn't re-flag this order
|
||||
await sqlite_service.update_partner_resync_data(order_number, {
|
||||
"id_partener": resync_result["new_partner_id"],
|
||||
"cod_fiscal_gomag": cod_fiscal_override or new_partner_data["cod_fiscal"],
|
||||
"cod_fiscal_roa": None,
|
||||
"denumire_roa": stored.get("denumire_roa"),
|
||||
"partner_mismatch": 0,
|
||||
})
|
||||
_log_line(run_id, f"#{order_number} RESYNC: partener neschimbat, mismatch cleared")
|
||||
else:
|
||||
new_partner_id = resync_result["new_partner_id"]
|
||||
|
||||
Reference in New Issue
Block a user