diff --git a/.changeset/chilled-radios-shop.md b/.changeset/chilled-radios-shop.md new file mode 100644 index 0000000000000..cadb06f0865d5 --- /dev/null +++ b/.changeset/chilled-radios-shop.md @@ -0,0 +1,6 @@ +--- +"@medusajs/core-flows": patch +"@medusajs/medusa": patch +--- + +feat(medusa,core-flows): update cart adjustments on item updates diff --git a/integration-tests/plugins/__tests__/cart/store/add-promotions-to-cart.spec.ts b/integration-tests/plugins/__tests__/cart/store/add-promotions-to-cart.spec.ts index 62273d24481ba..3a05145574789 100644 --- a/integration-tests/plugins/__tests__/cart/store/add-promotions-to-cart.spec.ts +++ b/integration-tests/plugins/__tests__/cart/store/add-promotions-to-cart.spec.ts @@ -134,7 +134,6 @@ describe("Store Carts API: Add promotions to cart", () => { id: "item-1", adjustments: expect.arrayContaining([ expect.objectContaining({ - promotion_id: createdPromotion.id, code: createdPromotion.code, amount: 1000, }), @@ -144,7 +143,6 @@ describe("Store Carts API: Add promotions to cart", () => { adjustments: expect.arrayContaining([ expect.objectContaining({ id: expect.not.stringContaining(lineItemAdjustment.id), - promotion_id: appliedPromotion.id, code: appliedPromotion.code, amount: 300, }), diff --git a/integration-tests/plugins/__tests__/cart/store/carts.spec.ts b/integration-tests/plugins/__tests__/cart/store/carts.spec.ts index fe077a33ffb3e..0387442f39f36 100644 --- a/integration-tests/plugins/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/plugins/__tests__/cart/store/carts.spec.ts @@ -557,10 +557,6 @@ describe("Store Carts API", () => { describe("POST /store/carts/:id/line-items", () => { it("should add item to cart", async () => { - const cart = await cartModuleService.create({ - currency_code: "usd", - }) - const [product] = await productModule.create([ { title: "Test product", @@ -572,6 +568,48 @@ describe("Store Carts API", () => { }, ]) + const cart = await cartModuleService.create({ + currency_code: "usd", + items: [ + { + id: "item-1", + unit_price: 2000, + quantity: 1, + title: "Test item", + product_id: "prod_mat", + } as any, + ], + }) + + const appliedPromotion = await promotionModule.create({ + code: "PROMOTION_APPLIED", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "300", + apply_to_quantity: 2, + target_rules: [ + { + attribute: "product_id", + operator: "in", + values: ["prod_mat", product.id], + }, + ], + }, + }) + + const [lineItemAdjustment] = + await cartModuleService.addLineItemAdjustments([ + { + code: appliedPromotion.code!, + amount: 300, + item_id: "item-1", + promotion_id: appliedPromotion.id, + }, + ]) + const priceSet = await pricingModule.create({ prices: [ { @@ -608,6 +646,24 @@ describe("Store Carts API", () => { unit_price: 3000, quantity: 1, title: "Test variant", + adjustments: [ + expect.objectContaining({ + code: "PROMOTION_APPLIED", + amount: 180, + }), + ], + }), + expect.objectContaining({ + unit_price: 2000, + quantity: 1, + title: "Test item", + adjustments: [ + expect.objectContaining({ + id: expect.not.stringContaining(lineItemAdjustment.id), + code: "PROMOTION_APPLIED", + amount: 120, + }), + ], }), ]), }) diff --git a/packages/core-flows/src/definition/cart/steps/index.ts b/packages/core-flows/src/definition/cart/steps/index.ts index d91c40f916783..c14bd9664327c 100644 --- a/packages/core-flows/src/definition/cart/steps/index.ts +++ b/packages/core-flows/src/definition/cart/steps/index.ts @@ -14,4 +14,3 @@ export * from "./remove-shipping-method-adjustments" export * from "./retrieve-cart" export * from "./update-carts" export * from "./validate-variants-existence" - diff --git a/packages/core-flows/src/definition/cart/steps/refresh-cart-promotions.ts b/packages/core-flows/src/definition/cart/steps/refresh-cart-promotions.ts new file mode 100644 index 0000000000000..6c4c7bc627378 --- /dev/null +++ b/packages/core-flows/src/definition/cart/steps/refresh-cart-promotions.ts @@ -0,0 +1,30 @@ +import { PromotionActions } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" +import { updateCartPromotionsWorkflow } from "../workflows" + +interface StepInput { + id: string + promo_codes?: string[] + action?: + | PromotionActions.ADD + | PromotionActions.REMOVE + | PromotionActions.REPLACE +} + +export const refreshCartPromotionsStepId = "refresh-cart-promotions" +export const refreshCartPromotionsStep = createStep( + refreshCartPromotionsStepId, + async (data: StepInput, { container }) => { + const { promo_codes = [], id, action = PromotionActions.ADD } = data + + await updateCartPromotionsWorkflow(container).run({ + input: { + action, + promoCodes: promo_codes, + cartId: id, + }, + }) + + return new StepResponse(null) + } +) diff --git a/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts b/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts index e997cb8bf51a3..36faf12b13974 100644 --- a/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts +++ b/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts @@ -13,6 +13,7 @@ import { getVariantsStep, validateVariantsExistStep, } from "../steps" +import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" import { prepareLineItemData } from "../utils/prepare-line-item-data" // TODO: The AddToCartWorkflow are missing the following steps: @@ -70,6 +71,8 @@ export const addToCartWorkflow = createWorkflow( const items = addToCartStep({ items: lineItems }) + refreshCartPromotionsStep({ id: input.cart.id }) + return items } ) diff --git a/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts b/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts index dd4550e5ca6fc..e83ec25c84949 100644 --- a/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts +++ b/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts @@ -1,4 +1,3 @@ -import { CartDTO } from "@medusajs/types" import { PromotionActions } from "@medusajs/utils" import { WorkflowData, @@ -27,7 +26,7 @@ type WorkflowInput = { export const updateCartPromotionsWorkflowId = "update-cart-promotions" export const updateCartPromotionsWorkflow = createWorkflow( updateCartPromotionsWorkflowId, - (input: WorkflowData): WorkflowData => { + (input: WorkflowData): WorkflowData => { const retrieveCartInput = { id: input.cartId, config: { @@ -65,9 +64,5 @@ export const updateCartPromotionsWorkflow = createWorkflow( createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }), createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate }) ) - - return retrieveCartStep(retrieveCartInput).config({ - name: "retrieve-cart-result-step", - }) } ) diff --git a/packages/core-flows/src/definition/cart/workflows/update-cart.ts b/packages/core-flows/src/definition/cart/workflows/update-cart.ts index b5ac88d85168a..4030f4b3b761e 100644 --- a/packages/core-flows/src/definition/cart/workflows/update-cart.ts +++ b/packages/core-flows/src/definition/cart/workflows/update-cart.ts @@ -7,35 +7,18 @@ import { transform, } from "@medusajs/workflows-sdk" import { - createLineItemAdjustmentsStep, - createShippingMethodAdjustmentsStep, findOneOrAnyRegionStep, findOrCreateCustomerStep, findSalesChannelStep, - getActionsToComputeFromPromotionsStep, - prepareAdjustmentsFromPromotionActionsStep, - removeLineItemAdjustmentsStep, - removeShippingMethodAdjustmentsStep, retrieveCartStep, updateCartsStep, } from "../steps" +import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" export const updateCartWorkflowId = "update-cart" export const updateCartWorkflow = createWorkflow( updateCartWorkflowId, (input: WorkflowData): WorkflowData => { - const retrieveCartInput = { - id: input.id, - config: { - relations: [ - "items", - "items.adjustments", - "shipping_methods", - "shipping_methods.adjustments", - ], - }, - } - const [salesChannel, region, customerData] = parallelize( findSalesChannelStep({ salesChannelId: input.sales_channel_id, @@ -79,34 +62,24 @@ export const updateCartWorkflow = createWorkflow( updateCartsStep([cartInput]) - const cart = retrieveCartStep(retrieveCartInput) - const actions = getActionsToComputeFromPromotionsStep({ - cart, - promoCodes: input.promo_codes, + refreshCartPromotionsStep({ + id: input.id, + promo_codes: input.promo_codes, action: PromotionActions.REPLACE, }) - const { - lineItemAdjustmentsToCreate, - lineItemAdjustmentIdsToRemove, - shippingMethodAdjustmentsToCreate, - shippingMethodAdjustmentIdsToRemove, - } = prepareAdjustmentsFromPromotionActionsStep({ actions }) - - parallelize( - removeLineItemAdjustmentsStep({ lineItemAdjustmentIdsToRemove }), - removeShippingMethodAdjustmentsStep({ - shippingMethodAdjustmentIdsToRemove, - }) - ) - - parallelize( - createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }), - createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate }) - ) + const retrieveCartInput = { + id: input.id, + config: { + relations: [ + "items", + "items.adjustments", + "shipping_methods", + "shipping_methods.adjustments", + ], + }, + } - return retrieveCartStep(retrieveCartInput).config({ - name: "retrieve-cart-result-step", - }) + return retrieveCartStep(retrieveCartInput) } ) diff --git a/packages/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts b/packages/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts index d5b44fe23c4e6..e62dbd4e28015 100644 --- a/packages/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts +++ b/packages/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts @@ -6,6 +6,7 @@ import { } from "@medusajs/workflows-sdk" import { getVariantPriceSetsStep } from ".." import { updateLineItemsStep } from "../../line-item/steps" +import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" // TODO: The UpdateLineItemsWorkflow are missing the following steps: // - Confirm inventory exists (inventory module) @@ -55,6 +56,8 @@ export const updateLineItemInCartWorkflow = createWorkflow( selector: lineItemUpdate.selector, }) + refreshCartPromotionsStep({ id: input.cart.id }) + const updatedItem = transform({ result }, (data) => data.result?.[0]) return updatedItem diff --git a/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts b/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts index fd334b975b8d4..3758965d9fdc6 100644 --- a/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts +++ b/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts @@ -1,13 +1,15 @@ import { updateCartPromotionsWorkflow } from "@medusajs/core-flows" -import { PromotionActions } from "@medusajs/utils" +import { PromotionActions, remoteQueryObjectFromString } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" +import { defaultStoreCartFields } from "../../query-config" import { StorePostCartsCartPromotionsReq } from "../../validators" export const POST = async (req: MedusaRequest, res: MedusaResponse) => { + const remoteQuery = req.scope.resolve("remoteQuery") const workflow = updateCartPromotionsWorkflow(req.scope) const payload = req.validatedBody as StorePostCartsCartPromotionsReq - const { result, errors } = await workflow.run({ + const { errors } = await workflow.run({ input: { promoCodes: payload.promo_codes, cartId: req.params.id, @@ -20,14 +22,24 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => { throw errors[0].error } - res.status(200).json({ cart: result }) + const query = remoteQueryObjectFromString({ + entryPoint: "cart", + fields: defaultStoreCartFields, + }) + + const [cart] = await remoteQuery(query, { + cart: { id: req.params.id }, + }) + + res.status(200).json({ cart }) } export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => { + const remoteQuery = req.scope.resolve("remoteQuery") const workflow = updateCartPromotionsWorkflow(req.scope) const payload = req.validatedBody as StorePostCartsCartPromotionsReq - const { result, errors } = await workflow.run({ + const { errors } = await workflow.run({ input: { promoCodes: payload.promo_codes, cartId: req.params.id, @@ -40,5 +52,14 @@ export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => { throw errors[0].error } - res.status(200).json({ cart: result }) + const query = remoteQueryObjectFromString({ + entryPoint: "cart", + fields: defaultStoreCartFields, + }) + + const [cart] = await remoteQuery(query, { + cart: { id: req.params.id }, + }) + + res.status(200).json({ cart }) }