feat(pricing) Add Price Set Rule Type (#4977)

* initial

* initial service

* update pricing module service

* add integration test for rule-type

* update pricing-module integration tests

* update pricing service interface

* feat(pricing): PriceSets as entry point to pricing module

* chore: add price set money amount

* chore: add price set money amount

* chore: change name of test

* chore: added changeset

* chore: use filterable props from money amount in price sets

* chore: update migrations

* test update integration test

* fix weird behavior

* Update packages/pricing/integration-tests/__fixtures__/rule-type/index.ts

Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>

* Apply suggestions from code review

Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>

* move rule-type to common

* chore: reset migration

* chore: remove incorrect conflicts

* chore: address review

* chore: remove ghost price list

* Apply suggestions from code review

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>

* update id prefix

* use persist not persistAndflush

* rename key_value to rule_attribute

* more renaming

---------

Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Philip Korsholm
2023-09-14 17:58:31 +02:00
committed by GitHub
parent 5b09f816cb
commit edf90eecb4
22 changed files with 1120 additions and 86 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/pricing": patch
"@medusajs/types": patch
---
feat(pricing,types): Add price set rule type

View File

@@ -1,36 +0,0 @@
export const defaultPriceListData = [
{
name: 'pl-1',
description: 'pl-1',
prices: [ {
id: "money-amount-USD",
currency_code: "USD",
amount: 500,
min_quantity: 1,
max_quantity: 10,
price_list_id: 'pl-1'
},
{
id: "money-amount-EUR",
currency_code: "EUR",
amount: 400,
min_quantity: 1,
max_quantity: 5,
price_list_id: 'pl-1'
}]
},
{
id: 'pl-2',
name: 'pl-2',
description: 'pl-2',
prices: [{
id: "money-amount-CAD",
currency_code: "CAD",
amount: 600,
min_quantity: 1,
max_quantity: 8,
price_list_id: 'pl-2'
}]
}
]

View File

@@ -1,25 +0,0 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { MoneyAmount, PriceList } from "@models"
import { defaultPriceListData } from "./data"
export async function createPriceLists(
manager: SqlEntityManager,
priceListsData: any[] = defaultPriceListData,
): Promise<MoneyAmount[]> {
const priceLists: PriceList[] = []
const moneyAmounts: MoneyAmount[] = []
for (let priceListdata of priceListsData) {
const { prices, ...rest } = priceListdata
const priceList = manager.create(MoneyAmount, rest)
priceLists.push(priceList)
const createdPrices = manager.create(MoneyAmount, prices)
moneyAmounts.push(createdPrices)
}
await manager.persistAndFlush(priceLists)
await manager.persistAndFlush(moneyAmounts)
return priceLists
}

View File

@@ -0,0 +1,12 @@
export const defaultRuleTypesData = [
{
id: "rule-type-1",
name: "rule 1",
rule_attribute: "region_id",
},
{
id: "rule-type-2",
name: "rule 2",
rule_attribute: "currency_code",
},
]

View File

@@ -0,0 +1,20 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { RuleType } from "@models"
import { defaultRuleTypesData } from "./data"
export async function createRuleTypes(
manager: SqlEntityManager,
ruletypesData: any[] = defaultRuleTypesData
): Promise<RuleType[]> {
const RuleTypes: RuleType[] = []
for (let ruleTypeData of ruletypesData) {
const ruleType = manager.create(RuleType, ruleTypeData)
RuleTypes.push(ruleType)
}
await manager.persistAndFlush(RuleTypes)
return RuleTypes
}

View File

@@ -0,0 +1,252 @@
import { IPricingModuleService } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { initialize } from "../../../../src"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
import { DB_URL, MikroOrmWrapper } from "../../../utils"
describe("PricingModuleService ruleType", () => {
let service: IPricingModuleService
let testManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PRICING_DB_SCHEMA,
},
})
testManager = MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("listRuleTypes", () => {
it("should list rule types", async () => {
const ruleTypeResult = await service.listRuleTypes()
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("should list rule types by id", async () => {
const ruleTypeResult = await service.listRuleTypes({
id: ["rule-type-1"],
})
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
])
})
})
describe("listAndCountRuleTypes", () => {
it("should return rule types and count", async () => {
const [ruleTypeResult, count] = await service.listAndCountRuleTypes()
expect(count).toEqual(2)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("should return rule types and count when filtered", async () => {
const [ruleTypeResult, count] = await service.listAndCountRuleTypes({
id: ["rule-type-1"],
})
expect(count).toEqual(1)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
])
})
it("should return rule types and count when using skip and take", async () => {
const [ruleTypeResult, count] = await service.listAndCountRuleTypes(
{},
{ skip: 1, take: 1 }
)
expect(count).toEqual(2)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("should return requested fields", async () => {
const [ruleTypeResult, count] = await service.listAndCountRuleTypes(
{},
{
take: 1,
select: ["name"],
}
)
const serialized = JSON.parse(JSON.stringify(ruleTypeResult))
expect(count).toEqual(2)
expect(serialized).toEqual([
{
id: "rule-type-1",
name: "rule 1",
},
])
})
})
describe("retrieveRuleType", () => {
it("should return ruleType for the given id", async () => {
const ruleType = await service.retrieveRuleType("rule-type-1")
expect(ruleType).toEqual(
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
})
)
})
it("should throw an error when ruleType with id does not exist", async () => {
let error
try {
await service.retrieveRuleType("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"RuleType with id: does-not-exist was not found"
)
})
it("should throw an error when an id is not provided", async () => {
let error
try {
await service.retrieveRuleType(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"ruleTypeId" must be defined')
})
it("should return ruleType based on config select param", async () => {
const ruleTypeResult = await service.retrieveRuleType("rule-type-1", {
select: ["name"],
})
const serialized = JSON.parse(JSON.stringify(ruleTypeResult))
expect(serialized).toEqual({
name: "rule 1",
id: "rule-type-1",
})
})
})
describe("deleteRuleTypes", () => {
const id = "rule-type-1"
it("should delete the ruleTypes given an id successfully", async () => {
await service.deleteRuleTypes([id])
const currencies = await service.listRuleTypes({
id: [id],
})
expect(currencies).toHaveLength(0)
})
})
describe("updateRuleTypes", () => {
const id = "rule-type-1"
it("should update the name of the ruleType successfully", async () => {
await service.updateRuleTypes([
{
id,
name: "rule 3",
},
])
const ruletype = await service.retrieveRuleType(id)
expect(ruletype.name).toEqual("rule 3")
})
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.updateRuleTypes([
{
id: "does-not-exist",
name: "rule 3",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'RuleType with id "does-not-exist" not found'
)
})
})
describe("createRuleTypes", () => {
it("should create a ruleType successfully", async () => {
await service.createRuleTypes([
{
name: "Test Rule",
rule_attribute: "region_id",
},
])
const [ruleType] = await service.listRuleTypes({
name: ["Test Rule"],
})
expect(ruleType).toEqual(
expect.objectContaining({
name: "Test Rule",
rule_attribute: "region_id",
})
)
})
})
})

View File

@@ -0,0 +1,257 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { RuleTypeRepository } from "@repositories"
import { RuleTypeService } from "@services"
import { MikroOrmWrapper } from "../../../utils"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
jest.setTimeout(30000)
describe("RuleType Service", () => {
let service: RuleTypeService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
const ruleTypeRepository = new RuleTypeRepository({
manager: repositoryManager,
})
service = new RuleTypeService({
ruleTypeRepository,
})
testManager = await MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("list rule types", async () => {
const ruleTypeResult = await service.list()
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("list rule types by id", async () => {
const ruleTypeResult = await service.list({ id: ["rule-type-1"] })
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
])
})
})
describe("listAndCount", () => {
it("should return rule types and count", async () => {
const [ruleTypeResult, count] = await service.listAndCount()
expect(count).toEqual(2)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("should return rule types and count when filtered", async () => {
const [ruleTypeResult, count] = await service.listAndCount({
id: ["rule-type-1"],
})
expect(count).toEqual(1)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
}),
])
})
it("should return rule types and count when using skip and take", async () => {
const [ruleTypeResult, count] = await service.listAndCount(
{},
{ skip: 1, take: 1 }
)
expect(count).toEqual(2)
expect(ruleTypeResult).toEqual([
expect.objectContaining({
id: "rule-type-2",
name: "rule 2",
}),
])
})
it("should return requested fields", async () => {
const [ruleTypeResult, count] = await service.listAndCount(
{},
{
take: 1,
select: ["name"],
}
)
const serialized = JSON.parse(JSON.stringify(ruleTypeResult))
expect(count).toEqual(2)
expect(serialized).toEqual([
{
id: "rule-type-1",
name: "rule 1",
},
])
})
})
describe("retrieve", () => {
it("should return ruleType for the given id", async () => {
const ruleType = await service.retrieve('rule-type-1')
expect(ruleType).toEqual(
expect.objectContaining({
id: "rule-type-1",
name: "rule 1",
})
)
})
it("should throw an error when ruleType with id does not exist", async () => {
let error
try {
await service.retrieve("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"RuleType with id: does-not-exist was not found"
)
})
it("should throw an error when an id is not provided", async () => {
let error
try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"ruleTypeId" must be defined')
})
it("should return ruleType based on config select param", async () => {
const ruleTypeResult = await service.retrieve('rule-type-1', {
select: ["name"],
})
const serialized = JSON.parse(JSON.stringify(ruleTypeResult))
expect(serialized).toEqual({
name: 'rule 1',
id: 'rule-type-1'
})
})
})
describe("delete", () => {
const id = "rule-type-1"
it("should delete the ruleTypes given an id successfully", async () => {
await service.delete([id])
const currencies = await service.list({
id: [id],
})
expect(currencies).toHaveLength(0)
})
})
describe("update", () => {
const id = "rule-type-1"
it("should update the name of the ruleType successfully", async () => {
await service.update([
{
id,
name: "rule 3",
},
])
const ruletype = await service.retrieve(id)
expect(ruletype.name).toEqual("rule 3")
})
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.update([
{
id: "does-not-exist",
name: "rule 3",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'RuleType with id "does-not-exist" not found'
)
})
})
describe("create", () => {
it("should create a ruleType successfully", async () => {
await service.create([
{
name: "Test Rule",
rule_attribute: 'region_id',
},
])
const [ruleType] = await service.list({
name: ["Test Rule"],
})
expect(ruleType).toEqual(
expect.objectContaining({
name: "Test Rule",
rule_attribute: 'region_id',
})
)
})
})
})

