Skip to content

Commit

Permalink
feat(medusa): Implement premises of the creation flow of an order edit (
Browse files Browse the repository at this point in the history
#2187)

**What**
- Implements the admin create end point 
- Service implementation of the create method and the retrieveActive as well as the totals computation
- Improve compute line items
- client
  - medusa-js api
  - medusa-react mutations hooks

**Tests**
- Unit tests of the create end points
- Unit tests of the service create method
- Integration tests for admin that also take into account totals computations
- client
  - medusa-js tests
  - medusa-react hooks tests

FIXES CORE-491
  • Loading branch information
adrien2p authored Sep 16, 2022
1 parent 6132711 commit f7177c9
Show file tree
Hide file tree
Showing 20 changed files with 742 additions and 112 deletions.
135 changes: 133 additions & 2 deletions integration-tests/api/__tests__/admin/order-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,16 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {

await simpleLineItemFactory(dbConnection, {
id: lineItemUpdateId,
order_id: orderEdit.order_id,
order_id: null,
variant_id: product1.variants[0].id,
unit_price: 1000,
quantity: 2,
})
await simpleLineItemFactory(dbConnection, {
id: lineItemCreateId,
order_id: orderEdit.order_id,
order_id: null,
variant_id: product3.variants[0].id,
unit_price: 100,
quantity: 2,
})

Expand Down Expand Up @@ -175,6 +177,13 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
removed_items: expect.arrayContaining([
expect.objectContaining({ id: lineItemId2, quantity: 1 }),
]),
shipping_total: 0,
gift_card_total: 0,
gift_card_tax_total: 0,
discount_total: 0,
tax_total: 0,
total: 2200,
subtotal: 2200,
})
)
expect(response.status).toEqual(200)
Expand Down Expand Up @@ -292,4 +301,126 @@ describe("[MEDUSA_FF_ORDER_EDITING] /admin/order-edits", () => {
})
})
})

describe("POST /admin/order-edits", () => {
let orderId
const prodId1 = IdMap.getId("prodId1")
const prodId2 = IdMap.getId("prodId2")
const lineItemId1 = IdMap.getId("line-item-1")
const lineItemId2 = IdMap.getId("line-item-2")

beforeEach(async () => {
await adminSeeder(dbConnection)

const product1 = await simpleProductFactory(dbConnection, {
id: prodId1,
})
const product2 = await simpleProductFactory(dbConnection, {
id: prodId2,
})

const order = await simpleOrderFactory(dbConnection, {
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: product1.variants[0].id,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
},
{
id: lineItemId2,
variant_id: product2.variants[0].id,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
},
],
})
orderId = order.id
})

afterEach(async () => {
const db = useDb()
return await db.teardown()
})

it("creates and order edit", async () => {
const api = useApi()

const response = await api.post(
`/admin/order-edits/`,
{
order_id: orderId,
internal_note: "This is an internal note",
},
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.order_edit).toEqual(
expect.objectContaining({
order_id: orderId,
created_by: "admin_user",
requested_by: null,
canceled_by: null,
confirmed_by: null,
internal_note: "This is an internal note",
items: expect.arrayContaining([
expect.objectContaining({
id: lineItemId1,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
}),
expect.objectContaining({
id: lineItemId2,
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
unit_price: 1000,
}),
]),
shipping_total: 0,
gift_card_total: 0,
gift_card_tax_total: 0,
discount_total: 0,
tax_total: 0,
total: 2000,
subtotal: 2000,
})
)
})

it("throw an error if an active order edit already exists", async () => {
const api = useApi()

const payload = {
order_id: orderId,
internal_note: "This is an internal note",
}

await api.post(`/admin/order-edits/`, payload, adminHeaders)
const err = await api
.post(`/admin/order-edits/`, payload, adminHeaders)
.catch((e) => e)

expect(err.message).toBe("Request failed with status code 400")
expect(err.response.data.message).toBe(
`An active order edit already exists for the order ${payload.order_id}`
)
})
})
})
13 changes: 11 additions & 2 deletions integration-tests/api/__tests__/store/order-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,16 @@ describe("[MEDUSA_FF_ORDER_EDITING] /store/order-edits", () => {

await simpleLineItemFactory(dbConnection, {
id: lineItemUpdateId,
order_id: orderEdit.order_id,
order_id: null,
variant_id: product1.variants[0].id,
unit_price: 1000,
quantity: 2,
})
await simpleLineItemFactory(dbConnection, {
id: lineItemCreateId,
order_id: orderEdit.order_id,
order_id: null,
variant_id: product3.variants[0].id,
unit_price: 100,
quantity: 2,
})

Expand Down Expand Up @@ -168,6 +170,13 @@ describe("[MEDUSA_FF_ORDER_EDITING] /store/order-edits", () => {
removed_items: expect.arrayContaining([
expect.objectContaining({ id: lineItemId2, quantity: 1 }),
]),
shipping_total: 0,
gift_card_total: 0,
gift_card_tax_total: 0,
discount_total: 0,
tax_total: 0,
total: 2200,
subtotal: 2200,
})
)

Expand Down
15 changes: 12 additions & 3 deletions packages/medusa-js/src/resources/admin/order-edits.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AdminOrdersEditsRes,
AdminOrderEditDeleteRes
AdminOrderEditDeleteRes,
AdminOrderEditsRes,
AdminPostOrderEditsReq,
} from "@medusajs/medusa"
import { ResponsePromise } from "../../typings"
import BaseResource from "../base"
Expand All @@ -9,11 +10,19 @@ class AdminOrderEditsResource extends BaseResource {
retrieve(
id: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminOrdersEditsRes> {
): ResponsePromise<AdminOrderEditsRes> {
const path = `/admin/order-edits/${id}`
return this.client.request("GET", path, undefined, {}, customHeaders)
}

create(
payload: AdminPostOrderEditsReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminOrderEditsRes> {
const path = `/admin/order-edits`
return this.client.request("POST", path, payload, {}, customHeaders)
}

delete(
id: string,
customHeaders: Record<string, any> = {}
Expand Down
12 changes: 12 additions & 0 deletions packages/medusa-react/mocks/handlers/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,18 @@ export const adminHandlers = [
)
}),

rest.post("/admin/order-edits/", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
order_edit: {
...fixtures.get("order_edit"),
...(req.body as any),
},
})
)
}),

