From 8d301a1ab7fa34af7bc2da0f8d2904767afe4da1 Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 9 Jun 2026 14:50:06 -0500 Subject: [PATCH] fix(checkout): SO creation falls back to generic item if a catalog Item is missing Tracing Mitchell's batch, SO creation 404'd on a missing ERPNext Item (INTRASTATE-AUTHORITY) -- 30 of 70 catalog erpnext_item codes were missing in ERPNext. Created all 30. Added a safety net: if createResource('Sales Order') 404s on a missing Item, retry once with every line remapped to the generic COMPLIANCE-SERVICE item so a missing catalog Item never strands a paid order's SO. --- api/src/routes/checkout.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/api/src/routes/checkout.ts b/api/src/routes/checkout.ts index 8f8c2ea..69e9693 100644 --- a/api/src/routes/checkout.ts +++ b/api/src/routes/checkout.ts @@ -348,6 +348,27 @@ export async function ensureComplianceSalesOrder( custom_surcharge_pct: surchargePct, workflow_state: "Received", items: lineItems, + }).catch(async (e: unknown) => { + // Resilience: if a service's ERPNext Item is missing, the SO would 404. + // Retry once with every line item remapped to the generic COMPLIANCE-SERVICE + // item so a missing catalog Item never strands a paid order's SO. (The + // catalog Item should still be created; this is a safety net.) + const msg = String((e as { body?: { _server_messages?: string } })?.body?._server_messages || e); + if (/not found/i.test(msg)) { + console.warn(`[checkout] SO item missing for ${orderId}, retrying with generic item:`, msg.slice(0, 120)); + const fallback = lineItems.map(li => ({ ...li, item_code: "COMPLIANCE-SERVICE" })); + return createResource("Sales Order", { + customer: erpnextCustomer, + delivery_date: new Date(Date.now() + 30 * 86400000).toISOString().split("T")[0], + custom_external_order_id: orderId, + custom_order_type: "compliance", + custom_payment_gateway: GATEWAY_LABELS[paymentMethod] || paymentMethod, + custom_surcharge_pct: surchargePct, + workflow_state: "Received", + items: fallback, + }); + } + throw e; })) as { name: string }; try {