View File

@@ -21,6 +21,7 @@ export default async ({
currencyService: asClass(defaultServices.CurrencyService).singleton(),
moneyAmountService: asClass(defaultServices.MoneyAmountService).singleton(),
priceSetService: asClass(defaultServices.PriceSetService).singleton(),
ruleTypeService: asClass(defaultServices.RuleTypeService).singleton(),
})
if (customRepositories) {
@@ -46,5 +47,8 @@ function loadDefaultRepositories({ container }) {
priceSetRepository: asClass(
defaultRepositories.PriceSetRepository
).singleton(),
ruleTypeRepository: asClass(
defaultRepositories.RuleTypeRepository
).singleton(),
})
}

View File

@@ -185,13 +185,22 @@
"nullable": false,
"mappedType": "text"
},
"title": {
"name": "title",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"price_set_id": {
"name": "price_set_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"nullable": false,
"mappedType": "text"
},
"money_amount_id": {
@@ -200,15 +209,6 @@
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"title": {
"name": "title",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
@@ -217,13 +217,29 @@
"schema": "public",
"indexes": [
{
"keyName": "price_set_money_amount_pkey",
"columnNames": [
"id",
"price_set_id",
"price_set_id"
],
"composite": false,
"keyName": "IDX_price_set_money_amount_price_set_id",
"primary": false,
"unique": false
},
{
"columnNames": [
"money_amount_id"
],
"composite": true,
"composite": false,
"keyName": "IDX_price_set_money_amount_money_amount_id",
"primary": false,
"unique": false
},
{
"keyName": "price_set_money_amount_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
@@ -240,6 +256,7 @@
"id"
],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_money_amount_money_amount_id_foreign": {
@@ -255,6 +272,71 @@
"updateRule": "cascade"
}
}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"name": {
"name": "name",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"rule_attribute": {
"name": "rule_attribute",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"default_priority": {
"name": "default_priority",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "0",
"mappedType": "integer"
}
},
"name": "rule_type",
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_attribute"
],
"composite": false,
"keyName": "IDX_rule_type_rule_attribute",
"primary": false,
"unique": false
},
{
"keyName": "rule_type_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
}
]
}

