feat(medusa, medusa-js, medusa-react): Implement item change deletion from an order edit (#2241)
This commit is contained in:
@@ -520,4 +520,230 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("DELETE /admin/order-edits/:id/changes/:change_id", () => {
|
||||||
|
let product
|
||||||
|
const orderId1 = IdMap.getId("order-id-1")
|
||||||
|
const orderEditId = IdMap.getId("order-edit-1")
|
||||||
|
const orderEditId2 = IdMap.getId("order-edit-2")
|
||||||
|
const prodId1 = IdMap.getId("prodId1")
|
||||||
|
const lineItemId1 = IdMap.getId("line-item-1")
|
||||||
|
const changeUpdateId = IdMap.getId("order-id-1-change-1")
|
||||||
|
const changeUpdateId2 = IdMap.getId("order-id-1-change-2")
|
||||||
|
const lineItemUpdateId = IdMap.getId("line-item-1-update")
|
||||||
|
const lineItemUpdateId2 = IdMap.getId("line-item-1-update-2")
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: prodId1,
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderFactory(dbConnection, {
|
||||||
|
id: orderId1,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleLineItemFactory(dbConnection, {
|
||||||
|
id: lineItemUpdateId,
|
||||||
|
order_id: null,
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
unit_price: 100,
|
||||||
|
quantity: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleLineItemFactory(dbConnection, {
|
||||||
|
id: lineItemUpdateId2,
|
||||||
|
order_id: null,
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
unit_price: 100,
|
||||||
|
quantity: 2,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
return await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("deletes an item change from an order edit", async () => {
|
||||||
|
await simpleLineItemFactory(dbConnection, {
|
||||||
|
id: lineItemUpdateId,
|
||||||
|
order_id: null,
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
unit_price: 100,
|
||||||
|
quantity: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: orderEditId,
|
||||||
|
order_id: orderId1,
|
||||||
|
created_by: "admin_user",
|
||||||
|
internal_note: "test internal note",
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderItemChangeFactory(dbConnection, {
|
||||||
|
id: changeUpdateId,
|
||||||
|
type: OrderEditItemChangeType.ITEM_UPDATE,
|
||||||
|
line_item_id: lineItemUpdateId,
|
||||||
|
original_line_item_id: lineItemId1,
|
||||||
|
order_edit_id: orderEditId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
let res = await api.get(`/admin/order-edits/${orderEditId}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.data.order_edit.changes.length).toBe(1)
|
||||||
|
|
||||||
|
res = await api.delete(
|
||||||
|
`/admin/order-edits/${orderEditId}/changes/${changeUpdateId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: changeUpdateId,
|
||||||
|
object: "item_change",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
res = await api.get(`/admin/order-edits/${orderEditId}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.data.order_edit.changes.length).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("return invalid error if the item change does not belong to the order edit", async () => {
|
||||||
|
await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: orderEditId,
|
||||||
|
order_id: orderId1,
|
||||||
|
created_by: "admin_user",
|
||||||
|
internal_note: "test internal note 2",
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderItemChangeFactory(dbConnection, {
|
||||||
|
id: changeUpdateId,
|
||||||
|
type: OrderEditItemChangeType.ITEM_UPDATE,
|
||||||
|
line_item_id: lineItemUpdateId,
|
||||||
|
original_line_item_id: lineItemId1,
|
||||||
|
order_edit_id: orderEditId,
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: orderEditId2,
|
||||||
|
order_id: orderId1,
|
||||||
|
created_by: "admin_user",
|
||||||
|
internal_note: "test internal note 2",
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderItemChangeFactory(dbConnection, {
|
||||||
|
id: changeUpdateId2,
|
||||||
|
type: OrderEditItemChangeType.ITEM_UPDATE,
|
||||||
|
line_item_id: lineItemUpdateId2,
|
||||||
|
original_line_item_id: lineItemId1,
|
||||||
|
order_edit_id: orderEditId2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.delete(
|
||||||
|
`/admin/order-edits/${orderEditId}/changes/${changeUpdateId2}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.response.status).toEqual(400)
|
||||||
|
expect(response.response.data.message).toEqual(
|
||||||
|
`The item change you are trying to delete doesn't belong to the OrderEdit with id: ${orderEditId}.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("return an error if the order edit is confirmed", async () => {
|
||||||
|
await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: orderEditId,
|
||||||
|
order_id: orderId1,
|
||||||
|
created_by: "admin_user",
|
||||||
|
internal_note: "test internal note 3",
|
||||||
|
confirmed_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderItemChangeFactory(dbConnection, {
|
||||||
|
id: changeUpdateId,
|
||||||
|
type: OrderEditItemChangeType.ITEM_UPDATE,
|
||||||
|
line_item_id: lineItemUpdateId,
|
||||||
|
original_line_item_id: lineItemId1,
|
||||||
|
order_edit_id: orderEditId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.delete(
|
||||||
|
`/admin/order-edits/${orderEditId}/changes/${changeUpdateId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.response.status).toEqual(400)
|
||||||
|
expect(response.response.data.message).toEqual(
|
||||||
|
"Cannot delete and item change from a confirmed order edit"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("return an error if the order edit is canceled", async () => {
|
||||||
|
await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: orderEditId,
|
||||||
|
order_id: orderId1,
|
||||||
|
created_by: "admin_user",
|
||||||
|
internal_note: "test internal note 4",
|
||||||
|
canceled_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleOrderItemChangeFactory(dbConnection, {
|
||||||
|
id: changeUpdateId,
|
||||||
|
type: OrderEditItemChangeType.ITEM_UPDATE,
|
||||||
|
line_item_id: lineItemUpdateId,
|
||||||
|
original_line_item_id: lineItemId1,
|
||||||
|
order_edit_id: orderEditId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.delete(
|
||||||
|
`/admin/order-edits/${orderEditId}/changes/${changeUpdateId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.response.status).toEqual(400)
|
||||||
|
expect(response.response.data.message).toEqual(
|
||||||
|
"Cannot delete and item change from a canceled order edit"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
AdminOrderEditsRes,
|
|
||||||
AdminPostOrderEditsReq,
|
|
||||||
AdminOrderEditDeleteRes,
|
AdminOrderEditDeleteRes,
|
||||||
|
AdminOrderEditItemChangeDeleteRes,
|
||||||
|
AdminOrderEditsRes,
|
||||||
AdminPostOrderEditsOrderEditReq,
|
AdminPostOrderEditsOrderEditReq,
|
||||||
|
AdminPostOrderEditsReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import { ResponsePromise } from "../../typings"
|
import { ResponsePromise } from "../../typings"
|
||||||
import BaseResource from "../base"
|
import BaseResource from "../base"
|
||||||
@@ -40,6 +41,15 @@ class AdminOrderEditsResource extends BaseResource {
|
|||||||
const path = `/admin/order-edits/${id}`
|
const path = `/admin/order-edits/${id}`
|
||||||
return this.client.request("DELETE", path, undefined, {}, customHeaders)
|
return this.client.request("DELETE", path, undefined, {}, customHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteItemChange(
|
||||||
|
orderEditId: string,
|
||||||
|
itemChangeId: string,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
): ResponsePromise<AdminOrderEditItemChangeDeleteRes> {
|
||||||
|
const path = `/admin/order-edits/${orderEditId}/changes/${itemChangeId}`
|
||||||
|
return this.client.request("DELETE", path, undefined, {}, customHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminOrderEditsResource
|
export default AdminOrderEditsResource
|
||||||
|
|||||||
@@ -1703,7 +1703,19 @@ export const adminHandlers = [
|
|||||||
ctx.json({
|
ctx.json({
|
||||||
id,
|
id,
|
||||||
object: "order_edit",
|
object: "order_edit",
|
||||||
deleted: true
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.delete("/admin/order-edits/:id/changes/:change_id", (req, res, ctx) => {
|
||||||
|
const { change_id } = req.params
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
id: change_id,
|
||||||
|
object: "item_change",
|
||||||
|
deleted: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { Response } from "@medusajs/medusa-js"
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AdminOrderEditDeleteRes,
|
AdminOrderEditDeleteRes,
|
||||||
AdminPostOrderEditsOrderEditReq,
|
AdminOrderEditItemChangeDeleteRes,
|
||||||
AdminOrderEditsRes,
|
AdminOrderEditsRes,
|
||||||
|
AdminPostOrderEditsOrderEditReq,
|
||||||
AdminPostOrderEditsReq,
|
AdminPostOrderEditsReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
|
||||||
@@ -45,6 +46,28 @@ export const useAdminDeleteOrderEdit = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useAdminDeleteOrderEditItemChange = (
|
||||||
|
orderEditId: string,
|
||||||
|
itemChangeId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminOrderEditItemChangeDeleteRes>,
|
||||||
|
Error,
|
||||||
|
void
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
() => client.admin.orderEdits.deleteItemChange(orderEditId, itemChangeId),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminOrderEditsKeys.detail(orderEditId), adminOrderEditsKeys.lists()],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const useAdminUpdateOrderEdit = (
|
export const useAdminUpdateOrderEdit = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
|
|||||||
@@ -2,13 +2,38 @@ import { renderHook } from "@testing-library/react-hooks"
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
useAdminCreateOrderEdit,
|
useAdminCreateOrderEdit,
|
||||||
useAdminUpdateOrderEdit,
|
|
||||||
useAdminDeleteOrderEdit,
|
useAdminDeleteOrderEdit,
|
||||||
|
useAdminDeleteOrderEditItemChange,
|
||||||
|
useAdminUpdateOrderEdit,
|
||||||
} from "../../../../src/"
|
} from "../../../../src/"
|
||||||
import { fixtures } from "../../../../mocks/data"
|
import { fixtures } from "../../../../mocks/data"
|
||||||
import { fixtures } from "../../../../mocks/data"
|
|
||||||
import { createWrapper } from "../../../utils"
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminDeleteOrderEditItemChange hook", () => {
|
||||||
|
test("Deletes an order edit item change", async () => {
|
||||||
|
const id = "oe_1"
|
||||||
|
const itemChangeId = "oeic_1"
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminDeleteOrderEditItemChange(id, itemChangeId),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate()
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: itemChangeId,
|
||||||
|
object: "item_change",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("useAdminDelete hook", () => {
|
describe("useAdminDelete hook", () => {
|
||||||
test("Deletes an order edit", async () => {
|
test("Deletes an order edit", async () => {
|
||||||
const id = "oe_1"
|
const id = "oe_1"
|
||||||
|
|||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
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/changes/:change_id", () => {
|
||||||
|
describe("deletes an order edit item change", () => {
|
||||||
|
const orderEditId = IdMap.getId("test-order-edit")
|
||||||
|
const orderEditItemChangeId = IdMap.getId("test-order-edit-item-change")
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request(
|
||||||
|
"DELETE",
|
||||||
|
`/admin/order-edits/${orderEditId}/changes/${orderEditItemChangeId}`,
|
||||||
|
{
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: [OrderEditingFeatureFlag],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls orderEditService delete", () => {
|
||||||
|
expect(orderEditServiceMock.deleteItemChange).toHaveBeenCalledTimes(1)
|
||||||
|
expect(orderEditServiceMock.deleteItemChange).toHaveBeenCalledWith(
|
||||||
|
orderEditId,
|
||||||
|
orderEditItemChangeId
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns delete result", () => {
|
||||||
|
expect(subject.body).toEqual({
|
||||||
|
id: orderEditItemChangeId,
|
||||||
|
object: "item_change",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -23,7 +23,7 @@ describe("DELETE /admin/order-edits/:id", () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("calls orderService retrieve", () => {
|
it("calls orderEditService delete", () => {
|
||||||
expect(orderEditServiceMock.delete).toHaveBeenCalledTimes(1)
|
expect(orderEditServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||||
expect(orderEditServiceMock.delete).toHaveBeenCalledWith(orderEditId)
|
expect(orderEditServiceMock.delete).toHaveBeenCalledWith(orderEditId)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import { OrderEditService } from "../../../../services"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @oas [delete] /order-edits/{id}/changes/{change_id}
|
||||||
|
* operationId: "DeleteOrderEditsOrderEditItemChange"
|
||||||
|
* summary: "Delete an Order Edit Item Change"
|
||||||
|
* description: "Deletes an Order Edit Item Change"
|
||||||
|
* x-authenticated: true
|
||||||
|
* parameters:
|
||||||
|
* - (path) id=* {string} The ID of the Order Edit to delete.
|
||||||
|
* - (path) change_id=* {string} The ID of the Order Edit Item Change to delete.
|
||||||
|
* 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.deleteItemChange(item_change_id, order_edit_id)
|
||||||
|
* .then(({ id, object, deleted }) => {
|
||||||
|
* console.log(id);
|
||||||
|
* });
|
||||||
|
* - lang: Shell
|
||||||
|
* label: cURL
|
||||||
|
* source: |
|
||||||
|
* curl --location --request DELETE 'https://medusa-url.com/admin/order-edits/{id}/changes/{change_id}' \
|
||||||
|
* --header 'Authorization: Bearer {api_token}'
|
||||||
|
* security:
|
||||||
|
* - api_token: []
|
||||||
|
* - cookie_auth: []
|
||||||
|
* tags:
|
||||||
|
* - OrderEdit
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: OK
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* type: string
|
||||||
|
* description: The ID of the deleted Order Edit Item Change.
|
||||||
|
* object:
|
||||||
|
* type: string
|
||||||
|
* description: The type of the object that was deleted.
|
||||||
|
* format: item_change
|
||||||
|
* deleted:
|
||||||
|
* type: boolean
|
||||||
|
* description: Whether or not the Order Edit Item Change was deleted.
|
||||||
|
* default: true
|
||||||
|
* "400":
|
||||||
|
* $ref: "#/components/responses/400_error"
|
||||||
|
*/
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id, change_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)
|
||||||
|
.deleteItemChange(id, change_id)
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).send({
|
||||||
|
id: change_id,
|
||||||
|
object: "item_change",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -48,6 +48,11 @@ export default (app) => {
|
|||||||
|
|
||||||
route.delete("/:id", middlewares.wrap(require("./delete-order-edit").default))
|
route.delete("/:id", middlewares.wrap(require("./delete-order-edit").default))
|
||||||
|
|
||||||
|
route.delete(
|
||||||
|
"/:id/changes/:change_id",
|
||||||
|
middlewares.wrap(require("./delete-order-edit-item-change").default)
|
||||||
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +60,11 @@ export type AdminOrderEditsRes = {
|
|||||||
order_edit: OrderEdit
|
order_edit: OrderEdit
|
||||||
}
|
}
|
||||||
export type AdminOrderEditDeleteRes = DeleteResponse
|
export type AdminOrderEditDeleteRes = DeleteResponse
|
||||||
|
export type AdminOrderEditItemChangeDeleteRes = {
|
||||||
|
id: string
|
||||||
|
object: "item_change"
|
||||||
|
deleted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export * from "./update-order-edit"
|
export * from "./update-order-edit"
|
||||||
|
|
||||||
export * from "./create-order-edit"
|
export * from "./create-order-edit"
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export const LineItemServiceMock = {
|
|||||||
metadata,
|
metadata,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
delete: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||||
}
|
}
|
||||||
|
|
||||||
const mock = jest.fn().mockImplementation(() => {
|
const mock = jest.fn().mockImplementation(() => {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export const orderEditItemChangeServiceMock = {
|
||||||
|
withTransaction: function () {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
retrieveItemChangeByOrderEdit: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((itemChangeId, orderEditId) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: itemChangeId,
|
||||||
|
order_edit_id: orderEditId,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
delete: jest.fn().mockImplementation(() => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mock = jest.fn().mockImplementation(() => {
|
||||||
|
return orderEditItemChangeServiceMock
|
||||||
|
})
|
||||||
|
|
||||||
|
export default mock
|
||||||
@@ -90,6 +90,9 @@ export const orderEditServiceMock = {
|
|||||||
...withLineItems,
|
...withLineItems,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
deleteItemChange: jest.fn().mockImplementation((_) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
const mock = jest.fn().mockImplementation(() => {
|
const mock = jest.fn().mockImplementation(() => {
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||||
|
import {
|
||||||
|
EventBusService,
|
||||||
|
LineItemService,
|
||||||
|
OrderEditItemChangeService,
|
||||||
|
TaxProviderService,
|
||||||
|
} from "../index"
|
||||||
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
|
import { FindManyOptions, In } from "typeorm"
|
||||||
|
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||||
|
|
||||||
|
const taxProviderServiceMock = {
|
||||||
|
withTransaction: function () {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
clearLineItemsTaxLines: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("OrderEditItemChangeService", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderItemChangeRepository = MockRepository({
|
||||||
|
delete: jest.fn().mockImplementation(() => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
find: jest.fn().mockImplementation((conditions: FindManyOptions) => {
|
||||||
|
return Promise.resolve(
|
||||||
|
conditions.where?.id?.value?.map((id) => ({
|
||||||
|
id,
|
||||||
|
line_item_id: "li_" + id,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderEditItemChangeService = new OrderEditItemChangeService({
|
||||||
|
manager: MockManager,
|
||||||
|
orderItemChangeRepository,
|
||||||
|
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||||
|
lineItemService: LineItemServiceMock as unknown as LineItemService,
|
||||||
|
taxProviderService: taxProviderServiceMock as unknown as TaxProviderService,
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove a item change", async () => {
|
||||||
|
const itemChangeId = IdMap.getId("order-edit-item-change-1")
|
||||||
|
await orderEditItemChangeService.delete(itemChangeId)
|
||||||
|
|
||||||
|
expect(orderItemChangeRepository.delete).toHaveBeenCalledTimes(1)
|
||||||
|
expect(orderItemChangeRepository.delete).toHaveBeenCalledWith({
|
||||||
|
id: In([itemChangeId]),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(taxProviderServiceMock.clearLineItemsTaxLines).toHaveBeenCalledTimes(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
expect(taxProviderServiceMock.clearLineItemsTaxLines).toHaveBeenCalledWith([
|
||||||
|
"li_" + itemChangeId,
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
|
||||||
|
OrderEditItemChangeService.Events.DELETED,
|
||||||
|
{ ids: [itemChangeId] }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -2,6 +2,7 @@ import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
|||||||
import {
|
import {
|
||||||
EventBusService,
|
EventBusService,
|
||||||
LineItemService,
|
LineItemService,
|
||||||
|
OrderEditItemChangeService,
|
||||||
OrderEditService,
|
OrderEditService,
|
||||||
OrderService,
|
OrderService,
|
||||||
TotalsService,
|
TotalsService,
|
||||||
@@ -11,6 +12,7 @@ import { OrderServiceMock } from "../__mocks__/order"
|
|||||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
import { LineItemServiceMock } from "../__mocks__/line-item"
|
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||||
import { TotalsServiceMock } from "../__mocks__/totals"
|
import { TotalsServiceMock } from "../__mocks__/totals"
|
||||||
|
import { orderEditItemChangeServiceMock } from "../__mocks__/order-edit-item-change"
|
||||||
|
|
||||||
const orderEditToUpdate = {
|
const orderEditToUpdate = {
|
||||||
id: IdMap.getId("order-edit-to-update"),
|
id: IdMap.getId("order-edit-to-update"),
|
||||||
@@ -115,6 +117,8 @@ describe("OrderEditService", () => {
|
|||||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||||
totalsService: TotalsServiceMock as unknown as TotalsService,
|
totalsService: TotalsServiceMock as unknown as TotalsService,
|
||||||
lineItemService: lineItemServiceMock as unknown as LineItemService,
|
lineItemService: lineItemServiceMock as unknown as LineItemService,
|
||||||
|
orderEditItemChangeService:
|
||||||
|
orderEditItemChangeServiceMock as unknown as OrderEditItemChangeService,
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should retrieve an order edit and call the repository with the right arguments", async () => {
|
it("should retrieve an order edit and call the repository with the right arguments", async () => {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export { default as NotificationService } from "./notification"
|
|||||||
export { default as OauthService } from "./oauth"
|
export { default as OauthService } from "./oauth"
|
||||||
export { default as OrderService } from "./order"
|
export { default as OrderService } from "./order"
|
||||||
export { default as OrderEditService } from "./order-edit"
|
export { default as OrderEditService } from "./order-edit"
|
||||||
|
export { default as OrderEditItemChangeService } from "./order-edit-item-change"
|
||||||
export { default as PaymentProviderService } from "./payment-provider"
|
export { default as PaymentProviderService } from "./payment-provider"
|
||||||
export { default as PricingService } from "./pricing"
|
export { default as PricingService } from "./pricing"
|
||||||
export { default as ProductCollectionService } from "./product-collection"
|
export { default as ProductCollectionService } from "./product-collection"
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
|
import { OrderItemChangeRepository } from "../repositories/order-item-change"
|
||||||
|
import { EntityManager, In } from "typeorm"
|
||||||
|
import { EventBusService, LineItemService } from "./index"
|
||||||
|
import { FindConfig } from "../types/common"
|
||||||
|
import { OrderItemChange } from "../models"
|
||||||
|
import { buildQuery } from "../utils"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import TaxProviderService from "./tax-provider"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
manager: EntityManager
|
||||||
|
orderItemChangeRepository: typeof OrderItemChangeRepository
|
||||||
|
eventBusService: EventBusService
|
||||||
|
lineItemService: LineItemService
|
||||||
|
taxProviderService: TaxProviderService
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class OrderEditItemChangeService extends TransactionBaseService {
|
||||||
|
static readonly Events = {
|
||||||
|
DELETED: "order-edit-item-change.DELETED",
|
||||||
|
}
|
||||||
|
|
||||||
|
protected manager_: EntityManager
|
||||||
|
protected transactionManager_: EntityManager | undefined
|
||||||
|
|
||||||
|
protected readonly orderItemChangeRepository_: typeof OrderItemChangeRepository
|
||||||
|
protected readonly eventBus_: EventBusService
|
||||||
|
protected readonly lineItemService_: LineItemService
|
||||||
|
protected readonly taxProviderService_: TaxProviderService
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
manager,
|
||||||
|
orderItemChangeRepository,
|
||||||
|
eventBusService,
|
||||||
|
lineItemService,
|
||||||
|
taxProviderService,
|
||||||
|
}: InjectedDependencies) {
|
||||||
|
// @ts-ignore
|
||||||
|
super(arguments[0])
|
||||||
|
|
||||||
|
this.manager_ = manager
|
||||||
|
this.orderItemChangeRepository_ = orderItemChangeRepository
|
||||||
|
this.eventBus_ = eventBusService
|
||||||
|
this.lineItemService_ = lineItemService
|
||||||
|
this.taxProviderService_ = taxProviderService
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieve(
|
||||||
|
id: string,
|
||||||
|
config: FindConfig<OrderItemChange> = {}
|
||||||
|
): Promise<OrderItemChange> {
|
||||||
|
const manager = this.transactionManager_ ?? this.manager_
|
||||||
|
const orderItemChangeRepo = manager.getCustomRepository(
|
||||||
|
this.orderItemChangeRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const query = buildQuery({ id }, config)
|
||||||
|
const itemChange = await orderItemChangeRepo.findOne(query)
|
||||||
|
|
||||||
|
if (!itemChange) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`Order edit item change ${id} was not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemChange
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(itemChangeIds: string | string[]): Promise<void> {
|
||||||
|
itemChangeIds = Array.isArray(itemChangeIds)
|
||||||
|
? itemChangeIds
|
||||||
|
: [itemChangeIds]
|
||||||
|
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const orderItemChangeRepo = manager.getCustomRepository(
|
||||||
|
this.orderItemChangeRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const changes = await orderItemChangeRepo.find({
|
||||||
|
where: {
|
||||||
|
id: In(itemChangeIds as string[]),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const lineItemIdsToRemove = changes
|
||||||
|
.map((change) => {
|
||||||
|
return change.line_item_id
|
||||||
|
})
|
||||||
|
.filter(Boolean) as string[]
|
||||||
|
|
||||||
|
await orderItemChangeRepo.delete({ id: In(itemChangeIds as string[]) })
|
||||||
|
|
||||||
|
const lineItemServiceTx = this.lineItemService_.withTransaction(manager)
|
||||||
|
await Promise.all([
|
||||||
|
...lineItemIdsToRemove.map((id) => lineItemServiceTx.delete(id)),
|
||||||
|
this.taxProviderService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.clearLineItemsTaxLines(lineItemIdsToRemove),
|
||||||
|
])
|
||||||
|
|
||||||
|
await this.eventBus_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.emit(OrderEditItemChangeService.Events.DELETED, { ids: itemChangeIds })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,11 +14,11 @@ import { TransactionBaseService } from "../interfaces"
|
|||||||
import {
|
import {
|
||||||
EventBusService,
|
EventBusService,
|
||||||
LineItemService,
|
LineItemService,
|
||||||
|
OrderEditItemChangeService,
|
||||||
OrderService,
|
OrderService,
|
||||||
TotalsService,
|
TotalsService,
|
||||||
} from "./index"
|
} from "./index"
|
||||||
import { CreateOrderEditInput } from "../types/order-edit"
|
import { CreateOrderEditInput, UpdateOrderEditInput } from "../types/order-edit"
|
||||||
import { UpdateOrderEditInput } from "../types/order-edit"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
manager: EntityManager
|
manager: EntityManager
|
||||||
@@ -27,6 +27,7 @@ type InjectedDependencies = {
|
|||||||
eventBusService: EventBusService
|
eventBusService: EventBusService
|
||||||
totalsService: TotalsService
|
totalsService: TotalsService
|
||||||
lineItemService: LineItemService
|
lineItemService: LineItemService
|
||||||
|
orderEditItemChangeService: OrderEditItemChangeService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class OrderEditService extends TransactionBaseService {
|
export default class OrderEditService extends TransactionBaseService {
|
||||||
@@ -43,6 +44,7 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
protected readonly lineItemService_: LineItemService
|
protected readonly lineItemService_: LineItemService
|
||||||
protected readonly eventBusService_: EventBusService
|
protected readonly eventBusService_: EventBusService
|
||||||
protected readonly totalsService_: TotalsService
|
protected readonly totalsService_: TotalsService
|
||||||
|
protected readonly orderEditItemChangeService_: OrderEditItemChangeService
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
manager,
|
manager,
|
||||||
@@ -51,6 +53,7 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
lineItemService,
|
lineItemService,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
totalsService,
|
totalsService,
|
||||||
|
orderEditItemChangeService,
|
||||||
}: InjectedDependencies) {
|
}: InjectedDependencies) {
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
super(arguments[0])
|
super(arguments[0])
|
||||||
@@ -61,6 +64,7 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
this.lineItemService_ = lineItemService
|
this.lineItemService_ = lineItemService
|
||||||
this.eventBusService_ = eventBusService
|
this.eventBusService_ = eventBusService
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
|
this.orderEditItemChangeService_ = orderEditItemChangeService
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrieve(
|
async retrieve(
|
||||||
@@ -382,4 +386,36 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
|
|
||||||
return orderEdit
|
return orderEdit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteItemChange(
|
||||||
|
orderEditId: string,
|
||||||
|
itemChangeId: string
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const itemChange = await this.orderEditItemChangeService_.retrieve(
|
||||||
|
itemChangeId,
|
||||||
|
{ select: ["id", "order_edit_id"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
const orderEdit = await this.retrieve(orderEditId, {
|
||||||
|
select: ["id", "confirmed_at", "canceled_at"],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (orderEdit.id !== itemChange.order_edit_id) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`The item change you are trying to delete doesn't belong to the OrderEdit with id: ${orderEditId}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderEdit.confirmed_at !== null || orderEdit.canceled_at !== null) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
`Cannot delete and item change from a ${orderEdit.status} order edit`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.orderEditItemChangeService_.delete(itemChangeId)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import { AwilixContainer } from "awilix"
|
import { AwilixContainer } from "awilix"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager, In } from "typeorm"
|
||||||
import Redis from "ioredis"
|
import Redis from "ioredis"
|
||||||
|
|
||||||
import { LineItemTaxLineRepository } from "../repositories/line-item-tax-line"
|
import { LineItemTaxLineRepository } from "../repositories/line-item-tax-line"
|
||||||
import { ShippingMethodTaxLineRepository } from "../repositories/shipping-method-tax-line"
|
import { ShippingMethodTaxLineRepository } from "../repositories/shipping-method-tax-line"
|
||||||
import { TaxProviderRepository } from "../repositories/tax-provider"
|
import { TaxProviderRepository } from "../repositories/tax-provider"
|
||||||
import {
|
import {
|
||||||
LineItemTaxLine,
|
|
||||||
TaxProvider,
|
|
||||||
LineItem,
|
|
||||||
ShippingMethodTaxLine,
|
|
||||||
ShippingMethod,
|
|
||||||
Region,
|
|
||||||
Cart,
|
Cart,
|
||||||
|
LineItem,
|
||||||
|
LineItemTaxLine,
|
||||||
|
Region,
|
||||||
|
ShippingMethod,
|
||||||
|
ShippingMethodTaxLine,
|
||||||
|
TaxProvider,
|
||||||
} from "../models"
|
} from "../models"
|
||||||
import { isCart } from "../types/cart"
|
import { isCart } from "../types/cart"
|
||||||
import {
|
import {
|
||||||
@@ -91,6 +91,16 @@ class TaxProviderService extends TransactionBaseService {
|
|||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearLineItemsTaxLines(itemIds: string[]): Promise<void> {
|
||||||
|
return await this.atomicPhase_(async (transactionManager) => {
|
||||||
|
const taxLineRepo = transactionManager.getCustomRepository(
|
||||||
|
this.taxLineRepo_
|
||||||
|
)
|
||||||
|
|
||||||
|
await taxLineRepo.delete({ item_id: In(itemIds) })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async clearTaxLines(cartId: string): Promise<void> {
|
async clearTaxLines(cartId: string): Promise<void> {
|
||||||
return await this.atomicPhase_(async (transactionManager) => {
|
return await this.atomicPhase_(async (transactionManager) => {
|
||||||
const taxLineRepo = transactionManager.getCustomRepository(
|
const taxLineRepo = transactionManager.getCustomRepository(
|
||||||
|
|||||||
Reference in New Issue
Block a user