feat: carry over promotions toggle on exchanges (#14128)

* feat: carry over promotions toggle on exchanges

* fix: inital flag value, return the flag on preview

* fix: validation of allocation type

* fix: revert client changes

* fix: invert condition

* feat: recompute adjustments when outbound item is updated

* fix: condition again

* fix: display more accurate inbound/outbound totals for exchanges

* fix: make exchanges specs green

* feat: more testing cases

* wip: pr feedback

* fix: use plural for the flag on Admin

* fix: schema test, route refactor

* feat: tooltip

* feat: refactor to use update workflow

* feat: display applied promotion per item on order details, show copy sku on hover

* feat: refactor edits and exchanges to have common flag toggle flow

* fix: delete empty file

* fix: exchange_id param query
This commit is contained in:
Frane Polić
2025-11-30 19:31:31 +01:00
committed by GitHub
parent 9d1f09ac7b
commit 5da51064d7
40 changed files with 1367 additions and 214 deletions

View File

@@ -1089,6 +1089,19 @@ medusaIntegrationTestRunner({
expect(result.original_total).toEqual(11) // $10 + 10% tax
expect(result.total).toEqual(10 * 0.9 * 1.1) // ($10 - 10% discount) + 10% tax
const orderChange = (
await api.get(`/admin/orders/${orderId}/preview`, adminHeaders)
).data.order.order_change
// opt in for carry over promotions
await api.post(
`/admin/order-changes/${orderChange.id}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// Add outbound item with price $12, 10% discount and 10% tax
result = (
await api
@@ -1214,6 +1227,363 @@ medusaIntegrationTestRunner({
expect(orderResult2.total).toEqual(11.016)
expect(orderResult2.original_total).toEqual(12.24)
})
it("should enable carry_over_promotions flag and apply promotions to outbound items (flag disabled before request)", async () => {
// fulfill item so it can be returned
await api.post(
`/admin/orders/${orderWithPromotion.id}/fulfillments`,
{
items: [
{
id: orderWithPromotion.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
let result = await api.post(
"/admin/exchanges",
{
order_id: orderWithPromotion.id,
description: "Test",
},
adminHeaders
)
const exchangeId = result.data.exchange.id
// Query order change for the exchange
const orderChange = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order.order_change
const orderChangeId = orderChange.id
// return original item
await api.post(
`/admin/exchanges/${exchangeId}/inbound/items`,
{
items: [
{
id: orderWithPromotion.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
// add outbound item
await api.post(
`/admin/exchanges/${exchangeId}/outbound/items`,
{
items: [
{
variant_id: productForAdjustmentTest.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
// Initially, promotions should be disabled by default when adding outbound items
let orderPreview = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order
expect(orderPreview.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [], // outbound item has no adjustments initially
}),
])
)
// Enable carry_over_promotions
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// Verify adjustments are added
orderPreview = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order
expect(orderPreview.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [
// outbound item has adjustments after carry_over_promotions is enabled
expect.objectContaining({
amount: 1.2,
}),
],
}),
])
)
// Disable carry_over_promotions
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: false,
},
adminHeaders
)
// Verify adjustments are removed again
orderPreview = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order
expect(orderPreview.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [], // outbound item has no adjustments
}),
])
)
await api.post(
`/admin/exchanges/${exchangeId}/request`,
{},
adminHeaders
)
const finalOrder = (
await api.get(
`/admin/orders/${orderWithPromotion.id}`,
adminHeaders
)
).data.order
// items adjustment state is equal to the last state of the order preview (flag disabled)
expect(finalOrder.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [],
}),
])
)
})
it("should enable carry_over_promotions flag and apply promotions to outbound items (flag enabled before request)", async () => {
// fulfill item so it can be returned
await api.post(
`/admin/orders/${orderWithPromotion.id}/fulfillments`,
{
items: [
{
id: orderWithPromotion.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
let result = await api.post(
"/admin/exchanges",
{
order_id: orderWithPromotion.id,
description: "Test",
},
adminHeaders
)
const exchangeId = result.data.exchange.id
// Query order change for the exchange
const orderChange = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order.order_change
const orderChangeId = orderChange.id
// return original item
await api.post(
`/admin/exchanges/${exchangeId}/inbound/items`,
{
items: [
{
id: orderWithPromotion.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
// add outbound item
await api.post(
`/admin/exchanges/${exchangeId}/outbound/items`,
{
items: [
{
variant_id: productForAdjustmentTest.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
let orderPreview = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order
expect(orderPreview.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [], // outbound item has no adjustments initially
}),
])
)
// Enable carry_over_promotions
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// Verify adjustments are added
orderPreview = (
await api.get(
`/admin/orders/${orderWithPromotion.id}/preview`,
adminHeaders
)
).data.order
expect(orderPreview.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [
// outbound item has adjustments after carry_over_promotions is enabled
expect.objectContaining({
amount: 1.2,
}),
],
}),
])
)
await api.post(
`/admin/exchanges/${exchangeId}/request`,
{},
adminHeaders
)
const finalOrder = (
await api.get(
`/admin/orders/${orderWithPromotion.id}`,
adminHeaders
)
).data.order
// items adjustment state is equal to the last state of the order preview (flag enabled)
expect(finalOrder.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "item-1", // original item
adjustments: [
expect.objectContaining({
amount: 1,
}),
],
}),
expect.objectContaining({
variant_id: productForAdjustmentTest.variants[0].id,
adjustments: [
expect.objectContaining({
amount: 1.2,
}),
],
}),
])
)
})
})
})
},

View File

@@ -1297,6 +1297,16 @@ medusaIntegrationTestRunner({
adminHeaders
)
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const orderId = result.data.order_change.order_id
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
@@ -1375,6 +1385,16 @@ medusaIntegrationTestRunner({
const orderId = result.data.order_change.order_id
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const item = order.items[0]
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
@@ -1451,6 +1471,16 @@ medusaIntegrationTestRunner({
const orderId = result.data.order_change.order_id
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const item = order.items[0]
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
@@ -1559,6 +1589,16 @@ medusaIntegrationTestRunner({
)
const orderChange1 = response.data.order_change
// allow carry over promotions flag on the edit
const orderChangeId = response.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// 2. Add a new item in the first edit
response = await api.post(
`/admin/order-edits/${orderChange1.order_id}/items`,
@@ -1648,6 +1688,13 @@ medusaIntegrationTestRunner({
adminHeaders
)
const orderChange2 = response.data.order_change
await api.post(
`/admin/order-changes/${orderChange2.id}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// 5. Add another productExtra item
response = await api.post(
@@ -1943,6 +1990,17 @@ medusaIntegrationTestRunner({
},
adminHeaders
)
// allow carry over promotions flag on the edit
const orderChangeId = editRes.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const editOrderId = editRes.data.order_change.order_id
const extraVariantId = productExtra.variants[0].id
@@ -2038,6 +2096,16 @@ medusaIntegrationTestRunner({
const orderId = result.data.order_change.order_id
const originalItem = order.items[0]
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
// Add a new item
result = (
await api.post(
@@ -2154,6 +2222,16 @@ medusaIntegrationTestRunner({
adminHeaders
)
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const orderId = result.data.order_change.order_id
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
@@ -2332,6 +2410,16 @@ medusaIntegrationTestRunner({
adminHeaders
)
// allow carry over promotions flag on the edit
const orderChangeId = result.data.order_change.id
await api.post(
`/admin/order-changes/${orderChangeId}`,
{
carry_over_promotions: true,
},
adminHeaders
)
const orderId = result.data.order_change.order_id
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data