feat(pricing,types): price list API + price calculations with price lists (#5498)

**what:**

**PriceList Service APIs:**

- createPriceList
- updatePriceList
- addPriceListPrices
- removePriceListRules
- setPriceListRules
- deletePriceList
- listPriceLists
- listAndCountPriceLists

**Price Calculations**

- Returns prices with price list prices
- Returns a new shape with calculated and original prices


Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
Riqwan Thamir
2023-11-15 21:24:29 +01:00
committed by GitHub
parent 2947f57db1
commit 1772e80ed1
58 changed files with 5745 additions and 1112 deletions
+16
View File
@@ -32,6 +32,13 @@ export default async ({
priceSetMoneyAmountService: asClass(
defaultServices.PriceSetMoneyAmountService
).singleton(),
priceListService: asClass(defaultServices.PriceListService).singleton(),
priceListRuleService: asClass(
defaultServices.PriceListRuleService
).singleton(),
priceListRuleValueService: asClass(
defaultServices.PriceListRuleValueService
).singleton(),
})
if (customRepositories) {
@@ -75,5 +82,14 @@ function loadDefaultRepositories({ container }) {
priceSetMoneyAmountRepository: asClass(
defaultRepositories.PriceSetMoneyAmountRepository
).singleton(),
priceListRepository: asClass(
defaultRepositories.PriceListRepository
).singleton(),
priceListRuleRepository: asClass(
defaultRepositories.PriceListRuleRepository
).singleton(),
priceListRuleValueRepository: asClass(
defaultRepositories.PriceListRuleValueRepository
).singleton(),
})
}
@@ -146,6 +146,151 @@
}
}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"title": {
"name": "title",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"description": {
"name": "description",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"status": {
"name": "status",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "'draft'",
"enumItems": [
"active",
"draft"
],
"mappedType": "enum"
},
"type": {
"name": "type",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "'sale'",
"enumItems": [
"sale",
"override"
],
"mappedType": "enum"
},
"starts_at": {
"name": "starts_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"ends_at": {
"name": "ends_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"number_rules": {
"name": "number_rules",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "0",
"mappedType": "integer"
},
"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": "price_list",
"schema": "public",
"indexes": [
{
"columnNames": [
"deleted_at"
],
"composite": false,
"keyName": "IDX_price_list_deleted_at",
"primary": false,
"unique": false
},
{
"keyName": "price_list_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
@@ -221,6 +366,15 @@
"nullable": false,
"default": "0",
"mappedType": "integer"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
}
},
"name": "price_set_money_amount",
@@ -244,6 +398,15 @@
"primary": false,
"unique": false
},
{
"columnNames": [
"price_list_id"
],
"composite": false,
"keyName": "IDX_price_rule_price_list_id",
"primary": false,
"unique": false
},
{
"keyName": "price_set_money_amount_pkey",
"columnNames": [
@@ -281,6 +444,19 @@
"referencedTableName": "public.money_amount",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_money_amount_price_list_id_foreign": {
"constraintName": "price_set_money_amount_price_list_id_foreign",
"columnNames": [
"price_list_id"
],
"localTableName": "public.price_set_money_amount",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list",
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
@@ -604,15 +780,6 @@
"primary": false,
"nullable": false,
"mappedType": "text"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "price_rule",
@@ -696,6 +863,184 @@
"updateRule": "cascade"
}
}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"rule_type_id": {
"name": "rule_type_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"priority": {
"name": "priority",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "0",
"mappedType": "integer"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "price_list_rule",
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_type_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_rule_type_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"price_list_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_id",
"primary": false,
"unique": false
},
{
"keyName": "IDX_price_list_rule_rule_type_id_price_list_id_unique",
"columnNames": [
"price_list_id",
"rule_type_id"
],
"composite": true,
"primary": false,
"unique": true
},
{
"keyName": "price_list_rule_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"price_list_rule_rule_type_id_foreign": {
"constraintName": "price_list_rule_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
},
"price_list_rule_price_list_id_foreign": {
"constraintName": "price_list_rule_price_list_id_foreign",
"columnNames": [
"price_list_id"
],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list",
"updateRule": "cascade"
}
}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"price_list_rule_id": {
"name": "price_list_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": "price_list_rule_value",
"schema": "public",
"indexes": [
{
"columnNames": [
"price_list_rule_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_rule_value_id",
"primary": false,
"unique": false
},
{
"keyName": "price_list_rule_value_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"price_list_rule_value_price_list_rule_id_foreign": {
"constraintName": "price_list_rule_value_price_list_rule_id_foreign",
"columnNames": [
"price_list_rule_id"
],
"localTableName": "public.price_list_rule_value",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list_rule",
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
}
]
}
@@ -101,5 +101,39 @@ export class Migration20230929122253 extends Migration {
this.addSql(
'alter table "price_rule" add constraint "price_rule_price_set_money_amount_id_foreign" foreign key ("price_set_money_amount_id") references "price_set_money_amount" ("id") on update cascade on delete cascade;'
)
this.addSql(
'create table if not exists "price_list" ("id" text not null, "status" text check ("status" in (\'active\', \'draft\')) not null default \'draft\', "starts_at" timestamptz null, "ends_at" timestamptz null, "number_rules" integer not null default 0, constraint "price_list_pkey" primary key ("id"));'
)
this.addSql(
'create table "price_list_rule" ("id" text not null, "rule_type_id" text not null, "value" text not null, "price_list_id" text not null, constraint "price_list_rule_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_list_rule_rule_type_id" on "price_list_rule" ("rule_type_id");'
)
this.addSql(
'create index "IDX_price_list_rule_price_list_id" on "price_list_rule" ("price_list_id");'
)
this.addSql(
'alter table "price_list_rule" add constraint "price_list_rule_rule_type_id_foreign" foreign key ("rule_type_id") references "rule_type" ("id") on update cascade;'
)
this.addSql(
'alter table "price_list_rule" add constraint "price_list_rule_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade;'
)
this.addSql(
'alter table "price_set_money_amount" add column "price_list_id" text null;'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;'
)
this.addSql(
'create index "IDX_price_rule_price_list_id" on "price_set_money_amount" ("price_list_id");'
)
this.addSql('alter table "price_rule" drop column "price_list_id";')
}
}
@@ -0,0 +1,86 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20231101232834 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table "price_list_rule_value" ("id" text not null, "value" text not null, "price_list_rule_id" text not null, constraint "price_list_rule_value_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_list_rule_price_list_rule_value_id" on "price_list_rule_value" ("price_list_rule_id");'
)
this.addSql(
'alter table "price_list_rule_value" add constraint "price_list_rule_value_price_list_rule_id_foreign" foreign key ("price_list_rule_id") references "price_list_rule" ("id") on update cascade on delete cascade;'
)
this.addSql(
`ALTER TABLE price_list ADD COLUMN IF NOT EXISTS number_rules integer not null default 0`
)
this.addSql(
'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\';'
)
this.addSql(
'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete cascade;'
)
this.addSql(
'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\', add column if not exists "created_at" timestamptz not null default now(), add column if not exists "updated_at" timestamptz not null default now(), add column if not exists "deleted_at" timestamptz null;'
)
this.addSql(
'create index if not exists "IDX_price_list_deleted_at" on "price_list" ("deleted_at");'
)
this.addSql(
'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique"'
)
this.addSql(
'alter table "price_list_rule" add constraint "IDX_price_list_rule_rule_type_id_price_list_id_unique" unique ("price_list_id", "rule_type_id");'
)
}
async down(): Promise<void> {
this.addSql('drop table if exists "price_list_rule_value" cascade;')
this.addSql(`ALTER TABLE price_list DROP COLUMN IF EXISTS number_rules`)
this.addSql('alter table "price_list" drop column if exists "title";')
this.addSql('alter table "price_list" drop column if exists "description";')
this.addSql('alter table "price_list" drop column if exists "type";')
this.addSql(
'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;'
)
this.addSql('drop index if exists "IDX_price_list_deleted_at";')
this.addSql('alter table "price_list" drop column if exists "title";')
this.addSql('alter table "price_list" drop column if exists "description";')
this.addSql('alter table "price_list" drop column if exists "type";')
this.addSql('alter table "price_list" drop column if exists "created_at";')
this.addSql('alter table "price_list" drop column if exists "updated_at";')
this.addSql('alter table "price_list" drop column if exists "deleted_at";')
this.addSql(
'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique";'
)
}
}
+3
View File
@@ -1,5 +1,8 @@
export { default as Currency } from "./currency"
export { default as MoneyAmount } from "./money-amount"
export { default as PriceList } from "./price-list"
export { default as PriceListRule } from "./price-list-rule"
export { default as PriceListRuleValue } from "./price-list-rule-value"
export { default as PriceRule } from "./price-rule"
export { default as PriceSet } from "./price-set"
export { default as PriceSetMoneyAmount } from "./price-set-money-amount"
+36 -1
View File
@@ -3,17 +3,27 @@ import {
BeforeCreate,
Collection,
Entity,
Index,
ManyToMany,
ManyToOne,
OneToOne,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import Currency from "./currency"
import { PriceSetMoneyAmount } from "./index"
import PriceSet from "./price-set"
@Entity()
class MoneyAmount {
[OptionalProps]?:
| "created_at"
| "updated_at"
| "deleted_at"
| "price_set_money_amount"
@PrimaryKey({ columnType: "text" })
id!: string
@@ -26,6 +36,12 @@ class MoneyAmount {
})
price_sets = new Collection<PriceSet>(this)
@OneToOne({
entity: () => PriceSetMoneyAmount,
mappedBy: (psma) => psma.money_amount,
})
price_set_money_amount: PriceSetMoneyAmount
@ManyToOne(() => Currency, {
nullable: true,
index: "IDX_money_amount_currency_code",
@@ -33,7 +49,7 @@ class MoneyAmount {
})
currency?: Currency
@Property({ columnType: "numeric", nullable: true })
@Property({ columnType: "numeric", nullable: true, serializer: Number })
amount?: number
@Property({ columnType: "numeric", nullable: true })
@@ -42,6 +58,25 @@ class MoneyAmount {
@Property({ columnType: "numeric", nullable: true })
max_quantity?: number | null
@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
@Index({ name: "IDX_money_amount_deleted_at" })
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "ma")
@@ -0,0 +1,31 @@
import {
BeforeCreate,
Entity,
ManyToOne,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import PriceListRule from "./price-list-rule"
import { generateEntityId } from "@medusajs/utils"
@Entity()
export default class PriceListRuleValue {
@PrimaryKey({ columnType: "text" })
id!: string
@ManyToOne(() => PriceListRule, {
onDelete: "cascade",
fieldName: 'price_list_rule_id',
index: "IDX_price_list_rule_price_list_rule_value_id",
})
price_list_rule: PriceListRule
@Property({ columnType: "text" })
value: string
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "plrv")
}
}
@@ -0,0 +1,57 @@
import {
BeforeCreate,
Cascade,
Collection,
Entity,
ManyToOne,
OneToMany,
OptionalProps,
PrimaryKey,
Unique,
} from "@mikro-orm/core"
import { generateEntityId } from "@medusajs/utils"
import PriceList from "./price-list"
import PriceListRuleValue from "./price-list-rule-value"
import RuleType from "./rule-type"
type OptionalFields = "id"
type OptionalRelations = "rule_type" | "price_list"
@Entity()
@Unique({
name: "IDX_price_list_rule_rule_type_id_price_list_id_unique",
properties: ["price_list", "rule_type"],
})
export default class PriceListRule {
[OptionalProps]: OptionalFields | OptionalRelations
@PrimaryKey({ columnType: "text" })
id!: string
@ManyToOne({
entity: () => RuleType,
fieldName: "rule_type_id",
name: "price_rule_rule_type_id_unique",
index: "IDX_price_list_rule_rule_type_id",
})
rule_type: RuleType
@OneToMany(() => PriceListRuleValue, (plrv) => plrv.price_list_rule, {
cascade: [Cascade.REMOVE],
})
price_list_rule_values = new Collection<PriceListRuleValue>(this)
@ManyToOne({
entity: () => PriceList,
fieldName: "price_list_id",
name: "price_rule_price_list_id",
index: "IDX_price_list_rule_price_list_id",
})
price_list: PriceList
@BeforeCreate()
beforeCreate() {
this.id = generateEntityId(this.id, "plrule")
}
}
+110
View File
@@ -0,0 +1,110 @@
import { generateEntityId } from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Enum,
Index,
ManyToMany,
OneToMany,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { PriceListStatus, PriceListType } from "@medusajs/utils"
import PriceListRule from "./price-list-rule"
import PriceSetMoneyAmount from "./price-set-money-amount"
import RuleType from "./rule-type"
type OptionalFields =
| "status"
| "type"
| "number_rules"
| "starts_at"
| "ends_at"
| "created_at"
| "updated_at"
| "deleted_at"
type OptionalRelations =
| "price_set_money_amounts"
| "rule_types"
| "price_list_rules"
@Entity()
export default class PriceList {
[OptionalProps]: OptionalFields | OptionalRelations
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text" })
title: string
@Property({ columnType: "text" })
description: string
@Enum({ items: () => PriceListStatus, default: PriceListStatus.DRAFT })
status?: PriceListStatus
@Enum({ items: () => PriceListType, default: PriceListType.SALE })
type?: PriceListType
@Property({
columnType: "timestamptz",
nullable: true,
})
starts_at?: Date | null
@Property({
columnType: "timestamptz",
nullable: true,
})
ends_at?: Date | null
@OneToMany(() => PriceSetMoneyAmount, (psma) => psma.price_list, {
cascade: [Cascade.REMOVE],
})
price_set_money_amounts = new Collection<PriceSetMoneyAmount>(this)
@OneToMany(() => PriceListRule, (pr) => pr.price_list, {
cascade: [Cascade.REMOVE],
})
price_list_rules = new Collection<PriceListRule>(this)
@ManyToMany({
entity: () => RuleType,
pivotEntity: () => PriceListRule,
cascade: [Cascade.REMOVE],
})
rule_types = new Collection<RuleType>(this)
@Property({ columnType: "integer", default: 0 })
number_rules?: number
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at?: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at?: Date
@Index({ name: "IDX_price_list_deleted_at" })
@Property({ columnType: "timestamptz", nullable: true })
deleted_at?: Date
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "plist")
}
}
@@ -57,11 +57,6 @@ export default class PriceRule {
})
price_set_money_amount: PriceSetMoneyAmount
@Property({ columnType: "text" })
price_list_id!: string
// TODO: Add price list
@BeforeCreate()
beforeCreate() {
this.id = generateEntityId(this.id, "prule")
@@ -5,12 +5,14 @@ import {
Entity,
ManyToOne,
OneToMany,
OneToOne,
PrimaryKey,
PrimaryKeyType,
Property,
} from "@mikro-orm/core"
import MoneyAmount from "./money-amount"
import PriceList from "./price-list"
import PriceRule from "./price-rule"
import PriceSet from "./price-set"
import PriceSetMoneyAmountRules from "./price-set-money-amount-rules"
@@ -29,7 +31,7 @@ export default class PriceSetMoneyAmount {
})
price_set?: PriceSet
@ManyToOne(() => MoneyAmount, {
@OneToOne(() => MoneyAmount, {
onDelete: "cascade",
index: "IDX_price_set_money_amount_money_amount_id",
})
@@ -50,6 +52,13 @@ export default class PriceSetMoneyAmount {
})
price_set_money_amount_rules = new Collection<PriceSetMoneyAmountRules>(this)
@ManyToOne(() => PriceList, {
index: "IDX_price_rule_price_list_id",
onDelete: "cascade",
nullable: true,
})
price_list?: PriceList
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "psma")
+1 -1
View File
@@ -18,7 +18,7 @@ import RuleType from "./rule-type"
@Entity()
export default class PriceSet {
[OptionalProps]?: "price_set_money_amounts" | "rule_types"
[OptionalProps]?: "price_set_money_amounts" | "rule_types" | "money_amounts"
@PrimaryKey({ columnType: "text" })
id!: string
@@ -1,6 +1,9 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
export { CurrencyRepository } from "./currency"
export { MoneyAmountRepository } from "./money-amount"
export { PriceListRepository } from "./price-list"
export { PriceListRuleRepository } from "./price-list-rule"
export { PriceListRuleValueRepository } from "./price-list-rule-value"
export { PriceRuleRepository } from "./price-rule"
export { PriceSetRepository } from "./price-set"
export { PriceSetMoneyAmountRepository } from "./price-set-money-amount"
@@ -0,0 +1,143 @@
import { Context, DAL } from "@medusajs/types"
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRuleValue } from "@models"
import {
CreatePriceListRuleValueDTO,
UpdatePriceListRuleValueDTO,
} from "../types"
export class PriceListRuleValueRepository 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<PriceListRuleValue> = { where: {} },
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceListRuleValue,
findOptions_.where as MikroFilterQuery<PriceListRuleValue>,
findOptions_.options as MikroOptions<PriceListRuleValue>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceListRuleValue> = { where: {} },
context: Context = {}
): Promise<[PriceListRuleValue[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceListRuleValue,
findOptions_.where as MikroFilterQuery<PriceListRuleValue>,
findOptions_.options as MikroOptions<PriceListRuleValue>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceListRuleValue, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListRuleValueDTO[],
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListRuleValues = data.map((priceRuleValueData) => {
const { price_list_rule_id: priceListRuleId, ...priceRuleValue } =
priceRuleValueData
if (priceListRuleId) {
priceRuleValue.price_list_rule = priceListRuleId
}
return manager.create(PriceListRuleValue, priceRuleValue)
})
manager.persist(priceListRuleValues)
return priceListRuleValues
}
async update(
data: UpdatePriceListRuleValueDTO[],
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceValueIds = data.map(
(priceRuleValueData) => priceRuleValueData.id
)
const existingPriceValues = await this.find(
{
where: {
id: {
$in: priceValueIds,
},
},
},
context
)
const dataAndExistingIdDifference = arrayDifference(
data.map((d) => d.id),
existingPriceValues.map((pv) => pv.id)
)
if (dataAndExistingIdDifference.length) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceListRuleValue with id(s) "${dataAndExistingIdDifference.join(
", "
)}" not found`
)
}
const existingValuesMap = new Map(
existingPriceValues.map<[string, PriceListRuleValue]>((priceValue) => [
priceValue.id,
priceValue,
])
)
const priceValues = data.map((priceRuleValueData) => {
const existingPriceValue = existingValuesMap.get(priceRuleValueData.id)!
return manager.assign(existingPriceValue, priceRuleValueData)
})
manager.persist(priceValues)
return priceValues
}
}
@@ -0,0 +1,161 @@
import {
Context,
CreatePriceListRuleDTO,
DAL,
UpdatePriceListRuleDTO,
} from "@medusajs/types"
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRule } from "@models"
export class PriceListRuleRepository 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<PriceListRule> = { where: {} },
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceListRule,
findOptions_.where as MikroFilterQuery<PriceListRule>,
findOptions_.options as MikroOptions<PriceListRule>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceListRule> = { where: {} },
context: Context = {}
): Promise<[PriceListRule[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceListRule,
findOptions_.where as MikroFilterQuery<PriceListRule>,
findOptions_.options as MikroOptions<PriceListRule>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceListRule, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListRuleDTO[],
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListRule = data.map((priceListRule) => {
const {
price_list_id: priceListId,
rule_type_id: ruleTypeId,
...createData
} = priceListRule
if (priceListId) {
createData.price_list = priceListId
}
if (ruleTypeId) {
createData.rule_type = ruleTypeId
}
return manager.create(PriceListRule, createData)
})
manager.persist(priceListRule)
return priceListRule
}
async update(
data: UpdatePriceListRuleDTO[],
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListIds = data.map((priceListRule) => priceListRule.id)
const existingPriceListRules = await this.find(
{
where: {
id: {
$in: priceListIds,
},
},
},
context
)
const dataAndExistingIdDifference = arrayDifference(
data.map((d) => d.id),
existingPriceListRules.map((plr) => plr.id)
)
if (dataAndExistingIdDifference.length) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceListRule with id(s) "${dataAndExistingIdDifference.join(
", "
)}" not found`
)
}
const existingPriceListRuleMap = new Map(
existingPriceListRules.map<[string, PriceListRule]>((priceList) => [
priceList.id,
priceList,
])
)
const priceListRules = data.map((priceListRule) => {
const { price_list_id, rule_type_id, ...priceListRuleData } =
priceListRule
const existingPriceListRule = existingPriceListRuleMap.get(
priceListRule.id
)!
if (price_list_id) {
priceListRuleData.price_list = price_list_id
}
if (rule_type_id) {
priceListRuleData.rule_type = rule_type_id
}
return manager.assign(existingPriceListRule, priceListRuleData)
})
manager.persist(priceListRules)
return priceListRules
}
}
@@ -0,0 +1,133 @@
import { Context, DAL, UpdatePriceListDTO } from "@medusajs/types"
import { DALUtils, MedusaError } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceList } from "@models"
import { CreatePriceListDTO } from "../types"
export class PriceListRepository 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<PriceList> = { where: {} },
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceList,
findOptions_.where as MikroFilterQuery<PriceList>,
findOptions_.options as MikroOptions<PriceList>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceList> = { where: {} },
context: Context = {}
): Promise<[PriceList[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceList,
findOptions_.where as MikroFilterQuery<PriceList>,
findOptions_.options as MikroOptions<PriceList>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceList, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListDTO[],
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceLists = data.map((priceListData) => {
return manager.create(PriceList, priceListData)
})
manager.persist(priceLists)
return priceLists
}
async update(
data: Omit<UpdatePriceListDTO, "rules">[],
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListIds = data.map((priceListData) => priceListData.id)
const existingPriceLists = await this.find(
{
where: {
id: {
$in: priceListIds,
},
},
},
context
)
const existingPriceListMap = new Map(
existingPriceLists.map<[string, PriceList]>((priceList) => [
priceList.id,
priceList,
])
)
const priceLists = data.map((priceListData) => {
const existingPriceList = existingPriceListMap.get(priceListData.id)
if (!existingPriceList) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceList with id "${priceListData.id}" not found`
)
}
if (!!priceListData.starts_at) {
priceListData.starts_at = new Date(
priceListData.starts_at
).toISOString()
}
if (!!priceListData.ends_at) {
priceListData.ends_at = new Date(priceListData.ends_at).toISOString()
}
return manager.assign(existingPriceList, priceListData)
})
manager.persist(priceLists)
return priceLists
}
}
@@ -74,13 +74,16 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito
): Promise<PriceSetMoneyAmount[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const currencies = data.map((currencyData) => {
return manager.create(PriceSetMoneyAmount, currencyData as PriceSetMoneyAmount)
const psma = data.map((psmaData) => {
return manager.create(
PriceSetMoneyAmount,
psmaData as unknown as PriceSetMoneyAmount
)
})
manager.persist(currencies)
manager.persist(psma)
return currencies
return psma
}
async update(
@@ -101,23 +104,22 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito
)
const existingPSMAMap = new Map(
existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>((psma) => [
psma.id,
psma,
])
existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>(
(psma) => [psma.id, psma]
)
)
const priceSetMoneyAmounts = data.map((currencyData) => {
const existingPSMA = existingPSMAMap.get(currencyData.id)
const priceSetMoneyAmounts = data.map((psmaData) => {
const existingPSMA = existingPSMAMap.get(psmaData.id)
if (!existingPSMA) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceSetMoneyAmount with id "${currencyData.id}" not found`
`PriceSetMoneyAmount with id "${psmaData.id}" not found`
)
}
return manager.assign(existingPSMA, currencyData)
return manager.assign(existingPSMA, psmaData)
})
manager.persist(priceSetMoneyAmounts)
@@ -1,3 +1,4 @@
import { UpdatePriceListDTO } from "@medusajs/types"
import {
Context,
DAL,
@@ -84,7 +85,7 @@ export class PriceSetRepository extends DALUtils.MikroOrmBaseRepository {
}
async update(
data: UpdatePriceSetDTO[],
data: Omit<UpdatePriceListDTO, "rules">[],
context: Context = {}
): Promise<PriceSet[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
+70 -16
View File
@@ -57,43 +57,96 @@ export class PricingRepository
return []
}
const date = new Date().toISOString()
// Gets all the price set money amounts where rules match for each of the contexts
// that the price set is configured for
const psmaSubQueryKnex = knex({
psma: "price_set_money_amount",
})
.select({
id: "psma.id",
price_set_id: "psma.price_set_id",
money_amount_id: "psma.money_amount_id",
number_rules: "psma.number_rules",
id: "psma1.id",
price_set_id: "psma1.price_set_id",
money_amount_id: "psma1.money_amount_id",
number_rules: "psma1.number_rules",
price_list_id: "psma1.price_list_id",
pl_number_rules: "pl.number_rules",
pl_type: "pl.type",
})
.leftJoin("price_set_money_amount as psma1", "psma1.id", "psma.id")
.leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma.id")
.leftJoin("price_set_money_amount as psma1", "psma1.id", "psma1.id")
.leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma1.id")
.leftJoin("price_list as pl", "pl.id", "psma1.price_list_id")
.leftJoin("price_list_rule as plr", "plr.price_list_id", "pl.id")
.leftJoin(
"price_list_rule_value as plrv",
"plrv.price_list_rule_id",
"plr.id"
)
.leftJoin("rule_type as plrt", "plrt.id", "plr.rule_type_id")
.leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id")
.orderBy("pl.number_rules", "desc")
.orderBy("number_rules", "desc")
.orWhere("psma1.number_rules", "=", 0)
.groupBy("psma.id")
.having(knex.raw("count(DISTINCT rt.rule_attribute) = psma.number_rules"))
.orderBy([
{ column: "number_rules", order: "desc" },
{ column: "pl.number_rules", order: "desc" },
])
.groupBy("psma1.id", "pl.id")
.having(
knex.raw(
"count(DISTINCT rt.rule_attribute) = psma1.number_rules AND psma1.price_list_id IS NULL"
)
)
.orHaving(
knex.raw(
"count(DISTINCT plrt.rule_attribute) = pl.number_rules AND psma1.price_list_id IS NOT NULL"
)
)
psmaSubQueryKnex.orWhere((q) => {
for (const [key, value] of Object.entries(context)) {
q.orWhere({
"rt.rule_attribute": key,
"pr.value": value,
})
}
for (const [key, value] of Object.entries(context)) {
psmaSubQueryKnex.orWhere({
"rt.rule_attribute": key,
"pr.value": value,
})
}
q.orWhere("psma1.number_rules", "=", 0)
q.whereNull("psma1.price_list_id")
})
psmaSubQueryKnex.orWhere((q) => {
q.whereNotNull("psma1.price_list_id")
.andWhere(function () {
this.whereNull("pl.starts_at").orWhere("pl.starts_at", "<=", date)
})
.andWhere(function () {
this.whereNull("pl.ends_at").orWhere("pl.ends_at", ">=", date)
})
.andWhere(function () {
for (const [key, value] of Object.entries(context)) {
this.orWhere({
"plrt.rule_attribute": key,
})
this.whereIn("plrv.value", [value])
}
this.orWhere("pl.number_rules", "=", 0)
})
})
const priceSetQueryKnex = knex({
ps: "price_set",
})
.select({
id: "ps.id",
id: "ma.id",
price_set_id: "ps.id",
amount: "ma.amount",
min_quantity: "ma.min_quantity",
max_quantity: "ma.max_quantity",
currency_code: "ma.currency_code",
default_priority: "rt.default_priority",
number_rules: "psma.number_rules",
pl_number_rules: "psma.pl_number_rules",
price_list_type: "psma.pl_type",
price_list_id: "psma.price_list_id",
})
.join(psmaSubQueryKnex.as("psma"), "psma.price_set_id", "ps.id")
.join("money_amount as ma", "ma.id", "psma.money_amount_id")
@@ -102,6 +155,7 @@ export class PricingRepository
.whereIn("ps.id", pricingFilters.id)
.andWhere("ma.currency_code", "=", currencyCode)
.orderBy([
{ column: "price_list_id", order: "asc" },
{ column: "number_rules", order: "desc" },
{ column: "default_priority", order: "desc" },
])
+3
View File
@@ -1,5 +1,8 @@
export { default as CurrencyService } from "./currency"
export { default as MoneyAmountService } from "./money-amount"
export { default as PriceListService } from "./price-list"
export { default as PriceListRuleService } from "./price-list-rule"
export { default as PriceListRuleValueService } from "./price-list-rule-value"
export { default as PriceRuleService } from "./price-rule"
export { default as PriceSetService } from "./price-set"
export { default as PriceSetMoneyAmountService } from "./price-set-money-amount"
@@ -0,0 +1,117 @@
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
ModulesSdkUtils,
doNotForceTransaction,
retrieveEntity,
shouldForceTransaction,
} from "@medusajs/utils"
import { PriceListRuleValue } from "@models"
import { PriceListRuleValueRepository } from "@repositories"
import { CreatePriceListRuleValueDTO } from "../types"
type InjectedDependencies = {
priceListRuleValueRepository: DAL.RepositoryService
}
export default class PriceListRuleValueService<
TEntity extends PriceListRuleValue = PriceListRuleValue
> {
protected readonly priceListRuleValueRepository_: DAL.RepositoryService
constructor({ priceListRuleValueRepository }: InjectedDependencies) {
this.priceListRuleValueRepository_ = priceListRuleValueRepository
}
@InjectManager("priceListRuleValueRepository_")
async retrieve(
priceSetId: string,
config: FindConfig<PricingTypes.PriceListRuleValueDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<
PriceListRuleValue,
PricingTypes.PriceListRuleValueDTO
>({
id: priceSetId,
entityName: PriceListRuleValue.name,
repository: this.priceListRuleValueRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("priceListRuleValueRepository_")
async list(
filters: PricingTypes.FilterablePriceListRuleValueProps = {},
config: FindConfig<PricingTypes.PriceListRuleValueDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceListRuleValue>(
filters,
config
)
return (await this.priceListRuleValueRepository_.find(
queryOptions,
sharedContext
)) as TEntity[]
}
@InjectManager("priceListRuleValueRepository_")
async listAndCount(
filters: PricingTypes.FilterablePriceListRuleValueProps = {},
config: FindConfig<PricingTypes.PriceListRuleValueDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceListRuleValue>(
filters,
config
)
return (await this.priceListRuleValueRepository_.findAndCount(
queryOptions,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager(
shouldForceTransaction,
"priceListRuleValueRepository_"
)
async create(
data: CreatePriceListRuleValueDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (
this.priceListRuleValueRepository_ as PriceListRuleValueRepository
).create(data, sharedContext)) as TEntity[]
}
@InjectTransactionManager(
shouldForceTransaction,
"priceListRuleValueRepository_"
)
async update(
data: PricingTypes.UpdatePriceListRuleValueDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (
this.priceListRuleValueRepository_ as PriceListRuleValueRepository
).update(data, sharedContext)) as TEntity[]
}
@InjectTransactionManager(
doNotForceTransaction,
"priceListRuleValueRepository_"
)
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListRuleValueRepository_.delete(ids, sharedContext)
}
}
@@ -0,0 +1,103 @@
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
ModulesSdkUtils,
doNotForceTransaction,
retrieveEntity,
shouldForceTransaction,
} from "@medusajs/utils"
import { PriceListRule } from "@models"
import { PriceListRuleRepository } from "@repositories"
type InjectedDependencies = {
priceListRuleRepository: DAL.RepositoryService
}
export default class PriceListRuleService<
TEntity extends PriceListRule = PriceListRule
> {
protected readonly priceListRuleRepository_: DAL.RepositoryService
constructor({ priceListRuleRepository }: InjectedDependencies) {
this.priceListRuleRepository_ = priceListRuleRepository
}
@InjectManager("priceListRuleRepository_")
async retrieve(
priceSetId: string,
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<PriceListRule, PricingTypes.PriceListRuleDTO>({
id: priceSetId,
entityName: PriceListRule.name,
repository: this.priceListRuleRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("priceListRuleRepository_")
async list(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceListRule>(
filters,
config
)
return (await this.priceListRuleRepository_.find(
queryOptions,
sharedContext
)) as TEntity[]
}
@InjectManager("priceListRuleRepository_")
async listAndCount(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceListRule>(
filters,
config
)
return (await this.priceListRuleRepository_.findAndCount(
queryOptions,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager(shouldForceTransaction, "priceListRuleRepository_")
async create(
data: PricingTypes.CreatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (
this.priceListRuleRepository_ as PriceListRuleRepository
).create(data, sharedContext)) as TEntity[]
}
@InjectTransactionManager(shouldForceTransaction, "priceListRuleRepository_")
async update(
data: PricingTypes.UpdatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (
this.priceListRuleRepository_ as PriceListRuleRepository
).update(data, sharedContext)) as TEntity[]
}
@InjectTransactionManager(doNotForceTransaction, "priceListRuleRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListRuleRepository_.delete(ids, sharedContext)
}
}
@@ -0,0 +1,98 @@
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
ModulesSdkUtils,
doNotForceTransaction,
retrieveEntity,
shouldForceTransaction,
} from "@medusajs/utils"
import { PriceList } from "@models"
import { PriceListRepository } from "@repositories"
import { CreatePriceListDTO } from "../types"
type InjectedDependencies = {
priceListRepository: DAL.RepositoryService
}
export default class PriceListService<TEntity extends PriceList = PriceList> {
protected readonly priceListRepository_: DAL.RepositoryService
constructor({ priceListRepository }: InjectedDependencies) {
this.priceListRepository_ = priceListRepository
}
@InjectManager("priceListRepository_")
async retrieve(
priceListId: string,
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<PriceList, PricingTypes.PriceListDTO>({
id: priceListId,
entityName: PriceList.name,
repository: this.priceListRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("priceListRepository_")
async list(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceList>(filters, config)
return (await this.priceListRepository_.find(
queryOptions,
sharedContext
)) as TEntity[]
}
@InjectManager("priceListRepository_")
async listAndCount(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryOptions = ModulesSdkUtils.buildQuery<PriceList>(filters, config)
return (await this.priceListRepository_.findAndCount(
queryOptions,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager(shouldForceTransaction, "priceListRepository_")
async create(
data: CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (this.priceListRepository_ as PriceListRepository).create(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager(shouldForceTransaction, "priceListRepository_")
async update(
data: Omit<PricingTypes.UpdatePriceListDTO, "rules">[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (this.priceListRepository_ as PriceListRepository).update(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager(doNotForceTransaction, "priceListRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListRepository_.delete(ids, sharedContext)
}
}
+733 -9
View File
@@ -2,6 +2,7 @@ import {
AddPricesDTO,
Context,
CreateMoneyAmountDTO,
CreatePriceListRuleDTO,
DAL,
FindConfig,
InternalModuleDeclaration,
@@ -12,10 +13,14 @@ import {
PricingTypes,
RuleTypeDTO,
} from "@medusajs/types"
import { PriceListType } from "@medusajs/utils"
import {
Currency,
MoneyAmount,
PriceList,
PriceListRule,
PriceListRuleValue,
PriceRule,
PriceSet,
PriceSetMoneyAmount,
@@ -27,6 +32,9 @@ import {
import {
CurrencyService,
MoneyAmountService,
PriceListRuleService,
PriceListRuleValueService,
PriceListService,
PriceRuleService,
PriceSetMoneyAmountRulesService,
PriceSetMoneyAmountService,
@@ -44,7 +52,7 @@ import {
removeNullish,
} from "@medusajs/utils"
import { joinerConfig } from "../joiner-config"
import { PricingRepositoryService } from "../types"
import { CreatePriceListRuleValueDTO, PricingRepositoryService } from "../types"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
@@ -57,6 +65,9 @@ type InjectedDependencies = {
priceRuleService: PriceRuleService<any>
priceSetRuleTypeService: PriceSetRuleTypeService<any>
priceSetMoneyAmountService: PriceSetMoneyAmountService<any>
priceListService: PriceListService<any>
priceListRuleService: PriceListRuleService<any>
priceListRuleValueService: PriceListRuleValueService<any>
}
export default class PricingModuleService<
@@ -67,7 +78,10 @@ export default class PricingModuleService<
TPriceSetMoneyAmountRules extends PriceSetMoneyAmountRules = PriceSetMoneyAmountRules,
TPriceRule extends PriceRule = PriceRule,
TPriceSetRuleType extends PriceSetRuleType = PriceSetRuleType,
TPriceSetMoneyAmount extends PriceSetMoneyAmount = PriceSetMoneyAmount
TPriceSetMoneyAmount extends PriceSetMoneyAmount = PriceSetMoneyAmount,
TPriceList extends PriceList = PriceList,
TPriceListRule extends PriceListRule = PriceListRule,
TPriceListRuleValue extends PriceListRuleValue = PriceListRuleValue
> implements PricingTypes.IPricingModuleService
{
protected baseRepository_: DAL.RepositoryService
@@ -80,6 +94,9 @@ export default class PricingModuleService<
protected readonly priceRuleService_: PriceRuleService<TPriceRule>
protected readonly priceSetRuleTypeService_: PriceSetRuleTypeService<TPriceSetRuleType>
protected readonly priceSetMoneyAmountService_: PriceSetMoneyAmountService<TPriceSetMoneyAmount>
protected readonly priceListService_: PriceListService<TPriceList>
protected readonly priceListRuleService_: PriceListRuleService<TPriceListRule>
protected readonly priceListRuleValueService_: PriceListRuleValueService<TPriceListRuleValue>
constructor(
{
@@ -93,6 +110,9 @@ export default class PricingModuleService<
priceRuleService,
priceSetRuleTypeService,
priceSetMoneyAmountService,
priceListService,
priceListRuleService,
priceListRuleValueService,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
@@ -107,6 +127,9 @@ export default class PricingModuleService<
this.priceRuleService_ = priceRuleService
this.priceSetRuleTypeService_ = priceSetRuleTypeService
this.priceSetMoneyAmountService_ = priceSetMoneyAmountService
this.priceListService_ = priceListService
this.priceListRuleService_ = priceListRuleService
this.priceListRuleValueService_ = priceListRuleValueService
}
__joinerConfig(): ModuleJoinerConfig {
@@ -124,21 +147,53 @@ export default class PricingModuleService<
pricingContext,
sharedContext
)
const pricesSetPricesMap = groupBy(results, "id")
const pricesSetPricesMap = groupBy(results, "price_set_id")
const calculatedPrices = pricingFilters.id.map(
(priceSetId: string): PricingTypes.CalculatedPriceSetDTO => {
(priceSetId: string): PricingTypes.CalculatedPriceSet => {
// This is where we select prices, for now we just do a first match based on the database results
// which is prioritized by number_rules first for exact match and then deafult_priority of the rule_type
// inject custom price selection here
const price = pricesSetPricesMap.get(priceSetId)?.[0]
const prices = pricesSetPricesMap.get(priceSetId) || []
const priceListPrice = prices?.find((p) => !!p.price_list_id)
const defaultPrice = prices?.find((p) => !!!p.price_list_id)
let calculatedPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
let originalPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
if (priceListPrice) {
calculatedPrice = priceListPrice
if (priceListPrice.price_list_type === PriceListType.OVERRIDE) {
originalPrice = priceListPrice
}
}
return {
id: priceSetId,
amount: price?.amount || null,
currency_code: price?.currency_code || null,
min_quantity: price?.min_quantity || null,
max_quantity: price?.max_quantity || null,
is_calculated_price_price_list: !!calculatedPrice?.price_list_id,
calculated_amount: parseInt(calculatedPrice?.amount || "") || null,
is_original_price_price_list: !!originalPrice?.price_list_id,
original_amount: parseInt(originalPrice?.amount || "") || null,
currency_code: calculatedPrice?.currency_code || null,
calculated_price: {
money_amount_id: calculatedPrice?.id || null,
price_list_id: calculatedPrice?.price_list_id || null,
price_list_type: calculatedPrice?.price_list_type || null,
min_quantity: parseInt(calculatedPrice?.min_quantity || "") || null,
max_quantity: parseInt(calculatedPrice?.max_quantity || "") || null,
},
original_price: {
money_amount_id: originalPrice?.id || null,
price_list_id: originalPrice?.price_list_id || null,
price_list_type: originalPrice?.price_list_type || null,
min_quantity: parseInt(originalPrice?.min_quantity || "") || null,
max_quantity: parseInt(originalPrice?.max_quantity || "") || null,
},
}
}
)
@@ -1241,4 +1296,673 @@ export default class PricingModuleService<
): Promise<void> {
await this.priceRuleService_.delete(priceRuleIds, sharedContext)
}
@InjectManager("baseRepository_")
async retrievePriceList(
id: string,
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const priceList = await this.priceListService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO>(
priceList,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceLists(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountPriceLists(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceListDTO[], number]> {
const [priceLists, count] = await this.priceListService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
),
count,
]
}
@InjectManager("baseRepository_")
async createPriceLists(
data: PricingTypes.CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.createPriceLists_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
protected async createPriceLists_(
data: PricingTypes.CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const createdPriceLists: PricingTypes.PriceListDTO[] = []
const ruleAttributes = data
.map((priceListData) => Object.keys(priceListData.rules || {}))
.flat()
const ruleTypes = await this.listRuleTypes({
rule_attribute: ruleAttributes,
})
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
for (const priceListData of data) {
const { rules = {}, prices = [], ...priceListOnlyData } = priceListData
const [createdPriceList] = (await this.priceListService_.create(
[
{
...priceListOnlyData,
number_rules: Object.keys(rules).length,
},
],
sharedContext
)) as unknown as PricingTypes.PriceListDTO[]
createdPriceLists.push(createdPriceList)
for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) {
// Find or create rule type
let ruleType = ruleTypeMap.get(ruleAttribute)
if (!ruleType) {
;[ruleType] = await this.createRuleTypes(
[
{
name: ruleAttribute,
rule_attribute: ruleAttribute,
},
],
sharedContext
)
ruleTypeMap.set(ruleAttribute, ruleType)
}
// Create the rule
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: createdPriceList,
rule_type: ruleType?.id || ruleType,
},
],
sharedContext
)
// Create the values for the rule
for (const ruleValue of ruleValues as string[]) {
await this.priceListRuleValueService_.create(
[
{
price_list_rule: priceListRule,
value: ruleValue,
},
],
sharedContext
)
}
}
for (const price of prices) {
const { price_set_id: priceSetId, ...moneyAmountData } = price
const [moneyAmount] = await this.moneyAmountService_.create(
[moneyAmountData],
sharedContext
)
await this.priceSetMoneyAmountService_.create(
[
{
price_set: priceSetId,
price_list: createdPriceList,
money_amount: moneyAmount,
title: "test",
number_rules: 0,
},
] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[],
sharedContext
)
}
}
return createdPriceLists
}
@InjectTransactionManager("baseRepository_")
async updatePriceLists(
data: PricingTypes.UpdatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.updatePriceLists_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
protected async updatePriceLists_(
data: PricingTypes.UpdatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const updatedPriceLists: PricingTypes.PriceListDTO[] = []
const priceListIds = data.map((d) => d.id)
const ruleAttributes = data
.map((priceListData) => Object.keys(priceListData.rules || {}))
.flat()
const existingPriceLists = await this.listPriceLists(
{ id: priceListIds },
{ relations: ["price_list_rules"] },
sharedContext
)
const priceListRuleIds = existingPriceLists
.map((pl) => (pl.price_list_rules || []).map((plr) => plr.id))
.flat()
const existingPriceListRules = await this.listPriceListRules(
{
id: priceListRuleIds,
},
{},
sharedContext
)
if (existingPriceListRules.length) {
await this.deletePriceListRules(
existingPriceListRules.map((plr) => plr.id),
sharedContext
)
}
const ruleTypes = await this.listRuleTypes(
{
rule_attribute: ruleAttributes,
},
{},
sharedContext
)
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
for (const priceListData of data) {
const { rules = {}, ...priceListOnlyData } = priceListData
const [updatedPriceList] = (await this.priceListService_.update(
[
{
...priceListOnlyData,
number_rules: Object.keys(rules).length,
},
],
sharedContext
)) as unknown as PricingTypes.PriceListDTO[]
updatedPriceLists.push(updatedPriceList)
for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) {
let ruleType = ruleTypeMap.get(ruleAttribute)
if (!ruleType) {
;[ruleType] = await this.createRuleTypes(
[
{
name: ruleAttribute,
rule_attribute: ruleAttribute,
},
],
sharedContext
)
ruleTypeMap.set(ruleAttribute, ruleType)
}
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: updatedPriceList,
rule_type: ruleType?.id || ruleType,
},
],
sharedContext
)
for (const ruleValue of ruleValues as string[]) {
await this.priceListRuleValueService_.create(
[
{
price_list_rule: priceListRule,
value: ruleValue,
},
],
sharedContext
)
}
}
}
return updatedPriceLists
}
@InjectTransactionManager("baseRepository_")
async deletePriceLists(
priceListIds: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListService_.delete(priceListIds, sharedContext)
}
@InjectManager("baseRepository_")
async retrievePriceListRule(
id: string,
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO> {
const priceList = await this.priceListRuleService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO>(
priceList,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceListRules(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.priceListRuleService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountPriceListRules(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceListRuleDTO[], number]> {
const [priceLists, count] = await this.priceListRuleService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
),
count,
]
}
@InjectManager("baseRepository_")
async createPriceListRules(
data: PricingTypes.CreatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.createPriceListRules_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async createPriceListRules_(
data: PricingTypes.CreatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
) {
return await this.priceListRuleService_.create(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async updatePriceListRules(
data: PricingTypes.UpdatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.priceListRuleService_.update(
data,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deletePriceListRules(
priceListRuleIds: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListRuleService_.delete(priceListRuleIds, sharedContext)
}
@InjectManager("baseRepository_")
async addPriceListPrices(
data: PricingTypes.AddPriceListPricesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
return await this.addPriceListPrices_(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
protected async addPriceListPrices_(
data: PricingTypes.AddPriceListPricesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.listPriceLists(
{ id: data.map((d) => d.priceListId) },
{},
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
for (const { priceListId, prices } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
await Promise.all(
prices.map(async (price) => {
const [moneyAmount] = await this.moneyAmountService_.create(
[price] as unknown as CreateMoneyAmountDTO[],
sharedContext
)
const psma = await this.priceSetMoneyAmountService_.create(
[
{
price_set: price.price_set_id,
money_amount: moneyAmount.id,
title: "test",
price_list: priceList.id,
},
],
sharedContext
)
return psma
})
)
}
return priceLists
}
@InjectManager("baseRepository_")
async setPriceListRules(
data: PricingTypes.SetPriceListRulesDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const [priceList] = await this.setPriceListRules_([data], sharedContext)
return priceList
}
@InjectTransactionManager("baseRepository_")
protected async setPriceListRules_(
data: PricingTypes.SetPriceListRulesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
{ id: data.map((d) => d.priceListId) },
{ relations: ["price_list_rules", "price_list_rules.rule_type"] },
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
const ruleTypes = await this.listRuleTypes({
rule_attribute: data.map((d) => Object.keys(d.rules)).flat(),
})
const ruleTypeMap = new Map(ruleTypes.map((rt) => [rt.rule_attribute, rt]))
const ruleIdsToUpdate: string[] = []
const rulesToCreate: CreatePriceListRuleDTO[] = []
const priceRuleValues = new Map<string, Map<string, string[]>>()
for (const { priceListId, rules } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
const priceListRulesMap: Map<string, PriceListRule> = new Map(
priceList.price_list_rules
.getItems()
.map((p) => [p.rule_type.rule_attribute, p])
)
const priceListRuleValues = new Map<string, string[]>()
await Promise.all(
Object.entries(rules).map(async ([key, value]) => {
const ruleType = ruleTypeMap.get(key)
if (!ruleType) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Rule type with attribute: ${key} not found`
)
}
const rule = priceListRulesMap.get(key)
priceListRuleValues.set(
ruleType.id,
Array.isArray(value) ? value : [value]
)
if (!rule) {
rulesToCreate.push({
rule_type: ruleType.id,
price_list: priceListId,
})
} else {
ruleIdsToUpdate.push(rule.id)
}
})
)
priceRuleValues.set(priceListId, priceListRuleValues)
}
const [createdRules, priceListValuesToDelete] = await Promise.all([
this.priceListRuleService_.create(rulesToCreate),
this.priceListRuleValueService_.list({
price_list_rule_id: ruleIdsToUpdate,
}),
])
const priceListRuleValuesToCreate: CreatePriceListRuleValueDTO[] = []
for (const { id, price_list, rule_type } of createdRules) {
const ruleValues = priceRuleValues.get(
price_list.id ?? (price_list as unknown as string)
)
if (!ruleValues) {
continue
}
const values = ruleValues.get(
rule_type.id ?? (rule_type as unknown as string)
)
if (!values) {
continue
}
values.forEach((v) => {
priceListRuleValuesToCreate.push({
price_list_rule: id,
value: v,
})
})
}
await Promise.all([
this.priceListRuleValueService_.delete(
priceListValuesToDelete.map((p) => p.id)
),
this.priceListRuleValueService_.create(priceListRuleValuesToCreate),
])
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async removePriceListRules(
data: PricingTypes.RemovePriceListRulesDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const [priceList] = await this.removePriceListRules_([data], sharedContext)
return priceList
}
@InjectTransactionManager("baseRepository_")
protected async removePriceListRules_(
data: PricingTypes.RemovePriceListRulesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
{ id: data.map((d) => d.priceListId) },
{ relations: ["price_list_rules", "price_list_rules.rule_type"] },
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
const idsToDelete: string[] = []
for (const { priceListId, rules } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
const priceListRulesMap: Map<string, PriceListRule> = new Map(
priceList.price_list_rules
.getItems()
.map((p) => [p.rule_type.rule_attribute, p])
)
await Promise.all(
rules.map(async (rule_attribute) => {
const rule = priceListRulesMap.get(rule_attribute)
if (rule) {
idsToDelete.push(rule.id)
}
})
)
}
await this.priceListRuleService_.delete(idsToDelete)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
}
@@ -1 +1,3 @@
export * from "./price-list"
export * from "./price-list-rule-value"
export * from "./pricing"
@@ -0,0 +1,12 @@
import { PriceListRule } from "@models"
export interface CreatePriceListRuleValueDTO {
price_list_rule_id?: string
price_list_rule: PriceListRule | string
value: string
}
export interface UpdatePriceListRuleValueDTO {
id: string
value: string
}
@@ -0,0 +1,11 @@
import { PriceListStatus, PriceListType } from "@medusajs/utils"
export interface CreatePriceListDTO {
title: string
description: string
starts_at?: string
ends_at?: string
status?: PriceListStatus
type?: PriceListType
number_rules?: number
}