feat(medusa,core-flows): update cart adjustments on item updates (#6539)

This commit is contained in:
Riqwan Thamir
2024-02-28 19:35:24 +01:00
committed by GitHub
parent adad66e13f
commit 557d86afbf
10 changed files with 145 additions and 61 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
---
feat(medusa,core-flows): update cart adjustments on item updates
@@ -134,7 +134,6 @@ describe("Store Carts API: Add promotions to cart", () => {
id: "item-1", id: "item-1",
adjustments: expect.arrayContaining([ adjustments: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
promotion_id: createdPromotion.id,
code: createdPromotion.code, code: createdPromotion.code,
amount: 1000, amount: 1000,
}), }),
@@ -144,7 +143,6 @@ describe("Store Carts API: Add promotions to cart", () => {
adjustments: expect.arrayContaining([ adjustments: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: expect.not.stringContaining(lineItemAdjustment.id), id: expect.not.stringContaining(lineItemAdjustment.id),
promotion_id: appliedPromotion.id,
code: appliedPromotion.code, code: appliedPromotion.code,
amount: 300, amount: 300,
}), }),
@@ -557,10 +557,6 @@ describe("Store Carts API", () => {
describe("POST /store/carts/:id/line-items", () => { describe("POST /store/carts/:id/line-items", () => {
it("should add item to cart", async () => { it("should add item to cart", async () => {
const cart = await cartModuleService.create({
currency_code: "usd",
})
const [product] = await productModule.create([ const [product] = await productModule.create([
{ {
title: "Test product", 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({ const priceSet = await pricingModule.create({
prices: [ prices: [
{ {
@@ -608,6 +646,24 @@ describe("Store Carts API", () => {
unit_price: 3000, unit_price: 3000,
quantity: 1, quantity: 1,
title: "Test variant", 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,
}),
],
}), }),
]), ]),
}) })
@@ -14,4 +14,3 @@ export * from "./remove-shipping-method-adjustments"
export * from "./retrieve-cart" export * from "./retrieve-cart"
export * from "./update-carts" export * from "./update-carts"
export * from "./validate-variants-existence" export * from "./validate-variants-existence"
@@ -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)
}
)
@@ -13,6 +13,7 @@ import {
getVariantsStep, getVariantsStep,
validateVariantsExistStep, validateVariantsExistStep,
} from "../steps" } from "../steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
import { prepareLineItemData } from "../utils/prepare-line-item-data" import { prepareLineItemData } from "../utils/prepare-line-item-data"
// TODO: The AddToCartWorkflow are missing the following steps: // TODO: The AddToCartWorkflow are missing the following steps:
@@ -70,6 +71,8 @@ export const addToCartWorkflow = createWorkflow(
const items = addToCartStep({ items: lineItems }) const items = addToCartStep({ items: lineItems })
refreshCartPromotionsStep({ id: input.cart.id })
return items return items
} }
) )
@@ -1,4 +1,3 @@
import { CartDTO } from "@medusajs/types"
import { PromotionActions } from "@medusajs/utils" import { PromotionActions } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
@@ -27,7 +26,7 @@ type WorkflowInput = {
export const updateCartPromotionsWorkflowId = "update-cart-promotions" export const updateCartPromotionsWorkflowId = "update-cart-promotions"
export const updateCartPromotionsWorkflow = createWorkflow( export const updateCartPromotionsWorkflow = createWorkflow(
updateCartPromotionsWorkflowId, updateCartPromotionsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO> => { (input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
const retrieveCartInput = { const retrieveCartInput = {
id: input.cartId, id: input.cartId,
config: { config: {
@@ -65,9 +64,5 @@ export const updateCartPromotionsWorkflow = createWorkflow(
createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }), createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }),
createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate }) createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate })
) )
return retrieveCartStep(retrieveCartInput).config({
name: "retrieve-cart-result-step",
})
} }
) )
@@ -7,35 +7,18 @@ import {
transform, transform,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { import {
createLineItemAdjustmentsStep,
createShippingMethodAdjustmentsStep,
findOneOrAnyRegionStep, findOneOrAnyRegionStep,
findOrCreateCustomerStep, findOrCreateCustomerStep,
findSalesChannelStep, findSalesChannelStep,
getActionsToComputeFromPromotionsStep,
prepareAdjustmentsFromPromotionActionsStep,
removeLineItemAdjustmentsStep,
removeShippingMethodAdjustmentsStep,
retrieveCartStep, retrieveCartStep,
updateCartsStep, updateCartsStep,
} from "../steps" } from "../steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
export const updateCartWorkflowId = "update-cart" export const updateCartWorkflowId = "update-cart"
export const updateCartWorkflow = createWorkflow( export const updateCartWorkflow = createWorkflow(
updateCartWorkflowId, updateCartWorkflowId,
(input: WorkflowData<UpdateCartWorkflowInputDTO>): WorkflowData<CartDTO> => { (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( const [salesChannel, region, customerData] = parallelize(
findSalesChannelStep({ findSalesChannelStep({
salesChannelId: input.sales_channel_id, salesChannelId: input.sales_channel_id,
@@ -79,34 +62,24 @@ export const updateCartWorkflow = createWorkflow(
updateCartsStep([cartInput]) updateCartsStep([cartInput])
const cart = retrieveCartStep(retrieveCartInput) refreshCartPromotionsStep({
const actions = getActionsToComputeFromPromotionsStep({ id: input.id,
cart, promo_codes: input.promo_codes,
promoCodes: input.promo_codes,
action: PromotionActions.REPLACE, action: PromotionActions.REPLACE,
}) })
const { const retrieveCartInput = {
lineItemAdjustmentsToCreate, id: input.id,
lineItemAdjustmentIdsToRemove, config: {
shippingMethodAdjustmentsToCreate, relations: [
shippingMethodAdjustmentIdsToRemove, "items",
} = prepareAdjustmentsFromPromotionActionsStep({ actions }) "items.adjustments",
"shipping_methods",
"shipping_methods.adjustments",
],
},
}
parallelize( return retrieveCartStep(retrieveCartInput)
removeLineItemAdjustmentsStep({ lineItemAdjustmentIdsToRemove }),
removeShippingMethodAdjustmentsStep({
shippingMethodAdjustmentIdsToRemove,
})
)
parallelize(
createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }),
createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate })
)
return retrieveCartStep(retrieveCartInput).config({
name: "retrieve-cart-result-step",
})
} }
) )
@@ -6,6 +6,7 @@ import {
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { getVariantPriceSetsStep } from ".." import { getVariantPriceSetsStep } from ".."
import { updateLineItemsStep } from "../../line-item/steps" import { updateLineItemsStep } from "../../line-item/steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
// TODO: The UpdateLineItemsWorkflow are missing the following steps: // TODO: The UpdateLineItemsWorkflow are missing the following steps:
// - Confirm inventory exists (inventory module) // - Confirm inventory exists (inventory module)
@@ -55,6 +56,8 @@ export const updateLineItemInCartWorkflow = createWorkflow(
selector: lineItemUpdate.selector, selector: lineItemUpdate.selector,
}) })
refreshCartPromotionsStep({ id: input.cart.id })
const updatedItem = transform({ result }, (data) => data.result?.[0]) const updatedItem = transform({ result }, (data) => data.result?.[0])
return updatedItem return updatedItem
@@ -1,13 +1,15 @@
import { updateCartPromotionsWorkflow } from "@medusajs/core-flows" import { updateCartPromotionsWorkflow } from "@medusajs/core-flows"
import { PromotionActions } from "@medusajs/utils" import { PromotionActions, remoteQueryObjectFromString } from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
import { defaultStoreCartFields } from "../../query-config"
import { StorePostCartsCartPromotionsReq } from "../../validators" import { StorePostCartsCartPromotionsReq } from "../../validators"
export const POST = async (req: MedusaRequest, res: MedusaResponse) => { export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const workflow = updateCartPromotionsWorkflow(req.scope) const workflow = updateCartPromotionsWorkflow(req.scope)
const payload = req.validatedBody as StorePostCartsCartPromotionsReq const payload = req.validatedBody as StorePostCartsCartPromotionsReq
const { result, errors } = await workflow.run({ const { errors } = await workflow.run({
input: { input: {
promoCodes: payload.promo_codes, promoCodes: payload.promo_codes,
cartId: req.params.id, cartId: req.params.id,
@@ -20,14 +22,24 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
throw errors[0].error 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) => { export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const workflow = updateCartPromotionsWorkflow(req.scope) const workflow = updateCartPromotionsWorkflow(req.scope)
const payload = req.validatedBody as StorePostCartsCartPromotionsReq const payload = req.validatedBody as StorePostCartsCartPromotionsReq
const { result, errors } = await workflow.run({ const { errors } = await workflow.run({
input: { input: {
promoCodes: payload.promo_codes, promoCodes: payload.promo_codes,
cartId: req.params.id, cartId: req.params.id,
@@ -40,5 +52,14 @@ export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
throw errors[0].error 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 })
} }