feat(utils,types): campaigns and campaign budgets + services CRUD (#6063)
* chore: added item/shipping adjustments for order/items/shipping_methods * chore: add validation for order type and target rules * chore: add comment for applied promotions * chore: add shipping method and item adjustments * chore: include applied promotions to items/shipping_method for each case * chore: handle case for items across and order to consider existing applications * chore: handle case for applied promo values to shipping => across * chore: added changeset * chore: update return of function * chore: campaigns and campaign budgets + services CRUD * Apply suggestions from code review Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> * chore: minor refactor * chore: added single/bulk interfaces * Apply suggestions from code review Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> * chore: use DAL date entity * chore: align nullable * Update packages/promotion/src/models/promotion-rule.ts * chore: fix types * chore: review changes * Update packages/promotion/src/utils/compute-actions/shipping-methods.ts Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
co-authored by
Oli Juhl
Philip Korsholm
parent
b782d3bcb7
commit
fade8ea7bf
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(utils,types): campaigns and campaign budgets + services CRUD
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { CampaignBudgetType } from "@medusajs/utils"
|
||||||
|
|
||||||
|
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,
|
||||||
|
used: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
used: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { CreateCampaignDTO } from "@medusajs/types"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { Campaign } from "@models"
|
||||||
|
import { defaultCampaignsData } from "./data"
|
||||||
|
|
||||||
|
export * from "./data"
|
||||||
|
|
||||||
|
export async function createCampaigns(
|
||||||
|
manager: SqlEntityManager,
|
||||||
|
campaignsData: CreateCampaignDTO[] = defaultCampaignsData
|
||||||
|
): Promise<Campaign[]> {
|
||||||
|
const campaigns: Campaign[] = []
|
||||||
|
|
||||||
|
for (let campaignData of campaignsData) {
|
||||||
|
let campaign = manager.create(Campaign, campaignData)
|
||||||
|
|
||||||
|
manager.persist(campaign)
|
||||||
|
|
||||||
|
await manager.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return campaigns
|
||||||
|
}
|
||||||
+241
@@ -0,0 +1,241 @@
|
|||||||
|
import { IPromotionModuleService } from "@medusajs/types"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { initialize } from "../../../../src"
|
||||||
|
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||||
|
import { DB_URL, MikroOrmWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
describe("Promotion Module Service: Campaigns", () => {
|
||||||
|
let service: IPromotionModuleService
|
||||||
|
let repositoryManager: SqlEntityManager
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await MikroOrmWrapper.setupDatabase()
|
||||||
|
repositoryManager = MikroOrmWrapper.forkManager()
|
||||||
|
|
||||||
|
service = await initialize({
|
||||||
|
database: {
|
||||||
|
clientUrl: DB_URL,
|
||||||
|
schema: process.env.MEDUSA_PROMOTION_DB_SCHEMA,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await MikroOrmWrapper.clearDatabase()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("createCampaigns", () => {
|
||||||
|
it("should throw an error when required params are not passed", async () => {
|
||||||
|
const error = await service
|
||||||
|
.createCampaigns([
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
} as any,
|
||||||
|
])
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain(
|
||||||
|
"Value for Campaign.campaign_identifier is required, 'undefined' found"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a basic campaign successfully", async () => {
|
||||||
|
const startsAt = new Date("01/01/2024")
|
||||||
|
const endsAt = new Date("01/01/2025")
|
||||||
|
const [createdCampaign] = await service.createCampaigns([
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test",
|
||||||
|
starts_at: startsAt,
|
||||||
|
ends_at: endsAt,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const campaign = await service.retrieveCampaign(createdCampaign.id)
|
||||||
|
|
||||||
|
expect(campaign).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test",
|
||||||
|
starts_at: startsAt,
|
||||||
|
ends_at: endsAt,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a campaign with campaign budget successfully", async () => {
|
||||||
|
const startsAt = new Date("01/01/2024")
|
||||||
|
const endsAt = new Date("01/01/2025")
|
||||||
|
|
||||||
|
const [createdPromotion] = await service.createCampaigns([
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test",
|
||||||
|
starts_at: startsAt,
|
||||||
|
ends_at: endsAt,
|
||||||
|
budget: {
|
||||||
|
limit: 1000,
|
||||||
|
type: "usage",
|
||||||
|
used: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const campaign = await service.retrieveCampaign(createdPromotion.id, {
|
||||||
|
relations: ["budget"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(campaign).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test",
|
||||||
|
starts_at: startsAt,
|
||||||
|
ends_at: endsAt,
|
||||||
|
budget: expect.objectContaining({
|
||||||
|
limit: 1000,
|
||||||
|
type: "usage",
|
||||||
|
used: 10,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("updateCampaigns", () => {
|
||||||
|
it("should throw an error when required params are not passed", async () => {
|
||||||
|
const error = await service
|
||||||
|
.updateCampaigns([
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
} as any,
|
||||||
|
])
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain('Campaign with id "" not found')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update the attributes of a campaign successfully", async () => {
|
||||||
|
await createCampaigns(repositoryManager)
|
||||||
|
|
||||||
|
const [updatedCampaign] = await service.updateCampaigns([
|
||||||
|
{
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
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"),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update the attributes of a campaign budget successfully", async () => {
|
||||||
|
await createCampaigns(repositoryManager)
|
||||||
|
|
||||||
|
const [updatedCampaign] = await service.updateCampaigns([
|
||||||
|
{
|
||||||
|
id: "campaign-id-1",
|
||||||
|
budget: {
|
||||||
|
limit: 100,
|
||||||
|
used: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(updatedCampaign).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
budget: expect.objectContaining({
|
||||||
|
limit: 100,
|
||||||
|
used: 100,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("retrieveCampaign", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await createCampaigns(repositoryManager)
|
||||||
|
})
|
||||||
|
|
||||||
|
const id = "campaign-id-1"
|
||||||
|
|
||||||
|
it("should return campaign for the given id", async () => {
|
||||||
|
const campaign = await service.retrieveCampaign(id)
|
||||||
|
|
||||||
|
expect(campaign).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when campaign with id does not exist", async () => {
|
||||||
|
let error
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.retrieveCampaign("does-not-exist")
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
"Campaign with id: does-not-exist was not found"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when a id is not provided", async () => {
|
||||||
|
let error
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.retrieveCampaign(undefined as unknown as string)
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('"campaignId" must be defined')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return campaign based on config select param", async () => {
|
||||||
|
const campaign = await service.retrieveCampaign(id, {
|
||||||
|
select: ["id"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const serialized = JSON.parse(JSON.stringify(campaign))
|
||||||
|
|
||||||
|
expect(serialized).toEqual({
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("deleteCampaigns", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await createCampaigns(repositoryManager)
|
||||||
|
})
|
||||||
|
|
||||||
|
const id = "campaign-id-1"
|
||||||
|
|
||||||
|
it("should delete the campaigns given an id successfully", async () => {
|
||||||
|
await service.deleteCampaigns([id])
|
||||||
|
|
||||||
|
const campaigns = await service.list({
|
||||||
|
id: [id],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(campaigns).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -3,6 +3,8 @@ module.exports = {
|
|||||||
"^@models": "<rootDir>/src/models",
|
"^@models": "<rootDir>/src/models",
|
||||||
"^@services": "<rootDir>/src/services",
|
"^@services": "<rootDir>/src/services",
|
||||||
"^@repositories": "<rootDir>/src/repositories",
|
"^@repositories": "<rootDir>/src/repositories",
|
||||||
|
"^@utils": "<rootDir>/src/utils",
|
||||||
|
"^@types": "<rootDir>/src/types",
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.[jt]s?$": [
|
"^.+\\.[jt]s?$": [
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ export default async ({
|
|||||||
applicationMethodService: asClass(
|
applicationMethodService: asClass(
|
||||||
defaultServices.ApplicationMethodService
|
defaultServices.ApplicationMethodService
|
||||||
).singleton(),
|
).singleton(),
|
||||||
|
campaignService: asClass(defaultServices.CampaignService).singleton(),
|
||||||
|
campaignBudgetService: asClass(
|
||||||
|
defaultServices.CampaignBudgetService
|
||||||
|
).singleton(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (customRepositories) {
|
if (customRepositories) {
|
||||||
@@ -56,5 +60,11 @@ function loadDefaultRepositories({ container }) {
|
|||||||
promotionRuleValueRepository: asClass(
|
promotionRuleValueRepository: asClass(
|
||||||
defaultRepositories.PromotionRuleValueRepository
|
defaultRepositories.PromotionRuleValueRepository
|
||||||
).singleton(),
|
).singleton(),
|
||||||
|
campaignRepository: asClass(
|
||||||
|
defaultRepositories.CampaignRepository
|
||||||
|
).singleton(),
|
||||||
|
campaignBudgetRepository: asClass(
|
||||||
|
defaultRepositories.CampaignBudgetRepository
|
||||||
|
).singleton(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
ApplicationMethodAllocationValues,
|
ApplicationMethodAllocationValues,
|
||||||
ApplicationMethodTargetTypeValues,
|
ApplicationMethodTargetTypeValues,
|
||||||
ApplicationMethodTypeValues,
|
ApplicationMethodTypeValues,
|
||||||
|
DAL,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
@@ -24,9 +25,8 @@ type OptionalFields =
|
|||||||
| "value"
|
| "value"
|
||||||
| "max_quantity"
|
| "max_quantity"
|
||||||
| "allocation"
|
| "allocation"
|
||||||
| "created_at"
|
|
||||||
| "updated_at"
|
|
||||||
| "deleted_at"
|
| "deleted_at"
|
||||||
|
| DAL.EntityDateColumns
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export default class ApplicationMethod {
|
export default class ApplicationMethod {
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { CampaignBudgetTypeValues, DAL } from "@medusajs/types"
|
||||||
|
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
BeforeCreate,
|
||||||
|
Entity,
|
||||||
|
Enum,
|
||||||
|
Index,
|
||||||
|
OnInit,
|
||||||
|
OneToOne,
|
||||||
|
OptionalProps,
|
||||||
|
PrimaryKey,
|
||||||
|
Property,
|
||||||
|
} from "@mikro-orm/core"
|
||||||
|
import Campaign from "./campaign"
|
||||||
|
|
||||||
|
type OptionalFields =
|
||||||
|
| "description"
|
||||||
|
| "limit"
|
||||||
|
| "used"
|
||||||
|
| "deleted_at"
|
||||||
|
| DAL.EntityDateColumns
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export default class CampaignBudget {
|
||||||
|
[OptionalProps]?: OptionalFields
|
||||||
|
|
||||||
|
@PrimaryKey({ columnType: "text" })
|
||||||
|
id!: string
|
||||||
|
|
||||||
|
@Index({ name: "IDX_campaign_budget_type" })
|
||||||
|
@Enum(() => PromotionUtils.CampaignBudgetType)
|
||||||
|
type: CampaignBudgetTypeValues
|
||||||
|
|
||||||
|
@OneToOne({
|
||||||
|
entity: () => Campaign,
|
||||||
|
})
|
||||||
|
campaign?: Campaign
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
columnType: "numeric",
|
||||||
|
nullable: true,
|
||||||
|
serializer: Number,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
limit: number | null
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
columnType: "numeric",
|
||||||
|
serializer: Number,
|
||||||
|
default: 0,
|
||||||
|
})
|
||||||
|
used: number
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
onCreate: () => new Date(),
|
||||||
|
columnType: "timestamptz",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
})
|
||||||
|
created_at: Date
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
onCreate: () => new Date(),
|
||||||
|
onUpdate: () => new Date(),
|
||||||
|
columnType: "timestamptz",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
})
|
||||||
|
updated_at: Date
|
||||||
|
|
||||||
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
|
deleted_at: Date | null
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
onCreate() {
|
||||||
|
this.id = generateEntityId(this.id, "probudg")
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "probudg")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { DAL } from "@medusajs/types"
|
||||||
|
import { generateEntityId } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
BeforeCreate,
|
||||||
|
Collection,
|
||||||
|
Entity,
|
||||||
|
OnInit,
|
||||||
|
OneToMany,
|
||||||
|
OneToOne,
|
||||||
|
OptionalProps,
|
||||||
|
PrimaryKey,
|
||||||
|
Property,
|
||||||
|
Unique,
|
||||||
|
} from "@mikro-orm/core"
|
||||||
|
import CampaignBudget from "./campaign-budget"
|
||||||
|
import Promotion from "./promotion"
|
||||||
|
|
||||||
|
type OptionalFields =
|
||||||
|
| "description"
|
||||||
|
| "currency"
|
||||||
|
| "starts_at"
|
||||||
|
| "ends_at"
|
||||||
|
| "deleted_at"
|
||||||
|
| DAL.EntityDateColumns
|
||||||
|
|
||||||
|
type OptionalRelations = "budget"
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export default class Campaign {
|
||||||
|
[OptionalProps]?: OptionalFields | OptionalRelations
|
||||||
|
|
||||||
|
@PrimaryKey({ columnType: "text" })
|
||||||
|
id!: string
|
||||||
|
|
||||||
|
@Property({ columnType: "text" })
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@Property({ columnType: "text", nullable: true })
|
||||||
|
description: string | null
|
||||||
|
|
||||||
|
@Property({ columnType: "text", nullable: true })
|
||||||
|
currency: string | null
|
||||||
|
|
||||||
|
@Property({ columnType: "text" })
|
||||||
|
@Unique({
|
||||||
|
name: "IDX_campaign_identifier_unique",
|
||||||
|
properties: ["campaign_identifier"],
|
||||||
|
})
|
||||||
|
campaign_identifier: string
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
columnType: "timestamptz",
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
starts_at: Date | null
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
columnType: "timestamptz",
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
ends_at: Date | null
|
||||||
|
|
||||||
|
@OneToOne({
|
||||||
|
entity: () => CampaignBudget,
|
||||||
|
mappedBy: (cb) => cb.campaign,
|
||||||
|
cascade: ["soft-remove"] as any,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
budget: CampaignBudget | null
|
||||||
|
|
||||||
|
@OneToMany(() => Promotion, (promotion) => promotion.campaign, {
|
||||||
|
orphanRemoval: true,
|
||||||
|
})
|
||||||
|
promotions = new Collection<Promotion>(this)
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
onCreate: () => new Date(),
|
||||||
|
columnType: "timestamptz",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
})
|
||||||
|
created_at: Date
|
||||||
|
|
||||||
|
@Property({
|
||||||
|
onCreate: () => new Date(),
|
||||||
|
onUpdate: () => new Date(),
|
||||||
|
columnType: "timestamptz",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
})
|
||||||
|
updated_at: Date
|
||||||
|
|
||||||
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
|
deleted_at: Date | null
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
onCreate() {
|
||||||
|
this.id = generateEntityId(this.id, "procamp")
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "procamp")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export { default as ApplicationMethod } from "./application-method"
|
export { default as ApplicationMethod } from "./application-method"
|
||||||
|
export { default as Campaign } from "./campaign"
|
||||||
|
export { default as CampaignBudget } from "./campaign-budget"
|
||||||
export { default as Promotion } from "./promotion"
|
export { default as Promotion } from "./promotion"
|
||||||
export { default as PromotionRule } from "./promotion-rule"
|
export { default as PromotionRule } from "./promotion-rule"
|
||||||
export { default as PromotionRuleValue } from "./promotion-rule-value"
|
export { default as PromotionRuleValue } from "./promotion-rule-value"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PromotionRuleOperatorValues } from "@medusajs/types"
|
import { DAL, PromotionRuleOperatorValues } from "@medusajs/types"
|
||||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
@@ -18,7 +18,7 @@ import ApplicationMethod from "./application-method"
|
|||||||
import Promotion from "./promotion"
|
import Promotion from "./promotion"
|
||||||
import PromotionRuleValue from "./promotion-rule-value"
|
import PromotionRuleValue from "./promotion-rule-value"
|
||||||
|
|
||||||
type OptionalFields = "description" | "created_at" | "updated_at" | "deleted_at"
|
type OptionalFields = "description" | "deleted_at" | DAL.EntityDateColumns
|
||||||
type OptionalRelations = "values" | "promotions"
|
type OptionalRelations = "values" | "promotions"
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PromotionType } from "@medusajs/types"
|
import { DAL, PromotionType } from "@medusajs/types"
|
||||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Enum,
|
Enum,
|
||||||
Index,
|
Index,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
|
ManyToOne,
|
||||||
OnInit,
|
OnInit,
|
||||||
OneToOne,
|
OneToOne,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
@@ -15,14 +16,12 @@ import {
|
|||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import ApplicationMethod from "./application-method"
|
import ApplicationMethod from "./application-method"
|
||||||
|
import Campaign from "./campaign"
|
||||||
import PromotionRule from "./promotion-rule"
|
import PromotionRule from "./promotion-rule"
|
||||||
|
|
||||||
type OptionalFields =
|
type OptionalFields = "is_automatic" | "deleted_at" | DAL.EntityDateColumns
|
||||||
| "is_automatic"
|
type OptionalRelations = "application_method" | "campaign"
|
||||||
| "created_at"
|
|
||||||
| "updated_at"
|
|
||||||
| "deleted_at"
|
|
||||||
type OptionalRelations = "application_method"
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export default class Promotion {
|
export default class Promotion {
|
||||||
[OptionalProps]?: OptionalFields | OptionalRelations
|
[OptionalProps]?: OptionalFields | OptionalRelations
|
||||||
@@ -38,6 +37,13 @@ export default class Promotion {
|
|||||||
})
|
})
|
||||||
code: string
|
code: string
|
||||||
|
|
||||||
|
@ManyToOne(() => Campaign, {
|
||||||
|
joinColumn: "campaign",
|
||||||
|
fieldName: "campaign_id",
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
campaign: Campaign
|
||||||
|
|
||||||
@Property({ columnType: "boolean", default: false })
|
@Property({ columnType: "boolean", default: false })
|
||||||
is_automatic?: boolean = false
|
is_automatic?: boolean = false
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { DALUtils } from "@medusajs/utils"
|
||||||
|
import { CampaignBudget } from "@models"
|
||||||
|
import { CreateCampaignBudgetDTO, UpdateCampaignBudgetDTO } from "@types"
|
||||||
|
|
||||||
|
export class CampaignBudgetRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
||||||
|
CampaignBudget,
|
||||||
|
{
|
||||||
|
create: CreateCampaignBudgetDTO
|
||||||
|
update: UpdateCampaignBudgetDTO
|
||||||
|
}
|
||||||
|
>(CampaignBudget) {}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { DALUtils } from "@medusajs/utils"
|
||||||
|
import { Campaign } from "@models"
|
||||||
|
import { CreateCampaignDTO, UpdateCampaignDTO } from "@types"
|
||||||
|
|
||||||
|
export class CampaignRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
||||||
|
Campaign,
|
||||||
|
{
|
||||||
|
create: CreateCampaignDTO
|
||||||
|
update: UpdateCampaignDTO
|
||||||
|
}
|
||||||
|
>(Campaign) {}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
|
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
|
||||||
export { ApplicationMethodRepository } from "./application-method"
|
export { ApplicationMethodRepository } from "./application-method"
|
||||||
|
export { CampaignRepository } from "./campaign"
|
||||||
|
export { CampaignBudgetRepository } from "./campaign-budget"
|
||||||
export { PromotionRepository } from "./promotion"
|
export { PromotionRepository } from "./promotion"
|
||||||
export { PromotionRuleRepository } from "./promotion-rule"
|
export { PromotionRuleRepository } from "./promotion-rule"
|
||||||
export { PromotionRuleValueRepository } from "./promotion-rule-value"
|
export { PromotionRuleValueRepository } from "./promotion-rule-value"
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import { Context, DAL, FindConfig, PromotionTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
InjectManager,
|
||||||
|
InjectTransactionManager,
|
||||||
|
MedusaContext,
|
||||||
|
ModulesSdkUtils,
|
||||||
|
retrieveEntity,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { CampaignBudget } from "@models"
|
||||||
|
import { CampaignBudgetRepository } from "@repositories"
|
||||||
|
import { CreateCampaignBudgetDTO, UpdateCampaignBudgetDTO } from "../types"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
campaignBudgetRepository: DAL.RepositoryService
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CampaignBudgetService<
|
||||||
|
TEntity extends CampaignBudget = CampaignBudget
|
||||||
|
> {
|
||||||
|
protected readonly campaignBudgetRepository_: DAL.RepositoryService
|
||||||
|
|
||||||
|
constructor({ campaignBudgetRepository }: InjectedDependencies) {
|
||||||
|
this.campaignBudgetRepository_ = campaignBudgetRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignBudgetRepository_")
|
||||||
|
async retrieve(
|
||||||
|
campaignBudgetId: string,
|
||||||
|
config: FindConfig<PromotionTypes.CampaignBudgetDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity> {
|
||||||
|
return (await retrieveEntity<
|
||||||
|
CampaignBudget,
|
||||||
|
PromotionTypes.CampaignBudgetDTO
|
||||||
|
>({
|
||||||
|
id: campaignBudgetId,
|
||||||
|
entityName: CampaignBudget.name,
|
||||||
|
repository: this.campaignBudgetRepository_,
|
||||||
|
config,
|
||||||
|
sharedContext,
|
||||||
|
})) as TEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignBudgetRepository_")
|
||||||
|
async list(
|
||||||
|
filters: PromotionTypes.FilterableCampaignBudgetProps = {},
|
||||||
|
config: FindConfig<PromotionTypes.CampaignBudgetDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
const queryOptions = ModulesSdkUtils.buildQuery<CampaignBudget>(
|
||||||
|
filters,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
return (await this.campaignBudgetRepository_.find(
|
||||||
|
queryOptions,
|
||||||
|
sharedContext
|
||||||
|
)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignBudgetRepository_")
|
||||||
|
async listAndCount(
|
||||||
|
filters: PromotionTypes.FilterableCampaignBudgetProps = {},
|
||||||
|
config: FindConfig<PromotionTypes.CampaignBudgetDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[TEntity[], number]> {
|
||||||
|
const queryOptions = ModulesSdkUtils.buildQuery<CampaignBudget>(
|
||||||
|
filters,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
return (await this.campaignBudgetRepository_.findAndCount(
|
||||||
|
queryOptions,
|
||||||
|
sharedContext
|
||||||
|
)) as [TEntity[], number]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignBudgetRepository_")
|
||||||
|
async create(
|
||||||
|
data: CreateCampaignBudgetDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
return (await (
|
||||||
|
this.campaignBudgetRepository_ as CampaignBudgetRepository
|
||||||
|
).create(data, sharedContext)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignBudgetRepository_")
|
||||||
|
async update(
|
||||||
|
data: UpdateCampaignBudgetDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
return (await (
|
||||||
|
this.campaignBudgetRepository_ as CampaignBudgetRepository
|
||||||
|
).update(data, sharedContext)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignBudgetRepository_")
|
||||||
|
async delete(
|
||||||
|
ids: string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
await this.campaignBudgetRepository_.delete(ids, sharedContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { Context, DAL, FindConfig, PromotionTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
InjectManager,
|
||||||
|
InjectTransactionManager,
|
||||||
|
MedusaContext,
|
||||||
|
ModulesSdkUtils,
|
||||||
|
retrieveEntity,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { Campaign } from "@models"
|
||||||
|
import { CampaignRepository } from "@repositories"
|
||||||
|
import { CreateCampaignDTO, UpdateCampaignDTO } from "../types"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
campaignRepository: DAL.RepositoryService
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CampaignService<TEntity extends Campaign = Campaign> {
|
||||||
|
protected readonly campaignRepository_: DAL.RepositoryService
|
||||||
|
|
||||||
|
constructor({ campaignRepository }: InjectedDependencies) {
|
||||||
|
this.campaignRepository_ = campaignRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignRepository_")
|
||||||
|
async retrieve(
|
||||||
|
campaignId: string,
|
||||||
|
config: FindConfig<PromotionTypes.CampaignDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity> {
|
||||||
|
return (await retrieveEntity<Campaign, PromotionTypes.CampaignDTO>({
|
||||||
|
id: campaignId,
|
||||||
|
entityName: Campaign.name,
|
||||||
|
repository: this.campaignRepository_,
|
||||||
|
config,
|
||||||
|
sharedContext,
|
||||||
|
})) as TEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignRepository_")
|
||||||
|
async list(
|
||||||
|
filters: PromotionTypes.FilterableCampaignProps = {},
|
||||||
|
config: FindConfig<PromotionTypes.CampaignDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
const queryOptions = ModulesSdkUtils.buildQuery<Campaign>(filters, config)
|
||||||
|
|
||||||
|
return (await this.campaignRepository_.find(
|
||||||
|
queryOptions,
|
||||||
|
sharedContext
|
||||||
|
)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("campaignRepository_")
|
||||||
|
async listAndCount(
|
||||||
|
filters: PromotionTypes.FilterableCampaignProps = {},
|
||||||
|
config: FindConfig<PromotionTypes.CampaignDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[TEntity[], number]> {
|
||||||
|
const queryOptions = ModulesSdkUtils.buildQuery<Campaign>(filters, config)
|
||||||
|
|
||||||
|
return (await this.campaignRepository_.findAndCount(
|
||||||
|
queryOptions,
|
||||||
|
sharedContext
|
||||||
|
)) as [TEntity[], number]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignRepository_")
|
||||||
|
async create(
|
||||||
|
data: CreateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
return (await (this.campaignRepository_ as CampaignRepository).create(
|
||||||
|
data,
|
||||||
|
sharedContext
|
||||||
|
)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignRepository_")
|
||||||
|
async update(
|
||||||
|
data: UpdateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
return (await (this.campaignRepository_ as CampaignRepository).update(
|
||||||
|
data,
|
||||||
|
sharedContext
|
||||||
|
)) as TEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("campaignRepository_")
|
||||||
|
async delete(
|
||||||
|
ids: string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
await this.campaignRepository_.delete(ids, sharedContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export { default as ApplicationMethodService } from "./application-method"
|
export { default as ApplicationMethodService } from "./application-method"
|
||||||
|
export { default as CampaignService } from "./campaign"
|
||||||
|
export { default as CampaignBudgetService } from "./campaign-budget"
|
||||||
export { default as PromotionService } from "./promotion"
|
export { default as PromotionService } from "./promotion"
|
||||||
export { default as PromotionModuleService } from "./promotion-module"
|
export { default as PromotionModuleService } from "./promotion-module"
|
||||||
export { default as PromotionRuleService } from "./promotion-rule"
|
export { default as PromotionRuleService } from "./promotion-rule"
|
||||||
|
|||||||
@@ -17,25 +17,31 @@ import {
|
|||||||
import { ApplicationMethod, Promotion } from "@models"
|
import { ApplicationMethod, Promotion } from "@models"
|
||||||
import {
|
import {
|
||||||
ApplicationMethodService,
|
ApplicationMethodService,
|
||||||
|
CampaignBudgetService,
|
||||||
|
CampaignService,
|
||||||
PromotionRuleService,
|
PromotionRuleService,
|
||||||
PromotionRuleValueService,
|
PromotionRuleValueService,
|
||||||
PromotionService,
|
PromotionService,
|
||||||
} from "@services"
|
} from "@services"
|
||||||
import { joinerConfig } from "../joiner-config"
|
|
||||||
import {
|
import {
|
||||||
CreateApplicationMethodDTO,
|
CreateApplicationMethodDTO,
|
||||||
|
CreateCampaignBudgetDTO,
|
||||||
|
CreateCampaignDTO,
|
||||||
CreatePromotionDTO,
|
CreatePromotionDTO,
|
||||||
CreatePromotionRuleDTO,
|
CreatePromotionRuleDTO,
|
||||||
UpdateApplicationMethodDTO,
|
UpdateApplicationMethodDTO,
|
||||||
|
UpdateCampaignBudgetDTO,
|
||||||
|
UpdateCampaignDTO,
|
||||||
UpdatePromotionDTO,
|
UpdatePromotionDTO,
|
||||||
} from "../types"
|
} from "@types"
|
||||||
import {
|
import {
|
||||||
ComputeActionUtils,
|
ComputeActionUtils,
|
||||||
allowedAllocationForQuantity,
|
allowedAllocationForQuantity,
|
||||||
areRulesValidForContext,
|
areRulesValidForContext,
|
||||||
validateApplicationMethodAttributes,
|
validateApplicationMethodAttributes,
|
||||||
validatePromotionRuleAttributes,
|
validatePromotionRuleAttributes,
|
||||||
} from "../utils"
|
} from "@utils"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -43,6 +49,8 @@ type InjectedDependencies = {
|
|||||||
applicationMethodService: ApplicationMethodService
|
applicationMethodService: ApplicationMethodService
|
||||||
promotionRuleService: PromotionRuleService
|
promotionRuleService: PromotionRuleService
|
||||||
promotionRuleValueService: PromotionRuleValueService
|
promotionRuleValueService: PromotionRuleValueService
|
||||||
|
campaignService: CampaignService
|
||||||
|
campaignBudgetService: CampaignBudgetService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PromotionModuleService<
|
export default class PromotionModuleService<
|
||||||
@@ -54,6 +62,8 @@ export default class PromotionModuleService<
|
|||||||
protected applicationMethodService_: ApplicationMethodService
|
protected applicationMethodService_: ApplicationMethodService
|
||||||
protected promotionRuleService_: PromotionRuleService
|
protected promotionRuleService_: PromotionRuleService
|
||||||
protected promotionRuleValueService_: PromotionRuleValueService
|
protected promotionRuleValueService_: PromotionRuleValueService
|
||||||
|
protected campaignService_: CampaignService
|
||||||
|
protected campaignBudgetService_: CampaignBudgetService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@@ -62,6 +72,8 @@ export default class PromotionModuleService<
|
|||||||
applicationMethodService,
|
applicationMethodService,
|
||||||
promotionRuleService,
|
promotionRuleService,
|
||||||
promotionRuleValueService,
|
promotionRuleValueService,
|
||||||
|
campaignService,
|
||||||
|
campaignBudgetService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
@@ -70,6 +82,8 @@ export default class PromotionModuleService<
|
|||||||
this.applicationMethodService_ = applicationMethodService
|
this.applicationMethodService_ = applicationMethodService
|
||||||
this.promotionRuleService_ = promotionRuleService
|
this.promotionRuleService_ = promotionRuleService
|
||||||
this.promotionRuleValueService_ = promotionRuleValueService
|
this.promotionRuleValueService_ = promotionRuleValueService
|
||||||
|
this.campaignService_ = campaignService
|
||||||
|
this.campaignBudgetService_ = campaignBudgetService
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): ModuleJoinerConfig {
|
__joinerConfig(): ModuleJoinerConfig {
|
||||||
@@ -271,15 +285,28 @@ export default class PromotionModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
async create(
|
||||||
|
data: PromotionTypes.CreatePromotionDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.PromotionDTO>
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
data: PromotionTypes.CreatePromotionDTO[],
|
data: PromotionTypes.CreatePromotionDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
sharedContext?: Context
|
||||||
): Promise<PromotionTypes.PromotionDTO[]> {
|
): Promise<PromotionTypes.PromotionDTO[]>
|
||||||
const promotions = await this.create_(data, sharedContext)
|
|
||||||
|
|
||||||
return await this.list(
|
@InjectManager("baseRepository_")
|
||||||
{ id: promotions.map((p) => p!.id) },
|
async create(
|
||||||
|
data:
|
||||||
|
| PromotionTypes.CreatePromotionDTO
|
||||||
|
| PromotionTypes.CreatePromotionDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.PromotionDTO | PromotionTypes.PromotionDTO[]> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
const createdPromotions = await this.create_(input, sharedContext)
|
||||||
|
|
||||||
|
const promotions = await this.list(
|
||||||
|
{ id: createdPromotions.map((p) => p!.id) },
|
||||||
{
|
{
|
||||||
relations: [
|
relations: [
|
||||||
"application_method",
|
"application_method",
|
||||||
@@ -291,6 +318,8 @@ export default class PromotionModuleService<
|
|||||||
},
|
},
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return Array.isArray(data) ? promotions : promotions[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -399,15 +428,28 @@ export default class PromotionModuleService<
|
|||||||
return createdPromotions
|
return createdPromotions
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
async update(
|
||||||
|
data: PromotionTypes.UpdatePromotionDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.PromotionDTO>
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
data: PromotionTypes.UpdatePromotionDTO[],
|
data: PromotionTypes.UpdatePromotionDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
sharedContext?: Context
|
||||||
): Promise<PromotionTypes.PromotionDTO[]> {
|
): Promise<PromotionTypes.PromotionDTO[]>
|
||||||
const promotions = await this.update_(data, sharedContext)
|
|
||||||
|
|
||||||
return await this.list(
|
@InjectManager("baseRepository_")
|
||||||
{ id: promotions.map((p) => p!.id) },
|
async update(
|
||||||
|
data:
|
||||||
|
| PromotionTypes.UpdatePromotionDTO
|
||||||
|
| PromotionTypes.UpdatePromotionDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.PromotionDTO | PromotionTypes.PromotionDTO[]> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
const updatedPromotions = await this.update_(input, sharedContext)
|
||||||
|
|
||||||
|
const promotions = await this.list(
|
||||||
|
{ id: updatedPromotions.map((p) => p!.id) },
|
||||||
{
|
{
|
||||||
relations: [
|
relations: [
|
||||||
"application_method",
|
"application_method",
|
||||||
@@ -418,6 +460,8 @@ export default class PromotionModuleService<
|
|||||||
},
|
},
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return Array.isArray(data) ? promotions : promotions[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -587,10 +631,12 @@ export default class PromotionModuleService<
|
|||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async delete(
|
async delete(
|
||||||
ids: string[],
|
ids: string | string[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.promotionService_.delete(ids, sharedContext)
|
const promotionIds = Array.isArray(ids) ? ids : [ids]
|
||||||
|
|
||||||
|
await this.promotionService_.delete(promotionIds, sharedContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
@@ -691,4 +737,214 @@ export default class PromotionModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async retrieveCampaign(
|
||||||
|
id: string,
|
||||||
|
config: FindConfig<PromotionTypes.CampaignDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.CampaignDTO> {
|
||||||
|
const campaign = await this.campaignService_.retrieve(
|
||||||
|
id,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<PromotionTypes.CampaignDTO>(
|
||||||
|
campaign,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listCampaigns(
|
||||||
|
filters: PromotionTypes.FilterableCampaignProps = {},
|
||||||
|
config: FindConfig<PromotionTypes.CampaignDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.CampaignDTO[]> {
|
||||||
|
const campaigns = await this.campaignService_.list(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<PromotionTypes.CampaignDTO[]>(
|
||||||
|
campaigns,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCampaigns(
|
||||||
|
data: PromotionTypes.CreateCampaignDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.CampaignDTO>
|
||||||
|
|
||||||
|
async createCampaigns(
|
||||||
|
data: PromotionTypes.CreateCampaignDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.CampaignDTO[]>
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async createCampaigns(
|
||||||
|
data: PromotionTypes.CreateCampaignDTO | PromotionTypes.CreateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.CampaignDTO | PromotionTypes.CampaignDTO[]> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
const createdCampaigns = await this.createCampaigns_(input, sharedContext)
|
||||||
|
|
||||||
|
const campaigns = await this.listCampaigns(
|
||||||
|
{ id: createdCampaigns.map((p) => p!.id) },
|
||||||
|
{
|
||||||
|
relations: ["budget"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return Array.isArray(data) ? campaigns : campaigns[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async createCampaigns_(
|
||||||
|
data: PromotionTypes.CreateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const campaignsData: CreateCampaignDTO[] = []
|
||||||
|
const campaignBudgetsData: CreateCampaignBudgetDTO[] = []
|
||||||
|
const campaignIdentifierBudgetMap = new Map<
|
||||||
|
string,
|
||||||
|
CreateCampaignBudgetDTO
|
||||||
|
>()
|
||||||
|
|
||||||
|
for (const createCampaignData of data) {
|
||||||
|
const { budget: campaignBudgetData, ...campaignData } = createCampaignData
|
||||||
|
|
||||||
|
if (campaignBudgetData) {
|
||||||
|
campaignIdentifierBudgetMap.set(
|
||||||
|
campaignData.campaign_identifier,
|
||||||
|
campaignBudgetData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
campaignsData.push(campaignData)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdCampaigns = await this.campaignService_.create(
|
||||||
|
campaignsData,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const createdCampaign of createdCampaigns) {
|
||||||
|
const campaignBudgetData = campaignIdentifierBudgetMap.get(
|
||||||
|
createdCampaign.campaign_identifier
|
||||||
|
)
|
||||||
|
|
||||||
|
if (campaignBudgetData) {
|
||||||
|
campaignBudgetsData.push({
|
||||||
|
...campaignBudgetData,
|
||||||
|
campaign: createdCampaign.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (campaignBudgetsData.length) {
|
||||||
|
await this.campaignBudgetService_.create(
|
||||||
|
campaignBudgetsData,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdCampaigns
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCampaigns(
|
||||||
|
data: PromotionTypes.UpdateCampaignDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.CampaignDTO>
|
||||||
|
|
||||||
|
async updateCampaigns(
|
||||||
|
data: PromotionTypes.UpdateCampaignDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionTypes.CampaignDTO[]>
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async updateCampaigns(
|
||||||
|
data: PromotionTypes.UpdateCampaignDTO | PromotionTypes.UpdateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.CampaignDTO | PromotionTypes.CampaignDTO[]> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
const updatedCampaigns = await this.updateCampaigns_(input, sharedContext)
|
||||||
|
|
||||||
|
const campaigns = await this.listCampaigns(
|
||||||
|
{ id: updatedCampaigns.map((p) => p!.id) },
|
||||||
|
{
|
||||||
|
relations: ["budget"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return Array.isArray(data) ? campaigns : campaigns[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async updateCampaigns_(
|
||||||
|
data: PromotionTypes.UpdateCampaignDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const campaignIds = data.map((d) => d.id)
|
||||||
|
const campaignsData: UpdateCampaignDTO[] = []
|
||||||
|
const campaignBudgetsData: UpdateCampaignBudgetDTO[] = []
|
||||||
|
|
||||||
|
const existingCampaigns = await this.listCampaigns(
|
||||||
|
{
|
||||||
|
id: campaignIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["budget"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const existingCampaignsMap = new Map<string, PromotionTypes.CampaignDTO>(
|
||||||
|
existingCampaigns.map((campaign) => [campaign.id, campaign])
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const updateCampaignData of data) {
|
||||||
|
const { budget: campaignBudgetData, ...campaignData } = updateCampaignData
|
||||||
|
|
||||||
|
const existingCampaign = existingCampaignsMap.get(campaignData.id)
|
||||||
|
const existingCampaignBudget = existingCampaign?.budget
|
||||||
|
|
||||||
|
campaignsData.push(campaignData)
|
||||||
|
|
||||||
|
if (existingCampaignBudget && campaignBudgetData) {
|
||||||
|
campaignBudgetsData.push({
|
||||||
|
id: existingCampaignBudget.id,
|
||||||
|
...campaignBudgetData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedCampaigns = await this.campaignService_.update(campaignsData)
|
||||||
|
|
||||||
|
if (campaignBudgetsData.length) {
|
||||||
|
await this.campaignBudgetService_.update(campaignBudgetsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedCampaigns
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async deleteCampaigns(
|
||||||
|
ids: string | string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
const campaignIds = Array.isArray(ids) ? ids : [ids]
|
||||||
|
|
||||||
|
await this.promotionService_.delete(campaignIds, sharedContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { CampaignBudgetTypeValues } from "@medusajs/types"
|
||||||
|
import { Campaign } from "@models"
|
||||||
|
|
||||||
|
export interface CreateCampaignBudgetDTO {
|
||||||
|
type: CampaignBudgetTypeValues
|
||||||
|
limit: number | null
|
||||||
|
used?: number
|
||||||
|
campaign?: Campaign | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCampaignBudgetDTO {
|
||||||
|
id: string
|
||||||
|
type?: CampaignBudgetTypeValues
|
||||||
|
limit?: number | null
|
||||||
|
used?: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export interface CreateCampaignDTO {
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
currency?: string
|
||||||
|
campaign_identifier: string
|
||||||
|
starts_at: Date
|
||||||
|
ends_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCampaignDTO {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
description?: string
|
||||||
|
currency?: string
|
||||||
|
campaign_identifier?: string
|
||||||
|
starts_at?: Date
|
||||||
|
ends_at?: Date
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ export type InitializeModuleInjectableDependencies = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from "./application-method"
|
export * from "./application-method"
|
||||||
|
export * from "./campaign"
|
||||||
|
export * from "./campaign-budget"
|
||||||
export * from "./promotion"
|
export * from "./promotion"
|
||||||
export * from "./promotion-rule"
|
export * from "./promotion-rule"
|
||||||
export * from "./promotion-rule-value"
|
export * from "./promotion-rule-value"
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { BaseFilterable } from "../../dal"
|
||||||
|
|
||||||
|
export type CampaignBudgetTypeValues = "spend" | "usage"
|
||||||
|
|
||||||
|
export interface CampaignBudgetDTO {
|
||||||
|
id: string
|
||||||
|
type?: CampaignBudgetTypeValues
|
||||||
|
limit?: string | null
|
||||||
|
used?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterableCampaignBudgetProps
|
||||||
|
extends BaseFilterable<FilterableCampaignBudgetProps> {
|
||||||
|
id?: string[]
|
||||||
|
type?: string[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { BaseFilterable } from "../../dal"
|
||||||
|
import { CampaignBudgetDTO } from "./campaign-budget"
|
||||||
|
|
||||||
|
export interface CampaignDTO {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
description?: string
|
||||||
|
currency?: string
|
||||||
|
campaign_identifier?: string
|
||||||
|
starts_at?: Date
|
||||||
|
ends_at?: Date
|
||||||
|
budget?: CampaignBudgetDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterableCampaignProps
|
||||||
|
extends BaseFilterable<FilterableCampaignProps> {
|
||||||
|
id?: string[]
|
||||||
|
campaign_identifier?: string[]
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export * from "./application-method"
|
export * from "./application-method"
|
||||||
|
export * from "./campaign"
|
||||||
|
export * from "./campaign-budget"
|
||||||
export * from "./compute-actions"
|
export * from "./compute-actions"
|
||||||
export * from "./promotion"
|
export * from "./promotion"
|
||||||
export * from "./promotion-rule"
|
export * from "./promotion-rule"
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./common"
|
export * from "./common"
|
||||||
|
export * from "./mutations"
|
||||||
export * from "./service"
|
export * from "./service"
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { CampaignBudgetTypeValues } from "./common"
|
||||||
|
|
||||||
|
export interface CreateCampaignBudgetDTO {
|
||||||
|
type: CampaignBudgetTypeValues
|
||||||
|
limit: number | null
|
||||||
|
used?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCampaignBudgetDTO {
|
||||||
|
id: string
|
||||||
|
type?: CampaignBudgetTypeValues
|
||||||
|
limit?: number | null
|
||||||
|
used?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateCampaignDTO {
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
currency?: string
|
||||||
|
campaign_identifier: string
|
||||||
|
starts_at: Date
|
||||||
|
ends_at: Date
|
||||||
|
budget?: CreateCampaignBudgetDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCampaignDTO {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
description?: string
|
||||||
|
currency?: string
|
||||||
|
campaign_identifier?: string
|
||||||
|
starts_at?: Date
|
||||||
|
ends_at?: Date
|
||||||
|
budget?: Omit<UpdateCampaignBudgetDTO, "id">
|
||||||
|
}
|
||||||
@@ -2,16 +2,20 @@ import { FindConfig } from "../common"
|
|||||||
import { IModuleService } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
import {
|
import {
|
||||||
|
CampaignDTO,
|
||||||
ComputeActionContext,
|
ComputeActionContext,
|
||||||
ComputeActions,
|
ComputeActions,
|
||||||
CreatePromotionDTO,
|
CreatePromotionDTO,
|
||||||
CreatePromotionRuleDTO,
|
CreatePromotionRuleDTO,
|
||||||
|
FilterableCampaignProps,
|
||||||
FilterablePromotionProps,
|
FilterablePromotionProps,
|
||||||
PromotionDTO,
|
PromotionDTO,
|
||||||
RemovePromotionRuleDTO,
|
RemovePromotionRuleDTO,
|
||||||
UpdatePromotionDTO,
|
UpdatePromotionDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
|
import { CreateCampaignDTO, UpdateCampaignDTO } from "./mutations"
|
||||||
|
|
||||||
export interface IPromotionModuleService extends IModuleService {
|
export interface IPromotionModuleService extends IModuleService {
|
||||||
computeActions(
|
computeActions(
|
||||||
promotionCodesToApply: string[],
|
promotionCodesToApply: string[],
|
||||||
@@ -24,11 +28,21 @@ export interface IPromotionModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PromotionDTO[]>
|
): Promise<PromotionDTO[]>
|
||||||
|
|
||||||
|
create(
|
||||||
|
data: CreatePromotionDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
update(
|
update(
|
||||||
data: UpdatePromotionDTO[],
|
data: UpdatePromotionDTO[],
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PromotionDTO[]>
|
): Promise<PromotionDTO[]>
|
||||||
|
|
||||||
|
update(
|
||||||
|
data: UpdatePromotionDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
list(
|
list(
|
||||||
filters?: FilterablePromotionProps,
|
filters?: FilterablePromotionProps,
|
||||||
config?: FindConfig<PromotionDTO>,
|
config?: FindConfig<PromotionDTO>,
|
||||||
@@ -42,6 +56,7 @@ export interface IPromotionModuleService extends IModuleService {
|
|||||||
): Promise<PromotionDTO>
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
delete(ids: string[], sharedContext?: Context): Promise<void>
|
delete(ids: string[], sharedContext?: Context): Promise<void>
|
||||||
|
delete(ids: string, sharedContext?: Context): Promise<void>
|
||||||
|
|
||||||
addPromotionRules(
|
addPromotionRules(
|
||||||
promotionId: string,
|
promotionId: string,
|
||||||
@@ -66,4 +81,39 @@ export interface IPromotionModuleService extends IModuleService {
|
|||||||
rulesData: RemovePromotionRuleDTO[],
|
rulesData: RemovePromotionRuleDTO[],
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PromotionDTO>
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
|
createCampaigns(
|
||||||
|
data: CreateCampaignDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO>
|
||||||
|
|
||||||
|
createCampaigns(
|
||||||
|
data: CreateCampaignDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO[]>
|
||||||
|
|
||||||
|
updateCampaigns(
|
||||||
|
data: UpdateCampaignDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO[]>
|
||||||
|
|
||||||
|
updateCampaigns(
|
||||||
|
data: UpdateCampaignDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO>
|
||||||
|
|
||||||
|
listCampaigns(
|
||||||
|
filters?: FilterableCampaignProps,
|
||||||
|
config?: FindConfig<CampaignDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO[]>
|
||||||
|
|
||||||
|
retrieveCampaign(
|
||||||
|
id: string,
|
||||||
|
config?: FindConfig<CampaignDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CampaignDTO>
|
||||||
|
|
||||||
|
deleteCampaigns(ids: string[], sharedContext?: Context): Promise<void>
|
||||||
|
deleteCampaigns(ids: string, sharedContext?: Context): Promise<void>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,8 @@ export enum PromotionRuleOperator {
|
|||||||
NE = "ne",
|
NE = "ne",
|
||||||
IN = "in",
|
IN = "in",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum CampaignBudgetType {
|
||||||
|
SPEND = "spend",
|
||||||
|
USAGE = "usage",
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user