Feat(medusa, medusa-js, medusa-react): order edit item update (#2246)

**what**
Support `updateLineItem` which does the following:
- If no item change exist then create a new one and attaches the clone item with the adjustments and tax lines
- if an item change exists then delete/create adjustments and tax lines and update the cloned item quantity

**Tests**
- Unit tests core + client
- integration tests
  - When no item change already exists
  - When an item change already exists

FIXES CORE-497
This commit is contained in:
Adrien de Peretti
2022-09-28 11:09:33 +02:00
committed by GitHub
parent 1807bff029
commit 474e97252c
24 changed files with 1247 additions and 63 deletions

View File

@@ -16,10 +16,13 @@ const {
simpleLineItemFactory,
simpleProductFactory,
simpleOrderFactory,
simpleDiscountFactory,
simpleRegionFactory,
simpleCartFactory,
} = require("../../factories")
const { OrderEditItemChangeType, OrderEdit } = require("@medusajs/medusa")
jest.setTimeout(30000)
jest.setTimeout(50000)
const adminHeaders = {
headers: {
@@ -1094,4 +1097,725 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
}
})
})
describe("POST /admin/order-edits/:id/items/:item_id", () => {
let product, product2
const orderId = IdMap.getId("order-1")
const prodId1 = IdMap.getId("product-1")
const prodId2 = IdMap.getId("product-2")
const lineItemId1 = IdMap.getId("line-item-1")
const lineItemId2 = IdMap.getId("line-item-2")
beforeEach(async () => {
await adminSeeder(dbConnection)
product = await simpleProductFactory(dbConnection, {
id: prodId1,
})
product2 = await simpleProductFactory(dbConnection, {
id: prodId2,
})
await simpleOrderFactory(dbConnection, {
id: orderId,
email: "test@testson.com",
tax_rate: null,
fulfillment_status: "fulfilled",
payment_status: "captured",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12.5,
},
line_items: [
{
id: lineItemId1,
variant_id: product.variants[0].id,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
tax_lines: [
{
item_id: lineItemId1,
rate: 12.5,
code: "default",
name: "default",
},
],
},
{
id: lineItemId2,
variant_id: product2.variants[0].id,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
tax_lines: [
{
item_id: lineItemId2,
rate: 12.5,
code: "default",
name: "default",
},
],
},
],
})
})
afterEach(async () => {
const db = useDb()
return await db.teardown()
})
it("creates an order edit item change of type update on line item update", async () => {
const api = useApi()
const {
data: { order_edit },
} = await api.post(
`/admin/order-edits/`,
{
order_id: orderId,
internal_note: "This is an internal note",
},
adminHeaders
)
const orderEditId = order_edit.id
const updateItemId = order_edit.items.find(
(item) => item.original_item_id === lineItemId1
).id
const response = await api.post(
`/admin/order-edits/${orderEditId}/items/${updateItemId}`,
{ quantity: 2 },
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.order_edit.changes).toHaveLength(1)
expect(response.data.order_edit).toEqual(
expect.objectContaining({
id: orderEditId,
changes: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
type: "item_update",
order_edit_id: orderEditId,
original_line_item_id: lineItemId1,
line_item_id: expect.any(String),
line_item: expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
description: "",
thumbnail: "",
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 2,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
variant: expect.any(Object),
}),
original_line_item: expect.objectContaining({
id: lineItemId1,
created_at: expect.any(String),
updated_at: expect.any(String),
cart_id: null,
order_id: orderId,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
description: "",
thumbnail: "",
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
variant: expect.any(Object),
}),
}),
]),
status: "created",
order_id: orderId,
internal_note: "This is an internal note",
created_by: "admin_user",
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 2,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 12.5,
name: "default",
code: "default",
}),
]),
}),
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId2,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 12.5,
name: "default",
code: "default",
}),
]),
}),
]),
discount_total: 0,
gift_card_total: 0,
gift_card_tax_total: 0,
shipping_total: 0,
subtotal: 3000,
tax_total: 375,
total: 3375,
})
)
})
it("update an exising order edit item change of type update on multiple line item update", async () => {
const api = useApi()
const {
data: { order_edit },
} = await api.post(
`/admin/order-edits/`,
{
order_id: orderId,
internal_note: "This is an internal note",
},
adminHeaders
)
const orderEditId = order_edit.id
const updateItemId = order_edit.items.find(
(item) => item.original_item_id === lineItemId1
).id
await api.post(
`/admin/order-edits/${orderEditId}/items/${updateItemId}`,
{ quantity: 2 },
adminHeaders
)
const response = await api.post(
`/admin/order-edits/${orderEditId}/items/${updateItemId}`,
{ quantity: 3 },
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.order_edit.changes).toHaveLength(1)
expect(response.data.order_edit).toEqual(
expect.objectContaining({
id: orderEditId,
changes: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
type: "item_update",
order_edit_id: orderEditId,
original_line_item_id: lineItemId1,
line_item_id: expect.any(String),
line_item: expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
description: "",
thumbnail: "",
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 3,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
variant: expect.any(Object),
}),
original_line_item: expect.objectContaining({
id: lineItemId1,
created_at: expect.any(String),
updated_at: expect.any(String),
cart_id: null,
order_id: orderId,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
description: "",
thumbnail: "",
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
variant: expect.any(Object),
}),
}),
]),
status: "created",
order_id: orderId,
internal_note: "This is an internal note",
created_by: "admin_user",
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 3,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 12.5,
name: "default",
code: "default",
}),
]),
}),
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId2,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: 1,
returned_quantity: null,
shipped_quantity: 1,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 12.5,
name: "default",
code: "default",
}),
]),
}),
]),
discount_total: 0,
gift_card_total: 0,
gift_card_tax_total: 0,
shipping_total: 0,
subtotal: 4000,
tax_total: 500,
total: 4500,
})
)
})
it("update an exising order edit item change of type update on multiple line item update with correct totals including discounts", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection, { tax_rate: 10 })
const discountCode = "FIX_DISCOUNT"
const discount = await simpleDiscountFactory(dbConnection, {
code: discountCode,
rule: {
type: "fixed",
allocation: "total",
value: 2000,
},
regions: [region.id],
})
const cart = await simpleCartFactory(dbConnection, {
email: "adrien@test.com",
region: region.id,
line_items: [
{
id: lineItemId1,
variant_id: product.variants[0].id,
quantity: 1,
unit_price: 1000,
},
{
id: lineItemId2,
variant_id: product2.variants[0].id,
quantity: 1,
unit_price: 1000,
},
],
})
await api.post(`/store/carts/${cart.id}`, {
discounts: [{ code: discountCode }],
})
await api.post(`/store/carts/${cart.id}/payment-sessions`)
const completeRes = await api.post(`/store/carts/${cart.id}/complete`)
const order = completeRes.data.data
const {
data: { order_edit },
} = await api.post(
`/admin/order-edits/`,
{
order_id: order.id,
internal_note: "This is an internal note",
},
adminHeaders
)
const orderEditId = order_edit.id
const updateItemId = order_edit.items.find(
(item) => item.original_item_id === lineItemId1
).id
await api.post(
`/admin/order-edits/${orderEditId}/items/${updateItemId}`,
{ quantity: 2 },
adminHeaders
)
let response = await api.get(
`/admin/order-edits/${orderEditId}?expand=changes,items,items.tax_lines,items.adjustments`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.order_edit.changes).toHaveLength(1)
let item1 = response.data.order_edit.items.find(
(item) => item.original_item_id === lineItemId1
)
expect(item1.adjustments).toHaveLength(1)
let item2 = response.data.order_edit.items.find(
(item) => item.original_item_id === lineItemId2
)
expect(item2.adjustments).toHaveLength(1)
expect(response.data.order_edit).toEqual(
expect.objectContaining({
id: orderEditId,
changes: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
type: "item_update",
order_edit_id: orderEditId,
original_line_item_id: lineItemId1,
line_item_id: expect.any(String),
}),
]),
status: "created",
order_id: order.id,
internal_note: "This is an internal note",
created_by: "admin_user",
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 2,
fulfilled_quantity: null,
returned_quantity: null,
shipped_quantity: null,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 10,
}),
]),
adjustments: expect.arrayContaining([
expect.objectContaining({
discount_id: discount.id,
amount: 1333,
}),
]),
}),
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId2,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: null,
returned_quantity: null,
shipped_quantity: null,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 10,
}),
]),
adjustments: expect.arrayContaining([
expect.objectContaining({
discount_id: discount.id,
amount: 667,
}),
]),
}),
]),
discount_total: 2000,
gift_card_total: 0,
gift_card_tax_total: 0,
shipping_total: 0,
subtotal: 3000,
tax_total: 100,
total: 1100,
})
)
await api.post(
`/admin/order-edits/${orderEditId}/items/${updateItemId}`,
{ quantity: 3 },
adminHeaders
)
response = await api.get(
`/admin/order-edits/${orderEditId}?expand=changes,items,items.tax_lines,items.adjustments`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.order_edit.changes).toHaveLength(1)
item1 = response.data.order_edit.items.find(
(item) => item.original_item_id === lineItemId1
)
expect(item1.adjustments).toHaveLength(1)
item2 = response.data.order_edit.items.find(
(item) => item.original_item_id === lineItemId2
)
expect(item2.adjustments).toHaveLength(1)
expect(response.data.order_edit).toEqual(
expect.objectContaining({
id: orderEditId,
changes: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
type: "item_update",
order_edit_id: orderEditId,
original_line_item_id: lineItemId1,
line_item_id: expect.any(String),
}),
]),
status: "created",
order_id: order.id,
internal_note: "This is an internal note",
created_by: "admin_user",
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId1,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 3,
fulfilled_quantity: null,
returned_quantity: null,
shipped_quantity: null,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 10,
}),
]),
adjustments: expect.arrayContaining([
expect.objectContaining({
discount_id: discount.id,
amount: 1500,
}),
]),
}),
expect.objectContaining({
id: expect.any(String),
original_item_id: lineItemId2,
order_edit_id: orderEditId,
cart_id: null,
order_id: null,
swap_id: null,
claim_order_id: null,
title: expect.any(String),
is_return: false,
is_giftcard: false,
should_merge: true,
allow_discounts: true,
has_shipping: null,
unit_price: 1000,
variant_id: expect.any(String),
quantity: 1,
fulfilled_quantity: null,
returned_quantity: null,
shipped_quantity: null,
metadata: null,
tax_lines: expect.arrayContaining([
expect.objectContaining({
rate: 10,
}),
]),
adjustments: expect.arrayContaining([
expect.objectContaining({
discount_id: discount.id,
amount: 500,
}),
]),
}),
]),
discount_total: 2000,
gift_card_total: 0,
gift_card_tax_total: 0,
shipping_total: 0,
subtotal: 4000,
tax_total: 200,
total: 2200,
})
)
})
})
})