feat(dashboard,medusa): Promotion Campaign fixes (#7337)
* chore(medusa): strict zod versions in workspace * feat(dashboard): add campaign create to promotion UI * wip * fix(medusa): Missing middlewares export (#7289) * fix(docblock-generator): fix how type names created from Zod objects are inferred (#7292) * feat(api-ref): show schema of a tag (#7297) * feat: Add support for sendgrid and logger notification providers (#7290) * feat: Add support for sendgrid and logger notification providers * fix: changes based on PR review * chore: add action to automatically label docs (#7284) * chore: add action to automatically label docs * removes the paths param * docs: preparations for preview (#7267) * configured base paths + added development banner * fix typelist site url * added navbar and sidebar badges * configure algolia filters * remove AI assistant * remove unused imports * change navbar text and badge * lint fixes * fix build error * add to api reference rewrites * fix build error * fix build errors in user-guide * fix feedback component * add parent title to pagination * added breadcrumbs component * remove user-guide links * resolve todos * fix details about authentication * change documentation title * lint content * chore: fix bug with form reset * chore: address reviews * chore: fix specs * chore: loads of FE fixes + BE adds * chore: add more polishes + reorg files * chore: fixes to promotions modal * chore: cleanup * chore: cleanup * chore: fix build * chore: fkix cart spec * chore: fix module tests * chore: fix moar tests * wip * chore: templates + fixes + migrate currency * chore: fix build, add validation for max_quantity * chore: allow removing campaigns * chore: fix specs * chore: scope campaigns based on currency * remove console logs * chore: add translations + update keys * chore: move over filesfrom v2 to routes * chore(dashboard): Delete old translation files (#7423) * feat(dashboard,admin-sdk,admin-shared,admin-vite-plugin): Add support for UI extensions (#7383) * intial work * update lock * add routes and fix HMR of configs * cleanup * rm imports * rm debug from plugin * address feedback * address feedback * temp skip specs --------- Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com> Co-authored-by: Shahed Nasser <shahednasser@gmail.com> Co-authored-by: Stevche Radevski <sradevski@live.com> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com>
This commit is contained in:
72 files changed
+5380
-3473
No files matched your search
@@ -5,13 +5,13 @@ export const defaultCampaignsData = [
|
||||
id: "campaign-id-1",
|
||||
name: "campaign 1",
|
||||
description: "test description",
|
||||
currency: "USD",
|
||||
campaign_identifier: "test-1",
|
||||
starts_at: new Date("01/01/2023"),
|
||||
ends_at: new Date("01/01/2024"),
|
||||
budget: {
|
||||
type: CampaignBudgetType.SPEND,
|
||||
limit: 1000,
|
||||
currency_code: "USD",
|
||||
used: 0,
|
||||
},
|
||||
},
|
||||
@@ -19,13 +19,13 @@ export const defaultCampaignsData = [
|
||||
id: "campaign-id-2",
|
||||
name: "campaign 1",
|
||||
description: "test description",
|
||||
currency: "USD",
|
||||
campaign_identifier: "test-2",
|
||||
starts_at: new Date("01/01/2023"),
|
||||
ends_at: new Date("01/01/2024"),
|
||||
budget: {
|
||||
type: CampaignBudgetType.USAGE,
|
||||
limit: 1000,
|
||||
currency_code: "USD",
|
||||
used: 0,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
import { CreatePromotionDTO } from "@medusajs/types"
|
||||
import { PromotionType } from "@medusajs/utils"
|
||||
|
||||
export const defaultPromotionsData = [
|
||||
export const defaultPromotionsData: CreatePromotionDTO[] = [
|
||||
{
|
||||
id: "promotion-id-1",
|
||||
code: "PROMOTION_1",
|
||||
type: PromotionType.STANDARD,
|
||||
application_method: {
|
||||
currency_code: "USD",
|
||||
target_type: "items",
|
||||
type: "fixed",
|
||||
allocation: "across",
|
||||
value: 1000,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "promotion-id-2",
|
||||
code: "PROMOTION_2",
|
||||
type: PromotionType.STANDARD,
|
||||
application_method: {
|
||||
currency_code: "USD",
|
||||
target_type: "items",
|
||||
type: "fixed",
|
||||
allocation: "across",
|
||||
value: 1000,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,4 +1,9 @@
|
||||
import { CreatePromotionDTO } from "@medusajs/types"
|
||||
import {
|
||||
CreatePromotionDTO,
|
||||
IPromotionModuleService,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/types"
|
||||
import { isPresent } from "@medusajs/utils"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { Promotion } from "@models"
|
||||
import { defaultPromotionsData } from "./data"
|
||||
@@ -21,3 +26,47 @@ export async function createPromotions(
|
||||
|
||||
return promotions
|
||||
}
|
||||
|
||||
export async function createDefaultPromotions(
|
||||
service: IPromotionModuleService,
|
||||
promotionsData: Partial<CreatePromotionDTO>[] = defaultPromotionsData
|
||||
): Promise<Promotion[]> {
|
||||
const promotions: Promotion[] = []
|
||||
|
||||
for (let promotionData of promotionsData) {
|
||||
let promotion = await createDefaultPromotion(service, promotionData)
|
||||
|
||||
promotions.push(promotion)
|
||||
}
|
||||
|
||||
return promotions
|
||||
}
|
||||
|
||||
export async function createDefaultPromotion(
|
||||
service: IPromotionModuleService,
|
||||
data: Partial<CreatePromotionDTO>
|
||||
): Promise<PromotionDTO> {
|
||||
const { application_method = {}, campaign = {}, ...promotion } = data
|
||||
|
||||
return await service.create({
|
||||
code: "PROMOTION_TEST",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-1",
|
||||
...promotion,
|
||||
application_method: {
|
||||
currency_code: "USD",
|
||||
target_type: "items",
|
||||
type: "fixed",
|
||||
allocation: "across",
|
||||
value: 1000,
|
||||
...application_method,
|
||||
},
|
||||
campaign: isPresent(campaign)
|
||||
? {
|
||||
campaign_identifier: "campaign-identifier",
|
||||
name: "new campaign",
|
||||
...campaign,
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
}
|
||||
+54
-4
@@ -1,6 +1,7 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { CampaignBudgetType } from "../../../../../../core/utils/src/promotion/index"
|
||||
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||
import { createPromotions } from "../../../__fixtures__/promotion"
|
||||
|
||||
@@ -27,7 +28,7 @@ moduleIntegrationTestRunner({
|
||||
id: "campaign-id-1",
|
||||
name: "campaign 1",
|
||||
description: "test description",
|
||||
currency: "USD",
|
||||
|
||||
campaign_identifier: "test-1",
|
||||
starts_at: expect.any(Date),
|
||||
ends_at: expect.any(Date),
|
||||
@@ -40,7 +41,7 @@ moduleIntegrationTestRunner({
|
||||
id: "campaign-id-2",
|
||||
name: "campaign 1",
|
||||
description: "test description",
|
||||
currency: "USD",
|
||||
|
||||
campaign_identifier: "test-2",
|
||||
starts_at: expect.any(Date),
|
||||
ends_at: expect.any(Date),
|
||||
@@ -92,6 +93,26 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when required budget params are not met", async () => {
|
||||
const error = await service
|
||||
.createCampaigns([
|
||||
{
|
||||
name: "test",
|
||||
campaign_identifier: "test",
|
||||
budget: {
|
||||
limit: 1000,
|
||||
type: "spend",
|
||||
used: 10,
|
||||
},
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toContain(
|
||||
"Campaign Budget type is a required field"
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a basic campaign successfully", async () => {
|
||||
const startsAt = new Date("01/01/2024")
|
||||
const endsAt = new Date("01/01/2025")
|
||||
@@ -174,7 +195,6 @@ moduleIntegrationTestRunner({
|
||||
{
|
||||
id: "campaign-id-1",
|
||||
description: "test description 1",
|
||||
currency: "EUR",
|
||||
campaign_identifier: "new",
|
||||
starts_at: new Date("01/01/2024"),
|
||||
ends_at: new Date("01/01/2025"),
|
||||
@@ -184,7 +204,6 @@ moduleIntegrationTestRunner({
|
||||
expect(updatedCampaign).toEqual(
|
||||
expect.objectContaining({
|
||||
description: "test description 1",
|
||||
currency: "EUR",
|
||||
campaign_identifier: "new",
|
||||
starts_at: new Date("01/01/2024"),
|
||||
ends_at: new Date("01/01/2025"),
|
||||
@@ -214,6 +233,37 @@ moduleIntegrationTestRunner({
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a campaign budget if not present successfully", async () => {
|
||||
await createCampaigns(MikroOrmWrapper.forkManager(), [
|
||||
{
|
||||
id: "campaign-id-new",
|
||||
name: "campaign 1",
|
||||
description: "test description",
|
||||
campaign_identifier: "test-1",
|
||||
} as any,
|
||||
])
|
||||
|
||||
const [updatedCampaign] = await service.updateCampaigns([
|
||||
{
|
||||
id: "campaign-id-new",
|
||||
budget: {
|
||||
type: CampaignBudgetType.SPEND,
|
||||
limit: 100,
|
||||
used: 100,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(updatedCampaign).toEqual(
|
||||
expect.objectContaining({
|
||||
budget: expect.objectContaining({
|
||||
limit: 100,
|
||||
used: 100,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveCampaign", () => {
|
||||
|
||||
+1524
-1849
File diff suppressed because it is too large.
Load diff
+291
-496
File diff suppressed because it is too large.
Load diff
+5
-16
@@ -2,6 +2,7 @@ import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||
import { createDefaultPromotion } from "../../../__fixtures__/promotion"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -18,11 +19,7 @@ moduleIntegrationTestRunner({
|
||||
|
||||
describe("registerUsage", () => {
|
||||
it("should register usage for type spend", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_SPEND",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-1",
|
||||
})
|
||||
const createdPromotion = await createDefaultPromotion(service, {})
|
||||
|
||||
await service.registerUsage([
|
||||
{
|
||||
@@ -53,9 +50,7 @@ moduleIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should register usage for type usage", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_USAGE",
|
||||
type: "standard",
|
||||
const createdPromotion = await createDefaultPromotion(service, {
|
||||
campaign_id: "campaign-id-2",
|
||||
})
|
||||
|
||||
@@ -103,9 +98,7 @@ moduleIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should not register usage when limit is exceed for type usage", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_USAGE",
|
||||
type: "standard",
|
||||
const createdPromotion = await createDefaultPromotion(service, {
|
||||
campaign_id: "campaign-id-2",
|
||||
})
|
||||
|
||||
@@ -144,11 +137,7 @@ moduleIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should not register usage above limit when exceeded for type spend", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_SPEND",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-1",
|
||||
})
|
||||
const createdPromotion = await createDefaultPromotion(service, {})
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-1",
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
import { PromotionType } from "@medusajs/utils"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { createPromotions } from "../../../__fixtures__/promotion"
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PROMOTION,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IPromotionModuleService>) => {
|
||||
describe("Promotion Service", () => {
|
||||
beforeEach(async () => {
|
||||
await createPromotions(MikroOrmWrapper.forkManager())
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should throw an error when required params are not passed", async () => {
|
||||
const error = await service
|
||||
.create([
|
||||
{
|
||||
type: PromotionType.STANDARD,
|
||||
} as any,
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toContain(
|
||||
"Value for Promotion.code is required, 'undefined' found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a promotion successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
},
|
||||
])
|
||||
|
||||
const [promotion] = await service.list({
|
||||
code: ["PROMOTION_TEST"],
|
||||
})
|
||||
|
||||
expect(promotion).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "PROMOTION_TEST",
|
||||
is_automatic: false,
|
||||
type: "standard",
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
Reference in new issue
Block a user