Skip to content

Commit d9af2f5

Browse files
authored
feat(core-flows,order,medusa,types): Version shipping method adjustments & implement missing creation flow for versioned adjustments (#14482)
* Pass shipping_methods to computeActions context * Tests * Add changeset * Implementation plan * Update db model and add missing indexes * Migration * Update todos status * Data migration script * Update plan * Create versioned shipping methods upon order changes * Delete adjustments on version reversion * Update plan * Fix * Module tests * Add changeset * New order change action type to include shipping methods adjustments on previews * Inlcude backfill inside migration file since it is not cross module * Fix tests * Add versioned find of shipping_method adjustments to order repo * Pass adjustments to populate when related entity * Fix tests * Remove new indes on order line item adjustment and version * Test * Update
1 parent ba364f1 commit d9af2f5

File tree

20 files changed

+1923
-318
lines changed

20 files changed

+1923
-318
lines changed

.changeset/sixty-eagles-melt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@medusajs/core-flows": patch
3+
---
4+
5+
fix(core-flows): Pass shipping_methods to computeActions context

.changeset/strong-ants-warn.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@medusajs/core-flows": patch
3+
"@medusajs/order": patch
4+
"@medusajs/types": patch
5+
"@medusajs/medusa": patch
6+
---
7+
8+
feat(core-flows,order,medusa,types): Version shipping method adjustments & implement missing creation flow for versioned adjustments

integration-tests/http/__tests__/order-edits/order-edits.spec.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2656,6 +2656,186 @@ medusaIntegrationTestRunner({
26562656
expect(orderResult2.total).toEqual(20)
26572657
expect(orderResult2.original_total).toEqual(20)
26582658
})
2659+
2660+
it("should maintain shipping method adjustments when adding a new item with promotion targeting shipping methods", async () => {
2661+
// Create a promotion that targets shipping methods
2662+
const shippingPromotion = await promotionModule.createPromotions({
2663+
code: "SHIPPING_PROMO",
2664+
type: PromotionType.STANDARD,
2665+
status: PromotionStatus.ACTIVE,
2666+
application_method: {
2667+
type: "fixed",
2668+
target_type: "shipping_methods",
2669+
allocation: "each",
2670+
value: 2,
2671+
max_quantity: 1,
2672+
currency_code: "usd",
2673+
target_rules: [
2674+
{
2675+
attribute: "name",
2676+
operator: "in",
2677+
values: ["Test shipping method"],
2678+
},
2679+
],
2680+
},
2681+
})
2682+
2683+
// Create an order with shipping method
2684+
// @ts-ignore
2685+
const orderWithShipping = await orderModule.createOrders({
2686+
email: "foo@bar.com",
2687+
region_id: region.id,
2688+
sales_channel_id: salesChannel.id,
2689+
items: [
2690+
{
2691+
// @ts-ignore
2692+
id: "item-shipping-1",
2693+
title: "Custom Item",
2694+
quantity: 1,
2695+
unit_price: 10,
2696+
},
2697+
],
2698+
shipping_address: {
2699+
first_name: "Test",
2700+
last_name: "Test",
2701+
address_1: "Test",
2702+
city: "Test",
2703+
country_code: "US",
2704+
postal_code: "12345",
2705+
phone: "12345",
2706+
},
2707+
billing_address: {
2708+
first_name: "Test",
2709+
last_name: "Test",
2710+
address_1: "Test",
2711+
city: "Test",
2712+
country_code: "US",
2713+
postal_code: "12345",
2714+
},
2715+
shipping_methods: [
2716+
{
2717+
name: "Test shipping method",
2718+
amount: 5,
2719+
},
2720+
],
2721+
currency_code: "usd",
2722+
})
2723+
2724+
// Create shipping method adjustment for the promotion
2725+
if (orderWithShipping.shipping_methods?.[0]) {
2726+
await orderModule.createOrderShippingMethodAdjustments(
2727+
orderWithShipping.id,
2728+
[
2729+
{
2730+
shipping_method_id: orderWithShipping.shipping_methods[0].id,
2731+
code: shippingPromotion.code!,
2732+
amount: 2,
2733+
},
2734+
]
2735+
)
2736+
}
2737+
2738+
// Link promotion to order
2739+
await remoteLink.create({
2740+
[Modules.ORDER]: { order_id: orderWithShipping.id },
2741+
[Modules.PROMOTION]: { promotion_id: shippingPromotion.id },
2742+
})
2743+
2744+
// Create order edit
2745+
let result = await api.post(
2746+
"/admin/order-edits",
2747+
{
2748+
order_id: orderWithShipping.id,
2749+
description: "Test shipping promotion",
2750+
},
2751+
adminHeaders
2752+
)
2753+
2754+
const orderChangeId = result.data.order_change.id
2755+
const orderId = result.data.order_change.order_id
2756+
2757+
// Enable carry over promotions
2758+
await api.post(
2759+
`/admin/order-changes/${orderChangeId}`,
2760+
{
2761+
carry_over_promotions: true,
2762+
},
2763+
adminHeaders
2764+
)
2765+
2766+
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
2767+
.order
2768+
2769+
// Original order: $10 item + $5 shipping - $2 shipping discount = $13
2770+
expect(result.total).toEqual(13)
2771+
expect(result.shipping_methods[0].adjustments).toEqual([
2772+
expect.objectContaining({
2773+
code: shippingPromotion.code!,
2774+
amount: 2,
2775+
}),
2776+
])
2777+
2778+
// Add item with price $12
2779+
result = (
2780+
await api.post(
2781+
`/admin/order-edits/${orderId}/items`,
2782+
{
2783+
items: [
2784+
{
2785+
variant_id: productExtra.variants[0].id,
2786+
quantity: 1,
2787+
},
2788+
],
2789+
},
2790+
adminHeaders
2791+
)
2792+
).data.order_preview
2793+
2794+
// After adding item: $22 items + $5 shipping - $2 shipping discount + $1.2 tax = $26.2
2795+
expect(result.total).toEqual(26.2)
2796+
expect(result.original_total).toEqual(28.2) // +$2 shipping discount
2797+
2798+
// Shipping method adjustment should still be present
2799+
expect(result.shipping_methods[0].adjustments).toEqual([
2800+
expect.objectContaining({
2801+
code: shippingPromotion.code!,
2802+
amount: 2,
2803+
}),
2804+
])
2805+
2806+
// Confirm original order is not updated
2807+
const orderResult = (
2808+
await api.get(`/admin/orders/${orderId}`, adminHeaders)
2809+
).data.order
2810+
2811+
expect(orderResult.total).toEqual(13)
2812+
expect(orderResult.shipping_methods[0].adjustments).toEqual([
2813+
expect.objectContaining({
2814+
code: shippingPromotion.code!,
2815+
amount: 2,
2816+
}),
2817+
])
2818+
2819+
// Confirm the order edit
2820+
await api.post(
2821+
`/admin/order-edits/${orderId}/confirm`,
2822+
{},
2823+
adminHeaders
2824+
)
2825+
2826+
const orderResult2 = (
2827+
await api.get(`/admin/orders/${orderId}`, adminHeaders)
2828+
).data.order
2829+
2830+
expect(orderResult2.total).toEqual(26.2)
2831+
expect(orderResult2.original_total).toEqual(28.2)
2832+
expect(orderResult2.shipping_methods[0].adjustments).toEqual([
2833+
expect.objectContaining({
2834+
code: shippingPromotion.code!,
2835+
amount: 2,
2836+
}),
2837+
])
2838+
})
26592839
})
26602840
},
26612841
})

