feat(types,utils): added promotion create with rules and application target rules (#5957)
* feat(types,utils): added promotion create with rules * chore: add rules to promotion and application method * chore: use common code for rule and values * chore: address pr reviews * chore: fix test
This commit is contained in:
6
.changeset/breezy-horses-destroy.md
Normal file
6
.changeset/breezy-horses-destroy.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/types": patch
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(types,utils): added promotion create with rules
|
||||
@@ -75,15 +75,82 @@ describe("Promotion Service", () => {
|
||||
},
|
||||
])
|
||||
|
||||
const [promotion] = await service.list({
|
||||
id: [createdPromotion.id],
|
||||
})
|
||||
const [promotion] = await service.list(
|
||||
{
|
||||
id: [createdPromotion.id],
|
||||
},
|
||||
{
|
||||
relations: ["application_method"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(promotion).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "PROMOTION_TEST",
|
||||
is_automatic: false,
|
||||
type: "standard",
|
||||
application_method: expect.objectContaining({
|
||||
type: "fixed",
|
||||
target_type: "order",
|
||||
value: 100,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a promotion with order application method with rules successfully", async () => {
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "order",
|
||||
value: 100,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_id",
|
||||
operator: "eq",
|
||||
values: ["prod_tshirt"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const [promotion] = await service.list(
|
||||
{
|
||||
id: [createdPromotion.id],
|
||||
},
|
||||
{
|
||||
relations: [
|
||||
"application_method",
|
||||
"application_method.target_rules.values",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(promotion).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "PROMOTION_TEST",
|
||||
is_automatic: false,
|
||||
type: "standard",
|
||||
application_method: expect.objectContaining({
|
||||
type: "fixed",
|
||||
target_type: "order",
|
||||
value: 100,
|
||||
target_rules: [
|
||||
expect.objectContaining({
|
||||
attribute: "product_id",
|
||||
operator: "eq",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "prod_tshirt",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
@@ -128,5 +195,156 @@ describe("Promotion Service", () => {
|
||||
"application_method.max_quantity is required when application_method.allocation is 'each'"
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a promotion with rules successfully", async () => {
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer_group_id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [promotion] = await service.list(
|
||||
{
|
||||
id: [createdPromotion.id],
|
||||
},
|
||||
{
|
||||
relations: ["rules", "rules.values"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(promotion).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "PROMOTION_TEST",
|
||||
is_automatic: false,
|
||||
type: "standard",
|
||||
rules: [
|
||||
expect.objectContaining({
|
||||
attribute: "customer_group_id",
|
||||
operator: "in",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "VIP",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "top100",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a promotion with rules with single value successfully", async () => {
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer_group_id",
|
||||
operator: "eq",
|
||||
values: "VIP",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [promotion] = await service.list(
|
||||
{
|
||||
id: [createdPromotion.id],
|
||||
},
|
||||
{
|
||||
relations: ["rules", "rules.values"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(promotion).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "PROMOTION_TEST",
|
||||
is_automatic: false,
|
||||
type: "standard",
|
||||
rules: [
|
||||
expect.objectContaining({
|
||||
attribute: "customer_group_id",
|
||||
operator: "eq",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "VIP",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when rule attribute is invalid", async () => {
|
||||
const error = await service
|
||||
.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "",
|
||||
operator: "eq",
|
||||
values: "VIP",
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toContain("rules[].attribute is a required field")
|
||||
})
|
||||
|
||||
it("should throw an error when rule operator is invalid", async () => {
|
||||
let error = await service
|
||||
.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer_group",
|
||||
operator: "",
|
||||
values: "VIP",
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toContain("rules[].operator is a required field")
|
||||
|
||||
error = await service
|
||||
.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer_group",
|
||||
operator: "doesnotexist",
|
||||
values: "VIP",
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toContain(
|
||||
"rules[].operator (doesnotexist) is invalid. It should be one of gte, lte, gt, lt, eq, ne, in"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,12 @@ export default async ({
|
||||
|
||||
container.register({
|
||||
promotionService: asClass(defaultServices.PromotionService).singleton(),
|
||||
promotionRuleService: asClass(
|
||||
defaultServices.PromotionRuleService
|
||||
).singleton(),
|
||||
promotionRuleValueService: asClass(
|
||||
defaultServices.PromotionRuleValueService
|
||||
).singleton(),
|
||||
applicationMethodService: asClass(
|
||||
defaultServices.ApplicationMethodService
|
||||
).singleton(),
|
||||
@@ -44,5 +50,11 @@ function loadDefaultRepositories({ container }) {
|
||||
promotionRepository: asClass(
|
||||
defaultRepositories.PromotionRepository
|
||||
).singleton(),
|
||||
promotionRuleRepository: asClass(
|
||||
defaultRepositories.PromotionRuleRepository
|
||||
).singleton(),
|
||||
promotionRuleValueRepository: asClass(
|
||||
defaultRepositories.PromotionRuleValueRepository
|
||||
).singleton(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"namespaces": [
|
||||
"public"
|
||||
],
|
||||
"namespaces": ["public"],
|
||||
"name": "public",
|
||||
"tables": [
|
||||
{
|
||||
@@ -41,10 +39,7 @@
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"enumItems": [
|
||||
"standard",
|
||||
"buyget"
|
||||
],
|
||||
"enumItems": ["standard", "buyget"],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"created_at": {
|
||||
@@ -84,18 +79,14 @@
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": [
|
||||
"code"
|
||||
],
|
||||
"columnNames": ["code"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_promotion_code",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": [
|
||||
"type"
|
||||
],
|
||||
"columnNames": ["type"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_promotion_type",
|
||||
"primary": false,
|
||||
@@ -103,18 +94,14 @@
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_promotion_code_unique",
|
||||
"columnNames": [
|
||||
"code"
|
||||
],
|
||||
"columnNames": ["code"],
|
||||
"composite": false,
|
||||
"primary": false,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"keyName": "promotion_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"columnNames": ["id"],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
@@ -159,10 +146,7 @@
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"enumItems": [
|
||||
"fixed",
|
||||
"percentage"
|
||||
],
|
||||
"enumItems": ["fixed", "percentage"],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"target_type": {
|
||||
@@ -172,11 +156,7 @@
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"enumItems": [
|
||||
"order",
|
||||
"shipping",
|
||||
"item"
|
||||
],
|
||||
"enumItems": ["order", "shipping", "item"],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"allocation": {
|
||||
@@ -186,10 +166,7 @@
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"enumItems": [
|
||||
"each",
|
||||
"across"
|
||||
],
|
||||
"enumItems": ["each", "across"],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"promotion_id": {
|
||||
@@ -238,36 +215,28 @@
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": [
|
||||
"type"
|
||||
],
|
||||
"columnNames": ["type"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_application_method_type",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": [
|
||||
"target_type"
|
||||
],
|
||||
"columnNames": ["target_type"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_application_method_target_type",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": [
|
||||
"allocation"
|
||||
],
|
||||
"columnNames": ["allocation"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_application_method_allocation",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": [
|
||||
"promotion_id"
|
||||
],
|
||||
"columnNames": ["promotion_id"],
|
||||
"composite": false,
|
||||
"keyName": "application_method_promotion_id_unique",
|
||||
"primary": false,
|
||||
@@ -275,9 +244,7 @@
|
||||
},
|
||||
{
|
||||
"keyName": "application_method_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"columnNames": ["id"],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
@@ -287,17 +254,282 @@
|
||||
"foreignKeys": {
|
||||
"application_method_promotion_id_foreign": {
|
||||
"constraintName": "application_method_promotion_id_foreign",
|
||||
"columnNames": [
|
||||
"promotion_id"
|
||||
],
|
||||
"columnNames": ["promotion_id"],
|
||||
"localTableName": "public.application_method",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedColumnNames": ["id"],
|
||||
"referencedTableName": "public.promotion",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"attribute": {
|
||||
"name": "attribute",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"operator": {
|
||||
"name": "operator",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"enumItems": ["gte", "lte", "gt", "lt", "eq", "ne", "in"],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "promotion_rule",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": ["attribute"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_promotion_rule_attribute",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": ["operator"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_promotion_rule_operator",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"keyName": "promotion_rule_pkey",
|
||||
"columnNames": ["id"],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"promotion_id": {
|
||||
"name": "promotion_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": "promotion_promotion_rule",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "promotion_promotion_rule_pkey",
|
||||
"columnNames": ["promotion_id", "promotion_rule_id"],
|
||||
"composite": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"promotion_promotion_rule_promotion_id_foreign": {
|
||||
"constraintName": "promotion_promotion_rule_promotion_id_foreign",
|
||||
"columnNames": ["promotion_id"],
|
||||
"localTableName": "public.promotion_promotion_rule",
|
||||
"referencedColumnNames": ["id"],
|
||||
"referencedTableName": "public.promotion",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"promotion_promotion_rule_promotion_rule_id_foreign": {
|
||||
"constraintName": "promotion_promotion_rule_promotion_rule_id_foreign",
|
||||
"columnNames": ["promotion_rule_id"],
|
||||
"localTableName": "public.promotion_promotion_rule",
|
||||
"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_promotion_rule",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "application_method_promotion_rule_pkey",
|
||||
"columnNames": ["application_method_id", "promotion_rule_id"],
|
||||
"composite": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"application_method_promotion_rule_application_method_id_foreign": {
|
||||
"constraintName": "application_method_promotion_rule_application_method_id_foreign",
|
||||
"columnNames": ["application_method_id"],
|
||||
"localTableName": "public.application_method_promotion_rule",
|
||||
"referencedColumnNames": ["id"],
|
||||
"referencedTableName": "public.application_method",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"application_method_promotion_rule_promotion_rule_id_foreign": {
|
||||
"constraintName": "application_method_promotion_rule_promotion_rule_id_foreign",
|
||||
"columnNames": ["promotion_rule_id"],
|
||||
"localTableName": "public.application_method_promotion_rule",
|
||||
"referencedColumnNames": ["id"],
|
||||
"referencedTableName": "public.promotion_rule",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "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"
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
}
|
||||
},
|
||||
"name": "promotion_rule_value",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": ["promotion_rule_id"],
|
||||
"composite": false,
|
||||
"keyName": "IDX_promotion_rule_promotion_rule_value_id",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"keyName": "promotion_rule_value_pkey",
|
||||
"columnNames": ["id"],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"promotion_rule_value_promotion_rule_id_foreign": {
|
||||
"constraintName": "promotion_rule_value_promotion_rule_id_foreign",
|
||||
"columnNames": ["promotion_rule_id"],
|
||||
"localTableName": "public.promotion_rule_value",
|
||||
"referencedColumnNames": ["id"],
|
||||
"referencedTableName": "public.promotion_rule",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Migration } from "@mikro-orm/migrations"
|
||||
|
||||
export class Migration20231221104256 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
this.addSql(
|
||||
'create table "promotion" ("id" text not null, "code" text not null, "is_automatic" boolean not null default false, "type" text check ("type" in (\'standard\', \'buyget\')) not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "promotion_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql('create index "IDX_promotion_code" on "promotion" ("code");')
|
||||
this.addSql('create index "IDX_promotion_type" on "promotion" ("type");')
|
||||
this.addSql(
|
||||
'alter table "promotion" add constraint "IDX_promotion_code_unique" unique ("code");'
|
||||
)
|
||||
|
||||
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\', \'item\')) 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(
|
||||
'create index "IDX_application_method_type" on "application_method" ("type");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_application_method_target_type" on "application_method" ("target_type");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_application_method_allocation" on "application_method" ("allocation");'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "application_method" add constraint "application_method_promotion_id_unique" unique ("promotion_id");'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'alter table "application_method" add constraint "application_method_promotion_id_foreign" foreign key ("promotion_id") references "promotion" ("id") on update cascade;'
|
||||
)
|
||||
}
|
||||
}
|
||||
77
packages/promotion/src/migrations/Migration20240102130345.ts
Normal file
77
packages/promotion/src/migrations/Migration20240102130345.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Migration } from "@mikro-orm/migrations"
|
||||
|
||||
export class Migration20240102130345 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
this.addSql(
|
||||
'create table "promotion" ("id" text not null, "code" text not null, "is_automatic" boolean not null default false, "type" text check ("type" in (\'standard\', \'buyget\')) not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "promotion_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql('create index "IDX_promotion_code" on "promotion" ("code");')
|
||||
this.addSql('create index "IDX_promotion_type" on "promotion" ("type");')
|
||||
this.addSql(
|
||||
'alter table "promotion" add constraint "IDX_promotion_code_unique" unique ("code");'
|
||||
)
|
||||
|
||||
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\', \'item\')) 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(
|
||||
'create index "IDX_application_method_type" on "application_method" ("type");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_application_method_target_type" on "application_method" ("target_type");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_application_method_allocation" on "application_method" ("allocation");'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "application_method" add constraint "application_method_promotion_id_unique" unique ("promotion_id");'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'create table "promotion_rule" ("id" text not null, "description" text null, "attribute" text not null, "operator" text check ("operator" in (\'gte\', \'lte\', \'gt\', \'lt\', \'eq\', \'ne\', \'in\')) not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "promotion_rule_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_promotion_rule_attribute" on "promotion_rule" ("attribute");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_promotion_rule_operator" on "promotion_rule" ("operator");'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'create table "promotion_promotion_rule" ("promotion_id" text not null, "promotion_rule_id" text not null, constraint "promotion_promotion_rule_pkey" primary key ("promotion_id", "promotion_rule_id"));'
|
||||
)
|
||||
|
||||
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"));'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'create table "promotion_rule_value" ("id" text not null, "promotion_rule_id" text not null, "value" text not null, constraint "promotion_rule_value_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'create index "IDX_promotion_rule_promotion_rule_value_id" on "promotion_rule_value" ("promotion_rule_id");'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'alter table "application_method" add constraint "application_method_promotion_id_foreign" foreign key ("promotion_id") references "promotion" ("id") on update cascade;'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'alter table "promotion_promotion_rule" add constraint "promotion_promotion_rule_promotion_id_foreign" foreign key ("promotion_id") references "promotion" ("id") on update cascade on delete cascade;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "promotion_promotion_rule" add constraint "promotion_promotion_rule_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_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;'
|
||||
)
|
||||
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;'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'alter table "promotion_rule_value" add constraint "promotion_rule_value_promotion_rule_id_foreign" foreign key ("promotion_rule_id") references "promotion_rule" ("id") on update cascade on delete cascade;'
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@ import {
|
||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Enum,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OnInit,
|
||||
OneToOne,
|
||||
OptionalProps,
|
||||
@@ -16,8 +18,15 @@ import {
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import Promotion from "./promotion"
|
||||
import PromotionRule from "./promotion-rule"
|
||||
|
||||
type OptionalFields = "value" | "max_quantity" | "allocation"
|
||||
type OptionalFields =
|
||||
| "value"
|
||||
| "max_quantity"
|
||||
| "allocation"
|
||||
| "created_at"
|
||||
| "updated_at"
|
||||
| "deleted_at"
|
||||
@Entity()
|
||||
export default class ApplicationMethod {
|
||||
[OptionalProps]?: OptionalFields
|
||||
@@ -26,10 +35,10 @@ export default class ApplicationMethod {
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||
value?: number
|
||||
value?: number | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||
max_quantity?: number
|
||||
max_quantity?: number | null
|
||||
|
||||
@Index({ name: "IDX_application_method_type" })
|
||||
@Enum(() => PromotionUtils.ApplicationMethodType)
|
||||
@@ -51,12 +60,19 @@ export default class ApplicationMethod {
|
||||
})
|
||||
promotion: Promotion
|
||||
|
||||
@ManyToMany(() => PromotionRule, "application_methods", {
|
||||
owner: true,
|
||||
pivotTable: "application_method_promotion_rule",
|
||||
cascade: ["soft-remove"] as any,
|
||||
})
|
||||
target_rules = new Collection<PromotionRule>(this)
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at?: Date
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
@@ -64,18 +80,18 @@ export default class ApplicationMethod {
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at?: Date
|
||||
updated_at: Date
|
||||
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
deleted_at: Date | null
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "app_method")
|
||||
this.id = generateEntityId(this.id, "proappmet")
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "promo")
|
||||
this.id = generateEntityId(this.id, "proappmet")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export { default as ApplicationMethod } from "./application-method"
|
||||
export { default as Promotion } from "./promotion"
|
||||
export { default as PromotionRule } from "./promotion-rule"
|
||||
export { default as PromotionRuleValue } from "./promotion-rule-value"
|
||||
|
||||
37
packages/promotion/src/models/promotion-rule-value.ts
Normal file
37
packages/promotion/src/models/promotion-rule-value.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import PromotionRule from "./promotion-rule"
|
||||
|
||||
@Entity()
|
||||
export default class PromotionRuleValue {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@ManyToOne(() => PromotionRule, {
|
||||
onDelete: "cascade",
|
||||
fieldName: "promotion_rule_id",
|
||||
index: "IDX_promotion_rule_promotion_rule_value_id",
|
||||
})
|
||||
promotion_rule: PromotionRule
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
value: string
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "prorulval")
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "prorulval")
|
||||
}
|
||||
}
|
||||
83
packages/promotion/src/models/promotion-rule.ts
Normal file
83
packages/promotion/src/models/promotion-rule.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { PromotionRuleOperatorValues } from "@medusajs/types"
|
||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Collection,
|
||||
Entity,
|
||||
Enum,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
OptionalProps,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import ApplicationMethod from "./application-method"
|
||||
import Promotion from "./promotion"
|
||||
import PromotionRuleValue from "./promotion-rule-value"
|
||||
|
||||
type OptionalFields = "description" | "created_at" | "updated_at" | "deleted_at"
|
||||
type OptionalRelations = "values" | "promotions"
|
||||
|
||||
@Entity()
|
||||
export default class PromotionRule {
|
||||
[OptionalProps]?: OptionalFields | OptionalRelations
|
||||
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
description: string | null
|
||||
|
||||
@Index({ name: "IDX_promotion_rule_attribute" })
|
||||
@Property({ columnType: "text" })
|
||||
attribute: string
|
||||
|
||||
@Index({ name: "IDX_promotion_rule_operator" })
|
||||
@Enum(() => PromotionUtils.PromotionRuleOperator)
|
||||
operator: PromotionRuleOperatorValues
|
||||
|
||||
@OneToMany(() => PromotionRuleValue, (prv) => prv.promotion_rule, {
|
||||
cascade: [Cascade.REMOVE],
|
||||
})
|
||||
values = new Collection<PromotionRuleValue>(this)
|
||||
|
||||
@ManyToMany(() => Promotion, (promotion) => promotion.rules)
|
||||
promotions = new Collection<Promotion>(this)
|
||||
|
||||
@ManyToMany(
|
||||
() => ApplicationMethod,
|
||||
(applicationMethod) => applicationMethod.target_rules
|
||||
)
|
||||
application_methods = new Collection<ApplicationMethod>(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, "prorul")
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "prorul")
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@ import { PromotionType } from "@medusajs/types"
|
||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Enum,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OnInit,
|
||||
OneToOne,
|
||||
OptionalProps,
|
||||
@@ -13,8 +15,13 @@ import {
|
||||
Unique,
|
||||
} from "@mikro-orm/core"
|
||||
import ApplicationMethod from "./application-method"
|
||||
import PromotionRule from "./promotion-rule"
|
||||
|
||||
type OptionalFields = "is_automatic"
|
||||
type OptionalFields =
|
||||
| "is_automatic"
|
||||
| "created_at"
|
||||
| "updated_at"
|
||||
| "deleted_at"
|
||||
type OptionalRelations = "application_method"
|
||||
@Entity()
|
||||
export default class Promotion {
|
||||
@@ -44,12 +51,19 @@ export default class Promotion {
|
||||
})
|
||||
application_method: ApplicationMethod
|
||||
|
||||
@ManyToMany(() => PromotionRule, "promotions", {
|
||||
owner: true,
|
||||
pivotTable: "promotion_promotion_rule",
|
||||
cascade: ["soft-remove"] as any,
|
||||
})
|
||||
rules = new Collection<PromotionRule>(this)
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at?: Date
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
@@ -57,10 +71,10 @@ export default class Promotion {
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at?: Date
|
||||
updated_at: Date
|
||||
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
deleted_at: Date | null
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
|
||||
export { ApplicationMethodRepository } from "./application-method"
|
||||
export { PromotionRepository } from "./promotion"
|
||||
export { PromotionRuleRepository } from "./promotion-rule"
|
||||
export { PromotionRuleValueRepository } from "./promotion-rule-value"
|
||||
|
||||
128
packages/promotion/src/repositories/promotion-rule-value.ts
Normal file
128
packages/promotion/src/repositories/promotion-rule-value.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Context, DAL } from "@medusajs/types"
|
||||
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
|
||||
import {
|
||||
FilterQuery as MikroFilterQuery,
|
||||
FindOptions as MikroOptions,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { PromotionRuleValue } from "@models"
|
||||
import {
|
||||
CreatePromotionRuleValueDTO,
|
||||
UpdatePromotionRuleValueDTO,
|
||||
} from "@types"
|
||||
|
||||
export class PromotionRuleValueRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(...arguments)
|
||||
this.manager_ = manager
|
||||
}
|
||||
|
||||
async find(
|
||||
findOptions: DAL.FindOptions<PromotionRuleValue> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<PromotionRuleValue[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
return await manager.find(
|
||||
PromotionRuleValue,
|
||||
findOptions_.where as MikroFilterQuery<PromotionRuleValue>,
|
||||
findOptions_.options as MikroOptions<PromotionRuleValue>
|
||||
)
|
||||
}
|
||||
|
||||
async findAndCount(
|
||||
findOptions: DAL.FindOptions<PromotionRuleValue> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<[PromotionRuleValue[], number]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
return await manager.findAndCount(
|
||||
PromotionRuleValue,
|
||||
findOptions_.where as MikroFilterQuery<PromotionRuleValue>,
|
||||
findOptions_.options as MikroOptions<PromotionRuleValue>
|
||||
)
|
||||
}
|
||||
|
||||
async delete(ids: string[], context: Context = {}): Promise<void> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
await manager.nativeDelete(PromotionRuleValue, { id: { $in: ids } }, {})
|
||||
}
|
||||
|
||||
async create(
|
||||
data: CreatePromotionRuleValueDTO[],
|
||||
context: Context = {}
|
||||
): Promise<PromotionRuleValue[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const promotionRuleValue = data.map((promotionRuleValue) => {
|
||||
return manager.create(PromotionRuleValue, promotionRuleValue)
|
||||
})
|
||||
|
||||
manager.persist(promotionRuleValue)
|
||||
|
||||
return promotionRuleValue
|
||||
}
|
||||
|
||||
async update(
|
||||
data: UpdatePromotionRuleValueDTO[],
|
||||
context: Context = {}
|
||||
): Promise<PromotionRuleValue[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
const promotionRuleValueIds = data.map(
|
||||
(promotionRuleValue) => promotionRuleValue.id
|
||||
)
|
||||
const existingPromotionRuleValues = await this.find(
|
||||
{
|
||||
where: {
|
||||
id: {
|
||||
$in: promotionRuleValueIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
const dataAndExistingIdDifference = arrayDifference(
|
||||
data.map((d) => d.id),
|
||||
existingPromotionRuleValues.map((plr) => plr.id)
|
||||
)
|
||||
|
||||
if (dataAndExistingIdDifference.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`PromotionRuleValue with id(s) "${dataAndExistingIdDifference.join(
|
||||
", "
|
||||
)}" not found`
|
||||
)
|
||||
}
|
||||
|
||||
const existingPromotionRuleValueMap = new Map(
|
||||
existingPromotionRuleValues.map<[string, PromotionRuleValue]>(
|
||||
(promotionRuleValue) => [promotionRuleValue.id, promotionRuleValue]
|
||||
)
|
||||
)
|
||||
|
||||
const promotionRuleValue = data.map((promotionRuleValueData) => {
|
||||
const existingPromotionRuleValue = existingPromotionRuleValueMap.get(
|
||||
promotionRuleValueData.id
|
||||
)!
|
||||
|
||||
return manager.assign(existingPromotionRuleValue, promotionRuleValueData)
|
||||
})
|
||||
|
||||
manager.persist(promotionRuleValue)
|
||||
|
||||
return promotionRuleValue
|
||||
}
|
||||
}
|
||||
124
packages/promotion/src/repositories/promotion-rule.ts
Normal file
124
packages/promotion/src/repositories/promotion-rule.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Context, DAL } from "@medusajs/types"
|
||||
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
|
||||
import {
|
||||
FilterQuery as MikroFilterQuery,
|
||||
FindOptions as MikroOptions,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { PromotionRule } from "@models"
|
||||
import { CreatePromotionRuleDTO, UpdatePromotionRuleDTO } from "@types"
|
||||
|
||||
export class PromotionRuleRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(...arguments)
|
||||
this.manager_ = manager
|
||||
}
|
||||
|
||||
async find(
|
||||
findOptions: DAL.FindOptions<PromotionRule> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<PromotionRule[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
return await manager.find(
|
||||
PromotionRule,
|
||||
findOptions_.where as MikroFilterQuery<PromotionRule>,
|
||||
findOptions_.options as MikroOptions<PromotionRule>
|
||||
)
|
||||
}
|
||||
|
||||
async findAndCount(
|
||||
findOptions: DAL.FindOptions<PromotionRule> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<[PromotionRule[], number]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
return await manager.findAndCount(
|
||||
PromotionRule,
|
||||
findOptions_.where as MikroFilterQuery<PromotionRule>,
|
||||
findOptions_.options as MikroOptions<PromotionRule>
|
||||
)
|
||||
}
|
||||
|
||||
async delete(ids: string[], context: Context = {}): Promise<void> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
await manager.nativeDelete(PromotionRule, { id: { $in: ids } }, {})
|
||||
}
|
||||
|
||||
async create(
|
||||
data: CreatePromotionRuleDTO[],
|
||||
context: Context = {}
|
||||
): Promise<PromotionRule[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const promotionRule = data.map((promotionRule) => {
|
||||
return manager.create(PromotionRule, promotionRule)
|
||||
})
|
||||
|
||||
manager.persist(promotionRule)
|
||||
|
||||
return promotionRule
|
||||
}
|
||||
|
||||
async update(
|
||||
data: UpdatePromotionRuleDTO[],
|
||||
context: Context = {}
|
||||
): Promise<PromotionRule[]> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||
const promotionRuleIds = data.map((promotionRule) => promotionRule.id)
|
||||
const existingPromotionRules = await this.find(
|
||||
{
|
||||
where: {
|
||||
id: {
|
||||
$in: promotionRuleIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
const dataAndExistingIdDifference = arrayDifference(
|
||||
data.map((d) => d.id),
|
||||
existingPromotionRules.map((plr) => plr.id)
|
||||
)
|
||||
|
||||
if (dataAndExistingIdDifference.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`PromotionRule with id(s) "${dataAndExistingIdDifference.join(
|
||||
", "
|
||||
)}" not found`
|
||||
)
|
||||
}
|
||||
|
||||
const existingPromotionRuleMap = new Map(
|
||||
existingPromotionRules.map<[string, PromotionRule]>((promotionRule) => [
|
||||
promotionRule.id,
|
||||
promotionRule,
|
||||
])
|
||||
)
|
||||
|
||||
const promotionRule = data.map((promotionRuleData) => {
|
||||
const existingPromotionRule = existingPromotionRuleMap.get(
|
||||
promotionRuleData.id
|
||||
)!
|
||||
|
||||
return manager.assign(existingPromotionRule, promotionRuleData)
|
||||
})
|
||||
|
||||
manager.persist(promotionRule)
|
||||
|
||||
return promotionRule
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export { default as ApplicationMethodService } from "./application-method"
|
||||
export { default as PromotionService } from "./promotion"
|
||||
export { default as PromotionModuleService } from "./promotion-module"
|
||||
export { default as PromotionRuleService } from "./promotion-rule"
|
||||
export { default as PromotionRuleValueService } from "./promotion-rule-value"
|
||||
|
||||
@@ -11,16 +11,26 @@ import {
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
} from "@medusajs/utils"
|
||||
import { Promotion } from "@models"
|
||||
import { ApplicationMethodService, PromotionService } from "@services"
|
||||
import { ApplicationMethod, Promotion } from "@models"
|
||||
import {
|
||||
ApplicationMethodService,
|
||||
PromotionRuleService,
|
||||
PromotionRuleValueService,
|
||||
PromotionService,
|
||||
} from "@services"
|
||||
import { joinerConfig } from "../joiner-config"
|
||||
import { CreateApplicationMethodDTO, CreatePromotionDTO } from "../types"
|
||||
import { validateApplicationMethodAttributes } from "../utils"
|
||||
import {
|
||||
validateApplicationMethodAttributes,
|
||||
validatePromotionRuleAttributes,
|
||||
} from "../utils"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
promotionService: PromotionService
|
||||
applicationMethodService: ApplicationMethodService
|
||||
promotionRuleService: PromotionRuleService
|
||||
promotionRuleValueService: PromotionRuleValueService
|
||||
}
|
||||
|
||||
export default class PromotionModuleService<
|
||||
@@ -30,18 +40,24 @@ export default class PromotionModuleService<
|
||||
protected baseRepository_: DAL.RepositoryService
|
||||
protected promotionService_: PromotionService
|
||||
protected applicationMethodService_: ApplicationMethodService
|
||||
protected promotionRuleService_: PromotionRuleService
|
||||
protected promotionRuleValueService_: PromotionRuleValueService
|
||||
|
||||
constructor(
|
||||
{
|
||||
baseRepository,
|
||||
promotionService,
|
||||
applicationMethodService,
|
||||
promotionRuleService,
|
||||
promotionRuleValueService,
|
||||
}: InjectedDependencies,
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
) {
|
||||
this.baseRepository_ = baseRepository
|
||||
this.promotionService_ = promotionService
|
||||
this.applicationMethodService_ = applicationMethodService
|
||||
this.promotionRuleService_ = promotionRuleService
|
||||
this.promotionRuleValueService_ = promotionRuleValueService
|
||||
}
|
||||
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
@@ -78,7 +94,7 @@ export default class PromotionModuleService<
|
||||
return await this.list(
|
||||
{ id: promotions.map((p) => p!.id) },
|
||||
{
|
||||
relations: ["application_method"],
|
||||
relations: ["application_method", "rules", "rules.values"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
@@ -89,15 +105,26 @@ export default class PromotionModuleService<
|
||||
data: PromotionTypes.CreatePromotionDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const promotionsData: CreatePromotionDTO[] = []
|
||||
const applicationMethodsData: CreateApplicationMethodDTO[] = []
|
||||
|
||||
const promotionCodeApplicationMethodDataMap = new Map<
|
||||
string,
|
||||
PromotionTypes.CreateApplicationMethodDTO
|
||||
>()
|
||||
const promotionsData: CreatePromotionDTO[] = []
|
||||
const applicationMethodsData: CreateApplicationMethodDTO[] = []
|
||||
|
||||
const promotionCodeRulesDataMap = new Map<
|
||||
string,
|
||||
PromotionTypes.CreatePromotionRuleDTO[]
|
||||
>()
|
||||
const applicationMethodRuleMap = new Map<
|
||||
string,
|
||||
PromotionTypes.CreatePromotionRuleDTO[]
|
||||
>()
|
||||
|
||||
for (const {
|
||||
application_method: applicationMethodData,
|
||||
rules: rulesData,
|
||||
...promotionData
|
||||
} of data) {
|
||||
if (applicationMethodData) {
|
||||
@@ -107,33 +134,94 @@ export default class PromotionModuleService<
|
||||
)
|
||||
}
|
||||
|
||||
if (rulesData) {
|
||||
promotionCodeRulesDataMap.set(promotionData.code, rulesData)
|
||||
}
|
||||
|
||||
promotionsData.push(promotionData)
|
||||
}
|
||||
|
||||
const createdPromotions = await this.promotionService_.create(
|
||||
data,
|
||||
promotionsData,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
for (const promotion of createdPromotions) {
|
||||
const data = promotionCodeApplicationMethodDataMap.get(promotion.code)
|
||||
const applMethodData = promotionCodeApplicationMethodDataMap.get(
|
||||
promotion.code
|
||||
)
|
||||
|
||||
if (!data) continue
|
||||
if (applMethodData) {
|
||||
const {
|
||||
target_rules: targetRulesData = [],
|
||||
...applicationMethodWithoutRules
|
||||
} = applMethodData
|
||||
const applicationMethodData = {
|
||||
...applicationMethodWithoutRules,
|
||||
promotion,
|
||||
}
|
||||
|
||||
const applicationMethodData = {
|
||||
...data,
|
||||
promotion,
|
||||
validateApplicationMethodAttributes(applicationMethodData)
|
||||
applicationMethodsData.push(applicationMethodData)
|
||||
|
||||
if (targetRulesData.length) {
|
||||
applicationMethodRuleMap.set(promotion.id, targetRulesData)
|
||||
}
|
||||
}
|
||||
|
||||
validateApplicationMethodAttributes(applicationMethodData)
|
||||
applicationMethodsData.push(applicationMethodData)
|
||||
await this.createPromotionRulesAndValues(
|
||||
promotionCodeRulesDataMap.get(promotion.code) || [],
|
||||
"promotions",
|
||||
promotion,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
await this.applicationMethodService_.create(
|
||||
applicationMethodsData,
|
||||
sharedContext
|
||||
)
|
||||
const createdApplicationMethods =
|
||||
await this.applicationMethodService_.create(
|
||||
applicationMethodsData,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
for (const applicationMethod of createdApplicationMethods) {
|
||||
await this.createPromotionRulesAndValues(
|
||||
applicationMethodRuleMap.get(applicationMethod.promotion.id) || [],
|
||||
"application_methods",
|
||||
applicationMethod,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
return createdPromotions
|
||||
}
|
||||
|
||||
protected async createPromotionRulesAndValues(
|
||||
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||
relationName: "promotions" | "application_methods",
|
||||
relation: Promotion | ApplicationMethod,
|
||||
sharedContext: Context
|
||||
) {
|
||||
validatePromotionRuleAttributes(rulesData)
|
||||
|
||||
for (const ruleData of rulesData) {
|
||||
const { values, ...rest } = ruleData
|
||||
const promotionRuleData = {
|
||||
...rest,
|
||||
[relationName]: [relation],
|
||||
}
|
||||
|
||||
const [createdPromotionRule] = await this.promotionRuleService_.create(
|
||||
[promotionRuleData],
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const ruleValues = Array.isArray(values) ? values : [values]
|
||||
const promotionRuleValuesData = ruleValues.map((ruleValue) => ({
|
||||
value: ruleValue,
|
||||
promotion_rule: createdPromotionRule,
|
||||
}))
|
||||
|
||||
await this.promotionRuleValueService_.create(promotionRuleValuesData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
108
packages/promotion/src/services/promotion-rule-value.ts
Normal file
108
packages/promotion/src/services/promotion-rule-value.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Context, DAL, FindConfig, PromotionTypes } from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
ModulesSdkUtils,
|
||||
retrieveEntity,
|
||||
} from "@medusajs/utils"
|
||||
import { PromotionRuleValue } from "@models"
|
||||
import { PromotionRuleValueRepository } from "@repositories"
|
||||
import {
|
||||
CreatePromotionRuleValueDTO,
|
||||
UpdatePromotionRuleValueDTO,
|
||||
} from "../types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
promotionRuleValueRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
export default class PromotionRuleValueService<
|
||||
TEntity extends PromotionRuleValue = PromotionRuleValue
|
||||
> {
|
||||
protected readonly promotionRuleValueRepository_: DAL.RepositoryService
|
||||
|
||||
constructor({ promotionRuleValueRepository }: InjectedDependencies) {
|
||||
this.promotionRuleValueRepository_ = promotionRuleValueRepository
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleValueRepository_")
|
||||
async retrieve(
|
||||
promotionRuleValueId: string,
|
||||
config: FindConfig<PromotionTypes.PromotionRuleValueDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await retrieveEntity<
|
||||
PromotionRuleValue,
|
||||
PromotionTypes.PromotionRuleValueDTO
|
||||
>({
|
||||
id: promotionRuleValueId,
|
||||
entityName: PromotionRuleValue.name,
|
||||
repository: this.promotionRuleValueRepository_,
|
||||
config,
|
||||
sharedContext,
|
||||
})) as TEntity
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleValueRepository_")
|
||||
async list(
|
||||
filters: PromotionTypes.FilterablePromotionRuleValueProps = {},
|
||||
config: FindConfig<PromotionTypes.PromotionRuleValueDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<PromotionRuleValue>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
|
||||
return (await this.promotionRuleValueRepository_.find(
|
||||
queryOptions,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleValueRepository_")
|
||||
async listAndCount(
|
||||
filters: PromotionTypes.FilterablePromotionRuleValueProps = {},
|
||||
config: FindConfig<PromotionTypes.PromotionRuleValueDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TEntity[], number]> {
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<PromotionRuleValue>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
|
||||
return (await this.promotionRuleValueRepository_.findAndCount(
|
||||
queryOptions,
|
||||
sharedContext
|
||||
)) as [TEntity[], number]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleValueRepository_")
|
||||
async create(
|
||||
data: CreatePromotionRuleValueDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.promotionRuleValueRepository_ as PromotionRuleValueRepository
|
||||
).create(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleValueRepository_")
|
||||
async update(
|
||||
data: UpdatePromotionRuleValueDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.promotionRuleValueRepository_ as PromotionRuleValueRepository
|
||||
).update(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleValueRepository_")
|
||||
async delete(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.promotionRuleValueRepository_.delete(ids, sharedContext)
|
||||
}
|
||||
}
|
||||
105
packages/promotion/src/services/promotion-rule.ts
Normal file
105
packages/promotion/src/services/promotion-rule.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Context, DAL, FindConfig, PromotionTypes } from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
ModulesSdkUtils,
|
||||
retrieveEntity,
|
||||
} from "@medusajs/utils"
|
||||
import { PromotionRule } from "@models"
|
||||
import { PromotionRuleRepository } from "@repositories"
|
||||
import { CreatePromotionRuleDTO, UpdatePromotionRuleDTO } from "../types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
promotionRuleRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
export default class PromotionRuleService<
|
||||
TEntity extends PromotionRule = PromotionRule
|
||||
> {
|
||||
protected readonly promotionRuleRepository_: DAL.RepositoryService
|
||||
|
||||
constructor({ promotionRuleRepository }: InjectedDependencies) {
|
||||
this.promotionRuleRepository_ = promotionRuleRepository
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleRepository_")
|
||||
async retrieve(
|
||||
promotionRuleId: string,
|
||||
config: FindConfig<PromotionTypes.PromotionRuleDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await retrieveEntity<
|
||||
PromotionRule,
|
||||
PromotionTypes.PromotionRuleDTO
|
||||
>({
|
||||
id: promotionRuleId,
|
||||
entityName: PromotionRule.name,
|
||||
repository: this.promotionRuleRepository_,
|
||||
config,
|
||||
sharedContext,
|
||||
})) as TEntity
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleRepository_")
|
||||
async list(
|
||||
filters: PromotionTypes.FilterablePromotionRuleProps = {},
|
||||
config: FindConfig<PromotionTypes.PromotionRuleDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<PromotionRule>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
|
||||
return (await this.promotionRuleRepository_.find(
|
||||
queryOptions,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectManager("promotionRuleRepository_")
|
||||
async listAndCount(
|
||||
filters: PromotionTypes.FilterablePromotionRuleProps = {},
|
||||
config: FindConfig<PromotionTypes.PromotionRuleDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TEntity[], number]> {
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<PromotionRule>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
|
||||
return (await this.promotionRuleRepository_.findAndCount(
|
||||
queryOptions,
|
||||
sharedContext
|
||||
)) as [TEntity[], number]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleRepository_")
|
||||
async create(
|
||||
data: CreatePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.promotionRuleRepository_ as PromotionRuleRepository
|
||||
).create(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleRepository_")
|
||||
async update(
|
||||
data: UpdatePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.promotionRuleRepository_ as PromotionRuleRepository
|
||||
).update(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("promotionRuleRepository_")
|
||||
async delete(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.promotionRuleRepository_.delete(ids, sharedContext)
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,5 @@ export type InitializeModuleInjectableDependencies = {
|
||||
|
||||
export * from "./application-method"
|
||||
export * from "./promotion"
|
||||
export * from "./promotion-rule"
|
||||
export * from "./promotion-rule-value"
|
||||
|
||||
12
packages/promotion/src/types/promotion-rule-value.ts
Normal file
12
packages/promotion/src/types/promotion-rule-value.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { PromotionRuleDTO } from "@medusajs/types"
|
||||
|
||||
export interface CreatePromotionRuleValueDTO {
|
||||
value: any
|
||||
promotion_rule: string | PromotionRuleDTO
|
||||
}
|
||||
|
||||
export interface UpdatePromotionRuleValueDTO {
|
||||
id: string
|
||||
value: any
|
||||
promotion_rule: string | PromotionRuleDTO
|
||||
}
|
||||
11
packages/promotion/src/types/promotion-rule.ts
Normal file
11
packages/promotion/src/types/promotion-rule.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { PromotionRuleOperatorValues } from "@medusajs/types"
|
||||
|
||||
export interface CreatePromotionRuleDTO {
|
||||
description?: string
|
||||
attribute: string
|
||||
operator: PromotionRuleOperatorValues
|
||||
}
|
||||
|
||||
export interface UpdatePromotionRuleDTO {
|
||||
id: string
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./application-method"
|
||||
export * from "./promotion-rule"
|
||||
|
||||
39
packages/promotion/src/utils/validations/promotion-rule.ts
Normal file
39
packages/promotion/src/utils/validations/promotion-rule.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { PromotionRuleOperatorValues } from "@medusajs/types"
|
||||
import { MedusaError, PromotionRuleOperator, isPresent } from "@medusajs/utils"
|
||||
import { CreatePromotionRuleDTO } from "../../types"
|
||||
|
||||
export function validatePromotionRuleAttributes(
|
||||
promotionRulesData: CreatePromotionRuleDTO[]
|
||||
) {
|
||||
const errors: string[] = []
|
||||
|
||||
for (const promotionRuleData of promotionRulesData) {
|
||||
if (!isPresent(promotionRuleData.attribute)) {
|
||||
errors.push("rules[].attribute is a required field")
|
||||
}
|
||||
|
||||
if (!isPresent(promotionRuleData.operator)) {
|
||||
errors.push("rules[].operator is a required field")
|
||||
}
|
||||
|
||||
if (isPresent(promotionRuleData.operator)) {
|
||||
const allowedOperators: PromotionRuleOperatorValues[] = Object.values(
|
||||
PromotionRuleOperator
|
||||
)
|
||||
|
||||
if (!allowedOperators.includes(promotionRuleData.operator)) {
|
||||
errors.push(
|
||||
`rules[].operator (${
|
||||
promotionRuleData.operator
|
||||
}) is invalid. It should be one of ${allowedOperators.join(", ")}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
errors.push("rules[].operator is a required field")
|
||||
}
|
||||
}
|
||||
|
||||
if (!errors.length) return
|
||||
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, errors.join(", "))
|
||||
}
|
||||
@@ -23,7 +23,9 @@
|
||||
"paths": {
|
||||
"@models": ["./src/models"],
|
||||
"@services": ["./src/services"],
|
||||
"@repositories": ["./src/repositories"]
|
||||
"@repositories": ["./src/repositories"],
|
||||
"@types": ["./src/types"],
|
||||
"@utils": ["./src/utils"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import { PromotionDTO } from "./promotion"
|
||||
import { CreatePromotionRuleDTO } from "./promotion-rule"
|
||||
|
||||
export type ApplicationMethodType = "fixed" | "percentage"
|
||||
export type ApplicationMethodTargetType = "order" | "shipping" | "item"
|
||||
@@ -16,6 +17,7 @@ export interface CreateApplicationMethodDTO {
|
||||
value?: number
|
||||
max_quantity?: number
|
||||
promotion?: PromotionDTO | string
|
||||
target_rules?: CreatePromotionRuleDTO[]
|
||||
}
|
||||
|
||||
export interface UpdateApplicationMethodDTO {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from "./application-method"
|
||||
export * from "./promotion"
|
||||
export * from "./promotion-rule"
|
||||
export * from "./promotion-rule-value"
|
||||
|
||||
20
packages/types/src/promotion/common/promotion-rule-value.ts
Normal file
20
packages/types/src/promotion/common/promotion-rule-value.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import { PromotionRuleDTO } from "./promotion-rule"
|
||||
|
||||
export interface PromotionRuleValueDTO {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface CreatePromotionRuleValueDTO {
|
||||
value: any
|
||||
promotion_rule: PromotionRuleDTO
|
||||
}
|
||||
|
||||
export interface UpdatePromotionRuleValueDTO {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface FilterablePromotionRuleValueProps
|
||||
extends BaseFilterable<FilterablePromotionRuleValueProps> {
|
||||
id?: string[]
|
||||
}
|
||||
30
packages/types/src/promotion/common/promotion-rule.ts
Normal file
30
packages/types/src/promotion/common/promotion-rule.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { BaseFilterable } from "../../dal"
|
||||
|
||||
export type PromotionRuleOperatorValues =
|
||||
| "gt"
|
||||
| "lt"
|
||||
| "eq"
|
||||
| "ne"
|
||||
| "in"
|
||||
| "lte"
|
||||
| "gte"
|
||||
|
||||
export interface PromotionRuleDTO {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface CreatePromotionRuleDTO {
|
||||
description?: string
|
||||
attribute: string
|
||||
operator: PromotionRuleOperatorValues
|
||||
values: string[] | string
|
||||
}
|
||||
|
||||
export interface UpdatePromotionRuleDTO {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface FilterablePromotionRuleProps
|
||||
extends BaseFilterable<FilterablePromotionRuleProps> {
|
||||
id?: string[]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import { CreateApplicationMethodDTO } from "./application-method"
|
||||
import { CreatePromotionRuleDTO } from "./promotion-rule"
|
||||
|
||||
export type PromotionType = "standard" | "buyget"
|
||||
|
||||
@@ -12,6 +13,7 @@ export interface CreatePromotionDTO {
|
||||
type: PromotionType
|
||||
is_automatic?: boolean
|
||||
application_method?: CreateApplicationMethodDTO
|
||||
rules?: CreatePromotionRuleDTO[]
|
||||
}
|
||||
|
||||
export interface UpdatePromotionDTO {
|
||||
|
||||
61
packages/utils/src/common/__tests__/is-present.spec.ts
Normal file
61
packages/utils/src/common/__tests__/is-present.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { isPresent } from "../is-present"
|
||||
|
||||
describe("isPresent", function () {
|
||||
it("should return true or false for different types of data", function () {
|
||||
const expectations = [
|
||||
{
|
||||
input: null,
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
input: undefined,
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
input: "Testing",
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
input: {},
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
input: { test: 1 },
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
input: [],
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
input: [{ test: 1 }],
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
input: new Map([["test", "test"]]),
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
input: new Map([]),
|
||||
output: false,
|
||||
},
|
||||
|
||||
{
|
||||
input: new Set(["test"]),
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
input: new Set([]),
|
||||
output: false,
|
||||
},
|
||||
]
|
||||
|
||||
expectations.forEach((expectation) => {
|
||||
expect(isPresent(expectation.input)).toEqual(expectation.output)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -8,14 +8,15 @@ export * from "./deep-equal-obj"
|
||||
export * from "./errors"
|
||||
export * from "./generate-entity-id"
|
||||
export * from "./get-config-file"
|
||||
export * from "./get-iso-string-from-date"
|
||||
export * from "./group-by"
|
||||
export * from "./handle-postgres-database-error"
|
||||
export * from "./is-date"
|
||||
export * from "./is-defined"
|
||||
export * from "./is-email"
|
||||
export * from "./is-object"
|
||||
export * from "./is-present"
|
||||
export * from "./is-string"
|
||||
export * from "./get-iso-string-from-date"
|
||||
export * from "./lower-case-first"
|
||||
export * from "./map-object-to"
|
||||
export * from "./medusa-container"
|
||||
|
||||
23
packages/utils/src/common/is-present.ts
Normal file
23
packages/utils/src/common/is-present.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { isDefined } from "./is-defined"
|
||||
import { isObject } from "./is-object"
|
||||
import { isString } from "./is-string"
|
||||
|
||||
export function isPresent(value: any): boolean {
|
||||
if (!isDefined(value) || value === null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isString(value) || Array.isArray(value)) {
|
||||
return value.length > 0
|
||||
}
|
||||
|
||||
if (value instanceof Map || value instanceof Set) {
|
||||
return value.size > 0
|
||||
}
|
||||
|
||||
if (isObject(value)) {
|
||||
return Object.keys(value).length > 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -18,3 +18,13 @@ export enum ApplicationMethodAllocation {
|
||||
EACH = "each",
|
||||
ACROSS = "across",
|
||||
}
|
||||
|
||||
export enum PromotionRuleOperator {
|
||||
GTE = "gte",
|
||||
LTE = "lte",
|
||||
GT = "gt",
|
||||
LT = "lt",
|
||||
EQ = "eq",
|
||||
NE = "ne",
|
||||
IN = "in",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user