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 20:24:29 +00:00
committed by GitHub
co-authored by Philip Korsholm
parent 2947f57db1
commit 1772e80ed1
58 changed files with 5745 additions and 1112 deletions
+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,
}
)
}
}