Feat(medusa): remove item from order (#2273)
* wait for update to order edit model * delete line item tests * create remove method for lineitem with tax lines * add remove item tests * split delete allocation tests into two: more and less than total * remove unused import * cleanup * add medusa-js and react endpoints * pr feedback fixes * linting * remove unused relation from query * remove removed-event and unused imports * add await
This commit is contained in:
@@ -1419,7 +1419,8 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
|
||||
})
|
||||
|
||||
describe("POST /admin/order-edits/:id/confirm", () => {
|
||||
let product, product2
|
||||
let product
|
||||
let product2
|
||||
const prodId1 = IdMap.getId("product-1")
|
||||
const prodId2 = IdMap.getId("product-2")
|
||||
const lineItemId1 = IdMap.getId("line-item-1")
|
||||
@@ -1623,7 +1624,8 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
|
||||
})
|
||||
|
||||
describe("POST /admin/order-edits/:id/items/:item_id", () => {
|
||||
let product, product2
|
||||
let product
|
||||
let product2
|
||||
const orderId = IdMap.getId("order-1")
|
||||
const prodId1 = IdMap.getId("product-1")
|
||||
const prodId2 = IdMap.getId("product-2")
|
||||
@@ -2332,4 +2334,511 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("DELETE /admin/order-edits/:id/items/:item_id", () => {
|
||||
let product
|
||||
let product2
|
||||
let discountCode
|
||||
let discountCodeLarge
|
||||
let cart
|
||||
const orderId = IdMap.getId("order-1")
|
||||
const discountOrderId = IdMap.getId("order-2")
|
||||
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")
|
||||
const lineItemId1Discount = IdMap.getId("line-item-1-discount")
|
||||
const lineItemId2Discount = IdMap.getId("line-item-2-discount")
|
||||
|
||||
beforeEach(async () => {
|
||||
const api = useApi()
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
product = await simpleProductFactory(dbConnection, {
|
||||
id: prodId1,
|
||||
})
|
||||
|
||||
product2 = await simpleProductFactory(dbConnection, {
|
||||
id: prodId2,
|
||||
})
|
||||
|
||||
const reagion = await simpleRegionFactory(dbConnection, {
|
||||
id: "test-region",
|
||||
name: "Test region",
|
||||
tax_rate: 12.5,
|
||||
})
|
||||
|
||||
await simpleOrderFactory(dbConnection, {
|
||||
id: orderId,
|
||||
email: "test-2@testson.com",
|
||||
tax_rate: null,
|
||||
fulfillment_status: "fulfilled",
|
||||
payment_status: "captured",
|
||||
region_id: "test-region",
|
||||
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",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
discountCode = "FIX_DISCOUNT_SMALL"
|
||||
const discount = await simpleDiscountFactory(dbConnection, {
|
||||
code: discountCode,
|
||||
rule: {
|
||||
type: "fixed",
|
||||
allocation: "total",
|
||||
value: 500,
|
||||
},
|
||||
regions: ["test-region"],
|
||||
})
|
||||
|
||||
discountCodeLarge = "FIX_DISCOUNT_LARGE"
|
||||
const discountLarge = await simpleDiscountFactory(dbConnection, {
|
||||
code: discountCodeLarge,
|
||||
rule: {
|
||||
type: "fixed",
|
||||
allocation: "total",
|
||||
value: 1200,
|
||||
},
|
||||
regions: ["test-region"],
|
||||
})
|
||||
|
||||
cart = await simpleCartFactory(dbConnection, {
|
||||
email: "adrien@test.com",
|
||||
region: "test-region",
|
||||
line_items: [
|
||||
{
|
||||
id: lineItemId1Discount,
|
||||
variant_id: product.variants[0].id,
|
||||
quantity: 1,
|
||||
unit_price: 1000,
|
||||
},
|
||||
{
|
||||
id: lineItemId2Discount,
|
||||
variant_id: product2.variants[0].id,
|
||||
quantity: 1,
|
||||
unit_price: 1000,
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
it("creates an order edit item change of type delete on line item delete", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const {
|
||||
data: { order_edit },
|
||||
} = await api
|
||||
.post(
|
||||
`/admin/order-edits/`,
|
||||
{
|
||||
order_id: orderId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
.catch(console.log)
|
||||
|
||||
const orderEditId = order_edit.id
|
||||
const editLineItemId = order_edit.items.find(
|
||||
(it) => it.original_item_id === lineItemId1
|
||||
).id
|
||||
|
||||
const response = await api
|
||||
.delete(
|
||||
`/admin/order-edits/${orderEditId}/items/${editLineItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
.catch(console.log)
|
||||
|
||||
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_remove",
|
||||
order_edit_id: orderEditId,
|
||||
original_line_item_id: lineItemId1,
|
||||
line_item_id: null,
|
||||
line_item: null,
|
||||
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,
|
||||
created_by: "admin_user",
|
||||
items: [
|
||||
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: 1000,
|
||||
tax_total: 125,
|
||||
total: 1125,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates an order edit item change of type delete on line item delete and adjusts discount allocation from one to two items", async () => {
|
||||
const api = useApi()
|
||||
|
||||
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 discountOrder = completeRes.data.data
|
||||
|
||||
const {
|
||||
data: { order_edit },
|
||||
} = await api.post(
|
||||
`/admin/order-edits/`,
|
||||
{
|
||||
order_id: discountOrder.id,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const editLineItemId = order_edit.items.find(
|
||||
(it) => it.original_item_id === lineItemId1Discount
|
||||
).id
|
||||
|
||||
const orderEditId = order_edit.id
|
||||
await api.delete(
|
||||
`/admin/order-edits/${orderEditId}/items/${editLineItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const response = await api.get(
|
||||
`/admin/order-edits/${orderEditId}`,
|
||||
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.objectContaining({
|
||||
id: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
type: "item_remove",
|
||||
order_edit_id: orderEditId,
|
||||
original_line_item_id: lineItemId1Discount,
|
||||
line_item_id: null,
|
||||
original_line_item: expect.objectContaining({
|
||||
id: lineItemId1Discount,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
cart_id: expect.any(String),
|
||||
order_id: discountOrder.id,
|
||||
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,
|
||||
returned_quantity: null,
|
||||
shipped_quantity: null,
|
||||
metadata: null,
|
||||
variant: expect.any(Object),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
status: "created",
|
||||
order_id: discountOrder.id,
|
||||
created_by: "admin_user",
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
original_item_id: lineItemId2Discount,
|
||||
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: 1,
|
||||
returned_quantity: null,
|
||||
metadata: null,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
item_id: expect.any(String),
|
||||
description: "discount",
|
||||
discount_id: expect.any(String),
|
||||
amount: 500,
|
||||
metadata: null,
|
||||
}),
|
||||
],
|
||||
tax_lines: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rate: 12.5,
|
||||
name: "default",
|
||||
code: "default",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
discount_total: 500,
|
||||
gift_card_total: 0,
|
||||
gift_card_tax_total: 0,
|
||||
shipping_total: 0,
|
||||
subtotal: 1000,
|
||||
tax_total: 63,
|
||||
total: 563,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates an order edit item change of type delete on line item delete and adjusts discount allocation from one to two items with discount amount being larger than the unit price of the remaining line item", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await api.post(`/store/carts/${cart.id}`, {
|
||||
discounts: [{ code: discountCodeLarge }],
|
||||
})
|
||||
|
||||
await api.post(`/store/carts/${cart.id}/payment-sessions`)
|
||||
|
||||
const completeRes = await api.post(`/store/carts/${cart.id}/complete`)
|
||||
|
||||
const discountOrder = completeRes.data.data
|
||||
|
||||
const {
|
||||
data: { order_edit },
|
||||
} = await api.post(
|
||||
`/admin/order-edits/`,
|
||||
{
|
||||
order_id: discountOrder.id,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const editLineItemId = order_edit.items.find(
|
||||
(it) => it.original_item_id === lineItemId1Discount
|
||||
).id
|
||||
|
||||
const orderEditId = order_edit.id
|
||||
await api.delete(
|
||||
`/admin/order-edits/${orderEditId}/items/${editLineItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const response = await api.get(
|
||||
`/admin/order-edits/${orderEditId}`,
|
||||
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.objectContaining({
|
||||
id: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
type: "item_remove",
|
||||
order_edit_id: orderEditId,
|
||||
original_line_item_id: lineItemId1Discount,
|
||||
line_item_id: null,
|
||||
original_line_item: expect.objectContaining({
|
||||
id: lineItemId1Discount,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
cart_id: expect.any(String),
|
||||
order_id: discountOrder.id,
|
||||
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,
|
||||
returned_quantity: null,
|
||||
shipped_quantity: null,
|
||||
metadata: null,
|
||||
variant: expect.any(Object),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
status: "created",
|
||||
order_id: discountOrder.id,
|
||||
created_by: "admin_user",
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
original_item_id: lineItemId2Discount,
|
||||
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: 1,
|
||||
returned_quantity: null,
|
||||
metadata: null,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
item_id: expect.any(String),
|
||||
description: "discount",
|
||||
discount_id: expect.any(String),
|
||||
amount: 1000,
|
||||
metadata: null,
|
||||
}),
|
||||
],
|
||||
tax_lines: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rate: 12.5,
|
||||
name: "default",
|
||||
code: "default",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
discount_total: 1000,
|
||||
gift_card_total: 0,
|
||||
gift_card_tax_total: 0,
|
||||
shipping_total: 0,
|
||||
subtotal: 1000,
|
||||
tax_total: 0,
|
||||
total: 0,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -95,6 +95,15 @@ class AdminOrderEditsResource extends BaseResource {
|
||||
const path = `/admin/order-edits/${orderEditId}/items/${itemId}`
|
||||
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||
}
|
||||
|
||||
removeLineItem(
|
||||
orderEditId: string,
|
||||
itemId: string,
|
||||
customHeaders: Record<string, any> = {}
|
||||
): ResponsePromise<AdminOrderEditsRes> {
|
||||
const path = `/admin/order-edits/${orderEditId}/items/${itemId}`
|
||||
return this.client.request("DELETE", path, undefined, {}, customHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminOrderEditsResource
|
||||
|
||||
@@ -1783,6 +1783,22 @@ export const adminHandlers = [
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.delete("/admin/order-edits/:id/items/:item_id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
order_edit: {
|
||||
...fixtures.get("order_edit"),
|
||||
changes: [
|
||||
{
|
||||
type: 'item_remove'
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/admin/auth", (req, res, ctx) => {
|
||||
return res(
|
||||
|
||||
@@ -93,6 +93,27 @@ export const useAdminOrderEditUpdateLineItem = (
|
||||
)
|
||||
}
|
||||
|
||||
export const useAdminOrderEditDeleteLineItem = (
|
||||
orderEditId: string,
|
||||
itemId: string,
|
||||
options?: UseMutationOptions<
|
||||
Response<AdminOrderEditsRes>,
|
||||
Error
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation(
|
||||
(() => client.admin.orderEdits.removeLineItem(orderEditId, itemId)),
|
||||
buildOptions(
|
||||
queryClient,
|
||||
[adminOrderEditsKeys.detail(orderEditId), adminOrderEditsKeys.lists()],
|
||||
options
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export const useAdminUpdateOrderEdit = (
|
||||
id: string,
|
||||
options?: UseMutationOptions<
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
useAdminOrderEditLineItem,
|
||||
useAdminCancelOrderEdit,
|
||||
useAdminUpdateOrderEdit,
|
||||
useAdminOrderEditDeleteLineItem,
|
||||
} from "../../../../src/"
|
||||
import { fixtures } from "../../../../mocks/data"
|
||||
import { createWrapper } from "../../../utils"
|
||||
@@ -247,3 +248,32 @@ describe("useAdminConfirmOrderEdit hook", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe("useAdminOrderEditDeleteLineItem hook", () => {
|
||||
test("Remove line item of an order edit and create an item change", async () => {
|
||||
const id = "oe_1"
|
||||
const itemId = "item_1"
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useAdminOrderEditDeleteLineItem(id, itemId),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate()
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.order_edit).toEqual(
|
||||
expect.objectContaining({
|
||||
...fixtures.get("order_edit"),
|
||||
changes: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'item_remove'
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import OrderEditingFeatureFlag from "../../../../../loaders/feature-flags/order-editing"
|
||||
import { orderEditServiceMock } from "../../../../../services/__mocks__/order-edit"
|
||||
|
||||
describe("DELETE /admin/order-edits/:id/items/:item_id", () => {
|
||||
describe("deletes a line item", () => {
|
||||
const lineItemId = IdMap.getId("testLineItem")
|
||||
const orderEditId = IdMap.getId("testCreatedOrder")
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("DELETE", `/admin/order-edits/${orderEditId}/items/${lineItemId}`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
flags: [OrderEditingFeatureFlag],
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls orderService removeLineItem", () => {
|
||||
expect(orderEditServiceMock.removeLineItem).toHaveBeenCalledTimes(1)
|
||||
expect(orderEditServiceMock.removeLineItem).toHaveBeenCalledWith(orderEditId, lineItemId)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns retrieve result", () => {
|
||||
expect(subject.body.order_edit).toEqual(expect.objectContaining({
|
||||
id: orderEditId,
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,85 @@
|
||||
import { EntityManager } from "typeorm"
|
||||
import { OrderEditService } from "../../../../services"
|
||||
import { Request, Response } from "express"
|
||||
import { IsNumber } from "class-validator"
|
||||
import {
|
||||
defaultOrderEditFields,
|
||||
defaultOrderEditRelations,
|
||||
} from "../../../../types/order-edit"
|
||||
|
||||
/**
|
||||
* @oas [delete] /order-edits/{id}/items/{item_id}
|
||||
* operationId: "DeleteOrderEditsOrderEditLineItemsLineItem"
|
||||
* summary: "Delete line items from an order edit and create change item"
|
||||
* description: "Delete line items from an order edit and create change item"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Order Edit to delete from.
|
||||
* - (path) item_id=* {string} The ID of the order edit item to delete from order.
|
||||
* x-codeSamples:
|
||||
* - lang: JavaScript
|
||||
* label: JS Client
|
||||
* source: |
|
||||
* import Medusa from "@medusajs/medusa-js"
|
||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||
* // must be previously logged in or use api token
|
||||
* medusa.admin.orderEdits.removeLineItem(order_edit_id, line_item_id)
|
||||
* .then(({ order_edit }) => {
|
||||
* console.log(order_edit.id)
|
||||
* })
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request DELETE 'https://medusa-url.com/admin/order-edits/{id}/items/{item_id}' \
|
||||
* --header 'Authorization: Bearer {api_token}'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - OrderEdit
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order_edit:
|
||||
* $ref: "#/components/schemas/order_edit"
|
||||
* "400":
|
||||
* $ref: "#/components/responses/400_error"
|
||||
* "401":
|
||||
* $ref: "#/components/responses/unauthorized"
|
||||
* "404":
|
||||
* $ref: "#/components/responses/not_found_error"
|
||||
* "409":
|
||||
* $ref: "#/components/responses/invalid_state_error"
|
||||
* "422":
|
||||
* $ref: "#/components/responses/invalid_request_error"
|
||||
* "500":
|
||||
* $ref: "#/components/responses/500_error"
|
||||
*/
|
||||
export default async (req: Request, res: Response) => {
|
||||
const { id, item_id } = req.params
|
||||
|
||||
const orderEditService: OrderEditService =
|
||||
req.scope.resolve("orderEditService")
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await orderEditService
|
||||
.withTransaction(transactionManager)
|
||||
.removeLineItem(id, item_id)
|
||||
})
|
||||
|
||||
let orderEdit = await orderEditService.retrieve(id, {
|
||||
select: defaultOrderEditFields,
|
||||
relations: defaultOrderEditRelations,
|
||||
})
|
||||
orderEdit = await orderEditService.decorateTotals(orderEdit)
|
||||
|
||||
res.status(200).send({
|
||||
order_edit: orderEdit,
|
||||
})
|
||||
}
|
||||
@@ -82,6 +82,11 @@ export default (app) => {
|
||||
middlewares.wrap(require("./update-order-edit-line-item").default)
|
||||
)
|
||||
|
||||
route.delete(
|
||||
"/:id/items/:item_id",
|
||||
middlewares.wrap(require("./delete-line-item").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
* description: "Create or update the order edit change holding the line item changes"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Order Edit to delete.
|
||||
* - (path) id=* {string} The ID of the Order Edit to update.
|
||||
* - (path) item_id=* {string} The ID of the order edit item to update.
|
||||
* x-codeSamples:
|
||||
* - lang: JavaScript
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request DELETE 'https://medusa-url.com/admin/order-edits/{id}/items/{item_id}' \
|
||||
* curl --location --request POST 'https://medusa-url.com/admin/order-edits/{id}/items/{item_id}' \
|
||||
* --header 'Authorization: Bearer {api_token}'
|
||||
* -d '{ "quantity": 5 }'
|
||||
* security:
|
||||
|
||||
@@ -22,10 +22,6 @@ export default (app, featureFlagRouter: FlagRouter) => {
|
||||
relations.push("sales_channel")
|
||||
}
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(OrderEditingFeatureFlag.key)) {
|
||||
relations.push("edits")
|
||||
}
|
||||
|
||||
/**
|
||||
* List orders
|
||||
*/
|
||||
|
||||
@@ -146,6 +146,9 @@ export const orderEditServiceMock = {
|
||||
updateLineItem: jest.fn().mockImplementation((_) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
removeLineItem: jest.fn().mockImplementation((_) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ProductService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
TaxProviderService,
|
||||
} from "./index"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
@@ -30,6 +31,7 @@ type InjectedDependencies = {
|
||||
pricingService: PricingService
|
||||
regionService: RegionService
|
||||
lineItemAdjustmentService: LineItemAdjustmentService
|
||||
taxProviderService: TaxProviderService
|
||||
featureFlagRouter: FlagRouter
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ class LineItemService extends TransactionBaseService {
|
||||
protected readonly regionService_: RegionService
|
||||
protected readonly featureFlagRouter_: FlagRouter
|
||||
protected readonly lineItemAdjustmentService_: LineItemAdjustmentService
|
||||
protected readonly taxProviderService_: TaxProviderService
|
||||
|
||||
constructor({
|
||||
manager,
|
||||
@@ -57,6 +60,7 @@ class LineItemService extends TransactionBaseService {
|
||||
regionService,
|
||||
cartRepository,
|
||||
lineItemAdjustmentService,
|
||||
taxProviderService,
|
||||
featureFlagRouter,
|
||||
}: InjectedDependencies) {
|
||||
super(arguments[0])
|
||||
@@ -70,6 +74,7 @@ class LineItemService extends TransactionBaseService {
|
||||
this.regionService_ = regionService
|
||||
this.cartRepository_ = cartRepository
|
||||
this.lineItemAdjustmentService_ = lineItemAdjustmentService
|
||||
this.taxProviderService_ = taxProviderService
|
||||
this.featureFlagRouter_ = featureFlagRouter
|
||||
}
|
||||
|
||||
@@ -352,6 +357,27 @@ class LineItemService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a line item with the tax lines.
|
||||
* @param id - the id of the line item to delete
|
||||
* @return the result of the delete operation
|
||||
*/
|
||||
async deleteWithTaxLines(id: string): Promise<LineItem | undefined> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepository = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
await this.taxProviderService_
|
||||
.withTransaction(transactionManager)
|
||||
.clearLineItemsTaxLines([id])
|
||||
|
||||
return await this.delete(id)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a line item tax line.
|
||||
* @param args - tax line partial passed to the repo create method
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class OrderEditItemChangeService extends TransactionBaseService {
|
||||
lineItemService,
|
||||
taxProviderService,
|
||||
}: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
|
||||
this.manager_ = manager
|
||||
@@ -125,7 +125,9 @@ export default class OrderEditItemChangeService extends TransactionBaseService {
|
||||
|
||||
const lineItemServiceTx = this.lineItemService_.withTransaction(manager)
|
||||
await Promise.all([
|
||||
...lineItemIdsToRemove.map((id) => lineItemServiceTx.delete(id)),
|
||||
...lineItemIdsToRemove.map(
|
||||
async (id) => await lineItemServiceTx.delete(id)
|
||||
),
|
||||
this.taxProviderService_
|
||||
.withTransaction(manager)
|
||||
.clearLineItemsTaxLines(lineItemIdsToRemove),
|
||||
|
||||
@@ -389,6 +389,63 @@ export default class OrderEditService extends TransactionBaseService {
|
||||
})
|
||||
}
|
||||
|
||||
async removeLineItem(orderEditId: string, lineItemId: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const orderEdit = await this.retrieve(orderEditId, {
|
||||
select: [
|
||||
"id",
|
||||
"created_at",
|
||||
"requested_at",
|
||||
"confirmed_at",
|
||||
"declined_at",
|
||||
"canceled_at",
|
||||
],
|
||||
})
|
||||
|
||||
const isOrderEditActive = OrderEditService.isOrderEditActive(orderEdit)
|
||||
|
||||
if (!isOrderEditActive) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Can not update an item on the order edit ${orderEditId} with the status ${orderEdit.status}`
|
||||
)
|
||||
}
|
||||
|
||||
const lineItem = await this.lineItemService_
|
||||
.withTransaction(manager)
|
||||
.retrieve(lineItemId, {
|
||||
select: ["id", "order_edit_id", "original_item_id"],
|
||||
})
|
||||
.catch(() => void 0)
|
||||
|
||||
if (!lineItem) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
lineItem.order_edit_id !== orderEditId ||
|
||||
!lineItem.original_item_id
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Invalid line item id ${lineItemId} it does not belong to the same order edit ${orderEdit.order_id}.`
|
||||
)
|
||||
}
|
||||
|
||||
await this.lineItemService_
|
||||
.withTransaction(manager)
|
||||
.deleteWithTaxLines(lineItem.id)
|
||||
|
||||
await this.refreshAdjustments(orderEditId)
|
||||
|
||||
await this.orderEditItemChangeService_.withTransaction(manager).create({
|
||||
original_line_item_id: lineItem.original_item_id,
|
||||
type: OrderEditItemChangeType.ITEM_REMOVE,
|
||||
order_edit_id: orderEdit.id,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async refreshAdjustments(orderEditId: string) {
|
||||
const manager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
|
||||
@@ -966,4 +966,3 @@ const SalesChannelsSchema: ProductImportCsvSchema = {
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user