Skip to content

Commit

Permalink
feat(workflows-sdk,core-flows,medusa,types): add workflow to update p…
Browse files Browse the repository at this point in the history
…romotions 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>
  • Loading branch information
riqwan and adrien2p authored Feb 26, 2024
1 parent 63be070 commit ac86362
Show file tree
Hide file tree
Showing 30 changed files with 1,187 additions and 101 deletions.
8 changes: 8 additions & 0 deletions .changeset/clean-llamas-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@medusajs/workflows-sdk": patch
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
"@medusajs/types": patch
---

feat(workflows-sdk,core-flows,medusa,types): add workflow to add promotions to cart
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { ICartModuleService, IPromotionModuleService } from "@medusajs/types"
import { PromotionType } from "@medusajs/utils"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import { getContainer } from "../../../../environment-helpers/use-container"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import adminSeeder from "../../../../helpers/admin-seeder"

jest.setTimeout(50000)

const env = { MEDUSA_FF_MEDUSA_V2: true }

describe("Store Carts API: Add promotions to cart", () => {
let dbConnection
let appContainer
let shutdownServer
let cartModuleService: ICartModuleService
let promotionModuleService: IPromotionModuleService

beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd, env } as any)
shutdownServer = await startBootstrapApp({ cwd, env })
appContainer = getContainer()
cartModuleService = appContainer.resolve(ModuleRegistrationName.CART)
promotionModuleService = appContainer.resolve(
ModuleRegistrationName.PROMOTION
)
})

afterAll(async () => {
const db = useDb()
await db.shutdown()
await shutdownServer()
})

beforeEach(async () => {
await adminSeeder(dbConnection)
})

afterEach(async () => {
const db = useDb()
await db.teardown()
})

describe("POST /store/carts/:id/promotions", () => {
it("should add line item adjustments to a cart based on promotions", async () => {
const appliedPromotion = await promotionModuleService.create({
code: "PROMOTION_APPLIED",
type: PromotionType.STANDARD,
application_method: {
type: "fixed",
target_type: "items",
allocation: "each",
value: "300",
apply_to_quantity: 1,
max_quantity: 1,
target_rules: [
{
attribute: "product_id",
operator: "eq",
values: "prod_tshirt",
},
],
},
})

const createdPromotion = await promotionModuleService.create({
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
application_method: {
type: "fixed",
target_type: "items",
allocation: "across",
value: "1000",
apply_to_quantity: 1,
target_rules: [
{
attribute: "product_id",
operator: "eq",
values: "prod_mat",
},
],
},
})

const cart = await cartModuleService.create({
currency_code: "usd",
items: [
// Adjustment to add
{
id: "item-1",
unit_price: 2000,
quantity: 1,
title: "Test item",
product_id: "prod_mat",
} as any,
// This adjustment will be removed and recreated
{
id: "item-2",
unit_price: 1000,
quantity: 1,
title: "Test item",
product_id: "prod_tshirt",
} as any,
],
})

// Adjustment to keep
const [lineItemAdjustment] =
await cartModuleService.addLineItemAdjustments([
{
code: appliedPromotion.code!,
amount: 300,
item_id: "item-2",
promotion_id: appliedPromotion.id,
},
])

const api = useApi() as any

const created = await api.post(`/store/carts/${cart.id}/promotions`, {
promo_codes: [createdPromotion.code],
})

expect(created.status).toEqual(200)
expect(created.data.cart).toEqual(
expect.objectContaining({
id: expect.any(String),
items: expect.arrayContaining([
expect.objectContaining({
id: "item-1",
adjustments: expect.arrayContaining([
expect.objectContaining({
promotion_id: createdPromotion.id,
code: createdPromotion.code,
amount: 1000,
}),
]),
}),
expect.objectContaining({
adjustments: expect.arrayContaining([
expect.objectContaining({
id: expect.not.stringContaining(lineItemAdjustment.id),
promotion_id: appliedPromotion.id,
code: appliedPromotion.code,
amount: 300,
}),
]),
}),
]),
})
)
})

it("should add shipping method adjustments to a cart based on promotions", async () => {
const [appliedPromotion] = await promotionModuleService.create([
{
code: "PROMOTION_APPLIED",
type: PromotionType.STANDARD,
rules: [
{
attribute: "customer_id",
operator: "in",
values: ["cus_test"],
},
{
attribute: "currency_code",
operator: "in",
values: ["eur"],
},
],
application_method: {
type: "fixed",
target_type: "shipping_methods",
allocation: "each",
value: "100",
max_quantity: 1,
target_rules: [
{
attribute: "name",
operator: "in",
values: ["express"],
},
],
},
},
])

const [newPromotion] = await promotionModuleService.create([
{
code: "PROMOTION_NEW",
type: PromotionType.STANDARD,
rules: [
{
attribute: "customer_id",
operator: "in",
values: ["cus_test"],
},
{
attribute: "currency_code",
operator: "in",
values: ["eur"],
},
],
application_method: {
type: "fixed",
target_type: "shipping_methods",
allocation: "each",
value: "200",
max_quantity: 1,
target_rules: [
{
attribute: "name",
operator: "in",
values: ["express", "standard"],
},
],
},
},
])

const cart = await cartModuleService.create({
currency_code: "eur",
customer_id: "cus_test",
items: [
{
unit_price: 2000,
quantity: 1,
title: "Test item",
product_id: "prod_mat",
} as any,
],
})

const [express, standard] = await cartModuleService.addShippingMethods(
cart.id,
[
{
amount: 500,
name: "express",
},
{
amount: 500,
name: "standard",
},
]
)

const [adjustment] = await cartModuleService.addShippingMethodAdjustments(
cart.id,
[
{
shipping_method_id: express.id,
amount: 100,
code: appliedPromotion.code!,
},
]
)

const api = useApi() as any

const created = await api.post(`/store/carts/${cart.id}/promotions`, {
promo_codes: [newPromotion.code],
})

expect(created.status).toEqual(200)
expect(created.data.cart).toEqual(
expect.objectContaining({
id: expect.any(String),
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
}),
]),
shipping_methods: expect.arrayContaining([
expect.objectContaining({
id: express.id,
adjustments: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
amount: 200,
code: newPromotion.code,
}),
expect.objectContaining({
id: expect.not.stringContaining(adjustment.id),
amount: 100,
code: appliedPromotion.code,
}),
]),
}),
expect.objectContaining({
id: standard.id,
adjustments: [
expect.objectContaining({
id: expect.any(String),
amount: 200,
code: newPromotion.code,
}),
],
}),
]),
})
)
})
})
})
Loading

0 comments on commit ac86362

Please sign in to comment.