rest.get("/store/order-edits/:id", (req, res, ctx) => {
const { id } = req.params
return res(
Expand Down
20 changes: 19 additions & 1 deletion packages/medusa-react/src/hooks/admin/order-edits/mutations.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import {
AdminOrderEditDeleteRes,
AdminOrderEditsRes,
AdminPostOrderEditsReq,
} from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
import { adminOrderEditsKeys } from "."
import { useMedusa } from "../../../contexts/medusa"
import { buildOptions } from "../../utils/buildOptions"
import { useMedusa } from "../../../contexts"

export const useAdminCreateOrderEdit = (
options?: UseMutationOptions<
Response<AdminOrderEditsRes>,
Error,
AdminPostOrderEditsReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminPostOrderEditsReq) =>
client.admin.orderEdits.create(payload),
buildOptions(queryClient, adminOrderEditsKeys.lists(), options)
)
}

export const useAdminDeleteOrderEdit = (
id: string,
Expand Down
4 changes: 2 additions & 2 deletions packages/medusa-react/src/hooks/admin/order-edits/queries.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AdminOrdersEditsRes } from "@medusajs/medusa"
import { AdminOrderEditsRes } from "@medusajs/medusa"
import { queryKeysFactory } from "../../utils"
import { UseQueryOptionsWrapper } from "../../../types"
import { Response } from "@medusajs/medusa-js"
Expand All @@ -13,7 +13,7 @@ type OrderEditQueryKeys = typeof adminOrderEditsKeys
export const useAdminOrderEdit = (
id: string,
options?: UseQueryOptionsWrapper<
Response<AdminOrdersEditsRes>,
Response<AdminOrderEditsRes>,
Error,
ReturnType<OrderEditQueryKeys["detail"]>
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { useAdminDeleteOrderEdit } from "../../../../src/"
import {
useAdminCreateOrderEdit,
useAdminDeleteOrderEdit,
} from "../../../../src/"
import { renderHook } from "@testing-library/react-hooks"
import { fixtures } from "../../../../mocks/data"
import { createWrapper } from "../../../utils"
import { fixtures } from "../../../../mocks/data"

describe("useAdminDelete hook", () => {
test("Deletes an order edit", async () => {
Expand All @@ -11,7 +14,6 @@ describe("useAdminDelete hook", () => {
})

result.current.mutate()

await waitFor(() => result.current.isSuccess)

expect(result.current.data.response.status).toEqual(200)
Expand All @@ -24,3 +26,30 @@ describe("useAdminDelete hook", () => {
)
})
})

describe("useAdminCreateOrderEdit hook", () => {
test("Created an order edit", async () => {
const { result, waitFor } = renderHook(() => useAdminCreateOrderEdit(), {
wrapper: createWrapper(),
})

const payload = {
order_id: "ord_1",
internal_note: "This is an internal note",
}

result.current.mutate(payload)

await waitFor(() => result.current.isSuccess)

expect(result.current.data.response.status).toEqual(200)
expect(result.current.data).toEqual(
expect.objectContaining({
order_edit: {
...fixtures.get("order_edit"),
...payload,
},
})
)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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"

describe("POST /admin/order-edits", () => {
describe("successfully create an order edit", () => {
const orderId = IdMap.getId("order-edit-order-id-test")
const internalNote = "test internal note"
let subject

beforeAll(async () => {
subject = await request("POST", "/admin/order-edits", {
payload: {
order_id: orderId,
internal_note: internalNote,
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
flags: [OrderEditingFeatureFlag],
})
})

afterAll(async () => {
jest.clearAllMocks()
})

it("returns 200", () => {
expect(subject.status).toEqual(200)
})

it("calls order edit service create", () => {
expect(orderEditServiceMock.create).toHaveBeenCalledTimes(1)
expect(orderEditServiceMock.create).toHaveBeenCalledWith(
{
order_id: orderId,
internal_note: internalNote,
},
{
loggedInUserId: IdMap.getId("admin_user"),
}
)
})
})
})
Loading

0 comments on commit f7177c9

Please sign in to comment.