feat(core-flows,pricing,medusa,pricing,types,utils): Price List Prices can have their own rules (#5752)

**What**
- Add price-rules for prices in price-lists
- make rules object optional when creating prices

**Why**
- more price granularity

Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
Riqwan Thamir
2023-12-12 08:20:21 +00:00
committed by GitHub
co-authored by Philip Korsholm
parent f05c20695e
commit 079f0da83f
41 changed files with 984 additions and 370 deletions
@@ -1,7 +1,5 @@
{
"namespaces": [
"public"
],
"namespaces": ["public"],
"name": "public",
"tables": [
{
@@ -48,9 +46,7 @@
"indexes": [
{
"keyName": "currency_pkey",
"columnNames": [
"code"
],
"columnNames": ["code"],
"composite": false,
"primary": true,
"unique": true
@@ -143,18 +139,14 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"currency_code"
],
"columnNames": ["currency_code"],
"composite": false,
"keyName": "IDX_money_amount_currency_code",
"primary": false,
"unique": false
},
{
"columnNames": [
"deleted_at"
],
"columnNames": ["deleted_at"],
"composite": false,
"keyName": "IDX_money_amount_deleted_at",
"primary": false,
@@ -162,9 +154,7 @@
},
{
"keyName": "money_amount_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -174,13 +164,9 @@
"foreignKeys": {
"money_amount_currency_code_foreign": {
"constraintName": "money_amount_currency_code_foreign",
"columnNames": [
"currency_code"
],
"columnNames": ["currency_code"],
"localTableName": "public.money_amount",
"referencedColumnNames": [
"code"
],
"referencedColumnNames": ["code"],
"referencedTableName": "public.currency",
"deleteRule": "set null",
"updateRule": "cascade"
@@ -224,10 +210,7 @@
"primary": false,
"nullable": false,
"default": "'draft'",
"enumItems": [
"active",
"draft"
],
"enumItems": ["active", "draft"],
"mappedType": "enum"
},
"type": {
@@ -238,10 +221,7 @@
"primary": false,
"nullable": false,
"default": "'sale'",
"enumItems": [
"sale",
"override"
],
"enumItems": ["sale", "override"],
"mappedType": "enum"
},
"starts_at": {
@@ -264,8 +244,8 @@
"length": 6,
"mappedType": "datetime"
},
"number_rules": {
"name": "number_rules",
"rules_count": {
"name": "rules_count",
"type": "integer",
"unsigned": false,
"autoincrement": false,
@@ -311,9 +291,7 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"deleted_at"
],
"columnNames": ["deleted_at"],
"composite": false,
"keyName": "IDX_price_list_deleted_at",
"primary": false,
@@ -321,9 +299,7 @@
},
{
"keyName": "price_list_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -349,9 +325,7 @@
"indexes": [
{
"keyName": "price_set_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -398,8 +372,8 @@
"nullable": false,
"mappedType": "text"
},
"number_rules": {
"name": "number_rules",
"rules_count": {
"name": "rules_count",
"type": "integer",
"unsigned": false,
"autoincrement": false,
@@ -422,36 +396,28 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"composite": false,
"keyName": "IDX_price_set_money_amount_price_set_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"money_amount_id"
],
"columnNames": ["money_amount_id"],
"composite": false,
"keyName": "IDX_price_set_money_amount_money_amount_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"money_amount_id"
],
"columnNames": ["money_amount_id"],
"composite": false,
"keyName": "price_set_money_amount_money_amount_id_unique",
"primary": false,
"unique": true
},
{
"columnNames": [
"price_list_id"
],
"columnNames": ["price_list_id"],
"composite": false,
"keyName": "IDX_price_rule_price_list_id",
"primary": false,
@@ -459,9 +425,7 @@
},
{
"keyName": "price_set_money_amount_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -471,39 +435,27 @@
"foreignKeys": {
"price_set_money_amount_price_set_id_foreign": {
"constraintName": "price_set_money_amount_price_set_id_foreign",
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"localTableName": "public.price_set_money_amount",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_money_amount_money_amount_id_foreign": {
"constraintName": "price_set_money_amount_money_amount_id_foreign",
"columnNames": [
"money_amount_id"
],
"columnNames": ["money_amount_id"],
"localTableName": "public.price_set_money_amount",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"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"
],
"columnNames": ["price_list_id"],
"localTableName": "public.price_set_money_amount",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_list",
"deleteRule": "cascade",
"updateRule": "cascade"
@@ -554,9 +506,7 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_attribute"
],
"columnNames": ["rule_attribute"],
"composite": false,
"keyName": "IDX_rule_type_rule_attribute",
"primary": false,
@@ -564,9 +514,7 @@
},
{
"keyName": "rule_type_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -609,18 +557,14 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"composite": false,
"keyName": "IDX_price_set_rule_type_price_set_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"composite": false,
"keyName": "IDX_price_set_rule_type_rule_type_id",
"primary": false,
@@ -628,9 +572,7 @@
},
{
"keyName": "price_set_rule_type_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -640,26 +582,18 @@
"foreignKeys": {
"price_set_rule_type_price_set_id_foreign": {
"constraintName": "price_set_rule_type_price_set_id_foreign",
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"localTableName": "public.price_set_rule_type",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_rule_type_rule_type_id_foreign": {
"constraintName": "price_set_rule_type_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"localTableName": "public.price_set_rule_type",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
}
@@ -708,18 +642,14 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"price_set_money_amount_id"
],
"columnNames": ["price_set_money_amount_id"],
"composite": false,
"keyName": "IDX_price_set_money_amount_rules_price_set_money_amount_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"composite": false,
"keyName": "IDX_price_set_money_amount_rules_rule_type_id",
"primary": false,
@@ -727,9 +657,7 @@
},
{
"keyName": "price_set_money_amount_rules_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -739,26 +667,18 @@
"foreignKeys": {
"price_set_money_amount_rules_price_set_money_amount_id_foreign": {
"constraintName": "price_set_money_amount_rules_price_set_money_amount_id_foreign",
"columnNames": [
"price_set_money_amount_id"
],
"columnNames": ["price_set_money_amount_id"],
"localTableName": "public.price_set_money_amount_rules",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_set_money_amount",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_money_amount_rules_rule_type_id_foreign": {
"constraintName": "price_set_money_amount_rules_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"localTableName": "public.price_set_money_amount_rules",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
}
@@ -836,27 +756,21 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"composite": false,
"keyName": "IDX_price_rule_price_set_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"composite": false,
"keyName": "IDX_price_rule_rule_type_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"price_set_money_amount_id"
],
"columnNames": ["price_set_money_amount_id"],
"composite": false,
"keyName": "IDX_price_rule_price_set_money_amount_id",
"primary": false,
@@ -864,9 +778,7 @@
},
{
"keyName": "price_rule_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -876,38 +788,26 @@
"foreignKeys": {
"price_rule_price_set_id_foreign": {
"constraintName": "price_rule_price_set_id_foreign",
"columnNames": [
"price_set_id"
],
"columnNames": ["price_set_id"],
"localTableName": "public.price_rule",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_rule_rule_type_id_foreign": {
"constraintName": "price_rule_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"localTableName": "public.price_rule",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
},
"price_rule_price_set_money_amount_id_foreign": {
"constraintName": "price_rule_price_set_money_amount_id_foreign",
"columnNames": [
"price_set_money_amount_id"
],
"columnNames": ["price_set_money_amount_id"],
"localTableName": "public.price_rule",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_set_money_amount",
"deleteRule": "cascade",
"updateRule": "cascade"
@@ -948,18 +848,14 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"composite": false,
"keyName": "IDX_price_list_rule_rule_type_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"price_list_id"
],
"columnNames": ["price_list_id"],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_id",
"primary": false,
@@ -967,19 +863,14 @@
},
{
"keyName": "IDX_price_list_rule_rule_type_id_price_list_id_unique",
"columnNames": [
"price_list_id",
"rule_type_id"
],
"columnNames": ["price_list_id", "rule_type_id"],
"composite": true,
"primary": false,
"unique": true
},
{
"keyName": "price_list_rule_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -989,25 +880,17 @@
"foreignKeys": {
"price_list_rule_rule_type_id_foreign": {
"constraintName": "price_list_rule_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"columnNames": ["rule_type_id"],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"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"
],
"columnNames": ["price_list_id"],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_list",
"updateRule": "cascade"
}
@@ -1047,9 +930,7 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"price_list_rule_id"
],
"columnNames": ["price_list_rule_id"],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_rule_value_id",
"primary": false,
@@ -1057,9 +938,7 @@
},
{
"keyName": "price_list_rule_value_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -1069,13 +948,9 @@
"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"
],
"columnNames": ["price_list_rule_id"],
"localTableName": "public.price_list_rule_value",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.price_list_rule",
"deleteRule": "cascade",
"updateRule": "cascade"
@@ -18,7 +18,7 @@ export class Migration20230929122253 extends Migration {
)
this.addSql(
'create table "price_set_money_amount" ("id" text not null, "title" text not null, "price_set_id" text not null, "money_amount_id" text not null, "number_rules" integer not null default 0, constraint "price_set_money_amount_pkey" primary key ("id"));'
'create table "price_set_money_amount" ("id" text not null, "title" text not null, "price_set_id" text not null, "money_amount_id" text not null, "rules_count" integer not null default 0, constraint "price_set_money_amount_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_set_money_amount_price_set_id" on "price_set_money_amount" ("price_set_id");'
@@ -103,7 +103,7 @@ export class Migration20230929122253 extends Migration {
)
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"));'
'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, "rules_count" integer not null default 0, constraint "price_list_pkey" primary key ("id"));'
)
this.addSql(
@@ -15,7 +15,7 @@ export class Migration20231101232834 extends Migration {
this.addSql(
`ALTER TABLE price_list
ADD COLUMN IF NOT EXISTS number_rules integer not null default 0`
ADD COLUMN IF NOT EXISTS rules_count integer not null default 0`
)
this.addSql(
@@ -65,7 +65,7 @@ export class Migration20231101232834 extends Migration {
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 rules_count`)
this.addSql('alter table "price_list" drop column if exists "title";')
+2 -2
View File
@@ -21,7 +21,7 @@ import RuleType from "./rule-type"
type OptionalFields =
| "status"
| "type"
| "number_rules"
| "rules_count"
| "starts_at"
| "ends_at"
| "created_at"
@@ -82,7 +82,7 @@ export default class PriceList {
rule_types = new Collection<RuleType>(this)
@Property({ columnType: "integer", default: 0 })
number_rules?: number
rules_count?: number
@Property({
onCreate: () => new Date(),
@@ -38,7 +38,7 @@ export default class PriceSetMoneyAmount {
money_amount?: MoneyAmount
@Property({ columnType: "integer", default: 0 })
number_rules?: number
rules_count?: number
@OneToMany({
entity: () => PriceRule,
+35 -21
View File
@@ -5,6 +5,7 @@ import {
PricingFilters,
} from "@medusajs/types"
import { MedusaError, MikroOrmBase } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PricingRepositoryService } from "../types"
@@ -67,9 +68,9 @@ export class PricingRepository
id: "psma1.id",
price_set_id: "psma1.price_set_id",
money_amount_id: "psma1.money_amount_id",
number_rules: "psma1.number_rules",
rules_count: "psma1.rules_count",
price_list_id: "psma1.price_list_id",
pl_number_rules: "pl.number_rules",
pl_rules_count: "pl.rules_count",
pl_type: "pl.type",
has_price_list: knex.raw(
"case when psma1.price_list_id IS NULL then False else True end"
@@ -86,23 +87,22 @@ export class PricingRepository
)
.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")
.orderBy([
{ column: "number_rules", order: "desc" },
{ column: "pl.number_rules", order: "desc" },
{ column: "rules_count", order: "desc" },
{ column: "pl.rules_count", 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"
"count(DISTINCT rt.rule_attribute) = psma1.rules_count 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"
"count(DISTINCT plrt.rule_attribute) = pl.rules_count AND psma1.price_list_id IS NOT NULL"
)
)
psmaSubQueryKnex.orWhere((q) => {
for (const [key, value] of Object.entries(context)) {
q.orWhere({
@@ -110,8 +110,7 @@ export class PricingRepository
"pr.value": value,
})
}
q.orWhere("psma1.number_rules", "=", 0)
q.orWhere("psma1.rules_count", "=", 0)
q.whereNull("psma1.price_list_id")
})
@@ -124,14 +123,29 @@ export class PricingRepository
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.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)
this.orWhere("pl.rules_count", "=", 0)
})
this.andWhere(function () {
this.andWhere(function () {
for (const [key, value] of Object.entries(context)) {
this.orWhere({
"rt.rule_attribute": key,
"pr.value": value,
})
}
this.andWhere("psma1.rules_count", ">", 0)
})
this.orWhere("psma1.rules_count", "=", 0)
})
})
})
@@ -146,8 +160,8 @@ export class PricingRepository
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",
rules_count: "psma.rules_count",
pl_rules_count: "psma.pl_rules_count",
price_list_type: "psma.pl_type",
price_list_id: "psma.price_list_id",
})
@@ -160,9 +174,9 @@ export class PricingRepository
.orderBy([
{ column: "psma.has_price_list", order: "asc" },
{ column: "number_rules", order: "desc" },
{ column: "default_priority", order: "desc" },
{ column: "amount", order: "asc" },
{ column: "rules_count", order: "desc" },
{ column: "default_priority", order: "desc" },
])
if (quantity) {
@@ -1,6 +1,5 @@
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
import {
doNotForceTransaction,
InjectManager,
InjectTransactionManager,
MedusaContext,
+193 -79
View File
@@ -14,12 +14,14 @@ import {
RuleTypeDTO,
} from "@medusajs/types"
import {
groupBy,
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
PriceListType,
arrayDifference,
deduplicate,
groupBy,
removeNullish,
} from "@medusajs/utils"
@@ -153,7 +155,7 @@ export default class PricingModuleService<
pricingFilters.id.map(
(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
// which is prioritized by rules_count first for exact match and then deafult_priority of the rule_type
// inject custom price selection here
const prices = pricesSetPricesMap.get(priceSetId) || []
const priceListPrice = prices.find((p) => p.price_list_id)
@@ -288,7 +290,6 @@ export default class PricingModuleService<
{ id: priceSets.filter((p) => !!p).map((p) => p!.id) },
{
relations: ["rule_types", "money_amounts", "price_rules"],
take: null,
},
sharedContext
)
@@ -332,7 +333,9 @@ export default class PricingModuleService<
}
const invalidMoneyAmountRule = data
.map((d) => d.prices?.map((ma) => Object.keys(ma.rules)).flat() ?? [])
.map(
(d) => d.prices?.map((ma) => Object.keys(ma?.rules ?? {})).flat() ?? []
)
.flat()
.filter((r) => !ruleTypeMap.has(r))
@@ -360,6 +363,7 @@ export default class PricingModuleService<
price_set: createdPriceSets[index],
})) || []
)
if (ruleTypeData.length > 0) {
await this.priceSetRuleTypeService_.create(
ruleTypeData as unknown as PricingTypes.CreatePriceSetRuleTypeDTO[],
@@ -387,7 +391,7 @@ export default class PricingModuleService<
price_set: createdPriceSets[index],
money_amount: createdMoneyAmounts[moneyAmountIndex++],
title: "test", // TODO: accept title
number_rules: numberOfRules,
rules_count: numberOfRules,
}
priceSetMoneyAmountData.push(priceSetMoneyAmount)
@@ -412,7 +416,7 @@ export default class PricingModuleService<
// Update price set money amount references
for (let i = 0, j = 0; i < priceSetMoneyAmountData.length; i++) {
const rulesCount = (priceSetMoneyAmountData[i] as any).number_rules
const rulesCount = (priceSetMoneyAmountData[i] as any).rules_count
for (let k = 0; k < rulesCount; k++, j++) {
;(priceRulesData[j] as any).price_set_money_amount =
createdPriceSetMoneyAmounts[i]
@@ -449,19 +453,22 @@ export default class PricingModuleService<
const priceSets = await this.addRules_(inputs, sharedContext)
return (Array.isArray(data) ? priceSets : priceSets[0]) as unknown as
| PricingTypes.PriceSetDTO[]
| PricingTypes.PriceSetDTO
return await this.list(
{ id: priceSets.map(({ id }) => id) },
{
relations: ["rule_types"],
}
)
}
@InjectTransactionManager("baseRepository_")
protected async addRules_(
inputs: PricingTypes.AddRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetDTO[]> {
): Promise<TPriceSet[]> {
const priceSets = await this.priceSetService_.list(
{ id: inputs.map((d) => d.priceSetId) },
{ relations: ["rule_types"], take: null },
{ relations: ["rule_types"] },
sharedContext
)
@@ -537,12 +544,7 @@ export default class PricingModuleService<
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceSetDTO[]>(
priceSets,
{
populate: true,
}
)
return priceSets
}
async addPrices(
@@ -566,7 +568,7 @@ export default class PricingModuleService<
return (await this.list(
{ id: input.map((d) => d.priceSetId) },
{ relations: ["money_amounts"], take: null },
{ relations: ["money_amounts"] },
sharedContext
)) as unknown as PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO
}
@@ -578,7 +580,7 @@ export default class PricingModuleService<
) {
const priceSets = await this.list(
{ id: input.map((d) => d.priceSetId) },
{ relations: ["rule_types"], take: null },
{ relations: ["rule_types"] },
sharedContext
)
@@ -640,7 +642,7 @@ export default class PricingModuleService<
price_set: priceSetId,
money_amount: ma,
title: "test", // TODO: accept title
number_rules: numberOfRules,
rules_count: numberOfRules,
}
})
)
@@ -684,7 +686,7 @@ export default class PricingModuleService<
{
id: data.map((d) => d.id),
},
{ take: null },
{},
sharedContext
)
const priceSetIds = priceSets.map((ps) => ps.id)
@@ -1386,61 +1388,71 @@ export default class PricingModuleService<
data: PricingTypes.CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const createdPriceLists: PricingTypes.PriceListDTO[] = []
const ruleAttributes = data
.map((priceListData) => Object.keys(priceListData.rules || {}))
.flat()
const ruleTypeAttributes: string[] = []
for (const priceListData of data) {
const { prices = [], rules: priceListRules = {} } = priceListData
ruleTypeAttributes.push(...Object.keys(priceListRules))
for (const price of prices) {
const { rules: priceListPriceRules = {} } = price
ruleTypeAttributes.push(...Object.keys(priceListPriceRules))
}
}
const ruleTypes = await this.listRuleTypes(
{
rule_attribute: ruleAttributes,
},
{ rule_attribute: ruleTypeAttributes },
{ take: null }
)
const invalidRuleTypes = arrayDifference(
deduplicate(ruleTypeAttributes),
ruleTypes.map((ruleType) => ruleType.rule_attribute)
)
if (invalidRuleTypes.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot find RuleTypes with rule_attribute - ${invalidRuleTypes.join(
", "
)}`
)
}
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
const priceListsToCreate: PricingTypes.CreatePriceListDTO[] = []
for (const priceListData of data) {
const { rules = {}, prices = [], ...priceListOnlyData } = priceListData
const { rules = {}, ...priceListOnlyData } = priceListData
const [createdPriceList] = (await this.priceListService_.create(
[
{
...priceListOnlyData,
number_rules: Object.keys(rules).length,
},
],
sharedContext
)) as unknown as PricingTypes.PriceListDTO[]
priceListsToCreate.push({
...priceListOnlyData,
rules_count: Object.keys(rules).length,
})
}
createdPriceLists.push(createdPriceList)
const priceLists = (await this.priceListService_.create(
priceListsToCreate
)) as unknown as PricingTypes.PriceListDTO[]
for (var i = 0; i < data.length; i++) {
const { rules = {}, prices = [] } = data[i]
const priceList = priceLists[i]
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)
}
let ruleType = ruleTypeMap.get(ruleAttribute)!
// Create the rule
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: createdPriceList,
rule_type: ruleType?.id || ruleType,
price_list: priceList,
rule_type: ruleType.id,
},
],
sharedContext
@@ -1461,29 +1473,48 @@ export default class PricingModuleService<
}
for (const price of prices) {
const { price_set_id: priceSetId, ...moneyAmountData } = price
const {
price_set_id: priceSetId,
rules: priceRules = {},
...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[],
const [priceSetMoneyAmount] =
await this.priceSetMoneyAmountService_.create(
[
{
price_set: priceSetId,
price_list: priceList,
money_amount: moneyAmount,
title: "test",
rules_count: Object.keys(priceRules).length,
},
] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[],
sharedContext
)
await this.createPriceRules(
Object.entries(priceRules).map(([ruleAttribute, ruleValue]) => {
return {
price_set_id: priceSetId,
rule_type:
ruleTypeMap.get(ruleAttribute)!?.id ||
ruleTypeMap.get(ruleAttribute)!,
value: ruleValue,
price_set_money_amount: priceSetMoneyAmount as any,
}
}),
sharedContext
)
}
}
return createdPriceLists
return priceLists
}
@InjectTransactionManager("baseRepository_")
@@ -1519,7 +1550,7 @@ export default class PricingModuleService<
const existingPriceLists = await this.listPriceLists(
{ id: priceListIds },
{ relations: ["price_list_rules"], take: null },
{ relations: ["price_list_rules"] },
sharedContext
)
@@ -1531,7 +1562,7 @@ export default class PricingModuleService<
{
id: priceListRuleIds,
},
{ take: null },
{},
sharedContext
)
@@ -1561,7 +1592,7 @@ export default class PricingModuleService<
}
if (typeof rules === "object") {
updatePriceListData.number_rules = Object.keys(rules).length
updatePriceListData.rules_count = Object.keys(rules).length
}
const [updatedPriceList] = (await this.priceListService_.update(
@@ -1750,11 +1781,78 @@ export default class PricingModuleService<
data: PricingTypes.AddPriceListPricesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.listPriceLists(
{ id: data.map((d) => d.priceListId) },
{
take: null,
const ruleTypeAttributes: string[] = []
const priceListIds: string[] = []
const priceSetIds: string[] = []
for (const priceListData of data) {
priceListIds.push(priceListData.priceListId)
for (const price of priceListData.prices) {
ruleTypeAttributes.push(...Object.keys(price.rules || {}))
priceSetIds.push(price.price_set_id)
}
}
const ruleTypes = await this.listRuleTypes(
{ rule_attribute: ruleTypeAttributes },
{ take: null },
sharedContext
)
const priceSets = await this.list(
{ id: priceSetIds },
{ relations: ["rule_types"] },
sharedContext
)
const priceSetRuleTypeMap: Map<string, Set<string>> = priceSets.reduce(
(acc, curr) => {
const priceSetRuleAttributeSet: Set<string> =
acc.get(curr.id) || new Set()
for (const rt of curr.rule_types ?? []) {
priceSetRuleAttributeSet.add(rt.rule_attribute)
}
acc.set(curr.id, priceSetRuleAttributeSet)
return acc
},
new Map()
)
const ruleTypeErrors: string[] = []
for (const priceListData of data) {
for (const price of priceListData.prices) {
for (const rule_attribute of Object.keys(price.rules ?? {})) {
if (
!priceSetRuleTypeMap.get(price.price_set_id)?.has(rule_attribute)
) {
ruleTypeErrors.push(
`rule_attribute "${rule_attribute}" in price set ${price.price_set_id}`
)
}
}
}
}
if (ruleTypeErrors.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Invalid rule type configuration: Price set rules doesn't exist for ${ruleTypeErrors.join(
", "
)}`
)
}
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
const priceLists = await this.listPriceLists(
{ id: priceListIds },
{},
sharedContext
)
@@ -1772,23 +1870,41 @@ export default class PricingModuleService<
await Promise.all(
prices.map(async (price) => {
const priceRules = price.rules || {}
const noOfRules = Object.keys(priceRules).length
const [moneyAmount] = await this.moneyAmountService_.create(
[price] as unknown as CreateMoneyAmountDTO[],
sharedContext
)
const psma = await this.priceSetMoneyAmountService_.create(
const [psma] = await this.priceSetMoneyAmountService_.create(
[
{
price_set: price.price_set_id,
money_amount: moneyAmount.id,
title: "test",
price_list: priceList.id,
rules_count: noOfRules,
},
],
sharedContext
)
await this.createPriceRules(
Object.entries(priceRules).map(([ruleAttribute, ruleValue]) => {
return {
price_set_id: price.price_set_id,
rule_type:
ruleTypeMap.get(ruleAttribute)!?.id ||
ruleTypeMap.get(ruleAttribute)!,
value: ruleValue,
price_set_money_amount: psma as any,
}
}),
sharedContext
)
return psma
})
)
@@ -1816,7 +1932,6 @@ export default class PricingModuleService<
{ id: data.map((d) => d.priceListId) },
{
relations: ["price_list_rules", "price_list_rules.rule_type"],
take: null,
},
sharedContext
)
@@ -1956,7 +2071,6 @@ export default class PricingModuleService<
{ id: data.map((d) => d.priceListId) },
{
relations: ["price_list_rules", "price_list_rules.rule_type"],
take: null,
},
sharedContext
)
@@ -7,5 +7,5 @@ export interface CreatePriceListDTO {
ends_at?: string
status?: PriceListStatus
type?: PriceListType
number_rules?: number
rules_count?: number
}