fix: Disallow creating duplicate prices (#7866)
* fix: Disallow creating duplicate prices * fix: Don't pass id to manager create in upsertWithReplace
This commit is contained in:
1 parent
87375db9ef
commit
b4aa7fb9a7
12 files changed
+560
-350
No files matched your search
@@ -113,7 +113,9 @@ medusaIntegrationTestRunner({
|
|||||||
{
|
{
|
||||||
amount: 105,
|
amount: 105,
|
||||||
currency_code: region1.currency_code,
|
currency_code: region1.currency_code,
|
||||||
region_id: region1.id,
|
rules: {
|
||||||
|
region_id: region1.id,
|
||||||
|
},
|
||||||
variant_id: product1.variants[0].id,
|
variant_id: product1.variants[0].id,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -508,84 +510,61 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip("Adds a batch of new prices to a price list overriding existing prices", async () => {
|
it("Adds a batch of new prices to a price list overriding existing prices", async () => {
|
||||||
// BREAKING: There is no support for overriding configuration
|
// BREAKING: The payload of the batch request changed
|
||||||
|
// BREAKING: The create dataset does an upsert (before an explicit `override` flag was passed)
|
||||||
const payload = {
|
const payload = {
|
||||||
prices: [
|
create: [
|
||||||
{
|
{
|
||||||
amount: 45,
|
amount: 45,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
variant_id: "test-variant",
|
variant_id: product1.variants[0].id,
|
||||||
min_quantity: 1001,
|
min_quantity: 1,
|
||||||
max_quantity: 2000,
|
max_quantity: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
amount: 35,
|
amount: 35,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
variant_id: "test-variant",
|
variant_id: product1.variants[0].id,
|
||||||
min_quantity: 2001,
|
min_quantity: 101,
|
||||||
max_quantity: 3000,
|
max_quantity: 500,
|
||||||
},
|
|
||||||
{
|
|
||||||
amount: 25,
|
|
||||||
currency_code: "usd",
|
|
||||||
variant_id: "test-variant",
|
|
||||||
min_quantity: 3001,
|
|
||||||
max_quantity: 4000,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
override: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await api.post(
|
await api.post(
|
||||||
"/admin/price-lists/pl_no_customer_groups/prices/batch",
|
`/admin/price-lists/${pricelist1.id}/prices/batch`,
|
||||||
payload,
|
payload,
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/price-lists/${pricelist1.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.price_list.prices.length).toEqual(3)
|
expect(response.data.price_list.prices.length).toEqual(2)
|
||||||
expect(response.data.price_list.prices).toMatchSnapshot([
|
expect(response.data.price_list.prices).toEqual(
|
||||||
{
|
expect.arrayContaining([
|
||||||
id: expect.any(String),
|
expect.objectContaining({
|
||||||
price_list_id: "pl_no_customer_groups",
|
id: expect.any(String),
|
||||||
amount: 45,
|
amount: 45,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
variant_id: "test-variant",
|
min_quantity: "1",
|
||||||
min_quantity: 1001,
|
max_quantity: "100",
|
||||||
max_quantity: 2000,
|
variant_id: product1.variants[0].id,
|
||||||
created_at: expect.any(String),
|
}),
|
||||||
updated_at: expect.any(String),
|
expect.objectContaining({
|
||||||
variant: expect.any(Object),
|
id: expect.any(String),
|
||||||
variants: expect.any(Array),
|
amount: 35,
|
||||||
},
|
currency_code: "usd",
|
||||||
{
|
min_quantity: "101",
|
||||||
id: expect.any(String),
|
max_quantity: "500",
|
||||||
price_list_id: "pl_no_customer_groups",
|
variant_id: product1.variants[0].id,
|
||||||
amount: 35,
|
}),
|
||||||
currency_code: "usd",
|
])
|
||||||
variant_id: "test-variant",
|
)
|
||||||
min_quantity: 2001,
|
|
||||||
max_quantity: 3000,
|
|
||||||
created_at: expect.any(String),
|
|
||||||
updated_at: expect.any(String),
|
|
||||||
variant: expect.any(Object),
|
|
||||||
variants: expect.any(Array),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: expect.any(String),
|
|
||||||
price_list_id: "pl_no_customer_groups",
|
|
||||||
amount: 25,
|
|
||||||
currency_code: "usd",
|
|
||||||
variant_id: "test-variant",
|
|
||||||
min_quantity: 3001,
|
|
||||||
max_quantity: 4000,
|
|
||||||
created_at: expect.any(String),
|
|
||||||
updated_at: expect.any(String),
|
|
||||||
variant: expect.any(Object),
|
|
||||||
variants: expect.any(Array),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Adds a batch of new prices where a MA record have a `region_id` instead of `currency_code`", async () => {
|
it("Adds a batch of new prices where a MA record have a `region_id` instead of `currency_code`", async () => {
|
||||||
@@ -596,7 +575,9 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 100,
|
amount: 100,
|
||||||
variant_id: product1.variants[0].id,
|
variant_id: product1.variants[0].id,
|
||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
region_id: region1.id,
|
rules: {
|
||||||
|
region_id: region1.id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
amount: 200,
|
amount: 200,
|
||||||
@@ -640,7 +621,7 @@ medusaIntegrationTestRunner({
|
|||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
amount: 100,
|
amount: 100,
|
||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
// region_id: region1.id,
|
rules: { region_id: region1.id },
|
||||||
variant_id: product1.variants[0].id,
|
variant_id: product1.variants[0].id,
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|||||||
import {
|
import {
|
||||||
AddPriceListPricesDTO,
|
AddPriceListPricesDTO,
|
||||||
CreatePriceListPriceDTO,
|
CreatePriceListPriceDTO,
|
||||||
|
CreatePriceListPriceWorkflowDTO,
|
||||||
CreatePriceListPricesWorkflowStepDTO,
|
CreatePriceListPricesWorkflowStepDTO,
|
||||||
IPricingModuleService,
|
IPricingModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
@@ -22,10 +23,12 @@ export const createPriceListPricesStep = createStep(
|
|||||||
const pricesToAdd: CreatePriceListPriceDTO[] = []
|
const pricesToAdd: CreatePriceListPriceDTO[] = []
|
||||||
|
|
||||||
for (const price of prices) {
|
for (const price of prices) {
|
||||||
pricesToAdd.push({
|
const toPush = {
|
||||||
...price,
|
...price,
|
||||||
price_set_id: variantPriceSetMap[price.variant_id!],
|
price_set_id: variantPriceSetMap[price.variant_id!],
|
||||||
})
|
} as CreatePriceListPriceDTO
|
||||||
|
delete (toPush as Partial<CreatePriceListPriceWorkflowDTO>).variant_id
|
||||||
|
pricesToAdd.push(toPush)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pricesToAdd.length) {
|
if (pricesToAdd.length) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
PriceDTO,
|
PriceDTO,
|
||||||
UpdatePriceListPriceDTO,
|
UpdatePriceListPriceDTO,
|
||||||
UpdatePriceListPricesDTO,
|
UpdatePriceListPricesDTO,
|
||||||
|
UpdatePriceListPriceWorkflowDTO,
|
||||||
UpdatePriceListPriceWorkflowStepDTO,
|
UpdatePriceListPriceWorkflowStepDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { buildPriceSetPricesForModule } from "@medusajs/utils"
|
import { buildPriceSetPricesForModule } from "@medusajs/utils"
|
||||||
@@ -25,10 +26,13 @@ export const updatePriceListPricesStep = createStep(
|
|||||||
const { prices = [], id } = priceListData
|
const { prices = [], id } = priceListData
|
||||||
|
|
||||||
for (const price of prices) {
|
for (const price of prices) {
|
||||||
pricesToUpdate.push({
|
const toPush = {
|
||||||
...price,
|
...price,
|
||||||
price_set_id: variantPriceSetMap[price.variant_id!],
|
price_set_id: variantPriceSetMap[price.variant_id!],
|
||||||
})
|
} as UpdatePriceListPriceDTO
|
||||||
|
delete (toPush as Partial<UpdatePriceListPriceWorkflowDTO>).variant_id
|
||||||
|
|
||||||
|
pricesToUpdate.push(toPush)
|
||||||
|
|
||||||
if (price.id) {
|
if (price.id) {
|
||||||
priceIds.push(price.id)
|
priceIds.push(price.id)
|
||||||
|
|||||||
@@ -747,16 +747,22 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
entityName: string,
|
entityName: string,
|
||||||
data: any
|
data: any
|
||||||
): Record<string, any> & { id: string } {
|
): Record<string, any> & { id: string } {
|
||||||
const created = manager.create(entityName, data, {
|
// We set the id to undefined to make sure the entity isn't fetched from the entity map if it is an update,
|
||||||
managed: false,
|
// giving us incorrect data for the bignumberdata field (I though managed: false and persist: false would already do this)
|
||||||
persist: false,
|
const created = manager.create(
|
||||||
})
|
entityName,
|
||||||
|
{ ...data, id: undefined },
|
||||||
|
{
|
||||||
|
managed: false,
|
||||||
|
persist: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const resp = {
|
const resp = {
|
||||||
// `create` will omit non-existent fields, but we want to pass the data the user provided through so the correct errors get thrown
|
// `create` will omit non-existent fields, but we want to pass the data the user provided through so the correct errors get thrown
|
||||||
...data,
|
...data,
|
||||||
...(created as any).__helper.__bignumberdata,
|
...(created as any).__helper.__bignumberdata,
|
||||||
id: (created as any).id,
|
id: data.id ?? (created as any).id,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-persist relation columns should be removed before we do the upsert.
|
// Non-persist relation columns should be removed before we do the upsert.
|
||||||
|
|||||||
+1
-1
@@ -799,7 +799,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
{
|
{
|
||||||
id: "price-set-PLN",
|
id: "price-set-PLN",
|
||||||
is_calculated_price_price_list: true,
|
is_calculated_price_price_list: true,
|
||||||
calculated_amount: 116,
|
calculated_amount: 232,
|
||||||
is_original_price_price_list: false,
|
is_original_price_price_list: false,
|
||||||
original_amount: 400,
|
original_amount: 400,
|
||||||
currency_code: "PLN",
|
currency_code: "PLN",
|
||||||
|
|||||||
+120
@@ -586,6 +586,63 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should take the later price when passing two equivalent prices twice", async () => {
|
||||||
|
const [created] = await service.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test",
|
||||||
|
description: "test",
|
||||||
|
starts_at: "10/10/2010",
|
||||||
|
ends_at: "10/20/2030",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
currency_code: "EUR",
|
||||||
|
price_set_id: "price-set-1",
|
||||||
|
rules: {
|
||||||
|
region_id: "DE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 600,
|
||||||
|
currency_code: "EUR",
|
||||||
|
price_set_id: "price-set-1",
|
||||||
|
rules: {
|
||||||
|
region_id: "DE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const [priceList] = await service.listPriceLists(
|
||||||
|
{
|
||||||
|
id: [created.id],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["prices", "prices.price_rules"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(priceList).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
prices: [
|
||||||
|
expect.objectContaining({
|
||||||
|
rules_count: 1,
|
||||||
|
price_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
value: "DE",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
amount: 600,
|
||||||
|
currency_code: "EUR",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("addPriceListPrices", () => {
|
describe("addPriceListPrices", () => {
|
||||||
@@ -810,6 +867,69 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should do an update to the price if an equivalent price already exists", async () => {
|
||||||
|
const [priceSet] = await service.createPriceSets([{}])
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await service.addPriceListPrices([
|
||||||
|
{
|
||||||
|
price_list_id: "price-list-1",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: "test-price-id",
|
||||||
|
amount: 234,
|
||||||
|
currency_code: "EUR",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
rules: {
|
||||||
|
region_id: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const [priceList] = await service.listPriceLists(
|
||||||
|
{ id: ["price-list-1"] },
|
||||||
|
{
|
||||||
|
relations: ["prices", "prices.price_rules"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(priceList).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
price_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
value: "test",
|
||||||
|
attribute: "region_id",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
amount: 234,
|
||||||
|
currency_code: "EUR",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("removePrices", () => {
|
describe("removePrices", () => {
|
||||||
|
|||||||
+124
-2
@@ -398,6 +398,40 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should upsert the later price when setting a price set with existing equivalent rules", async () => {
|
||||||
|
await service.updatePriceSets(id, {
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 100,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "1234" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "1234" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const priceSet = await service.retrievePriceSet(id, {
|
||||||
|
relations: ["prices", "prices.price_rules"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(priceSet.prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
price_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "region_id",
|
||||||
|
value: "1234",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
@@ -512,6 +546,43 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should take the later price when passing two prices with equivalent rules", async () => {
|
||||||
|
await service.createPriceSets([
|
||||||
|
{
|
||||||
|
id: "price-set-new",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 100,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "1234" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "1234" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as CreatePriceSetDTO,
|
||||||
|
])
|
||||||
|
|
||||||
|
const priceSet = await service.retrievePriceSet("price-set-new", {
|
||||||
|
relations: ["prices", "prices.price_rules"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(priceSet.prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
price_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "region_id",
|
||||||
|
value: "1234",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("addPrices", () => {
|
describe("addPrices", () => {
|
||||||
@@ -523,7 +594,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
{
|
{
|
||||||
amount: 100,
|
amount: 100,
|
||||||
currency_code: "USD",
|
currency_code: "USD",
|
||||||
rules: { currency_code: "USD" },
|
rules: { region_id: "1234" },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -574,7 +645,7 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
{
|
{
|
||||||
amount: 100,
|
amount: 100,
|
||||||
currency_code: "USD",
|
currency_code: "USD",
|
||||||
rules: { currency_code: "USD" },
|
rules: { region_id: "region-1" },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -616,6 +687,57 @@ moduleIntegrationTestRunner<IPricingModuleService>({
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should do an update if a price exists with the equivalent rules", async () => {
|
||||||
|
await service.addPrices([
|
||||||
|
{
|
||||||
|
priceSetId: "price-set-1",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 100,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "123" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await service.addPrices([
|
||||||
|
{
|
||||||
|
priceSetId: "price-set-1",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
rules: { region_id: "123" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const priceSet = await service.retrievePriceSet("price-set-1", {
|
||||||
|
relations: ["prices", "prices.price_rules"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(
|
||||||
|
priceSet.prices?.sort((a: any, b: any) => a.amount - b.amount)
|
||||||
|
).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 200,
|
||||||
|
currency_code: "USD",
|
||||||
|
price_rules: [
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "region_id",
|
||||||
|
value: "123",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 500,
|
||||||
|
currency_code: "USD",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -163,6 +163,9 @@ export class PricingRepository
|
|||||||
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",
|
||||||
price_list_id: "price.price_list_id",
|
price_list_id: "price.price_list_id",
|
||||||
|
all_rules_count: knex.raw(
|
||||||
|
"COALESCE(price.rules_count, 0) + COALESCE(price.pl_rules_count, 0)"
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.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")
|
||||||
@@ -171,8 +174,8 @@ export class PricingRepository
|
|||||||
|
|
||||||
.orderBy([
|
.orderBy([
|
||||||
{ column: "price.has_price_list", order: "asc" },
|
{ column: "price.has_price_list", order: "asc" },
|
||||||
|
{ column: "all_rules_count", order: "desc" },
|
||||||
{ column: "amount", order: "asc" },
|
{ column: "amount", order: "asc" },
|
||||||
{ column: "rules_count", order: "desc" },
|
|
||||||
])
|
])
|
||||||
|
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
ModulesSdkTypes,
|
ModulesSdkTypes,
|
||||||
PriceDTO,
|
|
||||||
PriceSetDTO,
|
PriceSetDTO,
|
||||||
PricingContext,
|
PricingContext,
|
||||||
PricingFilters,
|
PricingFilters,
|
||||||
@@ -32,6 +31,7 @@ import {
|
|||||||
PriceListType,
|
PriceListType,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
removeNullish,
|
removeNullish,
|
||||||
|
simpleHash,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
import { Price, PriceList, PriceListRule, PriceRule, PriceSet } from "@models"
|
import { Price, PriceList, PriceListRule, PriceRule, PriceSet } from "@models"
|
||||||
@@ -39,7 +39,7 @@ import { Price, PriceList, PriceListRule, PriceRule, PriceSet } from "@models"
|
|||||||
import { ServiceTypes } from "@types"
|
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 { CreatePriceListDTO } from "src/types/services"
|
import { CreatePriceListDTO, UpsertPriceDTO } from "src/types/services"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -426,35 +426,6 @@ export default class PricingModuleService
|
|||||||
return isString(idOrSelector) ? priceSets[0] : priceSets
|
return isString(idOrSelector) ? priceSets[0] : priceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
private async normalizeUpdateData(data: ServiceTypes.UpdatePriceSetInput[]) {
|
|
||||||
return data.map((priceSet) => {
|
|
||||||
const prices = priceSet.prices?.map((price) => {
|
|
||||||
const rules = Object.entries(price.rules ?? {}).map(
|
|
||||||
([attribute, value]) => {
|
|
||||||
return {
|
|
||||||
attribute,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const hasRulesInput = isPresent(price.rules)
|
|
||||||
delete price.rules
|
|
||||||
return {
|
|
||||||
...price,
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
price_rules: hasRulesInput ? rules : undefined,
|
|
||||||
rules_count: hasRulesInput ? rules.length : undefined,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...priceSet,
|
|
||||||
prices,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
protected async updatePriceSets_(
|
protected async updatePriceSets_(
|
||||||
data: ServiceTypes.UpdatePriceSetInput[],
|
data: ServiceTypes.UpdatePriceSetInput[],
|
||||||
@@ -496,6 +467,67 @@ export default class PricingModuleService
|
|||||||
return priceSets
|
return priceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async normalizeUpdateData(data: ServiceTypes.UpdatePriceSetInput[]) {
|
||||||
|
return data.map((priceSet) => {
|
||||||
|
return {
|
||||||
|
...priceSet,
|
||||||
|
prices: this.normalizePrices(
|
||||||
|
priceSet.prices?.map((p) => ({ ...p, price_set_id: priceSet.id })),
|
||||||
|
[]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizePrices(
|
||||||
|
data: CreatePricesDTO[] | undefined,
|
||||||
|
existingPrices: PricingTypes.PriceDTO[],
|
||||||
|
priceListId?: string | undefined
|
||||||
|
) {
|
||||||
|
const pricesToUpsert = new Map<
|
||||||
|
string,
|
||||||
|
CreatePricesDTO & { price_rules?: CreatePriceRuleDTO[] }
|
||||||
|
>()
|
||||||
|
const existingPricesMap = new Map<string, PricingTypes.PriceDTO>()
|
||||||
|
existingPrices?.forEach((price) => {
|
||||||
|
existingPricesMap.set(hashPrice(price), price)
|
||||||
|
})
|
||||||
|
|
||||||
|
data?.forEach((price) => {
|
||||||
|
const cleanRules = price.rules ? removeNullish(price.rules) : {}
|
||||||
|
const ruleEntries = Object.entries(cleanRules)
|
||||||
|
const rules = ruleEntries.map(([attribute, value]) => {
|
||||||
|
return {
|
||||||
|
attribute,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasRulesInput = isPresent(price.rules)
|
||||||
|
const entry = {
|
||||||
|
...price,
|
||||||
|
price_list_id: priceListId,
|
||||||
|
price_rules: hasRulesInput ? rules : undefined,
|
||||||
|
rules_count: hasRulesInput ? ruleEntries.length : undefined,
|
||||||
|
} as UpsertPriceDTO
|
||||||
|
delete (entry as CreatePricesDTO).rules
|
||||||
|
|
||||||
|
const entryHash = hashPrice(entry)
|
||||||
|
|
||||||
|
// We want to keep the existing rules as they might already have ids, but any other data should come from the updated input
|
||||||
|
const existing = existingPricesMap.get(entryHash)
|
||||||
|
pricesToUpsert.set(entryHash, {
|
||||||
|
...entry,
|
||||||
|
id: existing?.id ?? entry.id,
|
||||||
|
price_rules: existing?.price_rules ?? entry.price_rules,
|
||||||
|
})
|
||||||
|
|
||||||
|
return entry
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(pricesToUpsert.values())
|
||||||
|
}
|
||||||
|
|
||||||
async addPrices(
|
async addPrices(
|
||||||
data: AddPricesDTO,
|
data: AddPricesDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
@@ -619,35 +651,8 @@ export default class PricingModuleService
|
|||||||
const toCreate = input.map((inputData) => {
|
const toCreate = input.map((inputData) => {
|
||||||
const entry = {
|
const entry = {
|
||||||
...inputData,
|
...inputData,
|
||||||
|
prices: this.normalizePrices(inputData.prices, []),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputData.prices) {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
const pricesData: CreatePricesDTO[] = inputData.prices.map((price) => {
|
|
||||||
let { rules: priceRules = {}, ...rest } = price
|
|
||||||
const cleanRules = priceRules ? removeNullish(priceRules) : {}
|
|
||||||
const rules = Object.entries(cleanRules)
|
|
||||||
const numberOfRules = rules.length
|
|
||||||
|
|
||||||
const rulesDataMap = new Map()
|
|
||||||
rules.map(([attribute, value]) => {
|
|
||||||
const rule = {
|
|
||||||
attribute,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
rulesDataMap.set(JSON.stringify(rule), rule)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
rules_count: numberOfRules,
|
|
||||||
price_rules: Array.from(rulesDataMap.values()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
entry.prices = pricesData
|
|
||||||
return entry
|
return entry
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -710,86 +715,74 @@ export default class PricingModuleService
|
|||||||
) {
|
) {
|
||||||
const priceSets = await this.listPriceSets(
|
const priceSets = await this.listPriceSets(
|
||||||
{ id: input.map((d) => d.priceSetId) },
|
{ id: input.map((d) => d.priceSetId) },
|
||||||
{},
|
{ take: null, relations: ["prices", "prices.price_rules"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
const priceSetMap = new Map(priceSets.map((p) => [p.id, p]))
|
const existingPrices = priceSets
|
||||||
input.forEach(({ priceSetId }) => {
|
.map((p) => p.prices)
|
||||||
const priceSet = priceSetMap.get(priceSetId)
|
.flat() as PricingTypes.PriceDTO[]
|
||||||
|
|
||||||
|
const pricesToUpsert = input
|
||||||
|
.map((addPrice) =>
|
||||||
|
this.normalizePrices(
|
||||||
|
addPrice.prices?.map((p) => ({
|
||||||
|
...p,
|
||||||
|
price_set_id: addPrice.priceSetId,
|
||||||
|
})),
|
||||||
|
existingPrices
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(Boolean)
|
||||||
|
.flat() as UpsertPriceDTO[]
|
||||||
|
|
||||||
|
const priceSetMap = new Map<string, PriceSetDTO>(
|
||||||
|
priceSets.map((p) => [p.id, p])
|
||||||
|
)
|
||||||
|
pricesToUpsert.forEach((price) => {
|
||||||
|
const priceSet = priceSetMap.get(price.price_set_id)
|
||||||
|
|
||||||
if (!priceSet) {
|
if (!priceSet) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Price set with id: ${priceSetId} not found`
|
`Price set with id: ${price.price_set_id} not found`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const pricesToCreate: PricingTypes.CreatePriceDTO[] = input.flatMap(
|
const { entities, performedActions } =
|
||||||
({ priceSetId, prices }) =>
|
await this.priceService_.upsertWithReplace(
|
||||||
prices.map((price) => {
|
pricesToUpsert,
|
||||||
const numberOfRules = Object.entries(price?.rules ?? {}).length
|
{ relations: ["price_rules"] },
|
||||||
|
sharedContext
|
||||||
const priceRules = Object.entries(price.rules ?? {}).map(
|
)
|
||||||
([attribute, value]) => ({
|
|
||||||
price_set_id: priceSetId,
|
|
||||||
attribute: attribute,
|
|
||||||
value,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...price,
|
|
||||||
price_set_id: priceSetId,
|
|
||||||
rules_count: numberOfRules,
|
|
||||||
price_rules: priceRules,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const prices = await this.priceService_.create(
|
|
||||||
pricesToCreate,
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preparing data for emitting events
|
|
||||||
*/
|
|
||||||
const eventsData = prices.reduce(
|
|
||||||
(eventsData, price) => {
|
|
||||||
eventsData.prices.push({
|
|
||||||
id: price.id,
|
|
||||||
})
|
|
||||||
price.price_rules.map((priceRule) => {
|
|
||||||
eventsData.priceRules.push({
|
|
||||||
id: priceRule.id,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return eventsData
|
|
||||||
},
|
|
||||||
{
|
|
||||||
priceRules: [],
|
|
||||||
prices: [],
|
|
||||||
} as {
|
|
||||||
priceRules: { id: string }[]
|
|
||||||
prices: { id: string }[]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitting events for all created entities
|
|
||||||
*/
|
|
||||||
eventBuilders.createdPrice({
|
eventBuilders.createdPrice({
|
||||||
data: eventsData.prices,
|
data: performedActions.created[Price.name] ?? [],
|
||||||
sharedContext,
|
sharedContext,
|
||||||
})
|
})
|
||||||
eventBuilders.createdPriceRule({
|
eventBuilders.updatedPrice({
|
||||||
data: eventsData.priceRules,
|
data: performedActions.updated[Price.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.deletedPrice({
|
||||||
|
data: performedActions.deleted[Price.name] ?? [],
|
||||||
sharedContext,
|
sharedContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
return prices
|
eventBuilders.createdPriceRule({
|
||||||
|
data: performedActions.created[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.updatedPriceRule({
|
||||||
|
data: performedActions.updated[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.deletedPriceRule({
|
||||||
|
data: performedActions.deleted[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
return entities
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -807,29 +800,10 @@ export default class PricingModuleService
|
|||||||
} as CreatePriceListDTO
|
} as CreatePriceListDTO
|
||||||
|
|
||||||
if (priceListData.prices) {
|
if (priceListData.prices) {
|
||||||
const pricesData = priceListData.prices.map((price) => {
|
entry.prices = this.normalizePrices(
|
||||||
let { rules: priceRules = {}, ...rest } = price
|
priceListData.prices,
|
||||||
const cleanRules = priceRules ? removeNullish(priceRules) : {}
|
[]
|
||||||
const rules = Object.entries(cleanRules)
|
) as UpsertPriceDTO[]
|
||||||
const numberOfRules = rules.length
|
|
||||||
|
|
||||||
const rulesDataMap = new Map()
|
|
||||||
rules.map(([attribute, value]) => {
|
|
||||||
const rule = {
|
|
||||||
attribute,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
rulesDataMap.set(JSON.stringify(rule), rule)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
rules_count: numberOfRules,
|
|
||||||
price_rules: Array.from(rulesDataMap.values()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
entry.prices = pricesData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priceListData.rules) {
|
if (priceListData.rules) {
|
||||||
@@ -994,39 +968,29 @@ export default class PricingModuleService
|
|||||||
data: PricingTypes.UpdatePriceListPricesDTO[],
|
data: PricingTypes.UpdatePriceListPricesDTO[],
|
||||||
sharedContext: Context = {}
|
sharedContext: Context = {}
|
||||||
): Promise<Price[]> {
|
): Promise<Price[]> {
|
||||||
const priceListIds: string[] = []
|
|
||||||
const priceIds: string[] = []
|
|
||||||
|
|
||||||
for (const priceListData of data) {
|
|
||||||
priceListIds.push(priceListData.price_list_id)
|
|
||||||
|
|
||||||
for (const price of priceListData.prices) {
|
|
||||||
priceIds.push(price.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const prices = await this.listPrices(
|
|
||||||
{ id: priceIds },
|
|
||||||
{ take: null, relations: ["price_rules"] },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceMap: Map<string, PricingTypes.PriceDTO> = new Map(
|
|
||||||
prices.map((price) => [price.id, price])
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceLists = await this.listPriceLists(
|
const priceLists = await this.listPriceLists(
|
||||||
{ id: priceListIds },
|
{ id: data.map((p) => p.price_list_id) },
|
||||||
{ take: null },
|
{ take: null, relations: ["prices", "prices.price_rules"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const existingPrices = priceLists
|
||||||
|
.map((p) => p.prices ?? [])
|
||||||
|
.flat() as PricingTypes.PriceDTO[]
|
||||||
|
|
||||||
|
const pricesToUpsert = data
|
||||||
|
.map((addPrice) =>
|
||||||
|
this.normalizePrices(
|
||||||
|
addPrice.prices as UpsertPriceDTO[],
|
||||||
|
existingPrices,
|
||||||
|
addPrice.price_list_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(Boolean)
|
||||||
|
.flat() as UpsertPriceDTO[]
|
||||||
|
|
||||||
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
||||||
|
|
||||||
const pricesToUpdate: Partial<Price>[] = []
|
|
||||||
const priceRuleIdsToDelete: string[] = []
|
|
||||||
const priceRulesToCreate: CreatePriceRuleDTO[] = []
|
|
||||||
|
|
||||||
for (const { price_list_id: priceListId, prices } of data) {
|
for (const { price_list_id: priceListId, prices } of data) {
|
||||||
const priceList = priceListMap.get(priceListId)
|
const priceList = priceListMap.get(priceListId)
|
||||||
|
|
||||||
@@ -1036,38 +1000,15 @@ export default class PricingModuleService
|
|||||||
`Price list with id: ${priceListId} not found`
|
`Price list with id: ${priceListId} not found`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const priceData of prices) {
|
|
||||||
const { rules = {}, price_set_id, ...rest } = priceData
|
|
||||||
const price = priceMap.get(rest.id)!
|
|
||||||
const priceRules = price.price_rules!
|
|
||||||
|
|
||||||
priceRulesToCreate.push(
|
|
||||||
...Object.entries(rules).map(([ruleAttribute, ruleValue]) => ({
|
|
||||||
price_set_id,
|
|
||||||
attribute: ruleAttribute,
|
|
||||||
value: ruleValue,
|
|
||||||
price_id: price.id,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
|
|
||||||
pricesToUpdate.push({
|
|
||||||
...rest,
|
|
||||||
rules_count: Object.keys(rules).length,
|
|
||||||
} as unknown as Price)
|
|
||||||
|
|
||||||
priceRuleIdsToDelete.push(...priceRules.map((pr) => pr.id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [_deletedPriceRule, _createdPriceRule, updatedPrices] =
|
const { entities } = await this.priceService_.upsertWithReplace(
|
||||||
await promiseAll([
|
pricesToUpsert,
|
||||||
this.priceRuleService_.delete(priceRuleIdsToDelete),
|
{ relations: ["price_rules"] },
|
||||||
this.priceRuleService_.create(priceRulesToCreate),
|
sharedContext
|
||||||
this.priceService_.update(pricesToUpdate),
|
)
|
||||||
])
|
|
||||||
|
|
||||||
return updatedPrices
|
return entities
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -1083,97 +1024,73 @@ export default class PricingModuleService
|
|||||||
data: PricingTypes.AddPriceListPricesDTO[],
|
data: PricingTypes.AddPriceListPricesDTO[],
|
||||||
sharedContext: Context = {}
|
sharedContext: Context = {}
|
||||||
): Promise<Price[]> {
|
): Promise<Price[]> {
|
||||||
const priceListIds: string[] = []
|
|
||||||
|
|
||||||
for (const priceListData of data) {
|
|
||||||
priceListIds.push(priceListData.price_list_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const priceLists = await this.listPriceLists(
|
const priceLists = await this.listPriceLists(
|
||||||
{ id: priceListIds },
|
{ id: data.map((p) => p.price_list_id) },
|
||||||
{},
|
{ take: null, relations: ["prices", "prices.price_rules"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const existingPrices = priceLists
|
||||||
|
.map((p) => p.prices ?? [])
|
||||||
|
.flat() as PricingTypes.PriceDTO[]
|
||||||
|
|
||||||
|
const pricesToUpsert = data
|
||||||
|
.map((addPrice) =>
|
||||||
|
this.normalizePrices(
|
||||||
|
addPrice.prices,
|
||||||
|
existingPrices,
|
||||||
|
addPrice.price_list_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(Boolean)
|
||||||
|
.flat() as UpsertPriceDTO[]
|
||||||
|
|
||||||
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
||||||
|
pricesToUpsert.forEach((price) => {
|
||||||
const pricesToCreate: Partial<Price>[] = []
|
const priceList = priceListMap.get(price.price_list_id!)
|
||||||
|
|
||||||
for (const { price_list_id: priceListId, prices } of data) {
|
|
||||||
const priceList = priceListMap.get(priceListId)
|
|
||||||
|
|
||||||
if (!priceList) {
|
if (!priceList) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Price list with id: ${priceListId} not found`
|
`Price list with id: ${price.price_list_id} not found`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const priceListPricesToCreate = prices.map((priceData) => {
|
const { entities, performedActions } =
|
||||||
const priceRules = priceData.rules || {}
|
await this.priceService_.upsertWithReplace(
|
||||||
const noOfRules = Object.keys(priceRules).length
|
pricesToUpsert,
|
||||||
|
{ relations: ["price_rules"] },
|
||||||
const priceRulesToCreate = Object.entries(priceRules).map(
|
sharedContext
|
||||||
([ruleAttribute, ruleValue]) => {
|
)
|
||||||
return {
|
|
||||||
price_list_id: priceData.price_set_id,
|
|
||||||
attribute: ruleAttribute,
|
|
||||||
value: ruleValue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...priceData,
|
|
||||||
price_set_id: priceData.price_set_id,
|
|
||||||
title: "test",
|
|
||||||
price_list_id: priceList.id,
|
|
||||||
rules_count: noOfRules,
|
|
||||||
price_rules: priceRulesToCreate,
|
|
||||||
} as unknown as Price
|
|
||||||
})
|
|
||||||
|
|
||||||
pricesToCreate.push(...priceListPricesToCreate)
|
|
||||||
}
|
|
||||||
|
|
||||||
const createdPrices = await this.priceService_.create(
|
|
||||||
pricesToCreate,
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const eventsData = createdPrices.reduce(
|
|
||||||
(eventsData, price) => {
|
|
||||||
eventsData.prices.push({
|
|
||||||
id: price.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
price.price_rules.map((priceRule) => {
|
|
||||||
eventsData.priceRules.push({
|
|
||||||
id: priceRule.id,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return eventsData
|
|
||||||
},
|
|
||||||
{
|
|
||||||
priceRules: [],
|
|
||||||
prices: [],
|
|
||||||
} as {
|
|
||||||
priceRules: { id: string }[]
|
|
||||||
prices: { id: string }[]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
eventBuilders.createdPrice({
|
eventBuilders.createdPrice({
|
||||||
data: eventsData.prices,
|
data: performedActions.created[Price.name] ?? [],
|
||||||
sharedContext,
|
sharedContext,
|
||||||
})
|
})
|
||||||
eventBuilders.createdPriceRule({
|
eventBuilders.updatedPrice({
|
||||||
data: eventsData.priceRules,
|
data: performedActions.updated[Price.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.deletedPrice({
|
||||||
|
data: performedActions.deleted[Price.name] ?? [],
|
||||||
sharedContext,
|
sharedContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
return createdPrices
|
eventBuilders.createdPriceRule({
|
||||||
|
data: performedActions.created[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.updatedPriceRule({
|
||||||
|
data: performedActions.updated[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
eventBuilders.deletedPriceRule({
|
||||||
|
data: performedActions.deleted[PriceRule.name] ?? [],
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
return entities
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -1335,3 +1252,24 @@ export default class PricingModuleService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hashPrice = (
|
||||||
|
price: PricingTypes.PriceDTO | PricingTypes.CreatePricesDTO
|
||||||
|
): string => {
|
||||||
|
const data = Object.entries({
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
price_set_id: "price_set_id" in price ? price.price_set_id ?? null : null,
|
||||||
|
price_list_id:
|
||||||
|
"price_list_id" in price ? price.price_list_id ?? null : null,
|
||||||
|
min_quantity: price.min_quantity ? price.min_quantity.toString() : null,
|
||||||
|
max_quantity: price.max_quantity ? price.max_quantity.toString() : null,
|
||||||
|
...("price_rules" in price
|
||||||
|
? price.price_rules?.reduce((agg, pr) => {
|
||||||
|
agg[pr.attribute] = pr.value
|
||||||
|
return agg
|
||||||
|
}, {})
|
||||||
|
: {}),
|
||||||
|
}).sort(([a], [b]) => a.localeCompare(b))
|
||||||
|
|
||||||
|
return simpleHash(JSON.stringify(data))
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./price-list"
|
export * from "./price-list"
|
||||||
export * from "./price-set"
|
export * from "./price-set"
|
||||||
|
export * from "./price"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { PricingTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export interface UpsertPriceDTO
|
||||||
|
extends Omit<PricingTypes.CreatePriceDTO, "rules"> {
|
||||||
|
id?: string
|
||||||
|
price_list_id?: string
|
||||||
|
price_rules: PricingTypes.CreatePriceRuleDTO[]
|
||||||
|
}
|
||||||
@@ -42,4 +42,28 @@ export const eventBuilders = {
|
|||||||
object: "price_list_rule",
|
object: "price_list_rule",
|
||||||
eventsEnum: PricingEvents,
|
eventsEnum: PricingEvents,
|
||||||
}),
|
}),
|
||||||
|
updatedPrice: eventBuilderFactory({
|
||||||
|
source: Modules.PRICING,
|
||||||
|
action: CommonEvents.UPDATED,
|
||||||
|
object: "price",
|
||||||
|
eventsEnum: PricingEvents,
|
||||||
|
}),
|
||||||
|
updatedPriceRule: eventBuilderFactory({
|
||||||
|
source: Modules.PRICING,
|
||||||
|
action: CommonEvents.UPDATED,
|
||||||
|
object: "price_rule",
|
||||||
|
eventsEnum: PricingEvents,
|
||||||
|
}),
|
||||||
|
deletedPrice: eventBuilderFactory({
|
||||||
|
source: Modules.PRICING,
|
||||||
|
action: CommonEvents.DELETED,
|
||||||
|
object: "price",
|
||||||
|
eventsEnum: PricingEvents,
|
||||||
|
}),
|
||||||
|
deletedPriceRule: eventBuilderFactory({
|
||||||
|
source: Modules.PRICING,
|
||||||
|
action: CommonEvents.DELETED,
|
||||||
|
object: "price_rule",
|
||||||
|
eventsEnum: PricingEvents,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user