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:
1 parent
9d1f09ac7b
commit
5da51064d7
40 files changed
+1367
-214
No files matched your search
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/dashboard": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/order": patch
|
||||||
|
"@medusajs/js-sdk": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: carry over promotions toggle on exchanges
|
||||||
@@ -1089,6 +1089,19 @@ medusaIntegrationTestRunner({
|
|||||||
expect(result.original_total).toEqual(11) // $10 + 10% tax
|
expect(result.original_total).toEqual(11) // $10 + 10% tax
|
||||||
expect(result.total).toEqual(10 * 0.9 * 1.1) // ($10 - 10% discount) + 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
|
// Add outbound item with price $12, 10% discount and 10% tax
|
||||||
result = (
|
result = (
|
||||||
await api
|
await api
|
||||||
@@ -1214,6 +1227,363 @@ medusaIntegrationTestRunner({
|
|||||||
expect(orderResult2.total).toEqual(11.016)
|
expect(orderResult2.total).toEqual(11.016)
|
||||||
expect(orderResult2.original_total).toEqual(12.24)
|
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,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1297,6 +1297,16 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
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
|
const orderId = result.data.order_change.order_id
|
||||||
|
|
||||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
||||||
@@ -1375,6 +1385,16 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
const orderId = result.data.order_change.order_id
|
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]
|
const item = order.items[0]
|
||||||
|
|
||||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
||||||
@@ -1451,6 +1471,16 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
const orderId = result.data.order_change.order_id
|
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]
|
const item = order.items[0]
|
||||||
|
|
||||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
||||||
@@ -1559,6 +1589,16 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
const orderChange1 = response.data.order_change
|
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
|
// 2. Add a new item in the first edit
|
||||||
response = await api.post(
|
response = await api.post(
|
||||||
`/admin/order-edits/${orderChange1.order_id}/items`,
|
`/admin/order-edits/${orderChange1.order_id}/items`,
|
||||||
@@ -1648,6 +1688,13 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
const orderChange2 = response.data.order_change
|
const orderChange2 = response.data.order_change
|
||||||
|
await api.post(
|
||||||
|
`/admin/order-changes/${orderChange2.id}`,
|
||||||
|
{
|
||||||
|
carry_over_promotions: true,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
// 5. Add another productExtra item
|
// 5. Add another productExtra item
|
||||||
response = await api.post(
|
response = await api.post(
|
||||||
@@ -1943,6 +1990,17 @@ medusaIntegrationTestRunner({
|
|||||||
},
|
},
|
||||||
adminHeaders
|
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 editOrderId = editRes.data.order_change.order_id
|
||||||
const extraVariantId = productExtra.variants[0].id
|
const extraVariantId = productExtra.variants[0].id
|
||||||
|
|
||||||
@@ -2038,6 +2096,16 @@ medusaIntegrationTestRunner({
|
|||||||
const orderId = result.data.order_change.order_id
|
const orderId = result.data.order_change.order_id
|
||||||
const originalItem = order.items[0]
|
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
|
// Add a new item
|
||||||
result = (
|
result = (
|
||||||
await api.post(
|
await api.post(
|
||||||
@@ -2154,6 +2222,16 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
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
|
const orderId = result.data.order_change.order_id
|
||||||
|
|
||||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
||||||
@@ -2332,6 +2410,16 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
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
|
const orderId = result.data.order_change.order_id
|
||||||
|
|
||||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders)).data
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
import { sdk } from "../../lib/client"
|
import { sdk } from "../../lib/client"
|
||||||
import { queryClient } from "../../lib/query-client"
|
import { queryClient } from "../../lib/query-client"
|
||||||
import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory"
|
import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory"
|
||||||
import { inventoryItemsQueryKeys } from "./inventory"
|
|
||||||
import { reservationItemsQueryKeys } from "./reservations"
|
import { reservationItemsQueryKeys } from "./reservations"
|
||||||
|
import { inventoryItemsQueryKeys } from "./inventory"
|
||||||
|
|
||||||
const ORDERS_QUERY_KEY = "orders" as const
|
const ORDERS_QUERY_KEY = "orders" as const
|
||||||
const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey<"orders"> & {
|
const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey<"orders"> & {
|
||||||
@@ -406,3 +406,35 @@ export const useCreateOrderCreditLine = (
|
|||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useUpdateOrderChange = (
|
||||||
|
orderChangeId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
HttpTypes.AdminOrderChangeResponse,
|
||||||
|
FetchError,
|
||||||
|
{ carry_over_promotions: boolean }
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload: { carry_over_promotions: boolean }) =>
|
||||||
|
sdk.admin.order.updateOrderChange(orderChangeId, payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
const orderId = data.order_change.order_id
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.details(),
|
||||||
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.preview(orderId),
|
||||||
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.changes(orderId),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -2008,6 +2008,7 @@
|
|||||||
"uploadImagesLabel",
|
"uploadImagesLabel",
|
||||||
"uploadImagesHint",
|
"uploadImagesHint",
|
||||||
"invalidFileType",
|
"invalidFileType",
|
||||||
|
"fileTooLarge",
|
||||||
"failedToUpload",
|
"failedToUpload",
|
||||||
"deleteWarning_one",
|
"deleteWarning_one",
|
||||||
"deleteWarning_other",
|
"deleteWarning_other",
|
||||||
@@ -4907,6 +4908,15 @@
|
|||||||
"refundAmount": {
|
"refundAmount": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"carryOverPromotion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"carryOverPromotionHint": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"carryOverPromotionTooltip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"activeChangeError": {
|
"activeChangeError": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -4988,6 +4998,9 @@
|
|||||||
"outboundShipping",
|
"outboundShipping",
|
||||||
"outboundShippingHint",
|
"outboundShippingHint",
|
||||||
"refundAmount",
|
"refundAmount",
|
||||||
|
"carryOverPromotion",
|
||||||
|
"carryOverPromotionHint",
|
||||||
|
"carryOverPromotionTooltip",
|
||||||
"activeChangeError",
|
"activeChangeError",
|
||||||
"actions",
|
"actions",
|
||||||
"cancel",
|
"cancel",
|
||||||
|
|||||||
@@ -1305,6 +1305,9 @@
|
|||||||
"outboundShipping": "Outbound shipping",
|
"outboundShipping": "Outbound shipping",
|
||||||
"outboundShippingHint": "Choose which method you want to use.",
|
"outboundShippingHint": "Choose which method you want to use.",
|
||||||
"refundAmount": "Estimated difference",
|
"refundAmount": "Estimated difference",
|
||||||
|
"carryOverPromotion": "Carry over promotions",
|
||||||
|
"carryOverPromotionHint": "Apply the order's promotions to the exchange items",
|
||||||
|
"carryOverPromotionTooltip": "Only fixed type promotions with EACH allocation and percentage type promotions with EACH or ACROSS allocation can be carried over to outbound exchange items.",
|
||||||
"activeChangeError": "There is an active order change on this order. Please finish or discard the previous change.",
|
"activeChangeError": "There is an active order change on this order. Please finish or discard the previous change.",
|
||||||
"actions": {
|
"actions": {
|
||||||
"cancelExchange": {
|
"cancelExchange": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const sdk = new Medusa({
|
|||||||
baseUrl: backendUrl,
|
baseUrl: backendUrl,
|
||||||
auth: {
|
auth: {
|
||||||
type: authType,
|
type: authType,
|
||||||
jwtTokenStorageKey
|
jwtTokenStorageKey,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+84
-6
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { PencilSquare } from "@medusajs/icons"
|
import { InformationCircleSolid, PencilSquare } from "@medusajs/icons"
|
||||||
import { AdminExchange, AdminOrder, AdminOrderPreview } from "@medusajs/types"
|
import { AdminExchange, AdminOrder, AdminOrderPreview } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Switch,
|
Switch,
|
||||||
toast,
|
toast,
|
||||||
|
Tooltip,
|
||||||
usePrompt,
|
usePrompt,
|
||||||
} from "@medusajs/ui"
|
} from "@medusajs/ui"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
@@ -31,6 +32,7 @@ import {
|
|||||||
useUpdateExchangeInboundShipping,
|
useUpdateExchangeInboundShipping,
|
||||||
useUpdateExchangeOutboundShipping,
|
useUpdateExchangeOutboundShipping,
|
||||||
} from "../../../../../hooks/api/exchanges"
|
} from "../../../../../hooks/api/exchanges"
|
||||||
|
import { useUpdateOrderChange } from "../../../../../hooks/api/orders"
|
||||||
import { currencies } from "../../../../../lib/data/currencies"
|
import { currencies } from "../../../../../lib/data/currencies"
|
||||||
import { ExchangeInboundSection } from "./exchange-inbound-section.tsx"
|
import { ExchangeInboundSection } from "./exchange-inbound-section.tsx"
|
||||||
import { ExchangeOutboundSection } from "./exchange-outbound-section"
|
import { ExchangeOutboundSection } from "./exchange-outbound-section"
|
||||||
@@ -92,6 +94,15 @@ export const ExchangeCreateForm = ({
|
|||||||
isPending: isUpdatingInboundShipping,
|
isPending: isUpdatingInboundShipping,
|
||||||
} = useUpdateExchangeOutboundShipping(exchange.id, order.id)
|
} = useUpdateExchangeOutboundShipping(exchange.id, order.id)
|
||||||
|
|
||||||
|
const { mutateAsync: updateOrderChange } = useUpdateOrderChange(
|
||||||
|
preview?.order_change?.id!,
|
||||||
|
{
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const isRequestLoading =
|
const isRequestLoading =
|
||||||
isConfirming ||
|
isConfirming ||
|
||||||
isCanceling ||
|
isCanceling ||
|
||||||
@@ -117,6 +128,14 @@ export const ExchangeCreateForm = ({
|
|||||||
(item) => !!item.actions?.find((a) => a.action === "ITEM_ADD")
|
(item) => !!item.actions?.find((a) => a.action === "ITEM_ADD")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hasPromotions = useMemo(() => {
|
||||||
|
return (
|
||||||
|
(order as any).promotions &&
|
||||||
|
Array.isArray((order as any).promotions) &&
|
||||||
|
(order as any).promotions.length > 0
|
||||||
|
)
|
||||||
|
}, [order])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FORM
|
* FORM
|
||||||
*/
|
*/
|
||||||
@@ -161,6 +180,8 @@ export const ExchangeCreateForm = ({
|
|||||||
: "",
|
: "",
|
||||||
location_id: orderReturn?.location_id,
|
location_id: orderReturn?.location_id,
|
||||||
send_notification: false,
|
send_notification: false,
|
||||||
|
carry_over_promotions:
|
||||||
|
preview?.order_change?.carry_over_promotions ?? false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resolver: zodResolver(ExchangeCreateSchema),
|
resolver: zodResolver(ExchangeCreateSchema),
|
||||||
@@ -306,7 +327,14 @@ export const ExchangeCreateForm = ({
|
|||||||
const action = item.actions?.find(
|
const action = item.actions?.find(
|
||||||
(act) => act.action === "RETURN_ITEM"
|
(act) => act.action === "RETURN_ITEM"
|
||||||
)
|
)
|
||||||
acc = acc + (action?.amount || 0)
|
/**
|
||||||
|
* TODO: update this when the change actions return amounts are revamped
|
||||||
|
* it is might not cover all the cases but is more accurate then just using `unit_price` which does't consider adjustments
|
||||||
|
*/
|
||||||
|
acc =
|
||||||
|
acc +
|
||||||
|
((action?.details.quantity || 0) / item.quantity) *
|
||||||
|
item.total
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, 0) * -1,
|
}, 0) * -1,
|
||||||
@@ -323,10 +351,7 @@ export const ExchangeCreateForm = ({
|
|||||||
<span className="txt-small text-ui-fg-subtle">
|
<span className="txt-small text-ui-fg-subtle">
|
||||||
{getStylizedAmount(
|
{getStylizedAmount(
|
||||||
outboundPreviewItems.reduce((acc, item) => {
|
outboundPreviewItems.reduce((acc, item) => {
|
||||||
const action = item.actions?.find(
|
acc = acc + (item.total || 0) // outbound items entire quantity is used for calculating outbound total
|
||||||
(act) => act.action === "ITEM_ADD"
|
|
||||||
)
|
|
||||||
acc = acc + (action?.amount || 0)
|
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, 0),
|
}, 0),
|
||||||
@@ -498,6 +523,59 @@ export const ExchangeCreateForm = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* CARRY OVER PROMOTION */}
|
||||||
|
{hasPromotions && (
|
||||||
|
<div className="bg-ui-bg-field mt-4 rounded-lg border py-2 pl-2 pr-4">
|
||||||
|
<Form.Field
|
||||||
|
control={form.control}
|
||||||
|
name="carry_over_promotions"
|
||||||
|
render={({ field: { onChange, value, ...field } }) => {
|
||||||
|
return (
|
||||||
|
<Form.Item>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Form.Control className="mr-4 self-start">
|
||||||
|
<Switch
|
||||||
|
dir="ltr"
|
||||||
|
className="mt-[2px] rtl:rotate-180"
|
||||||
|
checked={!!value}
|
||||||
|
onCheckedChange={async (checked) => {
|
||||||
|
onChange(checked)
|
||||||
|
if (preview?.order_change?.id) {
|
||||||
|
await updateOrderChange({
|
||||||
|
carry_over_promotions: checked,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
<div className="block">
|
||||||
|
<Form.Label className="flex items-center gap-x-2">
|
||||||
|
{t("orders.exchanges.carryOverPromotion")}
|
||||||
|
<Form.Hint>
|
||||||
|
<Tooltip
|
||||||
|
content={t(
|
||||||
|
"orders.exchanges.carryOverPromotionTooltip"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<InformationCircleSolid />
|
||||||
|
</Tooltip>
|
||||||
|
</Form.Hint>
|
||||||
|
</Form.Label>
|
||||||
|
<Form.Hint className="!mt-1">
|
||||||
|
{t("orders.exchanges.carryOverPromotionHint")}
|
||||||
|
</Form.Hint>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Form.ErrorMessage />
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* SEND NOTIFICATION*/}
|
{/* SEND NOTIFICATION*/}
|
||||||
<div className="bg-ui-bg-field mt-8 rounded-lg border py-2 pl-2 pr-4">
|
<div className="bg-ui-bg-field mt-8 rounded-lg border py-2 pl-2 pr-4">
|
||||||
<Form.Field
|
<Form.Field
|
||||||
|
|||||||
+1
@@ -19,6 +19,7 @@ export const ExchangeCreateSchema = z.object({
|
|||||||
inbound_option_id: z.string().nullish(),
|
inbound_option_id: z.string().nullish(),
|
||||||
outbound_option_id: z.string().nullish(),
|
outbound_option_id: z.string().nullish(),
|
||||||
send_notification: z.boolean().optional(),
|
send_notification: z.boolean().optional(),
|
||||||
|
carry_over_promotions: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type CreateExchangeSchemaType = z.infer<typeof ExchangeCreateSchema>
|
export type CreateExchangeSchemaType = z.infer<typeof ExchangeCreateSchema>
|
||||||
+36
-15
@@ -10,6 +10,7 @@ import {
|
|||||||
DocumentText,
|
DocumentText,
|
||||||
ExclamationCircle,
|
ExclamationCircle,
|
||||||
PencilSquare,
|
PencilSquare,
|
||||||
|
ReceiptPercent,
|
||||||
TriangleDownMini,
|
TriangleDownMini,
|
||||||
} from "@medusajs/icons"
|
} from "@medusajs/icons"
|
||||||
import {
|
import {
|
||||||
@@ -402,29 +403,49 @@ const Item = ({
|
|||||||
item.variant?.inventory_items?.some((i) => i.required_quantity > 1))
|
item.variant?.inventory_items?.some((i) => i.required_quantity > 1))
|
||||||
const hasUnfulfilledItems = item.quantity - item.detail.fulfilled_quantity > 0
|
const hasUnfulfilledItems = item.quantity - item.detail.fulfilled_quantity > 0
|
||||||
|
|
||||||
|
const appliedPromoCodes = (item.adjustments || []).map((a) => a.code)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="text-ui-fg-subtle grid grid-cols-2 items-center gap-x-4 px-6 py-4"
|
className="text-ui-fg-subtle grid grid-cols-2 items-center gap-x-4 px-6 py-4"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-x-4">
|
<div className=" flex justify-between gap-x-2 ">
|
||||||
<Thumbnail src={item.thumbnail} />
|
<div className=" group flex items-start gap-x-4">
|
||||||
<div>
|
<Thumbnail src={item.thumbnail} />
|
||||||
<Text size="small" leading="compact" className="text-ui-fg-base">
|
<div>
|
||||||
{item.title}
|
<Text size="small" leading="compact" className="text-ui-fg-base">
|
||||||
</Text>
|
{item.title}
|
||||||
|
</Text>
|
||||||
|
|
||||||
{item.variant_sku && (
|
{item.variant_sku && (
|
||||||
<div className="flex items-center gap-x-1">
|
<div className="flex items-center gap-x-1">
|
||||||
<Text size="small">{item.variant_sku}</Text>
|
<Text size="small">{item.variant_sku}</Text>
|
||||||
<Copy content={item.variant_sku} className="text-ui-fg-muted" />
|
<Copy
|
||||||
</div>
|
content={item.variant_sku}
|
||||||
)}
|
className="text-ui-fg-muted hidden group-hover:block"
|
||||||
<Text size="small">
|
/>
|
||||||
{item.variant?.options?.map((o) => o.value).join(" · ")}
|
</div>
|
||||||
</Text>
|
)}
|
||||||
|
<Text size="small">
|
||||||
|
{item.variant?.options?.map((o) => o.value).join(" · ")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{appliedPromoCodes.length > 0 && (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span className="text-pretty">
|
||||||
|
{appliedPromoCodes.map((code) => (
|
||||||
|
<div key={code}>{code}</div>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ReceiptPercent className="text-ui-fg-subtle flex-shrink self-center " />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 items-center gap-x-4">
|
<div className="grid grid-cols-3 items-center gap-x-4">
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export * from "./delete-order-change-actions"
|
|||||||
export * from "./delete-order-changes"
|
export * from "./delete-order-changes"
|
||||||
export * from "./delete-order-shipping-methods"
|
export * from "./delete-order-shipping-methods"
|
||||||
export * from "./exchange/cancel-exchange"
|
export * from "./exchange/cancel-exchange"
|
||||||
|
export * from "./list-order-change-actions-by-type"
|
||||||
export * from "./exchange/create-exchange"
|
export * from "./exchange/create-exchange"
|
||||||
export * from "./exchange/create-exchange-items-from-actions"
|
export * from "./exchange/create-exchange-items-from-actions"
|
||||||
export * from "./exchange/delete-exchanges"
|
export * from "./exchange/delete-exchanges"
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { IOrderModuleService } from "@medusajs/framework/types"
|
||||||
|
import { ChangeActionType, Modules } from "@medusajs/framework/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This step lists order change actions filtered by action type.
|
||||||
|
*/
|
||||||
|
export const listOrderChangeActionsByTypeStep = createStep(
|
||||||
|
"list-order-change-actions-by-type",
|
||||||
|
async function (
|
||||||
|
{
|
||||||
|
order_change_id,
|
||||||
|
action_type,
|
||||||
|
}: {
|
||||||
|
order_change_id: string
|
||||||
|
action_type: ChangeActionType
|
||||||
|
},
|
||||||
|
{ container }
|
||||||
|
) {
|
||||||
|
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
|
||||||
|
|
||||||
|
const actions = await service.listOrderChangeActions(
|
||||||
|
{
|
||||||
|
order_change_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "action"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const filteredActions = actions.filter(
|
||||||
|
(action) => action.action === action_type
|
||||||
|
)
|
||||||
|
|
||||||
|
return new StepResponse(filteredActions.map((action) => action.id))
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import {
|
||||||
|
ComputeActionContext,
|
||||||
|
OrderChangeDTO,
|
||||||
|
OrderDTO,
|
||||||
|
PromotionDTO,
|
||||||
|
} from "@medusajs/framework/types"
|
||||||
|
import { ChangeActionType } from "@medusajs/framework/utils"
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
when,
|
||||||
|
WorkflowData,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import {
|
||||||
|
getActionsToComputeFromPromotionsStep,
|
||||||
|
prepareAdjustmentsFromPromotionActionsStep,
|
||||||
|
} from "../../cart"
|
||||||
|
import { previewOrderChangeStep } from "../steps/preview-order-change"
|
||||||
|
import { createOrderChangeActionsWorkflow } from "./create-order-change-actions"
|
||||||
|
import {
|
||||||
|
deleteOrderChangeActionsStep,
|
||||||
|
listOrderChangeActionsByTypeStep,
|
||||||
|
} from "../steps"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data to compute adjustments for an order edit, exchange, claim, or return.
|
||||||
|
*/
|
||||||
|
export type ComputeAdjustmentsForPreviewWorkflowInput = {
|
||||||
|
/**
|
||||||
|
* The order's details.
|
||||||
|
*/
|
||||||
|
order: OrderDTO & { promotions: PromotionDTO[] }
|
||||||
|
/**
|
||||||
|
* The order change's details.
|
||||||
|
*/
|
||||||
|
orderChange: OrderChangeDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export const computeAdjustmentsForPreviewWorkflowId =
|
||||||
|
"compute-adjustments-for-preview"
|
||||||
|
/**
|
||||||
|
* This workflow computes adjustments for an order change if the carry over promotions flag is true on the order change.
|
||||||
|
* If the flag is false, it deletes the existing adjustments replacement actions.
|
||||||
|
*
|
||||||
|
* It is currently used as a part of the order edit and exchange flows.
|
||||||
|
* It's used by the [Add Items to Order Edit Admin API Route](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditems),
|
||||||
|
* [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems),
|
||||||
|
* and [Add Inbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinbounditems).
|
||||||
|
*
|
||||||
|
* You can use this workflow within your customizations or your own custom workflows, allowing you to compute adjustments
|
||||||
|
* in your custom flows.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { result } = await computeAdjustmentsForPreviewWorkflow(container)
|
||||||
|
* .run({
|
||||||
|
* input: {
|
||||||
|
* order: {
|
||||||
|
* id: "order_123",
|
||||||
|
* // other order details...
|
||||||
|
* },
|
||||||
|
* orderChange: {
|
||||||
|
* id: "orch_123",
|
||||||
|
* // other order change details...
|
||||||
|
* },
|
||||||
|
* exchange_id: "exchange_123", // optional, for exchanges
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @summary
|
||||||
|
*
|
||||||
|
* Compute adjustments for an order edit, exchange, claim, or return.
|
||||||
|
*/
|
||||||
|
export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
|
||||||
|
computeAdjustmentsForPreviewWorkflowId,
|
||||||
|
function (input: WorkflowData<ComputeAdjustmentsForPreviewWorkflowInput>) {
|
||||||
|
const previewedOrder = previewOrderChangeStep(input.order.id)
|
||||||
|
|
||||||
|
when(
|
||||||
|
{ order: input.order },
|
||||||
|
({ order }) =>
|
||||||
|
/**
|
||||||
|
* Compute adjustments only if the flag on the order change is true
|
||||||
|
*/
|
||||||
|
!!order.promotions.length && !!input.orderChange.carry_over_promotions
|
||||||
|
).then(() => {
|
||||||
|
const actionsToComputeItemsInput = transform(
|
||||||
|
{ previewedOrder, order: input.order },
|
||||||
|
({ previewedOrder, order }) => {
|
||||||
|
return {
|
||||||
|
currency_code: order.currency_code,
|
||||||
|
items: previewedOrder.items.map((item) => ({
|
||||||
|
...item,
|
||||||
|
// Buy-Get promotions rely on the product ID, so we need to manually set it before refreshing adjustments
|
||||||
|
product: { id: item.product_id },
|
||||||
|
})),
|
||||||
|
} as ComputeActionContext
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const orderPromotions = transform({ order: input.order }, ({ order }) => {
|
||||||
|
return order.promotions
|
||||||
|
.map((p) => p.code)
|
||||||
|
.filter((p) => p !== undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = getActionsToComputeFromPromotionsStep({
|
||||||
|
computeActionContext: actionsToComputeItemsInput,
|
||||||
|
promotionCodesToApply: orderPromotions,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { lineItemAdjustmentsToCreate } =
|
||||||
|
prepareAdjustmentsFromPromotionActionsStep({ actions })
|
||||||
|
|
||||||
|
const orderChangeActionAdjustmentsInput = transform(
|
||||||
|
{
|
||||||
|
order: input.order,
|
||||||
|
previewedOrder,
|
||||||
|
orderChange: input.orderChange,
|
||||||
|
lineItemAdjustmentsToCreate,
|
||||||
|
},
|
||||||
|
({
|
||||||
|
order,
|
||||||
|
previewedOrder,
|
||||||
|
orderChange,
|
||||||
|
lineItemAdjustmentsToCreate,
|
||||||
|
}) => {
|
||||||
|
return previewedOrder.items.map((item) => {
|
||||||
|
const itemAdjustments = lineItemAdjustmentsToCreate.filter(
|
||||||
|
(adjustment) => adjustment.item_id === item.id
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
order_change_id: orderChange.id,
|
||||||
|
order_id: order.id,
|
||||||
|
exchange_id: orderChange.exchange_id ?? undefined,
|
||||||
|
claim_id: orderChange.claim_id ?? undefined,
|
||||||
|
return_id: orderChange.return_id ?? undefined,
|
||||||
|
version: orderChange.version,
|
||||||
|
action: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
||||||
|
details: {
|
||||||
|
reference_id: item.id,
|
||||||
|
adjustments: itemAdjustments,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
createOrderChangeActionsWorkflow
|
||||||
|
.runAsStep({ input: orderChangeActionAdjustmentsInput })
|
||||||
|
.config({ name: "order-change-action-adjustments-input" })
|
||||||
|
})
|
||||||
|
|
||||||
|
when(
|
||||||
|
{ order: previewedOrder },
|
||||||
|
({ order }) => !order.order_change.carry_over_promotions
|
||||||
|
).then(() => {
|
||||||
|
const actionIds = listOrderChangeActionsByTypeStep({
|
||||||
|
order_change_id: input.orderChange.id,
|
||||||
|
action_type: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
||||||
|
})
|
||||||
|
|
||||||
|
deleteOrderChangeActionsStep({ ids: actionIds })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { addOrderLineItemsWorkflow } from "../add-line-items"
|
import { addOrderLineItemsWorkflow } from "../add-line-items"
|
||||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "../order-edit/compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
||||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||||
|
|
||||||
@@ -140,7 +140,13 @@ export const orderExchangeAddNewItemWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||||
entry_point: "order_change",
|
entry_point: "order_change",
|
||||||
fields: ["id", "status", "version"],
|
fields: [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"exchange_id",
|
||||||
|
"carry_over_promotions",
|
||||||
|
],
|
||||||
variables: {
|
variables: {
|
||||||
filters: {
|
filters: {
|
||||||
order_id: orderExchange.order_id,
|
order_id: orderExchange.order_id,
|
||||||
@@ -212,7 +218,6 @@ export const orderExchangeAddNewItemWorkflow = createWorkflow(
|
|||||||
input: {
|
input: {
|
||||||
order: orderWithPromotions,
|
order: orderWithPromotions,
|
||||||
orderChange,
|
orderChange,
|
||||||
exchange_id: orderExchange.id,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -33,7 +33,7 @@ import {
|
|||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "../order-edit/compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,7 +194,13 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||||
entry_point: "order_change",
|
entry_point: "order_change",
|
||||||
fields: ["id", "status", "version"],
|
fields: [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"exchange_id",
|
||||||
|
"carry_over_promotions",
|
||||||
|
],
|
||||||
variables: {
|
variables: {
|
||||||
filters: {
|
filters: {
|
||||||
order_id: orderExchange.order_id,
|
order_id: orderExchange.order_id,
|
||||||
@@ -321,7 +327,6 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
|
|||||||
input: {
|
input: {
|
||||||
order: orderWithPromotions,
|
order: orderWithPromotions,
|
||||||
orderChange,
|
orderChange,
|
||||||
exchange_id: orderExchange.id,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
OrderChangeDTO,
|
OrderChangeDTO,
|
||||||
OrderDTO,
|
OrderDTO,
|
||||||
OrderExchangeDTO,
|
OrderExchangeDTO,
|
||||||
|
PromotionDTO,
|
||||||
OrderPreviewDTO,
|
OrderPreviewDTO,
|
||||||
OrderWorkflow,
|
OrderWorkflow,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
@@ -24,6 +25,7 @@ import {
|
|||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||||
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data to validate that an outbound or new item in an exchange can be updated.
|
* The data to validate that an outbound or new item in an exchange can be updated.
|
||||||
@@ -150,7 +152,7 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const order: OrderDTO = useRemoteQueryStep({
|
const order: OrderDTO = useRemoteQueryStep({
|
||||||
entry_point: "orders",
|
entry_point: "orders",
|
||||||
fields: ["id", "status", "canceled_at", "items.*"],
|
fields: ["id", "status", "canceled_at", "items.*", "promotions.*"],
|
||||||
variables: { id: orderExchange.order_id },
|
variables: { id: orderExchange.order_id },
|
||||||
list: false,
|
list: false,
|
||||||
throw_if_key_not_found: true,
|
throw_if_key_not_found: true,
|
||||||
@@ -158,7 +160,14 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||||
entry_point: "order_change",
|
entry_point: "order_change",
|
||||||
fields: ["id", "status", "version", "actions.*"],
|
fields: [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"exchange_id",
|
||||||
|
"actions.*",
|
||||||
|
"carry_over_promotions",
|
||||||
|
],
|
||||||
variables: {
|
variables: {
|
||||||
filters: {
|
filters: {
|
||||||
order_id: orderExchange.order_id,
|
order_id: orderExchange.order_id,
|
||||||
@@ -207,6 +216,20 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const orderWithPromotions = transform({ order }, ({ order }) => {
|
||||||
|
return {
|
||||||
|
...order,
|
||||||
|
promotions: (order as any).promotions ?? [],
|
||||||
|
} as OrderDTO & { promotions: PromotionDTO[] }
|
||||||
|
})
|
||||||
|
|
||||||
|
computeAdjustmentsForPreviewWorkflow.runAsStep({
|
||||||
|
input: {
|
||||||
|
order: orderWithPromotions,
|
||||||
|
orderChange,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
refreshExchangeShippingWorkflow.runAsStep({
|
refreshExchangeShippingWorkflow.runAsStep({
|
||||||
input: refreshArgs,
|
input: refreshArgs,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export * from "./mark-payment-collection-as-paid"
|
|||||||
export * from "./maybe-refresh-shipping-methods"
|
export * from "./maybe-refresh-shipping-methods"
|
||||||
export * from "./order-edit/begin-order-edit"
|
export * from "./order-edit/begin-order-edit"
|
||||||
export * from "./order-edit/cancel-begin-order-edit"
|
export * from "./order-edit/cancel-begin-order-edit"
|
||||||
export * from "./order-edit/compute-adjustments-for-preview"
|
export * from "./compute-adjustments-for-preview"
|
||||||
export * from "./order-edit/confirm-order-edit-request"
|
export * from "./order-edit/confirm-order-edit-request"
|
||||||
export * from "./order-edit/create-order-edit-shipping-method"
|
export * from "./order-edit/create-order-edit-shipping-method"
|
||||||
export * from "./order-edit/order-edit-add-new-item"
|
export * from "./order-edit/order-edit-add-new-item"
|
||||||
@@ -81,11 +81,13 @@ export * from "./return/update-receive-item-return-request"
|
|||||||
export * from "./return/update-request-item-return"
|
export * from "./return/update-request-item-return"
|
||||||
export * from "./return/update-return"
|
export * from "./return/update-return"
|
||||||
export * from "./return/update-return-shipping-method"
|
export * from "./return/update-return-shipping-method"
|
||||||
|
export * from "./on-carry-promotions-flag-set"
|
||||||
export * from "./transfer/accept-order-transfer"
|
export * from "./transfer/accept-order-transfer"
|
||||||
export * from "./transfer/cancel-order-transfer"
|
export * from "./transfer/cancel-order-transfer"
|
||||||
export * from "./transfer/decline-order-transfer"
|
export * from "./transfer/decline-order-transfer"
|
||||||
export * from "./transfer/request-order-transfer"
|
export * from "./transfer/request-order-transfer"
|
||||||
export * from "./update-order"
|
export * from "./update-order"
|
||||||
|
export * from "./update-order-change"
|
||||||
export * from "./update-order-change-actions"
|
export * from "./update-order-change-actions"
|
||||||
export * from "./update-order-changes"
|
export * from "./update-order-changes"
|
||||||
export * from "./update-tax-lines"
|
export * from "./update-tax-lines"
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import {
|
||||||
|
OrderChangeDTO,
|
||||||
|
OrderDTO,
|
||||||
|
PromotionDTO,
|
||||||
|
} from "@medusajs/framework/types"
|
||||||
|
import {
|
||||||
|
ApplicationMethodAllocation,
|
||||||
|
MedusaError,
|
||||||
|
} from "@medusajs/framework/utils"
|
||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { useRemoteQueryStep } from "../../common"
|
||||||
|
import { throwIfOrderChangeIsNotActive } from "../utils/order-validation"
|
||||||
|
|
||||||
|
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data to set the carry over promotions flag for an order change.
|
||||||
|
*/
|
||||||
|
export type OnCarryPromotionsFlagSetWorkflowInput = {
|
||||||
|
/**
|
||||||
|
* The order change's ID.
|
||||||
|
*/
|
||||||
|
order_change_id: string
|
||||||
|
/**
|
||||||
|
* Whether to carry over promotions to outbound exchange items.
|
||||||
|
*/
|
||||||
|
carry_over_promotions: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This step validates that the order change is an exchange and validates promotion allocation.
|
||||||
|
*/
|
||||||
|
export const validateCarryPromotionsFlagStep = createStep(
|
||||||
|
"validate-carry-promotions-flag",
|
||||||
|
async function ({
|
||||||
|
orderChange,
|
||||||
|
order,
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
orderChange: OrderChangeDTO
|
||||||
|
order: OrderDTO & { promotions?: PromotionDTO[] }
|
||||||
|
input: OnCarryPromotionsFlagSetWorkflowInput
|
||||||
|
}) {
|
||||||
|
// Validate order change is active
|
||||||
|
throwIfOrderChangeIsNotActive({ orderChange })
|
||||||
|
|
||||||
|
// we don't need to validate promotion since we will be resetting the adjustments
|
||||||
|
if (!input.carry_over_promotions) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate promotion allocation if promotions exist
|
||||||
|
if (order.promotions && order.promotions.length > 0) {
|
||||||
|
const invalidPromotions: string[] = []
|
||||||
|
|
||||||
|
for (const promotion of order.promotions) {
|
||||||
|
const applicationMethod = (promotion as any).application_method
|
||||||
|
|
||||||
|
if (!applicationMethod) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const allocation = applicationMethod.allocation
|
||||||
|
const type = applicationMethod.type
|
||||||
|
|
||||||
|
if (
|
||||||
|
allocation !== ApplicationMethodAllocation.ACROSS &&
|
||||||
|
allocation !== ApplicationMethodAllocation.EACH
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Promotion ${
|
||||||
|
promotion.code || promotion.id
|
||||||
|
} has invalid allocation. Only promotions with EACH or ACROSS allocation can be carried over to outbound exchange items.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For fixed promotions, allocation must be EACH
|
||||||
|
if (
|
||||||
|
type === "fixed" &&
|
||||||
|
allocation !== ApplicationMethodAllocation.EACH
|
||||||
|
) {
|
||||||
|
invalidPromotions.push(promotion.code || promotion.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For percentage promotions, allocation must be EACH or ACROSS
|
||||||
|
if (
|
||||||
|
type === "percentage" &&
|
||||||
|
allocation !== ApplicationMethodAllocation.EACH &&
|
||||||
|
allocation !== ApplicationMethodAllocation.ACROSS
|
||||||
|
) {
|
||||||
|
invalidPromotions.push(promotion.code || promotion.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidPromotions.length > 0) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Promotions with codes ${invalidPromotions.join(
|
||||||
|
", "
|
||||||
|
)} have invalid allocation. Fixed promotions must have EACH allocation, and percentage promotions must have EACH or ACROSS allocation.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const onCarryPromotionsFlagSetId = "on-carry-promotions-flag-set"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This workflow sets the carry over promotions flag for an order change.
|
||||||
|
* It validates that the order change is active and is an exchange, validates promotion allocation,
|
||||||
|
* and either applies or removes promotion adjustments based on the flag value.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { result } = await onCarryPromotionsFlagSet(container)
|
||||||
|
* .run({
|
||||||
|
* input: {
|
||||||
|
* order_change_id: "orch_123",
|
||||||
|
* carry_over_promotions: true,
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @summary
|
||||||
|
*
|
||||||
|
* Set the carry over promotions flag for an order change.
|
||||||
|
*/
|
||||||
|
export const onCarryPromotionsFlagSet = createWorkflow(
|
||||||
|
onCarryPromotionsFlagSetId,
|
||||||
|
function (
|
||||||
|
input: WorkflowData<OnCarryPromotionsFlagSetWorkflowInput>
|
||||||
|
): WorkflowResponse<void> {
|
||||||
|
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||||
|
entry_point: "order_change",
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"exchange_id",
|
||||||
|
"claim_id",
|
||||||
|
"return_id",
|
||||||
|
"order_id",
|
||||||
|
"canceled_at",
|
||||||
|
"confirmed_at",
|
||||||
|
"declined_at",
|
||||||
|
"carry_over_promotions",
|
||||||
|
],
|
||||||
|
variables: {
|
||||||
|
filters: {
|
||||||
|
id: input.order_change_id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
list: false,
|
||||||
|
throw_if_key_not_found: true,
|
||||||
|
}).config({ name: "order-change-query" })
|
||||||
|
|
||||||
|
const order: OrderDTO & { promotions?: PromotionDTO[] } =
|
||||||
|
useRemoteQueryStep({
|
||||||
|
entry_point: "orders",
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"currency_code",
|
||||||
|
"promotions.*",
|
||||||
|
"promotions.application_method.*",
|
||||||
|
],
|
||||||
|
variables: {
|
||||||
|
id: orderChange.order_id,
|
||||||
|
},
|
||||||
|
list: false,
|
||||||
|
throw_if_key_not_found: true,
|
||||||
|
}).config({ name: "order-query" })
|
||||||
|
|
||||||
|
validateCarryPromotionsFlagStep({
|
||||||
|
orderChange,
|
||||||
|
order,
|
||||||
|
input,
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderWithPromotions = transform({ order }, ({ order }) => {
|
||||||
|
return {
|
||||||
|
...order,
|
||||||
|
promotions: (order as any).promotions ?? [],
|
||||||
|
} as OrderDTO & { promotions: PromotionDTO[] }
|
||||||
|
})
|
||||||
|
|
||||||
|
computeAdjustmentsForPreviewWorkflow.runAsStep({
|
||||||
|
input: {
|
||||||
|
orderChange,
|
||||||
|
order: orderWithPromotions,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return new WorkflowResponse(void 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
-163
@@ -1,163 +0,0 @@
|
|||||||
import {
|
|
||||||
ComputeActionContext,
|
|
||||||
OrderChangeDTO,
|
|
||||||
OrderDTO,
|
|
||||||
PromotionDTO,
|
|
||||||
} from "@medusajs/framework/types"
|
|
||||||
import { ChangeActionType } from "@medusajs/framework/utils"
|
|
||||||
import {
|
|
||||||
createWorkflow,
|
|
||||||
transform,
|
|
||||||
when,
|
|
||||||
WorkflowData,
|
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
|
||||||
import {
|
|
||||||
getActionsToComputeFromPromotionsStep,
|
|
||||||
prepareAdjustmentsFromPromotionActionsStep,
|
|
||||||
} from "../../../cart"
|
|
||||||
import { previewOrderChangeStep } from "../../steps/preview-order-change"
|
|
||||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The data to compute adjustments for an order edit, exchange, claim, or return.
|
|
||||||
*/
|
|
||||||
export type ComputeAdjustmentsForPreviewWorkflowInput = {
|
|
||||||
/**
|
|
||||||
* The order's details.
|
|
||||||
*/
|
|
||||||
order: OrderDTO & { promotions: PromotionDTO[] }
|
|
||||||
/**
|
|
||||||
* The order change's details.
|
|
||||||
*/
|
|
||||||
orderChange: OrderChangeDTO
|
|
||||||
/**
|
|
||||||
* Optional exchange ID to include in the order change action.
|
|
||||||
*/
|
|
||||||
exchange_id?: string
|
|
||||||
/**
|
|
||||||
* Optional claim ID to include in the order change action.
|
|
||||||
*/
|
|
||||||
claim_id?: string
|
|
||||||
/**
|
|
||||||
* Optional return ID to include in the order change action.
|
|
||||||
*/
|
|
||||||
return_id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const computeAdjustmentsForPreviewWorkflowId =
|
|
||||||
"compute-adjustments-for-preview"
|
|
||||||
/**
|
|
||||||
* This workflow computes adjustments for an order edit, exchange, claim, or return.
|
|
||||||
* It's used by the [Add Items to Order Edit Admin API Route](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditems),
|
|
||||||
* [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems),
|
|
||||||
* and [Add Inbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinbounditems).
|
|
||||||
*
|
|
||||||
* You can use this workflow within your customizations or your own custom workflows, allowing you to compute adjustments
|
|
||||||
* in your custom flows.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { result } = await computeAdjustmentsForPreviewWorkflow(container)
|
|
||||||
* .run({
|
|
||||||
* input: {
|
|
||||||
* order: {
|
|
||||||
* id: "order_123",
|
|
||||||
* // other order details...
|
|
||||||
* },
|
|
||||||
* orderChange: {
|
|
||||||
* id: "orch_123",
|
|
||||||
* // other order change details...
|
|
||||||
* },
|
|
||||||
* exchange_id: "exchange_123", // optional, for exchanges
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* @summary
|
|
||||||
*
|
|
||||||
* Compute adjustments for an order edit, exchange, claim, or return.
|
|
||||||
*/
|
|
||||||
export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
|
|
||||||
computeAdjustmentsForPreviewWorkflowId,
|
|
||||||
function (input: WorkflowData<ComputeAdjustmentsForPreviewWorkflowInput>) {
|
|
||||||
const previewedOrder = previewOrderChangeStep(input.order.id)
|
|
||||||
|
|
||||||
when({ order: input.order }, ({ order }) => !!order.promotions.length).then(
|
|
||||||
() => {
|
|
||||||
const actionsToComputeItemsInput = transform(
|
|
||||||
{ previewedOrder, order: input.order },
|
|
||||||
({ previewedOrder, order }) => {
|
|
||||||
return {
|
|
||||||
currency_code: order.currency_code,
|
|
||||||
items: previewedOrder.items.map((item) => ({
|
|
||||||
...item,
|
|
||||||
// Buy-Get promotions rely on the product ID, so we need to manually set it before refreshing adjustments
|
|
||||||
product: { id: item.product_id },
|
|
||||||
})),
|
|
||||||
} as ComputeActionContext
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const orderPromotions = transform(
|
|
||||||
{ order: input.order },
|
|
||||||
({ order }) => {
|
|
||||||
return order.promotions
|
|
||||||
.map((p) => p.code)
|
|
||||||
.filter((p) => p !== undefined)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const actions = getActionsToComputeFromPromotionsStep({
|
|
||||||
computeActionContext: actionsToComputeItemsInput,
|
|
||||||
promotionCodesToApply: orderPromotions,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { lineItemAdjustmentsToCreate } =
|
|
||||||
prepareAdjustmentsFromPromotionActionsStep({ actions })
|
|
||||||
|
|
||||||
const orderChangeActionAdjustmentsInput = transform(
|
|
||||||
{
|
|
||||||
order: input.order,
|
|
||||||
previewedOrder,
|
|
||||||
orderChange: input.orderChange,
|
|
||||||
lineItemAdjustmentsToCreate,
|
|
||||||
exchangeId: input.exchange_id,
|
|
||||||
claimId: input.claim_id,
|
|
||||||
returnId: input.return_id,
|
|
||||||
},
|
|
||||||
({
|
|
||||||
order,
|
|
||||||
previewedOrder,
|
|
||||||
orderChange,
|
|
||||||
lineItemAdjustmentsToCreate,
|
|
||||||
exchangeId,
|
|
||||||
claimId,
|
|
||||||
returnId,
|
|
||||||
}) => {
|
|
||||||
return previewedOrder.items.map((item) => {
|
|
||||||
const itemAdjustments = lineItemAdjustmentsToCreate.filter(
|
|
||||||
(adjustment) => adjustment.item_id === item.id
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
order_change_id: orderChange.id,
|
|
||||||
order_id: order.id,
|
|
||||||
...(exchangeId && { exchange_id: exchangeId }),
|
|
||||||
...(claimId && { claim_id: claimId }),
|
|
||||||
...(returnId && { return_id: returnId }),
|
|
||||||
version: orderChange.version,
|
|
||||||
action: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
|
||||||
details: {
|
|
||||||
reference_id: item.id,
|
|
||||||
adjustments: itemAdjustments,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
createOrderChangeActionsWorkflow
|
|
||||||
.runAsStep({ input: orderChangeActionAdjustmentsInput })
|
|
||||||
.config({ name: "order-change-action-adjustments-input" })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -22,7 +22,7 @@ import { addOrderLineItemsWorkflow } from "../add-line-items"
|
|||||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||||
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
||||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data to validate that new items can be added to an order edit.
|
* The data to validate that new items can be added to an order edit.
|
||||||
@@ -118,7 +118,7 @@ export const orderEditAddNewItemWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChangeResult = useQueryGraphStep({
|
const orderChangeResult = useQueryGraphStep({
|
||||||
entity: "order_change",
|
entity: "order_change",
|
||||||
fields: ["id", "status", "version", "actions.*"],
|
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||||
filters: {
|
filters: {
|
||||||
order_id: input.order_id,
|
order_id: input.order_id,
|
||||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||||
|
|||||||
+2
-2
@@ -24,7 +24,7 @@ import {
|
|||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,7 +128,7 @@ export const orderEditUpdateItemQuantityWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChangeResult = useQueryGraphStep({
|
const orderChangeResult = useQueryGraphStep({
|
||||||
entity: "order_change",
|
entity: "order_change",
|
||||||
fields: ["id", "status", "version", "actions.*"],
|
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||||
filters: {
|
filters: {
|
||||||
order_id: input.order_id,
|
order_id: input.order_id,
|
||||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||||
|
|||||||
+2
-2
@@ -22,7 +22,7 @@ import {
|
|||||||
throwIfIsCancelled,
|
throwIfIsCancelled,
|
||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,7 +143,7 @@ export const removeItemOrderEditActionWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChangeResult = useQueryGraphStep({
|
const orderChangeResult = useQueryGraphStep({
|
||||||
entity: "order_change",
|
entity: "order_change",
|
||||||
fields: ["id", "status", "version", "actions.*"],
|
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||||
filters: {
|
filters: {
|
||||||
order_id: input.order_id,
|
order_id: input.order_id,
|
||||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||||
|
|||||||
+2
-2
@@ -22,7 +22,7 @@ import {
|
|||||||
throwIfIsCancelled,
|
throwIfIsCancelled,
|
||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,7 +142,7 @@ export const updateOrderEditAddItemWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const orderChangeResult = useQueryGraphStep({
|
const orderChangeResult = useQueryGraphStep({
|
||||||
entity: "order_change",
|
entity: "order_change",
|
||||||
fields: ["id", "status", "version", "actions.*"],
|
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||||
filters: {
|
filters: {
|
||||||
order_id: input.order_id,
|
order_id: input.order_id,
|
||||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ import {
|
|||||||
throwIfIsCancelled,
|
throwIfIsCancelled,
|
||||||
throwIfOrderChangeIsNotActive,
|
throwIfOrderChangeIsNotActive,
|
||||||
} from "../../utils/order-validation"
|
} from "../../utils/order-validation"
|
||||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { OrderChangeDTO, UpdateOrderChangeDTO } from "@medusajs/framework/types"
|
||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
WorkflowResponse,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
when,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { updateOrderChangesStep } from "../steps/update-order-changes"
|
||||||
|
import { onCarryPromotionsFlagSet } from "./on-carry-promotions-flag-set"
|
||||||
|
|
||||||
|
export const updateOrderChangeWorkflowId = "update-order-change-workflow"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This workflow updates an order change.
|
||||||
|
* If the carry_over_promotions flag is provided, it calls onCarryPromotionsFlagSet
|
||||||
|
* to handle the promotion logic. Otherwise, it updates the order change directly.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { result } = await updateOrderChangeWorkflow(container)
|
||||||
|
* .run({
|
||||||
|
* input: {
|
||||||
|
* id: "orch_123",
|
||||||
|
* carry_over_promotions: true,
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @summary
|
||||||
|
*
|
||||||
|
* Update an order change, conditionally handling promotion carry-over if specified.
|
||||||
|
*/
|
||||||
|
export const updateOrderChangeWorkflow = createWorkflow(
|
||||||
|
updateOrderChangeWorkflowId,
|
||||||
|
function (
|
||||||
|
input: WorkflowData<UpdateOrderChangeDTO>
|
||||||
|
): WorkflowResponse<OrderChangeDTO> {
|
||||||
|
const updatedOrderChange = updateOrderChangesStep([input])
|
||||||
|
|
||||||
|
when(
|
||||||
|
"should-call-carry-over-promotion-workflow",
|
||||||
|
input,
|
||||||
|
({ carry_over_promotions }) => typeof carry_over_promotions === "boolean"
|
||||||
|
).then(() => {
|
||||||
|
return onCarryPromotionsFlagSet.runAsStep({
|
||||||
|
input: {
|
||||||
|
order_change_id: input.id,
|
||||||
|
carry_over_promotions: input.carry_over_promotions!,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return new WorkflowResponse(
|
||||||
|
transform(
|
||||||
|
{ updatedOrderChange },
|
||||||
|
({ updatedOrderChange }) => updatedOrderChange?.[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -633,4 +633,43 @@ export class Order {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method updates an order change. It sends a request to the
|
||||||
|
* [Update Order Change](https://docs.medusajs.com/api/admin#order-changes_postorder-changesid)
|
||||||
|
* API route.
|
||||||
|
*
|
||||||
|
* @param id - The order change's ID.
|
||||||
|
* @param body - The update details.
|
||||||
|
* @param query - Configure the fields to retrieve in the order change.
|
||||||
|
* @param headers - Headers to pass in the request
|
||||||
|
* @returns The order change's details.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* sdk.admin.order.updateOrderChange(
|
||||||
|
* "ordch_123",
|
||||||
|
* {
|
||||||
|
* carry_over_promotions: true
|
||||||
|
* }
|
||||||
|
* )
|
||||||
|
* .then(({ order_change }) => {
|
||||||
|
* console.log(order_change)
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
async updateOrderChange(
|
||||||
|
id: string,
|
||||||
|
body: { carry_over_promotions: boolean },
|
||||||
|
query?: SelectParams,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) {
|
||||||
|
return await this.client.fetch<HttpTypes.AdminOrderChangeResponse>(
|
||||||
|
`/admin/order-changes/${id}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,13 @@ export interface AdminOrderChangesResponse {
|
|||||||
order_changes: AdminOrderChange[]
|
order_changes: AdminOrderChange[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminOrderChangeResponse {
|
||||||
|
/**
|
||||||
|
* The order change's details.
|
||||||
|
*/
|
||||||
|
order_change: AdminOrderChange
|
||||||
|
}
|
||||||
|
|
||||||
export type AdminOrderListResponse = PaginatedResponse<{
|
export type AdminOrderListResponse = PaginatedResponse<{
|
||||||
/**
|
/**
|
||||||
* The list of orders.
|
* The list of orders.
|
||||||
|
|||||||
@@ -1012,6 +1012,11 @@ export interface BaseOrderChange {
|
|||||||
*/
|
*/
|
||||||
actions: BaseOrderChangeAction[]
|
actions: BaseOrderChangeAction[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to carry over promotions (apply promotions to outbound exchange items).
|
||||||
|
*/
|
||||||
|
carry_over_promotions?: boolean | null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The status of the order change
|
* The status of the order change
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2117,6 +2117,11 @@ export interface OrderChangeDTO {
|
|||||||
*/
|
*/
|
||||||
change_type?: "return" | "exchange" | "claim" | "edit" | "transfer"
|
change_type?: "return" | "exchange" | "claim" | "edit" | "transfer"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to carry over promotions (apply promotions to outbound exchange items).
|
||||||
|
*/
|
||||||
|
carry_over_promotions?: boolean | null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the associated order
|
* The ID of the associated order
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -908,6 +908,11 @@ export interface CreateOrderChangeDTO {
|
|||||||
*/
|
*/
|
||||||
internal_note?: string | null
|
internal_note?: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to carry over promotions (apply promotions to outbound exchange items).
|
||||||
|
*/
|
||||||
|
carry_over_promotions?: boolean | null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user or customer that requested the order change.
|
* The user or customer that requested the order change.
|
||||||
*/
|
*/
|
||||||
@@ -1016,6 +1021,11 @@ export interface UpdateOrderChangeDTO {
|
|||||||
* Holds custom data in key-value pairs.
|
* Holds custom data in key-value pairs.
|
||||||
*/
|
*/
|
||||||
metadata?: Record<string, unknown> | null
|
metadata?: Record<string, unknown> | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to carry over promotions to outbound exchange items.
|
||||||
|
*/
|
||||||
|
carry_over_promotions?: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { updateOrderChangeWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { HttpTypes, RemoteQueryFunction } from "@medusajs/framework/types"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { AdminPostOrderChangesReqSchemaType } from "../validators"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest<AdminPostOrderChangesReqSchemaType>,
|
||||||
|
res: MedusaResponse<HttpTypes.AdminOrderChangeResponse>
|
||||||
|
) => {
|
||||||
|
const { id } = req.params
|
||||||
|
const { carry_over_promotions } = req.validatedBody
|
||||||
|
const query = req.scope.resolve<RemoteQueryFunction>(
|
||||||
|
ContainerRegistrationKeys.QUERY
|
||||||
|
)
|
||||||
|
|
||||||
|
const workflow = updateOrderChangeWorkflow(req.scope)
|
||||||
|
await workflow.run({
|
||||||
|
input: {
|
||||||
|
id,
|
||||||
|
carry_over_promotions,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await query.graph({
|
||||||
|
entity: "order_change",
|
||||||
|
filters: {
|
||||||
|
...req.filterableFields,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
fields: req.queryConfig.fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ order_change: result.data[0] })
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
|
import {
|
||||||
|
validateAndTransformBody,
|
||||||
|
validateAndTransformQuery,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import * as QueryConfig from "./query-config"
|
||||||
|
import {
|
||||||
|
AdminPostOrderChangesReqSchema,
|
||||||
|
AdminOrderChangeParams,
|
||||||
|
} from "./validators"
|
||||||
|
|
||||||
|
export const adminOrderChangesRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/order-changes/:id",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformBody(AdminPostOrderChangesReqSchema),
|
||||||
|
validateAndTransformQuery(
|
||||||
|
AdminOrderChangeParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
export const defaultAdminRetrieveOrderChangeFields = [
|
||||||
|
"id",
|
||||||
|
"order_id",
|
||||||
|
"return_id",
|
||||||
|
"claim_id",
|
||||||
|
"exchange_id",
|
||||||
|
"version",
|
||||||
|
"change_type",
|
||||||
|
"*actions",
|
||||||
|
"description",
|
||||||
|
"status",
|
||||||
|
"internal_note",
|
||||||
|
"created_by",
|
||||||
|
"requested_by",
|
||||||
|
"requested_at",
|
||||||
|
"confirmed_by",
|
||||||
|
"confirmed_at",
|
||||||
|
"declined_by",
|
||||||
|
"declined_reason",
|
||||||
|
"metadata",
|
||||||
|
"declined_at",
|
||||||
|
"canceled_by",
|
||||||
|
"canceled_at",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"carry_over_promotions",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const retrieveTransformQueryConfig = {
|
||||||
|
defaults: defaultAdminRetrieveOrderChangeFields,
|
||||||
|
isList: false,
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { createOperatorMap, createSelectParams } from "../../utils/validators"
|
||||||
|
|
||||||
|
export const AdminOrderChangeParams = createSelectParams().merge(
|
||||||
|
z.object({
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
status: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
change_type: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
created_at: createOperatorMap().optional(),
|
||||||
|
updated_at: createOperatorMap().optional(),
|
||||||
|
deleted_at: createOperatorMap().optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const AdminPostOrderChangesReqSchema = z.object({
|
||||||
|
carry_over_promotions: z.boolean(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type AdminPostOrderChangesReqSchemaType = z.infer<
|
||||||
|
typeof AdminPostOrderChangesReqSchema
|
||||||
|
>
|
||||||
@@ -17,7 +17,6 @@ export const defaultAdminRetrieveOrderFields = [
|
|||||||
"total",
|
"total",
|
||||||
"subtotal",
|
"subtotal",
|
||||||
"tax_total",
|
"tax_total",
|
||||||
"order_change",
|
|
||||||
"discount_total",
|
"discount_total",
|
||||||
"discount_tax_total",
|
"discount_tax_total",
|
||||||
"original_total",
|
"original_total",
|
||||||
@@ -81,6 +80,7 @@ export const defaultAdminRetrieveOrderChangesFields = [
|
|||||||
"canceled_at",
|
"canceled_at",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
|
"carry_over_promotions",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const defaultAdminOrderItemsFields = [
|
export const defaultAdminOrderItemsFields = [
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { adminFulfillmentsRoutesMiddlewares } from "./admin/fulfillments/middlew
|
|||||||
import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares"
|
import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares"
|
||||||
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
|
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
|
||||||
import { adminNotificationRoutesMiddlewares } from "./admin/notifications/middlewares"
|
import { adminNotificationRoutesMiddlewares } from "./admin/notifications/middlewares"
|
||||||
|
import { adminOrderChangesRoutesMiddlewares } from "./admin/order-changes/middlewares"
|
||||||
import { adminOrderEditRoutesMiddlewares } from "./admin/order-edits/middlewares"
|
import { adminOrderEditRoutesMiddlewares } from "./admin/order-edits/middlewares"
|
||||||
import { adminOrderRoutesMiddlewares } from "./admin/orders/middlewares"
|
import { adminOrderRoutesMiddlewares } from "./admin/orders/middlewares"
|
||||||
import { adminPaymentCollectionsMiddlewares } from "./admin/payment-collections/middlewares"
|
import { adminPaymentCollectionsMiddlewares } from "./admin/payment-collections/middlewares"
|
||||||
@@ -129,6 +130,7 @@ export default defineMiddlewares([
|
|||||||
...adminExchangeRoutesMiddlewares,
|
...adminExchangeRoutesMiddlewares,
|
||||||
...adminProductVariantRoutesMiddlewares,
|
...adminProductVariantRoutesMiddlewares,
|
||||||
...adminTaxProviderRoutesMiddlewares,
|
...adminTaxProviderRoutesMiddlewares,
|
||||||
|
...adminOrderChangesRoutesMiddlewares,
|
||||||
...adminOrderEditRoutesMiddlewares,
|
...adminOrderEditRoutesMiddlewares,
|
||||||
...adminPaymentCollectionsMiddlewares,
|
...adminPaymentCollectionsMiddlewares,
|
||||||
...viewConfigurationRoutesMiddlewares,
|
...viewConfigurationRoutesMiddlewares,
|
||||||
|
|||||||
@@ -721,6 +721,15 @@
|
|||||||
"length": 6,
|
"length": 6,
|
||||||
"mappedType": "datetime"
|
"mappedType": "datetime"
|
||||||
},
|
},
|
||||||
|
"carry_over_promotions": {
|
||||||
|
"name": "carry_over_promotions",
|
||||||
|
"type": "boolean",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "boolean"
|
||||||
|
},
|
||||||
"order_id": {
|
"order_id": {
|
||||||
"name": "order_id",
|
"name": "order_id",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -1978,15 +1987,6 @@
|
|||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_order_item_order_id_version\" ON \"order_item\" (\"order_id\", \"version\") WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_order_item_order_id_version\" ON \"order_item\" (\"order_id\", \"version\") WHERE deleted_at IS NULL"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"keyName": "IDX_order_item_version",
|
|
||||||
"columnNames": [],
|
|
||||||
"composite": false,
|
|
||||||
"constraint": false,
|
|
||||||
"primary": false,
|
|
||||||
"unique": false,
|
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_order_item_version\" ON \"order_item\" (version) WHERE deleted_at IS NULL"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"keyName": "IDX_order_item_item_id",
|
"keyName": "IDX_order_item_item_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Migration } from "@mikro-orm/migrations"
|
||||||
|
|
||||||
|
export class Migration20251125164002 extends Migration {
|
||||||
|
override async up(): Promise<void> {
|
||||||
|
this.addSql(
|
||||||
|
`alter table if exists "order_change" add column if not exists "carry_over_promotions" boolean null;`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override async down(): Promise<void> {
|
||||||
|
this.addSql(
|
||||||
|
`alter table if exists "order_change" drop column if exists "carry_over_promotions";`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ const _OrderChange = model
|
|||||||
declined_at: model.dateTime().nullable(),
|
declined_at: model.dateTime().nullable(),
|
||||||
canceled_by: model.text().nullable(),
|
canceled_by: model.text().nullable(),
|
||||||
canceled_at: model.dateTime().nullable(),
|
canceled_at: model.dateTime().nullable(),
|
||||||
|
carry_over_promotions: model.boolean().nullable(),
|
||||||
order: model.belongsTo<() => typeof Order>(() => Order, {
|
order: model.belongsTo<() => typeof Order>(() => Order, {
|
||||||
mappedBy: "changes",
|
mappedBy: "changes",
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3336,6 +3336,7 @@ export default class OrderModuleService
|
|||||||
"status",
|
"status",
|
||||||
"description",
|
"description",
|
||||||
"internal_note",
|
"internal_note",
|
||||||
|
"carry_over_promotions",
|
||||||
],
|
],
|
||||||
relations: [] as string[],
|
relations: [] as string[],
|
||||||
order: {},
|
order: {},
|
||||||
|
|||||||
Reference in new issue
Block a user