From de778fb4e55d172469dfd789b3676463ee30c2c4 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 21 Aug 2024 15:15:09 +0200 Subject: [PATCH 1/5] fix(core-flows): account for unfulfilled items while generating order status --- .../http/__tests__/claims/claims.spec.ts | 81 ++++++++++++++++++- .../src/order/utils/aggregate-status.ts | 31 +++++-- .../src/order/workflows/get-orders-list.ts | 1 + 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/integration-tests/http/__tests__/claims/claims.spec.ts b/integration-tests/http/__tests__/claims/claims.spec.ts index 7960ff75097d2..3771881e9a8fa 100644 --- a/integration-tests/http/__tests__/claims/claims.spec.ts +++ b/integration-tests/http/__tests__/claims/claims.spec.ts @@ -877,7 +877,7 @@ medusaIntegrationTestRunner({ items: [ { variant_id: productExtra.variants[0].id, - quantity: 2, + quantity: 3, }, ], }, @@ -917,7 +917,7 @@ medusaIntegrationTestRunner({ await api.get(`/admin/orders/${order.id}`, adminHeaders) ).data.order - const fulfillableItem = fulfillOrder.items.find( + const fulfillableItem = fulfillOrder.items.filter( (item) => item.detail.fulfilled_quantity === 0 ) @@ -925,10 +925,85 @@ medusaIntegrationTestRunner({ `/admin/orders/${order.id}/fulfillments`, { location_id: location.id, - items: [{ id: fulfillableItem.id, quantity: 1 }], + items: [{ id: fulfillableItem[0].id, quantity: 1 }], + }, + adminHeaders + ) + + let orderResult = ( + await api.get(`/admin/orders/${order.id}`, adminHeaders) + ).data.order + + expect(orderResult.fulfillment_status).toEqual("partially_fulfilled") + + await api.post( + `/admin/orders/${order.id}/fulfillments`, + { + location_id: location.id, + items: [{ id: fulfillableItem[0].id, quantity: 2 }], }, adminHeaders ) + + orderResult = ( + await api.get( + `/admin/orders/${order.id}?fields=*fulfillments,*fulfillments.items`, + adminHeaders + ) + ).data.order + + expect(orderResult.fulfillment_status).toEqual("fulfilled") + + await api.post( + `admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[0].id}/shipments`, + { + items: orderResult.fulfillments[0]?.items?.map((i) => ({ + id: i.line_item_id, + quantity: i.quantity, + })), + }, + adminHeaders + ) + + orderResult = ( + await api.get( + `/admin/orders/${order.id}?fields=*fulfillments,*fulfillments.items`, + adminHeaders + ) + ).data.order + + expect(orderResult.fulfillment_status).toEqual("partially_shipped") + + await api.post( + `admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[1].id}/shipments`, + { + items: orderResult.fulfillments[1]?.items?.map((i) => ({ + id: i.line_item_id, + quantity: i.quantity, + })), + }, + adminHeaders + ) + + await api.post( + `admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[2].id}/shipments`, + { + items: orderResult.fulfillments[2]?.items?.map((i) => ({ + id: i.line_item_id, + quantity: i.quantity, + })), + }, + adminHeaders + ) + + orderResult = ( + await api.get( + `/admin/orders/${order.id}?fields=*fulfillments`, + adminHeaders + ) + ).data.order + + expect(orderResult.fulfillment_status).toEqual("shipped") }) }) }) diff --git a/packages/core/core-flows/src/order/utils/aggregate-status.ts b/packages/core/core-flows/src/order/utils/aggregate-status.ts index 9d0733e0f3477..b2d23d9251d77 100644 --- a/packages/core/core-flows/src/order/utils/aggregate-status.ts +++ b/packages/core/core-flows/src/order/utils/aggregate-status.ts @@ -1,5 +1,5 @@ import { OrderDetailDTO } from "@medusajs/types" -import { MathBN } from "@medusajs/utils" +import { isDefined, MathBN } from "@medusajs/utils" export const getLastPaymentStatus = (order: OrderDetailDTO) => { const PaymentStatus = { @@ -103,17 +103,22 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { } let fulfillmentStatus = {} + for (const status in FulfillmentStatus) { fulfillmentStatus[FulfillmentStatus[status]] = 0 } const statusMap = { - packed_at: FulfillmentStatus.FULFILLED, - shipped_at: FulfillmentStatus.SHIPPED, - delivered_at: FulfillmentStatus.DELIVERED, canceled_at: FulfillmentStatus.CANCELED, + delivered_at: FulfillmentStatus.DELIVERED, + shipped_at: FulfillmentStatus.SHIPPED, + packed_at: FulfillmentStatus.FULFILLED, } + for (const fulfillmentCollection of order.fulfillments) { + // Note: The order of the statusMap keys is important as we break + // the loop when we have found a match. The match should be prioritized + // based on order of precedence of statuses for (const key in statusMap) { if (fulfillmentCollection[key]) { fulfillmentStatus[statusMap[key]] += 1 @@ -126,10 +131,20 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { const totalFulfillmentsExceptCanceled = totalFulfillments - fulfillmentStatus[FulfillmentStatus.CANCELED] + // Whenever there are any unfulfilled items in the order, it should be + // considered partially_[STATUS] where status is picked up from the hierarchy + // of statuses + const unfulfilledItems = order.items!.filter( + (i) => + isDefined(i?.detail?.fulfilled_quantity) && + i.detail.fulfilled_quantity < i.quantity + ) + if (fulfillmentStatus[FulfillmentStatus.DELIVERED] > 0) { if ( fulfillmentStatus[FulfillmentStatus.DELIVERED] === - totalFulfillmentsExceptCanceled + totalFulfillmentsExceptCanceled && + !unfulfilledItems.length ) { return FulfillmentStatus.DELIVERED } @@ -140,7 +155,8 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { if (fulfillmentStatus[FulfillmentStatus.SHIPPED] > 0) { if ( fulfillmentStatus[FulfillmentStatus.SHIPPED] === - totalFulfillmentsExceptCanceled + totalFulfillmentsExceptCanceled && + !unfulfilledItems.length ) { return FulfillmentStatus.SHIPPED } @@ -151,7 +167,8 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { if (fulfillmentStatus[FulfillmentStatus.FULFILLED] > 0) { if ( fulfillmentStatus[FulfillmentStatus.FULFILLED] === - totalFulfillmentsExceptCanceled + totalFulfillmentsExceptCanceled && + !unfulfilledItems.length ) { return FulfillmentStatus.FULFILLED } diff --git a/packages/core/core-flows/src/order/workflows/get-orders-list.ts b/packages/core/core-flows/src/order/workflows/get-orders-list.ts index 9d91aa3f50a91..936c8e92cc932 100644 --- a/packages/core/core-flows/src/order/workflows/get-orders-list.ts +++ b/packages/core/core-flows/src/order/workflows/get-orders-list.ts @@ -37,6 +37,7 @@ export const getOrdersListWorkflow = createWorkflow( "id", "status", "version", + "items.*", "payment_collections.status", "payment_collections.amount", "payment_collections.captured_amount", From 375dbc7a080dddb859986d2e8617ea49b526c9f5 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 21 Aug 2024 15:41:35 +0200 Subject: [PATCH 2/5] chore: traverse safely --- packages/core/core-flows/src/order/utils/aggregate-status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/core-flows/src/order/utils/aggregate-status.ts b/packages/core/core-flows/src/order/utils/aggregate-status.ts index b2d23d9251d77..c2d38a7bd0903 100644 --- a/packages/core/core-flows/src/order/utils/aggregate-status.ts +++ b/packages/core/core-flows/src/order/utils/aggregate-status.ts @@ -134,7 +134,7 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { // Whenever there are any unfulfilled items in the order, it should be // considered partially_[STATUS] where status is picked up from the hierarchy // of statuses - const unfulfilledItems = order.items!.filter( + const unfulfilledItems = (order.items || [])?.filter( (i) => isDefined(i?.detail?.fulfilled_quantity) && i.detail.fulfilled_quantity < i.quantity From 8762139d08e2e84d8bf1bd7c6619a3d19f49f186 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 21 Aug 2024 16:19:00 +0200 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com> --- .../core/core-flows/src/order/utils/aggregate-status.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/core-flows/src/order/utils/aggregate-status.ts b/packages/core/core-flows/src/order/utils/aggregate-status.ts index c2d38a7bd0903..accdc486b600d 100644 --- a/packages/core/core-flows/src/order/utils/aggregate-status.ts +++ b/packages/core/core-flows/src/order/utils/aggregate-status.ts @@ -134,17 +134,17 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { // Whenever there are any unfulfilled items in the order, it should be // considered partially_[STATUS] where status is picked up from the hierarchy // of statuses - const unfulfilledItems = (order.items || [])?.filter( + const hasUnfulfilledItems = (order.items || [])?.filter( (i) => isDefined(i?.detail?.fulfilled_quantity) && - i.detail.fulfilled_quantity < i.quantity - ) + MathBN.lt(i.detail.raw_fulfilled_quantity, i.raw_quantity) + ).length > 0 if (fulfillmentStatus[FulfillmentStatus.DELIVERED] > 0) { if ( fulfillmentStatus[FulfillmentStatus.DELIVERED] === totalFulfillmentsExceptCanceled && - !unfulfilledItems.length + !hasUnfulfilledItems ) { return FulfillmentStatus.DELIVERED } From fe637666c5b2f365a6ba03abb302f3658792d9df Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 21 Aug 2024 16:19:34 +0200 Subject: [PATCH 4/5] Apply suggestions from code review --- packages/core/core-flows/src/order/utils/aggregate-status.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/core-flows/src/order/utils/aggregate-status.ts b/packages/core/core-flows/src/order/utils/aggregate-status.ts index accdc486b600d..d2fc85a272e99 100644 --- a/packages/core/core-flows/src/order/utils/aggregate-status.ts +++ b/packages/core/core-flows/src/order/utils/aggregate-status.ts @@ -156,7 +156,7 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { if ( fulfillmentStatus[FulfillmentStatus.SHIPPED] === totalFulfillmentsExceptCanceled && - !unfulfilledItems.length + !hasUnfulfilledItems ) { return FulfillmentStatus.SHIPPED } @@ -168,7 +168,7 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { if ( fulfillmentStatus[FulfillmentStatus.FULFILLED] === totalFulfillmentsExceptCanceled && - !unfulfilledItems.length + !hasUnfulfilledItems ) { return FulfillmentStatus.FULFILLED } From 31ad67d6471ac74e9cdf84e7e388e8a1c227bc1a Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 21 Aug 2024 16:20:13 +0200 Subject: [PATCH 5/5] Update packages/core/core-flows/src/order/utils/aggregate-status.ts --- packages/core/core-flows/src/order/utils/aggregate-status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/core-flows/src/order/utils/aggregate-status.ts b/packages/core/core-flows/src/order/utils/aggregate-status.ts index d2fc85a272e99..d59d26949ac44 100644 --- a/packages/core/core-flows/src/order/utils/aggregate-status.ts +++ b/packages/core/core-flows/src/order/utils/aggregate-status.ts @@ -136,7 +136,7 @@ export const getLastFulfillmentStatus = (order: OrderDetailDTO) => { // of statuses const hasUnfulfilledItems = (order.items || [])?.filter( (i) => - isDefined(i?.detail?.fulfilled_quantity) && + isDefined(i?.detail?.raw_fulfilled_quantity) && MathBN.lt(i.detail.raw_fulfilled_quantity, i.raw_quantity) ).length > 0