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(workflows-sdk,core-flows,medusa,types): add workflow to update promotions to cart #6474

Merged
merged 24 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6e4505e
chore: added workflow to add promotion to cart
riqwan Feb 21, 2024
907635c
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 22, 2024
737162f
chore: rename step
riqwan Feb 22, 2024
5a2bc5d
chore: fix type issue with transform
riqwan Feb 22, 2024
cfe0c73
fix types
adrien2p Feb 22, 2024
154b0da
Merge branch 'feat/cart-promotions' of github.com:medusajs/medusa int…
adrien2p Feb 22, 2024
cc2fc2c
chore: add specs for shipping methods + fixes for shipping method in …
riqwan Feb 22, 2024
0f08d03
chore: rename step
riqwan Feb 22, 2024
7b87884
merge with latest develop
riqwan Feb 22, 2024
e55c9f8
chore: fix specs
riqwan Feb 23, 2024
78afec5
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 23, 2024
4baf8b5
chore: fix promotions spec
riqwan Feb 23, 2024
cf6d1b6
chore: reenable restore for deletes
riqwan Feb 23, 2024
4e988d0
chore: fix type in cart module service
riqwan Feb 23, 2024
50caab9
chore: add remove promotion from cart api
riqwan Feb 23, 2024
93979a0
chore: address review
riqwan Feb 23, 2024
6ba41d9
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 23, 2024
3e21a83
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 25, 2024
d628ea7
Apply suggestions from code review
riqwan Feb 25, 2024
6553213
chore: use soft delete + review changes
riqwan Feb 26, 2024
5ed3ca3
chore: use new big number utils
riqwan Feb 26, 2024
9618798
chore: use different middleware validator for delete
riqwan Feb 26, 2024
ebf9a03
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 26, 2024
2f848b5
Merge branch 'develop' into feat/cart-promotions
riqwan Feb 26, 2024
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
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: [
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can create some fixtures that you can reuse, esp. in tests where the creation is just a matter of preparing for the actual test.

// 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
Loading