feat(workflows-sdk,core-flows,medusa,types): add workflow to update promotions 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>
This commit is contained in:
co-authored by
Adrien de Peretti
parent
63be07031b
commit
ac86362e81
@@ -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,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,300 @@
|
||||
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: Remove promotions from 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("DELETE /store/carts/:id/promotions", () => {
|
||||
it("should remove line item adjustments from 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 appliedPromotionToRemove = await promotionModuleService.create({
|
||||
code: "PROMOTION_APPLIED_TO_REMOVE",
|
||||
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 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,
|
||||
{
|
||||
id: "item-2",
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
title: "Test item",
|
||||
product_id: "prod_tshirt",
|
||||
} as any,
|
||||
],
|
||||
})
|
||||
|
||||
const [adjustment1, adjustment2] =
|
||||
await cartModuleService.addLineItemAdjustments([
|
||||
{
|
||||
code: appliedPromotion.code!,
|
||||
amount: 300,
|
||||
item_id: "item-2",
|
||||
promotion_id: appliedPromotionToRemove.id,
|
||||
},
|
||||
{
|
||||
code: appliedPromotionToRemove.code!,
|
||||
amount: 150,
|
||||
item_id: "item-1",
|
||||
promotion_id: appliedPromotionToRemove.id,
|
||||
},
|
||||
{
|
||||
code: appliedPromotionToRemove.code!,
|
||||
amount: 150,
|
||||
item_id: "item-2",
|
||||
promotion_id: appliedPromotionToRemove.id,
|
||||
},
|
||||
])
|
||||
|
||||
const api = useApi() as any
|
||||
|
||||
const response = await api.delete(`/store/carts/${cart.id}/promotions`, {
|
||||
data: {
|
||||
promo_codes: [appliedPromotionToRemove.code],
|
||||
},
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.cart).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
items: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "item-1",
|
||||
adjustments: [],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "item-2",
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
amount: 300,
|
||||
code: appliedPromotion.code,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
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 appliedPromotionToRemove = await promotionModuleService.create({
|
||||
code: "PROMOTION_APPLIED_TO_REMOVE",
|
||||
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 cart = await cartModuleService.create({
|
||||
currency_code: "eur",
|
||||
customer_id: "cus_test",
|
||||
items: [
|
||||
{
|
||||
unit_price: 2000,
|
||||
quantity: 1,
|
||||
title: "Test item",
|
||||
product_id: "prod_mat",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const [express, standard] = await cartModuleService.addShippingMethods(
|
||||
cart.id,
|
||||
[
|
||||
{
|
||||
amount: 500,
|
||||
name: "express",
|
||||
},
|
||||
{
|
||||
amount: 500,
|
||||
name: "standard",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
await cartModuleService.addShippingMethodAdjustments(cart.id, [
|
||||
{
|
||||
shipping_method_id: express.id,
|
||||
amount: 100,
|
||||
code: appliedPromotion.code!,
|
||||
},
|
||||
{
|
||||
shipping_method_id: express.id,
|
||||
amount: 100,
|
||||
code: appliedPromotionToRemove.code!,
|
||||
},
|
||||
])
|
||||
|
||||
const api = useApi() as any
|
||||
|
||||
const response = await api.delete(`/store/carts/${cart.id}/promotions`, {
|
||||
data: { promo_codes: [appliedPromotionToRemove.code] },
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.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.objectContaining({
|
||||
amount: 100,
|
||||
code: appliedPromotion.code,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: standard.id,
|
||||
adjustments: [],
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user