integration-tests/modules/__tests__/order/order.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,16 +637,33 @@ medusaIntegrationTestRunner({
637637
value: "1.1",
638638
precision: 20,
639639
},
640+
version: 1,
640641
},
641642
]),
642643
amount: 10,
643644
subtotal: 10,
644645
total: 9.9,
645646
original_total: 11,
647+
original_subtotal: 10,
646648
discount_total: 1.1,
649+
discount_subtotal: 1,
647650
discount_tax_total: 0.1,
648651
tax_total: 0.9,
649652
original_tax_total: 1,
653+
is_custom_amount: false,
654+
deleted_at: null,
655+
detail: expect.objectContaining({
656+
id: expect.any(String),
657+
order_id: expect.any(String),
658+
shipping_method_id: expect.any(String),
659+
version: 1,
660+
created_at: expect.any(String),
661+
updated_at: expect.any(String),
662+
deleted_at: null,
663+
claim_id: null,
664+
exchange_id: null,
665+
return_id: null,
666+
}),
650667
raw_subtotal: {
651668
value: "10",
652669
precision: 20,
@@ -659,10 +676,18 @@ medusaIntegrationTestRunner({
659676
value: "11",
660677
precision: 20,
661678
},
679+
raw_original_subtotal: {
680+
value: "10",
681+
precision: 20,
682+
},
662683
raw_discount_total: {
663684
value: "1.1",
664685
precision: 20,
665686
},
687+
raw_discount_subtotal: {
688+
value: "1",
689+
precision: 20,
690+
},
666691
raw_discount_tax_total: {
667692
value: "0.1",
668693
precision: 20,

packages/core/core-flows/src/draft-order/workflows/compute-draft-order-adjustments.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ export const computeDraftOrderAdjustmentsWorkflow = createWorkflow(
146146
})
147147

148148
const actionsToComputeItemsInput = prepareOrderComputeActionContextStep({
149-
order,
150-
previewedOrder,
151-
})
149+
order,
150+
previewedOrder,
151+
})
152152

