"""Inventory sync engine. Refreshes supplier_products inventory from each adapter, writes inventory_history, and flags zero/unavailable stock (pauses published products + audit log alert). """ from datetime import datetime, timezone from app.engines.suppliers.base import build_adapter from app.models import AuditLog, InventoryHistory, Supplier, SupplierProduct def sync_inventory(db, supplier=None): if supplier is not None: suppliers = [supplier] else: suppliers = db.query(Supplier).filter(Supplier.status == "active").all() stats = {"suppliers": len(suppliers), "checked": 0, "changed": 0, "zeroed": 0} for s in suppliers: try: adapter = build_adapter(s) current = {r["supplier_sku"]: int(r["inventory"]) for r in adapter.get_inventory()} except Exception as exc: # noqa: BLE001 — log and continue to next supplier db.add( AuditLog( actor="inventory", action="sync_failed", entity="supplier", entity_id=str(s.id), detail={"supplier": s.name, "error": str(exc)}, ) ) continue for sp in db.query(SupplierProduct).filter(SupplierProduct.supplier_id == s.id).all(): stats["checked"] += 1 new_value = current.get(sp.supplier_sku) if new_value is None: continue if sp.supplier_inventory != new_value: stats["changed"] += 1 sp.supplier_inventory = new_value sp.last_updated = datetime.now(timezone.utc) db.add(InventoryHistory(supplier_product_id=sp.id, inventory=new_value)) if new_value <= 0: stats["zeroed"] += 1 db.add( AuditLog( actor="inventory", action="inventory_zero", entity="supplier_product", entity_id=str(sp.id), detail={"supplier_sku": sp.supplier_sku, "inventory": 0}, ) ) if sp.product is not None and sp.product.status == "published": sp.product.status = "paused" db.add( AuditLog( actor="inventory", action="product_paused_zero_inventory", entity="product", entity_id=str(sp.product.id), detail={"sku": sp.product.sku}, ) ) db.commit() return stats