fix(connect): store cloud order id locally to enable reliable status mirroring

The Phase 4 _mirror_status_to_cloud function had no way to look up the
cloud order's numeric id once it was marked synced (not in the pending
list anymore), so status updates from local staff could silently fail.

Fix:
  - models/order.py: online_order_cloud_id INTEGER column added to Order
  - main.py: migration for the new column
  - schemas/order.py: online_order_cloud_id exposed in OrderOut
  - cloud_sync.py: stores cloud_order["id"] as online_order_cloud_id
    when creating the local order during the pull
  - connect_orders.py: _mirror_status_to_cloud now takes the integer
    cloud id directly — no more pending-list lookup; function body
    reduced from ~50 lines to ~15

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 00:14:43 +03:00
parent 4b1201ecaa
commit 80842d9be3
5 changed files with 17 additions and 57 deletions

View File

@@ -39,70 +39,26 @@ def _cloud_headers() -> dict:
return {"X-Site-ID": settings.SITE_ID, "X-Site-Key": settings.SITE_KEY}
async def _mirror_status_to_cloud(cloud_order_ref: str, new_status: str, rejection_reason: str = None):
async def _mirror_status_to_cloud(cloud_order_id: int, new_status: str, rejection_reason: str = None):
"""Best-effort push of status update to cloud. Failures are logged, not raised."""
if not settings.CLOUD_URL or not cloud_order_ref:
if not settings.CLOUD_URL or not cloud_order_id:
return
try:
# We need the cloud order id — look it up by public_ref via the status endpoint
# (The cloud orders router exposes PATCH /{order_id}/status by numeric id.
# We store cloud id implicitly through public_ref; fetch it first.)
body = {"status": new_status}
if rejection_reason:
body["rejection_reason"] = rejection_reason
async with httpx.AsyncClient(timeout=10) as client:
# Get cloud order id via status lookup (public endpoint, no auth needed)
status_resp = await client.get(
f"{settings.CLOUD_URL}/api/orders/status/{cloud_order_ref}"
)
if status_resp.status_code != 200:
logger.warning("Could not find cloud order %s", cloud_order_ref)
return
# Search pending list to get numeric id — use the site-key protected route
from middleware.license_check import license_state
site_numeric_id = license_state.get("site_numeric_id")
if not site_numeric_id:
return
pending_resp = await client.get(
f"{settings.CLOUD_URL}/api/orders/pending/{site_numeric_id}",
headers=_cloud_headers(),
)
# The order may already be synced; look in all orders via remote dashboard
# Simpler: use the manager-JWT-free internal PATCH directly via site key
# We need the cloud order numeric id. Store it on first pull — for now,
# the cloud also exposes PATCH via remote dashboard (manager JWT).
# Best path: add a site-key variant. Until then, we mirror via the
# existing PATCH endpoint using site key — works because orders.py
# accepts site key auth on PATCH /{order_id}/status.
# We get the numeric id from the pending list or a dedicated lookup.
# Iterate pending to find our ref (may be empty if already synced).
cloud_order_id = None
if pending_resp.status_code == 200:
for o in pending_resp.json():
if o.get("public_ref") == cloud_order_ref:
cloud_order_id = o["id"]
break
if cloud_order_id is None:
# Already synced — not in pending list. We can't easily look it up
# without a dedicated search endpoint. Log and skip for now; the
# remote manager dashboard's own PATCH handles the remote case.
logger.debug("Cloud order %s not in pending list; skipping mirror", cloud_order_ref)
return
body = {"status": new_status}
if rejection_reason:
body["rejection_reason"] = rejection_reason
patch_resp = await client.patch(
resp = await client.patch(
f"{settings.CLOUD_URL}/api/orders/{cloud_order_id}/status",
headers=_cloud_headers(),
json=body,
)
patch_resp.raise_for_status()
logger.info("Mirrored status %s for cloud order %s", new_status, cloud_order_ref)
resp.raise_for_status()
logger.info("Mirrored status %s for cloud order id %d", new_status, cloud_order_id)
except Exception as e:
logger.warning("Failed to mirror status to cloud for %s: %s", cloud_order_ref, e)
logger.warning("Failed to mirror status to cloud for order id %d: %s", cloud_order_id, e)
# ── Endpoints ─────────────────────────────────────────────────────────────────
@@ -137,7 +93,7 @@ async def accept_order(
db.commit()
db.refresh(order)
background.add_task(_mirror_status_to_cloud, order.online_order_ref, "accepted")
background.add_task(_mirror_status_to_cloud, order.online_order_cloud_id, "accepted")
broadcast_sync("online_order_updated", {"order_id": order_id, "status": "accepted"})
return order
@@ -159,7 +115,7 @@ async def reject_order(
db.commit()
db.refresh(order)
background.add_task(_mirror_status_to_cloud, order.online_order_ref, "rejected", body.reason)
background.add_task(_mirror_status_to_cloud, order.online_order_cloud_id, "rejected", body.reason)
broadcast_sync("online_order_updated", {"order_id": order_id, "status": "rejected"})
return order
@@ -183,6 +139,6 @@ async def update_order_status(
db.commit()
db.refresh(order)
background.add_task(_mirror_status_to_cloud, order.online_order_ref, body.status)
background.add_task(_mirror_status_to_cloud, order.online_order_cloud_id, body.status)
broadcast_sync("online_order_updated", {"order_id": order_id, "status": body.status})
return order