153153
const actions = getActionsToComputeFromPromotionsStep({
154154
computeActionContext: actionsToComputeItemsInput,

packages/core/core-flows/src/order/workflows/compute-adjustments-for-preview.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { OrderChangeDTO, OrderDTO, PromotionDTO } from "@medusajs/framework/types"
1+
import {
2+
OrderChangeDTO,
3+
OrderDTO,
4+
PromotionDTO,
5+
} from "@medusajs/framework/types"
26
import { ChangeActionType } from "@medusajs/framework/utils"
37
import {
48
createWorkflow,
@@ -102,7 +106,7 @@ export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
102106
},
103107
})
104108

105-
const { lineItemAdjustmentsToCreate } =
109+
const { lineItemAdjustmentsToCreate, shippingMethodAdjustmentsToCreate } =
106110
prepareAdjustmentsFromPromotionActionsStep({ actions })
107111

108112
const orderChangeActionAdjustmentsInput = transform(
@@ -111,14 +115,16 @@ export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
111115
previewedOrder,
112116
orderChange: input.orderChange,
113117
lineItemAdjustmentsToCreate,
118+
shippingMethodAdjustmentsToCreate,
114119
},
115120
({
116121
order,
117122
previewedOrder,
118123
orderChange,
119124
lineItemAdjustmentsToCreate,
125+
shippingMethodAdjustmentsToCreate,
120126
}) => {
121-
return previewedOrder.items.map((item) => {
127+
const itemActions = previewedOrder.items.map((item) => {
122128
const itemAdjustments = lineItemAdjustmentsToCreate.filter(
123129
(adjustment) => adjustment.item_id === item.id
124130
)
@@ -137,6 +143,32 @@ export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
137143
},
138144
}
139145
})
146+
147+
const shippingActions = previewedOrder.shipping_methods.map(
148+
(shippingMethod) => {
149+
const shippingAdjustments =
150+
shippingMethodAdjustmentsToCreate.filter(
151+
(adjustment) =>
152+
adjustment.shipping_method_id === shippingMethod.id
153+
)
154+
155+
return {
156+
order_change_id: orderChange.id,
157+
order_id: order.id,
158+
exchange_id: orderChange.exchange_id ?? undefined,
159+
claim_id: orderChange.claim_id ?? undefined,
160+
return_id: orderChange.return_id ?? undefined,
161+
version: orderChange.version,
162+
action: ChangeActionType.SHIPPING_ADJUSTMENTS_REPLACE,
163+
details: {
164+
reference_id: shippingMethod.id,
165+
adjustments: shippingAdjustments,
166+
},
167+
}
168+
}
169+
)
170+
171+
return [...itemActions, ...shippingActions]
140172
}
141173
)
142174

@@ -149,12 +181,24 @@ export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
149181
{ order: previewedOrder },
150182
({ order }) => !order.order_change.carry_over_promotions
151183
).then(() => {
152-
const actionIds = listOrderChangeActionsByTypeStep({
184+
const itemActionIds = listOrderChangeActionsByTypeStep({
153185
order_change_id: input.orderChange.id,
154186
action_type: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
155-
})
187+
}).config({ name: "list-item-adjustment-actions-for-deletion" })
188+
189+
const shippingActionIds = listOrderChangeActionsByTypeStep({
190+
order_change_id: input.orderChange.id,
191+
action_type: ChangeActionType.SHIPPING_ADJUSTMENTS_REPLACE,
192+
}).config({ name: "list-shipping-adjustment-actions-for-deletion" })
193+
194+
const allActionIds = transform(
195+
{ itemActionIds, shippingActionIds },
196+
({ itemActionIds, shippingActionIds }) => {
197+
return [...itemActionIds, ...shippingActionIds]
198+
}
199+
)
156200

157-
deleteOrderChangeActionsStep({ ids: actionIds })
201+
deleteOrderChangeActionsStep({ ids: allActionIds })
158202
})
159203
}
160204
)

packages/core/types/src/order/common.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type ChangeActionType =
3131
| "PROMOTION_ADD"
3232
| "PROMOTION_REMOVE"
3333
| "ITEM_ADJUSTMENTS_REPLACE"
34+
| "SHIPPING_ADJUSTMENTS_REPLACE"
3435

3536
export type OrderChangeStatus =
3637
| "confirmed"
@@ -130,6 +131,11 @@ export interface OrderShippingMethodAdjustmentDTO
130131
* The ID of the associated shipping method.
131132
*/
132133
shipping_method_id: string
134+
135+
/**
136+
* The version of the adjustment.
137+
*/
138+
version: number
133139
}
134140

135141
/**
@@ -147,6 +153,11 @@ export interface OrderLineItemAdjustmentDTO extends OrderAdjustmentLineDTO {
147153
* The ID of the associated line item.
148154
*/
149155
item_id: string
156+
157+
/**
158+
* The version of the adjustment.
159+
*/
160+
version: number
150161
}
151162

152163
/**

0 commit comments

Comments
 (0)