diff --git a/packages/core/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts b/packages/core/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts index b1eebded3e..3a8de587f0 100644 --- a/packages/core/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts +++ b/packages/core/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts @@ -1,9 +1,10 @@ -import { LinkModuleUtils, ModuleRegistrationName } from "@medusajs/modules-sdk" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CartDTO, IPromotionModuleService } from "@medusajs/types" import { StepResponse, createStep } from "@medusajs/workflows-sdk" interface StepInput { cart: CartDTO + promotionCodesToApply: string[] } export const getActionsToComputeFromPromotionsStepId = @@ -11,26 +12,13 @@ export const getActionsToComputeFromPromotionsStepId = export const getActionsToComputeFromPromotionsStep = createStep( getActionsToComputeFromPromotionsStepId, async (data: StepInput, { container }) => { - const { cart } = data - const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY) + const { cart, promotionCodesToApply = [] } = data const promotionService = container.resolve( ModuleRegistrationName.PROMOTION ) - const existingCartPromotionLinks = await remoteQuery({ - cart_promotion: { - __args: { cart_id: [cart.id] }, - fields: ["id", "cart_id", "promotion_id", "deleted_at"], - }, - }) - - const existingPromotions = await promotionService.list( - { id: existingCartPromotionLinks.map((l) => l.promotion_id) }, - { take: null, select: ["code"] } - ) - const actionsToCompute = await promotionService.computeActions( - existingPromotions.map((p) => p.code!), + promotionCodesToApply, cart as any ) diff --git a/packages/core/core-flows/src/definition/cart/steps/get-promotion-codes-to-apply.ts b/packages/core/core-flows/src/definition/cart/steps/get-promotion-codes-to-apply.ts new file mode 100644 index 0000000000..8c28a086cf --- /dev/null +++ b/packages/core/core-flows/src/definition/cart/steps/get-promotion-codes-to-apply.ts @@ -0,0 +1,63 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { IPromotionModuleService } from "@medusajs/types" +import { PromotionActions } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +interface StepInput { + cart: { + items?: { adjustments?: { code?: string }[] }[] + shipping_methods?: { adjustments?: { code?: string }[] }[] + } + promo_codes?: string[] + action?: + | PromotionActions.ADD + | PromotionActions.REMOVE + | PromotionActions.REPLACE +} + +export const getPromotionCodesToApplyId = "get-promotion-codes-to-apply" +export const getPromotionCodesToApply = createStep( + getPromotionCodesToApplyId, + async (data: StepInput, { container }) => { + const { promo_codes = [], cart, action = PromotionActions.ADD } = data + const { items = [], shipping_methods = [] } = cart + const adjustmentCodes: string[] = [] + const promotionService = container.resolve( + ModuleRegistrationName.PROMOTION + ) + + const objects = items.concat(shipping_methods) + + objects.forEach((object) => { + object.adjustments?.forEach((adjustment) => { + if (adjustment.code && !adjustmentCodes.includes(adjustment.code)) { + adjustmentCodes.push(adjustment.code) + } + }) + }) + + const promotionCodesToApply: Set = new Set( + ( + await promotionService.list( + { code: adjustmentCodes }, + { select: ["code"], take: null } + ) + ).map((p) => p.code!) + ) + + if (action === PromotionActions.ADD) { + promo_codes.forEach((code) => promotionCodesToApply.add(code)) + } + + if (action === PromotionActions.REMOVE) { + promo_codes.forEach((code) => promotionCodesToApply.delete(code)) + } + + if (action === PromotionActions.REPLACE) { + promotionCodesToApply.clear() + promo_codes.forEach((code) => promotionCodesToApply.add(code)) + } + + return new StepResponse(Array.from(promotionCodesToApply)) + } +) diff --git a/packages/core/core-flows/src/definition/cart/steps/index.ts b/packages/core/core-flows/src/definition/cart/steps/index.ts index 8efcab9ae5..cb6f553a9c 100644 --- a/packages/core/core-flows/src/definition/cart/steps/index.ts +++ b/packages/core/core-flows/src/definition/cart/steps/index.ts @@ -10,6 +10,7 @@ export * from "./find-or-create-customer" export * from "./find-sales-channel" export * from "./get-actions-to-compute-from-promotions" export * from "./get-item-tax-lines" +export * from "./get-promotion-codes-to-apply" export * from "./get-variant-price-sets" export * from "./get-variants" export * from "./prepare-adjustments-from-promotion-actions" diff --git a/packages/core/core-flows/src/definition/cart/steps/prepare-adjustments-from-promotion-actions.ts b/packages/core/core-flows/src/definition/cart/steps/prepare-adjustments-from-promotion-actions.ts index 9c12cc9905..6511ba1dd9 100644 --- a/packages/core/core-flows/src/definition/cart/steps/prepare-adjustments-from-promotion-actions.ts +++ b/packages/core/core-flows/src/definition/cart/steps/prepare-adjustments-from-promotion-actions.ts @@ -65,11 +65,17 @@ export const prepareAdjustmentsFromPromotionActionsStep = createStep( ) .map((a) => (a as RemoveShippingMethodAdjustment).adjustment_id) + const computedPromotionCodes = [ + ...lineItemAdjustmentsToCreate, + ...shippingMethodAdjustmentsToCreate, + ].map((adjustment) => adjustment.code) + return new StepResponse({ lineItemAdjustmentsToCreate, lineItemAdjustmentIdsToRemove, shippingMethodAdjustmentsToCreate, shippingMethodAdjustmentIdsToRemove, + computedPromotionCodes, }) } ) diff --git a/packages/core/core-flows/src/definition/cart/steps/update-cart-promotions.ts b/packages/core/core-flows/src/definition/cart/steps/update-cart-promotions.ts index 6411646098..ad4a19ffaa 100644 --- a/packages/core/core-flows/src/definition/cart/steps/update-cart-promotions.ts +++ b/packages/core/core-flows/src/definition/cart/steps/update-cart-promotions.ts @@ -21,7 +21,6 @@ export const updateCartPromotionsStep = createStep( updateCartPromotionsStepId, async (data: StepInput, { container }) => { const { promo_codes = [], id, action = PromotionActions.ADD } = data - const remoteLink = container.resolve(LinkModuleUtils.REMOTE_LINK) const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY) const promotionService = container.resolve( @@ -75,19 +74,14 @@ export const updateCartPromotionsStep = createStep( } } - const linksToDismissPromise = linksToDismiss.length - ? remoteLink.dismiss(linksToDismiss) - : [] + if (linksToDismiss.length) { + await remoteLink.dismiss(linksToDismiss) + } - const linksToCreatePromise = linksToCreate.length - ? remoteLink.create(linksToCreate) + const createdLinks = linksToCreate.length + ? await remoteLink.create(linksToCreate) : [] - const [_, createdLinks] = await Promise.all([ - linksToDismissPromise, - linksToCreatePromise, - ]) - return new StepResponse(null, { createdLinkIds: createdLinks.map((link) => link.id), dismissedLinks: linksToDismiss, diff --git a/packages/core/core-flows/src/definition/cart/utils/fields.ts b/packages/core/core-flows/src/definition/cart/utils/fields.ts index d9e63d4116..4b66ff3729 100644 --- a/packages/core/core-flows/src/definition/cart/utils/fields.ts +++ b/packages/core/core-flows/src/definition/cart/utils/fields.ts @@ -1,12 +1,21 @@ export const cartFieldsForRefreshSteps = [ + "subtotal", + "item_subtotal", + "shipping_subtotal", "region_id", "currency_code", "region.*", "items.*", + "items.product.id", + "items.product.collection_id", + "items.product.categories.id", + "items.product.tags.id", + "items.adjustments.*", "items.tax_lines.*", "shipping_address.*", "shipping_methods.*", - "shipping_methods.tax_lines*", + "shipping_methods.adjustments.*", + "shipping_methods.tax_lines.*", "customer.*", "customer.groups.*", ] diff --git a/packages/core/core-flows/src/definition/cart/workflows/update-cart-promotions.ts b/packages/core/core-flows/src/definition/cart/workflows/update-cart-promotions.ts index 78022217c9..ebfa13ac43 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/update-cart-promotions.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/update-cart-promotions.ts @@ -4,16 +4,18 @@ import { createWorkflow, parallelize, } from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" import { createLineItemAdjustmentsStep, createShippingMethodAdjustmentsStep, getActionsToComputeFromPromotionsStep, + getPromotionCodesToApply, prepareAdjustmentsFromPromotionActionsStep, removeLineItemAdjustmentsStep, removeShippingMethodAdjustmentsStep, - retrieveCartStep, - updateCartPromotionsStep, } from "../steps" +import { updateCartPromotionsStep } from "../steps/update-cart-promotions" +import { cartFieldsForRefreshSteps } from "../utils/fields" type WorkflowInput = { promoCodes: string[] @@ -28,27 +30,22 @@ export const updateCartPromotionsWorkflowId = "update-cart-promotions" export const updateCartPromotionsWorkflow = createWorkflow( updateCartPromotionsWorkflowId, (input: WorkflowData): WorkflowData => { - const retrieveCartInput = { - id: input.cartId, - config: { - relations: [ - "items", - "items.adjustments", - "shipping_methods", - "shipping_methods.adjustments", - ], - }, - } + const cart = useRemoteQueryStep({ + entry_point: "cart", + fields: cartFieldsForRefreshSteps, + variables: { id: input.cartId }, + list: false, + }) - updateCartPromotionsStep({ - id: input.cartId, + const promotionCodesToApply = getPromotionCodesToApply({ + cart: cart, promo_codes: input.promoCodes, action: input.action || PromotionActions.ADD, }) - const cart = retrieveCartStep(retrieveCartInput) const actions = getActionsToComputeFromPromotionsStep({ cart, + promotionCodesToApply, }) const { @@ -56,6 +53,7 @@ export const updateCartPromotionsWorkflow = createWorkflow( lineItemAdjustmentIdsToRemove, shippingMethodAdjustmentsToCreate, shippingMethodAdjustmentIdsToRemove, + computedPromotionCodes, } = prepareAdjustmentsFromPromotionActionsStep({ actions }) parallelize( @@ -69,5 +67,11 @@ export const updateCartPromotionsWorkflow = createWorkflow( createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }), createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate }) ) + + updateCartPromotionsStep({ + id: input.cartId, + promo_codes: computedPromotionCodes, + action: PromotionActions.REPLACE, + }) } ) diff --git a/packages/core/core-flows/src/definition/line-item/workflows/delete-line-items.ts b/packages/core/core-flows/src/definition/line-item/workflows/delete-line-items.ts index 5065dd588a..55533ea048 100644 --- a/packages/core/core-flows/src/definition/line-item/workflows/delete-line-items.ts +++ b/packages/core/core-flows/src/definition/line-item/workflows/delete-line-items.ts @@ -1,7 +1,8 @@ import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" +import { refreshCartPromotionsStep } from "../../cart/steps/refresh-cart-promotions" import { deleteLineItemsStep } from "../steps/delete-line-items" -type WorkflowInput = { ids: string[] } +type WorkflowInput = { cart_id: string; ids: string[] } // TODO: The DeleteLineItemsWorkflow are missing the following steps: // - Refresh/delete shipping methods (fulfillment module) @@ -12,6 +13,8 @@ export const deleteLineItemsWorkflowId = "delete-line-items" export const deleteLineItemsWorkflow = createWorkflow( deleteLineItemsWorkflowId, (input: WorkflowData) => { - return deleteLineItemsStep(input.ids) + deleteLineItemsStep(input.ids) + + refreshCartPromotionsStep({ id: input.cart_id }) } ) diff --git a/packages/core/types/src/promotion/common/compute-actions.ts b/packages/core/types/src/promotion/common/compute-actions.ts index c602b34945..3cd45007ad 100644 --- a/packages/core/types/src/promotion/common/compute-actions.ts +++ b/packages/core/types/src/promotion/common/compute-actions.ts @@ -200,6 +200,11 @@ export interface ComputeActionShippingLine extends Record { * The context provided when computing actions of promotions. */ export interface ComputeActionContext extends Record { + /** + * The cart's currency + */ + currency_code: string + /** * The cart's line items. */ diff --git a/packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts b/packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts index 3244745080..2286fcab48 100644 --- a/packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts +++ b/packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts @@ -2,12 +2,12 @@ import { deleteLineItemsWorkflow, updateLineItemInCartWorkflow, } from "@medusajs/core-flows" +import { DeleteResponse } from "@medusajs/types" import { MedusaError } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../../types/routing" +import { prepareListQuery } from "../../../../../../utils/get-query-config" import { refetchCart } from "../../../helpers" import { StoreUpdateCartLineItemType } from "../../../validators" -import { prepareListQuery } from "../../../../../../utils/get-query-config" -import { DeleteResponse } from "@medusajs/types" export const POST = async ( req: MedusaRequest, @@ -70,7 +70,7 @@ export const DELETE = async ( const id = req.params.line_id const { errors } = await deleteLineItemsWorkflow(req.scope).run({ - input: { ids: [id] }, + input: { cart_id: req.params.id, ids: [id] }, throwOnError: false, })