fix(sync): guard against mass DELETED_IN_ROA when ROA is recovering

After a power loss + reboot, COMENZI was queryable but not yet recovered;
phase 4b-3 read it as empty and sticky-marked 3794 live orders DELETED_IN_ROA
(nulling id_comanda). check_orders_exist also swallowed Oracle errors and
returned a partial set, which callers misread as deletions.

- check_orders_exist now re-raises on Oracle error instead of returning partial
- new invoice_service.deletions_or_guard() raises MassDeletionGuard when the
  would-delete fraction is implausibly high (>30% of >=25 imported orders)
- both deletion sites (auto sync + manual refresh) skip + log on guard trip

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-26 07:18:08 +00:00
parent 4a03fe1016
commit 395e2b997a
3 changed files with 54 additions and 8 deletions

View File

@@ -846,10 +846,14 @@ async def refresh_invoices():
existing_ids = await asyncio.to_thread(
invoice_service.check_orders_exist, id_comanda_list
)
for o in all_imported:
if o["id_comanda"] not in existing_ids:
await sqlite_service.mark_order_deleted_in_roa(o["order_number"])
orders_deleted += 1
try:
to_delete = invoice_service.deletions_or_guard(all_imported, existing_ids)
except invoice_service.MassDeletionGuard as g:
logger.warning(f"Mass-deletion guard tripped during refresh: {g}")
to_delete = []
for o in to_delete:
await sqlite_service.mark_order_deleted_in_roa(o["order_number"])
orders_deleted += 1
# Cherry-pick A: Batch refresh Oracle addresses for all orders with stored address IDs
addr_rows = await sqlite_service.get_orders_with_address_ids()