-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
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 907635c
Merge branch 'develop' into feat/cart-promotions
riqwan 737162f
chore: rename step
riqwan 5a2bc5d
chore: fix type issue with transform
riqwan cfe0c73
fix types
adrien2p 154b0da
Merge branch 'feat/cart-promotions' of github.com:medusajs/medusa int…
adrien2p cc2fc2c
chore: add specs for shipping methods + fixes for shipping method in …
riqwan 0f08d03
chore: rename step
riqwan 7b87884
merge with latest develop
riqwan e55c9f8
chore: fix specs
riqwan 78afec5
Merge branch 'develop' into feat/cart-promotions
riqwan 4baf8b5
chore: fix promotions spec
riqwan cf6d1b6
chore: reenable restore for deletes
riqwan 4e988d0
chore: fix type in cart module service
riqwan 50caab9
chore: add remove promotion from cart api
riqwan 93979a0
chore: address review
riqwan 6ba41d9
Merge branch 'develop' into feat/cart-promotions
riqwan 3e21a83
Merge branch 'develop' into feat/cart-promotions
riqwan d628ea7
Apply suggestions from code review
riqwan 6553213
chore: use soft delete + review changes
riqwan 5ed3ca3
chore: use new big number utils
riqwan 9618798
chore: use different middleware validator for delete
riqwan ebf9a03
Merge branch 'develop' into feat/cart-promotions
riqwan 2f848b5
Merge branch 'develop' into feat/cart-promotions
riqwan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
309 changes: 309 additions & 0 deletions
309
integration-tests/plugins/__tests__/cart/store/add-promotions-to-cart.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}), | ||
], | ||
}), | ||
]), | ||
}) | ||
) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.