View File

@@ -1,6 +1,6 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20230907144224 extends Migration {
export class Migration20230913123118 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table "currency" ("code" text not null, "symbol" text not null, "symbol_native" text not null, "name" text not null, constraint "currency_pkey" primary key ("code"));'
@@ -9,6 +9,7 @@ export class Migration20230907144224 extends Migration {
this.addSql(
'create table "money_amount" ("id" text not null, "currency_code" text null, "amount" numeric null, "min_quantity" numeric null, "max_quantity" numeric null, constraint "money_amount_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_money_amount_currency_code" on "money_amount" ("currency_code");'
)
@@ -18,7 +19,23 @@ export class Migration20230907144224 extends Migration {
)
this.addSql(
'create table "price_set_money_amount" ("id" text not null, "price_set_id" text null, "money_amount_id" text null, "title" text not null, constraint "price_set_money_amount_pkey" primary key ("id", "price_set_id", "money_amount_id"));'
'create table "price_set_money_amount" ("id" text not null, "title" text not null, "price_set_id" text not null, "money_amount_id" text not null, constraint "price_set_money_amount_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_price_set_money_amount_price_set_id" on "price_set_money_amount" ("price_set_id");'
)
this.addSql(
'create index "IDX_price_set_money_amount_money_amount_id" on "price_set_money_amount" ("money_amount_id");'
)
this.addSql(
'create table "rule_type" ("id" text not null, "name" text not null, "rule_attribute" text not null, "default_priority" integer not null default 0, constraint "rule_type_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_rule_type_rule_attribute" on "rule_type" ("rule_attribute");'
)
this.addSql(
@@ -26,9 +43,8 @@ export class Migration20230907144224 extends Migration {
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_set_id_foreign" foreign key ("price_set_id") references "price_set" ("id") on update cascade;'
'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_set_id_foreign" foreign key ("price_set_id") references "price_set" ("id") on update cascade on delete cascade;'
)
this.addSql(
'alter table "price_set_money_amount" add constraint "price_set_money_amount_money_amount_id_foreign" foreign key ("money_amount_id") references "money_amount" ("id") on update cascade;'
)

View File

@@ -2,3 +2,4 @@ export { default as Currency } from "./currency"
export { default as MoneyAmount } from "./money-amount"
export { default as PriceSet } from "./price-set"
export { default as PriceSetMoneyAmount } from "./price-set-money-amount"
export { default as RuleType } from "./rule-type"

View File

@@ -19,10 +19,15 @@ export default class PriceSetMoneyAmount {
@Property({ columnType: "text" })
title!: string
@ManyToOne(() => PriceSet, { onDelete: "cascade" })
@ManyToOne(() => PriceSet, {
onDelete: "cascade",
index: "IDX_price_set_money_amount_price_set_id",
})
price_set?: PriceSet
@ManyToOne(() => MoneyAmount, {})
@ManyToOne(() => MoneyAmount, {
index: "IDX_price_set_money_amount_money_amount_id",
})
money_amount?: MoneyAmount
@BeforeCreate()

View File

@@ -0,0 +1,34 @@
import { generateEntityId } from "@medusajs/utils"
import {
BeforeCreate,
Entity,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
type OptionalFields = "default_priority"
@Entity()
class RuleType {
[OptionalProps]?: OptionalFields
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text" })
name: string
@Property({ columnType: "text", index: "IDX_rule_type_rule_attribute" })
rule_attribute: string
@Property({ columnType: "integer", default: 0 })
default_priority: number
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "rul-typ")
}
}
export default RuleType

View File

@@ -2,3 +2,4 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
export { CurrencyRepository } from "./currency"
export { MoneyAmountRepository } from "./money-amount"
export { PriceSetRepository } from "./price-set"
export { RuleTypeRepository } from "./rule-type"

View File

@@ -0,0 +1,127 @@
import {
Context,
CreateRuleTypeDTO,
DAL,
UpdateRuleTypeDTO,
} 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 { RuleType } from "@models"
export class RuleTypeRepository 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<RuleType> = { where: {} },
context: Context = {}
): Promise<RuleType[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.find(
RuleType,
findOptions_.where as MikroFilterQuery<RuleType>,
findOptions_.options as MikroOptions<RuleType>
)
}
async findAndCount(
findOptions: DAL.FindOptions<RuleType> = { where: {} },
context: Context = {}
): Promise<[RuleType[], number]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}
Object.assign(findOptions_.options, {
strategy: LoadStrategy.SELECT_IN,
})
return await manager.findAndCount(
RuleType,
findOptions_.where as MikroFilterQuery<RuleType>,
findOptions_.options as MikroOptions<RuleType>
)
}
async delete(ids: string[], context: Context = {}): Promise<void> {
const manager = this.getActiveManager<SqlEntityManager>(context)
await manager.nativeDelete(RuleType, { id: { $in: ids } }, {})
}
async create(
data: CreateRuleTypeDTO[],
context: Context = {}
): Promise<RuleType[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const ruleTypes = data.map((ruleTypeData) => {
return manager.create(RuleType, ruleTypeData)
})
manager.persist(ruleTypes)
return ruleTypes
}
async update(
data: UpdateRuleTypeDTO[],
context: Context = {}
): Promise<RuleType[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const ruleTypeIds = data.map((ruleType) => ruleType.id)
const existingRuleTypes = await this.find(
{
where: {
id: {
$in: ruleTypeIds,
},
},
},
context
)
const existingRuleTypesMap = new Map(
existingRuleTypes.map<[string, RuleType]>((ruleType) => [
ruleType.id,
ruleType,
])
)
const ruleTypes = data.map((ruleTypeData) => {
const existingRuleType = existingRuleTypesMap.get(ruleTypeData.id)
if (!existingRuleType) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`RuleType with id "${ruleTypeData.id}" not found`
)
}
return manager.assign(existingRuleType, ruleTypeData)
})
manager.persist(ruleTypes)
return ruleTypes
}
}

