diff --git a/apps/shopify-app/app/features/delivery/delivery-labels.js b/apps/shopify-app/app/features/delivery/delivery-labels.js index 43a9067..ddfc790 100644 --- a/apps/shopify-app/app/features/delivery/delivery-labels.js +++ b/apps/shopify-app/app/features/delivery/delivery-labels.js @@ -50,12 +50,12 @@ export function inferDeliveryDateForOrder({ orderCreatedAt, } = {}) { return ( - normalizeExplicitDeliveryDate(deliveryDate, orderCreatedAt) ?? inferDeliveryDateFromLineItems({ deliveryDay, lineItems, orderCreatedAt, }) ?? + normalizeExplicitDeliveryDate(deliveryDate, orderCreatedAt) ?? inferDeliveryDateFromOrderCycle({ deliveryCycle, deliveryDay, @@ -122,7 +122,7 @@ export function inferDeliveryDateFromOrderCycle({ ); const cutoffWeekdayIndex = WEEKDAY_INDEX_BY_CODE[cycle.cutoffWeekday]; let daysUntilCutoff = (cutoffWeekdayIndex - orderDate.getUTCDay() + 7) % 7; - if (daysUntilCutoff === 0 && getLocalMinutes(orderLocalDate) > parseCutoffMinutes(cycle.cutoffTime)) { + if (daysUntilCutoff === 0 && getLocalMinutes(orderLocalDate) >= parseCutoffMinutes(cycle.cutoffTime)) { daysUntilCutoff = 7; } diff --git a/apps/shopify-app/app/features/delivery/delivery-labels.test.js b/apps/shopify-app/app/features/delivery/delivery-labels.test.js index 0155fa3..ff55276 100644 --- a/apps/shopify-app/app/features/delivery/delivery-labels.test.js +++ b/apps/shopify-app/app/features/delivery/delivery-labels.test.js @@ -88,6 +88,36 @@ test("honors a configured delivery cycle cutoff time", () => { ); }); +test("keeps orders placed through 16:59 in the current K-food delivery cycle", () => { + assert.equal( + inferDeliveryDateFromOrderCycle({ + deliveryCycle: { + cutoffTime: "17:00", + cutoffWeekday: "TUESDAY", + timeZone: "America/Toronto", + }, + deliveryDay: "Friday", + orderCreatedAt: "2026-05-05T20:59:00.000Z", + }), + "2026-05-08", + ); +}); + +test("starts the next K-food delivery cycle at exactly 17:00", () => { + assert.equal( + inferDeliveryDateFromOrderCycle({ + deliveryCycle: { + cutoffTime: "17:00", + cutoffWeekday: "TUESDAY", + timeZone: "America/Toronto", + }, + deliveryDay: "Friday", + orderCreatedAt: "2026-05-05T21:00:00.000Z", + }), + "2026-05-15", + ); +}); + test("normalizes invalid delivery cycle settings to the current default", () => { assert.deepEqual( normalizeDeliveryCycle({ cutoffTime: "99:99", cutoffWeekday: "NOPE" }), @@ -108,15 +138,16 @@ test("uses the order cycle when Shopify line items do not include a date range", ); }); -test("uses an explicit delivery date before weekday/range inference", () => { +test("uses the product date range before an explicit delivery date", () => { assert.equal( inferDeliveryDateForOrder({ deliveryDate: "2026-05-18", + deliveryDay: "Friday", lineItems: { - nodes: [{ title: "CLEVER 2026.05.18-05.31" }], + nodes: [{ title: "CLEVER 2026.05.21-05.23" }], }, - orderCreatedAt: null, + orderCreatedAt: "2026-05-01T15:30:00.000Z", }), - "2026-05-18", + "2026-05-22", ); }); diff --git a/apps/shopify-app/app/features/delivery/orders.server.js b/apps/shopify-app/app/features/delivery/orders.server.js index 191c5bf..084df8e 100644 --- a/apps/shopify-app/app/features/delivery/orders.server.js +++ b/apps/shopify-app/app/features/delivery/orders.server.js @@ -10,6 +10,7 @@ const DELIVERY_ORDERS_SYNC_REASON = "orders_page_open"; export async function syncDeliveryOrders(request, payload = {}, options = {}) { const result = await deliveryApiRequest(request, "/admin/orders/sync", { body: JSON.stringify({ + ...(payload.deliveryCycle ? { deliveryCycle: payload.deliveryCycle } : {}), source: DELIVERY_ORDERS_SYNC_SOURCE, reason: payload.reason ?? DELIVERY_ORDERS_SYNC_REASON, orders: Array.isArray(payload.orders) ? payload.orders : [], diff --git a/apps/shopify-app/app/features/delivery/orders.server.test.js b/apps/shopify-app/app/features/delivery/orders.server.test.js index 52a6074..3667ac8 100644 --- a/apps/shopify-app/app/features/delivery/orders.server.test.js +++ b/apps/shopify-app/app/features/delivery/orders.server.test.js @@ -18,7 +18,15 @@ test("syncs delivery orders through the delivery Admin API with an explicit clie const result = await syncDeliveryOrders( new Request("https://app.example/app/orders"), - { reason: "manual_refresh", orders }, + { + deliveryCycle: { + cutoffTime: "17:00", + cutoffWeekday: "TUESDAY", + timeZone: "America/Toronto", + }, + reason: "manual_refresh", + orders, + }, { fetch: async (url, options) => { calls.push({ url, options }); @@ -48,6 +56,11 @@ test("syncs delivery orders through the delivery Admin API with an explicit clie assert.equal(calls[0].options.headers["x-clever-app-id"], "clever-route-dev"); assert.equal(calls[0].options.headers["content-type"], "application/json"); assert.deepEqual(JSON.parse(calls[0].options.body), { + deliveryCycle: { + cutoffTime: "17:00", + cutoffWeekday: "TUESDAY", + timeZone: "America/Toronto", + }, source: "clever-app-orders", reason: "manual_refresh", orders, diff --git a/apps/shopify-app/app/features/delivery/route-detail.server.js b/apps/shopify-app/app/features/delivery/route-detail.server.js index 430794c..5401ffd 100644 --- a/apps/shopify-app/app/features/delivery/route-detail.server.js +++ b/apps/shopify-app/app/features/delivery/route-detail.server.js @@ -663,7 +663,11 @@ export async function refreshRouteOrders({ const snapshots = getOrderSyncSnapshots(shopifyOrderData.orders); const syncedOrderData = await syncDeliveryOrders( request, - { reason: "manual_refresh", orders: snapshots }, + { + deliveryCycle: preferencesData.appPreferences.deliveryCycle, + reason: "manual_refresh", + orders: snapshots, + }, { cacheKey: shopifyShopCacheKey, sessionToken }, ); errors.push(...(syncedOrderData.errors ?? [])); diff --git a/apps/shopify-app/app/features/orders/orders-page.server.js b/apps/shopify-app/app/features/orders/orders-page.server.js index 7e3e907..d6b2628 100644 --- a/apps/shopify-app/app/features/orders/orders-page.server.js +++ b/apps/shopify-app/app/features/orders/orders-page.server.js @@ -191,9 +191,18 @@ async function handleOrdersAction(request) { return { syncedOrders: [], sync: null, errors: [] }; } + const preferencesData = await fetchShopifyAppPreferences(admin); + if ((preferencesData.errors ?? []).length > 0) { + return { syncedOrders: [], sync: null, errors: preferencesData.errors }; + } + const syncedOrderData = await syncDeliveryOrders( request, - { reason: "orders_page_open", orders: orderSnapshots }, + { + deliveryCycle: preferencesData.appPreferences.deliveryCycle, + reason: "orders_page_open", + orders: orderSnapshots, + }, { cacheKey: shopifyShopCacheKey, primeOrdersCache: true, @@ -471,6 +480,7 @@ async function resolvePlannedOrdersForAction({ ? await syncDeliveryOrders( request, { + deliveryCycle: preferencesData.appPreferences.deliveryCycle, reason, orders: plannedShopifyOrderSnapshots, },