feat(core-flows,link-modules,modules-sdk): add cart <> promotion link as source of truth (#6561)

what:

- adds promotion cart link
- update steps to create and remove links
This commit is contained in:
Riqwan Thamir
2024-03-04 09:50:49 +00:00
committed by GitHub
parent 8dad2b51a2
commit d550be3685
14 changed files with 272 additions and 51 deletions
@@ -1,15 +1,9 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { LinkModuleUtils, ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CartDTO, IPromotionModuleService } from "@medusajs/types"
import { PromotionActions, deduplicate, isString } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
interface StepInput {
cart: CartDTO
promoCodes?: string[]
action:
| PromotionActions.ADD
| PromotionActions.REMOVE
| PromotionActions.REPLACE
}
export const getActionsToComputeFromPromotionsStepId =
@@ -17,46 +11,26 @@ export const getActionsToComputeFromPromotionsStepId =
export const getActionsToComputeFromPromotionsStep = createStep(
getActionsToComputeFromPromotionsStepId,
async (data: StepInput, { container }) => {
const promotionModuleService: IPromotionModuleService = container.resolve(
const { cart } = data
const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY)
const promotionService = container.resolve<IPromotionModuleService>(
ModuleRegistrationName.PROMOTION
)
const { action = PromotionActions.ADD, promoCodes, cart } = data
const existingCartPromotionLinks = await remoteQuery({
cart_promotion: {
__args: { cart_id: [cart.id] },
fields: ["id", "cart_id", "promotion_id", "deleted_at"],
},
})
if (!Array.isArray(promoCodes)) {
return new StepResponse([])
}
const existingPromotions = await promotionService.list(
{ id: existingCartPromotionLinks.map((l) => l.promotion_id) },
{ take: null, select: ["code"] }
)
const appliedItemPromoCodes = cart.items
?.map((item) => item.adjustments?.map((adjustment) => adjustment.code))
.flat(1)
.filter(isString) as string[]
const appliedShippingMethodPromoCodes = cart.shipping_methods
?.map((shippingMethod) =>
shippingMethod.adjustments?.map((adjustment) => adjustment.code)
)
.flat(1)
.filter(isString) as string[]
let promotionCodesToApply = deduplicate([
...promoCodes,
...appliedItemPromoCodes,
...appliedShippingMethodPromoCodes,
])
if (action === PromotionActions.REMOVE) {
promotionCodesToApply = promotionCodesToApply.filter(
(code) => !promoCodes.includes(code)
)
}
if (action === PromotionActions.REPLACE) {
promotionCodesToApply = promoCodes
}
const actionsToCompute = await promotionModuleService.computeActions(
promotionCodesToApply,
const actionsToCompute = await promotionService.computeActions(
existingPromotions.map((p) => p.code!),
cart as any
)
@@ -12,5 +12,6 @@ export * from "./prepare-adjustments-from-promotion-actions"
export * from "./remove-line-item-adjustments"
export * from "./remove-shipping-method-adjustments"
export * from "./retrieve-cart"
export * from "./update-cart-promotions"
export * from "./update-carts"
export * from "./validate-variants-existence"
@@ -0,0 +1,107 @@
import {
LinkModuleUtils,
ModuleRegistrationName,
Modules,
} from "@medusajs/modules-sdk"
import { IPromotionModuleService } from "@medusajs/types"
import { PromotionActions } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
interface StepInput {
id: string
promo_codes?: string[]
action?:
| PromotionActions.ADD
| PromotionActions.REMOVE
| PromotionActions.REPLACE
}
export const updateCartPromotionsStepId = "update-cart-promotions"
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<IPromotionModuleService>(
ModuleRegistrationName.PROMOTION
)
const existingCartPromotionLinks = await remoteQuery({
cart_promotion: {
__args: { cart_id: [id] },
fields: ["cart_id", "promotion_id"],
},
})
const promotionLinkMap = new Map<string, any>(
existingCartPromotionLinks.map((link) => [link.promotion_id, link])
)
const promotions = await promotionService.list(
{ code: promo_codes },
{ select: ["id"] }
)
const linksToCreate: any[] = []
const linksToDismiss: any[] = []
for (const promotion of promotions) {
const linkObject = {
[Modules.CART]: { cart_id: id },
[Modules.PROMOTION]: { promotion_id: promotion.id },
}
if ([PromotionActions.ADD, PromotionActions.REPLACE].includes(action)) {
linksToCreate.push(linkObject)
}
if (action === PromotionActions.REMOVE) {
const link = promotionLinkMap.get(promotion.id)
if (link) {
linksToDismiss.push(linkObject)
}
}
}
if (action === PromotionActions.REPLACE) {
for (const link of existingCartPromotionLinks) {
linksToDismiss.push({
[Modules.CART]: { cart_id: link.cart_id },
[Modules.PROMOTION]: { promotion_id: link.promotion_id },
})
}
}
const linksToDismissPromise = linksToDismiss.length
? remoteLink.dismiss(linksToDismiss)
: []
const linksToCreatePromise = linksToCreate.length
? remoteLink.create(linksToCreate)
: []
const [_, createdLinks] = await Promise.all([
linksToDismissPromise,
linksToCreatePromise,
])
return new StepResponse(null, {
createdLinkIds: createdLinks.map((link) => link.id),
dismissedLinks: linksToDismiss,
})
},
async (revertData, { container }) => {
const remoteLink = container.resolve(LinkModuleUtils.REMOTE_LINK)
if (revertData?.dismissedLinks?.length) {
await remoteLink.create(revertData.dismissedLinks)
}
if (revertData?.createdLinkIds?.length) {
await remoteLink.delete(revertData.createdLinkIds)
}
}
)
@@ -19,7 +19,6 @@ import { prepareLineItemData } from "../utils/prepare-line-item-data"
// TODO: The AddToCartWorkflow are missing the following steps:
// - Confirm inventory exists (inventory module)
// - Refresh/delete shipping methods (fulfillment module)
// - Create line item adjustments (promotion module)
// - Update payment sessions (payment module)
export const addToCartWorkflowId = "add-to-cart"
@@ -12,6 +12,7 @@ import {
removeLineItemAdjustmentsStep,
removeShippingMethodAdjustmentsStep,
retrieveCartStep,
updateCartPromotionsStep,
} from "../steps"
type WorkflowInput = {
@@ -39,11 +40,15 @@ export const updateCartPromotionsWorkflow = createWorkflow(
},
}
updateCartPromotionsStep({
id: input.cartId,
promo_codes: input.promoCodes,
action: input.action || PromotionActions.ADD,
})
const cart = retrieveCartStep(retrieveCartInput)
const actions = getActionsToComputeFromPromotionsStep({
cart,
promoCodes: input.promoCodes,
action: input.action || PromotionActions.ADD,
})
const {