feat(medusa,types): added buyget support for modules (#6159)
This commit is contained in:
1 parent
302323916b
commit
68d8daccd2
22 files changed
+1058
-109
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,types): added buyget support for modules
|
||||||
@@ -62,7 +62,7 @@ describe("POST /admin/promotions", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a promotion successfully", async () => {
|
it("should create a standard promotion successfully", async () => {
|
||||||
const api = useApi() as any
|
const api = useApi() as any
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/promotions`,
|
`/admin/promotions`,
|
||||||
@@ -148,4 +148,194 @@ describe("POST /admin/promotions", () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should throw an error if buy_rules params are not passed", async () => {
|
||||||
|
const api = useApi() as any
|
||||||
|
const { response } = await api
|
||||||
|
.post(
|
||||||
|
`/admin/promotions`,
|
||||||
|
{
|
||||||
|
code: "TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
is_automatic: true,
|
||||||
|
application_method: {
|
||||||
|
target_type: "items",
|
||||||
|
type: "fixed",
|
||||||
|
allocation: "each",
|
||||||
|
value: "100",
|
||||||
|
max_quantity: 100,
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(400)
|
||||||
|
expect(response.data.message).toEqual(
|
||||||
|
"Buy rules are required for buyget promotion type"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error if buy_rules params are not passed", async () => {
|
||||||
|
const api = useApi() as any
|
||||||
|
const { response } = await api
|
||||||
|
.post(
|
||||||
|
`/admin/promotions`,
|
||||||
|
{
|
||||||
|
code: "TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
is_automatic: true,
|
||||||
|
application_method: {
|
||||||
|
target_type: "items",
|
||||||
|
type: "fixed",
|
||||||
|
allocation: "each",
|
||||||
|
value: "100",
|
||||||
|
max_quantity: 100,
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(400)
|
||||||
|
expect(response.data.message).toEqual(
|
||||||
|
"Target rules are required for buyget promotion type"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a buyget promotion successfully", async () => {
|
||||||
|
const api = useApi() as any
|
||||||
|
const response = await api.post(
|
||||||
|
`/admin/promotions`,
|
||||||
|
{
|
||||||
|
code: "TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
is_automatic: true,
|
||||||
|
campaign: {
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test-1",
|
||||||
|
budget: {
|
||||||
|
type: "usage",
|
||||||
|
limit: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
application_method: {
|
||||||
|
target_type: "items",
|
||||||
|
type: "fixed",
|
||||||
|
allocation: "each",
|
||||||
|
value: "100",
|
||||||
|
max_quantity: 100,
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test.test",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["test1", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.promotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: "TEST",
|
||||||
|
type: "buyget",
|
||||||
|
is_automatic: true,
|
||||||
|
campaign: expect.objectContaining({
|
||||||
|
name: "test",
|
||||||
|
campaign_identifier: "test-1",
|
||||||
|
budget: expect.objectContaining({
|
||||||
|
type: "usage",
|
||||||
|
limit: 100,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
application_method: expect.objectContaining({
|
||||||
|
value: 100,
|
||||||
|
max_quantity: 100,
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "each",
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
target_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
operator: "eq",
|
||||||
|
attribute: "test.test",
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: "test1" }),
|
||||||
|
expect.objectContaining({ value: "test2" }),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
buy_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
operator: "eq",
|
||||||
|
attribute: "test.test",
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: "test1" }),
|
||||||
|
expect.objectContaining({ value: "test2" }),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
operator: "eq",
|
||||||
|
attribute: "test.test",
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: "test1" }),
|
||||||
|
expect.objectContaining({ value: "test2" }),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -74,28 +74,30 @@ describe("GET /admin/promotions", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.promotion).toEqual({
|
expect(response.data.promotion).toEqual(
|
||||||
id: expect.any(String),
|
expect.objectContaining({
|
||||||
code: "TEST",
|
|
||||||
campaign: null,
|
|
||||||
is_automatic: false,
|
|
||||||
type: "standard",
|
|
||||||
created_at: expect.any(String),
|
|
||||||
updated_at: expect.any(String),
|
|
||||||
deleted_at: null,
|
|
||||||
application_method: {
|
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
promotion: expect.any(Object),
|
code: "TEST",
|
||||||
value: 100,
|
campaign: null,
|
||||||
type: "fixed",
|
is_automatic: false,
|
||||||
target_type: "order",
|
type: "standard",
|
||||||
max_quantity: 0,
|
|
||||||
allocation: null,
|
|
||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
updated_at: expect.any(String),
|
updated_at: expect.any(String),
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
},
|
application_method: expect.objectContaining({
|
||||||
})
|
id: expect.any(String),
|
||||||
|
promotion: expect.any(Object),
|
||||||
|
value: 100,
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "order",
|
||||||
|
max_quantity: 0,
|
||||||
|
allocation: null,
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should get the requested promotion with filtered fields and relations", async () => {
|
it("should get the requested promotion with filtered fields and relations", async () => {
|
||||||
|
|||||||
@@ -132,4 +132,58 @@ describe("POST /admin/promotions/:id", () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update a buyget promotion successfully", async () => {
|
||||||
|
const createdPromotion = await promotionModuleService.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: "prod_mat",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const response = await api.post(
|
||||||
|
`/admin/promotions/${createdPromotion.id}`,
|
||||||
|
{
|
||||||
|
code: "TEST_TWO",
|
||||||
|
application_method: {
|
||||||
|
value: "200",
|
||||||
|
buy_rules_min_quantity: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.promotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: "TEST_TWO",
|
||||||
|
application_method: expect.objectContaining({
|
||||||
|
value: 200,
|
||||||
|
buy_rules_min_quantity: 6,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { PromotionTypeValues } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
ApplicationMethodAllocation,
|
ApplicationMethodAllocation,
|
||||||
ApplicationMethodTargetType,
|
ApplicationMethodTargetType,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
Validate,
|
Validate,
|
||||||
|
ValidateIf,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||||
@@ -43,7 +45,7 @@ export class AdminPostPromotionsReq {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(PromotionType)
|
@IsEnum(PromotionType)
|
||||||
type?: PromotionType
|
type?: PromotionTypeValues
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -56,8 +58,8 @@ export class AdminPostPromotionsReq {
|
|||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => ApplicationMethod)
|
@Type(() => ApplicationMethodsPostReq)
|
||||||
application_method: ApplicationMethod
|
application_method: ApplicationMethodsPostReq
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@@ -83,7 +85,7 @@ export class PromotionRule {
|
|||||||
values: string[]
|
values: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ApplicationMethod {
|
export class ApplicationMethodsPostReq {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
description?: string
|
description?: string
|
||||||
@@ -113,6 +115,68 @@ export class ApplicationMethod {
|
|||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => PromotionRule)
|
@Type(() => PromotionRule)
|
||||||
target_rules?: PromotionRule[]
|
target_rules?: PromotionRule[]
|
||||||
|
|
||||||
|
@ValidateIf((data) => data.type === PromotionType.BUYGET)
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => PromotionRule)
|
||||||
|
buy_rules?: PromotionRule[]
|
||||||
|
|
||||||
|
@ValidateIf((data) => data.type === PromotionType.BUYGET)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
apply_to_quantity?: number
|
||||||
|
|
||||||
|
@ValidateIf((data) => data.type === PromotionType.BUYGET)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
buy_rules_min_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApplicationMethodsMethodPostReq {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
value?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
max_quantity?: number
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ApplicationMethodType)
|
||||||
|
type?: ApplicationMethodType
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ApplicationMethodTargetType)
|
||||||
|
target_type?: ApplicationMethodTargetType
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ApplicationMethodAllocation)
|
||||||
|
allocation?: ApplicationMethodAllocation
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => PromotionRule)
|
||||||
|
target_rules?: PromotionRule[]
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => PromotionRule)
|
||||||
|
buy_rules?: PromotionRule[]
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
apply_to_quantity?: number
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
buy_rules_min_quantity?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdminPostPromotionsPromotionReq {
|
export class AdminPostPromotionsPromotionReq {
|
||||||
@@ -141,8 +205,8 @@ export class AdminPostPromotionsPromotionReq {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => ApplicationMethod)
|
@Type(() => ApplicationMethodsMethodPostReq)
|
||||||
application_method?: ApplicationMethod
|
application_method?: ApplicationMethodsMethodPostReq
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { ValidatorOptions } from "class-validator"
|
||||||
import { NextFunction, Request, Response } from "express"
|
import { NextFunction, Request, Response } from "express"
|
||||||
import { ClassConstructor } from "../../types/global"
|
import { ClassConstructor } from "../../types/global"
|
||||||
import { ValidatorOptions } from "class-validator"
|
|
||||||
import { validator } from "../../utils/validator"
|
import { validator } from "../../utils/validator"
|
||||||
|
|
||||||
export function transformBody<T>(
|
export function transformBody<T>(
|
||||||
|
|||||||
+374
@@ -428,6 +428,199 @@ describe("Promotion Service", () => {
|
|||||||
"rules[].operator (doesnotexist) is invalid. It should be one of gte, lte, gt, lt, eq, ne, in"
|
"rules[].operator (doesnotexist) is invalid. It should be one of gte, lte, gt, lt, eq, ne, in"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should create a basic buyget promotion successfully", async () => {
|
||||||
|
const createdPromotion = await service
|
||||||
|
.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
const [promotion] = await service.list({
|
||||||
|
id: [createdPromotion.id],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(promotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
is_automatic: false,
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when target_rules are not present for buyget promotion", async () => {
|
||||||
|
const error = await service
|
||||||
|
.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain(
|
||||||
|
"Target rules are required for buyget promotion type"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when buy_rules are not present for buyget promotion", async () => {
|
||||||
|
const error = await service
|
||||||
|
.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain(
|
||||||
|
"Buy rules are required for buyget promotion type"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when apply_to_quantity is not present for buyget promotion", async () => {
|
||||||
|
const error = await service
|
||||||
|
.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["prod_mat"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain(
|
||||||
|
"apply_to_quantity is a required field for Promotion type of buyget"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when buy_rules_min_quantity is not present for buyget promotion", async () => {
|
||||||
|
const error = await service
|
||||||
|
.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["prod_mat"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.message).toContain(
|
||||||
|
"buy_rules_min_quantity is a required field for Promotion type of buyget"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a buyget promotion with rules successfully", async () => {
|
||||||
|
const createdPromotion = await service.create({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: "100",
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: "prod_mat",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(createdPromotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: "PROMOTION_TEST",
|
||||||
|
is_automatic: false,
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: expect.objectContaining({
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "across",
|
||||||
|
value: 100,
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
target_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: [expect.objectContaining({ value: "prod_mat" })],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
buy_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: [expect.objectContaining({ value: "pcol_towel" })],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
@@ -966,6 +1159,103 @@ describe("Promotion Service", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("addPromotionBuyRules", () => {
|
||||||
|
let promotion
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
;[promotion] = await service.create([
|
||||||
|
{
|
||||||
|
code: "TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "each",
|
||||||
|
value: "100",
|
||||||
|
max_quantity: 500,
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "in",
|
||||||
|
values: ["prod_1", "prod_2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when promotion with id does not exist", async () => {
|
||||||
|
let error
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.addPromotionBuyRules("does-not-exist", [])
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
"Promotion 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.addPromotionBuyRules(undefined as unknown as string, [])
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('"promotionId" must be defined')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully create buy rules for a buyget promotion", async () => {
|
||||||
|
promotion = await service.addPromotionBuyRules(promotion.id, [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "in",
|
||||||
|
values: ["prod_3", "prod_4"],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(promotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: promotion.id,
|
||||||
|
application_method: expect.objectContaining({
|
||||||
|
buy_rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "product_collection.id",
|
||||||
|
operator: "eq",
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: "pcol_towel" }),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "in",
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ value: "prod_3" }),
|
||||||
|
expect.objectContaining({ value: "prod_4" }),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("removePromotionRules", () => {
|
describe("removePromotionRules", () => {
|
||||||
let promotion
|
let promotion
|
||||||
|
|
||||||
@@ -1108,4 +1398,88 @@ describe("Promotion Service", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("removePromotionBuyRules", () => {
|
||||||
|
let promotion
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
;[promotion] = await service.create([
|
||||||
|
{
|
||||||
|
code: "TEST",
|
||||||
|
type: PromotionType.BUYGET,
|
||||||
|
application_method: {
|
||||||
|
type: "fixed",
|
||||||
|
target_type: "items",
|
||||||
|
allocation: "each",
|
||||||
|
value: "100",
|
||||||
|
max_quantity: 500,
|
||||||
|
apply_to_quantity: 1,
|
||||||
|
buy_rules_min_quantity: 1,
|
||||||
|
target_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product.id",
|
||||||
|
operator: "in",
|
||||||
|
values: ["prod_1", "prod_2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buy_rules: [
|
||||||
|
{
|
||||||
|
attribute: "product_collection",
|
||||||
|
operator: "eq",
|
||||||
|
values: ["pcol_towel"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when promotion with id does not exist", async () => {
|
||||||
|
let error
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.removePromotionBuyRules("does-not-exist", [])
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
"Promotion 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.removePromotionBuyRules(
|
||||||
|
undefined as unknown as string,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('"promotionId" must be defined')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully remove rules for a promotion", async () => {
|
||||||
|
const [ruleId] = promotion.application_method.buy_rules.map(
|
||||||
|
(rule) => rule.id
|
||||||
|
)
|
||||||
|
|
||||||
|
promotion = await service.removePromotionBuyRules(promotion.id, [
|
||||||
|
{ id: ruleId },
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(promotion).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: promotion.id,
|
||||||
|
application_method: expect.objectContaining({
|
||||||
|
buy_rules: [],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -398,6 +398,24 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"mappedType": "decimal"
|
"mappedType": "decimal"
|
||||||
},
|
},
|
||||||
|
"apply_to_quantity": {
|
||||||
|
"name": "apply_to_quantity",
|
||||||
|
"type": "numeric",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "decimal"
|
||||||
|
},
|
||||||
|
"buy_rules_min_quantity": {
|
||||||
|
"name": "buy_rules_min_quantity",
|
||||||
|
"type": "numeric",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "decimal"
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"name": "type",
|
"name": "type",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -697,11 +715,11 @@
|
|||||||
"mappedType": "text"
|
"mappedType": "text"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "application_method_promotion_rule",
|
"name": "application_method_target_rules",
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "application_method_promotion_rule_pkey",
|
"keyName": "application_method_target_rules_pkey",
|
||||||
"columnNames": ["application_method_id", "promotion_rule_id"],
|
"columnNames": ["application_method_id", "promotion_rule_id"],
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
@@ -710,19 +728,73 @@
|
|||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"application_method_promotion_rule_application_method_id_foreign": {
|
"application_method_target_rules_application_method_id_foreign": {
|
||||||
"constraintName": "application_method_promotion_rule_application_method_id_foreign",
|
"constraintName": "application_method_target_rules_application_method_id_foreign",
|
||||||
"columnNames": ["application_method_id"],
|
"columnNames": ["application_method_id"],
|
||||||
"localTableName": "public.application_method_promotion_rule",
|
"localTableName": "public.application_method_target_rules",
|
||||||
"referencedColumnNames": ["id"],
|
"referencedColumnNames": ["id"],
|
||||||
"referencedTableName": "public.application_method",
|
"referencedTableName": "public.application_method",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"application_method_promotion_rule_promotion_rule_id_foreign": {
|
"application_method_target_rules_promotion_rule_id_foreign": {
|
||||||
"constraintName": "application_method_promotion_rule_promotion_rule_id_foreign",
|
"constraintName": "application_method_target_rules_promotion_rule_id_foreign",
|
||||||
"columnNames": ["promotion_rule_id"],
|
"columnNames": ["promotion_rule_id"],
|
||||||
"localTableName": "public.application_method_promotion_rule",
|
"localTableName": "public.application_method_target_rules",
|
||||||
|
"referencedColumnNames": ["id"],
|
||||||
|
"referencedTableName": "public.promotion_rule",
|
||||||
|
"deleteRule": "cascade",
|
||||||
|
"updateRule": "cascade"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": {
|
||||||
|
"application_method_id": {
|
||||||
|
"name": "application_method_id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"promotion_rule_id": {
|
||||||
|
"name": "promotion_rule_id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "application_method_buy_rules",
|
||||||
|
"schema": "public",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"keyName": "application_method_buy_rules_pkey",
|
||||||
|
"columnNames": ["application_method_id", "promotion_rule_id"],
|
||||||
|
"composite": true,
|
||||||
|
"primary": true,
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [],
|
||||||
|
"foreignKeys": {
|
||||||
|
"application_method_buy_rules_application_method_id_foreign": {
|
||||||
|
"constraintName": "application_method_buy_rules_application_method_id_foreign",
|
||||||
|
"columnNames": ["application_method_id"],
|
||||||
|
"localTableName": "public.application_method_buy_rules",
|
||||||
|
"referencedColumnNames": ["id"],
|
||||||
|
"referencedTableName": "public.application_method",
|
||||||
|
"deleteRule": "cascade",
|
||||||
|
"updateRule": "cascade"
|
||||||
|
},
|
||||||
|
"application_method_buy_rules_promotion_rule_id_foreign": {
|
||||||
|
"constraintName": "application_method_buy_rules_promotion_rule_id_foreign",
|
||||||
|
"columnNames": ["promotion_rule_id"],
|
||||||
|
"localTableName": "public.application_method_buy_rules",
|
||||||
"referencedColumnNames": ["id"],
|
"referencedColumnNames": ["id"],
|
||||||
"referencedTableName": "public.promotion_rule",
|
"referencedTableName": "public.promotion_rule",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
|
|||||||
+16
-5
@@ -1,6 +1,6 @@
|
|||||||
import { Migration } from "@mikro-orm/migrations"
|
import { Migration } from "@mikro-orm/migrations"
|
||||||
|
|
||||||
export class Migration20240122070028 extends Migration {
|
export class Migration20240122084316 extends Migration {
|
||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'create table "campaign" ("id" text not null, "name" text not null, "description" text null, "currency" text null, "campaign_identifier" text not null, "starts_at" timestamptz null, "ends_at" timestamptz null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "campaign_pkey" primary key ("id"));'
|
'create table "campaign" ("id" text not null, "name" text not null, "description" text null, "currency" text null, "campaign_identifier" text not null, "starts_at" timestamptz null, "ends_at" timestamptz null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "campaign_pkey" primary key ("id"));'
|
||||||
@@ -29,7 +29,7 @@ export class Migration20240122070028 extends Migration {
|
|||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'create table "application_method" ("id" text not null, "value" numeric null, "max_quantity" numeric null, "type" text check ("type" in (\'fixed\', \'percentage\')) not null, "target_type" text check ("target_type" in (\'order\', \'shipping_methods\', \'items\')) not null, "allocation" text check ("allocation" in (\'each\', \'across\')) null, "promotion_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "application_method_pkey" primary key ("id"));'
|
'create table "application_method" ("id" text not null, "value" numeric null, "max_quantity" numeric null, "apply_to_quantity" numeric null, "buy_rules_min_quantity" numeric null, "type" text check ("type" in (\'fixed\', \'percentage\')) not null, "target_type" text check ("target_type" in (\'order\', \'shipping_methods\', \'items\')) not null, "allocation" text check ("allocation" in (\'each\', \'across\')) null, "promotion_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "application_method_pkey" primary key ("id"));'
|
||||||
)
|
)
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'create index "IDX_application_method_type" on "application_method" ("type");'
|
'create index "IDX_application_method_type" on "application_method" ("type");'
|
||||||
@@ -59,7 +59,11 @@ export class Migration20240122070028 extends Migration {
|
|||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'create table "application_method_promotion_rule" ("application_method_id" text not null, "promotion_rule_id" text not null, constraint "application_method_promotion_rule_pkey" primary key ("application_method_id", "promotion_rule_id"));'
|
'create table "application_method_target_rules" ("application_method_id" text not null, "promotion_rule_id" text not null, constraint "application_method_target_rules_pkey" primary key ("application_method_id", "promotion_rule_id"));'
|
||||||
|
)
|
||||||
|
|
||||||
|
this.addSql(
|
||||||
|
'create table "application_method_buy_rules" ("application_method_id" text not null, "promotion_rule_id" text not null, constraint "application_method_buy_rules_pkey" primary key ("application_method_id", "promotion_rule_id"));'
|
||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
@@ -89,10 +93,17 @@ export class Migration20240122070028 extends Migration {
|
|||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'alter table "application_method_promotion_rule" add constraint "application_method_promotion_rule_application_method_id_foreign" foreign key ("application_method_id") references "application_method" ("id") on update cascade on delete cascade;'
|
'alter table "application_method_target_rules" add constraint "application_method_target_rules_application_method_id_foreign" foreign key ("application_method_id") references "application_method" ("id") on update cascade on delete cascade;'
|
||||||
)
|
)
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'alter table "application_method_promotion_rule" add constraint "application_method_promotion_rule_promotion_rule_id_foreign" foreign key ("promotion_rule_id") references "promotion_rule" ("id") on update cascade on delete cascade;'
|
'alter table "application_method_target_rules" add constraint "application_method_target_rules_promotion_rule_id_foreign" foreign key ("promotion_rule_id") references "promotion_rule" ("id") on update cascade on delete cascade;'
|
||||||
|
)
|
||||||
|
|
||||||
|
this.addSql(
|
||||||
|
'alter table "application_method_buy_rules" add constraint "application_method_buy_rules_application_method_id_foreign" foreign key ("application_method_id") references "application_method" ("id") on update cascade on delete cascade;'
|
||||||
|
)
|
||||||
|
this.addSql(
|
||||||
|
'alter table "application_method_buy_rules" add constraint "application_method_buy_rules_promotion_rule_id_foreign" foreign key ("promotion_rule_id") references "promotion_rule" ("id") on update cascade on delete cascade;'
|
||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
@@ -25,6 +25,8 @@ import PromotionRule from "./promotion-rule"
|
|||||||
type OptionalFields =
|
type OptionalFields =
|
||||||
| "value"
|
| "value"
|
||||||
| "max_quantity"
|
| "max_quantity"
|
||||||
|
| "apply_to_quantity"
|
||||||
|
| "buy_rules_min_quantity"
|
||||||
| "allocation"
|
| "allocation"
|
||||||
| DAL.SoftDeletableEntityDateColumns
|
| DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
@@ -37,10 +39,16 @@ export default class ApplicationMethod {
|
|||||||
id!: string
|
id!: string
|
||||||
|
|
||||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||||
value?: string | null
|
value?: string | null = null
|
||||||
|
|
||||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null = null
|
||||||
|
|
||||||
|
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||||
|
apply_to_quantity?: number | null = null
|
||||||
|
|
||||||
|
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||||
|
buy_rules_min_quantity?: number | null = null
|
||||||
|
|
||||||
@Index({ name: "IDX_application_method_type" })
|
@Index({ name: "IDX_application_method_type" })
|
||||||
@Enum(() => PromotionUtils.ApplicationMethodType)
|
@Enum(() => PromotionUtils.ApplicationMethodType)
|
||||||
@@ -63,13 +71,20 @@ export default class ApplicationMethod {
|
|||||||
})
|
})
|
||||||
promotion: Promotion
|
promotion: Promotion
|
||||||
|
|
||||||
@ManyToMany(() => PromotionRule, "application_methods", {
|
@ManyToMany(() => PromotionRule, "method_target_rules", {
|
||||||
owner: true,
|
owner: true,
|
||||||
pivotTable: "application_method_promotion_rule",
|
pivotTable: "application_method_target_rules",
|
||||||
cascade: ["soft-remove"] as any,
|
cascade: ["soft-remove"] as any,
|
||||||
})
|
})
|
||||||
target_rules = new Collection<PromotionRule>(this)
|
target_rules = new Collection<PromotionRule>(this)
|
||||||
|
|
||||||
|
@ManyToMany(() => PromotionRule, "method_buy_rules", {
|
||||||
|
owner: true,
|
||||||
|
pivotTable: "application_method_buy_rules",
|
||||||
|
cascade: ["soft-remove"] as any,
|
||||||
|
})
|
||||||
|
buy_rules = new Collection<PromotionRule>(this)
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
onCreate: () => new Date(),
|
onCreate: () => new Date(),
|
||||||
columnType: "timestamptz",
|
columnType: "timestamptz",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default class CampaignBudget {
|
|||||||
@OneToOne({
|
@OneToOne({
|
||||||
entity: () => Campaign,
|
entity: () => Campaign,
|
||||||
})
|
})
|
||||||
campaign?: Campaign | null
|
campaign: Campaign | null = null
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
columnType: "numeric",
|
columnType: "numeric",
|
||||||
@@ -43,7 +43,7 @@ export default class CampaignBudget {
|
|||||||
serializer: Number,
|
serializer: Number,
|
||||||
default: null,
|
default: null,
|
||||||
})
|
})
|
||||||
limit?: number | null
|
limit: number | null = null
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
columnType: "numeric",
|
columnType: "numeric",
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ export default class Campaign {
|
|||||||
name: string
|
name: string
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
description?: string | null
|
description: string | null = null
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
currency?: string | null
|
currency: string | null = null
|
||||||
|
|
||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
@Unique({
|
@Unique({
|
||||||
@@ -52,13 +52,13 @@ export default class Campaign {
|
|||||||
columnType: "timestamptz",
|
columnType: "timestamptz",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
starts_at?: Date | null
|
starts_at: Date | null = null
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
columnType: "timestamptz",
|
columnType: "timestamptz",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
ends_at?: Date | null
|
ends_at: Date | null = null
|
||||||
|
|
||||||
@OneToOne({
|
@OneToOne({
|
||||||
entity: () => CampaignBudget,
|
entity: () => CampaignBudget,
|
||||||
@@ -66,7 +66,7 @@ export default class Campaign {
|
|||||||
cascade: ["soft-remove"] as any,
|
cascade: ["soft-remove"] as any,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
budget?: CampaignBudget | null
|
budget: CampaignBudget | null = null
|
||||||
|
|
||||||
@OneToMany(() => Promotion, (promotion) => promotion.campaign, {
|
@OneToMany(() => Promotion, (promotion) => promotion.campaign, {
|
||||||
orphanRemoval: true,
|
orphanRemoval: true,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default class PromotionRule {
|
|||||||
id!: string
|
id!: string
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
description?: string | null
|
description: string | null = null
|
||||||
|
|
||||||
@Index({ name: "IDX_promotion_rule_attribute" })
|
@Index({ name: "IDX_promotion_rule_attribute" })
|
||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
@@ -53,7 +53,13 @@ export default class PromotionRule {
|
|||||||
() => ApplicationMethod,
|
() => ApplicationMethod,
|
||||||
(applicationMethod) => applicationMethod.target_rules
|
(applicationMethod) => applicationMethod.target_rules
|
||||||
)
|
)
|
||||||
application_methods = new Collection<ApplicationMethod>(this)
|
method_target_rules = new Collection<ApplicationMethod>(this)
|
||||||
|
|
||||||
|
@ManyToMany(
|
||||||
|
() => ApplicationMethod,
|
||||||
|
(applicationMethod) => applicationMethod.buy_rules
|
||||||
|
)
|
||||||
|
method_buy_rules = new Collection<ApplicationMethod>(this)
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
onCreate: () => new Date(),
|
onCreate: () => new Date(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DAL, PromotionType } from "@medusajs/types"
|
import { DAL, PromotionTypeValues } from "@medusajs/types"
|
||||||
import { DALUtils, PromotionUtils, generateEntityId } from "@medusajs/utils"
|
import { DALUtils, PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
@@ -45,14 +45,14 @@ export default class Promotion {
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
cascade: ["soft-remove"] as any,
|
cascade: ["soft-remove"] as any,
|
||||||
})
|
})
|
||||||
campaign?: Campaign | null
|
campaign: Campaign | null = null
|
||||||
|
|
||||||
@Property({ columnType: "boolean", default: false })
|
@Property({ columnType: "boolean", default: false })
|
||||||
is_automatic: boolean = false
|
is_automatic: boolean = false
|
||||||
|
|
||||||
@Index({ name: "IDX_promotion_type" })
|
@Index({ name: "IDX_promotion_type" })
|
||||||
@Enum(() => PromotionUtils.PromotionType)
|
@Enum(() => PromotionUtils.PromotionType)
|
||||||
type: PromotionType
|
type: PromotionTypeValues
|
||||||
|
|
||||||
@OneToOne({
|
@OneToOne({
|
||||||
entity: () => ApplicationMethod,
|
entity: () => ApplicationMethod,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
|
PromotionType,
|
||||||
isString,
|
isString,
|
||||||
mapObjectTo,
|
mapObjectTo,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
@@ -35,6 +36,7 @@ import {
|
|||||||
PromotionService,
|
PromotionService,
|
||||||
} from "@services"
|
} from "@services"
|
||||||
import {
|
import {
|
||||||
|
ApplicationMethodRuleTypes,
|
||||||
CreateApplicationMethodDTO,
|
CreateApplicationMethodDTO,
|
||||||
CreateCampaignBudgetDTO,
|
CreateCampaignBudgetDTO,
|
||||||
CreateCampaignDTO,
|
CreateCampaignDTO,
|
||||||
@@ -456,6 +458,8 @@ export default class PromotionModuleService<
|
|||||||
"application_method",
|
"application_method",
|
||||||
"application_method.target_rules",
|
"application_method.target_rules",
|
||||||
"application_method.target_rules.values",
|
"application_method.target_rules.values",
|
||||||
|
"application_method.buy_rules",
|
||||||
|
"application_method.buy_rules.values",
|
||||||
"rules",
|
"rules",
|
||||||
"rules.values",
|
"rules.values",
|
||||||
"campaign",
|
"campaign",
|
||||||
@@ -485,7 +489,11 @@ export default class PromotionModuleService<
|
|||||||
string,
|
string,
|
||||||
PromotionTypes.CreatePromotionRuleDTO[]
|
PromotionTypes.CreatePromotionRuleDTO[]
|
||||||
>()
|
>()
|
||||||
const applicationMethodRuleMap = new Map<
|
const methodTargetRulesMap = new Map<
|
||||||
|
string,
|
||||||
|
PromotionTypes.CreatePromotionRuleDTO[]
|
||||||
|
>()
|
||||||
|
const methodBuyRulesMap = new Map<
|
||||||
string,
|
string,
|
||||||
PromotionTypes.CreatePromotionRuleDTO[]
|
PromotionTypes.CreatePromotionRuleDTO[]
|
||||||
>()
|
>()
|
||||||
@@ -551,6 +559,7 @@ export default class PromotionModuleService<
|
|||||||
if (applMethodData) {
|
if (applMethodData) {
|
||||||
const {
|
const {
|
||||||
target_rules: targetRulesData = [],
|
target_rules: targetRulesData = [],
|
||||||
|
buy_rules: buyRulesData = [],
|
||||||
...applicationMethodWithoutRules
|
...applicationMethodWithoutRules
|
||||||
} = applMethodData
|
} = applMethodData
|
||||||
const applicationMethodData = {
|
const applicationMethodData = {
|
||||||
@@ -569,11 +578,33 @@ export default class PromotionModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
validateApplicationMethodAttributes(applicationMethodData)
|
if (promotion.type === PromotionType.BUYGET && !buyRulesData.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Buy rules are required for ${PromotionType.BUYGET} promotion type`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
promotion.type === PromotionType.BUYGET &&
|
||||||
|
!targetRulesData.length
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Target rules are required for ${PromotionType.BUYGET} promotion type`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
validateApplicationMethodAttributes(applicationMethodData, promotion)
|
||||||
|
|
||||||
applicationMethodsData.push(applicationMethodData)
|
applicationMethodsData.push(applicationMethodData)
|
||||||
|
|
||||||
if (targetRulesData.length) {
|
if (targetRulesData.length) {
|
||||||
applicationMethodRuleMap.set(promotion.id, targetRulesData)
|
methodTargetRulesMap.set(promotion.id, targetRulesData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buyRulesData.length) {
|
||||||
|
methodBuyRulesMap.set(promotion.id, buyRulesData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,8 +628,15 @@ export default class PromotionModuleService<
|
|||||||
|
|
||||||
for (const applicationMethod of createdApplicationMethods) {
|
for (const applicationMethod of createdApplicationMethods) {
|
||||||
await this.createPromotionRulesAndValues_(
|
await this.createPromotionRulesAndValues_(
|
||||||
applicationMethodRuleMap.get(applicationMethod.promotion.id) || [],
|
methodTargetRulesMap.get(applicationMethod.promotion.id) || [],
|
||||||
"application_methods",
|
"method_target_rules",
|
||||||
|
applicationMethod,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.createPromotionRulesAndValues_(
|
||||||
|
methodBuyRulesMap.get(applicationMethod.promotion.id) || [],
|
||||||
|
"method_buy_rules",
|
||||||
applicationMethod,
|
applicationMethod,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
@@ -694,18 +732,10 @@ export default class PromotionModuleService<
|
|||||||
existingApplicationMethod.max_quantity = null
|
existingApplicationMethod.max_quantity = null
|
||||||
}
|
}
|
||||||
|
|
||||||
validateApplicationMethodAttributes({
|
validateApplicationMethodAttributes(
|
||||||
type: applicationMethodData.type || existingApplicationMethod.type,
|
applicationMethodData,
|
||||||
target_type:
|
existingPromotion
|
||||||
applicationMethodData.target_type ||
|
)
|
||||||
existingApplicationMethod.target_type,
|
|
||||||
allocation:
|
|
||||||
applicationMethodData.allocation ||
|
|
||||||
existingApplicationMethod.allocation,
|
|
||||||
max_quantity:
|
|
||||||
applicationMethodData.max_quantity ||
|
|
||||||
existingApplicationMethod.max_quantity,
|
|
||||||
})
|
|
||||||
|
|
||||||
applicationMethodsData.push({
|
applicationMethodsData.push({
|
||||||
...applicationMethodData,
|
...applicationMethodData,
|
||||||
@@ -771,7 +801,7 @@ export default class PromotionModuleService<
|
|||||||
|
|
||||||
await this.createPromotionRulesAndValues_(
|
await this.createPromotionRulesAndValues_(
|
||||||
rulesData,
|
rulesData,
|
||||||
"application_methods",
|
"method_target_rules",
|
||||||
applicationMethod,
|
applicationMethod,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
@@ -791,10 +821,51 @@ export default class PromotionModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async addPromotionBuyRules(
|
||||||
|
promotionId: string,
|
||||||
|
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.PromotionDTO> {
|
||||||
|
const promotion = await this.promotionService_.retrieve(promotionId, {
|
||||||
|
relations: ["application_method"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const applicationMethod = promotion.application_method
|
||||||
|
|
||||||
|
if (!applicationMethod) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`application_method for promotion not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.createPromotionRulesAndValues_(
|
||||||
|
rulesData,
|
||||||
|
"method_buy_rules",
|
||||||
|
applicationMethod,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.retrieve(
|
||||||
|
promotionId,
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"rules",
|
||||||
|
"rules.values",
|
||||||
|
"application_method",
|
||||||
|
"application_method.buy_rules",
|
||||||
|
"application_method.buy_rules.values",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
protected async createPromotionRulesAndValues_(
|
protected async createPromotionRulesAndValues_(
|
||||||
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||||
relationName: "promotions" | "application_methods",
|
relationName: "promotions" | "method_target_rules" | "method_buy_rules",
|
||||||
relation: Promotion | ApplicationMethod,
|
relation: Promotion | ApplicationMethod,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
) {
|
||||||
@@ -952,9 +1023,10 @@ export default class PromotionModuleService<
|
|||||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<PromotionTypes.PromotionDTO> {
|
): Promise<PromotionTypes.PromotionDTO> {
|
||||||
await this.removePromotionTargetRules_(
|
await this.removeApplicationMethodRules_(
|
||||||
promotionId,
|
promotionId,
|
||||||
rulesData,
|
rulesData,
|
||||||
|
ApplicationMethodRuleTypes.TARGET_RULES,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -973,16 +1045,47 @@ export default class PromotionModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
protected async removePromotionTargetRules_(
|
async removePromotionBuyRules(
|
||||||
promotionId: string,
|
promotionId: string,
|
||||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PromotionTypes.PromotionDTO> {
|
||||||
|
await this.removeApplicationMethodRules_(
|
||||||
|
promotionId,
|
||||||
|
rulesData,
|
||||||
|
ApplicationMethodRuleTypes.BUY_RULES,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.retrieve(
|
||||||
|
promotionId,
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"rules",
|
||||||
|
"rules.values",
|
||||||
|
"application_method",
|
||||||
|
"application_method.buy_rules",
|
||||||
|
"application_method.buy_rules.values",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async removeApplicationMethodRules_(
|
||||||
|
promotionId: string,
|
||||||
|
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||||
|
relation:
|
||||||
|
| ApplicationMethodRuleTypes.TARGET_RULES
|
||||||
|
| ApplicationMethodRuleTypes.BUY_RULES,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const promotionRuleIds = rulesData.map((ruleData) => ruleData.id)
|
const promotionRuleIds = rulesData.map((ruleData) => ruleData.id)
|
||||||
const promotion = await this.promotionService_.retrieve(
|
const promotion = await this.promotionService_.retrieve(
|
||||||
promotionId,
|
promotionId,
|
||||||
{ relations: ["application_method.target_rules"] },
|
{ relations: [`application_method.${relation}`] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -995,7 +1098,7 @@ export default class PromotionModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetRuleIdsToRemove = applicationMethod.target_rules
|
const targetRuleIdsToRemove = applicationMethod[relation]
|
||||||
.toArray()
|
.toArray()
|
||||||
.filter((rule) => promotionRuleIds.includes(rule.id))
|
.filter((rule) => promotionRuleIds.includes(rule.id))
|
||||||
.map((rule) => rule.id)
|
.map((rule) => rule.id)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export interface CreateApplicationMethodDTO {
|
|||||||
value?: string | null
|
value?: string | null
|
||||||
promotion: Promotion | string | PromotionDTO
|
promotion: Promotion | string | PromotionDTO
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null
|
||||||
|
buy_rules_min_quantity?: number | null
|
||||||
|
apply_to_quantity?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateApplicationMethodDTO {
|
export interface UpdateApplicationMethodDTO {
|
||||||
@@ -24,4 +26,6 @@ export interface UpdateApplicationMethodDTO {
|
|||||||
value?: string | null
|
value?: string | null
|
||||||
promotion?: Promotion | string | PromotionDTO
|
promotion?: Promotion | string | PromotionDTO
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null
|
||||||
|
buy_rules_min_quantity?: number | null
|
||||||
|
apply_to_quantity?: number | null
|
||||||
}
|
}
|
||||||
@@ -9,3 +9,8 @@ export interface CreatePromotionRuleDTO {
|
|||||||
export interface UpdatePromotionRuleDTO {
|
export interface UpdatePromotionRuleDTO {
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ApplicationMethodRuleTypes {
|
||||||
|
TARGET_RULES = "target_rules",
|
||||||
|
BUY_RULES = "buy_rules",
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { PromotionType } from "@medusajs/types"
|
import { PromotionTypeValues } from "@medusajs/types"
|
||||||
|
|
||||||
export interface CreatePromotionDTO {
|
export interface CreatePromotionDTO {
|
||||||
code: string
|
code: string
|
||||||
type: PromotionType
|
type: PromotionTypeValues
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
campaign?: string
|
campaign?: string
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,7 @@ export interface CreatePromotionDTO {
|
|||||||
export interface UpdatePromotionDTO {
|
export interface UpdatePromotionDTO {
|
||||||
id: string
|
id: string
|
||||||
code?: string
|
code?: string
|
||||||
// TODO: add this when buyget is available
|
type?: PromotionTypeValues
|
||||||
// type: PromotionType
|
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
campaign?: string
|
campaign?: string
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
import {
|
|
||||||
ApplicationMethodAllocationValues,
|
|
||||||
ApplicationMethodTargetTypeValues,
|
|
||||||
ApplicationMethodTypeValues,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import {
|
import {
|
||||||
ApplicationMethodAllocation,
|
ApplicationMethodAllocation,
|
||||||
ApplicationMethodTargetType,
|
ApplicationMethodTargetType,
|
||||||
ApplicationMethodType,
|
ApplicationMethodType,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
|
PromotionType,
|
||||||
isDefined,
|
isDefined,
|
||||||
isPresent,
|
isPresent,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
import { Promotion } from "@models"
|
||||||
|
import {
|
||||||
|
CreateApplicationMethodDTO,
|
||||||
|
UpdateApplicationMethodDTO,
|
||||||
|
} from "../../types"
|
||||||
|
|
||||||
export const allowedAllocationTargetTypes: string[] = [
|
export const allowedAllocationTargetTypes: string[] = [
|
||||||
ApplicationMethodTargetType.SHIPPING_METHODS,
|
ApplicationMethodTargetType.SHIPPING_METHODS,
|
||||||
@@ -26,17 +27,40 @@ export const allowedAllocationForQuantity: string[] = [
|
|||||||
ApplicationMethodAllocation.EACH,
|
ApplicationMethodAllocation.EACH,
|
||||||
]
|
]
|
||||||
|
|
||||||
export function validateApplicationMethodAttributes(data: {
|
export function validateApplicationMethodAttributes(
|
||||||
type: ApplicationMethodTypeValues
|
data: UpdateApplicationMethodDTO | CreateApplicationMethodDTO,
|
||||||
target_type: ApplicationMethodTargetTypeValues
|
promotion: Promotion
|
||||||
allocation?: ApplicationMethodAllocationValues
|
) {
|
||||||
max_quantity?: number | null
|
const applicationMethod = promotion?.application_method || {}
|
||||||
}) {
|
const buyRulesMinQuantity =
|
||||||
|
data.buy_rules_min_quantity || applicationMethod?.buy_rules_min_quantity
|
||||||
|
const applyToQuantity =
|
||||||
|
data.apply_to_quantity || applicationMethod?.apply_to_quantity
|
||||||
|
const targetType = data.target_type || applicationMethod?.target_type
|
||||||
|
const applicationMethodType = data.type || applicationMethod?.type
|
||||||
|
const maxQuantity = data.max_quantity || applicationMethod.max_quantity
|
||||||
|
const allocation = data.allocation || applicationMethod.allocation
|
||||||
const allTargetTypes: string[] = Object.values(ApplicationMethodTargetType)
|
const allTargetTypes: string[] = Object.values(ApplicationMethodTargetType)
|
||||||
|
|
||||||
|
if (promotion?.type === PromotionType.BUYGET) {
|
||||||
|
if (!isPresent(applyToQuantity)) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`apply_to_quantity is a required field for Promotion type of ${PromotionType.BUYGET}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPresent(buyRulesMinQuantity)) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`buy_rules_min_quantity is a required field for Promotion type of ${PromotionType.BUYGET}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.allocation === ApplicationMethodAllocation.ACROSS &&
|
allocation === ApplicationMethodAllocation.ACROSS &&
|
||||||
isPresent(data.max_quantity)
|
isPresent(maxQuantity)
|
||||||
) {
|
) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
@@ -44,7 +68,7 @@ export function validateApplicationMethodAttributes(data: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allTargetTypes.includes(data.target_type)) {
|
if (!allTargetTypes.includes(targetType)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`application_method.target_type should be one of ${allTargetTypes.join(
|
`application_method.target_type should be one of ${allTargetTypes.join(
|
||||||
@@ -55,7 +79,7 @@ export function validateApplicationMethodAttributes(data: {
|
|||||||
|
|
||||||
const allTypes: string[] = Object.values(ApplicationMethodType)
|
const allTypes: string[] = Object.values(ApplicationMethodType)
|
||||||
|
|
||||||
if (!allTypes.includes(data.type)) {
|
if (!allTypes.includes(applicationMethodType)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`application_method.type should be one of ${allTypes.join(", ")}`
|
`application_method.type should be one of ${allTypes.join(", ")}`
|
||||||
@@ -63,8 +87,8 @@ export function validateApplicationMethodAttributes(data: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
allowedAllocationTargetTypes.includes(data.target_type) &&
|
allowedAllocationTargetTypes.includes(targetType) &&
|
||||||
!allowedAllocationTypes.includes(data.allocation || "")
|
!allowedAllocationTypes.includes(allocation || "")
|
||||||
) {
|
) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
@@ -80,7 +104,7 @@ export function validateApplicationMethodAttributes(data: {
|
|||||||
ApplicationMethodAllocation
|
ApplicationMethodAllocation
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.allocation && !allAllocationTypes.includes(data.allocation)) {
|
if (allocation && !allAllocationTypes.includes(allocation)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`application_method.allocation should be one of ${allAllocationTypes.join(
|
`application_method.allocation should be one of ${allAllocationTypes.join(
|
||||||
@@ -90,9 +114,9 @@ export function validateApplicationMethodAttributes(data: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.allocation &&
|
allocation &&
|
||||||
allowedAllocationForQuantity.includes(data.allocation) &&
|
allowedAllocationForQuantity.includes(allocation) &&
|
||||||
!isDefined(data.max_quantity)
|
!isDefined(maxQuantity)
|
||||||
) {
|
) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ export interface ApplicationMethodDTO {
|
|||||||
allocation?: ApplicationMethodAllocationValues
|
allocation?: ApplicationMethodAllocationValues
|
||||||
value?: string | null
|
value?: string | null
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null
|
||||||
|
buy_rules_min_quantity?: number | null
|
||||||
|
apply_to_quantity?: number | null
|
||||||
promotion?: PromotionDTO | string
|
promotion?: PromotionDTO | string
|
||||||
target_rules?: PromotionRuleDTO[]
|
target_rules?: PromotionRuleDTO[]
|
||||||
|
buy_rules?: PromotionRuleDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateApplicationMethodDTO {
|
export interface CreateApplicationMethodDTO {
|
||||||
@@ -26,8 +29,11 @@ export interface CreateApplicationMethodDTO {
|
|||||||
allocation?: ApplicationMethodAllocationValues
|
allocation?: ApplicationMethodAllocationValues
|
||||||
value?: string | null
|
value?: string | null
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null
|
||||||
|
buy_rules_min_quantity?: number | null
|
||||||
|
apply_to_quantity?: number | null
|
||||||
promotion?: PromotionDTO | string
|
promotion?: PromotionDTO | string
|
||||||
target_rules?: CreatePromotionRuleDTO[]
|
target_rules?: CreatePromotionRuleDTO[]
|
||||||
|
buy_rules?: CreatePromotionRuleDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateApplicationMethodDTO {
|
export interface UpdateApplicationMethodDTO {
|
||||||
@@ -37,6 +43,8 @@ export interface UpdateApplicationMethodDTO {
|
|||||||
allocation?: ApplicationMethodAllocationValues
|
allocation?: ApplicationMethodAllocationValues
|
||||||
value?: string | null
|
value?: string | null
|
||||||
max_quantity?: number | null
|
max_quantity?: number | null
|
||||||
|
buy_rules_min_quantity?: number | null
|
||||||
|
apply_to_quantity?: number | null
|
||||||
promotion?: PromotionDTO | string
|
promotion?: PromotionDTO | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import {
|
|||||||
import { CampaignDTO } from "./campaign"
|
import { CampaignDTO } from "./campaign"
|
||||||
import { CreatePromotionRuleDTO, PromotionRuleDTO } from "./promotion-rule"
|
import { CreatePromotionRuleDTO, PromotionRuleDTO } from "./promotion-rule"
|
||||||
|
|
||||||
export type PromotionType = "standard" | "buyget"
|
export type PromotionTypeValues = "standard" | "buyget"
|
||||||
|
|
||||||
export interface PromotionDTO {
|
export interface PromotionDTO {
|
||||||
id: string
|
id: string
|
||||||
code?: string
|
code?: string
|
||||||
type?: PromotionType
|
type?: PromotionTypeValues
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
application_method?: ApplicationMethodDTO
|
application_method?: ApplicationMethodDTO
|
||||||
rules?: PromotionRuleDTO[]
|
rules?: PromotionRuleDTO[]
|
||||||
@@ -22,7 +22,7 @@ export interface PromotionDTO {
|
|||||||
|
|
||||||
export interface CreatePromotionDTO {
|
export interface CreatePromotionDTO {
|
||||||
code: string
|
code: string
|
||||||
type: PromotionType
|
type: PromotionTypeValues
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
application_method?: CreateApplicationMethodDTO
|
application_method?: CreateApplicationMethodDTO
|
||||||
rules?: CreatePromotionRuleDTO[]
|
rules?: CreatePromotionRuleDTO[]
|
||||||
@@ -34,7 +34,7 @@ export interface UpdatePromotionDTO {
|
|||||||
id: string
|
id: string
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
code?: string
|
code?: string
|
||||||
type?: PromotionType
|
type?: PromotionTypeValues
|
||||||
application_method?: UpdateApplicationMethodDTO
|
application_method?: UpdateApplicationMethodDTO
|
||||||
campaign_id?: string
|
campaign_id?: string
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,6 @@ export interface FilterablePromotionProps
|
|||||||
id?: string[]
|
id?: string[]
|
||||||
code?: string[]
|
code?: string[]
|
||||||
is_automatic?: boolean
|
is_automatic?: boolean
|
||||||
type?: PromotionType[]
|
type?: PromotionTypeValues[]
|
||||||
budget_id?: string[]
|
budget_id?: string[]
|
||||||
}
|
}
|
||||||
@@ -90,6 +90,12 @@ export interface IPromotionModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PromotionDTO>
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
|
addPromotionBuyRules(
|
||||||
|
promotionId: string,
|
||||||
|
rulesData: CreatePromotionRuleDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
removePromotionRules(
|
removePromotionRules(
|
||||||
promotionId: string,
|
promotionId: string,
|
||||||
rulesData: RemovePromotionRuleDTO[],
|
rulesData: RemovePromotionRuleDTO[],
|
||||||
@@ -102,6 +108,12 @@ export interface IPromotionModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PromotionDTO>
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
|
removePromotionBuyRules(
|
||||||
|
promotionId: string,
|
||||||
|
rulesData: RemovePromotionRuleDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PromotionDTO>
|
||||||
|
|
||||||
createCampaigns(
|
createCampaigns(
|
||||||
data: CreateCampaignDTO,
|
data: CreateCampaignDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
|
|||||||
Reference in new issue
Block a user