Files
medusa-store/packages/pricing/src/services/pricing-module.ts
Philip Korsholm c1c470e6b8 fix(medusa, pricing, types): pass dates as Date-objects rather than strings to the pricing module (#5768)
* init

* remove date string validator;

* add transformOptionalDate transformer to api

* move type conversion to the datalayer

* fix final module integration test

* update arrow-function

* make string optional

* move work to utils

* make check for value exists

* move util back to pricng

* change utils

* refactor get-iso-string

* fix build

* flip transform condition

* add null check for isDate

* feat(pricing): Separate Pricing Module internal types from `@medusajs/types` (#5777)

* create types for pricing repositories

* create RepositoryTypes input

* add service types

* use models for repository types

* fix build

* update types to match interface types

* add aliases

* types instead of moduletypes

* move repository to types for pricing module

* add changeset

* fix merge error

* fix conflict

* fix build

* re-add validation of dates in updatePriceLists_
2023-12-21 08:29:41 +01:00

2126 lines
59 KiB
TypeScript

import {
AddPricesDTO,
Context,
CreateMoneyAmountDTO,
CreatePriceListRuleDTO,
DAL,
FindConfig,
InternalModuleDeclaration,
ModuleJoinerConfig,
MoneyAmountDTO,
PriceSetDTO,
PricingContext,
PricingFilters,
PricingRepositoryService,
PricingTypes,
RuleTypeDTO,
} from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
PriceListType,
arrayDifference,
deduplicate,
groupBy,
removeNullish,
} from "@medusajs/utils"
import {
Currency,
MoneyAmount,
PriceList,
PriceListRule,
PriceListRuleValue,
PriceRule,
PriceSet,
PriceSetMoneyAmount,
PriceSetMoneyAmountRules,
PriceSetRuleType,
RuleType,
} from "@models"
import {
CurrencyService,
MoneyAmountService,
PriceListRuleService,
PriceListRuleValueService,
PriceListService,
PriceRuleService,
PriceSetMoneyAmountRulesService,
PriceSetMoneyAmountService,
PriceSetRuleTypeService,
PriceSetService,
RuleTypeService,
} from "@services"
import { joinerConfig } from "../joiner-config"
import { validatePriceListDates } from "@utils"
import { ServiceTypes } from "@types"
import { CreatePriceListRuleValueDTO } from "src/types/services"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
pricingRepository: PricingRepositoryService
currencyService: CurrencyService<any>
moneyAmountService: MoneyAmountService<any>
priceSetService: PriceSetService<any>
priceSetMoneyAmountRulesService: PriceSetMoneyAmountRulesService<any>
ruleTypeService: RuleTypeService<any>
priceRuleService: PriceRuleService<any>
priceSetRuleTypeService: PriceSetRuleTypeService<any>
priceSetMoneyAmountService: PriceSetMoneyAmountService<any>
priceListService: PriceListService<any>
priceListRuleService: PriceListRuleService<any>
priceListRuleValueService: PriceListRuleValueService<any>
}
export default class PricingModuleService<
TPriceSet extends PriceSet = PriceSet,
TMoneyAmount extends MoneyAmount = MoneyAmount,
TCurrency extends Currency = Currency,
TRuleType extends RuleType = RuleType,
TPriceSetMoneyAmountRules extends PriceSetMoneyAmountRules = PriceSetMoneyAmountRules,
TPriceRule extends PriceRule = PriceRule,
TPriceSetRuleType extends PriceSetRuleType = PriceSetRuleType,
TPriceSetMoneyAmount extends PriceSetMoneyAmount = PriceSetMoneyAmount,
TPriceList extends PriceList = PriceList,
TPriceListRule extends PriceListRule = PriceListRule,
TPriceListRuleValue extends PriceListRuleValue = PriceListRuleValue
> implements PricingTypes.IPricingModuleService
{
protected baseRepository_: DAL.RepositoryService
protected readonly pricingRepository_: PricingRepositoryService
protected readonly currencyService_: CurrencyService<TCurrency>
protected readonly moneyAmountService_: MoneyAmountService<TMoneyAmount>
protected readonly ruleTypeService_: RuleTypeService<TRuleType>
protected readonly priceSetService_: PriceSetService<TPriceSet>
protected readonly priceSetMoneyAmountRulesService_: PriceSetMoneyAmountRulesService<TPriceSetMoneyAmountRules>
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(
{
baseRepository,
pricingRepository,
moneyAmountService,
currencyService,
ruleTypeService,
priceSetService,
priceSetMoneyAmountRulesService,
priceRuleService,
priceSetRuleTypeService,
priceSetMoneyAmountService,
priceListService,
priceListRuleService,
priceListRuleValueService,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
this.baseRepository_ = baseRepository
this.pricingRepository_ = pricingRepository
this.currencyService_ = currencyService
this.moneyAmountService_ = moneyAmountService
this.ruleTypeService_ = ruleTypeService
this.priceSetService_ = priceSetService
this.priceSetMoneyAmountRulesService_ = priceSetMoneyAmountRulesService
this.ruleTypeService_ = ruleTypeService
this.priceRuleService_ = priceRuleService
this.priceSetRuleTypeService_ = priceSetRuleTypeService
this.priceSetMoneyAmountService_ = priceSetMoneyAmountService
this.priceListService_ = priceListService
this.priceListRuleService_ = priceListRuleService
this.priceListRuleValueService_ = priceListRuleValueService
}
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
@InjectManager("baseRepository_")
async calculatePrices(
pricingFilters: PricingFilters,
pricingContext: PricingContext = { context: {} },
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.CalculatedPriceSet[]> {
const results = await this.pricingRepository_.calculatePrices(
pricingFilters,
pricingContext,
sharedContext
)
const pricesSetPricesMap = groupBy(results, "price_set_id")
const calculatedPrices: PricingTypes.CalculatedPriceSet[] =
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 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)
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,
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,
},
}
}
)
return JSON.parse(JSON.stringify(calculatedPrices))
}
@InjectManager("baseRepository_")
async retrieve(
id: string,
config: FindConfig<PricingTypes.PriceSetDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetDTO> {
const priceSet = await this.priceSetService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceSetDTO>(priceSet, {
populate: true,
})
}
@InjectManager("baseRepository_")
async list(
filters: PricingTypes.FilterablePriceSetProps = {},
config: FindConfig<PricingTypes.PriceSetDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetDTO[]> {
const priceSets = await this.priceSetService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceSetDTO[]>(
priceSets,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCount(
filters: PricingTypes.FilterablePriceSetProps = {},
config: FindConfig<PricingTypes.PriceSetDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceSetDTO[], number]> {
const [priceSets, count] = await this.priceSetService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceSetDTO[]>(
priceSets,
{
populate: true,
}
),
count,
]
}
async create(
data: PricingTypes.CreatePriceSetDTO,
sharedContext?: Context
): Promise<PriceSetDTO>
async create(
data: PricingTypes.CreatePriceSetDTO[],
sharedContext?: Context
): Promise<PriceSetDTO[]>
@InjectManager("baseRepository_")
async create(
data: PricingTypes.CreatePriceSetDTO | PricingTypes.CreatePriceSetDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PriceSetDTO | PriceSetDTO[]> {
const input = Array.isArray(data) ? data : [data]
const priceSets = await this.create_(input, sharedContext)
const dbPriceSets = await this.list(
{ id: priceSets.filter((p) => !!p).map((p) => p!.id) },
{
relations: ["rule_types", "money_amounts", "price_rules"],
},
sharedContext
)
return (Array.isArray(data) ? dbPriceSets : dbPriceSets[0]) as unknown as
| PriceSetDTO
| PriceSetDTO[]
}
@InjectTransactionManager("baseRepository_")
protected async create_(
data: PricingTypes.CreatePriceSetDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const ruleAttributes = data
.map((d) => d.rules?.map((r) => r.rule_attribute) ?? [])
.flat()
const ruleTypes = await this.ruleTypeService_.list(
{
rule_attribute: ruleAttributes,
},
{ take: null },
sharedContext
)
const ruleTypeMap = ruleTypes.reduce((acc, curr) => {
acc.set(curr.rule_attribute, curr)
return acc
}, new Map())
const invalidRuleAttributes = ruleAttributes.filter(
(r) => !ruleTypeMap.has(r)
)
if (invalidRuleAttributes.length > 0) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Rule types don't exist for: ${invalidRuleAttributes.join(", ")}`
)
}
const invalidMoneyAmountRule = data
.map(
(d) => d.prices?.map((ma) => Object.keys(ma?.rules ?? {})).flat() ?? []
)
.flat()
.filter((r) => !ruleTypeMap.has(r))
if (invalidMoneyAmountRule.length > 0) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Rule types don't exist for money amounts with rule attribute: ${invalidMoneyAmountRule.join(
", "
)}`
)
}
// Bulk create price sets
const priceSetData = data.map(({ rules, prices, ...rest }) => rest)
const createdPriceSets = await this.priceSetService_.create(
priceSetData,
sharedContext
)
// Price set rule types
const ruleTypeData = data.flatMap(
(item, index) =>
item.rules?.map((rule) => ({
rule_type: ruleTypeMap.get(rule.rule_attribute),
price_set: createdPriceSets[index],
})) || []
)
if (ruleTypeData.length > 0) {
await this.priceSetRuleTypeService_.create(
ruleTypeData as unknown as PricingTypes.CreatePriceSetRuleTypeDTO[],
sharedContext
)
}
// Money amounts
const moneyAmountData = data.flatMap((item) => item.prices || [])
const createdMoneyAmounts = await this.moneyAmountService_.create(
moneyAmountData,
sharedContext
)
let moneyAmountIndex = 0
const priceSetMoneyAmountData: unknown[] = []
const priceRulesData: unknown[] = []
for (const [index, item] of data.entries()) {
for (const ma of item.prices || []) {
const cleanRules = ma.rules ? removeNullish(ma.rules) : {}
const numberOfRules = Object.entries(cleanRules).length
const priceSetMoneyAmount = {
price_set: createdPriceSets[index],
money_amount: createdMoneyAmounts[moneyAmountIndex++],
title: "test", // TODO: accept title
rules_count: numberOfRules,
}
priceSetMoneyAmountData.push(priceSetMoneyAmount)
for (const [k, v] of Object.entries(cleanRules)) {
priceRulesData.push({
price_set_money_amount: null, // Updated later
rule_type: ruleTypeMap.get(k),
price_set: createdPriceSets[index],
value: v,
price_list_id: "test",
})
}
}
}
// Bulk create price set money amounts
const createdPriceSetMoneyAmounts =
await this.priceSetMoneyAmountService_.create(
priceSetMoneyAmountData as PricingTypes.CreatePriceSetMoneyAmountDTO[],
sharedContext
)
// Update price set money amount references
for (let i = 0, j = 0; i < priceSetMoneyAmountData.length; i++) {
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]
}
}
// Price rules
if (priceRulesData.length > 0) {
await this.priceRuleService_.create(
priceRulesData as ServiceTypes.CreatePriceRuleDTO[],
sharedContext
)
}
return createdPriceSets
}
async addRules(
data: PricingTypes.AddRulesDTO,
sharedContext?: Context
): Promise<PricingTypes.PriceSetDTO>
async addRules(
data: PricingTypes.AddRulesDTO[],
sharedContext?: Context
): Promise<PricingTypes.PriceSetDTO[]>
@InjectManager("baseRepository_")
async addRules(
data: PricingTypes.AddRulesDTO | PricingTypes.AddRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO> {
const inputs = Array.isArray(data) ? data : [data]
const priceSets = await this.addRules_(inputs, sharedContext)
return await this.list(
{ id: priceSets.map(({ id }) => id) },
{
relations: ["rule_types"],
}
)
}
@InjectTransactionManager("baseRepository_")
protected async addRules_(
inputs: PricingTypes.AddRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TPriceSet[]> {
const priceSets = await this.priceSetService_.list(
{ id: inputs.map((d) => d.priceSetId) },
{ relations: ["rule_types"] },
sharedContext
)
const priceSetRuleTypeMap: Map<string, Map<string, RuleTypeDTO>> = new Map(
priceSets.map((priceSet) => [
priceSet.id,
new Map([...priceSet.rule_types].map((rt) => [rt.rule_attribute, rt])),
])
)
const priceSetMap = new Map(priceSets.map((p) => [p.id, p]))
const invalidPriceSetInputs = inputs.filter(
(d) => !priceSetMap.has(d.priceSetId)
)
if (invalidPriceSetInputs.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`PriceSets with ids: ${invalidPriceSetInputs
.map((d) => d.priceSetId)
.join(", ")} was not found`
)
}
const ruleTypes = await this.ruleTypeService_.list(
{
rule_attribute: inputs
.map((d) => d.rules.map((r) => r.attribute))
.flat(),
},
{ take: null },
sharedContext
)
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
const invalidRuleAttributeInputs = inputs
.map((d) => d.rules.map((r) => r.attribute))
.flat()
.filter((r) => !ruleTypeMap.has(r))
if (invalidRuleAttributeInputs.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Rule types don't exist for attributes: ${[
...new Set(invalidRuleAttributeInputs),
].join(", ")}`
)
}
const priceSetRuleTypesCreate: PricingTypes.CreatePriceSetRuleTypeDTO[] = []
inputs.forEach((d) => {
const priceSet = priceSetMap.get(d.priceSetId)
for (const r of d.rules) {
if (priceSetRuleTypeMap.get(d.priceSetId)!.has(r.attribute)) {
continue
}
priceSetRuleTypesCreate.push({
rule_type: ruleTypeMap.get(r.attribute) as RuleTypeDTO,
price_set: priceSet as unknown as PriceSetDTO,
})
}
})
await this.priceSetRuleTypeService_.create(
priceSetRuleTypesCreate as unknown as PricingTypes.CreatePriceSetRuleTypeDTO[],
sharedContext
)
return priceSets
}
async addPrices(
data: AddPricesDTO,
sharedContext?: Context
): Promise<PricingTypes.PriceSetDTO>
async addPrices(
data: AddPricesDTO[],
sharedContext?: Context
): Promise<PricingTypes.PriceSetDTO[]>
@InjectManager("baseRepository_")
async addPrices(
data: AddPricesDTO | AddPricesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO> {
const input = Array.isArray(data) ? data : [data]
await this.addPrices_(input, sharedContext)
return (await this.list(
{ id: input.map((d) => d.priceSetId) },
{ relations: ["money_amounts"] },
sharedContext
)) as unknown as PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO
}
@InjectTransactionManager("baseRepository_")
protected async addPrices_(
input: AddPricesDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const priceSets = await this.list(
{ id: input.map((d) => d.priceSetId) },
{ relations: ["rule_types"] },
sharedContext
)
const priceSetMap = new Map(priceSets.map((p) => [p.id, p]))
const ruleTypeMap: Map<string, Map<string, RuleTypeDTO>> = new Map(
priceSets.map((priceSet) => [
priceSet.id,
new Map(
priceSet.rule_types?.map((rt) => [rt.rule_attribute, rt]) ?? []
),
])
)
input.forEach(({ priceSetId, prices }) => {
const priceSet = priceSetMap.get(priceSetId)
if (!priceSet) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price set with id: ${priceSetId} not found`
)
}
const ruleAttributes = prices
.map((d) => (d.rules ? Object.keys(d.rules) : []))
.flat()
const invalidRuleAttributes = ruleAttributes.filter(
(r) => !ruleTypeMap.get(priceSetId)!.has(r)
)
if (invalidRuleAttributes.length > 0) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Rule types don't exist for: ${invalidRuleAttributes.join(", ")}`
)
}
})
// Money amounts
const moneyAmountsBulkData = input.flatMap((entry) => entry.prices)
const createdMoneyAmounts = await this.moneyAmountService_.create(
moneyAmountsBulkData as unknown as CreateMoneyAmountDTO[],
sharedContext
)
// Price set money amounts
let maCursor = 0
const priceSetMoneyAmountsBulkData: unknown[] =
input.flatMap(({ priceSetId, prices }) =>
prices.map(() => {
const ma = createdMoneyAmounts[maCursor]
const numberOfRules = Object.entries(
prices[maCursor]?.rules ?? {}
).length
maCursor++
return {
price_set: priceSetId,
money_amount: ma,
title: "test", // TODO: accept title
rules_count: numberOfRules,
}
})
)
const createdPriceSetMoneyAmounts =
await this.priceSetMoneyAmountService_.create(
priceSetMoneyAmountsBulkData as ServiceTypes.CreatePriceSetMoneyAmountDTO[],
sharedContext
)
// Price rules
let rulesCursor = 0
const priceRulesBulkData = input.flatMap(({ priceSetId, prices }) =>
prices.flatMap((ma) => {
const rules = ma.rules ?? {}
const priceSetMoneyAmount = createdPriceSetMoneyAmounts[rulesCursor]
rulesCursor++
return Object.entries(rules).map(([k, v]) => ({
price_set_money_amount: priceSetMoneyAmount,
rule_type_id: ruleTypeMap.get(priceSetId)!.get(k)!.id,
price_set: priceSetId,
value: v,
}))
})
)
if (priceRulesBulkData.length > 0) {
await this.priceRuleService_.create(priceRulesBulkData, sharedContext)
}
}
@InjectTransactionManager("baseRepository_")
async removeRules(
data: PricingTypes.RemovePriceSetRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
const priceSets = await this.priceSetService_.list(
{
id: data.map((d) => d.id),
},
{},
sharedContext
)
const priceSetIds = priceSets.map((ps) => ps.id)
const ruleTypes = await this.ruleTypeService_.list(
{
rule_attribute: data.map((d) => d.rules || []).flat(),
},
{ take: null },
sharedContext
)
const ruleTypeIds = ruleTypes.map((rt) => rt.id)
const priceSetRuleTypes = await this.priceSetRuleTypeService_.list(
{
price_set_id: priceSetIds,
rule_type_id: ruleTypeIds,
},
{ take: null },
sharedContext
)
const priceRules = await this.priceRuleService_.list(
{
price_set_id: priceSetIds,
rule_type_id: ruleTypeIds,
},
{
select: ["price_set_money_amount"],
take: null,
},
sharedContext
)
await this.priceSetRuleTypeService_.delete(
priceSetRuleTypes.map((psrt) => psrt.id),
sharedContext
)
await this.priceSetMoneyAmountService_.delete(
priceRules.map((pr) => pr.price_set_money_amount.id),
sharedContext
)
}
@InjectTransactionManager("baseRepository_")
async update(
data: PricingTypes.UpdatePriceSetDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const priceSets = await this.priceSetService_.update(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceSetDTO[]>(
priceSets,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceSetService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveMoneyAmount(
id: string,
config: FindConfig<PricingTypes.MoneyAmountDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.MoneyAmountDTO> {
const moneyAmount = await this.moneyAmountService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.MoneyAmountDTO>(
moneyAmount,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listMoneyAmounts(
filters: PricingTypes.FilterableMoneyAmountProps = {},
config: FindConfig<PricingTypes.MoneyAmountDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.MoneyAmountDTO[]> {
const moneyAmounts = await this.moneyAmountService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.MoneyAmountDTO[]>(
moneyAmounts,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountMoneyAmounts(
filters: PricingTypes.FilterableMoneyAmountProps = {},
config: FindConfig<PricingTypes.MoneyAmountDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.MoneyAmountDTO[], number]> {
const [moneyAmounts, count] = await this.moneyAmountService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.MoneyAmountDTO[]>(
moneyAmounts,
{
populate: true,
}
),
count,
]
}
@InjectTransactionManager("baseRepository_")
async createMoneyAmounts(
data: PricingTypes.CreateMoneyAmountDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const moneyAmounts = await this.moneyAmountService_.create(
data,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.MoneyAmountDTO[]>(
moneyAmounts,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async updateMoneyAmounts(
data: PricingTypes.UpdateMoneyAmountDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const moneyAmounts = await this.moneyAmountService_.update(
data,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.MoneyAmountDTO[]>(
moneyAmounts,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deleteMoneyAmounts(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.moneyAmountService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveCurrency(
code: string,
config: FindConfig<PricingTypes.CurrencyDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.CurrencyDTO> {
const currency = await this.currencyService_.retrieve(
code,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO>(currency, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listCurrencies(
filters: PricingTypes.FilterableCurrencyProps = {},
config: FindConfig<PricingTypes.CurrencyDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.CurrencyDTO[]> {
const currencies = await this.currencyService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
currencies,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountCurrencies(
filters: PricingTypes.FilterableCurrencyProps = {},
config: FindConfig<PricingTypes.CurrencyDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.CurrencyDTO[], number]> {
const [currencies, count] = await this.currencyService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
currencies,
{
populate: true,
}
),
count,
]
}
@InjectTransactionManager("baseRepository_")
async createCurrencies(
data: PricingTypes.CreateCurrencyDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const currencies = await this.currencyService_.create(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
currencies,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async updateCurrencies(
data: PricingTypes.UpdateCurrencyDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const currencies = await this.currencyService_.update(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
currencies,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deleteCurrencies(
currencyCodes: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.currencyService_.delete(currencyCodes, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveRuleType(
id: string,
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO> {
const ruleType = await this.ruleTypeService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO>(ruleType, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listRuleTypes(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountRuleTypes(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.RuleTypeDTO[], number]> {
const [ruleTypes, count] = await this.ruleTypeService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
),
count,
]
}
@InjectTransactionManager("baseRepository_")
async createRuleTypes(
data: PricingTypes.CreateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.create(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async updateRuleTypes(
data: PricingTypes.UpdateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.update(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deleteRuleTypes(
ruleTypeIds: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.ruleTypeService_.delete(ruleTypeIds, sharedContext)
}
@InjectManager("baseRepository_")
async retrievePriceSetMoneyAmountRules(
id: string,
config: FindConfig<PricingTypes.PriceSetMoneyAmountRulesDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetMoneyAmountRulesDTO> {
const record = await this.priceSetMoneyAmountRulesService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceSetMoneyAmountRulesDTO>(
record,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceSetMoneyAmountRules(
filters: PricingTypes.FilterablePriceSetMoneyAmountRulesProps = {},
config: FindConfig<PricingTypes.PriceSetMoneyAmountRulesDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetMoneyAmountRulesDTO[]> {
const records = await this.priceSetMoneyAmountRulesService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listAndCountPriceSetMoneyAmountRules(
filters: PricingTypes.FilterablePriceSetMoneyAmountRulesProps = {},
config: FindConfig<PricingTypes.PriceSetMoneyAmountRulesDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceSetMoneyAmountRulesDTO[], number]> {
const [records, count] =
await this.priceSetMoneyAmountRulesService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
}),
count,
]
}
@InjectManager("baseRepository_")
async listPriceSetMoneyAmounts(
filters: PricingTypes.FilterablePriceSetMoneyAmountProps = {},
config: FindConfig<PricingTypes.PriceSetMoneyAmountDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetMoneyAmountDTO[]> {
const records = await this.priceSetMoneyAmountService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listAndCountPriceSetMoneyAmounts(
filters: PricingTypes.FilterablePriceSetMoneyAmountProps = {},
config: FindConfig<PricingTypes.PriceSetMoneyAmountDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceSetMoneyAmountDTO[], number]> {
const [records, count] =
await this.priceSetMoneyAmountService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
}),
count,
]
}
@InjectTransactionManager("baseRepository_")
async createPriceSetMoneyAmountRules(
data: PricingTypes.CreatePriceSetMoneyAmountRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetMoneyAmountRulesDTO[]> {
const records = await this.priceSetMoneyAmountRulesService_.create(
data,
sharedContext
)
return this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
async updatePriceSetMoneyAmountRules(
data: PricingTypes.UpdatePriceSetMoneyAmountRulesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceSetMoneyAmountRulesDTO[]> {
const records = await this.priceSetMoneyAmountRulesService_.update(
data,
sharedContext
)
return this.baseRepository_.serialize<
PricingTypes.PriceSetMoneyAmountRulesDTO[]
>(records, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
async deletePriceSetMoneyAmountRules(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceSetMoneyAmountRulesService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async retrievePriceRule(
id: string,
config: FindConfig<PricingTypes.PriceRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceRuleDTO> {
const priceRule = await this.priceRuleService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceRuleDTO>(
priceRule,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceRules(
filters: PricingTypes.FilterablePriceRuleProps = {},
config: FindConfig<PricingTypes.PriceRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceRuleDTO[]> {
const priceRules = await this.priceRuleService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceRuleDTO[]>(
priceRules,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountPriceRules(
filters: PricingTypes.FilterablePriceRuleProps = {},
config: FindConfig<PricingTypes.PriceRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceRuleDTO[], number]> {
const [priceRules, count] = await this.priceRuleService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceRuleDTO[]>(
priceRules,
{
populate: true,
}
),
count,
]
}
@InjectTransactionManager("baseRepository_")
async createPriceRules(
data: PricingTypes.CreatePriceRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceRuleDTO[]> {
const priceRules = await this.priceRuleService_.create(
data as ServiceTypes.CreatePriceRuleDTO[],
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceRuleDTO[]>(
priceRules,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async updatePriceRules(
data: PricingTypes.UpdatePriceRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceRuleDTO[]> {
const priceRules = await this.priceRuleService_.update(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceRuleDTO[]>(
priceRules,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deletePriceRules(
priceRuleIds: string[],
@MedusaContext() sharedContext: Context = {}
): 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 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: 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
validatePriceListDates(priceListData)
priceListsToCreate.push({
...priceListOnlyData,
rules_count: Object.keys(rules).length,
})
}
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)) {
let ruleType = ruleTypeMap.get(ruleAttribute)!
// Create the rule
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: priceList,
rule_type: ruleType.id,
},
],
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,
rules: priceRules = {},
...moneyAmountData
} = price
const [moneyAmount] = await this.moneyAmountService_.create(
[moneyAmountData],
sharedContext
)
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 ServiceTypes.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 priceLists
}
@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 ruleAttributes: string[] = []
const priceListIds: string[] = []
for (const priceListData of data) {
if (typeof priceListData.rules === "object") {
ruleAttributes.push(...Object.keys(priceListData.rules))
priceListIds.push(priceListData.id)
}
}
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,
},
{ take: null },
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 updatePriceListData: any = {
...priceListOnlyData,
}
validatePriceListDates(updatePriceListData)
if (typeof rules === "object") {
updatePriceListData.rules_count = Object.keys(rules).length
}
const [updatedPriceList] = (await this.priceListService_.update(
[updatePriceListData],
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 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
)
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 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(
[
{
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
})
)
}
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(),
},
{
take: null,
}
)
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,
},
{ take: null }
),
])
const priceListRuleValuesToCreate: unknown[] = []
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 as CreatePriceListRuleValueDTO[]
),
])
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,
}
)
}
}