From d68e81fb3df116af70738eef93e227f41b72d80b Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Mon, 12 Dec 2022 10:53:40 +0100 Subject: [PATCH] fix(medusa): Update line item adjustments upon cart discount removal (#2751) **What** When a discount is deleted from a cart, the line item adjustments must be refreshed. Now, the cart service `removeDiscount` includes updating the adjustments. **Tests** **Add a new integration test which:** - create a cart with a discount - add a line item - validate the adjustments - remove the discount - check that the adjustments are not present anymore **Update unit tests** The actual tests cases now check that the adjustments repository is called when needed FIXES CORE-890 --- .changeset/young-jobs-wink.md | 5 + .../api/__tests__/store/cart/cart.js | 177 ++++++++++++++++-- .../medusa/src/services/__tests__/cart.js | 10 + packages/medusa/src/services/cart.ts | 11 +- 4 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 .changeset/young-jobs-wink.md diff --git a/.changeset/young-jobs-wink.md b/.changeset/young-jobs-wink.md new file mode 100644 index 0000000000..11da23a086 --- /dev/null +++ b/.changeset/young-jobs-wink.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix(medusa): Should update the adjustments upon cart discount removal diff --git a/integration-tests/api/__tests__/store/cart/cart.js b/integration-tests/api/__tests__/store/cart/cart.js index 8db9528163..8227ba24f3 100644 --- a/integration-tests/api/__tests__/store/cart/cart.js +++ b/integration-tests/api/__tests__/store/cart/cart.js @@ -520,6 +520,7 @@ describe("/store/carts", () => { let discountCart let discount + beforeEach(async () => { discount = await simpleDiscountFactory(dbConnection, discountData, 100) discountCart = await simpleCartFactory( @@ -1042,6 +1043,86 @@ describe("/store/carts", () => { expect(response.status).toEqual(200) }) + it("successfully removes adjustments upon update without discounts", async () => { + const discountData = { + code: "MEDUSA185DKK", + id: "medusa-185", + rule: { + allocation: "total", + type: "fixed", + value: 185, + }, + regions: ["test-region"], + } + + const cartId = "discount-cart" + + const discount = await simpleDiscountFactory(dbConnection, discountData, 100) + const discountCart = await simpleCartFactory( + dbConnection, + { + id: cartId, + customer: "test-customer", + region: "test-region", + shipping_address: { + address_1: "next door", + first_name: "lebron", + last_name: "james", + country_code: "dk", + postal_code: "100", + }, + shipping_methods: [ + { + shipping_option: "test-option", + price: 1000, + }, + ], + }, + 100 + ) + await dbConnection.manager + .createQueryBuilder() + .relation(Cart, "discounts") + .of(discountCart) + .add(discount) + + const api = useApi() + + let response = await api + .post( + `/store/carts/${cartId}/line-items`, + { + quantity: 1, + variant_id: "test-variant-quantity", + }, + ) + + expect(response.data.cart.items.length).toEqual(1) + expect(response.data.cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: [ + expect.objectContaining({ + amount: 185, + discount_id: "medusa-185", + }), + ], + }), + ]) + ) + + response = await api + .post( + `/store/carts/${cartId}`, + { + discounts: [], + }, + ) + + expect(response.data.cart.items.length).toEqual(1) + expect(response.data.cart.items[0].adjustments).toHaveLength(0) + }) + it("successfully passes customer conditions with `not_in` operator and applies discount", async () => { const api = useApi() @@ -2035,17 +2116,23 @@ describe("/store/carts", () => { }) describe("DELETE /store/carts/:id/discounts/:code", () => { + const discountData = { + code: "MEDUSA185DKK", + id: "medusa-185", + rule: { + allocation: "total", + type: "fixed", + value: 185, + }, + regions: ["test-region"], + } + beforeEach(async () => { - try { - await cartSeeder(dbConnection) - await dbConnection.manager.query( - `INSERT INTO "cart_discounts" (cart_id, discount_id) - VALUES ('test-cart', 'free-shipping')` - ) - } catch (err) { - console.log(err) - throw err - } + await cartSeeder(dbConnection) + await dbConnection.manager.query( + `INSERT INTO "cart_discounts" (cart_id, discount_id) + VALUES ('test-cart', 'free-shipping')` + ) }) afterEach(async () => { @@ -2073,16 +2160,74 @@ describe("/store/carts", () => { expect(response.data.cart.shipping_total).toBe(1000) expect(response.status).toEqual(200) }) + + it("removes line item adjustments upon discount deletion", async () => { + const cartId = "discount-cart" + const discount = await simpleDiscountFactory(dbConnection, discountData, 100) + const discountCart = await simpleCartFactory( + dbConnection, + { + id: cartId, + customer: "test-customer", + region: "test-region", + shipping_address: { + address_1: "next door", + first_name: "lebron", + last_name: "james", + country_code: "dk", + postal_code: "100", + }, + shipping_methods: [ + { + shipping_option: "test-option", + price: 1000, + }, + ], + }, + 100 + ) + await dbConnection.manager + .createQueryBuilder() + .relation(Cart, "discounts") + .of(discountCart) + .add(discount) + + const api = useApi() + + let response = await api + .post( + `/store/carts/${cartId}/line-items`, + { + quantity: 1, + variant_id: "test-variant-quantity", + }, + ) + + expect(response.data.cart.items.length).toEqual(1) + expect(response.data.cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: [ + expect.objectContaining({ + amount: 185, + discount_id: "medusa-185", + }), + ], + }), + ]) + ) + + response = await api + .delete(`/store/carts/${cartId}/discounts/${discountData.code}`) + + expect(response.data.cart.items.length).toEqual(1) + expect(response.data.cart.items[0].adjustments).toHaveLength(0) + }) }) describe("get-cart with session customer", () => { beforeEach(async () => { - try { - await cartSeeder(dbConnection) - } catch (err) { - console.log(err) - throw err - } + await cartSeeder(dbConnection) }) afterEach(async () => { diff --git a/packages/medusa/src/services/__tests__/cart.js b/packages/medusa/src/services/__tests__/cart.js index 4463eefd88..0cb3fd25f5 100644 --- a/packages/medusa/src/services/__tests__/cart.js +++ b/packages/medusa/src/services/__tests__/cart.js @@ -2281,6 +2281,10 @@ describe("CartService", () => { await cartService.update(IdMap.getId("with-d"), { discounts: [], }) + + expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledTimes(1) + expect(LineItemAdjustmentServiceMock.createAdjustments).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledTimes(1) expect(eventBusService.emit).toHaveBeenCalledWith( "cart.updated", @@ -2329,6 +2333,7 @@ describe("CartService", () => { }, }, ], + items: [], region_id: IdMap.getId("good"), }) }, @@ -2339,6 +2344,7 @@ describe("CartService", () => { totalsService, cartRepository, eventBusService, + lineItemAdjustmentService: LineItemAdjustmentServiceMock, taxProviderService: taxProviderServiceMock, newTotalsService: newTotalsServiceMock, featureFlagRouter: new FlagRouter({}), @@ -2351,6 +2357,9 @@ describe("CartService", () => { it("successfully removes discount", async () => { await cartService.removeDiscount(IdMap.getId("fr-cart"), "1234") + expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledTimes(1) + expect(LineItemAdjustmentServiceMock.createAdjustments).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledTimes(1) expect(eventBusService.emit).toHaveBeenCalledWith( "cart.updated", @@ -2361,6 +2370,7 @@ describe("CartService", () => { expect(cartRepository.save).toHaveBeenCalledWith({ id: IdMap.getId("cart"), region_id: IdMap.getId("good"), + items: [], discounts: [ { code: "FS1234", diff --git a/packages/medusa/src/services/cart.ts b/packages/medusa/src/services/cart.ts index 9db799c953..769bb49093 100644 --- a/packages/medusa/src/services/cart.ts +++ b/packages/medusa/src/services/cart.ts @@ -1053,7 +1053,7 @@ class CartService extends TransactionBaseService { } } - if (isDefined(data.discounts)) { + if (isDefined(data.discounts) && data.discounts.length) { const previousDiscounts = [...cart.discounts] cart.discounts.length = 0 @@ -1081,6 +1081,9 @@ class CartService extends TransactionBaseService { if (hasFreeShipping) { await this.adjustFreeShipping_(cart, true) } + } else if (isDefined(data.discounts) && !data.discounts.length) { + cart.discounts.length = 0 + await this.refreshAdjustments_(cart) } if ("gift_cards" in data) { @@ -1411,6 +1414,8 @@ class CartService extends TransactionBaseService { async (transactionManager: EntityManager) => { const cart = await this.retrieve(cartId, { relations: [ + "items", + "region", "discounts", "discounts.rule", "payment_sessions", @@ -1435,7 +1440,9 @@ class CartService extends TransactionBaseService { ) const updatedCart = await cartRepo.save(cart) - if (updatedCart.payment_sessions?.length) { + await this.refreshAdjustments_(updatedCart) + + if (cart.payment_sessions?.length) { await this.setPaymentSessions(cartId) }