feat: Completely revamp the pricing module (#7852)

* feat: Completely revamp the pricing module

* chore: Update all places to the new pricing interfaces

* fix: Remove unnecessary join to itself

* chore: Add data migration for existing users

* fix: Apply the correct index to price rule
This commit is contained in:
Stevche Radevski authored and GitHub committed 2024-07-01 09:47:03 +02:00
1 parent 1f360a3245
commit c661180c44
84 files changed
+613 -3677

No files matched your search

@@ -92,22 +92,6 @@ medusaIntegrationTestRunner({
adminHeaders adminHeaders
) )
).data.price_list ).data.price_list
// BREAKING: You need to register rule types before you can use them
await api.post(
"/admin/pricing/rule-types",
{ name: "Region ID", rule_attribute: "region_id", default_priority: 0 },
adminHeaders
)
await api.post(
"/admin/pricing/rule-types",
{
name: "Customer Group ID",
rule_attribute: "customer_group_id",
default_priority: 0,
},
adminHeaders
)
}) })
describe("/admin/price-lists", () => { describe("/admin/price-lists", () => {
@@ -808,12 +808,6 @@ medusaIntegrationTestRunner({
// }, // },
// async () => { // async () => {
// const variantId = baseProduct.variants[0].id // const variantId = baseProduct.variants[0].id
// await pricingService.createRuleTypes([
// {
// name: "Region ID",
// rule_attribute: "region_id",
// },
// ])
// const priceSet = await createVariantPriceSet({ // const priceSet = await createVariantPriceSet({
// container, // container,
// variantId, // variantId,
@@ -1228,16 +1222,6 @@ medusaIntegrationTestRunner({
}) })
it("creates a product variant with price rules", async () => { it("creates a product variant with price rules", async () => {
await api.post(
`/admin/pricing/rule-types`,
{
name: "Region",
rule_attribute: "region_id",
default_priority: 1,
},
adminHeaders
)
const response = await api.post( const response = await api.post(
"/admin/products", "/admin/products",
{ {
@@ -96,12 +96,6 @@ medusaIntegrationTestRunner({
describe("updates a variant's default prices (ignores prices associated with a Price List)", () => { describe("updates a variant's default prices (ignores prices associated with a Price List)", () => {
it("successfully updates a variant's default prices by changing an existing price (currency_code)", async () => { it("successfully updates a variant's default prices by changing an existing price (currency_code)", async () => {
await api.post(
`/admin/pricing/rule-types`,
{ name: "Region", rule_attribute: "region_id", default_priority: 1 },
adminHeaders
)
const data = { const data = {
prices: [ prices: [
{ {
@@ -10,7 +10,6 @@ import {
createAdminUser, createAdminUser,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
import { getProductFixture } from "../../../../helpers/fixtures" import { getProductFixture } from "../../../../helpers/fixtures"
import { createDefaultRuleTypes } from "../../../../modules/helpers/create-default-rule-types"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -83,7 +82,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
appContainer = getContainer() appContainer = getContainer()
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
const storeModule: IStoreModuleService = appContainer.resolve( const storeModule: IStoreModuleService = appContainer.resolve(
ModuleRegistrationName.STORE ModuleRegistrationName.STORE
@@ -46,16 +46,8 @@ medusaIntegrationTestRunner({
}, },
]) ])
await pricingModule.createRuleTypes([
{
name: "customer_group_id",
rule_attribute: "customer_group_id",
},
])
const [priceSet1, priceSet2] = await pricingModule.createPriceSets([ const [priceSet1, priceSet2] = await pricingModule.createPriceSets([
{ {
rules: [{ rule_attribute: "customer_group_id" }],
prices: [ prices: [
{ {
amount: 3000, amount: 3000,
@@ -71,7 +63,6 @@ medusaIntegrationTestRunner({
], ],
}, },
{ {
rules: [{ rule_attribute: "customer_group_id" }],
prices: [ prices: [
{ {
amount: 400, amount: 400,
@@ -6,7 +6,6 @@ import {
simpleRegionFactory, simpleRegionFactory,
} from "../../../../factories" } from "../../../../factories"
import { createAdminUser } from "../../../../helpers/create-admin-user" import { createAdminUser } from "../../../../helpers/create-admin-user"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -37,7 +36,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "test-region", id: "test-region",
@@ -5,7 +5,6 @@ import {
import { IPricingModuleService } from "@medusajs/types" import { IPricingModuleService } from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
import { createAdminUser } from "../../../../helpers/create-admin-user" import { createAdminUser } from "../../../../helpers/create-admin-user"
@@ -39,7 +38,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "test-region", id: "test-region",
@@ -5,7 +5,6 @@ import {
import { IPricingModuleService } from "@medusajs/types" import { IPricingModuleService } from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
import { createAdminUser } from "../../../../helpers/create-admin-user" import { createAdminUser } from "../../../../helpers/create-admin-user"
@@ -37,7 +36,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "test-region", id: "test-region",
@@ -5,7 +5,6 @@ import {
import { IPricingModuleService } from "@medusajs/types" import { IPricingModuleService } from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
import { createAdminUser } from "../../../../helpers/create-admin-user" import { createAdminUser } from "../../../../helpers/create-admin-user"
@@ -37,7 +36,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "test-region", id: "test-region",
@@ -66,11 +66,6 @@ medusaIntegrationTestRunner({
variant = product.variants[0] variant = product.variants[0]
variant2 = product.variants[1] variant2 = product.variants[1]
await pricingModule.createRuleTypes([
{ name: "Customer Group ID", rule_attribute: "customer_group_id" },
{ name: "Region ID", rule_attribute: "region_id" },
])
}) })
describe("GET /admin/price-lists", () => { describe("GET /admin/price-lists", () => {
@@ -7,7 +7,6 @@ import {
simpleRegionFactory, simpleRegionFactory,
} from "../../../../factories" } from "../../../../factories"
import { createAdminUser } from "../../../../helpers/create-admin-user" import { createAdminUser } from "../../../../helpers/create-admin-user"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -37,7 +36,6 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
await createDefaultRuleTypes(appContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "test-region", id: "test-region",
@@ -1,228 +0,0 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService, RuleTypeDTO } from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createAdminUser } from "../../../../helpers/create-admin-user"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
medusaIntegrationTestRunner({
env,
testSuite: ({ dbConnection, getContainer, api }) => {
describe("Admin: Pricing Rule Types API", () => {
let appContainer
let pricingModule: IPricingModuleService
let ruleTypes: RuleTypeDTO[]
beforeAll(async () => {
appContainer = getContainer()
pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING)
})
beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer)
ruleTypes = await pricingModule.createRuleTypes([
{ name: "Customer Group ID", rule_attribute: "customer_group_id" },
{ name: "Region ID", rule_attribute: "region_id" },
])
})
describe("GET /admin/pricing", () => {
it("should get all rule types and its prices with rules", async () => {
let response = await api.get(
`/admin/pricing/rule-types`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(2)
expect(response.data.rule_types).toEqual([
expect.objectContaining({ id: expect.any(String) }),
expect.objectContaining({ id: expect.any(String) }),
])
response = await api.get(
`/admin/pricing/rule-types?fields=id,rule_attribute,created_at`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(2)
expect(response.data.rule_types).toEqual(
expect.arrayContaining([
{
id: ruleTypes[0].id,
rule_attribute: ruleTypes[0].rule_attribute,
created_at: expect.any(String),
},
{
id: ruleTypes[1].id,
rule_attribute: ruleTypes[1].rule_attribute,
created_at: expect.any(String),
},
])
)
})
})
describe("GET /admin/pricing/:id", () => {
it("should retrieve a rule type and its prices with rules", async () => {
const ruleType = ruleTypes[0]
let response = await api.get(
`/admin/pricing/rule-types/${ruleType.id}`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.rule_type).toEqual(
expect.objectContaining({
id: ruleType.id,
})
)
response = await api.get(
`/admin/pricing/rule-types/${ruleType.id}?fields=id,created_at`,
adminHeaders
)
expect(response.data.rule_type).toEqual({
id: ruleType.id,
created_at: expect.any(String),
})
})
it("should throw an error when rule type is not found", async () => {
const error = await api
.get(`/admin/pricing/rule-types/does-not-exist`, adminHeaders)
.catch((e) => e)
expect(error.response.status).toBe(404)
expect(error.response.data).toEqual({
type: "not_found",
message: "RuleType with id: does-not-exist was not found",
})
})
})
describe("POST /admin/pricing/rule-types", () => {
it("should throw an error if required params are not passed", async () => {
const { response } = await api
.post(
`/admin/pricing/rule-types`,
{
rule_attribute: "rule_attr_test1",
default_priority: 7,
},
adminHeaders
)
.catch((e) => e)
expect(response.status).toEqual(400)
// expect(response.data.message).toEqual(
// "name must be a string, name should not be empty"
// )
})
it("should create a rule type successfully", async () => {
const response = await api.post(
`/admin/pricing/rule-types`,
{
name: "test",
rule_attribute: "rule_attr_test",
default_priority: 6,
},
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.rule_type).toEqual(
expect.objectContaining({
id: expect.any(String),
name: "test",
rule_attribute: "rule_attr_test",
default_priority: 6,
})
)
})
})
describe("POST /admin/pricing/rule-types/:id", () => {
it("should throw an error if id does not exist", async () => {
const { response } = await api
.post(`/admin/pricing/rule-types/does-not-exist`, {}, adminHeaders)
.catch((e) => e)
expect(response.status).toEqual(404)
expect(response.data.message).toEqual(
`RuleType with id "does-not-exist" not found`
)
})
it("should update a rule type successfully", async () => {
const [ruleType] = ruleTypes
const response = await api.post(
`/admin/pricing/rule-types/${ruleType.id}`,
{
name: "test update",
rule_attribute: "test_update",
default_priority: 7,
},
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.rule_type).toEqual(
expect.objectContaining({
id: expect.any(String),
name: "test update",
rule_attribute: "test_update",
default_priority: 7,
})
)
})
})
describe("DELETE /admin/pricing/rule-types/:id", () => {
it("should delete rule type successfully", async () => {
const [ruleType] = ruleTypes
const response = await api.delete(
`/admin/pricing/rule-types/${ruleType.id}`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
id: ruleType.id,
object: "rule_type",
deleted: true,
})
const deletedRuleTypes = await pricingModule.listRuleTypes({
id: [ruleType.id],
})
expect(deletedRuleTypes.length).toEqual(0)
})
it("should return 200 when id does not exist", async () => {
const response = await api.delete(
`/admin/pricing/rule-types/does-not-exist`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
id: "does-not-exist",
object: "rule_type",
deleted: true,
})
})
})
})
},
})
@@ -1,7 +1,6 @@
import { simpleCartFactory, simpleRegionFactory } from "../../../factories" import { simpleCartFactory, simpleRegionFactory } from "../../../factories"
import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { createDefaultRuleTypes } from "../../helpers/create-default-rule-types"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createAdminUser } from "../../../helpers/create-admin-user" import { createAdminUser } from "../../../helpers/create-admin-user"
@@ -33,7 +32,6 @@ medusaIntegrationTestRunner({
medusaContainer = getContainer() medusaContainer = getContainer()
}) })
beforeEach(async () => { beforeEach(async () => {
await createDefaultRuleTypes(medusaContainer)
await createAdminUser(dbConnection, adminHeaders, medusaContainer) await createAdminUser(dbConnection, adminHeaders, medusaContainer)
await simpleRegionFactory(dbConnection, { await simpleRegionFactory(dbConnection, {
id: "region-1", id: "region-1",
@@ -78,12 +76,7 @@ medusaIntegrationTestRunner({
productId = response.data.product.id productId = response.data.product.id
const variant = response.data.product.variants[0] const variant = response.data.product.variants[0]
ruleType = await pricingModuleService.createRuleTypes([
{ name: "region_id", rule_attribute: "region_id" },
])
priceSet = await pricingModuleService.createPriceSets({ priceSet = await pricingModuleService.createPriceSets({
rules: [{ rule_attribute: "region_id" }],
prices: [ prices: [
{ {
amount: 1000, amount: 1000,
@@ -6,7 +6,6 @@ import { getContainer } from "../../../../environment-helpers/use-container"
import { initDb, useDb } from "../../../../environment-helpers/use-db" import { initDb, useDb } from "../../../../environment-helpers/use-db"
import { simpleSalesChannelFactory } from "../../../../factories" import { simpleSalesChannelFactory } from "../../../../factories"
import productSeeder from "../../../../helpers/product-seeder" import productSeeder from "../../../../helpers/product-seeder"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
@@ -65,7 +64,6 @@ describe.skip("Batch job of product-export type", () => {
beforeEach(async () => { beforeEach(async () => {
const container = getContainer() const container = getContainer()
await createDefaultRuleTypes(container)
await productSeeder(dbConnection) await productSeeder(dbConnection)
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
await userSeeder(dbConnection) await userSeeder(dbConnection)
@@ -8,7 +8,6 @@ import { initDb, useDb } from "../../../../environment-helpers/use-db"
import { simpleProductFactory } from "../../../../factories" import { simpleProductFactory } from "../../../../factories"
import { simpleProductCollectionFactory } from "../../../../factories/simple-product-collection-factory" import { simpleProductCollectionFactory } from "../../../../factories/simple-product-collection-factory"
import batchJobSeeder from "../../../../helpers/batch-job-seeder" import batchJobSeeder from "../../../../helpers/batch-job-seeder"
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
@@ -99,7 +98,6 @@ describe.skip("Product import batch job", () => {
beforeEach(async () => { beforeEach(async () => {
const container = getContainer() const container = getContainer()
await createDefaultRuleTypes(container)
await batchJobSeeder(dbConnection) await batchJobSeeder(dbConnection)
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
await userSeeder(dbConnection) await userSeeder(dbConnection)
@@ -1,18 +0,0 @@
import { IPricingModuleService } from "@medusajs/types"
export const createDefaultRuleTypes = async (container) => {
const pricingModuleService: IPricingModuleService = container.resolve(
"pricingModuleService"
)
return await pricingModuleService.createRuleTypes([
{
name: "region_id",
rule_attribute: "region_id",
},
{
name: "customer_group_id",
rule_attribute: "customer_group_id",
},
])
}
@@ -13,18 +13,14 @@ const defaultPrices = [
}, },
] ]
const defaultPriceSetRules = [{ rule_attribute: "region_id" }]
export const createVariantPriceSet = async ({ export const createVariantPriceSet = async ({
container, container,
variantId, variantId,
prices = defaultPrices, prices = defaultPrices,
rules = defaultPriceSetRules,
}: { }: {
container: MedusaContainer container: MedusaContainer
variantId: string variantId: string
prices?: CreatePriceSetDTO["prices"] prices?: CreatePriceSetDTO["prices"]
rules?: CreatePriceSetDTO["rules"]
}): Promise<PriceSetDTO> => { }): Promise<PriceSetDTO> => {
const remoteLink = container.resolve("remoteLink") const remoteLink = container.resolve("remoteLink")
const pricingModuleService: IPricingModuleService = container.resolve( const pricingModuleService: IPricingModuleService = container.resolve(
@@ -32,7 +28,6 @@ export const createVariantPriceSet = async ({
) )
const priceSet = await pricingModuleService.createPriceSets({ const priceSet = await pricingModuleService.createPriceSets({
rules,
prices, prices,
}) })
@@ -25,8 +25,6 @@ function buildPriceSet(
prices: StepInput[0]["prices"], prices: StepInput[0]["prices"],
regionToCurrencyMap: Map<string, string> regionToCurrencyMap: Map<string, string>
): CreatePriceSetDTO { ): CreatePriceSetDTO {
const rules: CreatePriceSetDTO["rules"] = []
const shippingOptionPrices = prices.map((price) => { const shippingOptionPrices = prices.map((price) => {
if ("currency_code" in price) { if ("currency_code" in price) {
return { return {
@@ -35,10 +33,6 @@ function buildPriceSet(
} }
} }
rules.push({
rule_attribute: "region_id",
})
return { return {
currency_code: regionToCurrencyMap.get(price.region_id)!, currency_code: regionToCurrencyMap.get(price.region_id)!,
amount: price.amount, amount: price.amount,
@@ -48,7 +42,7 @@ function buildPriceSet(
} }
}) })
return { rules, prices: shippingOptionPrices } return { prices: shippingOptionPrices }
} }
export const createShippingOptionsPriceSetsStepId = export const createShippingOptionsPriceSetsStepId =
@@ -1,4 +1,4 @@
import { CreateRuleTypeDTO, FulfillmentWorkflow } from "@medusajs/types" import { FulfillmentWorkflow } from "@medusajs/types"
import { import {
createWorkflow, createWorkflow,
transform, transform,
@@ -9,7 +9,6 @@ import {
upsertShippingOptionsStep, upsertShippingOptionsStep,
} from "../steps" } from "../steps"
import { setShippingOptionsPriceSetsStep } from "../steps/set-shipping-options-price-sets" import { setShippingOptionsPriceSetsStep } from "../steps/set-shipping-options-price-sets"
import { createPricingRuleTypesStep } from "../../pricing"
export const createShippingOptionsWorkflowId = export const createShippingOptionsWorkflowId =
"create-shipping-options-workflow" "create-shipping-options-workflow"
@@ -45,18 +44,8 @@ export const createShippingOptionsWorkflow = createWorkflow(
shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices, shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
}, },
(data) => { (data) => {
const ruleTypes = new Set<CreateRuleTypeDTO>()
const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map( const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map(
({ shipping_option_index, prices }) => { ({ shipping_option_index, prices }) => {
prices.forEach((price) => {
if ("region_id" in price) {
ruleTypes.add({
name: "region_id",
rule_attribute: "region_id",
})
}
})
return { return {
id: data.shippingOptions[shipping_option_index].id, id: data.shippingOptions[shipping_option_index].id,
prices, prices,
@@ -66,13 +55,10 @@ export const createShippingOptionsWorkflow = createWorkflow(
return { return {
shippingOptionsPrices, shippingOptionsPrices,
ruleTypes: Array.from(ruleTypes) as CreateRuleTypeDTO[],
} }
} }
) )
createPricingRuleTypesStep(normalizedShippingOptionsPrices.ruleTypes)
const shippingOptionsPriceSetsLinkData = createShippingOptionsPriceSetsStep( const shippingOptionsPriceSetsLinkData = createShippingOptionsPriceSetsStep(
normalizedShippingOptionsPrices.shippingOptionsPrices normalizedShippingOptionsPrices.shippingOptionsPrices
) )
@@ -1,8 +1,4 @@
import { import { FulfillmentWorkflow } from "@medusajs/types"
CreateRuleTypeDTO,
FulfillmentWorkflow,
RuleTypeDTO,
} from "@medusajs/types"
import { import {
createWorkflow, createWorkflow,
transform, transform,
@@ -12,7 +8,6 @@ import {
setShippingOptionsPricesStep, setShippingOptionsPricesStep,
upsertShippingOptionsStep, upsertShippingOptionsStep,
} from "../steps" } from "../steps"
import { createPricingRuleTypesStep } from "../../pricing"
export const updateShippingOptionsWorkflowId = export const updateShippingOptionsWorkflowId =
"update-shipping-options-workflow" "update-shipping-options-workflow"
@@ -49,18 +44,8 @@ export const updateShippingOptionsWorkflow = createWorkflow(
shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices, shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
}, },
(data) => { (data) => {
const ruleTypes = new Set<Partial<RuleTypeDTO>>()
const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map( const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map(
({ shipping_option_index, prices }) => { ({ shipping_option_index, prices }) => {
prices?.forEach((price) => {
if ("region_id" in price) {
ruleTypes.add({
name: "region_id",
rule_attribute: "region_id",
})
}
})
return { return {
id: data.shippingOptions[shipping_option_index].id, id: data.shippingOptions[shipping_option_index].id,
prices, prices,
@@ -70,13 +55,10 @@ export const updateShippingOptionsWorkflow = createWorkflow(
return { return {
shippingOptionsPrices, shippingOptionsPrices,
ruleTypes: Array.from(ruleTypes) as CreateRuleTypeDTO[],
} }
} }
) )
createPricingRuleTypesStep(normalizedShippingOptionsPrices.ruleTypes)
setShippingOptionsPricesStep( setShippingOptionsPricesStep(
normalizedShippingOptionsPrices.shippingOptionsPrices normalizedShippingOptionsPrices.shippingOptionsPrices
) )
@@ -44,7 +44,7 @@ export const updatePriceListsStep = createStep(
await pricingModule.updatePriceLists( await pricingModule.updatePriceLists(
dataBeforeUpdate.map((data) => { dataBeforeUpdate.map((data) => {
const { price_list_rules: priceListRules = [], rules, ...rest } = data const { price_list_rules: priceListRules = [], ...rest } = data
const updateData: UpdatePriceListDTO = { const updateData: UpdatePriceListDTO = {
...rest, ...rest,
@@ -82,14 +82,8 @@ async function getDataBeforeUpdate(
selectsClone.splice(index, 1) selectsClone.splice(index, 1)
} }
selectsClone.push( selectsClone.push("price_list_rules.value", "price_list_rules.attribute")
"price_list_rules.price_list_rule_values.value", relationsClone.push("price_list_rules")
"price_list_rules.rule_type.rule_attribute"
)
relationsClone.push(
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type"
)
} }
const dataBeforeUpdate = await pricingModule.listPriceLists( const dataBeforeUpdate = await pricingModule.listPriceLists(
@@ -1,2 +1 @@
export * from "./steps" export * from "./steps"
export * from "./workflows"
@@ -1,47 +0,0 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CreateRuleTypeDTO, IPricingModuleService } from "@medusajs/types"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
export const createPricingRuleTypesStepId = "create-pricing-rule-types"
export const createPricingRuleTypesStep = createStep(
createPricingRuleTypesStepId,
async (data: CreateRuleTypeDTO[], { container }) => {
if (!data?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
const existingRuleTypes = await pricingModule.listRuleTypes({
rule_attribute: data.map((d) => d.rule_attribute),
})
const existingRuleTypeAttributes = new Set(
existingRuleTypes.map((ruleType) => ruleType.rule_attribute)
)
const ruleTypesToCreate = data.filter(
(dataItem) => !existingRuleTypeAttributes.has(dataItem.rule_attribute)
)
const ruleTypes = await pricingModule.createRuleTypes(ruleTypesToCreate)
return new StepResponse(
ruleTypes,
ruleTypes.map((ruleType) => ruleType.id)
)
},
async (ruleTypeIds, { container }) => {
if (!ruleTypeIds?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
await pricingModule.deleteRuleTypes(ruleTypeIds)
}
)
@@ -1,31 +0,0 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const deletePricingRuleTypesStepId = "delete-pricing-rule-types"
export const deletePricingRuleTypesStep = createStep(
deletePricingRuleTypesStepId,
async (ids: string[], { container }) => {
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
// TODO: implement soft deleting rule types
// await pricingModule.softDeleteRuleTypes(ids)
await pricingModule.deleteRuleTypes(ids)
return new StepResponse(void 0, ids)
},
async (ids, { container }) => {
if (!ids?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
// TODO: implement restoring soft deleted rule types
// await pricingModule.restoreRuleTypes(ids)
}
)
@@ -1,5 +1,2 @@
export * from "./create-price-sets" export * from "./create-price-sets"
export * from "./update-price-sets" export * from "./update-price-sets"
export * from "./create-pricing-rule-types"
export * from "./delete-pricing-rule-types"
export * from "./update-pricing-rule-types"
@@ -1,48 +0,0 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService, UpdateRuleTypeDTO } from "@medusajs/types"
import {
convertItemResponseToUpdateRequest,
getSelectsAndRelationsFromObjectArray,
} from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const updatePricingRuleTypesStepId = "update-pricing-rule-types"
export const updatePricingRuleTypesStep = createStep(
updatePricingRuleTypesStepId,
async (data: UpdateRuleTypeDTO[], { container }) => {
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
const dataBeforeUpdate = await pricingModule.listRuleTypes(
{ id: data.map((d) => d.id) },
{ relations, select: selects }
)
const updatedRuleTypes = await pricingModule.updateRuleTypes(data)
return new StepResponse(updatedRuleTypes, {
dataBeforeUpdate,
selects,
relations,
})
},
async (revertInput, { container }) => {
if (!revertInput) {
return
}
const { dataBeforeUpdate = [], selects, relations } = revertInput
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
await pricingModule.updateRuleTypes(
dataBeforeUpdate.map((data) =>
convertItemResponseToUpdateRequest(data, selects, relations)
)
)
}
)
@@ -1,13 +0,0 @@
import { CreateRuleTypeDTO, RuleTypeDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { createPricingRuleTypesStep } from "../steps"
type WorkflowInput = { data: CreateRuleTypeDTO[] }
export const createPricingRuleTypesWorkflowId = "create-pricing-rule-types"
export const createPricingRuleTypesWorkflow = createWorkflow(
createPricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<RuleTypeDTO[]> => {
return createPricingRuleTypesStep(input.data)
}
)
@@ -1,12 +0,0 @@
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
import { deletePricingRuleTypesStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const deletePricingRuleTypesWorkflowId = "delete-pricing-rule-types"
export const deletePricingRuleTypesWorkflow = createWorkflow(
deletePricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
deletePricingRuleTypesStep(input.ids)
}
)
@@ -1,3 +0,0 @@
export * from "./create-pricing-rule-types"
export * from "./delete-pricing-rule-types"
export * from "./update-pricing-rule-types"
@@ -1,13 +0,0 @@
import { RuleTypeDTO, UpdateRuleTypeDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { updatePricingRuleTypesStep } from "../steps"
type WorkflowInput = { data: UpdateRuleTypeDTO[] }
export const updatePricingRuleTypesWorkflowId = "update-pricing-rule-types"
export const updatePricingRuleTypesWorkflow = createWorkflow(
updatePricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<RuleTypeDTO[]> => {
return updatePricingRuleTypesStep(input.data)
}
)
@@ -3,6 +3,4 @@ export * from "./price"
export * from "./price-list" export * from "./price-list"
export * from "./price-rule" export * from "./price-rule"
export * from "./price-set" export * from "./price-set"
export * from "./price-set-rule-type"
export * from "./pricing-context" export * from "./pricing-context"
export * from "./rule-type"
@@ -5,7 +5,6 @@ import {
UpdateMoneyAmountDTO, UpdateMoneyAmountDTO,
} from "./money-amount" } from "./money-amount"
import { PriceDTO } from "./price" import { PriceDTO } from "./price"
import { RuleTypeDTO } from "./rule-type"
/** /**
* @enum * @enum
@@ -63,7 +62,7 @@ export interface PriceListDTO {
*/ */
rules_count?: number rules_count?: number
/** /**
* The associated price set money amounts. * The associated price list money amounts.
* *
* @expandable * @expandable
*/ */
@@ -75,22 +74,7 @@ export interface PriceListDTO {
*/ */
money_amounts?: MoneyAmountDTO[] money_amounts?: MoneyAmountDTO[]
/** /**
* The associated rule types. * The price list's rules.
*
* @expandable
*/
rule_types?: RuleTypeDTO[]
/**
* The price set's rules.
*
* @expandable
*/
rules?: PriceListRuleDTO[]
/**
* The price set's rules.
*
* @privateRemarks
* Do we need both this and `rules`?
* *
* @expandable * @expandable
*/ */
@@ -108,9 +92,9 @@ export interface CreatePriceListPriceDTO extends CreateMoneyAmountDTO {
*/ */
price_set_id: string price_set_id: string
/** /**
* The rules to add to the price. The object's keys are rule types' `rule_attribute` attribute, and values are the value of that rule associated with this price. * The rules to add to the price. The object's keys are the attribute, and values are the value of that rule associated with this price.
*/ */
rules?: CreatePriceSetPriceRules rules?: CreatePriceListPriceRules
} }
export interface UpdatePriceListPriceDTO extends UpdateMoneyAmountDTO { export interface UpdatePriceListPriceDTO extends UpdateMoneyAmountDTO {
@@ -119,25 +103,25 @@ export interface UpdatePriceListPriceDTO extends UpdateMoneyAmountDTO {
*/ */
price_set_id: string price_set_id: string
/** /**
* The rules to add to the price. The object's keys are rule types' `rule_attribute` attribute, and values are the value of that rule associated with this price. * The rules to add to the price. The object's keys are the attribute, and values are the value of that rule associated with this price.
*/ */
rules?: CreatePriceSetPriceRules rules?: CreatePriceListPriceRules
} }
/** /**
* @interface * @interface
* *
* The price rules to be set for each price in the price set. * The price rules to be set for each price in the price list.
* *
* Each key of the object is a rule type's `rule_attribute`, and its value * Each key of the object is an attribute, and its value
* is the values of the rule. * is the values of the rule.
*/ */
export interface CreatePriceSetPriceRules extends Record<string, string> {} export interface CreatePriceListPriceRules extends Record<string, string> {}
/** /**
* @interface * @interface
* *
* The price list's rules to be set. Each key of the object is a rule type's `rule_attribute`, and its value * The price list's rules to be set. Each key of the object the attribute, and its value
* is the values of the rule. * is the values of the rule.
*/ */
export interface CreatePriceListRules extends Record<string, string[]> {} export interface CreatePriceListRules extends Record<string, string[]> {}
@@ -172,10 +156,6 @@ export interface CreatePriceListDTO {
* The price list's type. * The price list's type.
*/ */
type?: PriceListType type?: PriceListType
/**
* The number of rules associated with the price list.
*/
rules_count?: number
/** /**
* The rules to be created and associated with the price list. * The rules to be created and associated with the price list.
*/ */
@@ -216,10 +196,6 @@ export interface UpdatePriceListDTO {
* The price list's status. * The price list's status.
*/ */
status?: PriceListStatus status?: PriceListStatus
/**
* The number of rules associated with the price list.
*/
rules_count?: number
/** /**
* The rules to be created and associated with the price list. * The rules to be created and associated with the price list.
*/ */
@@ -274,23 +250,12 @@ export interface FilterablePriceListRuleProps
* The values to filter price list rules by. * The values to filter price list rules by.
*/ */
value?: string[] value?: string[]
/**
* Filter price list rules by the ID of their associated rule types.
*/
rule_type?: string[]
/** /**
* Filter price list rules by the ID of their associated price lists. * Filter price list rules by the ID of their associated price lists.
*/ */
price_list_id?: string[] price_list_id?: string[]
} }
export interface FilterablePriceListRuleValueProps
extends BaseFilterable<FilterablePriceListRuleValueProps> {
id?: string[]
value?: string[]
price_list_rule_id?: string[]
}
/** /**
* @interface * @interface
* *
@@ -301,31 +266,22 @@ export interface PriceListRuleDTO {
* The price list rule's ID. * The price list rule's ID.
*/ */
id: string id: string
/**
* The attribute of the rule.
*
*/
attribute: string
/** /**
* The value of the rule. * The value of the rule.
* *
* @privateRemarks
* Shouldn't this be in PriceListRuleValueDTO only?
*/ */
value: string value: string | string[]
/**
* The associated rule type.
*
* @expandable
*/
rule_type: RuleTypeDTO
/** /**
* The associated price list. * The associated price list.
* *
* @expandable * @expandable
*/ */
price_list: PriceListDTO price_list: PriceListDTO
/**
* The associated rule values.
*
* @expandable
*/
price_list_rule_values?: PriceListRuleValueDTO[]
} }
/** /**
@@ -334,14 +290,6 @@ export interface PriceListRuleDTO {
* The price list rule to create. * The price list rule to create.
*/ */
export interface CreatePriceListRuleDTO { export interface CreatePriceListRuleDTO {
/**
* The ID of a rule type to be associated with the price list rule.
*/
rule_type_id?: string
/**
* The ID of a rule type or the details of an existing rule type to be associated with the price list rule.
*/
rule_type?: string | RuleTypeDTO
/** /**
* The ID of a price list to be associated with the price list rule. * The ID of a price list to be associated with the price list rule.
*/ */
@@ -352,68 +300,6 @@ export interface CreatePriceListRuleDTO {
price_list?: string | PriceListDTO price_list?: string | PriceListDTO
} }
/**
* @interface
*
* The attributes to update in a price list rule.
*/
export interface UpdatePriceListRuleDTO {
/**
* The ID of the price list rule to update.
*/
id: string
/**
* The ID of a price list to be associated with the price list rule.
*/
price_list_id?: string
/**
* The ID of a rule type to be associated with the price list rule.
*/
rule_type_id?: string
/**
* The ID of a price list to be associated with the price list rule.
*/
price_list?: string
/**
* The ID of a rule type or the details of an existing rule type to be associated with the price list rule.
*/
rule_type?: string
}
/**
* @interface
*
* The price list rule value's details.
*/
export interface PriceListRuleValueDTO {
/**
* The price list rule value's ID.
*/
id: string
/**
* The rule's value.
*/
value: string
/**
* The associated price list rule.
*
* @expandable
*/
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
}
/** /**
* @interface * @interface
* *
@@ -457,7 +343,7 @@ export interface SetPriceListRulesDTO {
*/ */
price_list_id: string price_list_id: string
/** /**
* The rules to add to the price list. Each key of the object is a rule type's `rule_attribute`, and its value * The rules to add to the price list. Each key of the object is the attribute, and its value
* is the value(s) of the rule. * is the value(s) of the rule.
*/ */
rules: Record<string, string | string[]> rules: Record<string, string | string[]>
@@ -474,7 +360,7 @@ export interface RemovePriceListRulesDTO {
*/ */
price_list_id: string price_list_id: string
/** /**
* The rules to remove from the price list. Each item being a rule type's `rule_attribute`. * The rules to remove from the price list. Each item being the attribute.
*/ */
rules: string[] rules: string[]
} }
@@ -1,6 +1,5 @@
import { BaseFilterable } from "../../dal" import { BaseFilterable } from "../../dal"
import { PriceSetDTO } from "./price-set" import { PriceSetDTO } from "./price-set"
import { RuleTypeDTO } from "./rule-type"
/** /**
* @interface * @interface
@@ -22,15 +21,9 @@ export interface PriceRuleDTO {
*/ */
price_set: PriceSetDTO price_set: PriceSetDTO
/** /**
* The ID of the associated rule type. * The attribute of the price rule
*/ */
rule_type_id: string attribute: string
/**
* The associated rule type.
*
* @expandable
*/
rule_type: RuleTypeDTO
/** /**
* The value of the price rule. * The value of the price rule.
*/ */
@@ -73,9 +66,9 @@ export interface CreatePriceRuleDTO {
*/ */
price_set_id?: string price_set_id?: string
/** /**
* The ID of the associated rule type. * The attribute of the price rule
*/ */
rule_type_id?: string attribute: string
/** /**
* The value of the price rule. * The value of the price rule.
*/ */
@@ -99,7 +92,10 @@ export interface CreatePriceRuleDTO {
export interface UpdatePriceRuleDTO { export interface UpdatePriceRuleDTO {
id: string id: string
price_set_id?: string price_set_id?: string
rule_type_id?: string /**
* The attribute of the price rule
*/
attribute?: string
/** /**
* The value of the price rule. * The value of the price rule.
*/ */
@@ -136,8 +132,4 @@ export interface FilterablePriceRuleProps
* The IDs to filter the price rule's associated price set. * The IDs to filter the price rule's associated price set.
*/ */
price_set_id?: string[] price_set_id?: string[]
/**
* The IDs to filter the price rule's associated rule type.
*/
rule_type_id?: string[]
} }
@@ -1,28 +0,0 @@
import { BaseFilterable } from "../../dal"
import { PriceSetDTO } from "./price-set"
import { RuleTypeDTO } from "./rule-type"
export interface PriceSetRuleTypeDTO {
id: string
price_set: PriceSetDTO
rule_type: RuleTypeDTO
value: string
}
export interface CreatePriceSetRuleTypeDTO {
price_set_id: string
rule_type_id: string
}
export interface UpdatePriceSetRuleTypeDTO {
id: string
price_set?: string
rule_type?: string
}
export interface FilterablePriceSetRuleTypeProps
extends BaseFilterable<FilterablePriceSetRuleTypeProps> {
id?: string[]
rule_type_id?: string[]
price_set_id?: string[]
}
@@ -6,8 +6,6 @@ import {
FilterableMoneyAmountProps, FilterableMoneyAmountProps,
MoneyAmountDTO, MoneyAmountDTO,
} from "./money-amount" } from "./money-amount"
import { CreatePriceSetPriceRules } from "./price-list"
import { RuleTypeDTO } from "./rule-type"
export interface PricingRepositoryService { export interface PricingRepositoryService {
calculatePrices( calculatePrices(
@@ -63,11 +61,6 @@ export interface PriceSetDTO {
* The calculated price based on the context. * The calculated price based on the context.
*/ */
calculated_price?: CalculatedPriceSet calculated_price?: CalculatedPriceSet
/**
* The rule types applied on this price set.
*/
rule_types?: RuleTypeDTO[]
} }
/** /**
@@ -204,23 +197,12 @@ export interface CalculatedPriceSet {
/** /**
* @interface * @interface
* *
* The rules to add to a price set. * The price rules to be set for each price in the price set.
*
* Each key of the object is a the attribute, and its value
* is the values of the rule.
*/ */
export interface AddRulesDTO { export interface CreatePriceSetPriceRules extends Record<string, string> {}
/**
* The ID of the price set to add the rules to.
*/
priceSetId: string
/**
* The rules to add to a price set.
*/
rules: {
/**
* The value of the rule's `rule_attribute` attribute.
*/
attribute: string
}[]
}
/** /**
* @interface * @interface
@@ -229,7 +211,7 @@ export interface AddRulesDTO {
*/ */
export interface CreatePricesDTO extends CreateMoneyAmountDTO { export interface CreatePricesDTO extends CreateMoneyAmountDTO {
/** /**
* The rules to add to the price. The object's keys are rule types' `rule_attribute` attribute, and values are the value of that rule associated with this price. * The rules to add to the price. The object's keys are the attribute, and values are the value of that rule associated with this price.
*/ */
rules?: CreatePriceSetPriceRules rules?: CreatePriceSetPriceRules
} }
@@ -250,37 +232,12 @@ export interface AddPricesDTO {
prices: CreatePricesDTO[] prices: CreatePricesDTO[]
} }
/**
* @interface
*
* The rules to remove from a price set.
*/
export interface RemovePriceSetRulesDTO {
/**
* The ID of the price set.
*/
id: string
/**
* The rules to remove. Each string is the `rule_attribute` of a rule to remove.
*/
rules: string[]
}
/** /**
* @interface * @interface
* *
* A price set to create. * A price set to create.
*/ */
export interface CreatePriceSetDTO { export interface CreatePriceSetDTO {
/**
* The rules to associate with the price set.
*/
rules?: {
/**
* the value of the rule's `rule_attribute` attribute.
*/
rule_attribute: string
}[]
/** /**
* The prices to create and add to this price set. * The prices to create and add to this price set.
*/ */
@@ -306,15 +263,6 @@ export interface UpsertPriceSetDTO extends UpdatePriceSetDTO {
* The data to update in a price set. * The data to update in a price set.
*/ */
export interface UpdatePriceSetDTO { export interface UpdatePriceSetDTO {
/**
* The rules to associate with the price set.
*/
rules?: {
/**
* the value of the rule's `rule_attribute` attribute.
*/
rule_attribute: string
}[]
/** /**
* The prices to create and add to this price set. * The prices to create and add to this price set.
*/ */
@@ -1,106 +0,0 @@
import { BaseFilterable } from "../../dal"
/**
* @interface
*
* A rule type's data.
*/
export interface RuleTypeDTO {
/**
* The ID of the rule type.
*/
id: string
/**
* The display name of the rule type.
*/
name: string
/**
* The unique name used to later identify the rule_attribute. For example, it can be used in the `context` parameter of
* the `calculatePrices` method to specify a rule for calculating the price.
*/
rule_attribute: string
/**
* The priority of the rule type. This is useful when calculating the price of a price set, and multiple rules satisfy
* the provided context. The higher the value, the higher the priority of the rule type.
*/
default_priority: number
/**
* The creation date of the rule type.
*/
created_at?: Date | string
/**
* The update date of the rule type.
*/
updated_at?: Date | string
}
/**
* @interface
*
* The rule type to create.
*/
export interface CreateRuleTypeDTO {
/**
* The ID of the rule type.
*/
id?: string
/**
* The display name of the rule type.
*/
name: string
/**
* The unique name used to later identify the rule_attribute. For example, it can be used in the `context` parameter of the `calculatePrices`
* method to specify a rule for calculating the price.
*/
rule_attribute: string
/**
* The priority of the rule type. This is useful when calculating the price of a price set, and multiple rules satisfy the provided context.
* The higher the value, the higher the priority of the rule type.
*/
default_priority?: number
}
/**
* @interface
*
* The data to update in a rule type. The `id` is used to identify which price set to update.
*/
export interface UpdateRuleTypeDTO {
/**
* The ID of the rule type to update.
*/
id: string
/**
* The display name of the rule type.
*/
name?: string
/**
* The unique name used to later identify the rule_attribute. For example, it can be used in the `context` parameter of the `calculatePrices` method to specify a rule for calculating the price.
*/
rule_attribute?: string
/**
* The priority of the rule type. This is useful when calculating the price of a price set, and multiple rules satisfy the provided context. The higher the value, the higher the priority of the rule type.
*/
default_priority?: number
}
/**
* @interface
*
* Filters to apply on rule types.
*/
export interface FilterableRuleTypeProps
extends BaseFilterable<FilterableRuleTypeProps> {
/**
* The IDs to filter rule types by.
*/
id?: string[]
/**
* The names to filter rule types by.
*/
name?: string[]
/**
* The rule attributes to filter rule types by.
*/
rule_attribute?: string[]
}
+4 -229
View File
@@ -9,13 +9,11 @@ import {
CreatePriceListDTO, CreatePriceListDTO,
CreatePriceRuleDTO, CreatePriceRuleDTO,
CreatePriceSetDTO, CreatePriceSetDTO,
CreateRuleTypeDTO,
FilterablePriceListProps, FilterablePriceListProps,
FilterablePriceListRuleProps, FilterablePriceListRuleProps,
FilterablePriceProps, FilterablePriceProps,
FilterablePriceRuleProps, FilterablePriceRuleProps,
FilterablePriceSetProps, FilterablePriceSetProps,
FilterableRuleTypeProps,
PriceDTO, PriceDTO,
PriceListDTO, PriceListDTO,
PriceListRuleDTO, PriceListRuleDTO,
@@ -24,13 +22,11 @@ import {
PricingContext, PricingContext,
PricingFilters, PricingFilters,
RemovePriceListRulesDTO, RemovePriceListRulesDTO,
RuleTypeDTO,
SetPriceListRulesDTO, SetPriceListRulesDTO,
UpdatePriceListDTO, UpdatePriceListDTO,
UpdatePriceListPricesDTO, UpdatePriceListPricesDTO,
UpdatePriceRuleDTO, UpdatePriceRuleDTO,
UpdatePriceSetDTO, UpdatePriceSetDTO,
UpdateRuleTypeDTO,
UpsertPriceSetDTO, UpsertPriceSetDTO,
} from "./common" } from "./common"
@@ -284,10 +280,6 @@ export interface IPricingModuleService extends IModuleService {
* *
* ```ts * ```ts
* const priceSet = await pricingModuleService.createPriceSets({ * const priceSet = await pricingModuleService.createPriceSets({
* rules: [
* { rule_attribute: "region_id" },
* { rule_attribute: "city" },
* ],
* prices: [ * prices: [
* { * {
* amount: 300, * amount: 300,
@@ -351,10 +343,6 @@ export interface IPricingModuleService extends IModuleService {
* }, * },
* // price set with rules * // price set with rules
* { * {
* rules: [
* { rule_attribute: "region_id" },
* { rule_attribute: "city" },
* ],
* prices: [ * prices: [
* { * {
* amount: 300, * amount: 300,
@@ -404,10 +392,6 @@ export interface IPricingModuleService extends IModuleService {
* }, * },
* ], * ],
* }, * },
* {
* id: "pset_123",
* rules: [{ rule_attribute: "region_id" }],
* },
* ]) * ])
*/ */
upsertPriceSets( upsertPriceSets(
@@ -425,7 +409,7 @@ export interface IPricingModuleService extends IModuleService {
* @example * @example
* const priceSet = await pricingModuleService.upsertPriceSets({ * const priceSet = await pricingModuleService.upsertPriceSets({
* id: "pset_123", * id: "pset_123",
* rules: [{ rule_attribute: "region_id" }], * prices: [{ amount: 100, currency_code: "USD" }],
* }) * })
*/ */
upsertPriceSets( upsertPriceSets(
@@ -445,7 +429,7 @@ export interface IPricingModuleService extends IModuleService {
* const priceSet = await pricingModuleService.updatePriceSets( * const priceSet = await pricingModuleService.updatePriceSets(
* "pset_123", * "pset_123",
* { * {
* rules: [{ rule_attribute: "region_id" }], * prices: [{ amount: 100, currency_code: "USD" }],
* } * }
* ) * )
*/ */
@@ -469,7 +453,7 @@ export interface IPricingModuleService extends IModuleService {
* id: ["pset_123", "pset_321"], * id: ["pset_123", "pset_321"],
* }, * },
* { * {
* rules: [{ rule_attribute: "region_id" }], * prices: [{ amount: 100, currency_code: "USD" }],
* } * }
* ) * )
*/ */
@@ -593,215 +577,6 @@ export interface IPricingModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<PriceSetDTO[]> ): Promise<PriceSetDTO[]>
/**
* This method is used to retrieve a rule type by its ID and and optionally based on the provided configurations.
*
* @param {string} id - The ID of the rule type to retrieve.
* @param {FindConfig<RuleTypeDTO>} config -
* The configurations determining how the rule type is retrieved. Its properties, such as `select` or `relations`, accept the
* attributes or relations associated with a rule type.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<RuleTypeDTO>} The retrieved rule type.
*
* @example
* A simple example that retrieves a rule type by its code:
*
* ```ts
* const ruleType =
* await pricingModuleService.retrieveRuleType("rul-typ_123")
* ```
*
* To specify relations that should be retrieved:
*
* ```ts
* const ruleType = await pricingModuleService.retrieveRuleType(
* "rul-typ_123",
* {
* relations: ["price_sets"],
* }
* )
* ```
*/
retrieveRuleType(
id: string,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<RuleTypeDTO>
/**
* This method is used to retrieve a paginated list of rule types based on optional filters and configuration.
*
* @param {FilterableRuleTypeProps} filters - The filters to apply on the retrieved rule types.
* @param {FindConfig<RuleTypeDTO>} config -
* The configurations determining how the rule types are retrieved. Its properties, such as `select` or `relations`, accept the
* attributes or relations associated with a rule type.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<RuleTypeDTO[]>} The list of rule types.
*
* @example
*
* To retrieve a list of rule types using their IDs:
*
* ```ts
* const ruleTypes = await pricingModuleService.listRuleTypes({
* id: ["rul-typ_123", "rul-typ_321"],
* })
* ```
*
* To specify relations that should be retrieved:
*
* ```ts
* const ruleTypes = await pricingModuleService.listRuleTypes(
* {
* id: ["rul-typ_123", "rul-typ_321"],
* },
* {
* relations: ["price_sets"],
* }
* )
* ```
*
* By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter:
*
* ```ts
* const ruleTypes = await pricingModuleService.listRuleTypes(
* {
* id: ["rul-typ_123", "rul-typ_321"],
* },
* {
* relations: ["price_sets"],
* take: 20,
* skip: 2,
* }
* )
* ```
*/
listRuleTypes(
filters?: FilterableRuleTypeProps,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<RuleTypeDTO[]>
/**
* This method is used to retrieve a paginated list of rule types along with the total count of available rule types satisfying the provided filters.
*
* @param {FilterableRuleTypeProps} filters - The filters to apply on the retrieved rule types.
* @param {FindConfig<RuleTypeDTO>} config -
* The configurations determining how the rule types are retrieved. Its properties, such as `select` or `relations`, accept the
* attributes or relations associated with a rule type.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<[RuleTypeDTO[], number]>} The list of rule types along with their total count.
*
* @example
*
* To retrieve a list of rule types using their IDs:
*
* ```ts
* const [ruleTypes, count] =
* await pricingModuleService.listAndCountRuleTypes({
* id: ["rul-typ_123", "rul-typ_321"],
* })
* ```
*
* To specify attributes that should be retrieved within the rule types:
*
* ```ts
* const [ruleTypes, count] =
* await pricingModuleService.listAndCountRuleTypes(
* {
* id: ["rul-typ_123", "rul-typ_321"],
* },
* {
* relations: ["price_sets"],
* }
* )
* ```
*
* By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter:
*
* ```ts
* const [ruleTypes, count] =
* await pricingModuleService.listAndCountRuleTypes(
* {
* id: ["rul-typ_123", "rul-typ_321"],
* },
* {
* relations: ["price_sets"],
* take: 20,
* skip: 2,
* }
* )
* ```
*/
listAndCountRuleTypes(
filters?: FilterableRuleTypeProps,
config?: FindConfig<RuleTypeDTO>,
sharedContext?: Context
): Promise<[RuleTypeDTO[], number]>
/**
* This method is used to create new rule types.
*
* @param {CreateRuleTypeDTO[]} data - The rule types to create.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<RuleTypeDTO[]>} The list of created rule types.
*
* @example
* const ruleTypes = await pricingModuleService.createRuleTypes([
* {
* name: "Region",
* rule_attribute: "region_id",
* },
* {
* name: "Customer Group",
* rule_attribute: "customer_group_id",
* },
* ])
*/
createRuleTypes(
data: CreateRuleTypeDTO[],
sharedContext?: Context
): Promise<RuleTypeDTO[]>
/**
* This method is used to update existing rule types with the provided data.
*
* @param {UpdateRuleTypeDTO[]} data - The rule types to update, each having the attributes that should be updated in a rule type.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<RuleTypeDTO[]>} The list of updated rule types.
*
* @example
* const ruleTypes = await pricingModuleService.updateRuleTypes([
* {
* id: "rul-typ_123",
* name: "Region",
* },
* {
* id: "rul-typ_321",
* name: "Customer Group",
* },
* ])
*/
updateRuleTypes(
data: UpdateRuleTypeDTO[],
sharedContext?: Context
): Promise<RuleTypeDTO[]>
/**
* This method is used to delete rule types based on the provided IDs.
*
* @param {string[]} ruleTypeIds - The IDs of the rule types to delete.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<void>} Resolves once the rule types are deleted.
*
* @example
* const ruleTypes = await pricingModuleService.deleteRuleTypes([
* "rul-typ_123",
* "rul-typ_321",
* ])
*/
deleteRuleTypes(ruleTypeIds: string[], sharedContext?: Context): Promise<void>
/** /**
* This method is used to retrieve a paginated list of prices based on optional filters and configuration. * This method is used to retrieve a paginated list of prices based on optional filters and configuration.
* *
@@ -1119,7 +894,7 @@ export interface IPricingModuleService extends IModuleService {
* await pricingModuleService.createPriceRules([ * await pricingModuleService.createPriceRules([
* { * {
* value: "VIP", * value: "VIP",
* rule_type_id: "rul-typ_123", * attribute: "customer_group",
* price_set_id: "pset_123", * price_set_id: "pset_123",
* }, * },
* ]) * ])
@@ -8,7 +8,6 @@ export interface CreatePriceListDTO {
starts_at?: string starts_at?: string
ends_at?: string ends_at?: string
status?: PriceListStatus status?: PriceListStatus
rules_count?: number
rules?: PriceListRuleDTO[] rules?: PriceListRuleDTO[]
prices?: { prices?: {
amount: number amount: number
@@ -23,7 +22,7 @@ export interface CreatePriceListDTO {
} }
export interface CreatePriceListRuleDTO { export interface CreatePriceListRuleDTO {
rule_attribute: string attribute: string
value: string[] value: string[]
} }
@@ -1,17 +0,0 @@
import { getInvalidRuleAttributes } from ".."
describe("getInvalidRuleAttributes", function () {
it("should return list of rule attributes that matches reserved keywords", function () {
let result = getInvalidRuleAttributes(["shouldnotmatch"])
expect(result).toEqual([])
result = getInvalidRuleAttributes(["currency_code", "shouldnotmatch"])
expect(result).toEqual(["currency_code"])
result = getInvalidRuleAttributes(["currency_code", "price_list_id"])
expect(result).toEqual(["currency_code", "price_list_id"])
result = getInvalidRuleAttributes(["shouldnotmatch", "quantity"])
expect(result).toEqual(["quantity"])
})
})
@@ -1,28 +0,0 @@
import { validateRuleAttributes } from ".."
describe("validateRuleAttributes", function () {
it("should return void if there are no validation errors", function () {
let result = validateRuleAttributes(["shouldpasswithouterrors"])
expect(result).toEqual(undefined)
})
it("should throw an error if one of the array strings matches a reserved keyword", function () {
let error
try {
validateRuleAttributes([
"currency_code",
"shouldnotbepresent",
"quantity",
"price_list_id",
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
"Can't create rule_attribute with reserved keywords [quantity, currency_code, price_list_id] - quantity, currency_code, price_list_id"
)
})
})
+4 -6
View File
@@ -10,11 +10,10 @@ export function buildPriceListRules(
priceListRules?: PriceListRuleDTO[] priceListRules?: PriceListRuleDTO[]
): Record<string, string[]> | undefined { ): Record<string, string[]> | undefined {
return priceListRules?.reduce((acc, curr) => { return priceListRules?.reduce((acc, curr) => {
const ruleAttribute = curr.rule_type.rule_attribute const ruleAttribute = curr.attribute
const ruleValues = curr.price_list_rule_values || [] const ruleValues = curr.value || []
acc[ruleAttribute] = ruleValues.map((ruleValue) => ruleValue.value)
acc[ruleAttribute] = ruleValues
return acc return acc
}, {}) }, {})
} }
@@ -27,11 +26,10 @@ export function buildPriceSetRules(
} }
return priceRules?.reduce((acc, curr) => { return priceRules?.reduce((acc, curr) => {
const ruleAttribute = curr.rule_type.rule_attribute const ruleAttribute = curr.attribute
const ruleValue = curr.value const ruleValue = curr.value
acc[ruleAttribute] = ruleValue acc[ruleAttribute] = ruleValue
return acc return acc
}, {}) }, {})
} }
+2 -14
View File
@@ -2,24 +2,12 @@ import { buildEventNamesFromEntityName } from "../event-bus"
import { Modules } from "../modules-sdk" import { Modules } from "../modules-sdk"
const eventBaseNames: [ const eventBaseNames: [
"priceListRuleValue",
"priceListRule", "priceListRule",
"priceList", "priceList",
"priceRule", "priceRule",
"priceSetRuleType",
"priceSet", "priceSet",
"price", "price"
"ruleType" ] = ["priceListRule", "priceList", "priceRule", "priceSet", "price"]
] = [
"priceListRuleValue",
"priceListRule",
"priceList",
"priceRule",
"priceSetRuleType",
"priceSet",
"price",
"ruleType",
]
export const PricingEvents = buildEventNamesFromEntityName( export const PricingEvents = buildEventNamesFromEntityName(
eventBaseNames, eventBaseNames,
-1
View File
@@ -1,4 +1,3 @@
export * from "./builders" export * from "./builders"
export * from "./price-list" export * from "./price-list"
export * from "./rule-type"
export * from "./events" export * from "./events"
@@ -1,38 +0,0 @@
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(", ")}`
)
}
}
@@ -13,7 +13,7 @@ export const adminPriceListPriceRemoteQueryFields = [
"updated_at", "updated_at",
"price_set.variant.id", "price_set.variant.id",
"price_rules.value", "price_rules.value",
"price_rules.rule_type.rule_attribute", "price_rules.attribute",
] ]
export const adminPriceListRemoteQueryFields = [ export const adminPriceListRemoteQueryFields = [
@@ -27,8 +27,8 @@ export const adminPriceListRemoteQueryFields = [
"created_at", "created_at",
"updated_at", "updated_at",
"deleted_at", "deleted_at",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
...adminPriceListPriceRemoteQueryFields.map((field) => `prices.${field}`), ...adminPriceListPriceRemoteQueryFields.map((field) => `prices.${field}`),
] ]
@@ -1,23 +0,0 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const refetchRuleType = async (
ruleTypeId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "rule_type",
variables: {
filters: { id: ruleTypeId },
},
fields: fields,
})
const ruleTypes = await remoteQuery(queryObject)
return ruleTypes[0]
}
@@ -1,60 +0,0 @@
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { validateAndTransformBody } from "../../utils/validate-body"
import { validateAndTransformQuery } from "../../utils/validate-query"
import * as QueryConfig from "./query-config"
import {
AdminCreatePricingRuleType,
AdminGetPricingRuleTypeParams,
AdminGetPricingRuleTypesParams,
AdminUpdatePricingRuleType,
} from "./validators"
export const adminPricingRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/admin/pricing/rule-types",
middlewares: [
validateAndTransformQuery(
AdminGetPricingRuleTypesParams,
QueryConfig.listTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/pricing/rule-types",
middlewares: [
validateAndTransformBody(AdminCreatePricingRuleType),
validateAndTransformQuery(
AdminGetPricingRuleTypeParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/admin/pricing/rule-types/:id",
middlewares: [
validateAndTransformQuery(
AdminGetPricingRuleTypeParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/pricing/rule-types/:id",
middlewares: [
validateAndTransformBody(AdminUpdatePricingRuleType),
validateAndTransformQuery(
AdminGetPricingRuleTypeParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/admin/pricing/rule-types/:id",
middlewares: [],
},
]
@@ -1,16 +0,0 @@
export const defaultAdminPricingRuleTypeFields = [
"id",
"name",
"rule_attribute",
"default_priority",
]
export const retrieveTransformQueryConfig = {
defaults: defaultAdminPricingRuleTypeFields,
isList: false,
}
export const listTransformQueryConfig = {
...retrieveTransformQueryConfig,
isList: true,
}
@@ -1,74 +0,0 @@
import {
deletePricingRuleTypesWorkflow,
updatePricingRuleTypesWorkflow,
} from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../types/routing"
import {
AdminGetPricingRuleTypeParamsType,
AdminUpdatePricingRuleTypeType,
} from "../../validators"
import { refetchRuleType } from "../../helpers"
import { MedusaError } from "@medusajs/utils"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetPricingRuleTypeParamsType>,
res: MedusaResponse
) => {
const ruleType = await refetchRuleType(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
if (!ruleType) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`RuleType with id: ${req.params.id} was not found`
)
}
res.status(200).json({ rule_type: ruleType })
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdatePricingRuleTypeType>,
res: MedusaResponse
) => {
const workflow = updatePricingRuleTypesWorkflow(req.scope)
await workflow.run({
input: {
data: [{ ...req.validatedBody, id: req.params.id }],
},
})
const ruleType = await refetchRuleType(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
rule_type: ruleType,
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const id = req.params.id
const workflow = deletePricingRuleTypesWorkflow(req.scope)
await workflow.run({
input: { ids: [id] },
})
res.status(200).json({
id,
object: "rule_type",
deleted: true,
})
}
@@ -1,60 +0,0 @@
import { createPricingRuleTypesWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import {
AdminCreatePricingRuleTypeType,
AdminGetPricingRuleTypesParamsType,
} from "../validators"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchRuleType } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetPricingRuleTypesParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "rule_type",
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: rule_types, metadata } = await remoteQuery(queryObject)
res.json({
rule_types: rule_types,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreatePricingRuleTypeType>,
res: MedusaResponse
) => {
const workflow = createPricingRuleTypesWorkflow(req.scope)
const ruleTypesData = [req.validatedBody]
const { result } = await workflow.run({
input: { data: ruleTypesData },
})
const ruleType = await refetchRuleType(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
rule_type: ruleType,
})
}
@@ -1,43 +0,0 @@
import { z } from "zod"
import { createFindParams, createSelectParams } from "../../utils/validators"
export type AdminGetPricingRuleTypeParamsType = z.infer<
typeof AdminGetPricingRuleTypeParams
>
export const AdminGetPricingRuleTypeParams = createSelectParams()
export type AdminGetPricingRuleTypesParamsType = z.infer<
typeof AdminGetPricingRuleTypesParams
>
export const AdminGetPricingRuleTypesParams = createFindParams({
limit: 100,
offset: 0,
}).merge(
z.object({
rule_attribute: z.union([z.string(), z.array(z.string())]).optional(),
$and: z.lazy(() => AdminGetPricingRuleTypesParams.array()).optional(),
$or: z.lazy(() => AdminGetPricingRuleTypesParams.array()).optional(),
})
)
export type AdminCreatePricingRuleTypeType = z.infer<
typeof AdminCreatePricingRuleType
>
export const AdminCreatePricingRuleType = z
.object({
name: z.string(),
rule_attribute: z.string(),
default_priority: z.number(),
})
.strict()
export type AdminUpdatePricingRuleTypeType = z.infer<
typeof AdminUpdatePricingRuleType
>
export const AdminUpdatePricingRuleType = z
.object({
name: z.string().optional(),
rule_attribute: z.string().optional(),
default_priority: z.number().optional(),
})
.strict()
@@ -92,7 +92,7 @@ export const buildRules = (price: PriceDTO) => {
const rules: Record<string, string> = {} const rules: Record<string, string> = {}
for (const priceRule of price.price_rules || []) { for (const priceRule of price.price_rules || []) {
const ruleAttribute = priceRule.rule_type?.rule_attribute const ruleAttribute = priceRule.attribute
if (ruleAttribute) { if (ruleAttribute) {
rules[ruleAttribute] = priceRule.value rules[ruleAttribute] = priceRule.value
@@ -23,7 +23,7 @@ export const defaultAdminProductsVariantFields = [
"barcode", "barcode",
"*prices", "*prices",
"prices.price_rules.value", "prices.price_rules.value",
"prices.price_rules.rule_type.rule_attribute", "prices.price_rules.attribute",
"*options", "*options",
] ]
@@ -85,7 +85,7 @@ export const defaultAdminProductFields = [
"*variants", "*variants",
"*variants.prices", "*variants.prices",
"variants.prices.price_rules.value", "variants.prices.price_rules.value",
"variants.prices.price_rules.rule_type.rule_attribute", "variants.prices.price_rules.attribute",
"*variants.options", "*variants.options",
"*sales_channels", "*sales_channels",
] ]
-2
View File
@@ -14,7 +14,6 @@ import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
import { adminOrderRoutesMiddlewares } from "./admin/orders/middlewares" import { adminOrderRoutesMiddlewares } from "./admin/orders/middlewares"
import { adminPaymentRoutesMiddlewares } from "./admin/payments/middlewares" import { adminPaymentRoutesMiddlewares } from "./admin/payments/middlewares"
import { adminPriceListsRoutesMiddlewares } from "./admin/price-lists/middlewares" import { adminPriceListsRoutesMiddlewares } from "./admin/price-lists/middlewares"
import { adminPricingRoutesMiddlewares } from "./admin/pricing/middlewares"
import { adminProductCategoryRoutesMiddlewares } from "./admin/product-categories/middlewares" import { adminProductCategoryRoutesMiddlewares } from "./admin/product-categories/middlewares"
import { adminProductTypeRoutesMiddlewares } from "./admin/product-types/middlewares" import { adminProductTypeRoutesMiddlewares } from "./admin/product-types/middlewares"
import { adminProductTagRoutesMiddlewares } from "./admin/product-tags/middlewares" import { adminProductTagRoutesMiddlewares } from "./admin/product-tags/middlewares"
@@ -81,7 +80,6 @@ export const config: MiddlewaresConfig = {
...adminPriceListsRoutesMiddlewares, ...adminPriceListsRoutesMiddlewares,
...adminInventoryRoutesMiddlewares, ...adminInventoryRoutesMiddlewares,
...adminCollectionRoutesMiddlewares, ...adminCollectionRoutesMiddlewares,
...adminPricingRoutesMiddlewares,
...adminShippingOptionRoutesMiddlewares, ...adminShippingOptionRoutesMiddlewares,
...adminDraftOrderRoutesMiddlewares, ...adminDraftOrderRoutesMiddlewares,
...adminSalesChannelRoutesMiddlewares, ...adminSalesChannelRoutesMiddlewares,
@@ -2,11 +2,13 @@ export const defaultPriceListRuleData = [
{ {
id: "price-list-rule-1", id: "price-list-rule-1",
price_list_id: "price-list-1", price_list_id: "price-list-1",
rule_type_id: "rule-type-1", attribute: "currency_code",
value: [],
}, },
{ {
id: "price-list-rule-2", id: "price-list-rule-2",
price_list_id: "price-list-1", price_list_id: "price-list-1",
rule_type_id: "rule-type-2", attribute: "region_id",
value: [],
}, },
] ]
@@ -6,7 +6,7 @@ export const defaultPriceRuleData = [
{ {
id: "price-rule-1", id: "price-rule-1",
price_set_id: "price-set-1", price_set_id: "price-set-1",
rule_type_id: "rule-type-1", attribute: "currency_code",
value: "USD", value: "USD",
price_list_id: "test", price_list_id: "test",
price_id: "price-set-money-amount-USD", price_id: "price-set-money-amount-USD",
@@ -14,7 +14,7 @@ export const defaultPriceRuleData = [
{ {
id: "price-rule-2", id: "price-rule-2",
price_set_id: "price-set-2", price_set_id: "price-set-2",
rule_type_id: "rule-type-2", attribute: "region_id",
value: "region_1", value: "region_1",
price_list_id: "test", price_list_id: "test",
price_id: "price-set-money-amount-EUR", price_id: "price-set-money-amount-EUR",
@@ -16,7 +16,7 @@ export async function createPriceRules(
const priceRuleDataClone: CreatePriceRuleDTO = { ...priceRuleData } const priceRuleDataClone: CreatePriceRuleDTO = { ...priceRuleData }
priceRuleDataClone.price_set_id = priceRuleDataClone.price_set_id priceRuleDataClone.price_set_id = priceRuleDataClone.price_set_id
priceRuleDataClone.rule_type_id = priceRuleDataClone.rule_type_id priceRuleDataClone.attribute = priceRuleDataClone.attribute
priceRuleDataClone.price_id = priceRuleDataClone.price_id priceRuleDataClone.price_id = priceRuleDataClone.price_id
const priceRule = manager.create(PriceRule, priceRuleDataClone) const priceRule = manager.create(PriceRule, priceRuleDataClone)
@@ -1,12 +0,0 @@
export const defaultRuleTypesData = [
{
id: "rule-type-1",
name: "rule 1",
rule_attribute: "currency_code",
},
{
id: "rule-type-2",
name: "rule 2",
rule_attribute: "region_id",
},
]
@@ -1,22 +0,0 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { RuleType } from "@models"
import { defaultRuleTypesData } from "./data"
export * 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
}
@@ -2,7 +2,6 @@ import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createPrices, defaultPricesData } from "./price" import { createPrices, defaultPricesData } from "./price"
import { createPriceRules, defaultPriceRuleData } from "./price-rule" import { createPriceRules, defaultPriceRuleData } from "./price-rule"
import { createPriceSets, defaultPriceSetsData } from "./price-set" import { createPriceSets, defaultPriceSetsData } from "./price-set"
import { createRuleTypes, defaultRuleTypesData } from "./rule-type"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -12,11 +11,9 @@ export async function seedPriceData(
priceSetsData = defaultPriceSetsData, priceSetsData = defaultPriceSetsData,
priceRuleData = defaultPriceRuleData, priceRuleData = defaultPriceRuleData,
pricesData = defaultPricesData, pricesData = defaultPricesData,
ruleTypesData = defaultRuleTypesData,
} = {} } = {}
) { ) {
await createPriceSets(testManager, priceSetsData) await createPriceSets(testManager, priceSetsData)
await createPrices(testManager, pricesData) await createPrices(testManager, pricesData)
await createRuleTypes(testManager, ruleTypesData)
await createPriceRules(testManager, priceRuleData) await createPriceRules(testManager, priceRuleData)
} }
@@ -154,32 +154,11 @@ moduleIntegrationTestRunner<IPricingModuleService>({
}, },
] ]
const ruleTypesData = [
{
id: "rule-type-company_id",
name: "rule type company id",
rule_attribute: "company_id",
default_priority: 2,
},
{
id: "rule-type-region_id",
name: "rule type region id",
rule_attribute: "region_id",
default_priority: 1,
},
{
id: "rule-type-customer_group_id",
name: "rule type customer group id",
rule_attribute: "customer_group_id",
default_priority: 3,
},
]
const priceRuleData = [ const priceRuleData = [
{ {
id: "price-rule-company_id-EUR", id: "price-rule-company_id-EUR",
price_set_id: "price-set-EUR", price_set_id: "price-set-EUR",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "EUR", value: "EUR",
price_list_id: "test", price_list_id: "test",
price_id: "price-company_id-EUR", price_id: "price-company_id-EUR",
@@ -187,7 +166,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-company_id-PLN", id: "price-rule-company_id-PLN",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "medusa-company-id", value: "medusa-company-id",
price_list_id: "test", price_list_id: "test",
price_id: "price-company_id-PLN", price_id: "price-company_id-PLN",
@@ -195,7 +174,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-PLN", id: "price-rule-region_id-PLN",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id-PLN", price_id: "price-region_id-PLN",
@@ -203,7 +182,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id+company_id-PL", id: "price-rule-region_id+company_id-PL",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id+company_id-PLN", price_id: "price-region_id+company_id-PLN",
@@ -211,7 +190,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id+company_id-medusa-company-id", id: "price-rule-region_id+company_id-medusa-company-id",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "medusa-company-id", value: "medusa-company-id",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id+company_id-PLN", price_id: "price-region_id+company_id-PLN",
@@ -219,7 +198,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-PLN-5-qty", id: "price-rule-region_id-PLN-5-qty",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id-PLN-5-qty", price_id: "price-region_id-PLN-5-qty",
@@ -227,7 +206,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-company_id-PL", id: "price-rule-region_id-company_id-PL",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR", price_id: "price-region_id_company_id-PL-EUR",
@@ -235,7 +214,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-company_id-PLN", id: "price-rule-region_id-company_id-PLN",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "medusa-company-id", value: "medusa-company-id",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR", price_id: "price-region_id_company_id-PL-EUR",
@@ -243,7 +222,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-company_id-PL-4-qty", id: "price-rule-region_id-company_id-PL-4-qty",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR-4-qty", price_id: "price-region_id_company_id-PL-EUR-4-qty",
@@ -251,7 +230,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-company_id-PLN-4-qty", id: "price-rule-region_id-company_id-PLN-4-qty",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "medusa-company-id", value: "medusa-company-id",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR-4-qty", price_id: "price-region_id_company_id-PL-EUR-4-qty",
@@ -259,7 +238,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-currency_customer_group_code-PL", id: "price-rule-region_id-currency_customer_group_code-PL",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-region_id", attribute: "region_id",
value: "PL", value: "PL",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR-customer-group", price_id: "price-region_id_company_id-PL-EUR-customer-group",
@@ -267,7 +246,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-currency_customer_group_code-PLN", id: "price-rule-region_id-currency_customer_group_code-PLN",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-company_id", attribute: "company_id",
value: "medusa-company-id", value: "medusa-company-id",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR-customer-group", price_id: "price-region_id_company_id-PL-EUR-customer-group",
@@ -275,7 +254,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
{ {
id: "price-rule-region_id-currency_customer_group_code-test_customer_group", id: "price-rule-region_id-currency_customer_group_code-test_customer_group",
price_set_id: "price-set-PLN", price_set_id: "price-set-PLN",
rule_type_id: "rule-type-customer_group_id", attribute: "customer_group_id",
value: "test-customer-group", value: "test-customer-group",
price_list_id: "test", price_list_id: "test",
price_id: "price-region_id_company_id-PL-EUR-customer-group", price_id: "price-region_id_company_id-PL-EUR-customer-group",
@@ -286,7 +265,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
priceSetsData, priceSetsData,
pricesData, pricesData,
priceRuleData, priceRuleData,
ruleTypesData,
}) })
}) })
@@ -3,7 +3,6 @@ import { SqlEntityManager } from "@mikro-orm/postgresql"
import { moduleIntegrationTestRunner } from "medusa-test-utils" import { moduleIntegrationTestRunner } from "medusa-test-utils"
import { createPriceLists } from "../../../__fixtures__/price-list" import { createPriceLists } from "../../../__fixtures__/price-list"
import { createPriceListRules } from "../../../__fixtures__/price-list-rules" import { createPriceListRules } from "../../../__fixtures__/price-list-rules"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
import { Modules } from "@medusajs/utils" import { Modules } from "@medusajs/utils"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -15,7 +14,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
let testManager: SqlEntityManager let testManager: SqlEntityManager
beforeEach(async () => { beforeEach(async () => {
testManager = await MikroOrmWrapper.forkManager() testManager = await MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
await createPriceLists(testManager) await createPriceLists(testManager)
await createPriceListRules(testManager) await createPriceListRules(testManager)
}) })
@@ -166,14 +164,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
describe("setPriceListRules", () => { describe("setPriceListRules", () => {
it("should add a price list rule to a price list", async () => { it("should add a price list rule to a price list", async () => {
await createRuleTypes(testManager, [
{
id: "rule-type-3",
name: "test",
rule_attribute: "sales_channel",
},
])
await service.setPriceListRules({ await service.setPriceListRules({
price_list_id: "price-list-1", price_list_id: "price-list-1",
rules: { rules: {
@@ -184,34 +174,21 @@ moduleIntegrationTestRunner<IPricingModuleService>({
const [priceList] = await service.listPriceLists( const [priceList] = await service.listPriceLists(
{ id: ["price-list-1"] }, { id: ["price-list-1"] },
{ {
relations: [ relations: ["price_list_rules"],
"price_list_rules",
"price_list_rules.price_list_rule_values",
],
} }
) )
expect(priceList.price_list_rules).toEqual( expect(priceList.price_list_rules).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
rule_type: { id: "rule-type-3" }, attribute: "sales_channel",
price_list_rule_values: [ value: "sc-1",
expect.objectContaining({ value: "sc-1" }),
],
}), }),
]) ])
) )
}) })
it("should multiple priceListRules to a priceList", async () => { it("should multiple priceListRules to a priceList", async () => {
await createRuleTypes(testManager, [
{
id: "rule-type-3",
name: "test",
rule_attribute: "sales_channel",
},
])
await service.setPriceListRules({ await service.setPriceListRules({
price_list_id: "price-list-1", price_list_id: "price-list-1",
rules: { rules: {
@@ -224,21 +201,15 @@ moduleIntegrationTestRunner<IPricingModuleService>({
id: ["price-list-1"], id: ["price-list-1"],
}, },
{ {
relations: [ relations: ["price_list_rules"],
"price_list_rules",
"price_list_rules.price_list_rule_values",
],
} }
) )
expect(priceList.price_list_rules).toEqual( expect(priceList.price_list_rules).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
rule_type: { id: "rule-type-3" }, attribute: "sales_channel",
price_list_rule_values: expect.arrayContaining([ value: ["sc-1", "sc-2"],
expect.objectContaining({ value: "sc-1" }),
expect.objectContaining({ value: "sc-2" }),
]),
}), }),
]) ])
) )
@@ -262,7 +233,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
) )
expect(priceList.price_list_rules).toEqual([ expect(priceList.price_list_rules).toEqual([
expect.objectContaining({ rule_type: { id: "rule-type-2" } }), expect.objectContaining({ attribute: "region_id" }),
]) ])
}) })
}) })
@@ -32,16 +32,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
const testManager = await MikroOrmWrapper.forkManager() const testManager = await MikroOrmWrapper.forkManager()
await createPriceSets(testManager) await createPriceSets(testManager)
await createPriceLists(testManager) await createPriceLists(testManager)
await service.createRuleTypes([
{
name: "Region ID",
rule_attribute: "region_id",
},
{
name: "Customer Group ID",
rule_attribute: "customer_group_id",
},
])
}) })
describe("list", () => { describe("list", () => {
@@ -287,20 +277,15 @@ moduleIntegrationTestRunner<IPricingModuleService>({
const [priceList] = await service.listPriceLists( const [priceList] = await service.listPriceLists(
{ id: [createdId] }, { id: [createdId] },
{ {
relations: [ relations: ["prices", "price_list_rules"],
"prices",
"prices.price_set",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
],
select: [ select: [
"id", "id",
"starts_at", "starts_at",
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -317,17 +302,8 @@ moduleIntegrationTestRunner<IPricingModuleService>({
]), ]),
price_list_rules: expect.arrayContaining([ price_list_rules: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), attribute: "new_rule",
rule_type: expect.objectContaining({ value: ["new-rule-value"],
id: expect.any(String),
rule_attribute: "new_rule",
}),
price_list_rule_values: [
expect.objectContaining({
id: expect.any(String),
value: "new-rule-value",
}),
],
}), }),
]), ]),
}) })
@@ -349,7 +325,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
} }
expect(error.message).toEqual( expect(error.message).toEqual(
'PriceList with id "does-not-exist" not found' "Price lists with ids: 'does-not-exist' not found"
) )
}) })
}) })
@@ -439,19 +415,14 @@ moduleIntegrationTestRunner<IPricingModuleService>({
id: [created.id], id: [created.id],
}, },
{ {
relations: [ relations: ["prices", "prices.price_set", "price_list_rules"],
"prices",
"prices.price_set",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
],
select: [ select: [
"id", "id",
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -468,37 +439,16 @@ moduleIntegrationTestRunner<IPricingModuleService>({
price_list_rules: expect.arrayContaining([ price_list_rules: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
rule_type: expect.objectContaining({ attribute: "customer_group_id",
id: expect.any(String), value: [
rule_attribute: "customer_group_id", "vip-customer-group-id",
}), "another-vip-customer-group-id",
price_list_rule_values: expect.arrayContaining([ ],
expect.objectContaining({
id: expect.any(String),
value: "vip-customer-group-id",
}), }),
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
value: "another-vip-customer-group-id", attribute: "region_id",
}), value: ["DE", "DK"],
]),
}),
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",
}),
]),
}), }),
]), ]),
}) })
@@ -581,8 +531,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices", "prices",
"prices.price_set", "prices.price_set",
"prices.price_rules", "prices.price_rules",
"price_list_rules.price_list_rule_values", "price_list_rules",
"price_list_rules.rule_type",
], ],
select: [ select: [
"id", "id",
@@ -591,8 +540,8 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -622,70 +571,19 @@ moduleIntegrationTestRunner<IPricingModuleService>({
price_list_rules: expect.arrayContaining([ price_list_rules: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
rule_type: expect.objectContaining({ attribute: "customer_group_id",
id: expect.any(String), value: [
rule_attribute: "customer_group_id", "vip-customer-group-id",
}), "another-vip-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",
}),
]),
}),
]),
})
)
})
it("should throw error when rule type does not exist", async () => {
const error = await service
.createPriceLists([
{
title: "test",
description: "test",
rules: {
region_id: ["DE", "DK"],
missing_1: ["test-missing-1"],
},
prices: [
{
amount: 400,
currency_code: "EUR",
price_set_id: "price-set-1",
rules: {
region_id: "DE",
missing_2: "test-missing-2",
},
},
], ],
}, }),
]) expect.objectContaining({
.catch((e) => e) id: expect.any(String),
attribute: "region_id",
expect(error.message).toEqual( value: ["DE", "DK"],
"Cannot find RuleTypes with rule_attribute - missing_1, missing_2" }),
]),
})
) )
}) })
}) })
@@ -714,8 +612,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices", "prices",
"prices.price_set", "prices.price_set",
"prices.price_rules", "prices.price_rules",
"price_list_rules.price_list_rule_values", "price_list_rules",
"price_list_rules.rule_type",
], ],
select: [ select: [
"id", "id",
@@ -724,8 +621,8 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -746,13 +643,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
}) })
it("should add a price with rules to a priceList successfully", async () => { it("should add a price with rules to a priceList successfully", async () => {
await service.createRuleTypes([
{
name: "region_id",
rule_attribute: "region_id",
},
])
await service.addPriceListPrices([ await service.addPriceListPrices([
{ {
price_list_id: "price-list-1", price_list_id: "price-list-1",
@@ -778,20 +668,18 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices", "prices",
"prices.price_set", "prices.price_set",
"prices.price_rules", "prices.price_rules",
"prices.price_rules.rule_type", "price_list_rules",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
], ],
select: [ select: [
"id", "id",
"prices.price_rules.value", "prices.price_rules.value",
"prices.price_rules.rule_type.rule_attribute", "prices.price_rules.attribute",
"prices.rules_count", "prices.rules_count",
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -805,9 +693,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
price_rules: [ price_rules: [
expect.objectContaining({ expect.objectContaining({
value: "EU", value: "EU",
rule_type: expect.objectContaining({ attribute: "region_id",
rule_attribute: "region_id",
}),
}), }),
], ],
amount: 123, amount: 123,
@@ -884,20 +770,18 @@ moduleIntegrationTestRunner<IPricingModuleService>({
"prices", "prices",
"prices.price_set", "prices.price_set",
"prices.price_rules", "prices.price_rules",
"prices.price_rules.rule_type", "price_list_rules",
"price_list_rules.price_list_rule_values",
"price_list_rules.rule_type",
], ],
select: [ select: [
"id", "id",
"prices.price_rules.value", "prices.price_rules.value",
"prices.price_rules.rule_type.rule_attribute", "prices.price_rules.attribute",
"prices.rules_count", "prices.rules_count",
"prices.amount", "prices.amount",
"prices.currency_code", "prices.currency_code",
"prices.price_list_id", "prices.price_list_id",
"price_list_rules.price_list_rule_values.value", "price_list_rules.value",
"price_list_rules.rule_type.rule_attribute", "price_list_rules.attribute",
], ],
} }
) )
@@ -911,15 +795,11 @@ moduleIntegrationTestRunner<IPricingModuleService>({
price_rules: expect.arrayContaining([ price_rules: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
value: "new test", value: "new test",
rule_type: expect.objectContaining({ attribute: "region_id",
rule_attribute: "region_id",
}),
}), }),
expect.objectContaining({ expect.objectContaining({
value: "new test", value: "new test",
rule_type: expect.objectContaining({ attribute: "customer_group_id",
rule_attribute: "customer_group_id",
}),
}), }),
]), ]),
amount: 123, amount: 123,
@@ -934,9 +814,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
describe("removePrices", () => { describe("removePrices", () => {
it("should remove prices from a priceList successfully", async () => { it("should remove prices from a priceList successfully", async () => {
const [priceSet] = await service.createPriceSets([ const [priceSet] = await service.createPriceSets([{}])
{ rules: [{ rule_attribute: "region_id" }] },
])
await service.addPriceListPrices([ await service.addPriceListPrices([
{ {
@@ -5,7 +5,6 @@ import { Price } from "../../../../src/models"
import { createPrices } from "../../../__fixtures__/price" import { createPrices } from "../../../__fixtures__/price"
import { createPriceRules } from "../../../__fixtures__/price-rule" import { createPriceRules } from "../../../__fixtures__/price-rule"
import { createPriceSets } from "../../../__fixtures__/price-set" import { createPriceSets } from "../../../__fixtures__/price-set"
import { createRuleTypes } from "../../../__fixtures__/rule-type"
import { Modules } from "@medusajs/utils" import { Modules } from "@medusajs/utils"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -19,7 +18,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
testManager = await MikroOrmWrapper.forkManager() testManager = await MikroOrmWrapper.forkManager()
await createPriceSets(testManager) await createPriceSets(testManager)
await createRuleTypes(testManager)
await createPrices(testManager) await createPrices(testManager)
await createPriceRules(testManager) await createPriceRules(testManager)
}) })
@@ -57,8 +55,8 @@ moduleIntegrationTestRunner<IPricingModuleService>({
id: ["price-rule-1"], id: ["price-rule-1"],
}, },
{ {
select: ["id", "price_set.id"], select: ["id", "price.id"],
relations: ["price_set"], relations: ["price"],
} }
) )
@@ -67,10 +65,10 @@ moduleIntegrationTestRunner<IPricingModuleService>({
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "price-rule-1", id: "price-rule-1",
price_set: { price: {
id: "price-set-1", id: "price-set-money-amount-USD",
}, },
price_set_id: "price-set-1", price_id: "price-set-money-amount-USD",
}, },
]) ])
}) })
@@ -112,8 +110,8 @@ moduleIntegrationTestRunner<IPricingModuleService>({
id: ["price-rule-1"], id: ["price-rule-1"],
}, },
{ {
select: ["id", "price_set.id"], select: ["id", "price.id"],
relations: ["price_set"], relations: ["price"],
} }
) )
@@ -123,10 +121,10 @@ moduleIntegrationTestRunner<IPricingModuleService>({
expect(serialized).toEqual([ expect(serialized).toEqual([
{ {
id: "price-rule-1", id: "price-rule-1",
price_set: { price: {
id: "price-set-1", id: "price-set-money-amount-USD",
}, },
price_set_id: "price-set-1", price_id: "price-set-money-amount-USD",
}, },
]) ])
}) })
@@ -283,7 +281,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
await service.createPriceRules({ await service.createPriceRules({
id: "price-rule-new", id: "price-rule-new",
price_set_id: "price-set-1", price_set_id: "price-set-1",
rule_type_id: "rule-type-1", attribute: "region_id",
value: "region_1", value: "region_1",
price_list_id: "test", price_list_id: "test",
price_id: price.id, price_id: price.id,
@@ -1,39 +1,18 @@
import { import { CreatePriceSetDTO, IPricingModuleService } from "@medusajs/types"
CreatePriceSetDTO,
CreatePriceSetRuleTypeDTO,
IPricingModuleService,
} from "@medusajs/types"
import { import {
CommonEvents, CommonEvents,
composeMessage, composeMessage,
Modules, Modules,
PricingEvents, PricingEvents,
} from "@medusajs/utils" } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { import {
MockEventBusService, MockEventBusService,
moduleIntegrationTestRunner, moduleIntegrationTestRunner,
} from "medusa-test-utils" } from "medusa-test-utils"
import { PriceSetRuleType } from "../../../../src/models"
import { seedPriceData } from "../../../__fixtures__/seed-price-data" import { seedPriceData } from "../../../__fixtures__/seed-price-data"
jest.setTimeout(30000) jest.setTimeout(30000)
async function createPriceSetPriceRules(
manager: SqlEntityManager,
priceSetRulesData: CreatePriceSetRuleTypeDTO[]
): Promise<void> {
const priceSetRules: PriceSetRuleType[] = []
for (let priceSetRuleData of priceSetRulesData) {
const priceRule = manager.create(PriceSetRuleType, priceSetRuleData as any)
priceSetRules.push(priceRule)
}
await manager.persistAndFlush(priceSetRules)
}
moduleIntegrationTestRunner<IPricingModuleService>({ moduleIntegrationTestRunner<IPricingModuleService>({
moduleName: Modules.PRICING, moduleName: Modules.PRICING,
testSuite: ({ MikroOrmWrapper, service }) => { testSuite: ({ MikroOrmWrapper, service }) => {
@@ -51,16 +30,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
beforeEach(async () => { beforeEach(async () => {
const testManager = await MikroOrmWrapper.forkManager() const testManager = await MikroOrmWrapper.forkManager()
await seedPriceData(testManager) await seedPriceData(testManager)
await createPriceSetPriceRules(testManager, [
{
price_set_id: "price-set-1",
rule_type_id: "rule-type-1",
},
{
price_set_id: "price-set-2",
rule_type_id: "rule-type-2",
},
])
}) })
describe("list", () => { describe("list", () => {
@@ -346,32 +315,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
}) })
describe("create", () => { describe("create", () => {
it("should fail to create a price set with rule types and money amounts with rule types that don't exits", async () => {
let error
try {
await service.createPriceSets([
{
rules: [{ rule_attribute: "region_id" }],
prices: [
{
amount: 100,
currency_code: "USD",
rules: {
city: "Berlin",
},
},
],
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
"Rule types don't exist for prices with rule attribute: city"
)
})
it("should create a price set with rule types and money amounts", async () => { it("should create a price set with rule types and money amounts", async () => {
const [priceSet] = await service.createPriceSets([ const [priceSet] = await service.createPriceSets([
{ {
@@ -399,10 +342,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
}) })
) )
const [priceRules] = await service.listPriceRules({
price_set_id: [priceSet.id],
})
const events = eventBusEmitSpy.mock.calls[0][0] const events = eventBusEmitSpy.mock.calls[0][0]
expect(events).toHaveLength(3) expect(events).toHaveLength(3)
expect(events[0]).toEqual( expect(events[0]).toEqual(
@@ -429,7 +368,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
action: CommonEvents.CREATED, action: CommonEvents.CREATED,
object: "price_rule", object: "price_rule",
data: { data: {
id: priceRules.id, id: (priceSet as any).prices![0].price_rules[0].id,
}, },
}) })
) )
@@ -591,26 +530,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
}), }),
]) ])
}) })
it("should fail with an appropriate error when trying to add a price with rule that doesn't exist", async () => {
let error
try {
await service.addPrices({
priceSetId: "price-set-1",
prices: [
{
amount: 100,
currency_code: "USD",
rules: { city: "Paris" },
},
],
})
} catch (e) {
error = e
}
expect(error.message).toEqual("Rule types don't exist for: city")
})
}) })
}) })
}, },
@@ -1,239 +0,0 @@
import { createRuleTypes } from "../../../__fixtures__/rule-type"
import { moduleIntegrationTestRunner } from "medusa-test-utils"
import { IPricingModuleService } from "@medusajs/types"
import { Modules } from "@medusajs/utils"
jest.setTimeout(30000)
moduleIntegrationTestRunner<IPricingModuleService>({
moduleName: Modules.PRICING,
testSuite: ({ MikroOrmWrapper, service }) => {
describe("PricingModuleService ruleType", () => {
beforeEach(async () => {
const testManager = MikroOrmWrapper.forkManager()
await createRuleTypes(testManager)
})
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("ruleType - id 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",
})
)
})
})
})
},
})
@@ -4,15 +4,14 @@ import {
MapToConfig, MapToConfig,
Modules, Modules,
} from "@medusajs/utils" } from "@medusajs/utils"
import { Price, PriceList, PriceSet, RuleType } from "@models" import { Price, PriceList, PriceSet } from "@models"
export const joinerConfig = defineJoinerConfig(Modules.PRICING, { export const joinerConfig = defineJoinerConfig(Modules.PRICING, {
entityQueryingConfig: [PriceSet, PriceList, Price, RuleType], entityQueryingConfig: [PriceSet, PriceList, Price],
linkableKeys: { linkableKeys: {
price_set_id: PriceSet.name, price_set_id: PriceSet.name,
price_list_id: PriceList.name, price_list_id: PriceList.name,
price_id: Price.name, price_id: Price.name,
rule_type_id: RuleType.name,
}, },
}) })
@@ -150,6 +150,127 @@
"checks": [], "checks": [],
"foreignKeys": {} "foreignKeys": {}
}, },
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"attribute": {
"name": "attribute",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"value": {
"name": "value",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"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_rule",
"schema": "public",
"indexes": [
{
"keyName": "IDX_price_list_rule_price_list_id",
"columnNames": [
"price_list_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_price_list_id\" ON \"price_list_rule\" (price_list_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_list_rule_deleted_at",
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_deleted_at\" ON \"price_list_rule\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"keyName": "price_list_rule_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"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",
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
{ {
"columns": { "columns": {
"id": { "id": {
@@ -440,260 +561,8 @@
"nullable": false, "nullable": false,
"mappedType": "text" "mappedType": "text"
}, },
"name": { "attribute": {
"name": "name", "name": "attribute",
"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"
},
"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": "rule_type",
"schema": "public",
"indexes": [
{
"keyName": "IDX_rule_type_rule_attribute",
"columnNames": [
"rule_attribute"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rule_type_rule_attribute\" ON \"rule_type\" (rule_attribute) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_rule_type_deleted_at",
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rule_type_deleted_at\" ON \"rule_type\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"keyName": "rule_type_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
"name": "id",
"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": false,
"mappedType": "text"
},
"rule_type_id": {
"name": "rule_type_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"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_set_rule_type",
"schema": "public",
"indexes": [
{
"keyName": "IDX_price_set_rule_type_price_set_id",
"columnNames": [
"price_set_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_set_rule_type_price_set_id\" ON \"price_set_rule_type\" (price_set_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_set_rule_type_rule_type_id",
"columnNames": [
"rule_type_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_set_rule_type_rule_type_id\" ON \"price_set_rule_type\" (rule_type_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_set_rule_type_deleted_at",
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_set_rule_type_deleted_at\" ON \"price_set_rule_type\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"keyName": "price_set_rule_type_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"price_set_rule_type_price_set_id_foreign": {
"constraintName": "price_set_rule_type_price_set_id_foreign",
"columnNames": [
"price_set_id"
],
"localTableName": "public.price_set_rule_type",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_set_rule_type_rule_type_id_foreign": {
"constraintName": "price_set_rule_type_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"localTableName": "public.price_set_rule_type",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.rule_type",
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
{
"columns": {
"id": {
"name": "id",
"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": false,
"mappedType": "text"
},
"rule_type_id": {
"name": "rule_type_id",
"type": "text", "type": "text",
"unsigned": false, "unsigned": false,
"autoincrement": false, "autoincrement": false,
@@ -766,34 +635,14 @@
"schema": "public", "schema": "public",
"indexes": [ "indexes": [
{ {
"keyName": "IDX_price_rule_price_set_id", "keyName": "IDX_price_rule_price_id_attribute_unique",
"columnNames": [
"price_set_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_rule_price_set_id\" ON \"price_rule\" (price_set_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_rule_rule_type_id",
"columnNames": [
"rule_type_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_rule_rule_type_id\" ON \"price_rule\" (rule_type_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_rule_price_id_rule_type_id_unique",
"columnNames": [ "columnNames": [
"price_id" "price_id"
], ],
"composite": false, "composite": false,
"primary": false, "primary": false,
"unique": false, "unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_price_rule_price_id_rule_type_id_unique\" ON \"price_rule\" (price_id, rule_type_id) WHERE deleted_at IS NULL" "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_price_rule_price_id_attribute_unique\" ON \"price_rule\" (price_id, attribute) WHERE deleted_at IS NULL"
}, },
{ {
"keyName": "IDX_price_rule_deleted_at", "keyName": "IDX_price_rule_deleted_at",
@@ -817,31 +666,6 @@
], ],
"checks": [], "checks": [],
"foreignKeys": { "foreignKeys": {
"price_rule_price_set_id_foreign": {
"constraintName": "price_rule_price_set_id_foreign",
"columnNames": [
"price_set_id"
],
"localTableName": "public.price_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.price_set",
"deleteRule": "cascade",
"updateRule": "cascade"
},
"price_rule_rule_type_id_foreign": {
"constraintName": "price_rule_rule_type_id_foreign",
"columnNames": [
"rule_type_id"
],
"localTableName": "public.price_rule",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.rule_type",
"updateRule": "cascade"
},
"price_rule_price_id_foreign": { "price_rule_price_id_foreign": {
"constraintName": "price_rule_price_id_foreign", "constraintName": "price_rule_price_id_foreign",
"columnNames": [ "columnNames": [
@@ -856,252 +680,6 @@
"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"
},
"price_list_id": {
"name": "price_list_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"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_rule",
"schema": "public",
"indexes": [
{
"keyName": "IDX_price_list_rule_rule_type_id_unique",
"columnNames": [
"rule_type_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_price_list_rule_rule_type_id_unique\" ON \"price_list_rule\" (rule_type_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_list_rule_price_list_id",
"columnNames": [
"price_list_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_price_list_id\" ON \"price_list_rule\" (price_list_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_list_rule_deleted_at",
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_deleted_at\" ON \"price_list_rule\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"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",
"deleteRule": "cascade",
"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"
},
"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_rule_value",
"schema": "public",
"indexes": [
{
"keyName": "IDX_price_list_rule_value_price_list_rule_id",
"columnNames": [
"price_list_rule_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_value_price_list_rule_id\" ON \"price_list_rule_value\" (price_list_rule_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_price_list_rule_value_deleted_at",
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_price_list_rule_value_deleted_at\" ON \"price_list_rule_value\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"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"
}
}
} }
] ]
} }
@@ -0,0 +1,72 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20240626133555 extends Migration {
async up(): Promise<void> {
this.addSql(
'alter table if exists "price_list_rule" add column if not exists "value" jsonb;'
)
// TODO: Added on 28.06.2024, Drop defaults after a while.
this.addSql(
'alter table if exists "price_list_rule" add column if not exists "attribute" text not null DEFAULT \'\';'
)
this.addSql(
'alter table if exists "price_rule" add column if not exists "attribute" text not null DEFAULT \'\';'
)
/* DATA MIGRATION */
this.addSql(
"update price_rule set attribute = (SELECT rule_attribute FROM rule_type WHERE rule_type.id = price_rule.rule_type_id);"
)
this.addSql(
"update price_list_rule set value = (SELECT array_to_json(ARRAY(SELECT value FROM price_list_rule_value WHERE price_list_rule_value.price_list_rule_id = price_list_rule.id))::jsonb);"
)
this.addSql(
"update price_list_rule set attribute = (SELECT rule_attribute FROM rule_type WHERE rule_type.id = price_list_rule.rule_type_id);"
)
/* DATA MIGRATION END */
this.addSql(
'alter table if exists "price_set_rule_type" drop constraint if exists "price_set_rule_type_rule_type_id_foreign";'
)
this.addSql(
'alter table if exists "price_rule" drop constraint if exists "price_rule_rule_type_id_foreign";'
)
this.addSql(
'alter table if exists "price_list_rule" drop constraint if exists "price_list_rule_rule_type_id_foreign";'
)
this.addSql(
'alter table if exists "price_rule" drop constraint if exists "price_rule_price_set_id_foreign";'
)
this.addSql(
'drop index if exists "IDX_price_list_rule_rule_type_id_unique";'
)
this.addSql('drop index if exists "IDX_price_rule_price_set_id";')
this.addSql('drop index if exists "IDX_price_rule_rule_type_id";')
this.addSql('drop index if exists "IDX_price_rule_price_id_unique";')
this.addSql(
'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_price_rule_price_id_attribute_unique" ON "price_rule" (price_id, attribute) WHERE deleted_at IS NULL;'
)
this.addSql(
'alter table if exists "price_rule" drop column if exists "price_set_id";'
)
this.addSql(
'alter table if exists "price_rule" drop column if exists "rule_type_id";'
)
this.addSql(
'alter table if exists "price_list_rule" drop column if exists "rule_type_id";'
)
this.addSql('drop table if exists "rule_type" cascade;')
this.addSql('drop table if exists "price_set_rule_type" cascade;')
this.addSql('drop table if exists "price_list_rule_value" cascade;')
}
}
@@ -1,8 +1,5 @@
export { default as Price } from "./price" export { default as Price } from "./price"
export { default as PriceList } from "./price-list" export { default as PriceList } from "./price-list"
export { default as PriceListRule } from "./price-list-rule" 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 PriceSetRuleType } from "./price-set-rule-type"
export { default as RuleType } from "./rule-type"
@@ -1,88 +0,0 @@
import { DAL } from "@medusajs/types"
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import PriceListRule from "./price-list-rule"
type OptionalFields = DAL.SoftDeletableEntityDateColumns
const tableName = "price_list_rule_value"
const PriceListRuleValueDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const PriceListPriceListRuleIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "price_list_rule_id",
where: "deleted_at IS NULL",
})
@Entity({ tableName })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class PriceListRuleValue {
[OptionalProps]?: OptionalFields
@PrimaryKey({ columnType: "text" })
id!: string
@PriceListPriceListRuleIdIndex.MikroORMIndex()
@ManyToOne(() => PriceListRule, {
columnType: "text",
mapToPk: true,
fieldName: "price_list_rule_id",
onDelete: "cascade",
})
price_list_rule_id: string
@ManyToOne(() => PriceListRule, { persist: false })
price_list_rule: Rel<PriceListRule>
@Property({ columnType: "text" })
value: string
@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
@PriceListRuleValueDeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "plrv")
this.price_list_rule_id ??= this.price_list_rule?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "plrv")
this.price_list_rule_id ??= this.price_list_rule?.id
}
}
@@ -6,12 +6,9 @@ import {
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
BeforeCreate, BeforeCreate,
Cascade,
Collection,
Entity, Entity,
Filter, Filter,
ManyToOne, ManyToOne,
OneToMany,
OnInit, OnInit,
OptionalProps, OptionalProps,
PrimaryKey, PrimaryKey,
@@ -19,8 +16,6 @@ import {
Rel, Rel,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import PriceList from "./price-list" import PriceList from "./price-list"
import PriceListRuleValue from "./price-list-rule-value"
import RuleType from "./rule-type"
type OptionalFields = DAL.SoftDeletableEntityDateColumns type OptionalFields = DAL.SoftDeletableEntityDateColumns
@@ -31,13 +26,6 @@ const PriceListRuleDeletedAtIndex = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL", where: "deleted_at IS NOT NULL",
}) })
const PriceListRuleRuleTypeIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "rule_type_id",
where: "deleted_at IS NULL",
unique: true,
})
const PriceListRulePriceListIdIndex = createPsqlIndexStatementHelper({ const PriceListRulePriceListIdIndex = createPsqlIndexStatementHelper({
tableName: tableName, tableName: tableName,
columns: "price_list_id", columns: "price_list_id",
@@ -52,21 +40,11 @@ export default class PriceListRule {
@PrimaryKey({ columnType: "text" }) @PrimaryKey({ columnType: "text" })
id!: string id!: string
@PriceListRuleRuleTypeIdIndex.MikroORMIndex() @Property({ columnType: "text" })
@ManyToOne(() => RuleType, { attribute: string
columnType: "text",
mapToPk: true,
fieldName: "rule_type_id",
})
rule_type_id: string
@ManyToOne(() => RuleType, { persist: false }) @Property({ columnType: "jsonb", nullable: true })
rule_type: Rel<RuleType> value: string | string[] | null = null
@OneToMany(() => PriceListRuleValue, (plrv) => plrv.price_list_rule, {
cascade: [Cascade.PERSIST, "soft-remove" as Cascade],
})
price_list_rule_values = new Collection<Rel<PriceListRuleValue>>(this)
@PriceListRulePriceListIdIndex.MikroORMIndex() @PriceListRulePriceListIdIndex.MikroORMIndex()
@ManyToOne(() => PriceList, { @ManyToOne(() => PriceList, {
@@ -103,13 +81,11 @@ export default class PriceListRule {
beforeCreate() { beforeCreate() {
this.id = generateEntityId(this.id, "plrule") this.id = generateEntityId(this.id, "plrule")
this.price_list_id ??= this.price_list?.id! this.price_list_id ??= this.price_list?.id!
this.rule_type_id ??= this.rule_type?.id!
} }
@OnInit() @OnInit()
onInit() { onInit() {
this.id = generateEntityId(this.id, "plrule") this.id = generateEntityId(this.id, "plrule")
this.price_list_id ??= this.price_list?.id! this.price_list_id ??= this.price_list?.id!
this.rule_type_id ??= this.rule_type?.id!
} }
} }
@@ -14,7 +14,6 @@ import {
Entity, Entity,
Enum, Enum,
Filter, Filter,
ManyToMany,
OneToMany, OneToMany,
OnInit, OnInit,
OptionalProps, OptionalProps,
@@ -24,7 +23,6 @@ import {
} from "@mikro-orm/core" } from "@mikro-orm/core"
import Price from "./price" import Price from "./price"
import PriceListRule from "./price-list-rule" import PriceListRule from "./price-list-rule"
import RuleType from "./rule-type"
type OptionalFields = type OptionalFields =
| "starts_at" | "starts_at"
@@ -84,12 +82,6 @@ export default class PriceList {
}) })
price_list_rules = new Collection<Rel<PriceListRule>>(this) price_list_rules = new Collection<Rel<PriceListRule>>(this)
@ManyToMany({
entity: () => RuleType,
pivotEntity: () => PriceListRule,
})
rule_types = new Collection<Rel<RuleType>>(this)
@Property({ columnType: "integer", default: 0 }) @Property({ columnType: "integer", default: 0 })
rules_count: number = 0 rules_count: number = 0
@@ -16,8 +16,6 @@ import {
Rel, Rel,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import Price from "./price" import Price from "./price"
import PriceSet from "./price-set"
import RuleType from "./rule-type"
type OptionalFields = DAL.SoftDeletableEntityDateColumns type OptionalFields = DAL.SoftDeletableEntityDateColumns
@@ -28,21 +26,9 @@ const PriceRuleDeletedAtIndex = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL", where: "deleted_at IS NOT NULL",
}) })
const PriceRulePriceSetIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "price_set_id",
where: "deleted_at IS NULL",
})
const PriceRuleRuleTypeIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "rule_type_id",
where: "deleted_at IS NULL",
})
const PriceRulePriceIdIndex = createPsqlIndexStatementHelper({ const PriceRulePriceIdIndex = createPsqlIndexStatementHelper({
tableName: tableName, tableName: tableName,
columns: ["price_id", "rule_type_id"], columns: ["price_id", "attribute"],
where: "deleted_at IS NULL", where: "deleted_at IS NULL",
unique: true, unique: true,
}) })
@@ -55,28 +41,8 @@ export default class PriceRule {
@PrimaryKey({ columnType: "text" }) @PrimaryKey({ columnType: "text" })
id!: string id!: string
@PriceRulePriceSetIdIndex.MikroORMIndex() @Property({ columnType: "text" })
@ManyToOne(() => PriceSet, { attribute: string
columnType: "text",
mapToPk: true,
fieldName: "price_set_id",
onDelete: "cascade",
})
price_set_id: string
@ManyToOne(() => PriceSet, { persist: false })
price_set: Rel<PriceSet>
@PriceRuleRuleTypeIdIndex.MikroORMIndex()
@ManyToOne(() => RuleType, {
columnType: "text",
mapToPk: true,
fieldName: "rule_type_id",
})
rule_type_id: string
@ManyToOne(() => RuleType, { persist: false })
rule_type: Rel<RuleType>
@Property({ columnType: "text" }) @Property({ columnType: "text" })
value: string value: string
@@ -118,16 +84,12 @@ export default class PriceRule {
@BeforeCreate() @BeforeCreate()
beforeCreate() { beforeCreate() {
this.id = generateEntityId(this.id, "prule") this.id = generateEntityId(this.id, "prule")
this.rule_type_id ??= this.rule_type?.id!
this.price_set_id ??= this.price_set?.id!
this.price_id ??= this.price?.id! this.price_id ??= this.price?.id!
} }
@OnInit() @OnInit()
onInit() { onInit() {
this.id = generateEntityId(this.id, "prule") this.id = generateEntityId(this.id, "prule")
this.rule_type_id ??= this.rule_type?.id!
this.price_set_id ??= this.price_set?.id!
this.price_id ??= this.price?.id! this.price_id ??= this.price?.id!
} }
} }
@@ -1,89 +0,0 @@
import {
DALUtils,
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import PriceSet from "./price-set"
import RuleType from "./rule-type"
const tableName = "price_set_rule_type"
const PriceSetRuleTypeDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const PriceSetRuleTypePriceSetIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "price_set_id",
where: "deleted_at IS NULL",
})
const PriceSetRuleTypeRuleTypeIdIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "rule_type_id",
where: "deleted_at IS NULL",
})
@Entity({ tableName })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class PriceSetRuleType {
@PrimaryKey({ columnType: "text" })
id!: string
@PriceSetRuleTypePriceSetIdIndex.MikroORMIndex()
@ManyToOne(() => PriceSet, {
columnType: "text",
mapToPk: true,
fieldName: "price_set_id",
onDelete: "cascade",
})
price_set_id: string
@PriceSetRuleTypeRuleTypeIdIndex.MikroORMIndex()
@ManyToOne(() => RuleType, {
columnType: "text",
mapToPk: true,
fieldName: "rule_type_id",
onDelete: "cascade",
})
rule_type_id: string
@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
@PriceSetRuleTypeDeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "psrt")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "psrt")
}
}
@@ -9,7 +9,6 @@ import {
Collection, Collection,
Entity, Entity,
Filter, Filter,
ManyToMany,
OneToMany, OneToMany,
OnInit, OnInit,
PrimaryKey, PrimaryKey,
@@ -17,9 +16,6 @@ import {
Rel, Rel,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import Price from "./price" import Price from "./price"
import PriceRule from "./price-rule"
import PriceSetRuleType from "./price-set-rule-type"
import RuleType from "./rule-type"
const tableName = "price_set" const tableName = "price_set"
const PriceSetDeletedAtIndex = createPsqlIndexStatementHelper({ const PriceSetDeletedAtIndex = createPsqlIndexStatementHelper({
@@ -41,18 +37,6 @@ export default class PriceSet {
}) })
prices = new Collection<Rel<Price>>(this) prices = new Collection<Rel<Price>>(this)
@OneToMany(() => PriceRule, (pr) => pr.price_set, {
cascade: [Cascade.PERSIST, "soft-remove" as Cascade],
})
price_rules = new Collection<Rel<PriceRule>>(this)
@ManyToMany({
entity: () => RuleType,
pivotEntity: () => PriceSetRuleType,
cascade: ["soft-remove" as Cascade],
})
rule_types = new Collection<Rel<RuleType>>(this)
@Property({ @Property({
onCreate: () => new Date(), onCreate: () => new Date(),
columnType: "timestamptz", columnType: "timestamptz",
@@ -1,86 +0,0 @@
import {
DALUtils,
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Collection,
Entity,
Filter,
ManyToMany,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import PriceSet from "./price-set"
type OptionalFields = "default_priority"
const tableName = "rule_type"
const RuleTypeDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const RuleTypeRuleAttributeIndex = createPsqlIndexStatementHelper({
tableName: tableName,
columns: "rule_attribute",
where: "deleted_at IS NULL",
})
@Entity({ tableName })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
class RuleType {
[OptionalProps]?: OptionalFields
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text" })
name: string
@RuleTypeRuleAttributeIndex.MikroORMIndex()
@Property({ columnType: "text" })
rule_attribute: string
@Property({ columnType: "integer", default: 0 })
default_priority: number
@ManyToMany(() => PriceSet, (priceSet) => priceSet.rule_types)
price_sets = new Collection<Rel<PriceSet>>(this)
@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
@RuleTypeDeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "rul-typ")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "rul-typ")
}
}
export default RuleType
@@ -61,65 +61,57 @@ export class PricingRepository
price: "price", price: "price",
}) })
.select({ .select({
id: "price1.id", id: "price.id",
amount: "price1.amount", amount: "price.amount",
min_quantity: "price1.min_quantity", min_quantity: "price.min_quantity",
max_quantity: "price1.max_quantity", max_quantity: "price.max_quantity",
currency_code: "price1.currency_code", currency_code: "price.currency_code",
price_set_id: "price1.price_set_id", price_set_id: "price.price_set_id",
rules_count: "price1.rules_count", rules_count: "price.rules_count",
price_list_id: "price1.price_list_id", price_list_id: "price.price_list_id",
pl_rules_count: "pl.rules_count", pl_rules_count: "pl.rules_count",
pl_type: "pl.type", pl_type: "pl.type",
has_price_list: knex.raw( has_price_list: knex.raw(
"case when price1.price_list_id IS NULL then False else True end" "case when price.price_list_id IS NULL then False else True end"
), ),
}) })
.leftJoin("price as price1", "price1.id", "price1.id") .leftJoin("price_rule as pr", "pr.price_id", "price.id")
.leftJoin("price_rule as pr", "pr.price_id", "price1.id")
.leftJoin("price_list as pl", function () { .leftJoin("price_list as pl", function () {
this.on("pl.id", "price1.price_list_id").andOn( this.on("pl.id", "price.price_list_id").andOn(
"pl.status", "pl.status",
knex.raw("?", [PriceListStatus.ACTIVE]) knex.raw("?", [PriceListStatus.ACTIVE])
) )
}) })
.leftJoin("price_list_rule as plr", "plr.price_list_id", "pl.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")
.orderBy([ .orderBy([
{ column: "rules_count", order: "desc" }, { column: "rules_count", order: "desc" },
{ column: "pl.rules_count", order: "desc" }, { column: "pl.rules_count", order: "desc" },
]) ])
.groupBy("price1.id", "pl.id") .groupBy("price.id", "pl.id")
.having( .having(
knex.raw( knex.raw(
"count(DISTINCT rt.rule_attribute) = price1.rules_count AND price1.price_list_id IS NULL" "count(DISTINCT pr.attribute) = price.rules_count AND price.price_list_id IS NULL"
) )
) )
.orHaving( .orHaving(
knex.raw( knex.raw(
"count(DISTINCT plrt.rule_attribute) = pl.rules_count AND price1.price_list_id IS NOT NULL" "count(DISTINCT plr.attribute) = pl.rules_count AND price.price_list_id IS NOT NULL"
) )
) )
priceSubQueryKnex.orWhere((q) => { priceSubQueryKnex.orWhere((q) => {
for (const [key, value] of Object.entries(context)) { for (const [key, value] of Object.entries(context)) {
q.orWhere({ q.orWhere({
"rt.rule_attribute": key, "pr.attribute": key,
"pr.value": value, "pr.value": value,
}) })
} }
q.orWhere("price1.rules_count", "=", 0) q.orWhere("price.rules_count", "=", 0)
q.whereNull("price1.price_list_id") q.whereNull("price.price_list_id")
}) })
priceSubQueryKnex.orWhere((q) => { priceSubQueryKnex.orWhere((q) => {
q.whereNotNull("price1.price_list_id") q.whereNotNull("price.price_list_id")
.andWhere(function () { .andWhere(function () {
this.whereNull("pl.starts_at").orWhere("pl.starts_at", "<=", date) this.whereNull("pl.starts_at").orWhere("pl.starts_at", "<=", date)
}) })
@@ -130,9 +122,13 @@ export class PricingRepository
this.andWhere(function () { this.andWhere(function () {
for (const [key, value] of Object.entries(context)) { for (const [key, value] of Object.entries(context)) {
this.orWhere({ this.orWhere({
"plrt.rule_attribute": key, "plr.attribute": key,
}) })
this.whereIn("plrv.value", [value]) this.where(
"plr.value",
"@>",
JSON.stringify(Array.isArray(value) ? value : [value])
)
} }
this.orWhere("pl.rules_count", "=", 0) this.orWhere("pl.rules_count", "=", 0)
@@ -142,13 +138,13 @@ export class PricingRepository
this.andWhere(function () { this.andWhere(function () {
for (const [key, value] of Object.entries(context)) { for (const [key, value] of Object.entries(context)) {
this.orWhere({ this.orWhere({
"rt.rule_attribute": key, "pr.attribute": key,
"pr.value": value, "pr.value": value,
}) })
} }
this.andWhere("price1.rules_count", ">", 0) this.andWhere("price.rules_count", ">", 0)
}) })
this.orWhere("price1.rules_count", "=", 0) this.orWhere("price.rules_count", "=", 0)
}) })
}) })
}) })
@@ -163,7 +159,6 @@ export class PricingRepository
min_quantity: "price.min_quantity", min_quantity: "price.min_quantity",
max_quantity: "price.max_quantity", max_quantity: "price.max_quantity",
currency_code: "price.currency_code", currency_code: "price.currency_code",
default_priority: "rt.default_priority",
rules_count: "price.rules_count", rules_count: "price.rules_count",
pl_rules_count: "price.pl_rules_count", pl_rules_count: "price.pl_rules_count",
price_list_type: "price.pl_type", price_list_type: "price.pl_type",
@@ -171,7 +166,6 @@ export class PricingRepository
}) })
.join(priceSubQueryKnex.as("price"), "price.price_set_id", "ps.id") .join(priceSubQueryKnex.as("price"), "price.price_set_id", "ps.id")
.leftJoin("price_rule as pr", "pr.price_id", "price.id") .leftJoin("price_rule as pr", "pr.price_id", "price.id")
.leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id")
.whereIn("ps.id", pricingFilters.id) .whereIn("ps.id", pricingFilters.id)
.andWhere("price.currency_code", "=", currencyCode) .andWhere("price.currency_code", "=", currencyCode)
@@ -179,7 +173,6 @@ export class PricingRepository
{ column: "price.has_price_list", order: "asc" }, { column: "price.has_price_list", order: "asc" },
{ column: "amount", order: "asc" }, { column: "amount", order: "asc" },
{ column: "rules_count", order: "desc" }, { column: "rules_count", order: "desc" },
{ column: "default_priority", order: "desc" },
]) ])
if (quantity) { if (quantity) {
@@ -1,3 +1 @@
export { default as PriceListService } from "./price-list"
export { default as PricingModuleService } from "./pricing-module" export { default as PricingModuleService } from "./pricing-module"
export { default as RuleTypeService } from "./rule-type"
@@ -1,67 +0,0 @@
import { Context, DAL } from "@medusajs/types"
import { GetIsoStringFromDate, ModulesSdkUtils } from "@medusajs/utils"
import { PriceList } from "@models"
import { ServiceTypes } from "@types"
type InjectedDependencies = {
priceListRepository: DAL.RepositoryService
}
export default class PriceListService extends ModulesSdkUtils.MedusaInternalService<InjectedDependencies>(
PriceList
)<PriceList> {
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
}
create(
data: ServiceTypes.CreatePriceListDTO[],
sharedContext?: Context
): Promise<PriceList[]>
create(
data: ServiceTypes.CreatePriceListDTO,
sharedContext?: Context
): Promise<PriceList>
async create(
data: ServiceTypes.CreatePriceListDTO | ServiceTypes.CreatePriceListDTO[],
sharedContext?: Context
): Promise<PriceList | PriceList[]> {
const data_ = Array.isArray(data) ? data : [data]
const priceLists = this.normalizePriceListDate(data_)
return await super.create(priceLists, sharedContext)
}
// @ts-ignore
update(data: any[], sharedContext?: Context): Promise<PriceList[]>
// @ts-ignore
update(data: any, sharedContext?: Context): Promise<PriceList>
// TODO: Add support for selector? and then rm ts ignore
// @ts-ignore
async update(
data: ServiceTypes.UpdatePriceListDTO | ServiceTypes.UpdatePriceListDTO[],
sharedContext?: Context
): Promise<PriceList | PriceList[]> {
const data_ = Array.isArray(data) ? data : [data]
const priceLists = this.normalizePriceListDate(data_)
return await super.update(priceLists, sharedContext)
}
protected normalizePriceListDate(
data: (ServiceTypes.UpdatePriceListDTO | ServiceTypes.CreatePriceListDTO)[]
) {
return data.map((priceListData: any) => {
if (!!priceListData.starts_at) {
priceListData.starts_at = GetIsoStringFromDate(priceListData.starts_at)
}
if (!!priceListData.ends_at) {
priceListData.ends_at = GetIsoStringFromDate(priceListData.ends_at)
}
return priceListData
})
}
}
File diff suppressed because it is too large. Load diff
@@ -1,66 +0,0 @@
import { Context, DAL, PricingTypes } from "@medusajs/types"
import {
InjectTransactionManager,
MedusaContext,
ModulesSdkUtils,
validateRuleAttributes,
} from "@medusajs/utils"
import { RuleType } from "@models"
type InjectedDependencies = {
ruleTypeRepository: DAL.RepositoryService
}
export default class RuleTypeService extends ModulesSdkUtils.MedusaInternalService<InjectedDependencies>(
RuleType
)<RuleType> {
protected readonly ruleTypeRepository_: DAL.RepositoryService<RuleType>
constructor({ ruleTypeRepository }: InjectedDependencies) {
// @ts-ignore
super(...arguments)
this.ruleTypeRepository_ = ruleTypeRepository
}
create(
data: PricingTypes.CreateRuleTypeDTO,
sharedContext: Context
): Promise<RuleType>
create(
data: PricingTypes.CreateRuleTypeDTO[],
sharedContext: Context
): Promise<RuleType[]>
@InjectTransactionManager("ruleTypeRepository_")
async create(
data: PricingTypes.CreateRuleTypeDTO | PricingTypes.CreateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<RuleType | RuleType[]> {
const data_ = Array.isArray(data) ? data : [data]
validateRuleAttributes(data_.map((d) => d.rule_attribute))
return await super.create(data, sharedContext)
}
// @ts-ignore
update(
data: PricingTypes.UpdateRuleTypeDTO[],
sharedContext: Context
): Promise<RuleType[]>
// @ts-ignore
update(
data: PricingTypes.UpdateRuleTypeDTO,
sharedContext: Context
): Promise<RuleType>
@InjectTransactionManager("ruleTypeRepository_")
// TODO: add support for selector? and then rm ts ignore
// @ts-ignore
async update(
data: PricingTypes.UpdateRuleTypeDTO | PricingTypes.UpdateRuleTypeDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<RuleType | RuleType[]> {
const data_ = Array.isArray(data) ? data : [data]
validateRuleAttributes(data_.map((d) => d.rule_attribute))
return await super.update(data, sharedContext)
}
}
@@ -1,13 +1,12 @@
import { PriceListStatus, PriceListType } from "@medusajs/types" import { PriceListStatus, PricingTypes } from "@medusajs/types"
export interface CreatePriceListDTO { export interface CreatePriceListDTO extends PricingTypes.CreatePriceListDTO {
title: string rules_count?: number
description: string price_list_rules?: {
starts_at?: string | null attribute: string
ends_at?: string | null value: string
status?: PriceListStatus }[]
type?: PriceListType prices?: PricingTypes.CreatePriceListPriceDTO[]
number_rules?: number
} }
export interface UpdatePriceListDTO { export interface UpdatePriceListDTO {
@@ -12,12 +12,6 @@ export const eventBuilders = {
object: "price_set", object: "price_set",
eventsEnum: PricingEvents, eventsEnum: PricingEvents,
}), }),
createdPriceSetRuleType: eventBuilderFactory({
source: Modules.PRICING,
action: CommonEvents.CREATED,
object: "price_set_rule_type",
eventsEnum: PricingEvents,
}),
createdPrice: eventBuilderFactory({ createdPrice: eventBuilderFactory({
source: Modules.PRICING, source: Modules.PRICING,
action: CommonEvents.CREATED, action: CommonEvents.CREATED,