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 ThamirandPhilip Korsholm authored and GitHub committed 2023-11-15 20:24:29 +00:00
1 parent 2947f57db1
commit 1772e80ed1
58 files changed
+5036 -403

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/pricing": patch
"@medusajs/types": patch
---
feat(pricing,types): price list API + price calculations with price lists
@@ -9,21 +9,20 @@ import {
import { import {
AdminPriceListPricesCreateReq, AdminPriceListPricesCreateReq,
CreatePriceListInput, CreatePriceListInput,
PriceListStatus,
PriceListType,
} from "../../../../types/price-list" } from "../../../../types/price-list"
import { PriceListStatus, PriceListType } from "@medusajs/utils"
import { Type } from "class-transformer" import { Type } from "class-transformer"
import { Request } from "express" import { Request } from "express"
import { EntityManager } from "typeorm" import { EntityManager } from "typeorm"
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing" import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
import { PriceList } from "../../../../models"
import PriceListService from "../../../../services/price-list" import PriceListService from "../../../../services/price-list"
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators" import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
import { import {
defaultAdminPriceListFields, defaultAdminPriceListFields,
defaultAdminPriceListRelations, defaultAdminPriceListRelations,
} from "./index" } from "./index"
import { PriceList } from "../../../../models"
/** /**
* @oas [post] /admin/price-lists * @oas [post] /admin/price-lists
@@ -1,3 +1,4 @@
import { PriceListStatus, PriceListType } from "@medusajs/utils"
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
@@ -7,11 +8,7 @@ import {
ValidateNested, ValidateNested,
} from "class-validator" } from "class-validator"
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "." import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
import { import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
AdminPriceListPricesUpdateReq,
PriceListStatus,
PriceListType,
} from "../../../../types/price-list"
import { Type } from "class-transformer" import { Type } from "class-transformer"
import { EntityManager } from "typeorm" import { EntityManager } from "typeorm"
@@ -250,13 +250,11 @@ export default async (req, res) => {
} }
} }
const isIsolateProductDomain = featureFlagRouter.isFeatureEnabled( const isMedusaV2Enabled = featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)
MedusaV2Flag.key
)
const promises: Promise<any>[] = [] const promises: Promise<any>[] = []
if (isIsolateProductDomain) { if (isMedusaV2Enabled) {
promises.push( promises.push(
listAndCountProductWithIsolatedProductModule( listAndCountProductWithIsolatedProductModule(
req, req,
+5 -5
View File
@@ -1,3 +1,4 @@
import { PriceListStatus, PriceListType } from "@medusajs/utils"
import { import {
BeforeInsert, BeforeInsert,
Column, Column,
@@ -7,14 +8,13 @@ import {
OneToMany, OneToMany,
} from "typeorm" } from "typeorm"
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column" import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
import { PriceListStatus, PriceListType } from "../types/price-list"
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
import { FeatureFlagColumn } from "../utils/feature-flag-decorators"
import { generateEntityId } from "../utils/generate-entity-id"
import { CustomerGroup } from "./customer-group" import { CustomerGroup } from "./customer-group"
import { MoneyAmount } from "./money-amount" import { MoneyAmount } from "./money-amount"
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
import { generateEntityId } from "../utils/generate-entity-id"
import { FeatureFlagColumn } from "../utils/feature-flag-decorators"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
@Entity() @Entity()
export class PriceList extends SoftDeletableEntity { export class PriceList extends SoftDeletableEntity {
+30 -12
View File
@@ -1,9 +1,10 @@
import { import {
CalculatedPriceSetDTO, CalculatedPriceSet,
IPricingModuleService, IPricingModuleService,
PriceSetMoneyAmountDTO, PriceSetMoneyAmountDTO,
RemoteQueryFunction, RemoteQueryFunction,
} from "@medusajs/types" } from "@medusajs/types"
import { import {
FlagRouter, FlagRouter,
MedusaV2Flag, MedusaV2Flag,
@@ -11,6 +12,7 @@ import {
removeNullish, removeNullish,
} from "@medusajs/utils" } from "@medusajs/utils"
import { ProductVariantService, RegionService, TaxProviderService } from "." import { ProductVariantService, RegionService, TaxProviderService } from "."
import { import {
IPriceSelectionStrategy, IPriceSelectionStrategy,
PriceSelectionContext, PriceSelectionContext,
@@ -222,19 +224,19 @@ class PricingService extends TransactionBaseService {
context.price_selection context.price_selection
) )
let priceSets: CalculatedPriceSetDTO[] = [] let calculatedPrices: CalculatedPriceSet[] = []
if (queryContext.currency_code) { if (queryContext.currency_code) {
priceSets = (await this.pricingModuleService.calculatePrices( calculatedPrices = (await this.pricingModuleService.calculatePrices(
{ id: priceSetIds }, { id: priceSetIds },
{ {
context: queryContext as any, context: queryContext as any,
} }
)) as unknown as CalculatedPriceSetDTO[] )) as unknown as CalculatedPriceSet[]
} }
const priceSetMap = new Map<string, CalculatedPriceSetDTO>( const calculatedPriceMap = new Map<string, CalculatedPriceSet>(
priceSets.map((priceSet) => [priceSet.id, priceSet]) calculatedPrices.map((priceSet) => [priceSet.id, priceSet])
) )
const pricingResultMap = new Map() const pricingResultMap = new Map()
@@ -257,14 +259,28 @@ class PricingService extends TransactionBaseService {
} }
if (priceSetId) { if (priceSetId) {
const prices = priceSetMap.get(priceSetId) const calculatedPrice: CalculatedPriceSet | undefined =
calculatedPriceMap.get(priceSetId)
if (prices) { if (calculatedPrice) {
pricingResult.prices = [prices] as MoneyAmount[] pricingResult.prices = [
pricingResult.original_price = prices.amount {
pricingResult.calculated_price = prices.amount id: calculatedPrice.calculated_price?.money_amount_id,
currency_code: calculatedPrice.currency_code,
amount: calculatedPrice.calculated_amount,
min_quantity: calculatedPrice.calculated_price?.min_quantity,
max_quantity: calculatedPrice.calculated_price?.max_quantity,
price_list_id: calculatedPrice.calculated_price?.price_list_id,
},
] as MoneyAmount[]
pricingResult.original_price = calculatedPrice?.original_amount
pricingResult.calculated_price = calculatedPrice?.calculated_amount
pricingResult.calculated_price_type =
calculatedPrice?.calculated_price?.price_list_type
} }
} }
pricingResultMap.set(variantId, pricingResult) pricingResultMap.set(variantId, pricingResult)
}) })
@@ -286,7 +302,7 @@ class PricingService extends TransactionBaseService {
.withTransaction(this.activeManager_) .withTransaction(this.activeManager_)
.calculateVariantPrice(data, context.price_selection) .calculateVariantPrice(data, context.price_selection)
const pricingResultMap = new Map() const pricingResultMap = new Map<string, ProductVariantPricing>()
for (const [variantId, pricing] of variantsPricing.entries()) { for (const [variantId, pricing] of variantsPricing.entries()) {
const pricingResult: ProductVariantPricing = { const pricingResult: ProductVariantPricing = {
@@ -668,6 +684,7 @@ class PricingService extends TransactionBaseService {
{ {
relations: [ relations: [
"money_amount", "money_amount",
"price_list",
"price_set", "price_set",
"price_rules", "price_rules",
"price_rules.rule_type", "price_rules.rule_type",
@@ -691,6 +708,7 @@ class PricingService extends TransactionBaseService {
const moneyAmount = { const moneyAmount = {
...priceSetMoneyAmount.money_amount, ...priceSetMoneyAmount.money_amount,
region_id: null as null | string, region_id: null as null | string,
price_list_id: priceSetMoneyAmount.price_list?.id,
} }
if (regionId) { if (regionId) {
@@ -0,0 +1,12 @@
export const defaultPriceListRuleData = [
{
id: "price-list-rule-1",
price_list: "price-list-1",
rule_type: "rule-type-1",
},
{
id: "price-list-rule-2",
price_list: "price-list-1",
rule_type: "rule-type-2",
},
]
@@ -0,0 +1,22 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRule } from "@models"
import { defaultPriceListRuleData } from "./data"
export * from "./data"
export async function createPriceListRules(
manager: SqlEntityManager,
priceListRuleData: any[] = defaultPriceListRuleData
): Promise<PriceListRule[]> {
const priceListRules: PriceListRule[] = []
for (let data of priceListRuleData) {
const plr = manager.create(PriceListRule, data)
priceListRules.push(plr)
}
await manager.persistAndFlush(priceListRules)
return priceListRules
}
@@ -0,0 +1,14 @@
export const defaultPriceListData = [
{
id: "price-list-1",
title: "Price List 1",
description: "test",
number_rules: 0,
},
{
id: "price-list-2",
title: "Price List 2",
description: "test",
number_rules: 0,
},
]
@@ -0,0 +1,22 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceList } from "@models"
import { defaultPriceListData } from "./data"
export * from "./data"
export async function createPriceLists(
manager: SqlEntityManager,
priceListData: any[] = defaultPriceListData
): Promise<PriceList[]> {
const priceLists: PriceList[] = []
for (let data of priceListData) {
const pl = manager.create(PriceList, data)
priceLists.push(pl)
}
await manager.persistAndFlush(priceLists)
return priceLists
}
@@ -42,20 +42,22 @@ describe("MoneyAmount Service", () => {
it("should list all moneyAmounts", async () => { it("should list all moneyAmounts", async () => {
const moneyAmountsResult = await service.list() const moneyAmountsResult = await service.list()
expect(moneyAmountsResult).toEqual([ expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual(
expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
amount: "500", amount: 500,
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-EUR", id: "money-amount-EUR",
amount: "400", amount: 400,
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-CAD", id: "money-amount-CAD",
amount: "600", amount: 600,
}), }),
]) ])
)
}) })
it("should list moneyAmounts by id", async () => { it("should list moneyAmounts by id", async () => {
@@ -63,7 +65,7 @@ describe("MoneyAmount Service", () => {
id: ["money-amount-USD"], id: ["money-amount-USD"],
}) })
expect(moneyAmountsResult).toEqual([ expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}), }),
@@ -76,7 +78,7 @@ describe("MoneyAmount Service", () => {
id: ["money-amount-USD"], id: ["money-amount-USD"],
}, },
{ {
select: ["id", "min_quantity", "currency.code"], select: ["id", "min_quantity", "currency.code", "amount"],
relations: ["currency"], relations: ["currency"],
} }
) )
@@ -86,6 +88,7 @@ describe("MoneyAmount Service", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: 500,
min_quantity: "1", min_quantity: "1",
currency_code: "USD", currency_code: "USD",
currency: { currency: {
@@ -101,7 +104,7 @@ describe("MoneyAmount Service", () => {
currency_code: ["USD"], currency_code: ["USD"],
}, },
{ {
select: ["id", "min_quantity", "currency.code"], select: ["id", "min_quantity", "currency.code", "amount"],
relations: ["currency"], relations: ["currency"],
} }
) )
@@ -113,6 +116,7 @@ describe("MoneyAmount Service", () => {
id: "money-amount-USD", id: "money-amount-USD",
min_quantity: "1", min_quantity: "1",
currency_code: "USD", currency_code: "USD",
amount: 500,
currency: { currency: {
code: "USD", code: "USD",
}, },
@@ -126,7 +130,8 @@ describe("MoneyAmount Service", () => {
const [moneyAmountsResult, count] = await service.listAndCount() const [moneyAmountsResult, count] = await service.listAndCount()
expect(count).toEqual(3) expect(count).toEqual(3)
expect(moneyAmountsResult).toEqual([ expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual(
expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}), }),
@@ -137,6 +142,7 @@ describe("MoneyAmount Service", () => {
id: "money-amount-CAD", id: "money-amount-CAD",
}), }),
]) ])
)
}) })
it("should return moneyAmounts and count when filtered", async () => { it("should return moneyAmounts and count when filtered", async () => {
@@ -158,7 +164,7 @@ describe("MoneyAmount Service", () => {
id: ["money-amount-USD"], id: ["money-amount-USD"],
}, },
{ {
select: ["id", "min_quantity", "currency.code"], select: ["id", "min_quantity", "currency.code", "amount"],
relations: ["currency"], relations: ["currency"],
} }
) )
@@ -169,6 +175,7 @@ describe("MoneyAmount Service", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: 500,
min_quantity: "1", min_quantity: "1",
currency_code: "USD", currency_code: "USD",
currency: { currency: {
@@ -197,7 +204,7 @@ describe("MoneyAmount Service", () => {
{}, {},
{ {
take: 1, take: 1,
select: ["id"], select: ["id", "amount"],
} }
) )
@@ -207,6 +214,7 @@ describe("MoneyAmount Service", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: 500,
}, },
]) ])
}) })
@@ -214,7 +222,7 @@ describe("MoneyAmount Service", () => {
describe("retrieve", () => { describe("retrieve", () => {
const id = "money-amount-USD" const id = "money-amount-USD"
const amount = "500" const amount = 500
it("should return moneyAmount for the given id", async () => { it("should return moneyAmount for the given id", async () => {
const moneyAmount = await service.retrieve(id) const moneyAmount = await service.retrieve(id)
@@ -291,9 +299,9 @@ describe("MoneyAmount Service", () => {
}, },
]) ])
const moneyAmount = await service.retrieve(id) const moneyAmount = JSON.parse(JSON.stringify(await service.retrieve(id)))
expect(moneyAmount.amount).toEqual("700") expect(moneyAmount.amount).toEqual(700)
}) })
it("should update the currency of the moneyAmount successfully", async () => { it("should update the currency of the moneyAmount successfully", async () => {
@@ -346,11 +354,11 @@ describe("MoneyAmount Service", () => {
id: ["money-amount-TESM"], id: ["money-amount-TESM"],
}) })
expect(moneyAmount).toEqual( expect(JSON.parse(JSON.stringify(moneyAmount))).toEqual(
expect.objectContaining({ expect.objectContaining({
id: "money-amount-TESM", id: "money-amount-TESM",
currency_code: "USD", currency_code: "USD",
amount: "333", amount: 333,
min_quantity: "1", min_quantity: "1",
max_quantity: "4", max_quantity: "4",
}) })
@@ -0,0 +1,243 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRuleRepository } from "@repositories"
import { PriceListRuleService } from "@services"
import { createPriceLists } from "../../../__fixtures__/price-list"
import { createPriceListRules } from "../../../__fixtures__/price-list-rules"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
import { MikroOrmWrapper } from "../../../utils"
jest.setTimeout(30000)
describe("PriceListRule Service", () => {
let service: PriceListRuleService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
const priceListRuleRepository = new PriceListRuleRepository({
manager: repositoryManager,
})
service = new PriceListRuleService({
priceListRuleRepository,
})
testManager = await MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
await createPriceLists(testManager)
await createPriceListRules(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("should list all priceListRules", async () => {
const priceListRuleResult = await service.list()
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should list priceListRules scoped by priceListRule id", async () => {
const priceListRuleResult = await service.list({
id: ["price-list-rule-1"],
})
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
])
})
})
describe("listAndCount", () => {
it("should return pricelistrules and count", async () => {
const [priceListRuleResult, count] = await service.listAndCount()
expect(count).toEqual(2)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should return pricelistrules and count when filtered", async () => {
const [priceListRuleResult, count] = await service.listAndCount({
id: ["price-list-rule-1"],
})
expect(count).toEqual(1)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
])
})
it("should return pricelistrules and count when using skip and take", async () => {
const [priceListRuleResult, count] = await service.listAndCount(
{},
{ skip: 1, take: 1 }
)
expect(count).toEqual(2)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should return requested fields", async () => {
const [priceListRuleResult, count] = await service.listAndCount(
{},
{
take: 1,
select: ["id"],
}
)
const serialized = JSON.parse(JSON.stringify(priceListRuleResult))
expect(count).toEqual(2)
expect(serialized).toEqual([
{
id: "price-list-rule-1",
},
])
})
})
describe("retrieve", () => {
const id = "price-list-rule-1"
it("should return priceList for the given id", async () => {
const priceListRuleResult = await service.retrieve(id)
expect(priceListRuleResult).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when priceListRule with id does not exist", async () => {
let error
try {
await service.retrieve("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"PriceListRule with id: does-not-exist was not found"
)
})
it("should throw an error when a id is not provided", async () => {
let error
try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"priceListRuleId" must be defined')
})
})
describe("delete", () => {
const id = "price-list-rule-1"
it("should delete the pricelists given an id successfully", async () => {
await service.delete([id])
const priceListResult = await service.list({
id: [id],
})
expect(priceListResult).toHaveLength(0)
})
})
describe("update", () => {
const id = "price-list-rule-2"
it("should update the value of the priceListRule successfully", async () => {
await service.update([
{
id,
price_list_id: "price-list-1",
},
])
const priceList = await service.retrieve(id, {
relations: ["price_list"],
})
expect(priceList.price_list.id).toEqual("price-list-1")
})
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.update([
{
id: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'PriceListRule with id(s) "does-not-exist" not found'
)
})
})
describe("create", () => {
it("should create a priceListRule successfully", async () => {
const [created] = await service.create([
{
price_list_id: "price-list-2",
rule_type_id: "rule-type-1",
},
])
const [priceListRule] = await service.list(
{
id: [created.id],
},
{
relations: ["price_list", "rule_type"],
select: ["price_list.id", "rule_type.id"],
}
)
expect(priceListRule.price_list.id).toEqual("price-list-2")
expect(priceListRule.rule_type.id).toEqual("rule-type-1")
})
})
})
@@ -1,37 +1,31 @@
import { SqlEntityManager } from "@mikro-orm/postgresql" import { PriceListRepository } from "@repositories"
import { PriceListService } from "@services"
import { Currency, MoneyAmount } from "@models"
import { MoneyAmountRepository } from "@repositories"
import { MoneyAmountService } from "@services"
import { createCurrencies } from "../../../__fixtures__/currency"
import { createMoneyAmounts } from "../../../__fixtures__/money-amount"
import { MikroOrmWrapper } from "../../../utils" import { MikroOrmWrapper } from "../../../utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createPriceLists } from "../../../__fixtures__/price-list"
jest.setTimeout(30000) jest.setTimeout(30000)
describe("MoneyAmount Service", () => { describe("PriceList Service", () => {
let service: MoneyAmountService let service: PriceListService
let testManager: SqlEntityManager let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager let repositoryManager: SqlEntityManager
let data!: MoneyAmount[]
let currencyData!: Currency[]
beforeEach(async () => { beforeEach(async () => {
await MikroOrmWrapper.setupDatabase() await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager() repositoryManager = await MikroOrmWrapper.forkManager()
const moneyAmountRepository = new MoneyAmountRepository({ const priceListRepository = new PriceListRepository({
manager: repositoryManager, manager: repositoryManager,
}) })
service = new MoneyAmountService({ service = new PriceListService({
moneyAmountRepository, priceListRepository,
}) })
testManager = await MikroOrmWrapper.forkManager() testManager = await MikroOrmWrapper.forkManager()
currencyData = await createCurrencies(testManager) await createPriceLists(testManager)
data = await createMoneyAmounts(testManager)
}) })
afterEach(async () => { afterEach(async () => {
@@ -39,136 +33,76 @@ describe("MoneyAmount Service", () => {
}) })
describe("list", () => { describe("list", () => {
it("list moneyAmounts", async () => { it("should return list priceLists", async () => {
const moneyAmountsResult = await service.list() const priceListResult = await service.list()
expect(moneyAmountsResult).toEqual([ expect(priceListResult).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "price-list-1",
amount: "500",
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-EUR", id: "price-list-2",
amount: "400",
}),
expect.objectContaining({
id: "money-amount-CAD",
amount: "600",
}), }),
]) ])
}) })
it("list moneyAmounts by id", async () => { it("should list pricelists by id", async () => {
const moneyAmountsResult = await service.list({ const priceListResult = await service.list({
id: ["money-amount-USD"], id: ["price-list-1"],
}) })
expect(moneyAmountsResult).toEqual([ expect(priceListResult).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "price-list-1",
}), }),
]) ])
}) })
it("list moneyAmounts with relations and selects", async () => {
const moneyAmountsResult = await service.list(
{
id: ["money-amount-USD"],
},
{
select: ["id", "min_quantity", "currency.code"],
relations: ["currency"],
}
)
const serialized = JSON.parse(JSON.stringify(moneyAmountsResult))
expect(serialized).toEqual([
{
id: "money-amount-USD",
min_quantity: "1",
currency_code: "USD",
currency: {
code: "USD",
},
},
])
})
}) })
describe("listAndCount", () => { describe("listAndCount", () => {
it("should return moneyAmounts and count", async () => { it("should return pricelists and count", async () => {
const [moneyAmountsResult, count] = await service.listAndCount() const [priceListResult, count] = await service.listAndCount()
expect(count).toEqual(3) expect(count).toEqual(2)
expect(moneyAmountsResult).toEqual([ expect(priceListResult).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "price-list-1",
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-EUR", id: "price-list-2",
}),
expect.objectContaining({
id: "money-amount-CAD",
}), }),
]) ])
}) })
it("should return moneyAmounts and count when filtered", async () => { it("should return pricelists and count when filtered", async () => {
const [moneyAmountsResult, count] = await service.listAndCount({ const [priceListResult, count] = await service.listAndCount({
id: ["money-amount-USD"], id: ["price-list-1"],
}) })
expect(count).toEqual(1) expect(count).toEqual(1)
expect(moneyAmountsResult).toEqual([ expect(priceListResult).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "price-list-1",
}), }),
]) ])
}) })
it("list moneyAmounts with relations and selects", async () => { it("should return pricelists and count when using skip and take", async () => {
const [moneyAmountsResult, count] = await service.listAndCount( const [priceListResult, count] = await service.listAndCount(
{
id: ["money-amount-USD"],
},
{
select: ["id", "min_quantity", "currency.code"],
relations: ["currency"],
}
)
const serialized = JSON.parse(JSON.stringify(moneyAmountsResult))
expect(count).toEqual(1)
expect(serialized).toEqual([
{
id: "money-amount-USD",
min_quantity: "1",
currency_code: "USD",
currency: {
code: "USD",
},
},
])
})
it("should return moneyAmounts and count when using skip and take", async () => {
const [moneyAmountsResult, count] = await service.listAndCount(
{}, {},
{ skip: 1, take: 1 } { skip: 1, take: 1 }
) )
expect(count).toEqual(3) expect(count).toEqual(2)
expect(moneyAmountsResult).toEqual([ expect(priceListResult).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-EUR", id: "price-list-2",
}), }),
]) ])
}) })
it("should return requested fields", async () => { it("should return requested fields", async () => {
const [moneyAmountsResult, count] = await service.listAndCount( const [priceListResult, count] = await service.listAndCount(
{}, {},
{ {
take: 1, take: 1,
@@ -176,32 +110,31 @@ describe("MoneyAmount Service", () => {
} }
) )
const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) const serialized = JSON.parse(JSON.stringify(priceListResult))
expect(count).toEqual(3) expect(count).toEqual(2)
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "price-list-1",
}, },
]) ])
}) })
}) })
describe("retrieve", () => { describe("retrieve", () => {
const id = "money-amount-USD" const id = "price-list-1"
const amount = "500"
it("should return moneyAmount for the given id", async () => { it("should return priceList for the given id", async () => {
const moneyAmount = await service.retrieve(id) const priceListResult = await service.retrieve(id)
expect(moneyAmount).toEqual( expect(priceListResult).toEqual(
expect.objectContaining({ expect.objectContaining({
id, id,
}) })
) )
}) })
it("should throw an error when moneyAmount with id does not exist", async () => { it("should throw an error when priceList with id does not exist", async () => {
let error let error
try { try {
@@ -211,7 +144,7 @@ describe("MoneyAmount Service", () => {
} }
expect(error.message).toEqual( expect(error.message).toEqual(
"MoneyAmount with id: does-not-exist was not found" "PriceList with id: does-not-exist was not found"
) )
}) })
@@ -224,65 +157,39 @@ describe("MoneyAmount Service", () => {
error = e error = e
} }
expect(error.message).toEqual('"moneyAmountId" must be defined') expect(error.message).toEqual('"priceListId" must be defined')
})
it("should return moneyAmount based on config select param", async () => {
const moneyAmount = await service.retrieve(id, {
select: ["id", "amount"],
})
const serialized = JSON.parse(JSON.stringify(moneyAmount))
expect(serialized).toEqual({
id,
amount,
})
}) })
}) })
describe("delete", () => { describe("delete", () => {
const id = "money-amount-USD" const id = "price-list-1"
it("should delete the moneyAmounts given an id successfully", async () => { it("should delete the pricelists given an id successfully", async () => {
await service.delete([id]) await service.delete([id])
const moneyAmounts = await service.list({ const priceListResult = await service.list({
id: [id], id: [id],
}) })
expect(moneyAmounts).toHaveLength(0) expect(priceListResult).toHaveLength(0)
}) })
}) })
describe("update", () => { describe("update", () => {
const id = "money-amount-USD" const id = "price-list-2"
it("should update the amount of the moneyAmount successfully", async () => { it("should update the starts_at date of the priceList successfully", async () => {
const updateDate = new Date()
await service.update([ await service.update([
{ {
id, id,
amount: 700, starts_at: updateDate.toISOString(),
}, },
]) ])
const moneyAmount = await service.retrieve(id) const priceList = await service.retrieve(id)
expect(moneyAmount.amount).toEqual("700") expect(priceList.starts_at).toEqual(updateDate)
})
it("should update the currency of the moneyAmount successfully", async () => {
await service.update([
{
id,
currency_code: "EUR",
},
])
const moneyAmount = await service.retrieve(id)
expect(moneyAmount.currency_code).toEqual("EUR")
expect(moneyAmount.currency?.code).toEqual("EUR")
}) })
it("should throw an error when a id does not exist", async () => { it("should throw an error when a id does not exist", async () => {
@@ -292,7 +199,6 @@ describe("MoneyAmount Service", () => {
await service.update([ await service.update([
{ {
id: "does-not-exist", id: "does-not-exist",
amount: 666,
}, },
]) ])
} catch (e) { } catch (e) {
@@ -300,36 +206,25 @@ describe("MoneyAmount Service", () => {
} }
expect(error.message).toEqual( expect(error.message).toEqual(
'MoneyAmount with id "does-not-exist" not found' 'PriceList with id "does-not-exist" not found'
) )
}) })
}) })
describe("create", () => { describe("create", () => {
it("should create a moneyAmount successfully", async () => { it("should create a priceList successfully", async () => {
await service.create([ const [created] = await service.create([
{ {
id: "money-amount-TESM", title: "test",
currency_code: "USD", description: "test",
amount: 333,
min_quantity: 1,
max_quantity: 4,
}, },
]) ])
const [moneyAmount] = await service.list({ const [priceList] = await service.list({
id: ["money-amount-TESM"], id: [created.id],
}) })
expect(moneyAmount).toEqual( expect(priceList.title).toEqual("test")
expect.objectContaining({
id: "money-amount-TESM",
currency_code: "USD",
amount: "333",
min_quantity: "1",
max_quantity: "4",
})
)
}) })
}) })
}) })
@@ -124,9 +124,9 @@ describe("PriceSet Service", () => {
{ {
id: "price-set-1", id: "price-set-1",
money_amounts: [ money_amounts: [
{ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}, }),
], ],
}, },
]) ])
@@ -151,9 +151,9 @@ describe("PriceSet Service", () => {
{ {
id: "price-set-1", id: "price-set-1",
money_amounts: [ money_amounts: [
{ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}, }),
], ],
}, },
]) ])
@@ -227,9 +227,9 @@ describe("PriceSet Service", () => {
{ {
id: "price-set-1", id: "price-set-1",
money_amounts: [ money_amounts: [
{ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}, }),
], ],
}, },
]) ])
@@ -42,20 +42,22 @@ describe("PricingModule Service - MoneyAmount", () => {
it("list moneyAmounts", async () => { it("list moneyAmounts", async () => {
const moneyAmountsResult = await service.listMoneyAmounts() const moneyAmountsResult = await service.listMoneyAmounts()
expect(moneyAmountsResult).toEqual([ expect(moneyAmountsResult).toEqual(
expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
amount: "500", amount: 500,
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-EUR", id: "money-amount-EUR",
amount: "400", amount: 400,
}), }),
expect.objectContaining({ expect.objectContaining({
id: "money-amount-CAD", id: "money-amount-CAD",
amount: "600", amount: 600,
}), }),
]) ])
)
}) })
it("should list moneyAmounts by id", async () => { it("should list moneyAmounts by id", async () => {
@@ -86,6 +88,7 @@ describe("PricingModule Service - MoneyAmount", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: null,
min_quantity: "1", min_quantity: "1",
currency_code: "USD", currency_code: "USD",
currency: { currency: {
@@ -102,7 +105,8 @@ describe("PricingModule Service - MoneyAmount", () => {
await service.listAndCountMoneyAmounts() await service.listAndCountMoneyAmounts()
expect(count).toEqual(3) expect(count).toEqual(3)
expect(moneyAmountsResult).toEqual([ expect(moneyAmountsResult).toEqual(
expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: "money-amount-USD", id: "money-amount-USD",
}), }),
@@ -113,6 +117,7 @@ describe("PricingModule Service - MoneyAmount", () => {
id: "money-amount-CAD", id: "money-amount-CAD",
}), }),
]) ])
)
}) })
it("should return moneyAmounts and count when filtered", async () => { it("should return moneyAmounts and count when filtered", async () => {
@@ -136,7 +141,7 @@ describe("PricingModule Service - MoneyAmount", () => {
id: ["money-amount-USD"], id: ["money-amount-USD"],
}, },
{ {
select: ["id", "min_quantity", "currency.code"], select: ["id", "min_quantity", "currency.code", "amount"],
relations: ["currency"], relations: ["currency"],
} }
) )
@@ -147,6 +152,7 @@ describe("PricingModule Service - MoneyAmount", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: 500,
min_quantity: "1", min_quantity: "1",
currency_code: "USD", currency_code: "USD",
currency: { currency: {
@@ -184,6 +190,7 @@ describe("PricingModule Service - MoneyAmount", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "money-amount-USD", id: "money-amount-USD",
amount: null,
}, },
]) ])
}) })
@@ -191,7 +198,7 @@ describe("PricingModule Service - MoneyAmount", () => {
describe("retrieveMoneyAmount", () => { describe("retrieveMoneyAmount", () => {
const id = "money-amount-USD" const id = "money-amount-USD"
const amount = "500" const amount = 500
it("should return moneyAmount for the given id", async () => { it("should return moneyAmount for the given id", async () => {
const moneyAmount = await service.retrieveMoneyAmount(id) const moneyAmount = await service.retrieveMoneyAmount(id)
@@ -268,9 +275,13 @@ describe("PricingModule Service - MoneyAmount", () => {
}, },
]) ])
const moneyAmount = await service.retrieveMoneyAmount(id) const moneyAmount = JSON.parse(
JSON.stringify(
await service.retrieveMoneyAmount(id, { select: ["amount"] })
)
)
expect(moneyAmount.amount).toEqual("700") expect(moneyAmount.amount).toEqual(700)
}) })
it("should update the currency of the moneyAmount successfully", async () => { it("should update the currency of the moneyAmount successfully", async () => {
@@ -329,7 +340,7 @@ describe("PricingModule Service - MoneyAmount", () => {
expect.objectContaining({ expect.objectContaining({
id: "money-amount-TESM", id: "money-amount-TESM",
currency_code: "USD", currency_code: "USD",
amount: "333", amount: 333,
min_quantity: "1", min_quantity: "1",
max_quantity: "4", max_quantity: "4",
}) })
@@ -0,0 +1,348 @@
import { DB_URL, MikroOrmWrapper } from "../../../utils"
import { IPricingModuleService } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { initialize } from "../../../../src"
import { createPriceLists } from "../../../__fixtures__/price-list"
import { createPriceListRules } from "../../../__fixtures__/price-list-rules"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
jest.setTimeout(30000)
describe("PriceListRule Service", () => {
let service: IPricingModuleService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PRICING_DB_SCHEMA,
},
})
testManager = await MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
await createPriceLists(testManager)
await createPriceListRules(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("should list priceListRules", async () => {
const priceListRuleResult = await service.listPriceListRules()
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should list priceListRules by pricelist id", async () => {
const priceListRuleResult = await service.listPriceListRules({
id: ["price-list-rule-1"],
})
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
])
})
})
describe("listAndCount", () => {
it("should return pricelistrules and count", async () => {
const [priceListRuleResult, count] =
await service.listAndCountPriceListRules()
expect(count).toEqual(2)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should return pricelistrules and count when filtered", async () => {
const [priceListRuleResult, count] =
await service.listAndCountPriceListRules({
id: ["price-list-rule-1"],
})
expect(count).toEqual(1)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-1",
}),
])
})
it("should return pricelistrules and count when using skip and take", async () => {
const [priceListRuleResult, count] =
await service.listAndCountPriceListRules({}, { skip: 1, take: 1 })
expect(count).toEqual(2)
expect(priceListRuleResult).toEqual([
expect.objectContaining({
id: "price-list-rule-2",
}),
])
})
it("should return requested fields", async () => {
const [priceListRuleResult, count] =
await service.listAndCountPriceListRules(
{},
{
take: 1,
select: ["id"],
}
)
const serialized = JSON.parse(JSON.stringify(priceListRuleResult))
expect(count).toEqual(2)
expect(serialized).toEqual([
{
id: "price-list-rule-1",
},
])
})
})
describe("retrieve", () => {
const id = "price-list-rule-1"
it("should return priceList for the given id", async () => {
const priceListRuleResult = await service.retrievePriceListRule(id)
expect(priceListRuleResult).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when priceListRule with id does not exist", async () => {
let error
try {
await service.retrievePriceListRule("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"PriceListRule with id: does-not-exist was not found"
)
})
it("should throw an error when a id is not provided", async () => {
let error
try {
await service.retrievePriceListRule(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"priceListRuleId" must be defined')
})
})
describe("delete", () => {
const id = "price-list-rule-1"
it("should delete the pricelists given an id successfully", async () => {
await service.deletePriceListRules([id])
const priceListResult = await service.listPriceListRules({
id: [id],
})
expect(priceListResult).toHaveLength(0)
})
})
describe("update", () => {
const id = "price-list-rule-2"
it("should update the value of the priceListRule successfully", async () => {
await service.updatePriceListRules([
{
id,
price_list_id: "price-list-2",
rule_type_id: "rule-type-2",
},
])
const priceList = await service.retrievePriceListRule(id, {
relations: ["price_list", "rule_type"],
})
expect(priceList.price_list.id).toEqual("price-list-2")
expect(priceList.rule_type.id).toEqual("rule-type-2")
})
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.updatePriceListRules([
{
id: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'PriceListRule with id(s) "does-not-exist" not found'
)
})
})
describe("create", () => {
it("should create a priceListRule successfully", async () => {
const [created] = await service.createPriceListRules([
{
price_list_id: "price-list-2",
rule_type_id: "rule-type-2",
},
])
const [priceListRule] = await service.listPriceListRules(
{
id: [created.id],
},
{
relations: ["price_list", "rule_type"],
}
)
expect(priceListRule.price_list.id).toEqual("price-list-2")
expect(priceListRule.rule_type.id).toEqual("rule-type-2")
})
})
describe("setPriceListRules", () => {
it("should add a priceListRule to a priceList", async () => {
await createRuleTypes(testManager, [
{
id: "rule-type-3",
name: "test",
rule_attribute: "sales_channel",
},
])
await service.setPriceListRules({
priceListId: "price-list-1",
rules: {
sales_channel: "sc-1",
},
})
const [priceList] = await service.listPriceLists(
{
id: ["price-list-1"],
},
{
relations: [
"price_list_rules",
"price_list_rules.price_list_rule_values",
],
}
)
expect(priceList.price_list_rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
rule_type: "rule-type-3",
price_list_rule_values: [
expect.objectContaining({ value: "sc-1" }),
],
}),
])
)
})
it("should multiple priceListRules to a priceList", async () => {
await createRuleTypes(testManager, [
{
id: "rule-type-3",
name: "test",
rule_attribute: "sales_channel",
},
])
await service.setPriceListRules({
priceListId: "price-list-1",
rules: {
sales_channel: ["sc-1", "sc-2"],
},
})
const [priceList] = await service.listPriceLists(
{
id: ["price-list-1"],
},
{
relations: [
"price_list_rules",
"price_list_rules.price_list_rule_values",
],
}
)
expect(priceList.price_list_rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
rule_type: "rule-type-3",
price_list_rule_values: expect.arrayContaining([
expect.objectContaining({ value: "sc-1" }),
expect.objectContaining({ value: "sc-2" }),
]),
}),
])
)
})
})
describe("removePriceListRules", () => {
it("should remove a priceListRule from a priceList", async () => {
await service.removePriceListRules({
priceListId: "price-list-1",
rules: ["currency_code"],
})
const [priceList] = await service.listPriceLists(
{
id: ["price-list-1"],
},
{
relations: ["price_list_rules"],
}
)
expect(priceList.price_list_rules).toEqual([
expect.objectContaining({ rule_type: "rule-type-2" }),
])
})
})
})
@@ -0,0 +1,397 @@
import { IPricingModuleService } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { initialize } from "../../../../src"
import { createCurrencies } from "../../../__fixtures__/currency"
import { createPriceLists } from "../../../__fixtures__/price-list"
import { createPriceSets } from "../../../__fixtures__/price-set"
import { DB_URL, MikroOrmWrapper } from "../../../utils"
jest.setTimeout(30000)
describe("PriceList Service", () => {
let service: IPricingModuleService
let testManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
await MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PRICING_DB_SCHEMA,
},
})
testManager = await MikroOrmWrapper.forkManager()
await createCurrencies(testManager)
await createPriceSets(testManager)
await createPriceLists(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("list priceLists", async () => {
const priceListResult = await service.listPriceLists()
expect(priceListResult).toEqual([
expect.objectContaining({
id: "price-list-1",
}),
expect.objectContaining({
id: "price-list-2",
}),
])
})
it("list pricelists by id", async () => {
const priceListResult = await service.listPriceLists({
id: ["price-list-1"],
})
expect(priceListResult).toEqual([
expect.objectContaining({
id: "price-list-1",
}),
])
})
})
describe("listAndCount", () => {
it("should return pricelists and count", async () => {
const [priceListResult, count] = await service.listAndCountPriceLists()
expect(count).toEqual(2)
expect(priceListResult).toEqual([
expect.objectContaining({
id: "price-list-1",
}),
expect.objectContaining({
id: "price-list-2",
}),
])
})
it("should return pricelists and count when filtered", async () => {
const [priceListResult, count] = await service.listAndCountPriceLists({
id: ["price-list-1"],
})
expect(count).toEqual(1)
expect(priceListResult).toEqual([
expect.objectContaining({
id: "price-list-1",
}),
])
})
it("should return pricelists and count when using skip and take", async () => {
const [priceListResult, count] = await service.listAndCountPriceLists(
{},
{ skip: 1, take: 1 }
)
expect(count).toEqual(2)
expect(priceListResult).toEqual([
expect.objectContaining({
id: "price-list-2",
}),
])
})
it("should return requested fields", async () => {
const [priceListResult, count] = await service.listAndCountPriceLists(
{},
{
take: 1,
select: ["id"],
}
)
const serialized = JSON.parse(JSON.stringify(priceListResult))
expect(count).toEqual(2)
expect(serialized).toEqual([
{
id: "price-list-1",
},
])
})
})
describe("retrieve", () => {
const id = "price-list-1"
it("should return priceList for the given id", async () => {
const priceListResult = await service.retrievePriceList(id)
expect(priceListResult).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when priceList with id does not exist", async () => {
let error
try {
await service.retrievePriceList("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"PriceList with id: does-not-exist was not found"
)
})
it("should throw an error when a id is not provided", async () => {
let error
try {
await service.retrievePriceList(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"priceListId" must be defined')
})
})
describe("delete", () => {
const id = "price-list-1"
it("should delete the pricelists given an id successfully", async () => {
await service.deletePriceLists([id])
const priceListResult = await service.listPriceLists({
id: [id],
})
expect(priceListResult).toHaveLength(0)
})
})
describe("update", () => {
const id = "price-list-2"
it("should update the starts_at date of the priceList successfully", async () => {
const [created] = await service.createPriceLists([
{
title: "test",
description: "test",
starts_at: "10/01/2023",
ends_at: "10/30/2023",
rules: {
customer_group_id: [
"vip-customer-group-id",
"another-vip-customer-group-id",
],
region_id: ["DE", "DK"],
},
prices: [
{
amount: 400,
currency_code: "EUR",
price_set_id: "price-set-1",
},
],
},
])
const updateDate = new Date()
await service.updatePriceLists([
{
id: created.id,
starts_at: updateDate.toISOString(),
rules: {
new_rule: ["new-rule-value"],
},
},
])
const [priceList] = await service.listPriceLists(
{
id: [created.id],
},
{
relations: [
"price_set_money_amounts.money_amount",
"price_set_money_amounts.price_set",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
],
select: [
"id",
"starts_at",
"price_set_money_amounts.money_amount.amount",
"price_set_money_amounts.money_amount.currency_code",
"price_set_money_amounts.money_amount.price_list_id",
"price_list_rules.price_list_rule_values.value",
"price_list_rules.rule_type.rule_attribute",
],
}
)
expect(priceList).toEqual(
expect.objectContaining({
id: expect.any(String),
starts_at: updateDate,
price_set_money_amounts: expect.arrayContaining([
expect.objectContaining({
price_list: expect.objectContaining({
id: expect.any(String),
}),
money_amount: expect.objectContaining({
amount: 400,
currency_code: "EUR",
}),
}),
]),
price_list_rules: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
rule_type: expect.objectContaining({
id: expect.any(String),
rule_attribute: "new_rule",
}),
price_list_rule_values: [
expect.objectContaining({
id: expect.any(String),
value: "new-rule-value",
}),
],
}),
]),
})
)
})
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.updatePriceLists([
{
id: "does-not-exist",
number_rules: 2,
rules: {},
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'PriceList with id "does-not-exist" not found'
)
})
})
describe("create", () => {
it("should create a priceList successfully", async () => {
const [created] = await service.createPriceLists([
{
title: "test",
description: "test",
starts_at: "10/01/2023",
ends_at: "10/30/2023",
rules: {
customer_group_id: [
"vip-customer-group-id",
"another-vip-customer-group-id",
],
region_id: ["DE", "DK"],
},
prices: [
{
amount: 400,
currency_code: "EUR",
price_set_id: "price-set-1",
},
],
},
])
const [priceList] = await service.listPriceLists(
{
id: [created.id],
},
{
relations: [
"price_set_money_amounts.money_amount",
"price_set_money_amounts.price_set",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
],
select: [
"id",
"price_set_money_amounts.money_amount.amount",
"price_set_money_amounts.money_amount.currency_code",
"price_set_money_amounts.money_amount.price_list_id",
"price_list_rules.price_list_rule_values.value",
"price_list_rules.rule_type.rule_attribute",
],
}
)
expect(priceList).toEqual(
expect.objectContaining({
id: expect.any(String),
price_set_money_amounts: expect.arrayContaining([
expect.objectContaining({
price_list: expect.objectContaining({
id: expect.any(String),
}),
money_amount: expect.objectContaining({
amount: 400,
currency_code: "EUR",
}),
}),
]),
price_list_rules: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
rule_type: expect.objectContaining({
id: expect.any(String),
rule_attribute: "customer_group_id",
}),
price_list_rule_values: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
value: "vip-customer-group-id",
}),
expect.objectContaining({
id: expect.any(String),
value: "another-vip-customer-group-id",
}),
]),
}),
expect.objectContaining({
id: expect.any(String),
rule_type: expect.objectContaining({
id: expect.any(String),
rule_attribute: "region_id",
}),
price_list_rule_values: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
value: "DE",
}),
expect.objectContaining({
id: expect.any(String),
value: "DK",
}),
]),
}),
]),
})
)
})
})
})
@@ -106,7 +106,9 @@ describe("PricingModule Service - PriceSet", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "price-set-1", id: "price-set-1",
money_amounts: [{ id: "money-amount-USD", amount: "500" }], money_amounts: [
expect.objectContaining({ id: "money-amount-USD", amount: 500 }),
],
}, },
]) ])
}) })
@@ -160,7 +162,7 @@ describe("PricingModule Service - PriceSet", () => {
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "price-set-1", id: "price-set-1",
money_amounts: [{ id: "money-amount-USD" }], money_amounts: [expect.objectContaining({ id: "money-amount-USD" })],
}, },
]) ])
}) })
@@ -375,7 +377,7 @@ describe("PricingModule Service - PriceSet", () => {
], ],
money_amounts: [ money_amounts: [
expect.objectContaining({ expect.objectContaining({
amount: "100", amount: 100,
currency_code: "USD", currency_code: "USD",
}), }),
], ],
@@ -413,11 +415,11 @@ describe("PricingModule Service - PriceSet", () => {
], ],
money_amounts: expect.arrayContaining([ money_amounts: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
amount: "100", amount: 100,
currency_code: "USD", currency_code: "USD",
}), }),
expect.objectContaining({ expect.objectContaining({
amount: "150", amount: 150,
currency_code: "USD", currency_code: "USD",
}), }),
]), ]),
@@ -450,7 +452,7 @@ describe("PricingModule Service - PriceSet", () => {
], ],
money_amounts: [ money_amounts: [
expect.objectContaining({ expect.objectContaining({
amount: "100", amount: 100,
currency_code: "USD", currency_code: "USD",
}), }),
], ],
@@ -544,7 +546,7 @@ describe("PricingModule Service - PriceSet", () => {
], ],
money_amounts: [ money_amounts: [
expect.objectContaining({ expect.objectContaining({
amount: "500", amount: 500,
currency_code: "EUR", currency_code: "EUR",
}), }),
], ],
@@ -608,7 +610,7 @@ describe("PricingModule Service - PriceSet", () => {
id: "price-set-1", id: "price-set-1",
money_amounts: expect.arrayContaining([ money_amounts: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
amount: "100", amount: 100,
currency_code: "USD", currency_code: "USD",
}), }),
]), ]),
@@ -650,7 +652,7 @@ describe("PricingModule Service - PriceSet", () => {
id: "price-set-1", id: "price-set-1",
money_amounts: expect.arrayContaining([ money_amounts: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
amount: "100", amount: 100,
currency_code: "USD", currency_code: "USD",
}), }),
]), ]),
@@ -659,7 +661,7 @@ describe("PricingModule Service - PriceSet", () => {
id: "price-set-2", id: "price-set-2",
money_amounts: expect.arrayContaining([ money_amounts: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
amount: "150", amount: 150,
currency_code: "EUR", currency_code: "EUR",
}), }),
]), ]),
+16
View File
@@ -32,6 +32,13 @@ export default async ({
priceSetMoneyAmountService: asClass( priceSetMoneyAmountService: asClass(
defaultServices.PriceSetMoneyAmountService defaultServices.PriceSetMoneyAmountService
).singleton(), ).singleton(),
priceListService: asClass(defaultServices.PriceListService).singleton(),
priceListRuleService: asClass(
defaultServices.PriceListRuleService
).singleton(),
priceListRuleValueService: asClass(
defaultServices.PriceListRuleValueService
).singleton(),
}) })
if (customRepositories) { if (customRepositories) {
@@ -75,5 +82,14 @@ function loadDefaultRepositories({ container }) {
priceSetMoneyAmountRepository: asClass( priceSetMoneyAmountRepository: asClass(
defaultRepositories.PriceSetMoneyAmountRepository defaultRepositories.PriceSetMoneyAmountRepository
).singleton(), ).singleton(),
priceListRepository: asClass(
defaultRepositories.PriceListRepository
).singleton(),
priceListRuleRepository: asClass(
defaultRepositories.PriceListRuleRepository
).singleton(),
priceListRuleValueRepository: asClass(
defaultRepositories.PriceListRuleValueRepository
).singleton(),
}) })
} }
@@ -146,6 +146,151 @@
} }
} }
}, },
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"title": {
"name": "title",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"description": {
"name": "description",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"status": {
"name": "status",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "'draft'",
"enumItems": [
"active",
"draft"
],
"mappedType": "enum"
},
"type": {
"name": "type",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "'sale'",
"enumItems": [
"sale",
"override"
],
"mappedType": "enum"
},
"starts_at": {
"name": "starts_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"ends_at": {
"name": "ends_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"number_rules": {
"name": "number_rules",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "0",
"mappedType": "integer"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"updated_at": {
"name": "updated_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"deleted_at": {
"name": "deleted_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
}
},
"name": "price_list",
"schema": "public",
"indexes": [
{
"columnNames": [
"deleted_at"
],
"composite": false,
"keyName": "IDX_price_list_deleted_at",
"primary": false,
"unique": false
},
{
"keyName": "price_list_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{ {
"columns": { "columns": {
"id": { "id": {
@@ -221,6 +366,15 @@
"nullable": false, "nullable": false,
"default": "0", "default": "0",
"mappedType": "integer" "mappedType": "integer"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
} }
}, },
"name": "price_set_money_amount", "name": "price_set_money_amount",
@@ -244,6 +398,15 @@
"primary": false, "primary": false,
"unique": false "unique": false
}, },
{
"columnNames": [
"price_list_id"
],
"composite": false,
"keyName": "IDX_price_rule_price_list_id",
"primary": false,
"unique": false
},
{ {
"keyName": "price_set_money_amount_pkey", "keyName": "price_set_money_amount_pkey",
"columnNames": [ "columnNames": [
@@ -281,6 +444,19 @@
"referencedTableName": "public.money_amount", "referencedTableName": "public.money_amount",
"deleteRule": "cascade", "deleteRule": "cascade",
"updateRule": "cascade" "updateRule": "cascade"
},
"price_set_money_amount_price_list_id_foreign": {
"constraintName": "price_set_money_amount_price_list_id_foreign",
"columnNames": [
"price_list_id"
],
"localTableName": "public.price_set_money_amount",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list",
"deleteRule": "cascade",
"updateRule": "cascade"
} }
} }
}, },
@@ -604,15 +780,6 @@
"primary": false, "primary": false,
"nullable": false, "nullable": false,
"mappedType": "text" "mappedType": "text"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
} }
}, },
"name": "price_rule", "name": "price_rule",
@@ -696,6 +863,184 @@
"updateRule": "cascade" "updateRule": "cascade"
} }
} }
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"rule_type_id": {
"name": "rule_type_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"priority": {
"name": "priority",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "0",
"mappedType": "integer"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "price_list_rule",
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_type_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_rule_type_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"price_list_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_id",
"primary": false,
"unique": false
},
{
"keyName": "IDX_price_list_rule_rule_type_id_price_list_id_unique",
"columnNames": [
"price_list_id",
"rule_type_id"
],
"composite": true,
"primary": false,
"unique": true
},
{
"keyName": "price_list_rule_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"price_list_rule_rule_type_id_foreign": {
"constraintName": "price_list_rule_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
},
"price_list_rule_price_list_id_foreign": {
"constraintName": "price_list_rule_price_list_id_foreign",
"columnNames": [
"price_list_id"
],
"localTableName": "public.price_list_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list",
"updateRule": "cascade"
}
}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"price_list_rule_id": {
"name": "price_list_rule_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"value": {
"name": "value",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "price_list_rule_value",
"schema": "public",
"indexes": [
{
"columnNames": [
"price_list_rule_id"
],
"composite": false,
"keyName": "IDX_price_list_rule_price_list_rule_value_id",
"primary": false,
"unique": false
},
{
"keyName": "price_list_rule_value_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"price_list_rule_value_price_list_rule_id_foreign": {
"constraintName": "price_list_rule_value_price_list_rule_id_foreign",
"columnNames": [
"price_list_rule_id"
],
"localTableName": "public.price_list_rule_value",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_list_rule",
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
} }
] ]
} }
@@ -101,5 +101,39 @@ export class Migration20230929122253 extends Migration {
this.addSql( this.addSql(
'alter table "price_rule" add constraint "price_rule_price_set_money_amount_id_foreign" foreign key ("price_set_money_amount_id") references "price_set_money_amount" ("id") on update cascade on delete cascade;' 'alter table "price_rule" add constraint "price_rule_price_set_money_amount_id_foreign" foreign key ("price_set_money_amount_id") references "price_set_money_amount" ("id") on update cascade on delete cascade;'
) )
this.addSql(
'create table if not exists "price_list" ("id" text not null, "status" text check ("status" in (\'active\', \'draft\')) not null default \'draft\', "starts_at" timestamptz null, "ends_at" timestamptz null, "number_rules" integer not null default 0, constraint "price_list_pkey" primary key ("id"));'
)
this.addSql(
'create table "price_list_rule" ("id" text not null, "rule_type_id" text not null, "value" text not null, "price_list_id" text not null, constraint "price_list_rule_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_list_rule_rule_type_id" on "price_list_rule" ("rule_type_id");'
)
this.addSql(
'create index "IDX_price_list_rule_price_list_id" on "price_list_rule" ("price_list_id");'
)
this.addSql(
'alter table "price_list_rule" add constraint "price_list_rule_rule_type_id_foreign" foreign key ("rule_type_id") references "rule_type" ("id") on update cascade;'
)
this.addSql(
'alter table "price_list_rule" add constraint "price_list_rule_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade;'
)
this.addSql(
'alter table "price_set_money_amount" add column "price_list_id" text null;'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;'
)
this.addSql(
'create index "IDX_price_rule_price_list_id" on "price_set_money_amount" ("price_list_id");'
)
this.addSql('alter table "price_rule" drop column "price_list_id";')
} }
} }
@@ -0,0 +1,86 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20231101232834 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table "price_list_rule_value" ("id" text not null, "value" text not null, "price_list_rule_id" text not null, constraint "price_list_rule_value_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_list_rule_price_list_rule_value_id" on "price_list_rule_value" ("price_list_rule_id");'
)
this.addSql(
'alter table "price_list_rule_value" add constraint "price_list_rule_value_price_list_rule_id_foreign" foreign key ("price_list_rule_id") references "price_list_rule" ("id") on update cascade on delete cascade;'
)
this.addSql(
`ALTER TABLE price_list ADD COLUMN IF NOT EXISTS number_rules integer not null default 0`
)
this.addSql(
'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\';'
)
this.addSql(
'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete cascade;'
)
this.addSql(
'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\', add column if not exists "created_at" timestamptz not null default now(), add column if not exists "updated_at" timestamptz not null default now(), add column if not exists "deleted_at" timestamptz null;'
)
this.addSql(
'create index if not exists "IDX_price_list_deleted_at" on "price_list" ("deleted_at");'
)
this.addSql(
'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique"'
)
this.addSql(
'alter table "price_list_rule" add constraint "IDX_price_list_rule_rule_type_id_price_list_id_unique" unique ("price_list_id", "rule_type_id");'
)
}
async down(): Promise<void> {
this.addSql('drop table if exists "price_list_rule_value" cascade;')
this.addSql(`ALTER TABLE price_list DROP COLUMN IF EXISTS number_rules`)
this.addSql('alter table "price_list" drop column if exists "title";')
this.addSql('alter table "price_list" drop column if exists "description";')
this.addSql('alter table "price_list" drop column if exists "type";')
this.addSql(
'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;'
)
this.addSql('drop index if exists "IDX_price_list_deleted_at";')
this.addSql('alter table "price_list" drop column if exists "title";')
this.addSql('alter table "price_list" drop column if exists "description";')
this.addSql('alter table "price_list" drop column if exists "type";')
this.addSql('alter table "price_list" drop column if exists "created_at";')
this.addSql('alter table "price_list" drop column if exists "updated_at";')
this.addSql('alter table "price_list" drop column if exists "deleted_at";')
this.addSql(
'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique";'
)
}
}
+3
View File
@@ -1,5 +1,8 @@
export { default as Currency } from "./currency" export { default as Currency } from "./currency"
export { default as MoneyAmount } from "./money-amount" export { default as MoneyAmount } from "./money-amount"
export { default as PriceList } from "./price-list"
export { default as PriceListRule } from "./price-list-rule"
export { default as PriceListRuleValue } from "./price-list-rule-value"
export { default as PriceRule } from "./price-rule" export { default as PriceRule } from "./price-rule"
export { default as PriceSet } from "./price-set" export { default as PriceSet } from "./price-set"
export { default as PriceSetMoneyAmount } from "./price-set-money-amount" export { default as PriceSetMoneyAmount } from "./price-set-money-amount"
+36 -1
View File
@@ -3,17 +3,27 @@ import {
BeforeCreate, BeforeCreate,
Collection, Collection,
Entity, Entity,
Index,
ManyToMany, ManyToMany,
ManyToOne, ManyToOne,
OneToOne,
OptionalProps,
PrimaryKey, PrimaryKey,
Property, Property,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import Currency from "./currency" import Currency from "./currency"
import { PriceSetMoneyAmount } from "./index"
import PriceSet from "./price-set" import PriceSet from "./price-set"
@Entity() @Entity()
class MoneyAmount { class MoneyAmount {
[OptionalProps]?:
| "created_at"
| "updated_at"
| "deleted_at"
| "price_set_money_amount"
@PrimaryKey({ columnType: "text" }) @PrimaryKey({ columnType: "text" })
id!: string id!: string
@@ -26,6 +36,12 @@ class MoneyAmount {
}) })
price_sets = new Collection<PriceSet>(this) price_sets = new Collection<PriceSet>(this)
@OneToOne({
entity: () => PriceSetMoneyAmount,
mappedBy: (psma) => psma.money_amount,
})
price_set_money_amount: PriceSetMoneyAmount
@ManyToOne(() => Currency, { @ManyToOne(() => Currency, {
nullable: true, nullable: true,
index: "IDX_money_amount_currency_code", index: "IDX_money_amount_currency_code",
@@ -33,7 +49,7 @@ class MoneyAmount {
}) })
currency?: Currency currency?: Currency
@Property({ columnType: "numeric", nullable: true }) @Property({ columnType: "numeric", nullable: true, serializer: Number })
amount?: number amount?: number
@Property({ columnType: "numeric", nullable: true }) @Property({ columnType: "numeric", nullable: true })
@@ -42,6 +58,25 @@ class MoneyAmount {
@Property({ columnType: "numeric", nullable: true }) @Property({ columnType: "numeric", nullable: true })
max_quantity?: number | null max_quantity?: number | null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Index({ name: "IDX_money_amount_deleted_at" })
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date
@BeforeCreate() @BeforeCreate()
onCreate() { onCreate() {
this.id = generateEntityId(this.id, "ma") this.id = generateEntityId(this.id, "ma")
@@ -0,0 +1,31 @@
import {
BeforeCreate,
Entity,
ManyToOne,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import PriceListRule from "./price-list-rule"
import { generateEntityId } from "@medusajs/utils"
@Entity()
export default class PriceListRuleValue {
@PrimaryKey({ columnType: "text" })
id!: string
@ManyToOne(() => PriceListRule, {
onDelete: "cascade",
fieldName: 'price_list_rule_id',
index: "IDX_price_list_rule_price_list_rule_value_id",
})
price_list_rule: PriceListRule
@Property({ columnType: "text" })
value: string
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "plrv")
}
}
@@ -0,0 +1,57 @@
import {
BeforeCreate,
Cascade,
Collection,
Entity,
ManyToOne,
OneToMany,
OptionalProps,
PrimaryKey,
Unique,
} from "@mikro-orm/core"
import { generateEntityId } from "@medusajs/utils"
import PriceList from "./price-list"
import PriceListRuleValue from "./price-list-rule-value"
import RuleType from "./rule-type"
type OptionalFields = "id"
type OptionalRelations = "rule_type" | "price_list"
@Entity()
@Unique({
name: "IDX_price_list_rule_rule_type_id_price_list_id_unique",
properties: ["price_list", "rule_type"],
})
export default class PriceListRule {
[OptionalProps]: OptionalFields | OptionalRelations
@PrimaryKey({ columnType: "text" })
id!: string
@ManyToOne({
entity: () => RuleType,
fieldName: "rule_type_id",
name: "price_rule_rule_type_id_unique",
index: "IDX_price_list_rule_rule_type_id",
})
rule_type: RuleType
@OneToMany(() => PriceListRuleValue, (plrv) => plrv.price_list_rule, {
cascade: [Cascade.REMOVE],
})
price_list_rule_values = new Collection<PriceListRuleValue>(this)
@ManyToOne({
entity: () => PriceList,
fieldName: "price_list_id",
name: "price_rule_price_list_id",
index: "IDX_price_list_rule_price_list_id",
})
price_list: PriceList
@BeforeCreate()
beforeCreate() {
this.id = generateEntityId(this.id, "plrule")
}
}
+110
View File
@@ -0,0 +1,110 @@
import { generateEntityId } from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Enum,
Index,
ManyToMany,
OneToMany,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { PriceListStatus, PriceListType } from "@medusajs/utils"
import PriceListRule from "./price-list-rule"
import PriceSetMoneyAmount from "./price-set-money-amount"
import RuleType from "./rule-type"
type OptionalFields =
| "status"
| "type"
| "number_rules"
| "starts_at"
| "ends_at"
| "created_at"
| "updated_at"
| "deleted_at"
type OptionalRelations =
| "price_set_money_amounts"
| "rule_types"
| "price_list_rules"
@Entity()
export default class PriceList {
[OptionalProps]: OptionalFields | OptionalRelations
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text" })
title: string
@Property({ columnType: "text" })
description: string
@Enum({ items: () => PriceListStatus, default: PriceListStatus.DRAFT })
status?: PriceListStatus
@Enum({ items: () => PriceListType, default: PriceListType.SALE })
type?: PriceListType
@Property({
columnType: "timestamptz",
nullable: true,
})
starts_at?: Date | null
@Property({
columnType: "timestamptz",
nullable: true,
})
ends_at?: Date | null
@OneToMany(() => PriceSetMoneyAmount, (psma) => psma.price_list, {
cascade: [Cascade.REMOVE],
})
price_set_money_amounts = new Collection<PriceSetMoneyAmount>(this)
@OneToMany(() => PriceListRule, (pr) => pr.price_list, {
cascade: [Cascade.REMOVE],
})
price_list_rules = new Collection<PriceListRule>(this)
@ManyToMany({
entity: () => RuleType,
pivotEntity: () => PriceListRule,
cascade: [Cascade.REMOVE],
})
rule_types = new Collection<RuleType>(this)
@Property({ columnType: "integer", default: 0 })
number_rules?: number
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at?: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at?: Date
@Index({ name: "IDX_price_list_deleted_at" })
@Property({ columnType: "timestamptz", nullable: true })
deleted_at?: Date
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "plist")
}
}
@@ -57,11 +57,6 @@ export default class PriceRule {
}) })
price_set_money_amount: PriceSetMoneyAmount price_set_money_amount: PriceSetMoneyAmount
@Property({ columnType: "text" })
price_list_id!: string
// TODO: Add price list
@BeforeCreate() @BeforeCreate()
beforeCreate() { beforeCreate() {
this.id = generateEntityId(this.id, "prule") this.id = generateEntityId(this.id, "prule")
@@ -5,12 +5,14 @@ import {
Entity, Entity,
ManyToOne, ManyToOne,
OneToMany, OneToMany,
OneToOne,
PrimaryKey, PrimaryKey,
PrimaryKeyType, PrimaryKeyType,
Property, Property,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import MoneyAmount from "./money-amount" import MoneyAmount from "./money-amount"
import PriceList from "./price-list"
import PriceRule from "./price-rule" import PriceRule from "./price-rule"
import PriceSet from "./price-set" import PriceSet from "./price-set"
import PriceSetMoneyAmountRules from "./price-set-money-amount-rules" import PriceSetMoneyAmountRules from "./price-set-money-amount-rules"
@@ -29,7 +31,7 @@ export default class PriceSetMoneyAmount {
}) })
price_set?: PriceSet price_set?: PriceSet
@ManyToOne(() => MoneyAmount, { @OneToOne(() => MoneyAmount, {
onDelete: "cascade", onDelete: "cascade",
index: "IDX_price_set_money_amount_money_amount_id", index: "IDX_price_set_money_amount_money_amount_id",
}) })
@@ -50,6 +52,13 @@ export default class PriceSetMoneyAmount {
}) })
price_set_money_amount_rules = new Collection<PriceSetMoneyAmountRules>(this) price_set_money_amount_rules = new Collection<PriceSetMoneyAmountRules>(this)
@ManyToOne(() => PriceList, {
index: "IDX_price_rule_price_list_id",
onDelete: "cascade",
nullable: true,
})
price_list?: PriceList
@BeforeCreate() @BeforeCreate()
onCreate() { onCreate() {
this.id = generateEntityId(this.id, "psma") this.id = generateEntityId(this.id, "psma")
+1 -1
View File
@@ -18,7 +18,7 @@ import RuleType from "./rule-type"
@Entity() @Entity()
export default class PriceSet { export default class PriceSet {
[OptionalProps]?: "price_set_money_amounts" | "rule_types" [OptionalProps]?: "price_set_money_amounts" | "rule_types" | "money_amounts"
@PrimaryKey({ columnType: "text" }) @PrimaryKey({ columnType: "text" })
id!: string id!: string
@@ -1,6 +1,9 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
export { CurrencyRepository } from "./currency" export { CurrencyRepository } from "./currency"
export { MoneyAmountRepository } from "./money-amount" export { MoneyAmountRepository } from "./money-amount"
export { PriceListRepository } from "./price-list"
export { PriceListRuleRepository } from "./price-list-rule"
export { PriceListRuleValueRepository } from "./price-list-rule-value"
export { PriceRuleRepository } from "./price-rule" export { PriceRuleRepository } from "./price-rule"
export { PriceSetRepository } from "./price-set" export { PriceSetRepository } from "./price-set"
export { PriceSetMoneyAmountRepository } from "./price-set-money-amount" export { PriceSetMoneyAmountRepository } from "./price-set-money-amount"
@@ -0,0 +1,143 @@
import { Context, DAL } from "@medusajs/types"
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRuleValue } from "@models"
import {
CreatePriceListRuleValueDTO,
UpdatePriceListRuleValueDTO,
} from "../types"
export class PriceListRuleValueRepository extends DALUtils.MikroOrmBaseRepository {
protected readonly manager_: SqlEntityManager
constructor({ manager }: { manager: SqlEntityManager }) {
// @ts-ignore
// eslint-disable-next-line prefer-rest-params
super(...arguments)
this.manager_ = manager
}
async find(
findOptions: DAL.FindOptions<PriceListRuleValue> = { where: {} },
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceListRuleValue,
findOptions_.where as MikroFilterQuery<PriceListRuleValue>,
findOptions_.options as MikroOptions<PriceListRuleValue>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceListRuleValue> = { where: {} },
context: Context = {}
): Promise<[PriceListRuleValue[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceListRuleValue,
findOptions_.where as MikroFilterQuery<PriceListRuleValue>,
findOptions_.options as MikroOptions<PriceListRuleValue>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceListRuleValue, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListRuleValueDTO[],
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListRuleValues = data.map((priceRuleValueData) => {
const { price_list_rule_id: priceListRuleId, ...priceRuleValue } =
priceRuleValueData
if (priceListRuleId) {
priceRuleValue.price_list_rule = priceListRuleId
}
return manager.create(PriceListRuleValue, priceRuleValue)
})
manager.persist(priceListRuleValues)
return priceListRuleValues
}
async update(
data: UpdatePriceListRuleValueDTO[],
context: Context = {}
): Promise<PriceListRuleValue[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceValueIds = data.map(
(priceRuleValueData) => priceRuleValueData.id
)
const existingPriceValues = await this.find(
{
where: {
id: {
$in: priceValueIds,
},
},
},
context
)
const dataAndExistingIdDifference = arrayDifference(
data.map((d) => d.id),
existingPriceValues.map((pv) => pv.id)
)
if (dataAndExistingIdDifference.length) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceListRuleValue with id(s) "${dataAndExistingIdDifference.join(
", "
)}" not found`
)
}
const existingValuesMap = new Map(
existingPriceValues.map<[string, PriceListRuleValue]>((priceValue) => [
priceValue.id,
priceValue,
])
)
const priceValues = data.map((priceRuleValueData) => {
const existingPriceValue = existingValuesMap.get(priceRuleValueData.id)!
return manager.assign(existingPriceValue, priceRuleValueData)
})
manager.persist(priceValues)
return priceValues
}
}
@@ -0,0 +1,161 @@
import {
Context,
CreatePriceListRuleDTO,
DAL,
UpdatePriceListRuleDTO,
} from "@medusajs/types"
import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceListRule } from "@models"
export class PriceListRuleRepository extends DALUtils.MikroOrmBaseRepository {
protected readonly manager_: SqlEntityManager
constructor({ manager }: { manager: SqlEntityManager }) {
// @ts-ignore
// eslint-disable-next-line prefer-rest-params
super(...arguments)
this.manager_ = manager
}
async find(
findOptions: DAL.FindOptions<PriceListRule> = { where: {} },
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceListRule,
findOptions_.where as MikroFilterQuery<PriceListRule>,
findOptions_.options as MikroOptions<PriceListRule>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceListRule> = { where: {} },
context: Context = {}
): Promise<[PriceListRule[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceListRule,
findOptions_.where as MikroFilterQuery<PriceListRule>,
findOptions_.options as MikroOptions<PriceListRule>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceListRule, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListRuleDTO[],
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListRule = data.map((priceListRule) => {
const {
price_list_id: priceListId,
rule_type_id: ruleTypeId,
...createData
} = priceListRule
if (priceListId) {
createData.price_list = priceListId
}
if (ruleTypeId) {
createData.rule_type = ruleTypeId
}
return manager.create(PriceListRule, createData)
})
manager.persist(priceListRule)
return priceListRule
}
async update(
data: UpdatePriceListRuleDTO[],
context: Context = {}
): Promise<PriceListRule[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListIds = data.map((priceListRule) => priceListRule.id)
const existingPriceListRules = await this.find(
{
where: {
id: {
$in: priceListIds,
},
},
},
context
)
const dataAndExistingIdDifference = arrayDifference(
data.map((d) => d.id),
existingPriceListRules.map((plr) => plr.id)
)
if (dataAndExistingIdDifference.length) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceListRule with id(s) "${dataAndExistingIdDifference.join(
", "
)}" not found`
)
}
const existingPriceListRuleMap = new Map(
existingPriceListRules.map<[string, PriceListRule]>((priceList) => [
priceList.id,
priceList,
])
)
const priceListRules = data.map((priceListRule) => {
const { price_list_id, rule_type_id, ...priceListRuleData } =
priceListRule
const existingPriceListRule = existingPriceListRuleMap.get(
priceListRule.id
)!
if (price_list_id) {
priceListRuleData.price_list = price_list_id
}
if (rule_type_id) {
priceListRuleData.rule_type = rule_type_id
}
return manager.assign(existingPriceListRule, priceListRuleData)
})
manager.persist(priceListRules)
return priceListRules
}
}
@@ -0,0 +1,133 @@
import { Context, DAL, UpdatePriceListDTO } from "@medusajs/types"
import { DALUtils, MedusaError } from "@medusajs/utils"
import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { PriceList } from "@models"
import { CreatePriceListDTO } from "../types"
export class PriceListRepository extends DALUtils.MikroOrmBaseRepository {
protected readonly manager_: SqlEntityManager
constructor({ manager }: { manager: SqlEntityManager }) {
// @ts-ignore
// eslint-disable-next-line prefer-rest-params
super(...arguments)
this.manager_ = manager
}
async find(
findOptions: DAL.FindOptions<PriceList> = { where: {} },
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
PriceList,
findOptions_.where as MikroFilterQuery<PriceList>,
findOptions_.options as MikroOptions<PriceList>
)
}
async findAndCount(
findOptions: DAL.FindOptions<PriceList> = { where: {} },
context: Context = {}
): Promise<[PriceList[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
PriceList,
findOptions_.where as MikroFilterQuery<PriceList>,
findOptions_.options as MikroOptions<PriceList>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(PriceList, { id: { $in: ids } }, {})
}
async create(
data: CreatePriceListDTO[],
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceLists = data.map((priceListData) => {
return manager.create(PriceList, priceListData)
})
manager.persist(priceLists)
return priceLists
}
async update(
data: Omit<UpdatePriceListDTO, "rules">[],
context: Context = {}
): Promise<PriceList[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const priceListIds = data.map((priceListData) => priceListData.id)
const existingPriceLists = await this.find(
{
where: {
id: {
$in: priceListIds,
},
},
},
context
)
const existingPriceListMap = new Map(
existingPriceLists.map<[string, PriceList]>((priceList) => [
priceList.id,
priceList,
])
)
const priceLists = data.map((priceListData) => {
const existingPriceList = existingPriceListMap.get(priceListData.id)
if (!existingPriceList) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`PriceList with id "${priceListData.id}" not found`
)
}
if (!!priceListData.starts_at) {
priceListData.starts_at = new Date(
priceListData.starts_at
).toISOString()
}
if (!!priceListData.ends_at) {
priceListData.ends_at = new Date(priceListData.ends_at).toISOString()
}
return manager.assign(existingPriceList, priceListData)
})
manager.persist(priceLists)
return priceLists
}
}
@@ -74,13 +74,16 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito
): Promise<PriceSetMoneyAmount[]> { ): Promise<PriceSetMoneyAmount[]> {
const manager = this.getActiveManager<SqlEntityManager>(context) const manager = this.getActiveManager<SqlEntityManager>(context)
const currencies = data.map((currencyData) => { const psma = data.map((psmaData) => {
return manager.create(PriceSetMoneyAmount, currencyData as PriceSetMoneyAmount) return manager.create(
PriceSetMoneyAmount,
psmaData as unknown as PriceSetMoneyAmount
)
}) })
manager.persist(currencies) manager.persist(psma)
return currencies return psma
} }
async update( async update(
@@ -101,23 +104,22 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito
) )
const existingPSMAMap = new Map( const existingPSMAMap = new Map(
existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>((psma) => [ existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>(
psma.id, (psma) => [psma.id, psma]
psma, )
])
) )
const priceSetMoneyAmounts = data.map((currencyData) => { const priceSetMoneyAmounts = data.map((psmaData) => {
const existingPSMA = existingPSMAMap.get(currencyData.id) const existingPSMA = existingPSMAMap.get(psmaData.id)
if (!existingPSMA) { if (!existingPSMA) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.NOT_FOUND, MedusaError.Types.NOT_FOUND,
`PriceSetMoneyAmount with id "${currencyData.id}" not found` `PriceSetMoneyAmount with id "${psmaData.id}" not found`
) )
} }
return manager.assign(existingPSMA, currencyData) return manager.assign(existingPSMA, psmaData)
}) })
manager.persist(priceSetMoneyAmounts) manager.persist(priceSetMoneyAmounts)
@@ -1,3 +1,4 @@
import { UpdatePriceListDTO } from "@medusajs/types"
import { import {
Context, Context,
DAL, DAL,
@@ -84,7 +85,7 @@ export class PriceSetRepository extends DALUtils.MikroOrmBaseRepository {
} }
async update( async update(
data: UpdatePriceSetDTO[], data: Omit<UpdatePriceListDTO, "rules">[],
context: Context = {} context: Context = {}
): Promise<PriceSet[]> { ): Promise<PriceSet[]> {
const manager = this.getActiveManager<SqlEntityManager>(context) const manager = this.getActiveManager<SqlEntityManager>(context)
+66 -12
View File
@@ -57,43 +57,96 @@ export class PricingRepository
return [] return []
} }
const date = new Date().toISOString()
// Gets all the price set money amounts where rules match for each of the contexts // Gets all the price set money amounts where rules match for each of the contexts
// that the price set is configured for // that the price set is configured for
const psmaSubQueryKnex = knex({ const psmaSubQueryKnex = knex({
psma: "price_set_money_amount", psma: "price_set_money_amount",
}) })
.select({ .select({
id: "psma.id", id: "psma1.id",
price_set_id: "psma.price_set_id", price_set_id: "psma1.price_set_id",
money_amount_id: "psma.money_amount_id", money_amount_id: "psma1.money_amount_id",
number_rules: "psma.number_rules", number_rules: "psma1.number_rules",
price_list_id: "psma1.price_list_id",
pl_number_rules: "pl.number_rules",
pl_type: "pl.type",
}) })
.leftJoin("price_set_money_amount as psma1", "psma1.id", "psma.id") .leftJoin("price_set_money_amount as psma1", "psma1.id", "psma1.id")
.leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma.id") .leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma1.id")
.leftJoin("price_list as pl", "pl.id", "psma1.price_list_id")
.leftJoin("price_list_rule as plr", "plr.price_list_id", "pl.id")
.leftJoin(
"price_list_rule_value as plrv",
"plrv.price_list_rule_id",
"plr.id"
)
.leftJoin("rule_type as plrt", "plrt.id", "plr.rule_type_id")
.leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id") .leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id")
.orderBy("pl.number_rules", "desc")
.orderBy("number_rules", "desc") .orderBy("number_rules", "desc")
.orWhere("psma1.number_rules", "=", 0) .orderBy([
.groupBy("psma.id") { column: "number_rules", order: "desc" },
.having(knex.raw("count(DISTINCT rt.rule_attribute) = psma.number_rules")) { column: "pl.number_rules", order: "desc" },
])
.groupBy("psma1.id", "pl.id")
.having(
knex.raw(
"count(DISTINCT rt.rule_attribute) = psma1.number_rules AND psma1.price_list_id IS NULL"
)
)
.orHaving(
knex.raw(
"count(DISTINCT plrt.rule_attribute) = pl.number_rules AND psma1.price_list_id IS NOT NULL"
)
)
psmaSubQueryKnex.orWhere((q) => {
for (const [key, value] of Object.entries(context)) { for (const [key, value] of Object.entries(context)) {
psmaSubQueryKnex.orWhere({ q.orWhere({
"rt.rule_attribute": key, "rt.rule_attribute": key,
"pr.value": value, "pr.value": value,
}) })
} }
q.orWhere("psma1.number_rules", "=", 0)
q.whereNull("psma1.price_list_id")
})
psmaSubQueryKnex.orWhere((q) => {
q.whereNotNull("psma1.price_list_id")
.andWhere(function () {
this.whereNull("pl.starts_at").orWhere("pl.starts_at", "<=", date)
})
.andWhere(function () {
this.whereNull("pl.ends_at").orWhere("pl.ends_at", ">=", date)
})
.andWhere(function () {
for (const [key, value] of Object.entries(context)) {
this.orWhere({
"plrt.rule_attribute": key,
})
this.whereIn("plrv.value", [value])
}
this.orWhere("pl.number_rules", "=", 0)
})
})
const priceSetQueryKnex = knex({ const priceSetQueryKnex = knex({
ps: "price_set", ps: "price_set",
}) })
.select({ .select({
id: "ps.id", id: "ma.id",
price_set_id: "ps.id",
amount: "ma.amount", amount: "ma.amount",
min_quantity: "ma.min_quantity", min_quantity: "ma.min_quantity",
max_quantity: "ma.max_quantity", max_quantity: "ma.max_quantity",
currency_code: "ma.currency_code", currency_code: "ma.currency_code",
default_priority: "rt.default_priority", default_priority: "rt.default_priority",
number_rules: "psma.number_rules", number_rules: "psma.number_rules",
pl_number_rules: "psma.pl_number_rules",
price_list_type: "psma.pl_type",
price_list_id: "psma.price_list_id",
}) })
.join(psmaSubQueryKnex.as("psma"), "psma.price_set_id", "ps.id") .join(psmaSubQueryKnex.as("psma"), "psma.price_set_id", "ps.id")
.join("money_amount as ma", "ma.id", "psma.money_amount_id") .join("money_amount as ma", "ma.id", "psma.money_amount_id")
@@ -102,6 +155,7 @@ export class PricingRepository
.whereIn("ps.id", pricingFilters.id) .whereIn("ps.id", pricingFilters.id)
.andWhere("ma.currency_code", "=", currencyCode) .andWhere("ma.currency_code", "=", currencyCode)
.orderBy([ .orderBy([
{ column: "price_list_id", order: "asc" },
{ column: "number_rules", order: "desc" }, { column: "number_rules", order: "desc" },
{ column: "default_priority", order: "desc" }, { column: "default_priority", order: "desc" },
]) ])
+3
View File
@@ -1,5 +1,8 @@
export { default as CurrencyService } from "./currency" export { default as CurrencyService } from "./currency"
export { default as MoneyAmountService } from "./money-amount" 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 PriceRuleService } from "./price-rule"
export { default as PriceSetService } from "./price-set" export { default as PriceSetService } from "./price-set"
export { default as PriceSetMoneyAmountService } from "./price-set-money-amount" 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, AddPricesDTO,
Context, Context,
CreateMoneyAmountDTO, CreateMoneyAmountDTO,
CreatePriceListRuleDTO,
DAL, DAL,
FindConfig, FindConfig,
InternalModuleDeclaration, InternalModuleDeclaration,
@@ -12,10 +13,14 @@ import {
PricingTypes, PricingTypes,
RuleTypeDTO, RuleTypeDTO,
} from "@medusajs/types" } from "@medusajs/types"
import { PriceListType } from "@medusajs/utils"
import { import {
Currency, Currency,
MoneyAmount, MoneyAmount,
PriceList,
PriceListRule,
PriceListRuleValue,
PriceRule, PriceRule,
PriceSet, PriceSet,
PriceSetMoneyAmount, PriceSetMoneyAmount,
@@ -27,6 +32,9 @@ import {
import { import {
CurrencyService, CurrencyService,
MoneyAmountService, MoneyAmountService,
PriceListRuleService,
PriceListRuleValueService,
PriceListService,
PriceRuleService, PriceRuleService,
PriceSetMoneyAmountRulesService, PriceSetMoneyAmountRulesService,
PriceSetMoneyAmountService, PriceSetMoneyAmountService,
@@ -44,7 +52,7 @@ import {
removeNullish, removeNullish,
} from "@medusajs/utils" } from "@medusajs/utils"
import { joinerConfig } from "../joiner-config" import { joinerConfig } from "../joiner-config"
import { PricingRepositoryService } from "../types" import { CreatePriceListRuleValueDTO, PricingRepositoryService } from "../types"
type InjectedDependencies = { type InjectedDependencies = {
baseRepository: DAL.RepositoryService baseRepository: DAL.RepositoryService
@@ -57,6 +65,9 @@ type InjectedDependencies = {
priceRuleService: PriceRuleService<any> priceRuleService: PriceRuleService<any>
priceSetRuleTypeService: PriceSetRuleTypeService<any> priceSetRuleTypeService: PriceSetRuleTypeService<any>
priceSetMoneyAmountService: PriceSetMoneyAmountService<any> priceSetMoneyAmountService: PriceSetMoneyAmountService<any>
priceListService: PriceListService<any>
priceListRuleService: PriceListRuleService<any>
priceListRuleValueService: PriceListRuleValueService<any>
} }
export default class PricingModuleService< export default class PricingModuleService<
@@ -67,7 +78,10 @@ export default class PricingModuleService<
TPriceSetMoneyAmountRules extends PriceSetMoneyAmountRules = PriceSetMoneyAmountRules, TPriceSetMoneyAmountRules extends PriceSetMoneyAmountRules = PriceSetMoneyAmountRules,
TPriceRule extends PriceRule = PriceRule, TPriceRule extends PriceRule = PriceRule,
TPriceSetRuleType extends PriceSetRuleType = PriceSetRuleType, 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 > implements PricingTypes.IPricingModuleService
{ {
protected baseRepository_: DAL.RepositoryService protected baseRepository_: DAL.RepositoryService
@@ -80,6 +94,9 @@ export default class PricingModuleService<
protected readonly priceRuleService_: PriceRuleService<TPriceRule> protected readonly priceRuleService_: PriceRuleService<TPriceRule>
protected readonly priceSetRuleTypeService_: PriceSetRuleTypeService<TPriceSetRuleType> protected readonly priceSetRuleTypeService_: PriceSetRuleTypeService<TPriceSetRuleType>
protected readonly priceSetMoneyAmountService_: PriceSetMoneyAmountService<TPriceSetMoneyAmount> protected readonly priceSetMoneyAmountService_: PriceSetMoneyAmountService<TPriceSetMoneyAmount>
protected readonly priceListService_: PriceListService<TPriceList>
protected readonly priceListRuleService_: PriceListRuleService<TPriceListRule>
protected readonly priceListRuleValueService_: PriceListRuleValueService<TPriceListRuleValue>
constructor( constructor(
{ {
@@ -93,6 +110,9 @@ export default class PricingModuleService<
priceRuleService, priceRuleService,
priceSetRuleTypeService, priceSetRuleTypeService,
priceSetMoneyAmountService, priceSetMoneyAmountService,
priceListService,
priceListRuleService,
priceListRuleValueService,
}: InjectedDependencies, }: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration protected readonly moduleDeclaration: InternalModuleDeclaration
) { ) {
@@ -107,6 +127,9 @@ export default class PricingModuleService<
this.priceRuleService_ = priceRuleService this.priceRuleService_ = priceRuleService
this.priceSetRuleTypeService_ = priceSetRuleTypeService this.priceSetRuleTypeService_ = priceSetRuleTypeService
this.priceSetMoneyAmountService_ = priceSetMoneyAmountService this.priceSetMoneyAmountService_ = priceSetMoneyAmountService
this.priceListService_ = priceListService
this.priceListRuleService_ = priceListRuleService
this.priceListRuleValueService_ = priceListRuleValueService
} }
__joinerConfig(): ModuleJoinerConfig { __joinerConfig(): ModuleJoinerConfig {
@@ -124,21 +147,53 @@ export default class PricingModuleService<
pricingContext, pricingContext,
sharedContext sharedContext
) )
const pricesSetPricesMap = groupBy(results, "id") const pricesSetPricesMap = groupBy(results, "price_set_id")
const calculatedPrices = pricingFilters.id.map( 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 // 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 number_rules first for exact match and then deafult_priority of the rule_type
// inject custom price selection here // 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 { return {
id: priceSetId, id: priceSetId,
amount: price?.amount || null, is_calculated_price_price_list: !!calculatedPrice?.price_list_id,
currency_code: price?.currency_code || null, calculated_amount: parseInt(calculatedPrice?.amount || "") || null,
min_quantity: price?.min_quantity || null,
max_quantity: price?.max_quantity || 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> { ): Promise<void> {
await this.priceRuleService_.delete(priceRuleIds, sharedContext) await this.priceRuleService_.delete(priceRuleIds, sharedContext)
} }
@InjectManager("baseRepository_")
async retrievePriceList(
id: string,
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const priceList = await this.priceListService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO>(
priceList,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceLists(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountPriceLists(
filters: PricingTypes.FilterablePriceListProps = {},
config: FindConfig<PricingTypes.PriceListDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceListDTO[], number]> {
const [priceLists, count] = await this.priceListService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
),
count,
]
}
@InjectManager("baseRepository_")
async createPriceLists(
data: PricingTypes.CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.createPriceLists_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
protected async createPriceLists_(
data: PricingTypes.CreatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const createdPriceLists: PricingTypes.PriceListDTO[] = []
const ruleAttributes = data
.map((priceListData) => Object.keys(priceListData.rules || {}))
.flat()
const ruleTypes = await this.listRuleTypes({
rule_attribute: ruleAttributes,
})
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
for (const priceListData of data) {
const { rules = {}, prices = [], ...priceListOnlyData } = priceListData
const [createdPriceList] = (await this.priceListService_.create(
[
{
...priceListOnlyData,
number_rules: Object.keys(rules).length,
},
],
sharedContext
)) as unknown as PricingTypes.PriceListDTO[]
createdPriceLists.push(createdPriceList)
for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) {
// Find or create rule type
let ruleType = ruleTypeMap.get(ruleAttribute)
if (!ruleType) {
;[ruleType] = await this.createRuleTypes(
[
{
name: ruleAttribute,
rule_attribute: ruleAttribute,
},
],
sharedContext
)
ruleTypeMap.set(ruleAttribute, ruleType)
}
// Create the rule
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: createdPriceList,
rule_type: ruleType?.id || ruleType,
},
],
sharedContext
)
// Create the values for the rule
for (const ruleValue of ruleValues as string[]) {
await this.priceListRuleValueService_.create(
[
{
price_list_rule: priceListRule,
value: ruleValue,
},
],
sharedContext
)
}
}
for (const price of prices) {
const { price_set_id: priceSetId, ...moneyAmountData } = price
const [moneyAmount] = await this.moneyAmountService_.create(
[moneyAmountData],
sharedContext
)
await this.priceSetMoneyAmountService_.create(
[
{
price_set: priceSetId,
price_list: createdPriceList,
money_amount: moneyAmount,
title: "test",
number_rules: 0,
},
] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[],
sharedContext
)
}
}
return createdPriceLists
}
@InjectTransactionManager("baseRepository_")
async updatePriceLists(
data: PricingTypes.UpdatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.updatePriceLists_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
protected async updatePriceLists_(
data: PricingTypes.UpdatePriceListDTO[],
@MedusaContext() sharedContext: Context = {}
) {
const updatedPriceLists: PricingTypes.PriceListDTO[] = []
const priceListIds = data.map((d) => d.id)
const ruleAttributes = data
.map((priceListData) => Object.keys(priceListData.rules || {}))
.flat()
const existingPriceLists = await this.listPriceLists(
{ id: priceListIds },
{ relations: ["price_list_rules"] },
sharedContext
)
const priceListRuleIds = existingPriceLists
.map((pl) => (pl.price_list_rules || []).map((plr) => plr.id))
.flat()
const existingPriceListRules = await this.listPriceListRules(
{
id: priceListRuleIds,
},
{},
sharedContext
)
if (existingPriceListRules.length) {
await this.deletePriceListRules(
existingPriceListRules.map((plr) => plr.id),
sharedContext
)
}
const ruleTypes = await this.listRuleTypes(
{
rule_attribute: ruleAttributes,
},
{},
sharedContext
)
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
ruleTypes.map((rt) => [rt.rule_attribute, rt])
)
for (const priceListData of data) {
const { rules = {}, ...priceListOnlyData } = priceListData
const [updatedPriceList] = (await this.priceListService_.update(
[
{
...priceListOnlyData,
number_rules: Object.keys(rules).length,
},
],
sharedContext
)) as unknown as PricingTypes.PriceListDTO[]
updatedPriceLists.push(updatedPriceList)
for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) {
let ruleType = ruleTypeMap.get(ruleAttribute)
if (!ruleType) {
;[ruleType] = await this.createRuleTypes(
[
{
name: ruleAttribute,
rule_attribute: ruleAttribute,
},
],
sharedContext
)
ruleTypeMap.set(ruleAttribute, ruleType)
}
const [priceListRule] = await this.priceListRuleService_.create(
[
{
price_list: updatedPriceList,
rule_type: ruleType?.id || ruleType,
},
],
sharedContext
)
for (const ruleValue of ruleValues as string[]) {
await this.priceListRuleValueService_.create(
[
{
price_list_rule: priceListRule,
value: ruleValue,
},
],
sharedContext
)
}
}
}
return updatedPriceLists
}
@InjectTransactionManager("baseRepository_")
async deletePriceLists(
priceListIds: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListService_.delete(priceListIds, sharedContext)
}
@InjectManager("baseRepository_")
async retrievePriceListRule(
id: string,
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO> {
const priceList = await this.priceListRuleService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO>(
priceList,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listPriceListRules(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.priceListRuleService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountPriceListRules(
filters: PricingTypes.FilterablePriceListRuleProps = {},
config: FindConfig<PricingTypes.PriceListRuleDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.PriceListRuleDTO[], number]> {
const [priceLists, count] = await this.priceListRuleService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
),
count,
]
}
@InjectManager("baseRepository_")
async createPriceListRules(
data: PricingTypes.CreatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.createPriceListRules_(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async createPriceListRules_(
data: PricingTypes.CreatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
) {
return await this.priceListRuleService_.create(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async updatePriceListRules(
data: PricingTypes.UpdatePriceListRuleDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListRuleDTO[]> {
const priceLists = await this.priceListRuleService_.update(
data,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.PriceListRuleDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectTransactionManager("baseRepository_")
async deletePriceListRules(
priceListRuleIds: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.priceListRuleService_.delete(priceListRuleIds, sharedContext)
}
@InjectManager("baseRepository_")
async addPriceListPrices(
data: PricingTypes.AddPriceListPricesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
return await this.addPriceListPrices_(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
protected async addPriceListPrices_(
data: PricingTypes.AddPriceListPricesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.listPriceLists(
{ id: data.map((d) => d.priceListId) },
{},
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
for (const { priceListId, prices } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
await Promise.all(
prices.map(async (price) => {
const [moneyAmount] = await this.moneyAmountService_.create(
[price] as unknown as CreateMoneyAmountDTO[],
sharedContext
)
const psma = await this.priceSetMoneyAmountService_.create(
[
{
price_set: price.price_set_id,
money_amount: moneyAmount.id,
title: "test",
price_list: priceList.id,
},
],
sharedContext
)
return psma
})
)
}
return priceLists
}
@InjectManager("baseRepository_")
async setPriceListRules(
data: PricingTypes.SetPriceListRulesDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const [priceList] = await this.setPriceListRules_([data], sharedContext)
return priceList
}
@InjectTransactionManager("baseRepository_")
protected async setPriceListRules_(
data: PricingTypes.SetPriceListRulesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
{ id: data.map((d) => d.priceListId) },
{ relations: ["price_list_rules", "price_list_rules.rule_type"] },
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
const ruleTypes = await this.listRuleTypes({
rule_attribute: data.map((d) => Object.keys(d.rules)).flat(),
})
const ruleTypeMap = new Map(ruleTypes.map((rt) => [rt.rule_attribute, rt]))
const ruleIdsToUpdate: string[] = []
const rulesToCreate: CreatePriceListRuleDTO[] = []
const priceRuleValues = new Map<string, Map<string, string[]>>()
for (const { priceListId, rules } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
const priceListRulesMap: Map<string, PriceListRule> = new Map(
priceList.price_list_rules
.getItems()
.map((p) => [p.rule_type.rule_attribute, p])
)
const priceListRuleValues = new Map<string, string[]>()
await Promise.all(
Object.entries(rules).map(async ([key, value]) => {
const ruleType = ruleTypeMap.get(key)
if (!ruleType) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Rule type with attribute: ${key} not found`
)
}
const rule = priceListRulesMap.get(key)
priceListRuleValues.set(
ruleType.id,
Array.isArray(value) ? value : [value]
)
if (!rule) {
rulesToCreate.push({
rule_type: ruleType.id,
price_list: priceListId,
})
} else {
ruleIdsToUpdate.push(rule.id)
}
})
)
priceRuleValues.set(priceListId, priceListRuleValues)
}
const [createdRules, priceListValuesToDelete] = await Promise.all([
this.priceListRuleService_.create(rulesToCreate),
this.priceListRuleValueService_.list({
price_list_rule_id: ruleIdsToUpdate,
}),
])
const priceListRuleValuesToCreate: CreatePriceListRuleValueDTO[] = []
for (const { id, price_list, rule_type } of createdRules) {
const ruleValues = priceRuleValues.get(
price_list.id ?? (price_list as unknown as string)
)
if (!ruleValues) {
continue
}
const values = ruleValues.get(
rule_type.id ?? (rule_type as unknown as string)
)
if (!values) {
continue
}
values.forEach((v) => {
priceListRuleValuesToCreate.push({
price_list_rule: id,
value: v,
})
})
}
await Promise.all([
this.priceListRuleValueService_.delete(
priceListValuesToDelete.map((p) => p.id)
),
this.priceListRuleValueService_.create(priceListRuleValuesToCreate),
])
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async removePriceListRules(
data: PricingTypes.RemovePriceListRulesDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO> {
const [priceList] = await this.removePriceListRules_([data], sharedContext)
return priceList
}
@InjectTransactionManager("baseRepository_")
protected async removePriceListRules_(
data: PricingTypes.RemovePriceListRulesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
const priceLists = await this.priceListService_.list(
{ id: data.map((d) => d.priceListId) },
{ relations: ["price_list_rules", "price_list_rules.rule_type"] },
sharedContext
)
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
const idsToDelete: string[] = []
for (const { priceListId, rules } of data) {
const priceList = priceListMap.get(priceListId)
if (!priceList) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Price list with id: ${priceListId} not found`
)
}
const priceListRulesMap: Map<string, PriceListRule> = new Map(
priceList.price_list_rules
.getItems()
.map((p) => [p.rule_type.rule_attribute, p])
)
await Promise.all(
rules.map(async (rule_attribute) => {
const rule = priceListRulesMap.get(rule_attribute)
if (rule) {
idsToDelete.push(rule.id)
}
})
)
}
await this.priceListRuleService_.delete(idsToDelete)
return this.baseRepository_.serialize<PricingTypes.PriceListDTO[]>(
priceLists,
{
populate: true,
}
)
}
} }
@@ -1 +1,3 @@
export * from "./price-list"
export * from "./price-list-rule-value"
export * from "./pricing" export * from "./pricing"
@@ -0,0 +1,12 @@
import { PriceListRule } from "@models"
export interface CreatePriceListRuleValueDTO {
price_list_rule_id?: string
price_list_rule: PriceListRule | string
value: string
}
export interface UpdatePriceListRuleValueDTO {
id: string
value: string
}
@@ -0,0 +1,11 @@
import { PriceListStatus, PriceListType } from "@medusajs/utils"
export interface CreatePriceListDTO {
title: string
description: string
starts_at?: string
ends_at?: string
status?: PriceListStatus
type?: PriceListType
number_rules?: number
}
@@ -6,3 +6,4 @@ export * from "./price-set-money-amount"
export * from "./price-set-money-amount-rules" export * from "./price-set-money-amount-rules"
export * from "./price-set-rule-type" export * from "./price-set-rule-type"
export * from "./rule-type" export * from "./rule-type"
export * from "./price-list"
@@ -1,5 +1,6 @@
import { BaseFilterable } from "../../dal" import { BaseFilterable } from "../../dal";
import { CreateCurrencyDTO, CurrencyDTO } from "./currency" import { CreateCurrencyDTO, CurrencyDTO } from "./currency";
import { PriceSetMoneyAmountDTO } from "./price-set-money-amount";
/** /**
* @interface * @interface
@@ -20,6 +21,7 @@ export interface MoneyAmountDTO {
amount?: number amount?: number
min_quantity?: number min_quantity?: number
max_quantity?: number max_quantity?: number
price_set_money_amount?: PriceSetMoneyAmountDTO
} }
/** /**
@@ -38,7 +40,7 @@ export interface CreateMoneyAmountDTO {
id?: string id?: string
currency_code: string currency_code: string
currency?: CreateCurrencyDTO currency?: CreateCurrencyDTO
amount?: number amount: number
min_quantity?: number | null min_quantity?: number | null
max_quantity?: number | null max_quantity?: number | null
} }
@@ -0,0 +1,137 @@
import { CreateMoneyAmountDTO, MoneyAmountDTO } from "./money-amount"
import { BaseFilterable } from "../../dal"
import { PriceSetMoneyAmountDTO } from "./price-set-money-amount"
import { RuleTypeDTO } from "./rule-type"
export enum PriceListStatus {
ACTIVE = "active",
DRAFT = "draft",
}
export enum PriceListType {
SALE = "sale",
OVERRIDE = "override",
}
export interface PriceListDTO {
id: string
title?: string
starts_at?: string | null
status?: PriceListStatus
ends_at?: string | null
number_rules?: number
price_set_money_amounts?: PriceSetMoneyAmountDTO[]
money_amounts?: MoneyAmountDTO[]
rule_types?: RuleTypeDTO[]
rules?: PriceListRuleDTO[]
price_list_rules?: PriceListRuleDTO[]
}
export interface PriceListPriceDTO extends CreateMoneyAmountDTO {
price_set_id: string
}
export interface CreatePriceListRules extends Record<string, string[]> {}
export interface CreatePriceListDTO {
title: string
description: string
starts_at?: string
ends_at?: string
status?: PriceListStatus
type?: PriceListType
number_rules?: number
rules?: CreatePriceListRules
prices?: PriceListPriceDTO[]
}
export interface UpdatePriceListDTO {
id: string
title?: string
starts_at?: string
ends_at?: string
status?: PriceListStatus
number_rules?: number
rules?: CreatePriceListRules
}
export interface FilterablePriceListProps
extends BaseFilterable<FilterablePriceListProps> {
id?: string[]
starts_at?: string[]
ends_at?: string[]
status?: PriceListStatus[]
number_rules?: number[]
}
export interface FilterablePriceListRuleProps
extends BaseFilterable<FilterablePriceListRuleProps> {
id?: string[]
value?: string[]
rule_type?: string[]
price_list_id?: string[]
}
export interface FilterablePriceListRuleValueProps
extends BaseFilterable<FilterablePriceListRuleValueProps> {
id?: string[]
value?: string[]
price_list_rule_id?: string[]
}
export interface PriceListRuleDTO {
id: string
value: string
rule_type: RuleTypeDTO
price_list: PriceListDTO
price_list_rule_values?: PriceListRuleValueDTO[]
}
export interface CreatePriceListRuleDTO {
rule_type_id?: string
rule_type?: string | RuleTypeDTO
price_list_id?: string
price_list?: string | PriceListDTO
}
export interface UpdatePriceListRuleDTO {
id: string
price_list_id?: string
rule_type_id?: string
price_list?: string
rule_type?: string
}
export interface PriceListRuleValueDTO {
id: string
value: string
price_list_rule: PriceListRuleDTO
}
export interface CreatePriceListRuleValueDTO {
value: string
price_list_rule_id?: string
price_list_rule?: PriceListRuleDTO | string
}
export interface UpdatePriceListRuleValueDTO {
id: string
value: string
price_list_rule_id: string
}
export interface AddPriceListPricesDTO {
priceListId: string
prices: PriceListPriceDTO[]
}
export interface SetPriceListRulesDTO {
priceListId: string
rules: Record<string, string | string[]>
}
export interface RemovePriceListRulesDTO {
priceListId: string
rules: string[]
}
@@ -64,7 +64,6 @@ export interface CreatePriceRuleDTO {
value: string value: string
priority?: number priority?: number
price_set_money_amount_id: string price_set_money_amount_id: string
price_list_id: string
} }
/** /**
@@ -1,5 +1,6 @@
import { BaseFilterable } from "../../dal" import { BaseFilterable } from "../../dal"
import { MoneyAmountDTO } from "./money-amount" import { MoneyAmountDTO } from "./money-amount"
import { PriceListDTO } from "./price-list"
import { PriceRuleDTO } from "./price-rule" import { PriceRuleDTO } from "./price-rule"
import { PriceSetDTO } from "./price-set" import { PriceSetDTO } from "./price-set"
@@ -17,6 +18,7 @@ export interface PriceSetMoneyAmountDTO {
id: string id: string
title?: string title?: string
price_set?: PriceSetDTO price_set?: PriceSetDTO
price_list?: PriceListDTO
price_set_id?: string price_set_id?: string
price_rules?: PriceRuleDTO[] price_rules?: PriceRuleDTO[]
money_amount?: MoneyAmountDTO money_amount?: MoneyAmountDTO
@@ -32,6 +34,7 @@ export interface UpdatePriceSetMoneyAmountDTO {
export interface CreatePriceSetMoneyAmountDTO { export interface CreatePriceSetMoneyAmountDTO {
title?: string title?: string
price_set?: PriceSetDTO | string price_set?: PriceSetDTO | string
price_list?: PriceListDTO | string
money_amount?: MoneyAmountDTO | string money_amount?: MoneyAmountDTO | string
} }
@@ -47,4 +50,5 @@ export interface FilterablePriceSetMoneyAmountProps
extends BaseFilterable<FilterablePriceSetMoneyAmountProps> { extends BaseFilterable<FilterablePriceSetMoneyAmountProps> {
id?: string[] id?: string[]
price_set_id?: string[] price_set_id?: string[]
price_list_id?: string[]
} }
+34 -8
View File
@@ -1,10 +1,6 @@
import { BaseFilterable } from "../../dal" import { BaseFilterable } from "../../dal";
import { import { CreateMoneyAmountDTO, FilterableMoneyAmountProps, MoneyAmountDTO } from "./money-amount";
CreateMoneyAmountDTO, import { RuleTypeDTO } from "./rule-type";
FilterableMoneyAmountProps,
MoneyAmountDTO,
} from "./money-amount"
import { RuleTypeDTO } from "./rule-type"
/** /**
* @interface * @interface
@@ -60,12 +56,42 @@ export interface PriceSetDTO {
*/ */
export interface CalculatedPriceSetDTO { export interface CalculatedPriceSetDTO {
id: string id: string
amount: number | null price_set_id: string
amount: string | null
currency_code: string | null currency_code: string | null
min_quantity: string | null
max_quantity: string | null
price_list_type: string | null
price_list_id: string | null
}
export interface CalculatedPriceSet {
id: string
is_calculated_price_price_list?: boolean
calculated_amount: number | null
is_original_price_price_list?: boolean
original_amount: number | null
currency_code: string | null
calculated_price?: {
money_amount_id: string | null
price_list_id: string | null
price_list_type: string | null
min_quantity: number | null min_quantity: number | null
max_quantity: number | null max_quantity: number | null
} }
original_price?: {
money_amount_id: string | null
price_list_id: string | null
price_list_type: string | null
min_quantity: number | null
max_quantity: number | null
}
}
/** /**
* @interface * @interface
* *
+93 -2
View File
@@ -1,9 +1,12 @@
import { import {
AddPriceListPricesDTO,
AddPricesDTO, AddPricesDTO,
AddRulesDTO, AddRulesDTO,
CalculatedPriceSetDTO, CalculatedPriceSetDTO,
CreateCurrencyDTO, CreateCurrencyDTO,
CreateMoneyAmountDTO, CreateMoneyAmountDTO,
CreatePriceListDTO,
CreatePriceListRuleDTO,
CreatePriceRuleDTO, CreatePriceRuleDTO,
CreatePriceSetDTO, CreatePriceSetDTO,
CreatePriceSetMoneyAmountRulesDTO, CreatePriceSetMoneyAmountRulesDTO,
@@ -11,31 +14,39 @@ import {
CurrencyDTO, CurrencyDTO,
FilterableCurrencyProps, FilterableCurrencyProps,
FilterableMoneyAmountProps, FilterableMoneyAmountProps,
FilterablePriceListProps,
FilterablePriceListRuleProps,
FilterablePriceRuleProps, FilterablePriceRuleProps,
FilterablePriceSetMoneyAmountProps, FilterablePriceSetMoneyAmountProps,
FilterablePriceSetMoneyAmountRulesProps, FilterablePriceSetMoneyAmountRulesProps,
FilterablePriceSetProps, FilterablePriceSetProps,
FilterableRuleTypeProps, FilterableRuleTypeProps,
MoneyAmountDTO, MoneyAmountDTO,
PriceListDTO,
PriceListRuleDTO,
PriceRuleDTO, PriceRuleDTO,
PriceSetDTO, PriceSetDTO,
PriceSetMoneyAmountDTO, PriceSetMoneyAmountDTO,
PriceSetMoneyAmountRulesDTO, PriceSetMoneyAmountRulesDTO,
PricingContext, PricingContext,
PricingFilters, PricingFilters,
RemovePriceListRulesDTO,
RemovePriceSetRulesDTO, RemovePriceSetRulesDTO,
RuleTypeDTO, RuleTypeDTO,
SetPriceListRulesDTO,
UpdateCurrencyDTO, UpdateCurrencyDTO,
UpdateMoneyAmountDTO, UpdateMoneyAmountDTO,
UpdatePriceListDTO,
UpdatePriceListRuleDTO,
UpdatePriceRuleDTO, UpdatePriceRuleDTO,
UpdatePriceSetDTO, UpdatePriceSetDTO,
UpdatePriceSetMoneyAmountRulesDTO, UpdatePriceSetMoneyAmountRulesDTO,
UpdateRuleTypeDTO, UpdateRuleTypeDTO,
} from "./common" } from "./common"
import { Context } from "../shared-context"
import { FindConfig } from "../common" import { FindConfig } from "../common"
import { ModuleJoinerConfig } from "../modules-sdk" import { ModuleJoinerConfig } from "../modules-sdk"
import { Context } from "../shared-context"
export interface IPricingModuleService { export interface IPricingModuleService {
/** /**
@@ -2415,7 +2426,6 @@ export interface IPricingModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<[PriceSetMoneyAmountDTO[], number]> ): Promise<[PriceSetMoneyAmountDTO[], number]>
/** /**
* This method is used to create new price set money amount rules. A price set money amount rule creates an association between a price set money amount and * This method is used to create new price set money amount rules. A price set money amount rule creates an association between a price set money amount and
* a rule type. * a rule type.
@@ -2865,4 +2875,85 @@ export interface IPricingModuleService {
priceRuleIds: string[], priceRuleIds: string[],
sharedContext?: Context sharedContext?: Context
): Promise<void> ): Promise<void>
retrievePriceList(
id: string,
config?: FindConfig<PriceListDTO>,
sharedContext?: Context
): Promise<PriceListDTO>
listPriceLists(
filters?: FilterablePriceListProps,
config?: FindConfig<PriceListDTO>,
sharedContext?: Context
): Promise<PriceListDTO[]>
listAndCountPriceLists(
filters?: FilterablePriceListProps,
config?: FindConfig<PriceListDTO>,
sharedContext?: Context
): Promise<[PriceListDTO[], number]>
createPriceLists(
data: CreatePriceListDTO[],
sharedContext?: Context
): Promise<PriceListDTO[]>
updatePriceLists(
data: UpdatePriceListDTO[],
sharedContext?: Context
): Promise<PriceListDTO[]>
deletePriceLists(
priceListIds: string[],
sharedContext?: Context
): Promise<void>
retrievePriceListRule(
id: string,
config?: FindConfig<PriceListRuleDTO>,
sharedContext?: Context
): Promise<PriceListRuleDTO>
listPriceListRules(
filters?: FilterablePriceListRuleProps,
config?: FindConfig<PriceListRuleDTO>,
sharedContext?: Context
): Promise<PriceListRuleDTO[]>
listAndCountPriceListRules(
filters?: FilterablePriceListRuleProps,
config?: FindConfig<PriceListRuleDTO>,
sharedContext?: Context
): Promise<[PriceListRuleDTO[], number]>
createPriceListRules(
data: CreatePriceListRuleDTO[],
sharedContext?: Context
): Promise<PriceListRuleDTO[]>
updatePriceListRules(
data: UpdatePriceListRuleDTO[],
sharedContext?: Context
): Promise<PriceListRuleDTO[]>
deletePriceListRules(
priceListRuleIds: string[],
sharedContext?: Context
): Promise<void>
addPriceListPrices(
data: AddPriceListPricesDTO[],
sharedContext?: Context
): Promise<PriceListDTO[]>
setPriceListRules(
data: SetPriceListRulesDTO,
sharedContext?: Context
): Promise<PriceListDTO>
removePriceListRules(
data: RemovePriceListRulesDTO,
sharedContext?: Context
): Promise<PriceListDTO>
} }
@@ -0,0 +1,15 @@
type ArrayDifferenceElement = string | number
export function arrayDifference(
mainArray: ArrayDifferenceElement[],
differingArray: ArrayDifferenceElement[]
): ArrayDifferenceElement[] {
const mainArraySet = new Set(mainArray)
const differingArraySet = new Set(differingArray)
const difference = [...mainArraySet].filter(
(element) => !differingArraySet.has(element)
)
return difference
}
+3 -2
View File
@@ -1,6 +1,9 @@
export * from "./array-difference"
export * from "./build-query" export * from "./build-query"
export * from "./camel-to-snake-case"
export * from "./container" export * from "./container"
export * from "./deduplicate" export * from "./deduplicate"
export * from "./deep-equal-obj"
export * from "./errors" export * from "./errors"
export * from "./generate-entity-id" export * from "./generate-entity-id"
export * from "./get-config-file" export * from "./get-config-file"
@@ -24,11 +27,9 @@ export * from "./set-metadata"
export * from "./simple-hash" export * from "./simple-hash"
export * from "./string-to-select-relation-object" export * from "./string-to-select-relation-object"
export * from "./stringify-circular" export * from "./stringify-circular"
export * from "./camel-to-snake-case"
export * from "./to-camel-case" export * from "./to-camel-case"
export * from "./to-kebab-case" export * from "./to-kebab-case"
export * from "./to-pascal-case" export * from "./to-pascal-case"
export * from "./transaction" export * from "./transaction"
export * from "./upper-case-first" export * from "./upper-case-first"
export * from "./wrap-handler" export * from "./wrap-handler"
export * from "./deep-equal-obj"
+2 -38
View File
@@ -1,38 +1,2 @@
import { MedusaError } from "../common" export * from "./price-list"
export * from "./rule-type"
export const ReservedPricingRuleAttributes = [
"quantity",
"currency_code",
"price_list_id",
]
type RuleAttributeInput = string | undefined
export const getInvalidRuleAttributes = (
ruleAttributes: RuleAttributeInput[]
): string[] => {
const invalidRuleAttributes: string[] = []
for (const attribute of ReservedPricingRuleAttributes) {
if (ruleAttributes.indexOf(attribute) > -1) {
invalidRuleAttributes.push(attribute)
}
}
return invalidRuleAttributes
}
export const validateRuleAttributes = (
ruleAttributes: RuleAttributeInput[]
): void => {
const invalidRuleAttributes = getInvalidRuleAttributes(ruleAttributes)
if (invalidRuleAttributes.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Can't create rule_attribute with reserved keywords [${ReservedPricingRuleAttributes.join(
", "
)}] - ${invalidRuleAttributes.join(", ")}`
)
}
}
+9
View File
@@ -0,0 +1,9 @@
export enum PriceListStatus {
ACTIVE = "active",
DRAFT = "draft",
}
export enum PriceListType {
SALE = "sale",
OVERRIDE = "override",
}
+37
View File
@@ -0,0 +1,37 @@
import { MedusaError } from "../common"
type RuleAttributeInput = string | undefined
export const ReservedPricingRuleAttributes = [
"quantity",
"currency_code",
"price_list_id",
]
export const getInvalidRuleAttributes = (
ruleAttributes: RuleAttributeInput[]
): string[] => {
const invalidRuleAttributes: string[] = []
for (const attribute of ReservedPricingRuleAttributes) {
if (ruleAttributes.indexOf(attribute) > -1) {
invalidRuleAttributes.push(attribute)
}
}
return invalidRuleAttributes
}
export const validateRuleAttributes = (
ruleAttributes: RuleAttributeInput[]
): void => {
const invalidRuleAttributes = getInvalidRuleAttributes(ruleAttributes)
if (invalidRuleAttributes.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Can't create rule_attribute with reserved keywords [${ReservedPricingRuleAttributes.join(
", "
)}] - ${invalidRuleAttributes.join(", ")}`
)
}
}