feat(core-flows,medusa,types,utils): adds update cart API with promotions (#6514)
what: - adds update cart API - workflow - promotions - sales channel - region - customer
This commit is contained in:
@@ -4,13 +4,13 @@ import { validateEmail } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
customerId?: string
|
||||
email?: string
|
||||
customerId?: string | null
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
interface StepOutput {
|
||||
customer?: CustomerDTO
|
||||
email?: string
|
||||
customer?: CustomerDTO | null
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
interface StepCompensateInput {
|
||||
@@ -22,11 +22,27 @@ export const findOrCreateCustomerStepId = "find-or-create-customer"
|
||||
export const findOrCreateCustomerStep = createStep(
|
||||
findOrCreateCustomerStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
if (
|
||||
typeof data.customerId === undefined &&
|
||||
typeof data.email === undefined
|
||||
) {
|
||||
return new StepResponse(
|
||||
{
|
||||
customer: undefined,
|
||||
email: undefined,
|
||||
},
|
||||
{ customerWasCreated: false }
|
||||
)
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const customerData: StepOutput = {}
|
||||
const customerData: StepOutput = {
|
||||
customer: null,
|
||||
email: null,
|
||||
}
|
||||
let customerWasCreated = false
|
||||
|
||||
if (data.customerId) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
salesChannelId?: string
|
||||
salesChannelId?: string | null
|
||||
}
|
||||
|
||||
export const findSalesChannelStepId = "find-sales-channel"
|
||||
@@ -15,6 +15,10 @@ export const findSalesChannelStep = createStep(
|
||||
ModuleRegistrationName.SALES_CHANNEL
|
||||
)
|
||||
|
||||
if (data.salesChannelId === null) {
|
||||
return new StepResponse(null)
|
||||
}
|
||||
|
||||
let salesChannel: SalesChannelDTO | undefined
|
||||
if (data.salesChannelId) {
|
||||
salesChannel = await salesChannelService.retrieve(data.salesChannelId)
|
||||
|
||||
+16
-5
@@ -1,12 +1,15 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, IPromotionModuleService } from "@medusajs/types"
|
||||
import { deduplicate, isString } from "@medusajs/utils"
|
||||
import { PromotionActions, deduplicate, isString } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartDTO
|
||||
promoCodes: string[]
|
||||
removePromotions: boolean
|
||||
promoCodes?: string[]
|
||||
action:
|
||||
| PromotionActions.ADD
|
||||
| PromotionActions.REMOVE
|
||||
| PromotionActions.REPLACE
|
||||
}
|
||||
|
||||
export const getActionsToComputeFromPromotionsStepId =
|
||||
@@ -18,7 +21,11 @@ export const getActionsToComputeFromPromotionsStep = createStep(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const { removePromotions = false, promoCodes = [], cart } = data
|
||||
const { action = PromotionActions.ADD, promoCodes, cart } = data
|
||||
|
||||
if (!Array.isArray(promoCodes)) {
|
||||
return new StepResponse([])
|
||||
}
|
||||
|
||||
const appliedItemPromoCodes = cart.items
|
||||
?.map((item) => item.adjustments?.map((adjustment) => adjustment.code))
|
||||
@@ -38,12 +45,16 @@ export const getActionsToComputeFromPromotionsStep = createStep(
|
||||
...appliedShippingMethodPromoCodes,
|
||||
])
|
||||
|
||||
if (removePromotions) {
|
||||
if (action === PromotionActions.REMOVE) {
|
||||
promotionCodesToApply = promotionCodesToApply.filter(
|
||||
(code) => !promoCodes.includes(code)
|
||||
)
|
||||
}
|
||||
|
||||
if (action === PromotionActions.REPLACE) {
|
||||
promotionCodesToApply = promoCodes
|
||||
}
|
||||
|
||||
const actionsToCompute = await promotionModuleService.computeActions(
|
||||
promotionCodesToApply,
|
||||
cart as any
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CartDTO, FindConfig, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cartId: string
|
||||
id: string
|
||||
config: FindConfig<CartDTO>
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const retrieveCartStep = createStep(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const cart = await cartModuleService.retrieve(data.cartId, data.config)
|
||||
const cart = await cartModuleService.retrieve(data.id, data.config)
|
||||
|
||||
// TODO: remove this when cart handles totals calculation
|
||||
cart.items = cart.items?.map((item) => {
|
||||
|
||||
@@ -1,61 +1,53 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CartDTO,
|
||||
FilterableCartProps,
|
||||
ICartModuleService,
|
||||
UpdateCartDataDTO,
|
||||
UpdateCartDTO,
|
||||
UpdateCartWorkflowInputDTO,
|
||||
} from "@medusajs/types"
|
||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type UpdateCartsStepInput = {
|
||||
selector: FilterableCartProps
|
||||
update: UpdateCartDataDTO
|
||||
}
|
||||
|
||||
export const updateCartsStepId = "update-carts"
|
||||
export const updateCartsStep = createStep(
|
||||
updateCartsStepId,
|
||||
async (data: UpdateCartsStepInput, { container }) => {
|
||||
const service = container.resolve<ICartModuleService>(
|
||||
async (data: UpdateCartWorkflowInputDTO[], { container }) => {
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
|
||||
const prevCarts = await service.list(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const updatedCarts = await service.update(
|
||||
data.selector as Partial<CartDTO>,
|
||||
data.update
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
|
||||
const cartsBeforeUpdate = await cartModule.list(
|
||||
{ id: data.map((d) => d.id) },
|
||||
{ select: selects, relations }
|
||||
)
|
||||
|
||||
return new StepResponse(updatedCarts, prevCarts)
|
||||
const updatedCart = await cartModule.update(data)
|
||||
|
||||
return new StepResponse(updatedCart, cartsBeforeUpdate)
|
||||
},
|
||||
async (previousCarts, { container }) => {
|
||||
if (!previousCarts?.length) {
|
||||
async (cartsBeforeUpdate, { container }) => {
|
||||
if (!cartsBeforeUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICartModuleService>(
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const toRestore = previousCarts.map((c) => ({
|
||||
id: c.id,
|
||||
region_id: c.region_id,
|
||||
customer_id: c.customer_id,
|
||||
sales_channel_id: c.sales_channel_id,
|
||||
email: c.email,
|
||||
currency_code: c.currency_code,
|
||||
metadata: c.metadata,
|
||||
}))
|
||||
const dataToUpdate: UpdateCartDTO[] = []
|
||||
|
||||
await service.update(toRestore)
|
||||
for (const cart of cartsBeforeUpdate) {
|
||||
dataToUpdate.push({
|
||||
id: cart.id,
|
||||
region_id: cart.region_id,
|
||||
customer_id: cart.customer_id,
|
||||
sales_channel_id: cart.sales_channel_id,
|
||||
email: cart.email,
|
||||
currency_code: cart.currency_code,
|
||||
metadata: cart.metadata,
|
||||
})
|
||||
}
|
||||
|
||||
await cartModule.update(dataToUpdate)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./add-to-cart"
|
||||
export * from "./create-carts"
|
||||
export * from "./update-cart"
|
||||
export * from "./update-cart-promotions"
|
||||
export * from "./update-carts"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CartDTO } from "@medusajs/types"
|
||||
import { PromotionActions } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
@@ -17,7 +18,10 @@ import {
|
||||
type WorkflowInput = {
|
||||
promoCodes: string[]
|
||||
cartId: string
|
||||
removePromotions?: boolean
|
||||
action?:
|
||||
| PromotionActions.ADD
|
||||
| PromotionActions.REMOVE
|
||||
| PromotionActions.REPLACE
|
||||
}
|
||||
|
||||
export const updateCartPromotionsWorkflowId = "update-cart-promotions"
|
||||
@@ -25,7 +29,7 @@ export const updateCartPromotionsWorkflow = createWorkflow(
|
||||
updateCartPromotionsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO> => {
|
||||
const retrieveCartInput = {
|
||||
cartId: input.cartId,
|
||||
id: input.cartId,
|
||||
config: {
|
||||
relations: [
|
||||
"items",
|
||||
@@ -40,7 +44,7 @@ export const updateCartPromotionsWorkflow = createWorkflow(
|
||||
const actions = getActionsToComputeFromPromotionsStep({
|
||||
cart,
|
||||
promoCodes: input.promoCodes,
|
||||
removePromotions: input.removePromotions || false,
|
||||
action: input.action || PromotionActions.ADD,
|
||||
})
|
||||
|
||||
const {
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { CartDTO, UpdateCartWorkflowInputDTO } from "@medusajs/types"
|
||||
import { PromotionActions, isPresent } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createLineItemAdjustmentsStep,
|
||||
createShippingMethodAdjustmentsStep,
|
||||
findOneOrAnyRegionStep,
|
||||
findOrCreateCustomerStep,
|
||||
findSalesChannelStep,
|
||||
getActionsToComputeFromPromotionsStep,
|
||||
prepareAdjustmentsFromPromotionActionsStep,
|
||||
removeLineItemAdjustmentsStep,
|
||||
removeShippingMethodAdjustmentsStep,
|
||||
retrieveCartStep,
|
||||
updateCartsStep,
|
||||
} from "../steps"
|
||||
|
||||
export const updateCartWorkflowId = "update-cart"
|
||||
export const updateCartWorkflow = createWorkflow(
|
||||
updateCartWorkflowId,
|
||||
(input: WorkflowData<UpdateCartWorkflowInputDTO>): WorkflowData<CartDTO> => {
|
||||
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,
|
||||
}),
|
||||
findOneOrAnyRegionStep({
|
||||
regionId: input.region_id,
|
||||
}),
|
||||
findOrCreateCustomerStep({
|
||||
customerId: input.customer_id,
|
||||
email: input.email,
|
||||
})
|
||||
)
|
||||
|
||||
const cartInput = transform(
|
||||
{ input, region, customerData, salesChannel },
|
||||
(data) => {
|
||||
const { promo_codes, ...updateCartData } = data.input
|
||||
const data_ = { ...updateCartData }
|
||||
|
||||
if (isPresent(updateCartData.region_id)) {
|
||||
data_.currency_code = data.region.currency_code
|
||||
data_.region_id = data.region.id
|
||||
}
|
||||
|
||||
if (
|
||||
updateCartData.customer_id !== undefined ||
|
||||
updateCartData.email !== undefined
|
||||
) {
|
||||
data_.customer_id = data.customerData.customer?.id || null
|
||||
data_.email =
|
||||
data.input?.email ?? (data.customerData.customer?.email || null)
|
||||
}
|
||||
|
||||
if (updateCartData.sales_channel_id !== undefined) {
|
||||
data_.sales_channel_id = data.salesChannel?.id || null
|
||||
}
|
||||
|
||||
return data_
|
||||
}
|
||||
)
|
||||
|
||||
updateCartsStep([cartInput])
|
||||
|
||||
const cart = retrieveCartStep(retrieveCartInput)
|
||||
const actions = getActionsToComputeFromPromotionsStep({
|
||||
cart,
|
||||
promoCodes: input.promo_codes,
|
||||
action: PromotionActions.REPLACE,
|
||||
})
|
||||
|
||||
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",
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -1,20 +0,0 @@
|
||||
import {
|
||||
CartDTO,
|
||||
FilterableCartProps,
|
||||
UpdateCartDataDTO,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateCartsStep } from "../steps/update-carts"
|
||||
|
||||
type WorkflowInput = {
|
||||
selector: FilterableCartProps
|
||||
update: UpdateCartDataDTO
|
||||
}
|
||||
|
||||
export const updateCartsWorkflowId = "update-carts"
|
||||
export const updateCartsWorkflow = createWorkflow(
|
||||
updateCartsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO[]> => {
|
||||
return updateCartsStep(input)
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user