feat(workflows-sdk,core-flows,medusa,types): add workflow to update promotions to cart (#6474)
what: - adds API + workflow to add/remove promotions in a cart - minor fixes in promotions module - minor type fixes in cart module - typing fix in workflows-sdk (Thanks @adrien2p) - fix step result in workflows-sdk (Thanks @adrien2p) RESOLVES CORE-1768 Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
co-authored by
Adrien de Peretti
parent
63be07031b
commit
ac86362e81
@@ -0,0 +1,41 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreateLineItemAdjustmentDTO,
|
||||
ICartModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
lineItemAdjustmentsToCreate: CreateLineItemAdjustmentDTO[]
|
||||
}
|
||||
|
||||
export const createLineItemAdjustmentsStepId = "create-line-item-adjustments"
|
||||
export const createLineItemAdjustmentsStep = createStep(
|
||||
createLineItemAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { lineItemAdjustmentsToCreate = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const createdLineItemAdjustments =
|
||||
await cartModuleService.addLineItemAdjustments(
|
||||
lineItemAdjustmentsToCreate
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, createdLineItemAdjustments)
|
||||
},
|
||||
async (createdLineItemAdjustments, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!createdLineItemAdjustments?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.softDeleteLineItemAdjustments(
|
||||
createdLineItemAdjustments.map((c) => c.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreateShippingMethodAdjustmentDTO,
|
||||
ICartModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
shippingMethodAdjustmentsToCreate: CreateShippingMethodAdjustmentDTO[]
|
||||
}
|
||||
|
||||
export const createShippingMethodAdjustmentsStepId =
|
||||
"create-shipping-method-adjustments"
|
||||
export const createShippingMethodAdjustmentsStep = createStep(
|
||||
createShippingMethodAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { shippingMethodAdjustmentsToCreate = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const createdShippingMethodAdjustments =
|
||||
await cartModuleService.addShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentsToCreate
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, createdShippingMethodAdjustments)
|
||||
},
|
||||
async (createdShippingMethodAdjustments, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!createdShippingMethodAdjustments?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.softDeleteShippingMethodAdjustments(
|
||||
createdShippingMethodAdjustments.map((c) => c.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, IPromotionModuleService } from "@medusajs/types"
|
||||
import { deduplicate, isString } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartDTO
|
||||
promoCodes: string[]
|
||||
removePromotions: boolean
|
||||
}
|
||||
|
||||
export const getActionsToComputeFromPromotionsStepId =
|
||||
"get-actions-to-compute-from-promotions"
|
||||
export const getActionsToComputeFromPromotionsStep = createStep(
|
||||
getActionsToComputeFromPromotionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const promotionModuleService: IPromotionModuleService = container.resolve(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const { removePromotions = false, promoCodes = [], cart } = data
|
||||
|
||||
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 (removePromotions) {
|
||||
promotionCodesToApply = promotionCodesToApply.filter(
|
||||
(code) => !promoCodes.includes(code)
|
||||
)
|
||||
}
|
||||
|
||||
const actionsToCompute = await promotionModuleService.computeActions(
|
||||
promotionCodesToApply,
|
||||
cart as any
|
||||
)
|
||||
|
||||
return new StepResponse(actionsToCompute)
|
||||
}
|
||||
)
|
||||
@@ -1,5 +1,12 @@
|
||||
export * from "./create-carts"
|
||||
export * from "./create-line-item-adjustments"
|
||||
export * from "./create-shipping-method-adjustments"
|
||||
export * from "./find-one-or-any-region"
|
||||
export * from "./find-or-create-customer"
|
||||
export * from "./find-sales-channel"
|
||||
export * from "./get-actions-to-compute-from-promotions"
|
||||
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-carts"
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
AddItemAdjustmentAction,
|
||||
AddShippingMethodAdjustment,
|
||||
ComputeActions,
|
||||
IPromotionModuleService,
|
||||
PromotionDTO,
|
||||
RemoveItemAdjustmentAction,
|
||||
RemoveShippingMethodAdjustment,
|
||||
} from "@medusajs/types"
|
||||
import { ComputedActions } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
actions: ComputeActions[]
|
||||
}
|
||||
|
||||
export const prepareAdjustmentsFromPromotionActionsStepId =
|
||||
"prepare-adjustments-from-promotion-actions"
|
||||
export const prepareAdjustmentsFromPromotionActionsStep = createStep(
|
||||
prepareAdjustmentsFromPromotionActionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const promotionModuleService: IPromotionModuleService = container.resolve(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const { actions = [] } = data
|
||||
const promotions = await promotionModuleService.list(
|
||||
{ code: actions.map((a) => a.code) },
|
||||
{ select: ["id", "code"] }
|
||||
)
|
||||
|
||||
const promotionsMap = new Map<string, PromotionDTO>(
|
||||
promotions.map((promotion) => [promotion.code!, promotion])
|
||||
)
|
||||
|
||||
const lineItemAdjustmentsToCreate = actions
|
||||
.filter((a) => a.action === ComputedActions.ADD_ITEM_ADJUSTMENT)
|
||||
.map((action) => ({
|
||||
code: action.code,
|
||||
amount: (action as AddItemAdjustmentAction).amount,
|
||||
item_id: (action as AddItemAdjustmentAction).item_id,
|
||||
promotion_id: promotionsMap.get(action.code)?.id,
|
||||
}))
|
||||
|
||||
const lineItemAdjustmentIdsToRemove = actions
|
||||
.filter((a) => a.action === ComputedActions.REMOVE_ITEM_ADJUSTMENT)
|
||||
.map((a) => (a as RemoveItemAdjustmentAction).adjustment_id)
|
||||
|
||||
const shippingMethodAdjustmentsToCreate = actions
|
||||
.filter(
|
||||
(a) => a.action === ComputedActions.ADD_SHIPPING_METHOD_ADJUSTMENT
|
||||
)
|
||||
.map((action) => ({
|
||||
code: action.code,
|
||||
amount: (action as AddShippingMethodAdjustment).amount,
|
||||
shipping_method_id: (action as AddShippingMethodAdjustment)
|
||||
.shipping_method_id,
|
||||
promotion_id: promotionsMap.get(action.code)?.id,
|
||||
}))
|
||||
|
||||
const shippingMethodAdjustmentIdsToRemove = actions
|
||||
.filter(
|
||||
(a) => a.action === ComputedActions.REMOVE_SHIPPING_METHOD_ADJUSTMENT
|
||||
)
|
||||
.map((a) => (a as RemoveShippingMethodAdjustment).adjustment_id)
|
||||
|
||||
return new StepResponse({
|
||||
lineItemAdjustmentsToCreate,
|
||||
lineItemAdjustmentIdsToRemove,
|
||||
shippingMethodAdjustmentsToCreate,
|
||||
shippingMethodAdjustmentIdsToRemove,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
lineItemAdjustmentIdsToRemove: string[]
|
||||
}
|
||||
|
||||
export const removeLineItemAdjustmentsStepId = "remove-line-item-adjustments"
|
||||
export const removeLineItemAdjustmentsStep = createStep(
|
||||
removeLineItemAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { lineItemAdjustmentIdsToRemove = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await cartModuleService.softDeleteLineItemAdjustments(
|
||||
lineItemAdjustmentIdsToRemove
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, lineItemAdjustmentIdsToRemove)
|
||||
},
|
||||
async (lineItemAdjustmentIdsToRemove, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!lineItemAdjustmentIdsToRemove?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.restoreLineItemAdjustments(
|
||||
lineItemAdjustmentIdsToRemove
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
shippingMethodAdjustmentIdsToRemove: string[]
|
||||
}
|
||||
|
||||
export const removeShippingMethodAdjustmentsStepId =
|
||||
"remove-shipping-method-adjustments"
|
||||
export const removeShippingMethodAdjustmentsStep = createStep(
|
||||
removeShippingMethodAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { shippingMethodAdjustmentIdsToRemove = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await cartModuleService.softDeleteShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentIdsToRemove
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, shippingMethodAdjustmentIdsToRemove)
|
||||
},
|
||||
async (shippingMethodAdjustmentIdsToRemove, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!shippingMethodAdjustmentIdsToRemove?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.restoreShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentIdsToRemove
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, FindConfig, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cartId: string
|
||||
config: FindConfig<CartDTO>
|
||||
}
|
||||
|
||||
export const retrieveCartStepId = "retrieve-cart"
|
||||
export const retrieveCartStep = createStep(
|
||||
retrieveCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const cartModuleService = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const cart = await cartModuleService.retrieve(data.cartId, data.config)
|
||||
|
||||
// TODO: remove this when cart handles totals calculation
|
||||
cart.items = cart.items?.map((item) => {
|
||||
item.subtotal = item.unit_price
|
||||
|
||||
return item
|
||||
})
|
||||
|
||||
// TODO: remove this when cart handles totals calculation
|
||||
cart.shipping_methods = cart.shipping_methods?.map((shipping_method) => {
|
||||
// TODO: should we align all amounts/prices fields to be unit_price?
|
||||
shipping_method.subtotal = shipping_method.amount
|
||||
|
||||
return shipping_method
|
||||
})
|
||||
|
||||
return new StepResponse(cart)
|
||||
}
|
||||
)
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from "./create-carts"
|
||||
export * from "./update-cart-promotions"
|
||||
export * from "./update-carts"
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { CartDTO } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createLineItemAdjustmentsStep,
|
||||
createShippingMethodAdjustmentsStep,
|
||||
getActionsToComputeFromPromotionsStep,
|
||||
prepareAdjustmentsFromPromotionActionsStep,
|
||||
removeLineItemAdjustmentsStep,
|
||||
removeShippingMethodAdjustmentsStep,
|
||||
retrieveCartStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
promoCodes: string[]
|
||||
cartId: string
|
||||
removePromotions?: boolean
|
||||
}
|
||||
|
||||
export const updateCartPromotionsWorkflowId = "update-cart-promotions"
|
||||
export const updateCartPromotionsWorkflow = createWorkflow(
|
||||
updateCartPromotionsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO> => {
|
||||
const retrieveCartInput = {
|
||||
cartId: input.cartId,
|
||||
config: {
|
||||
relations: [
|
||||
"items",
|
||||
"items.adjustments",
|
||||
"shipping_methods",
|
||||
"shipping_methods.adjustments",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const cart = retrieveCartStep(retrieveCartInput)
|
||||
const actions = getActionsToComputeFromPromotionsStep({
|
||||
cart,
|
||||
promoCodes: input.promoCodes,
|
||||
removePromotions: input.removePromotions || false,
|
||||
})
|
||||
|
||||
const {
|
||||
lineItemAdjustmentsToCreate,
|
||||
lineItemAdjustmentIdsToRemove,
|
||||
shippingMethodAdjustmentsToCreate,
|
||||
shippingMethodAdjustmentIdsToRemove,
|
||||
} = prepareAdjustmentsFromPromotionActionsStep({ actions })
|
||||
|
||||
parallelize(
|
||||
removeLineItemAdjustmentsStep({ lineItemAdjustmentIdsToRemove }),
|
||||
removeShippingMethodAdjustmentsStep({
|
||||
shippingMethodAdjustmentIdsToRemove,
|
||||
})
|
||||
)
|
||||
|
||||
parallelize(
|
||||
createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }),
|
||||
createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate })
|
||||
)
|
||||
|
||||
return retrieveCartStep(retrieveCartInput).config({
|
||||
name: "retrieve-cart-result-step",
|
||||
})
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user