diff --git a/packages/medusa/src/models/__mocks__/cart.js b/packages/medusa/src/models/__mocks__/cart.js index 906842177e..f3a0e0bcb8 100644 --- a/packages/medusa/src/models/__mocks__/cart.js +++ b/packages/medusa/src/models/__mocks__/cart.js @@ -143,6 +143,23 @@ export const carts = { }, quantity: 10, }, + { + _id: IdMap.getId("itemToRemove"), + 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("product"), + }, + quantity: 1, + }, + quantity: 1, + }, ], shipping_address: {}, billing_address: {}, diff --git a/packages/medusa/src/services/__tests__/cart.js b/packages/medusa/src/services/__tests__/cart.js index 6bd7e5f963..671ec0b421 100644 --- a/packages/medusa/src/services/__tests__/cart.js +++ b/packages/medusa/src/services/__tests__/cart.js @@ -14,6 +14,7 @@ import { CartModelMock, carts } from "../../models/__mocks__/cart" import { LineItemServiceMock } from "../__mocks__/line-item" import { DiscountModelMock, discounts } from "../../models/__mocks__/discount" import { DiscountServiceMock } from "../__mocks__/discount" +import idMap from "medusa-test-utils/dist/id-map" describe("CartService", () => { describe("retrieve", () => { @@ -337,6 +338,75 @@ describe("CartService", () => { }) }) + describe("removeLineItem", () => { + const cartService = new CartService({ + cartModel: CartModelMock, + productVariantService: ProductVariantServiceMock, + lineItemService: LineItemServiceMock, + eventBusService: EventBusServiceMock, + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("successfully removes a line item", async () => { + await cartService.removeLineItem( + IdMap.getId("cartWithLine"), + IdMap.getId("itemToRemove") + ) + + expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1) + expect(EventBusServiceMock.emit).toHaveBeenCalledWith( + "cart.updated", + expect.any(Object) + ) + + expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1) + expect(CartModelMock.updateOne).toHaveBeenCalledWith( + { + _id: IdMap.getId("cartWithLine"), + }, + { + $pull: { items: { _id: IdMap.getId("itemToRemove") } }, + } + ) + }) + + it("successfully decrements quantity if more than 1", async () => { + await cartService.removeLineItem( + IdMap.getId("cartWithLine"), + IdMap.getId("existingLine") + ) + + expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1) + expect(EventBusServiceMock.emit).toHaveBeenCalledWith( + "cart.updated", + expect.any(Object) + ) + + expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1) + expect(CartModelMock.updateOne).toHaveBeenCalledWith( + { + _id: IdMap.getId("cartWithLine"), + "items._id": IdMap.getId("existingLine"), + }, + { + $set: { "items.$.quantity": 9 }, + } + ) + }) + + it("resolves if line item is not in cart", async () => { + await cartService.removeLineItem( + IdMap.getId("cartWithLine"), + IdMap.getId("nonExisting") + ) + + expect(CartModelMock.updateOne).toHaveBeenCalledTimes(0) + }) + }) + describe("updateLineItem", () => { const cartService = new CartService({ cartModel: CartModelMock, diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index f99ee60fc2..29d52c3d59 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -230,6 +230,62 @@ class CartService extends BaseService { return c } + /** + * Removes a line item from the cart. + * @param {string} cartId - the id of the cart that we will remove from + * @param {LineItem} lineItemId - the line item to remove. + * @retur {Promise} the result of the update operation + */ + async removeLineItem(cartId, lineItemId) { + const cart = await this.retrieve(cartId) + const itemToRemove = cart.items.find(line => line._id === lineItemId) + + if (!itemToRemove) { + return Promise.resolve() + } + + // If cart has more than one of those line items, we update the quantity + // instead of removing it + if (itemToRemove.quantity > 1) { + const newQuantity = itemToRemove.quantity - 1 + + return this.cartModel_ + .updateOne( + { + _id: cartId, + "items._id": itemToRemove._id, + }, + { + $set: { + "items.$.quantity": newQuantity, + }, + } + ) + .then(result => { + // Notify subscribers + this.eventBus_.emit(CartService.Events.UPDATED, result) + return result + }) + } + + return this.cartModel_ + .updateOne( + { + _id: cartId, + }, + { + $pull: { + items: { _id: itemToRemove._id }, + }, + } + ) + .then(result => { + // Notify subscribers + this.eventBus_.emit(CartService.Events.UPDATED, result) + return result + }) + } + /** * Adds a line item to the cart. * @param {string} cartId - the id of the cart that we will add to