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,
}),
],
}),
])
)
})
})
})
},