feat(core-flows,types,pricing,medusa): Products API can create prices with rules (#7796)
* chore: Products API can create prices with rules * chore: fix tests * chore: cleanup * chore: address comments
This commit is contained in:
@@ -1227,6 +1227,64 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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(
|
||||||
|
"/admin/products",
|
||||||
|
{
|
||||||
|
title: "Test create",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Price with rules",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 100,
|
||||||
|
rules: { region_id: "eur" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceIdSelector = /^price_*/
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.stringMatching(/^prod_*/),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.stringMatching(/^variant_*/),
|
||||||
|
title: "Price with rules",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.stringMatching(priceIdSelector),
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 100,
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
variant_id: expect.stringMatching(/^variant_*/),
|
||||||
|
rules: { region_id: "eur" },
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("creates a product that is not discountable", async () => {
|
it("creates a product that is not discountable", async () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "Test",
|
title: "Test",
|
||||||
|
|||||||
@@ -96,11 +96,20 @@ 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: [
|
||||||
{
|
{
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
amount: 1500,
|
amount: 1500,
|
||||||
|
rules: {
|
||||||
|
region_id: "na",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -115,6 +124,7 @@ medusaIntegrationTestRunner({
|
|||||||
baseProduct.variants[0].prices.find((p) => p.currency_code === "usd")
|
baseProduct.variants[0].prices.find((p) => p.currency_code === "usd")
|
||||||
.amount
|
.amount
|
||||||
).toEqual(100)
|
).toEqual(100)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data).toEqual({
|
expect(response.data).toEqual({
|
||||||
product: expect.objectContaining({
|
product: expect.objectContaining({
|
||||||
@@ -126,6 +136,7 @@ medusaIntegrationTestRunner({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
amount: 1500,
|
amount: 1500,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
|
rules: { region_id: "na" },
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ export const updatePriceSetsStep = createStep(
|
|||||||
return new StepResponse([], null)
|
return new StepResponse([], null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
const { selects, relations } = getSelectsAndRelationsFromObjectArray(
|
||||||
data.update,
|
[data.update],
|
||||||
])
|
{ objectFields: ["rules"] }
|
||||||
|
)
|
||||||
|
|
||||||
const dataBeforeUpdate = await pricingModule.listPriceSets(data.selector, {
|
const dataBeforeUpdate = await pricingModule.listPriceSets(data.selector, {
|
||||||
select: selects,
|
select: selects,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Context } from "../shared-context"
|
|||||||
import {
|
import {
|
||||||
AddPriceListPricesDTO,
|
AddPriceListPricesDTO,
|
||||||
AddPricesDTO,
|
AddPricesDTO,
|
||||||
AddRulesDTO,
|
|
||||||
CalculatedPriceSet,
|
CalculatedPriceSet,
|
||||||
CreatePriceListDTO,
|
CreatePriceListDTO,
|
||||||
CreatePriceRuleDTO,
|
CreatePriceRuleDTO,
|
||||||
@@ -25,7 +24,6 @@ import {
|
|||||||
PricingContext,
|
PricingContext,
|
||||||
PricingFilters,
|
PricingFilters,
|
||||||
RemovePriceListRulesDTO,
|
RemovePriceListRulesDTO,
|
||||||
RemovePriceSetRulesDTO,
|
|
||||||
RuleTypeDTO,
|
RuleTypeDTO,
|
||||||
SetPriceListRulesDTO,
|
SetPriceListRulesDTO,
|
||||||
UpdatePriceListDTO,
|
UpdatePriceListDTO,
|
||||||
@@ -481,26 +479,6 @@ export interface IPricingModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PriceSetDTO[]>
|
): Promise<PriceSetDTO[]>
|
||||||
|
|
||||||
/**
|
|
||||||
* This method remove rules from a price set.
|
|
||||||
*
|
|
||||||
* @param {RemovePriceSetRulesDTO[]} data - The rules to remove per price set.
|
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
|
||||||
* @returns {Promise<void>} Resolves when rules are successfully removed.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* await pricingModuleService.removeRules([
|
|
||||||
* {
|
|
||||||
* id: "pset_123",
|
|
||||||
* rules: ["region_id"],
|
|
||||||
* },
|
|
||||||
* ])
|
|
||||||
*/
|
|
||||||
removeRules(
|
|
||||||
data: RemovePriceSetRulesDTO[],
|
|
||||||
sharedContext?: Context
|
|
||||||
): Promise<void>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deletes price sets by their IDs.
|
* This method deletes price sets by their IDs.
|
||||||
*
|
*
|
||||||
@@ -615,54 +593,6 @@ export interface IPricingModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PriceSetDTO[]>
|
): Promise<PriceSetDTO[]>
|
||||||
|
|
||||||
/**
|
|
||||||
* This method adds rules to a price set.
|
|
||||||
*
|
|
||||||
* @param {AddRulesDTO} data - The data defining the price set to add the rules to, along with the rules to add.
|
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
|
||||||
* @returns {Promise<PriceSetDTO>} The price set that the rules were added to.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const priceSet = await pricingModuleService.addRules({
|
|
||||||
* priceSetId: "pset_123",
|
|
||||||
* rules: [
|
|
||||||
* {
|
|
||||||
* attribute: "region_id",
|
|
||||||
* },
|
|
||||||
* ],
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
addRules(data: AddRulesDTO, sharedContext?: Context): Promise<PriceSetDTO>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method adds rules to multiple price sets.
|
|
||||||
*
|
|
||||||
* @param {AddRulesDTO[]} data - The data defining the rules to add per price set.
|
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
|
||||||
* @returns {Promise<PriceSetDTO[]>} The list of the price sets that the rules were added to.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const priceSets = await pricingModuleService.addRules([
|
|
||||||
* {
|
|
||||||
* priceSetId: "pset_123",
|
|
||||||
* rules: [
|
|
||||||
* {
|
|
||||||
* attribute: "region_id",
|
|
||||||
* },
|
|
||||||
* ],
|
|
||||||
* },
|
|
||||||
* {
|
|
||||||
* priceSetId: "pset_321",
|
|
||||||
* rules: [
|
|
||||||
* {
|
|
||||||
* attribute: "customer_group_id",
|
|
||||||
* },
|
|
||||||
* ],
|
|
||||||
* },
|
|
||||||
* ])
|
|
||||||
*/
|
|
||||||
addRules(data: AddRulesDTO[], sharedContext?: Context): Promise<PriceSetDTO[]>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to retrieve a rule type by its ID and and optionally based on the provided configurations.
|
* This method is used to retrieve a rule type by its ID and and optionally based on the provided configurations.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../../../../../types/routing"
|
|
||||||
import {
|
import {
|
||||||
deleteProductVariantsWorkflow,
|
deleteProductVariantsWorkflow,
|
||||||
updateProductVariantsWorkflow,
|
updateProductVariantsWorkflow,
|
||||||
} from "@medusajs/core-flows"
|
} from "@medusajs/core-flows"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../../../../types/routing"
|
||||||
|
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { refetchEntity } from "../../../../../utils/refetch-entity"
|
||||||
import {
|
import {
|
||||||
remapKeysForProduct,
|
remapKeysForProduct,
|
||||||
remapKeysForVariant,
|
remapKeysForVariant,
|
||||||
remapProductResponse,
|
remapProductResponse,
|
||||||
remapVariantResponse,
|
remapVariantResponse,
|
||||||
} from "../../../helpers"
|
} from "../../../helpers"
|
||||||
import { HttpTypes } from "@medusajs/types"
|
|
||||||
import { refetchEntity } from "../../../../../utils/refetch-entity"
|
|
||||||
|
|
||||||
export const GET = async (
|
export const GET = async (
|
||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
@@ -54,6 +54,7 @@ export const POST = async (
|
|||||||
req.scope,
|
req.scope,
|
||||||
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
|
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
|
||||||
)
|
)
|
||||||
|
|
||||||
res.status(200).json({ product: remapProductResponse(product) })
|
res.status(200).json({ product: remapProductResponse(product) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
BatchMethodResponse,
|
BatchMethodResponse,
|
||||||
HttpTypes,
|
HttpTypes,
|
||||||
MedusaContainer,
|
MedusaContainer,
|
||||||
|
PriceDTO,
|
||||||
ProductDTO,
|
ProductDTO,
|
||||||
ProductVariantDTO,
|
ProductVariantDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
@@ -25,6 +26,7 @@ export const remapKeysForProduct = (selectFields: string[]) => {
|
|||||||
const productFields = selectFields.filter(
|
const productFields = selectFields.filter(
|
||||||
(fieldName: string) => !isPricing(fieldName)
|
(fieldName: string) => !isPricing(fieldName)
|
||||||
)
|
)
|
||||||
|
|
||||||
const pricingFields = selectFields
|
const pricingFields = selectFields
|
||||||
.filter((fieldName: string) => isPricing(fieldName))
|
.filter((fieldName: string) => isPricing(fieldName))
|
||||||
.map((fieldName: string) =>
|
.map((fieldName: string) =>
|
||||||
@@ -38,6 +40,7 @@ export const remapKeysForVariant = (selectFields: string[]) => {
|
|||||||
const variantFields = selectFields.filter(
|
const variantFields = selectFields.filter(
|
||||||
(fieldName: string) => !isPricing(fieldName)
|
(fieldName: string) => !isPricing(fieldName)
|
||||||
)
|
)
|
||||||
|
|
||||||
const pricingFields = selectFields
|
const pricingFields = selectFields
|
||||||
.filter((fieldName: string) => isPricing(fieldName))
|
.filter((fieldName: string) => isPricing(fieldName))
|
||||||
.map((fieldName: string) =>
|
.map((fieldName: string) =>
|
||||||
@@ -75,14 +78,30 @@ export const remapVariantResponse = (
|
|||||||
variant_id: variant.id,
|
variant_id: variant.id,
|
||||||
created_at: price.created_at,
|
created_at: price.created_at,
|
||||||
updated_at: price.updated_at,
|
updated_at: price.updated_at,
|
||||||
|
rules: buildRules(price),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
delete (resp as any).price_set
|
delete (resp as any).price_set
|
||||||
|
|
||||||
// TODO: Remove any once all typings are cleaned up
|
// TODO: Remove any once all typings are cleaned up
|
||||||
return resp as any
|
return resp as any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const buildRules = (price: PriceDTO) => {
|
||||||
|
const rules: Record<string, string> = {}
|
||||||
|
|
||||||
|
for (const priceRule of price.price_rules || []) {
|
||||||
|
const ruleAttribute = priceRule.rule_type?.rule_attribute
|
||||||
|
|
||||||
|
if (ruleAttribute) {
|
||||||
|
rules[ruleAttribute] = priceRule.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
export const refetchVariant = async (
|
export const refetchVariant = async (
|
||||||
variantId: string,
|
variantId: string,
|
||||||
scope: MedusaContainer,
|
scope: MedusaContainer,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const defaultAdminProductsVariantFields = [
|
|||||||
"upc",
|
"upc",
|
||||||
"barcode",
|
"barcode",
|
||||||
"*prices",
|
"*prices",
|
||||||
|
"prices.price_rules.value",
|
||||||
|
"prices.price_rules.rule_type.rule_attribute",
|
||||||
"*options",
|
"*options",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -82,6 +84,8 @@ export const defaultAdminProductFields = [
|
|||||||
"*images",
|
"*images",
|
||||||
"*variants",
|
"*variants",
|
||||||
"*variants.prices",
|
"*variants.prices",
|
||||||
|
"variants.prices.price_rules.value",
|
||||||
|
"variants.prices.price_rules.rule_type.rule_attribute",
|
||||||
"*variants.options",
|
"*variants.options",
|
||||||
"*sales_channels",
|
"*sales_channels",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ export const AdminCreateVariantPrice = z.object({
|
|||||||
amount: z.number(),
|
amount: z.number(),
|
||||||
min_quantity: z.number().nullish(),
|
min_quantity: z.number().nullish(),
|
||||||
max_quantity: z.number().nullish(),
|
max_quantity: z.number().nullish(),
|
||||||
|
rules: z.record(z.string(), z.string()).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Add support for rules
|
// TODO: Add support for rules
|
||||||
@@ -118,6 +119,7 @@ export const AdminUpdateVariantPrice = z.object({
|
|||||||
amount: z.number().optional(),
|
amount: z.number().optional(),
|
||||||
min_quantity: z.number().nullish(),
|
min_quantity: z.number().nullish(),
|
||||||
max_quantity: z.number().nullish(),
|
max_quantity: z.number().nullish(),
|
||||||
|
rules: z.record(z.string(), z.string()).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type AdminCreateProductTypeType = z.infer<typeof AdminCreateProductType>
|
export type AdminCreateProductTypeType = z.infer<typeof AdminCreateProductType>
|
||||||
|
|||||||
+8
-105
@@ -1,16 +1,16 @@
|
|||||||
import { IPricingModuleService } from "@medusajs/types"
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
import {
|
|
||||||
MockEventBusService,
|
|
||||||
moduleIntegrationTestRunner,
|
|
||||||
} from "medusa-test-utils"
|
|
||||||
import { createPriceLists } from "../../../__fixtures__/price-list"
|
|
||||||
import { createPriceSets } from "../../../__fixtures__/price-set"
|
|
||||||
import {
|
import {
|
||||||
CommonEvents,
|
CommonEvents,
|
||||||
composeMessage,
|
composeMessage,
|
||||||
Modules,
|
Modules,
|
||||||
PricingEvents,
|
PricingEvents,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
MockEventBusService,
|
||||||
|
moduleIntegrationTestRunner,
|
||||||
|
} from "medusa-test-utils"
|
||||||
|
import { createPriceLists } from "../../../__fixtures__/price-list"
|
||||||
|
import { createPriceSets } from "../../../__fixtures__/price-set"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -745,41 +745,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => {
|
|
||||||
await service.createRuleTypes([
|
|
||||||
{
|
|
||||||
name: "twitter_handle",
|
|
||||||
rule_attribute: "twitter_handle",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
let error
|
|
||||||
try {
|
|
||||||
await service.addPriceListPrices([
|
|
||||||
{
|
|
||||||
price_list_id: "price-list-1",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 123,
|
|
||||||
currency_code: "EUR",
|
|
||||||
price_set_id: "price-set-1",
|
|
||||||
rules: {
|
|
||||||
twitter_handle: "owjuhl",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
} catch (err) {
|
|
||||||
error = err
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
|
||||||
"" +
|
|
||||||
`Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set price-set-1`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
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([
|
await service.createRuleTypes([
|
||||||
{
|
{
|
||||||
@@ -788,15 +753,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const r = await service.addRules([
|
|
||||||
{
|
|
||||||
priceSetId: "price-set-1",
|
|
||||||
rules: [{ attribute: "region_id" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
jest.clearAllMocks()
|
|
||||||
|
|
||||||
await service.addPriceListPrices([
|
await service.addPriceListPrices([
|
||||||
{
|
{
|
||||||
price_list_id: "price-list-1",
|
price_list_id: "price-list-1",
|
||||||
@@ -886,14 +842,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
|
|
||||||
describe("updatePriceListPrices", () => {
|
describe("updatePriceListPrices", () => {
|
||||||
it("should update a price to a priceList successfully", async () => {
|
it("should update a price to a priceList successfully", async () => {
|
||||||
const [priceSet] = await service.createPriceSets([
|
const [priceSet] = await service.createPriceSets([{}])
|
||||||
{
|
|
||||||
rules: [
|
|
||||||
{ rule_attribute: "region_id" },
|
|
||||||
{ rule_attribute: "customer_group_id" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
await service.addPriceListPrices([
|
await service.addPriceListPrices([
|
||||||
{
|
{
|
||||||
@@ -907,7 +856,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
rules: {
|
rules: {
|
||||||
region_id: "test",
|
region_id: "test",
|
||||||
},
|
},
|
||||||
} as any,
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
@@ -981,52 +930,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => {
|
|
||||||
await service.createRuleTypes([
|
|
||||||
{ name: "twitter_handle", rule_attribute: "twitter_handle" },
|
|
||||||
{ name: "region_id", rule_attribute: "region_id" },
|
|
||||||
])
|
|
||||||
|
|
||||||
const [priceSet] = await service.createPriceSets([
|
|
||||||
{ rules: [{ rule_attribute: "region_id" }] },
|
|
||||||
])
|
|
||||||
|
|
||||||
await service.addPriceListPrices([
|
|
||||||
{
|
|
||||||
price_list_id: "price-list-1",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
id: "test-price-id",
|
|
||||||
amount: 123,
|
|
||||||
currency_code: "EUR",
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
rules: { region_id: "test" },
|
|
||||||
} as any,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const error = await service
|
|
||||||
.updatePriceListPrices([
|
|
||||||
{
|
|
||||||
price_list_id: "price-list-1",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
id: "test-price-id",
|
|
||||||
amount: 123,
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
rules: { twitter_handle: "owjuhl" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
|
||||||
`Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set ${priceSet.id}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("removePrices", () => {
|
describe("removePrices", () => {
|
||||||
|
|||||||
+8
-245
@@ -3,6 +3,12 @@ import {
|
|||||||
CreatePriceSetRuleTypeDTO,
|
CreatePriceSetRuleTypeDTO,
|
||||||
IPricingModuleService,
|
IPricingModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
CommonEvents,
|
||||||
|
composeMessage,
|
||||||
|
Modules,
|
||||||
|
PricingEvents,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import {
|
import {
|
||||||
MockEventBusService,
|
MockEventBusService,
|
||||||
@@ -10,12 +16,6 @@ import {
|
|||||||
} from "medusa-test-utils"
|
} from "medusa-test-utils"
|
||||||
import { PriceSetRuleType } from "../../../../src/models"
|
import { PriceSetRuleType } from "../../../../src/models"
|
||||||
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
||||||
import {
|
|
||||||
CommonEvents,
|
|
||||||
composeMessage,
|
|
||||||
Modules,
|
|
||||||
PricingEvents,
|
|
||||||
} from "@medusajs/utils"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -346,24 +346,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("should throw an error when creating a price set with rule attributes that don't exist", async () => {
|
|
||||||
let error
|
|
||||||
|
|
||||||
try {
|
|
||||||
await service.createPriceSets([
|
|
||||||
{
|
|
||||||
rules: [{ rule_attribute: "does-not-exist" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
|
||||||
"Rule types don't exist for: does-not-exist"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should fail to create a price set with rule types and money amounts with rule types that don't exits", async () => {
|
it("should fail to create a price set with rule types and money amounts with rule types that don't exits", async () => {
|
||||||
let error
|
let error
|
||||||
|
|
||||||
@@ -386,32 +368,13 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
expect(error.message).toEqual(
|
expect(error.message).toEqual(
|
||||||
"Rule types don't exist for money amounts with rule attribute: city"
|
"Rule types don't exist for prices with rule attribute: city"
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should create a price set with rule types", async () => {
|
|
||||||
const [priceSet] = await service.createPriceSets([
|
|
||||||
{
|
|
||||||
rules: [{ rule_attribute: "region_id" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(priceSet).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_types: [
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "region_id",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
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([
|
||||||
{
|
{
|
||||||
rules: [{ rule_attribute: "region_id" }],
|
|
||||||
prices: [
|
prices: [
|
||||||
{
|
{
|
||||||
amount: 100,
|
amount: 100,
|
||||||
@@ -426,11 +389,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
|
|
||||||
expect(priceSet).toEqual(
|
expect(priceSet).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
rule_types: [
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "region_id",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
prices: [
|
prices: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
amount: 100,
|
amount: 100,
|
||||||
@@ -477,10 +435,9 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a price set with money amounts with and without rules", async () => {
|
it("should create a price set with prices", async () => {
|
||||||
const [priceSet] = await service.createPriceSets([
|
const [priceSet] = await service.createPriceSets([
|
||||||
{
|
{
|
||||||
rules: [{ rule_attribute: "region_id" }],
|
|
||||||
prices: [
|
prices: [
|
||||||
{
|
{
|
||||||
amount: 100,
|
amount: 100,
|
||||||
@@ -499,11 +456,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
|
|
||||||
expect(priceSet).toEqual(
|
expect(priceSet).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
rule_types: [
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "region_id",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
prices: expect.arrayContaining([
|
prices: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
amount: 100,
|
amount: 100,
|
||||||
@@ -518,44 +470,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a price set with rule types and money amounts", async () => {
|
|
||||||
const [priceSet] = await service.createPriceSets([
|
|
||||||
{
|
|
||||||
rules: [{ rule_attribute: "region_id" }],
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 100,
|
|
||||||
currency_code: "USD",
|
|
||||||
rules: {
|
|
||||||
region_id: "10",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(priceSet).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_types: [
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "region_id",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
prices: [
|
|
||||||
expect.objectContaining({
|
|
||||||
amount: 100,
|
|
||||||
currency_code: "USD",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
price_rules: [
|
|
||||||
expect.objectContaining({
|
|
||||||
value: "10",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should create a priceSet successfully", async () => {
|
it("should create a priceSet successfully", async () => {
|
||||||
await service.createPriceSets([
|
await service.createPriceSets([
|
||||||
{
|
{
|
||||||
@@ -575,92 +489,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("removeRules", () => {
|
|
||||||
it("should delete prices for a price set associated to the rules that are deleted", async () => {
|
|
||||||
const createdPriceSet = await service.createPriceSets([
|
|
||||||
{
|
|
||||||
rules: [
|
|
||||||
{ rule_attribute: "region_id" },
|
|
||||||
{ rule_attribute: "currency_code" },
|
|
||||||
],
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
currency_code: "EUR",
|
|
||||||
amount: 100,
|
|
||||||
rules: {
|
|
||||||
region_id: "test-region",
|
|
||||||
currency_code: "test-currency",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
currency_code: "EUR",
|
|
||||||
amount: 500,
|
|
||||||
rules: {
|
|
||||||
currency_code: "test-currency",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
await service.removeRules([
|
|
||||||
{ id: createdPriceSet[0].id, rules: ["region_id"] },
|
|
||||||
])
|
|
||||||
|
|
||||||
let priceSet = await service.listPriceSets(
|
|
||||||
{ id: [createdPriceSet[0].id] },
|
|
||||||
{ relations: ["rule_types", "prices", "price_rules"] }
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(
|
|
||||||
expect.arrayContaining(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: priceSet[0].id,
|
|
||||||
price_rules: [
|
|
||||||
{
|
|
||||||
id: expect.any(String),
|
|
||||||
rule_type: expect.objectContaining({
|
|
||||||
rule_attribute: "currency_code",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
prices: [
|
|
||||||
expect.objectContaining({
|
|
||||||
amount: 500,
|
|
||||||
currency_code: "EUR",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
rule_types: [
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "currency_code",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
await service.removeRules([
|
|
||||||
{ id: createdPriceSet[0].id, rules: ["currency_code"] },
|
|
||||||
])
|
|
||||||
|
|
||||||
priceSet = await service.listPriceSets(
|
|
||||||
{ id: [createdPriceSet[0].id] },
|
|
||||||
{ relations: ["rule_types", "prices", "price_rules"] }
|
|
||||||
)
|
|
||||||
expect(priceSet).toEqual([
|
|
||||||
{
|
|
||||||
id: expect.any(String),
|
|
||||||
price_rules: [],
|
|
||||||
prices: [],
|
|
||||||
rule_types: [],
|
|
||||||
created_at: expect.any(Date),
|
|
||||||
updated_at: expect.any(Date),
|
|
||||||
deleted_at: null,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("addPrices", () => {
|
describe("addPrices", () => {
|
||||||
it("should add prices to existing price set", async () => {
|
it("should add prices to existing price set", async () => {
|
||||||
await service.addPrices([
|
await service.addPrices([
|
||||||
@@ -784,71 +612,6 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
expect(error.message).toEqual("Rule types don't exist for: city")
|
expect(error.message).toEqual("Rule types don't exist for: city")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("addRules", () => {
|
|
||||||
it("should add rules to existing price set", async () => {
|
|
||||||
await service.addRules([
|
|
||||||
{
|
|
||||||
priceSetId: "price-set-1",
|
|
||||||
rules: [{ attribute: "region_id" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const [priceSet] = await service.listPriceSets(
|
|
||||||
{ id: ["price-set-1"] },
|
|
||||||
{ relations: ["rule_types"] }
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(priceSet).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: "price-set-1",
|
|
||||||
rule_types: expect.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "currency_code",
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
rule_attribute: "region_id",
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should fail to add rules to non-existent price sets", async () => {
|
|
||||||
let error
|
|
||||||
|
|
||||||
try {
|
|
||||||
await service.addRules([
|
|
||||||
{
|
|
||||||
priceSetId: "price-set-doesn't-exist",
|
|
||||||
rules: [{ attribute: "region_id" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
|
||||||
"PriceSets with ids: price-set-doesn't-exist was not found"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should fail to add rules with non-existent attributes", async () => {
|
|
||||||
let error
|
|
||||||
|
|
||||||
try {
|
|
||||||
await service.addRules([
|
|
||||||
{ priceSetId: "price-set-1", rules: [{ attribute: "city" }] },
|
|
||||||
])
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
|
||||||
"Rule types don't exist for attributes: city"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -43,16 +43,15 @@ import {
|
|||||||
PriceListRuleValue,
|
PriceListRuleValue,
|
||||||
PriceRule,
|
PriceRule,
|
||||||
PriceSet,
|
PriceSet,
|
||||||
PriceSetRuleType,
|
|
||||||
RuleType,
|
RuleType,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
|
|
||||||
import { PriceListService, RuleTypeService } from "@services"
|
import { PriceListService, RuleTypeService } from "@services"
|
||||||
|
import { ServiceTypes } from "@types"
|
||||||
import { eventBuilders, validatePriceListDates } from "@utils"
|
import { eventBuilders, validatePriceListDates } from "@utils"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||||
import { PriceListIdPrefix } from "../models/price-list"
|
import { PriceListIdPrefix } from "../models/price-list"
|
||||||
import { PriceSetIdPrefix } from "../models/price-set"
|
import { PriceSetIdPrefix } from "../models/price-set"
|
||||||
import { ServiceTypes } from "@types"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -74,7 +73,6 @@ const generateMethodForModels = {
|
|||||||
PriceListRuleValue,
|
PriceListRuleValue,
|
||||||
PriceRule,
|
PriceRule,
|
||||||
Price,
|
Price,
|
||||||
PriceSetRuleType,
|
|
||||||
RuleType,
|
RuleType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +100,6 @@ export default class PricingModuleService
|
|||||||
protected readonly ruleTypeService_: RuleTypeService
|
protected readonly ruleTypeService_: RuleTypeService
|
||||||
protected readonly priceSetService_: ModulesSdkTypes.IMedusaInternalService<PriceSet>
|
protected readonly priceSetService_: ModulesSdkTypes.IMedusaInternalService<PriceSet>
|
||||||
protected readonly priceRuleService_: ModulesSdkTypes.IMedusaInternalService<PriceRule>
|
protected readonly priceRuleService_: ModulesSdkTypes.IMedusaInternalService<PriceRule>
|
||||||
protected readonly priceSetRuleTypeService_: ModulesSdkTypes.IMedusaInternalService<PriceSetRuleType>
|
|
||||||
protected readonly priceService_: ModulesSdkTypes.IMedusaInternalService<Price>
|
protected readonly priceService_: ModulesSdkTypes.IMedusaInternalService<Price>
|
||||||
protected readonly priceListService_: PriceListService
|
protected readonly priceListService_: PriceListService
|
||||||
protected readonly priceListRuleService_: ModulesSdkTypes.IMedusaInternalService<PriceListRule>
|
protected readonly priceListRuleService_: ModulesSdkTypes.IMedusaInternalService<PriceListRule>
|
||||||
@@ -115,7 +112,6 @@ export default class PricingModuleService
|
|||||||
ruleTypeService,
|
ruleTypeService,
|
||||||
priceSetService,
|
priceSetService,
|
||||||
priceRuleService,
|
priceRuleService,
|
||||||
priceSetRuleTypeService,
|
|
||||||
priceService,
|
priceService,
|
||||||
priceListService,
|
priceListService,
|
||||||
priceListRuleService,
|
priceListRuleService,
|
||||||
@@ -132,7 +128,6 @@ export default class PricingModuleService
|
|||||||
this.priceSetService_ = priceSetService
|
this.priceSetService_ = priceSetService
|
||||||
this.ruleTypeService_ = ruleTypeService
|
this.ruleTypeService_ = ruleTypeService
|
||||||
this.priceRuleService_ = priceRuleService
|
this.priceRuleService_ = priceRuleService
|
||||||
this.priceSetRuleTypeService_ = priceSetRuleTypeService
|
|
||||||
this.priceService_ = priceService
|
this.priceService_ = priceService
|
||||||
this.priceListService_ = priceListService
|
this.priceListService_ = priceListService
|
||||||
this.priceListRuleService_ = priceListRuleService
|
this.priceListRuleService_ = priceListRuleService
|
||||||
@@ -497,9 +492,7 @@ export default class PricingModuleService
|
|||||||
const { entities: upsertedPrices } =
|
const { entities: upsertedPrices } =
|
||||||
await this.priceService_.upsertWithReplace(
|
await this.priceService_.upsertWithReplace(
|
||||||
prices,
|
prices,
|
||||||
{
|
{ relations: ["price_rules"] },
|
||||||
relations: ["price_rules"],
|
|
||||||
},
|
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -527,37 +520,6 @@ export default class PricingModuleService
|
|||||||
return priceSets
|
return priceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
async addRules(
|
|
||||||
data: PricingTypes.AddRulesDTO,
|
|
||||||
sharedContext?: Context
|
|
||||||
): Promise<PricingTypes.PriceSetDTO>
|
|
||||||
|
|
||||||
async addRules(
|
|
||||||
data: PricingTypes.AddRulesDTO[],
|
|
||||||
sharedContext?: Context
|
|
||||||
): Promise<PricingTypes.PriceSetDTO[]>
|
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
|
||||||
async addRules(
|
|
||||||
data: PricingTypes.AddRulesDTO | PricingTypes.AddRulesDTO[],
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO> {
|
|
||||||
const inputs = Array.isArray(data) ? data : [data]
|
|
||||||
|
|
||||||
const priceSets = await this.addRules_(inputs, sharedContext)
|
|
||||||
|
|
||||||
const dbPriceSets = await this.listPriceSets(
|
|
||||||
{ id: priceSets.map(({ id }) => id) },
|
|
||||||
{ relations: ["rule_types"] }
|
|
||||||
)
|
|
||||||
|
|
||||||
const orderedPriceSets = priceSets.map((priceSet) => {
|
|
||||||
return dbPriceSets.find((p) => p.id === priceSet.id)!
|
|
||||||
})
|
|
||||||
|
|
||||||
return Array.isArray(data) ? orderedPriceSets : orderedPriceSets[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
async addPrices(
|
async addPrices(
|
||||||
data: AddPricesDTO,
|
data: AddPricesDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
@@ -591,50 +553,6 @@ export default class PricingModuleService
|
|||||||
return Array.isArray(data) ? orderedPriceSets : orderedPriceSets[0]
|
return Array.isArray(data) ? orderedPriceSets : orderedPriceSets[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
|
||||||
async removeRules(
|
|
||||||
data: PricingTypes.RemovePriceSetRulesDTO[],
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<void> {
|
|
||||||
const priceSets = await this.priceSetService_.list(
|
|
||||||
{ id: data.map((d) => d.id) },
|
|
||||||
{},
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSetIds = priceSets.map((ps) => ps.id)
|
|
||||||
const ruleTypes = await this.ruleTypeService_.list(
|
|
||||||
{
|
|
||||||
rule_attribute: data.map((d) => d.rules || []).flat(),
|
|
||||||
},
|
|
||||||
{ take: null },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const ruleTypeIds = ruleTypes.map((rt) => rt.id)
|
|
||||||
const priceSetRuleTypes = await this.priceSetRuleTypeService_.list(
|
|
||||||
{ price_set_id: priceSetIds, rule_type_id: ruleTypeIds },
|
|
||||||
{ take: null },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceRules = await this.priceRuleService_.list(
|
|
||||||
{ price_set_id: priceSetIds, rule_type_id: ruleTypeIds },
|
|
||||||
{ select: ["price"], take: null },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.priceSetRuleTypeService_.delete(
|
|
||||||
priceSetRuleTypes.map((psrt) => psrt.id),
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.priceService_.delete(
|
|
||||||
priceRules.map((pr) => pr.price.id),
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
@EmitEvents()
|
@EmitEvents()
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -723,7 +641,12 @@ export default class PricingModuleService
|
|||||||
const input = Array.isArray(data) ? data : [data]
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
const ruleAttributes = deduplicate(
|
const ruleAttributes = deduplicate(
|
||||||
data.map((d) => d.rules?.map((r) => r.rule_attribute) ?? []).flat()
|
data
|
||||||
|
.map(
|
||||||
|
(d) =>
|
||||||
|
d.prices?.map((ma) => Object.keys(ma?.rules ?? {})).flat() ?? []
|
||||||
|
)
|
||||||
|
.flat()
|
||||||
)
|
)
|
||||||
|
|
||||||
const ruleTypes = await this.ruleTypeService_.list(
|
const ruleTypes = await this.ruleTypeService_.list(
|
||||||
@@ -744,56 +667,27 @@ export default class PricingModuleService
|
|||||||
if (invalidRuleAttributes.length > 0) {
|
if (invalidRuleAttributes.length > 0) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.NOT_FOUND,
|
MedusaError.Types.NOT_FOUND,
|
||||||
`Rule types don't exist for: ${invalidRuleAttributes.join(", ")}`
|
`Rule types don't exist for prices with rule attribute: ${invalidRuleAttributes.join(
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidMoneyAmountRule = data
|
|
||||||
.map(
|
|
||||||
(d) => d.prices?.map((ma) => Object.keys(ma?.rules ?? {})).flat() ?? []
|
|
||||||
)
|
|
||||||
.flat()
|
|
||||||
.filter((r) => !ruleTypeMap.has(r))
|
|
||||||
|
|
||||||
if (invalidMoneyAmountRule.length > 0) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Rule types don't exist for money amounts with rule attribute: ${invalidMoneyAmountRule.join(
|
|
||||||
", "
|
", "
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ruleSetRuleTypeToCreateMap: Map<string, PriceSetRuleType> = new Map()
|
|
||||||
|
|
||||||
const toCreate = input.map((inputData) => {
|
const toCreate = input.map((inputData) => {
|
||||||
const id = generateEntityId(
|
const id = generateEntityId(
|
||||||
(inputData as unknown as PriceSet).id,
|
(inputData as unknown as PriceSet).id,
|
||||||
PriceSetIdPrefix
|
PriceSetIdPrefix
|
||||||
)
|
)
|
||||||
|
|
||||||
const { prices, rules = [], ...rest } = inputData
|
const { prices, ...rest } = inputData
|
||||||
|
|
||||||
let pricesData: CreatePricesDTO[] = []
|
let pricesData: CreatePricesDTO[] = []
|
||||||
|
|
||||||
rules.forEach((rule) => {
|
|
||||||
const priceSetRuleType = {
|
|
||||||
rule_type_id: ruleTypeMap.get(rule.rule_attribute).id,
|
|
||||||
price_set_id: id,
|
|
||||||
} as PriceSetRuleType
|
|
||||||
|
|
||||||
ruleSetRuleTypeToCreateMap.set(
|
|
||||||
JSON.stringify(priceSetRuleType),
|
|
||||||
priceSetRuleType
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (inputData.prices) {
|
if (inputData.prices) {
|
||||||
pricesData = inputData.prices.map((price) => {
|
pricesData = inputData.prices.map((price) => {
|
||||||
let { rules: priceRules = {}, ...rest } = price
|
let { rules: priceRules = {}, ...rest } = price
|
||||||
const cleanRules = priceRules ? removeNullish(priceRules) : {}
|
const cleanRules = priceRules ? removeNullish(priceRules) : {}
|
||||||
const numberOfRules = Object.keys(cleanRules).length
|
const numberOfRules = Object.keys(cleanRules).length
|
||||||
|
|
||||||
const rulesDataMap = new Map()
|
const rulesDataMap = new Map()
|
||||||
|
|
||||||
Object.entries(priceRules).map(([attribute, value]) => {
|
Object.entries(priceRules).map(([attribute, value]) => {
|
||||||
@@ -803,16 +697,6 @@ export default class PricingModuleService
|
|||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
rulesDataMap.set(JSON.stringify(rule), rule)
|
rulesDataMap.set(JSON.stringify(rule), rule)
|
||||||
|
|
||||||
const priceSetRuleType = {
|
|
||||||
rule_type_id: ruleTypeMap.get(attribute).id,
|
|
||||||
price_set_id: id,
|
|
||||||
} as PriceSetRuleType
|
|
||||||
|
|
||||||
ruleSetRuleTypeToCreateMap.set(
|
|
||||||
JSON.stringify(priceSetRuleType),
|
|
||||||
priceSetRuleType
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -880,99 +764,9 @@ export default class PricingModuleService
|
|||||||
sharedContext,
|
sharedContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (ruleSetRuleTypeToCreateMap.size) {
|
|
||||||
await this.priceSetRuleTypeService_.create(
|
|
||||||
Array.from(ruleSetRuleTypeToCreateMap.values()),
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return createdPriceSets
|
return createdPriceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
|
||||||
protected async addRules_(
|
|
||||||
inputs: PricingTypes.AddRulesDTO[],
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<PriceSet[]> {
|
|
||||||
const priceSets = await this.priceSetService_.list(
|
|
||||||
{ id: inputs.map((d) => d.priceSetId) },
|
|
||||||
{ relations: ["rule_types"] },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSetRuleTypeMap: Map<string, Map<string, RuleTypeDTO>> = new Map(
|
|
||||||
priceSets.map((priceSet) => [
|
|
||||||
priceSet.id,
|
|
||||||
new Map([...priceSet.rule_types].map((rt) => [rt.rule_attribute, rt])),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSetMap = new Map(priceSets.map((p) => [p.id, p]))
|
|
||||||
const invalidPriceSetInputs = inputs.filter(
|
|
||||||
(d) => !priceSetMap.has(d.priceSetId)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (invalidPriceSetInputs.length) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`PriceSets with ids: ${invalidPriceSetInputs
|
|
||||||
.map((d) => d.priceSetId)
|
|
||||||
.join(", ")} was not found`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ruleTypes = await this.ruleTypeService_.list(
|
|
||||||
{
|
|
||||||
rule_attribute: inputs
|
|
||||||
.map((data) => data.rules.map((r) => r.attribute))
|
|
||||||
.flat(),
|
|
||||||
},
|
|
||||||
{ take: null },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
|
|
||||||
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
|
||||||
)
|
|
||||||
|
|
||||||
const invalidRuleAttributeInputs = inputs
|
|
||||||
.map((d) => d.rules.map((r) => r.attribute))
|
|
||||||
.flat()
|
|
||||||
.filter((r) => !ruleTypeMap.has(r))
|
|
||||||
|
|
||||||
if (invalidRuleAttributeInputs.length) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Rule types don't exist for attributes: ${[
|
|
||||||
...new Set(invalidRuleAttributeInputs),
|
|
||||||
].join(", ")}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const priceSetRuleTypesCreate: PricingTypes.CreatePriceSetRuleTypeDTO[] = []
|
|
||||||
|
|
||||||
inputs.forEach((data) => {
|
|
||||||
for (const rule of data.rules) {
|
|
||||||
if (priceSetRuleTypeMap.get(data.priceSetId)!.has(rule.attribute)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
priceSetRuleTypesCreate.push({
|
|
||||||
rule_type_id: ruleTypeMap.get(rule.attribute)!.id,
|
|
||||||
price_set_id: priceSetMap.get(data.priceSetId)!.id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await this.priceSetRuleTypeService_.create(
|
|
||||||
priceSetRuleTypesCreate,
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
return priceSets
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
protected async addPrices_(
|
protected async addPrices_(
|
||||||
input: AddPricesDTO[],
|
input: AddPricesDTO[],
|
||||||
@@ -1365,9 +1159,6 @@ export default class PricingModuleService
|
|||||||
const ruleTypeAttributes: string[] = []
|
const ruleTypeAttributes: string[] = []
|
||||||
const priceListIds: string[] = []
|
const priceListIds: string[] = []
|
||||||
const priceIds: string[] = []
|
const priceIds: string[] = []
|
||||||
const priceSetIds = data
|
|
||||||
.map((d) => d.prices.map((price) => price.price_set_id))
|
|
||||||
.flat()
|
|
||||||
|
|
||||||
for (const priceListData of data) {
|
for (const priceListData of data) {
|
||||||
priceListIds.push(priceListData.price_list_id)
|
priceListIds.push(priceListData.price_list_id)
|
||||||
@@ -1398,53 +1189,6 @@ export default class PricingModuleService
|
|||||||
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
||||||
)
|
)
|
||||||
|
|
||||||
const priceSets = await this.listPriceSets(
|
|
||||||
{ id: priceSetIds },
|
|
||||||
{ relations: ["rule_types"] },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSetRuleTypeMap: Map<string, Set<string>> = priceSets.reduce(
|
|
||||||
(acc, curr) => {
|
|
||||||
const priceSetRuleAttributeSet: Set<string> =
|
|
||||||
acc.get(curr.id) || new Set()
|
|
||||||
|
|
||||||
for (const rt of curr.rule_types ?? []) {
|
|
||||||
priceSetRuleAttributeSet.add(rt.rule_attribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.set(curr.id, priceSetRuleAttributeSet)
|
|
||||||
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
new Map()
|
|
||||||
)
|
|
||||||
|
|
||||||
const ruleTypeErrors: string[] = []
|
|
||||||
|
|
||||||
for (const priceListData of data) {
|
|
||||||
for (const price of priceListData.prices) {
|
|
||||||
for (const ruleAttribute of Object.keys(price.rules ?? {})) {
|
|
||||||
if (
|
|
||||||
!priceSetRuleTypeMap.get(price.price_set_id)?.has(ruleAttribute)
|
|
||||||
) {
|
|
||||||
ruleTypeErrors.push(
|
|
||||||
`rule_attribute "${ruleAttribute}" in price set ${price.price_set_id}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleTypeErrors.length) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Invalid rule type configuration: Price set rules doesn't exist for ${ruleTypeErrors.join(
|
|
||||||
", "
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const priceLists = await this.listPriceLists(
|
const priceLists = await this.listPriceLists(
|
||||||
{ id: priceListIds },
|
{ id: priceListIds },
|
||||||
{ take: null },
|
{ take: null },
|
||||||
@@ -1532,52 +1276,6 @@ export default class PricingModuleService
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
const priceSets = await this.listPriceSets(
|
|
||||||
{ id: priceSetIds },
|
|
||||||
{ relations: ["rule_types"] },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSetRuleTypeMap: Map<string, Set<string>> = priceSets.reduce(
|
|
||||||
(acc, curr) => {
|
|
||||||
const priceSetRuleAttributeSet: Set<string> =
|
|
||||||
acc.get(curr.id) || new Set()
|
|
||||||
|
|
||||||
for (const rt of curr.rule_types ?? []) {
|
|
||||||
priceSetRuleAttributeSet.add(rt.rule_attribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.set(curr.id, priceSetRuleAttributeSet)
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
new Map()
|
|
||||||
)
|
|
||||||
|
|
||||||
const ruleTypeErrors: string[] = []
|
|
||||||
|
|
||||||
for (const priceListData of data) {
|
|
||||||
for (const price of priceListData.prices) {
|
|
||||||
for (const rule_attribute of Object.keys(price.rules ?? {})) {
|
|
||||||
if (
|
|
||||||
!priceSetRuleTypeMap.get(price.price_set_id)?.has(rule_attribute)
|
|
||||||
) {
|
|
||||||
ruleTypeErrors.push(
|
|
||||||
`rule_attribute "${rule_attribute}" in price set ${price.price_set_id}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleTypeErrors.length) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Invalid rule type configuration: Price set rules doesn't exist for ${ruleTypeErrors.join(
|
|
||||||
", "
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
|
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
|
||||||
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user