View File

@@ -2,3 +2,4 @@ export { default as CurrencyService } from "./currency"
export { default as MoneyAmountService } from "./money-amount"
export { default as PriceSetService } from "./price-set"
export { default as PricingModuleService } from "./pricing-module"
export { default as RuleTypeService } from "./rule-type"

View File

@@ -8,34 +8,42 @@ import {
PricingFilters,
PricingTypes,
} from "@medusajs/types"
import { Currency, MoneyAmount, PriceSet } from "@models"
import { CurrencyService, MoneyAmountService, PriceSetService } from "@services"
import { Currency, MoneyAmount, PriceSet, RuleType } from "@models"
import {
CurrencyService,
MoneyAmountService,
PriceSetService,
RuleTypeService,
} from "@services"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
shouldForceTransaction,
} from "@medusajs/utils"
import { shouldForceTransaction } from "@medusajs/utils"
import { joinerConfig } from "../joiner-config"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
currencyService: CurrencyService<any>
moneyAmountService: MoneyAmountService<any>
ruleTypeService: RuleTypeService<any>
priceSetService: PriceSetService<any>
}
export default class PricingModuleService<
TPriceSet extends PriceSet = PriceSet,
TMoneyAmount extends MoneyAmount = MoneyAmount,
TCurrency extends Currency = Currency
TCurrency extends Currency = Currency,
TRuleType extends RuleType = RuleType
> implements PricingTypes.IPricingModuleService
{
protected baseRepository_: DAL.RepositoryService
protected readonly currencyService_: CurrencyService<TCurrency>
protected readonly moneyAmountService_: MoneyAmountService<TMoneyAmount>
protected readonly ruleTypeService_: RuleTypeService<TRuleType>
protected readonly priceSetService_: PriceSetService<TPriceSet>
constructor(
@@ -43,6 +51,7 @@ export default class PricingModuleService<
baseRepository,
moneyAmountService,
currencyService,
ruleTypeService,
priceSetService,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
@@ -50,6 +59,7 @@ export default class PricingModuleService<
this.baseRepository_ = baseRepository
this.currencyService_ = currencyService
this.moneyAmountService_ = moneyAmountService
this.ruleTypeService_ = ruleTypeService
this.priceSetService_ = priceSetService
}
@@ -412,4 +422,102 @@ export default class PricingModuleService<
): Promise<void> {
await this.currencyService_.delete(currencyCodes, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveRuleType(
id: string,
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO> {
const ruleType = await this.ruleTypeService_.retrieve(
id,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO>(ruleType, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listRuleTypes(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.list(
filters,
config,
sharedContext
)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectManager("baseRepository_")
async listAndCountRuleTypes(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[PricingTypes.RuleTypeDTO[], number]> {
const [ruleTypes, count] = await this.ruleTypeService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
),
count,
]
}
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
async createRuleTypes(
data: PricingTypes.CreateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.create(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
async updateRuleTypes(
data: PricingTypes.UpdateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.RuleTypeDTO[]> {
const ruleTypes = await this.ruleTypeService_.update(data, sharedContext)
return this.baseRepository_.serialize<PricingTypes.RuleTypeDTO[]>(
ruleTypes,
{
populate: true,
}
)
}
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
async deleteRuleTypes(
ruleTypes: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.ruleTypeService_.delete(ruleTypes, sharedContext)
}
}

View File

@@ -0,0 +1,106 @@
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
ModulesSdkUtils,
retrieveEntity,
} from "@medusajs/utils"
import { RuleType } from "@models"
import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils"
type InjectedDependencies = {
ruleTypeRepository: DAL.RepositoryService
}
export default class RuleTypeService<TEntity extends RuleType = RuleType> {
protected readonly ruleTypeRepository_: DAL.RepositoryService
constructor({ ruleTypeRepository }: InjectedDependencies) {
this.ruleTypeRepository_ = ruleTypeRepository
}
@InjectManager("ruleTypeRepository_")
async retrieve(
ruleTypeId: string,
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<RuleType, PricingTypes.RuleTypeDTO>({
id: ruleTypeId,
identifierColumn: "id",
entityName: RuleType.name,
repository: this.ruleTypeRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("ruleTypeRepository_")
async list(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await this.ruleTypeRepository_.find(
this.buildQueryForList(filters, config),
sharedContext
)) as TEntity[]
}
@InjectManager("ruleTypeRepository_")
async listAndCount(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
return (await this.ruleTypeRepository_.findAndCount(
this.buildQueryForList(filters, config),
sharedContext
)) as [TEntity[], number]
}
private buildQueryForList(
filters: PricingTypes.FilterableRuleTypeProps = {},
config: FindConfig<PricingTypes.RuleTypeDTO> = {}
) {
const queryOptions = ModulesSdkUtils.buildQuery<RuleType>(filters, config)
if (filters.id) {
queryOptions.where["id"] = { $in: filters.id }
}
return queryOptions
}
@InjectTransactionManager(shouldForceTransaction, "ruleTypeRepository_")
async create(
data: PricingTypes.CreateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await this.ruleTypeRepository_.create(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager(shouldForceTransaction, "ruleTypeRepository_")
async update(
data: PricingTypes.UpdateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await this.ruleTypeRepository_.update(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager(doNotForceTransaction, "ruleTypeRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.ruleTypeRepository_.delete(ids, sharedContext)
}
}

View File

@@ -1,3 +1,4 @@
export * from "./currency"
export * from "./money-amount"
export * from "./price-set"
export * from "./rule-type"

View File

@@ -0,0 +1,28 @@
import { BaseFilterable } from "../../dal"
export interface RuleTypeDTO {
id: string
name: string
rule_attribute: string
default_priority: number
}
export interface CreateRuleTypeDTO {
id?: string
name: string
rule_attribute: string
default_priority?: number
}
export interface UpdateRuleTypeDTO {
id: string
name?: string
rule_attribute?: string
default_priority?: number
}
export interface FilterableRuleTypeProps
extends BaseFilterable<FilterableRuleTypeProps> {
id?: string[]
name?: string[]
}

View File

@@ -1,2 +1,2 @@
export * from "./common"
export * from "./service"
export * from "./service"

View File

@@ -6,17 +6,21 @@ import {
CreateCurrencyDTO,
CreateMoneyAmountDTO,
CreatePriceSetDTO,
CreateRuleTypeDTO,
CurrencyDTO,
FilterableCurrencyProps,
FilterableMoneyAmountProps,
FilterablePriceSetProps,
FilterableRuleTypeProps,
MoneyAmountDTO,
PriceSetDTO,
PricingContext,
PricingFilters,
RuleTypeDTO,
UpdateCurrencyDTO,
UpdateMoneyAmountDTO,
UpdatePriceSetDTO,
UpdateRuleTypeDTO,
} from "./common"
export interface IPricingModuleService {
@@ -120,4 +124,34 @@ export interface IPricingModuleService {
currencyCodes: string[],
sharedContext?: Context
): Promise<void>
retrieveRuleType(
code: string,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<RuleTypeDTO>
listRuleTypes(
filters?: FilterableRuleTypeProps,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<RuleTypeDTO[]>
listAndCountRuleTypes(
filters?: FilterableRuleTypeProps,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<[RuleTypeDTO[], number]>
createRuleTypes(
data: CreateRuleTypeDTO[],
sharedContext?: Context
): Promise<RuleTypeDTO[]>
updateRuleTypes(
data: UpdateRuleTypeDTO[],
sharedContext?: Context
): Promise<RuleTypeDTO[]>
deleteRuleTypes(ruleTypes: string[], sharedContext?: Context): Promise<void>
}