Feat/decline order edit (#2234)
**What** - Decline an order edit from a store endpoint - Refactor totals setting to a service method Fixes CORE-502
This commit is contained in:
1 parent
de85a971c6
commit
c661cc789b
17 files changed
+486
-67
No files matched your search
@@ -186,4 +186,91 @@ describe("[MEDUSA_FF_ORDER_EDITING] /store/order-edits", () => {
|
|||||||
expect(response.data.order_edit.confirmed_by).not.toBeDefined()
|
expect(response.data.order_edit.confirmed_by).not.toBeDefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("POST /store/order-edits/:id/decline", () => {
|
||||||
|
let declineableOrderEdit
|
||||||
|
let declinedOrderEdit
|
||||||
|
let confirmedOrderEdit
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
declineableOrderEdit = await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: IdMap.getId("order-edit-1"),
|
||||||
|
created_by: "admin_user",
|
||||||
|
requested_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
declinedOrderEdit = await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: IdMap.getId("order-edit-2"),
|
||||||
|
created_by: "admin_user",
|
||||||
|
declined_reason: "wrong size",
|
||||||
|
declined_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
confirmedOrderEdit = await simpleOrderEditFactory(dbConnection, {
|
||||||
|
id: IdMap.getId("order-edit-3"),
|
||||||
|
created_by: "admin_user",
|
||||||
|
confirmed_at: new Date(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
return await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("declines an order edit", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
const result = await api.post(
|
||||||
|
`/store/order-edits/${declineableOrderEdit.id}/decline`,
|
||||||
|
{
|
||||||
|
declined_reason: "wrong color",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.status).toEqual(200)
|
||||||
|
expect(result.data.order_edit).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
status: "declined",
|
||||||
|
declined_reason: "wrong color",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails to decline an already declined order edit", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
const result = await api.post(
|
||||||
|
`/store/order-edits/${declinedOrderEdit.id}/decline`,
|
||||||
|
{
|
||||||
|
declined_reason: "wrong color",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.status).toEqual(200)
|
||||||
|
expect(result.data.order_edit).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: declinedOrderEdit.id,
|
||||||
|
status: "declined",
|
||||||
|
declined_reason: "wrong size",
|
||||||
|
declined_at: expect.any(String),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails to decline an already confirmed order edit", async () => {
|
||||||
|
expect.assertions(2)
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
await api
|
||||||
|
.post(`/store/order-edits/${confirmedOrderEdit.id}/decline`, {
|
||||||
|
declined_reason: "wrong color",
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
expect(err.response.status).toEqual(400)
|
||||||
|
expect(err.response.data.message).toEqual(
|
||||||
|
`Cannot decline an order edit with status confirmed.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { StoreOrderEditsRes } from "@medusajs/medusa"
|
import { StoreOrderEditsRes, StorePostOrderEditsOrderEditDecline } from "@medusajs/medusa"
|
||||||
import { ResponsePromise } from "../typings"
|
import { ResponsePromise } from "../typings"
|
||||||
import BaseResource from "./base"
|
import BaseResource from "./base"
|
||||||
|
|
||||||
@@ -10,6 +10,15 @@ class OrderEditsResource extends BaseResource {
|
|||||||
const path = `/store/order-edits/${id}`
|
const path = `/store/order-edits/${id}`
|
||||||
return this.client.request("GET", path, undefined, {}, customHeaders)
|
return this.client.request("GET", path, undefined, {}, customHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decline(
|
||||||
|
id: string,
|
||||||
|
payload: StorePostOrderEditsOrderEditDecline,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
) {
|
||||||
|
const path = `/store/order-edits/${id}/decline`
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OrderEditsResource
|
export default OrderEditsResource
|
||||||
@@ -69,6 +69,15 @@ export const storeHandlers = [
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
rest.post("/store/order-edits/:id/decline", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
order_edit: {...fixtures.get("order_edit"), declined_reason: req.body.declined_reason, status: 'declined'},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
rest.get("/store/orders/:id", (req, res, ctx) => {
|
rest.get("/store/orders/:id", (req, res, ctx) => {
|
||||||
return res(
|
return res(
|
||||||
ctx.status(200),
|
ctx.status(200),
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export * from "./queries"
|
export * from "./queries"
|
||||||
|
export * from './mutations'
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
|
||||||
|
import {
|
||||||
|
StorePostOrderEditsOrderEditDecline,
|
||||||
|
StoreOrderEditsRes
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
|
||||||
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { orderEditQueryKeys } from "."
|
||||||
|
|
||||||
|
export const useDeclineOrderEdit = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<StoreOrderEditsRes>,
|
||||||
|
Error,
|
||||||
|
StorePostOrderEditsOrderEditDecline
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: StorePostOrderEditsOrderEditDecline) =>
|
||||||
|
client.orderEdits.decline(id, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[orderEditQueryKeys.lists(), orderEditQueryKeys.detail(id)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { useDeclineOrderEdit } from "../../../../src/"
|
||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useCreateLineItem hook", () => {
|
||||||
|
test("creates a line item", async () => {
|
||||||
|
const declineBody = {
|
||||||
|
declined_reason: "Wrong color",
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useDeclineOrderEdit("test-cart"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(declineBody)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.order_edit).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
status: "declined",
|
||||||
|
...declineBody,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -33,7 +33,7 @@ describe("GET /admin/order-edits/:id", () => {
|
|||||||
select: defaultOrderEditFields,
|
select: defaultOrderEditFields,
|
||||||
relations: defaultOrderEditRelations,
|
relations: defaultOrderEditRelations,
|
||||||
})
|
})
|
||||||
expect(orderEditServiceMock.computeLineItems).toHaveBeenCalledTimes(1)
|
expect(orderEditServiceMock.decorateLineItemsAndTotals).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns order", () => {
|
it("returns order", () => {
|
||||||
|
|||||||
@@ -67,20 +67,7 @@ export default async (req: Request, res: Response) => {
|
|||||||
orderEditService.withTransaction(transactionManager)
|
orderEditService.withTransaction(transactionManager)
|
||||||
const orderEdit = await orderEditServiceTx.create(data, { loggedInUserId })
|
const orderEdit = await orderEditServiceTx.create(data, { loggedInUserId })
|
||||||
|
|
||||||
const { items } = await orderEditServiceTx.computeLineItems(orderEdit.id)
|
return await orderEditServiceTx.decorateLineItemsAndTotals(orderEdit)
|
||||||
orderEdit.items = items
|
|
||||||
orderEdit.removed_items = []
|
|
||||||
|
|
||||||
const totals = await orderEditServiceTx.getTotals(orderEdit.id)
|
|
||||||
orderEdit.discount_total = totals.discount_total
|
|
||||||
orderEdit.gift_card_total = totals.gift_card_total
|
|
||||||
orderEdit.gift_card_tax_total = totals.gift_card_tax_total
|
|
||||||
orderEdit.shipping_total = totals.shipping_total
|
|
||||||
orderEdit.subtotal = totals.subtotal
|
|
||||||
orderEdit.tax_total = totals.tax_total
|
|
||||||
orderEdit.total = totals.total
|
|
||||||
|
|
||||||
return orderEdit
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.json({ order_edit: orderEdit })
|
return res.json({ order_edit: orderEdit })
|
||||||
|
|||||||
@@ -59,20 +59,9 @@ export default async (req: Request, res: Response) => {
|
|||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const retrieveConfig = req.retrieveConfig
|
const retrieveConfig = req.retrieveConfig
|
||||||
|
|
||||||
const orderEdit = await orderEditService.retrieve(id, retrieveConfig)
|
let orderEdit = await orderEditService.retrieve(id, retrieveConfig)
|
||||||
|
|
||||||
const { items, removedItems } = await orderEditService.computeLineItems(id)
|
orderEdit = await orderEditService.decorateLineItemsAndTotals(orderEdit)
|
||||||
orderEdit.items = items
|
|
||||||
orderEdit.removed_items = removedItems
|
|
||||||
|
|
||||||
const totals = await orderEditService.getTotals(orderEdit.id)
|
|
||||||
orderEdit.discount_total = totals.discount_total
|
|
||||||
orderEdit.gift_card_total = totals.gift_card_total
|
|
||||||
orderEdit.gift_card_tax_total = totals.gift_card_tax_total
|
|
||||||
orderEdit.shipping_total = totals.shipping_total
|
|
||||||
orderEdit.subtotal = totals.subtotal
|
|
||||||
orderEdit.tax_total = totals.tax_total
|
|
||||||
orderEdit.total = totals.total
|
|
||||||
|
|
||||||
return res.json({ order_edit: orderEdit })
|
return res.json({ order_edit: orderEdit })
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
import { orderEditServiceMock } from "../../../../../services/__mocks__/order-edit"
|
||||||
|
import OrderEditingFeatureFlag from "../../../../../loaders/feature-flags/order-editing"
|
||||||
|
import {
|
||||||
|
defaultOrderEditFields,
|
||||||
|
defaultOrderEditRelations,
|
||||||
|
} from "../../../../../types/order-edit"
|
||||||
|
import { storeOrderEditNotAllowedFields } from "../index"
|
||||||
|
|
||||||
|
describe("GET /store/order-edits/:id", () => {
|
||||||
|
describe("successfully gets an order edit", () => {
|
||||||
|
const orderEditId = IdMap.getId("testDeclineOrderEdit")
|
||||||
|
let subject
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
declined_reason: "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request("POST", `/store/order-edits/${orderEditId}/decline`, {
|
||||||
|
payload,
|
||||||
|
flags: [OrderEditingFeatureFlag],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls orderService decline", () => {
|
||||||
|
expect(orderEditServiceMock.decline).toHaveBeenCalledTimes(1)
|
||||||
|
expect(orderEditServiceMock.decline).toHaveBeenCalledWith(orderEditId, { declinedReason: "test", loggedInUser: undefined})
|
||||||
|
expect(orderEditServiceMock.decorateLineItemsAndTotals).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns orderEdit", () => {
|
||||||
|
expect(subject.body.order_edit.id).toEqual(orderEditId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -33,7 +33,7 @@ describe("GET /store/order-edits/:id", () => {
|
|||||||
(field) => !storeOrderEditNotAllowedFields.includes(field)
|
(field) => !storeOrderEditNotAllowedFields.includes(field)
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
expect(orderEditServiceMock.computeLineItems).toHaveBeenCalledTimes(1)
|
expect(orderEditServiceMock.decorateLineItemsAndTotals).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns order", () => {
|
it("returns order", () => {
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { IsOptional, IsString } from "class-validator"
|
||||||
|
import { Request, Response } from "express"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import { OrderEditService } from "../../../../services"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @oas [post] /order-edits/{id}/decline
|
||||||
|
* operationId: "PostOrderEditsOrderEditDecline"
|
||||||
|
* summary: "Decline an OrderEdit"
|
||||||
|
* description: "Declines an OrderEdit."
|
||||||
|
* parameters:
|
||||||
|
* - (path) id=* {string} The ID of the OrderEdit.
|
||||||
|
* requestBody:
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* declined_reason:
|
||||||
|
* type: string
|
||||||
|
* description: The reason for declining the OrderEdit.
|
||||||
|
* x-codeSamples:
|
||||||
|
* - lang: JavaScript
|
||||||
|
* label: JS Client
|
||||||
|
* source: |
|
||||||
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
|
* medusa.orderEdit.decline(orderEditId)
|
||||||
|
* .then(({ order_edit }) => {
|
||||||
|
* console.log(order_edit.id);
|
||||||
|
* })
|
||||||
|
* - lang: Shell
|
||||||
|
* label: cURL
|
||||||
|
* source: |
|
||||||
|
* curl --location --request POST 'https://medusa-url.com/store/order-edits/{id}/decline'
|
||||||
|
* 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"
|
||||||
|
* "500":
|
||||||
|
* $ref: "#/components/responses/500_error"
|
||||||
|
*/
|
||||||
|
export default async (req: Request, res: Response) => {
|
||||||
|
const { id } = req.params
|
||||||
|
const { validatedBody } = req as {
|
||||||
|
validatedBody: StorePostOrderEditsOrderEditDecline
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderEditService: OrderEditService =
|
||||||
|
req.scope.resolve("orderEditService")
|
||||||
|
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
const userId = req.user?.customer_id ?? req.user?.id ?? req.user?.userId
|
||||||
|
|
||||||
|
await manager.transaction(async (manager) => {
|
||||||
|
await orderEditService.withTransaction(manager).decline(id, {
|
||||||
|
declinedReason: validatedBody.declined_reason,
|
||||||
|
loggedInUser: userId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let orderEdit = await orderEditService.retrieve(id)
|
||||||
|
|
||||||
|
orderEdit = await orderEditService.decorateLineItemsAndTotals(orderEdit)
|
||||||
|
|
||||||
|
res.status(200).json({ order_edit: orderEdit })
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StorePostOrderEditsOrderEditDecline {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
declined_reason?: string
|
||||||
|
}
|
||||||
@@ -53,20 +53,9 @@ export default async (req: Request, res: Response) => {
|
|||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const retrieveConfig = req.retrieveConfig
|
const retrieveConfig = req.retrieveConfig
|
||||||
|
|
||||||
const orderEdit = await orderEditService.retrieve(id, retrieveConfig)
|
let orderEdit = await orderEditService.retrieve(id, retrieveConfig)
|
||||||
|
|
||||||
const { items, removedItems } = await orderEditService.computeLineItems(id)
|
orderEdit = await orderEditService.decorateLineItemsAndTotals(orderEdit)
|
||||||
orderEdit.items = items
|
|
||||||
orderEdit.removed_items = removedItems
|
|
||||||
|
|
||||||
const totals = await orderEditService.getTotals(orderEdit.id)
|
|
||||||
orderEdit.discount_total = totals.discount_total
|
|
||||||
orderEdit.gift_card_total = totals.gift_card_total
|
|
||||||
orderEdit.gift_card_tax_total = totals.gift_card_tax_total
|
|
||||||
orderEdit.shipping_total = totals.shipping_total
|
|
||||||
orderEdit.subtotal = totals.subtotal
|
|
||||||
orderEdit.tax_total = totals.tax_total
|
|
||||||
orderEdit.total = totals.total
|
|
||||||
|
|
||||||
return res.json({ order_edit: orderEdit })
|
return res.json({ order_edit: orderEdit })
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import middlewares, { transformQuery } from "../../../middlewares"
|
import middlewares, {
|
||||||
|
transformBody,
|
||||||
|
transformQuery,
|
||||||
|
} from "../../../middlewares"
|
||||||
import { EmptyQueryParams } from "../../../../types/common"
|
import { EmptyQueryParams } from "../../../../types/common"
|
||||||
import { isFeatureFlagEnabled } from "../../../middlewares/feature-flag-enabled"
|
import { isFeatureFlagEnabled } from "../../../middlewares/feature-flag-enabled"
|
||||||
import OrderEditingFeatureFlag from "../../../../loaders/feature-flags/order-editing"
|
import OrderEditingFeatureFlag from "../../../../loaders/feature-flags/order-editing"
|
||||||
@@ -8,6 +11,7 @@ import {
|
|||||||
defaultOrderEditRelations,
|
defaultOrderEditRelations,
|
||||||
} from "../../../../types/order-edit"
|
} from "../../../../types/order-edit"
|
||||||
import { OrderEdit } from "../../../../models"
|
import { OrderEdit } from "../../../../models"
|
||||||
|
import { StorePostOrderEditsOrderEditDecline } from "./decline-order-edit"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -33,6 +37,12 @@ export default (app) => {
|
|||||||
middlewares.wrap(require("./get-order-edit").default)
|
middlewares.wrap(require("./get-order-edit").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
route.post(
|
||||||
|
"/:id/decline",
|
||||||
|
transformBody(StorePostOrderEditsOrderEditDecline),
|
||||||
|
middlewares.wrap(require("./decline-order-edit").default)
|
||||||
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +53,8 @@ export type StoreOrderEditsRes = {
|
|||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from "./decline-order-edit"
|
||||||
|
|
||||||
export const storeOrderEditNotAllowedFields = [
|
export const storeOrderEditNotAllowedFields = [
|
||||||
"internal_note",
|
"internal_note",
|
||||||
"created_by",
|
"created_by",
|
||||||
|
|||||||
@@ -19,6 +19,29 @@ export const orderEdits = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const computeLineItems = (orderEdit) => ({
|
||||||
|
...orderEdit,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: IdMap.getId("existingLine"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
content: {
|
||||||
|
unit_price: 123,
|
||||||
|
variant: {
|
||||||
|
id: IdMap.getId("can-cover"),
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
id: IdMap.getId("validId"),
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
quantity: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removedItems: [],
|
||||||
|
})
|
||||||
export const orderEditServiceMock = {
|
export const orderEditServiceMock = {
|
||||||
withTransaction: function () {
|
withTransaction: function () {
|
||||||
return this
|
return this
|
||||||
@@ -27,31 +50,18 @@ export const orderEditServiceMock = {
|
|||||||
if (orderId === IdMap.getId("testCreatedOrder")) {
|
if (orderId === IdMap.getId("testCreatedOrder")) {
|
||||||
return Promise.resolve(orderEdits.testCreatedOrder)
|
return Promise.resolve(orderEdits.testCreatedOrder)
|
||||||
}
|
}
|
||||||
|
if (orderId === IdMap.getId("testDeclineOrderEdit")) {
|
||||||
|
return Promise.resolve({
|
||||||
|
...orderEdits.testCreatedOrder,
|
||||||
|
id: IdMap.getId("testDeclineOrderEdit"),
|
||||||
|
declined_reason: "Wrong size",
|
||||||
|
declined_at: new Date(),
|
||||||
|
})
|
||||||
|
}
|
||||||
return Promise.resolve(undefined)
|
return Promise.resolve(undefined)
|
||||||
}),
|
}),
|
||||||
computeLineItems: jest.fn().mockImplementation((orderEdit) => {
|
computeLineItems: jest.fn().mockImplementation((orderEdit) => {
|
||||||
return Promise.resolve({
|
return Promise.resolve(computeLineItems(orderEdit))
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: IdMap.getId("existingLine"),
|
|
||||||
title: "merge line",
|
|
||||||
description: "This is a new line",
|
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
|
||||||
content: {
|
|
||||||
unit_price: 123,
|
|
||||||
variant: {
|
|
||||||
id: IdMap.getId("can-cover"),
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
id: IdMap.getId("validId"),
|
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
quantity: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removedItems: [],
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
create: jest.fn().mockImplementation((data, context) => {
|
create: jest.fn().mockImplementation((data, context) => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -60,12 +70,26 @@ export const orderEditServiceMock = {
|
|||||||
created_by: context.loggedInUserId,
|
created_by: context.loggedInUserId,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
decline: jest.fn().mockImplementation((id, reason, userId) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
id,
|
||||||
|
declined_reason: reason,
|
||||||
|
declined_by: userId,
|
||||||
|
declined_at: new Date(),
|
||||||
|
})
|
||||||
|
}),
|
||||||
getTotals: jest.fn().mockImplementation(() => {
|
getTotals: jest.fn().mockImplementation(() => {
|
||||||
return Promise.resolve({})
|
return Promise.resolve({})
|
||||||
}),
|
}),
|
||||||
delete: jest.fn().mockImplementation((_) => {
|
delete: jest.fn().mockImplementation((_) => {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}),
|
}),
|
||||||
|
decorateLineItemsAndTotals: jest.fn().mockImplementation((orderEdit) => {
|
||||||
|
const withLineItems = computeLineItems(orderEdit)
|
||||||
|
return Promise.resolve({
|
||||||
|
...withLineItems,
|
||||||
|
})
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
const mock = jest.fn().mockImplementation(() => {
|
const mock = jest.fn().mockImplementation(() => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
OrderService,
|
OrderService,
|
||||||
TotalsService,
|
TotalsService,
|
||||||
} from "../index"
|
} from "../index"
|
||||||
import { OrderEditItemChangeType } from "../../models"
|
import { OrderEditItemChangeType, OrderEditStatus } from "../../models"
|
||||||
import { OrderServiceMock } from "../__mocks__/order"
|
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"
|
||||||
@@ -18,6 +18,7 @@ const orderEditToUpdate = {
|
|||||||
|
|
||||||
const orderEditWithChanges = {
|
const orderEditWithChanges = {
|
||||||
id: IdMap.getId("order-edit-with-changes"),
|
id: IdMap.getId("order-edit-with-changes"),
|
||||||
|
status: OrderEditStatus.REQUESTED,
|
||||||
order: {
|
order: {
|
||||||
id: IdMap.getId("order-edit-with-changes-order"),
|
id: IdMap.getId("order-edit-with-changes-order"),
|
||||||
items: [
|
items: [
|
||||||
@@ -90,6 +91,12 @@ describe("OrderEditService", () => {
|
|||||||
if (query?.where?.id === IdMap.getId("order-edit-with-changes")) {
|
if (query?.where?.id === IdMap.getId("order-edit-with-changes")) {
|
||||||
return orderEditWithChanges
|
return orderEditWithChanges
|
||||||
}
|
}
|
||||||
|
if (query?.where?.id === IdMap.getId("confirmed-order-edit")) {
|
||||||
|
return { ...orderEditWithChanges, status: OrderEditStatus.CONFIRMED }
|
||||||
|
}
|
||||||
|
if (query?.where?.id === IdMap.getId("declined-order-edit")) {
|
||||||
|
return { ...orderEditWithChanges, declined_reason: 'wrong size', status: OrderEditStatus.DECLINED }
|
||||||
|
}
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
@@ -179,4 +186,50 @@ describe("OrderEditService", () => {
|
|||||||
{ id: expect.any(String) }
|
{ id: expect.any(String) }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("decline", () => {
|
||||||
|
it("declines an order edit", async () => {
|
||||||
|
const result = await orderEditService.decline(
|
||||||
|
IdMap.getId("order-edit-with-changes"),
|
||||||
|
{
|
||||||
|
declinedReason: "I requested a different color for the new product",
|
||||||
|
loggedInUser: "admin_user",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: IdMap.getId("order-edit-with-changes"),
|
||||||
|
declined_at: expect.any(Date),
|
||||||
|
declined_reason: "I requested a different color for the new product",
|
||||||
|
declined_by: "admin_user",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it("fails to decline a confirmed order edit", async () => {
|
||||||
|
await expect(
|
||||||
|
orderEditService.decline(IdMap.getId("confirmed-order-edit"), {
|
||||||
|
declinedReason: "I requested a different color for the new product",
|
||||||
|
loggedInUser: "admin_user",
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(
|
||||||
|
"Cannot decline an order edit with status confirmed."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it("fails to decline an already declined order edit", async () => {
|
||||||
|
const result = await orderEditService.decline(IdMap.getId("declined-order-edit"), {
|
||||||
|
declinedReason: "I requested a different color for the new product",
|
||||||
|
loggedInUser: "admin_user",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: IdMap.getId("order-edit-with-changes"),
|
||||||
|
declined_at: expect.any(Date),
|
||||||
|
declined_reason: "wrong size",
|
||||||
|
declined_by: "admin_user",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -33,6 +33,7 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
static readonly Events = {
|
static readonly Events = {
|
||||||
CREATED: "order-edit.created",
|
CREATED: "order-edit.created",
|
||||||
UPDATED: "order-edit.updated",
|
UPDATED: "order-edit.updated",
|
||||||
|
DECLINED: "order-edit.declined",
|
||||||
}
|
}
|
||||||
|
|
||||||
protected transactionManager_: EntityManager | undefined
|
protected transactionManager_: EntityManager | undefined
|
||||||
@@ -312,4 +313,73 @@ export default class OrderEditService extends TransactionBaseService {
|
|||||||
await orderEditRepo.remove(edit)
|
await orderEditRepo.remove(edit)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async decline(
|
||||||
|
orderEditId: string,
|
||||||
|
context: {
|
||||||
|
declinedReason?: string
|
||||||
|
loggedInUser?: string
|
||||||
|
}
|
||||||
|
): Promise<OrderEdit> {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const orderEditRepo = manager.getCustomRepository(
|
||||||
|
this.orderEditRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const { loggedInUser, declinedReason } = context
|
||||||
|
|
||||||
|
const orderEdit = await this.retrieve(orderEditId)
|
||||||
|
|
||||||
|
if (orderEdit.status === OrderEditStatus.DECLINED) {
|
||||||
|
return orderEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderEdit.status !== OrderEditStatus.REQUESTED) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
`Cannot decline an order edit with status ${orderEdit.status}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
orderEdit.declined_at = new Date()
|
||||||
|
orderEdit.declined_by = loggedInUser
|
||||||
|
orderEdit.declined_reason = declinedReason
|
||||||
|
|
||||||
|
const result = await orderEditRepo.save(orderEdit)
|
||||||
|
|
||||||
|
await this.eventBusService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.emit(OrderEditService.Events.DECLINED, {
|
||||||
|
id: result.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async decorateLineItemsAndTotals(orderEdit: OrderEdit): Promise<OrderEdit> {
|
||||||
|
const lineItemDecoratedOrderEdit = await this.decorateLineItems(orderEdit)
|
||||||
|
return await this.decorateTotals(lineItemDecoratedOrderEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
async decorateLineItems(orderEdit: OrderEdit): Promise<OrderEdit> {
|
||||||
|
const { items, removedItems } = await this.computeLineItems(orderEdit.id)
|
||||||
|
orderEdit.items = items
|
||||||
|
orderEdit.removed_items = removedItems
|
||||||
|
|
||||||
|
return orderEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
async decorateTotals(orderEdit: OrderEdit): Promise<OrderEdit> {
|
||||||
|
const totals = await this.getTotals(orderEdit.id)
|
||||||
|
orderEdit.discount_total = totals.discount_total
|
||||||
|
orderEdit.gift_card_total = totals.gift_card_total
|
||||||
|
orderEdit.gift_card_tax_total = totals.gift_card_tax_total
|
||||||
|
orderEdit.shipping_total = totals.shipping_total
|
||||||
|
orderEdit.subtotal = totals.subtotal
|
||||||
|
orderEdit.tax_total = totals.tax_total
|
||||||
|
orderEdit.total = totals.total
|
||||||
|
|
||||||
|
return orderEdit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user