Skip to content

Commit

Permalink
feat(medusa): Respond with order when cart is already completed (#5766)
Browse files Browse the repository at this point in the history
  • Loading branch information
olivermrbl authored Dec 1, 2023
1 parent c808317 commit 428331a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-moles-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": minor
---

feat(medusa): Respond with order when cart is already completed
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ Object {
}
`;

exports[`/store/carts POST /store/carts/:id returns early, if cart is already completed 1`] = `
Object {
"code": "cart_incompatible_state",
"message": "Cart has already been completed",
"type": "not_allowed",
}
`;

exports[`/store/carts shipping address + region updates updates region only - single to multiple countries 1`] = `
Object {
"address_1": null,
Expand Down
59 changes: 44 additions & 15 deletions integration-tests/api/__tests__/store/cart/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2234,24 +2234,27 @@ describe("/store/carts", () => {
expect(createdOrder.status).toEqual(200)
})

it("returns early, if cart is already completed", async () => {
const manager = dbConnection.manager
it("should return early, if cart is already completed", async () => {
const api = useApi()
await manager.query(
`UPDATE "cart"
SET completed_at=current_timestamp
WHERE id = 'test-cart-2'`

const completedCart = await api.post(
`/store/carts/test-cart-2/complete-cart`
)
try {
await api.post(`/store/carts/test-cart-2/complete-cart`)
} catch (error) {
expect(error.response.data).toMatchSnapshot({
type: "not_allowed",
message: "Cart has already been completed",
code: "cart_incompatible_state",

expect(completedCart.status).toEqual(200)

const alreadyCompletedCart = await api.post(
`/store/carts/test-cart-2/complete-cart`
)

expect(alreadyCompletedCart.data.data).toEqual(
expect.objectContaining({
cart_id: "test-cart-2",
id: expect.any(String),
})
expect(error.response.status).toEqual(409)
}
)
expect(alreadyCompletedCart.data.type).toEqual("order")
expect(alreadyCompletedCart.status).toEqual(200)
})

it("fails to complete cart with items inventory not/partially covered", async () => {
Expand Down Expand Up @@ -2354,6 +2357,32 @@ describe("/store/carts", () => {
expect(res.data.cart.completed_at).not.toBe(null)
})

it("should return the swap when cart is already completed", async () => {
const manager = dbConnection.manager
await manager.query(
"UPDATE swap SET cart_id='swap-cart' where id='test-swap'"
)

await manager.query("DELETE FROM payment where swap_id='test-swap'")

const api = useApi()

await api.post(`/store/carts/swap-cart/complete-cart`)

const alreadyCompletedCart = await api.post(
`/store/carts/swap-cart/complete-cart`
)

expect(alreadyCompletedCart.data.data).toEqual(
expect.objectContaining({
cart_id: "swap-cart",
id: expect.any(String),
})
)
expect(alreadyCompletedCart.data.type).toEqual("swap")
expect(alreadyCompletedCart.status).toEqual(200)
})

it("completes cart with a non-customer and for a customer with the same email created later the order doesn't show up", async () => {
const api = useApi()
const customerEmail = "[email protected]"
Expand Down
19 changes: 9 additions & 10 deletions integration-tests/api/__tests__/store/orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ describe("/store/carts", () => {
await db.teardown()
})

it("should fails on cart already completed", async () => {
it("should return an order on cart already completed", async () => {
const api = useApi()
const manager = dbConnection.manager

Expand Down Expand Up @@ -501,17 +501,16 @@ describe("/store/carts", () => {
})
)

const responseFail = await api
.post(`/store/carts/${cartId}/complete`)
.catch((err) => {
return err.response
})
const successRes = await api.post(`/store/carts/${cartId}/complete`)

expect(responseFail.status).toEqual(409)
expect(responseFail.data.code).toEqual("cart_incompatible_state")
expect(responseFail.data.message).toEqual(
"Cart has already been completed"
expect(successRes.status).toEqual(200)
expect(successRes.data.data).toEqual(
expect.objectContaining({
cart_id: cartId,
id: expect.any(String),
})
)
expect(successRes.data.type).toEqual("order")
})
})

Expand Down
18 changes: 11 additions & 7 deletions packages/medusa/src/strategies/__tests__/cart-completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const toTest = [
},
],
[
"returns 409",
"succeeds",
{
cart: {
id: "test-cart",
Expand All @@ -104,12 +104,15 @@ const toTest = [
idempotency_key: "ikey",
recovery_point: "started",
},
validate: function (value) {
expect(value.response_code).toEqual(409)
expect(value.response_body).toEqual({
code: "cart_incompatible_state",
message: "Cart has already been completed",
type: "not_allowed",
validate: function (value, { orderServiceMock }) {
expect(value.response_code).toEqual(200)
expect(
orderServiceMock.retrieveByCartIdWithTotals
).toHaveBeenCalledTimes(1)
expect(
orderServiceMock.retrieveByCartIdWithTotals
).toHaveBeenCalledWith("test-cart", {
relations: ["shipping_address", "items", "payments"],
})
},
},
Expand Down Expand Up @@ -204,6 +207,7 @@ describe("CartCompletionStrategy", () => {
createFromCart: jest.fn(() => Promise.resolve(cart)),
retrieve: jest.fn(() => Promise.resolve({})),
retrieveWithTotals: jest.fn(() => Promise.resolve({})),
retrieveByCartIdWithTotals: jest.fn(() => Promise.resolve({})),
newTotalsService: newTotalsServiceMock,
}
const swapServiceMock = {
Expand Down
54 changes: 35 additions & 19 deletions packages/medusa/src/strategies/cart-completion.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import {
AbstractCartCompletionStrategy,
CartCompletionResponse,
} from "../interfaces"
import {
IEventBusService,
IInventoryService,
ReservationItemDTO,
} from "@medusajs/types"
import {
AbstractCartCompletionStrategy,
CartCompletionResponse,
} from "../interfaces"
import { IdempotencyKey, Order } from "../models"
import OrderService, {
ORDER_CART_ALREADY_EXISTS_ERROR,
} from "../services/order"
import {
PaymentProviderService,
ProductVariantInventoryService,
} from "../services"
import OrderService, {
ORDER_CART_ALREADY_EXISTS_ERROR,
} from "../services/order"

import CartService from "../services/cart"
import { promiseAll } from "@medusajs/utils"
import { MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import CartService from "../services/cart"
import IdempotencyKeyService from "../services/idempotency-key"
import { MedusaError } from "medusa-core-utils"
import { RequestContext } from "../types/request"
import SwapService from "../services/swap"
import { promiseAll } from "@medusajs/utils"
import { RequestContext } from "../types/request"

type InjectedDependencies = {
productVariantInventoryService: ProductVariantInventoryService
Expand Down Expand Up @@ -201,13 +201,29 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
})

if (cart.completed_at) {
if (cart.type === "swap") {
const swapId = cart.metadata?.swap_id as string
const swapServiceTx = this.swapService_.withTransaction(manager)

const swap = await swapServiceTx.retrieve(swapId, {
relations: ["shipping_address"],
})

return {
response_code: 200,
response_body: { data: swap, type: "swap" },
}
}

const order = await this.orderService_
.withTransaction(manager)
.retrieveByCartIdWithTotals(id, {
relations: ["shipping_address", "items", "payments"],
})

return {
response_code: 409,
response_body: {
code: MedusaError.Codes.CART_INCOMPATIBLE_STATE,
message: "Cart has already been completed",
type: MedusaError.Types.NOT_ALLOWED,
},
response_code: 200,
response_body: { data: order, type: "order" },
}
}

Expand Down Expand Up @@ -449,8 +465,8 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
await this.removeReservations(reservations)

if (error && error.message === ORDER_CART_ALREADY_EXISTS_ERROR) {
order = await orderServiceTx.retrieveByCartId(id, {
relations: ["shipping_address", "payments"],
order = await orderServiceTx.retrieveByCartIdWithTotals(id, {
relations: ["shipping_address", "items", "payments"],
})

return {
Expand Down

0 comments on commit 428331a

Please sign in to comment.