Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/chilled-radios-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
---

feat(medusa,core-flows): update cart adjustments on item updates
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ describe("Store Carts API: Add promotions to cart", () => {
id: "item-1",
adjustments: expect.arrayContaining([
expect.objectContaining({
promotion_id: createdPromotion.id,
code: createdPromotion.code,
amount: 1000,
}),
Expand All @@ -144,7 +143,6 @@ describe("Store Carts API: Add promotions to cart", () => {
adjustments: expect.arrayContaining([
expect.objectContaining({
id: expect.not.stringContaining(lineItemAdjustment.id),
promotion_id: appliedPromotion.id,
code: appliedPromotion.code,
amount: 300,
}),
Expand Down
64 changes: 60 additions & 4 deletions integration-tests/plugins/__tests__/cart/store/carts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,10 +557,6 @@ describe("Store Carts API", () => {

describe("POST /store/carts/:id/line-items", () => {
it("should add item to cart", async () => {
const cart = await cartModuleService.create({
currency_code: "usd",
})

const [product] = await productModule.create([
{
title: "Test product",
Expand All @@ -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({
prices: [
{
Expand Down Expand Up @@ -608,6 +646,24 @@ describe("Store Carts API", () => {
unit_price: 3000,
quantity: 1,
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,
}),
],
}),
]),
})
Expand Down
1 change: 0 additions & 1 deletion packages/core-flows/src/definition/cart/steps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ export * from "./remove-shipping-method-adjustments"
export * from "./retrieve-cart"
export * from "./update-carts"
export * from "./validate-variants-existence"

Original file line number Diff line number Diff line change
@@ -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({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love this! 🎆

input: {
action,
promoCodes: promo_codes,
cartId: id,
},
})

return new StepResponse(null)
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getVariantsStep,
validateVariantsExistStep,
} from "../steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
import { prepareLineItemData } from "../utils/prepare-line-item-data"

// TODO: The AddToCartWorkflow are missing the following steps:
Expand Down Expand Up @@ -70,6 +71,8 @@ export const addToCartWorkflow = createWorkflow(

const items = addToCartStep({ items: lineItems })

refreshCartPromotionsStep({ id: input.cart.id })

return items
}
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CartDTO } from "@medusajs/types"
import { PromotionActions } from "@medusajs/utils"
import {
WorkflowData,
Expand Down Expand Up @@ -27,7 +26,7 @@ type WorkflowInput = {
export const updateCartPromotionsWorkflowId = "update-cart-promotions"
export const updateCartPromotionsWorkflow = createWorkflow(
updateCartPromotionsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO> => {
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
const retrieveCartInput = {
id: input.cartId,
config: {
Expand Down Expand Up @@ -65,9 +64,5 @@ export const updateCartPromotionsWorkflow = createWorkflow(
createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }),
createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate })
)

return retrieveCartStep(retrieveCartInput).config({
name: "retrieve-cart-result-step",
})
}
)
59 changes: 16 additions & 43 deletions packages/core-flows/src/definition/cart/workflows/update-cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,18 @@ import {
transform,
} from "@medusajs/workflows-sdk"
import {
createLineItemAdjustmentsStep,
createShippingMethodAdjustmentsStep,
findOneOrAnyRegionStep,
findOrCreateCustomerStep,
findSalesChannelStep,
getActionsToComputeFromPromotionsStep,
prepareAdjustmentsFromPromotionActionsStep,
removeLineItemAdjustmentsStep,
removeShippingMethodAdjustmentsStep,
retrieveCartStep,
updateCartsStep,
} from "../steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"

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,
Expand Down Expand Up @@ -79,34 +62,24 @@ export const updateCartWorkflow = createWorkflow(

updateCartsStep([cartInput])

const cart = retrieveCartStep(retrieveCartInput)
const actions = getActionsToComputeFromPromotionsStep({
cart,
promoCodes: input.promo_codes,
refreshCartPromotionsStep({
id: input.id,
promo_codes: input.promo_codes,
action: PromotionActions.REPLACE,
})

const {
lineItemAdjustmentsToCreate,
lineItemAdjustmentIdsToRemove,
shippingMethodAdjustmentsToCreate,
shippingMethodAdjustmentIdsToRemove,
} = prepareAdjustmentsFromPromotionActionsStep({ actions })

parallelize(
removeLineItemAdjustmentsStep({ lineItemAdjustmentIdsToRemove }),
removeShippingMethodAdjustmentsStep({
shippingMethodAdjustmentIdsToRemove,
})
)

parallelize(
createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }),
createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate })
)
const retrieveCartInput = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Can we do this? I didn't know that. Thought everything had to be either a step or transformer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We changed it recently to allow plain objects as well. You only need the transformer if its an unresolved object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one can't keep up with so many improvements 😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, let's keep it that way 🏎️

id: input.id,
config: {
relations: [
"items",
"items.adjustments",
"shipping_methods",
"shipping_methods.adjustments",
],
},
}

return retrieveCartStep(retrieveCartInput).config({
name: "retrieve-cart-result-step",
})
return retrieveCartStep(retrieveCartInput)
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "@medusajs/workflows-sdk"
import { getVariantPriceSetsStep } from ".."
import { updateLineItemsStep } from "../../line-item/steps"
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"

// TODO: The UpdateLineItemsWorkflow are missing the following steps:
// - Confirm inventory exists (inventory module)
Expand Down Expand Up @@ -55,6 +56,8 @@ export const updateLineItemInCartWorkflow = createWorkflow(
selector: lineItemUpdate.selector,
})

refreshCartPromotionsStep({ id: input.cart.id })

const updatedItem = transform({ result }, (data) => data.result?.[0])

return updatedItem
Expand Down
31 changes: 26 additions & 5 deletions packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { updateCartPromotionsWorkflow } from "@medusajs/core-flows"
import { PromotionActions } from "@medusajs/utils"
import { PromotionActions, remoteQueryObjectFromString } from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
import { defaultStoreCartFields } from "../../query-config"
import { StorePostCartsCartPromotionsReq } from "../../validators"

export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const workflow = updateCartPromotionsWorkflow(req.scope)
const payload = req.validatedBody as StorePostCartsCartPromotionsReq

const { result, errors } = await workflow.run({
const { errors } = await workflow.run({
input: {
promoCodes: payload.promo_codes,
cartId: req.params.id,
Expand All @@ -20,14 +22,24 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
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) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const workflow = updateCartPromotionsWorkflow(req.scope)
const payload = req.validatedBody as StorePostCartsCartPromotionsReq

const { result, errors } = await workflow.run({
const { errors } = await workflow.run({
input: {
promoCodes: payload.promo_codes,
cartId: req.params.id,
Expand All @@ -40,5 +52,14 @@ export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
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 })
}
Loading