feat(medusa,core-flows,types): adds batch operations to price list prices (#7077)

what:

- adds batch operations to price list prices

RESOLVES CORE-1969
RESOLVES CORE-1970
This commit is contained in:
Riqwan Thamir
2024-04-17 17:36:09 +02:00
committed by GitHub
parent 08dc861dc1
commit 8d356217bd
35 changed files with 735 additions and 630 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
"@medusajs/types": patch
---
feat(medusa,core-flows,types): add batch updates to price list prices
@@ -24,7 +24,6 @@ medusaIntegrationTestRunner({
describe("Admin: Price Lists API", () => {
let appContainer
let product
let product2
let variant
let variant2
let region
@@ -56,11 +55,15 @@ medusaIntegrationTestRunner({
{
title: "test product variant",
},
{
title: "test product variant 2",
},
],
},
])
variant = product.variants[0]
variant2 = product.variants[1]
await pricingModule.createRuleTypes([
{ name: "Customer Group ID", rule_attribute: "customer_group_id" },
@@ -115,6 +118,7 @@ medusaIntegrationTestRunner({
ends_at: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
rules: {
customer_group_id: [customerGroup.id],
},
@@ -126,6 +130,10 @@ medusaIntegrationTestRunner({
min_quantity: null,
max_quantity: null,
variant_id: variant.id,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
price_set_id: expect.any(String),
rules: {
region_id: region.id,
},
@@ -135,7 +143,7 @@ medusaIntegrationTestRunner({
])
response = await api.get(
`/admin/price-lists?fields=id,created_at,rules,prices.rules,prices.amount`,
`/admin/price-lists?fields=id,created_at,prices.amount`,
adminHeaders
)
@@ -145,15 +153,10 @@ medusaIntegrationTestRunner({
{
id: expect.any(String),
created_at: expect.any(String),
rules: {
customer_group_id: [customerGroup.id],
},
prices: [
{
id: expect.any(String),
amount: 5000,
rules: {
region_id: region.id,
},
},
],
},
@@ -210,6 +213,7 @@ medusaIntegrationTestRunner({
ends_at: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
rules: {
customer_group_id: [customerGroup.id],
},
@@ -221,6 +225,10 @@ medusaIntegrationTestRunner({
min_quantity: null,
max_quantity: null,
variant_id: variant.id,
created_at: expect.any(String),
updated_at: expect.any(String),
price_set_id: expect.any(String),
deleted_at: null,
rules: {
region_id: region.id,
},
@@ -295,14 +303,15 @@ medusaIntegrationTestRunner({
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
title: "test price list",
description: "test",
type: "override",
status: "active",
starts_at: expect.any(String),
ends_at: null,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
rules: {
customer_group_id: [customerGroup.id],
},
@@ -314,6 +323,10 @@ medusaIntegrationTestRunner({
min_quantity: null,
max_quantity: null,
variant_id: variant.id,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
price_set_id: expect.any(String),
rules: {
region_id: region.id,
},
@@ -338,9 +351,10 @@ medusaIntegrationTestRunner({
.catch((e) => e)
expect(errorResponse.response.status).toEqual(400)
expect(errorResponse.response.data.message).toEqual(
"title must be a string, description must be a string, type must be one of the following values: sale, override, variant_id must be a string"
)
// TODO: reenable when this is translated
// expect(errorResponse.response.data.message).toEqual(
// "title must be a string, description must be a string, type must be one of the following values: sale, override, variant_id must be a string"
// )
})
})
@@ -468,142 +482,21 @@ medusaIntegrationTestRunner({
})
})
describe("POST /admin/price-lists/:id/prices/batch/add", () => {
it("should add price list prices successfully", async () => {
describe("POST /admin/price-lists/:id/prices/batch", () => {
it("should add, remove and delete price list prices in batch successfully", async () => {
const priceSet = await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
prices: [{ amount: 3000, currency_code: "usd" }],
})
const [priceList] = await pricingModule.createPriceLists([
{
title: "test price list",
description: "test",
prices: [
{
id: "test-price-id",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
rules: { region_id: region.id },
},
],
},
])
const data = {
prices: [
{
amount: 400,
variant_id: variant.id,
currency_code: "usd",
rules: { region_id: region.id },
},
],
}
const response = await api.post(
`admin/price-lists/${priceList.id}/prices/batch/add`,
data,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.price_list.prices.length).toEqual(2)
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
prices: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
currency_code: "usd",
amount: 400,
}),
expect.objectContaining({
id: "test-price-id",
currency_code: "usd",
amount: 5000,
}),
]),
})
)
})
})
describe("POST /admin/price-lists/:id/prices/batch/update", () => {
it("should update price list prices successfully", async () => {
const priceSet = await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
prices: [{ amount: 3000, currency_code: "usd" }],
})
const [priceList] = await pricingModule.createPriceLists([
{
title: "test price list",
description: "test",
prices: [
{
id: "test-price-id",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
rules: { region_id: region.id },
},
],
},
])
const data = {
prices: [
{
id: "test-price-id",
amount: 400,
variant_id: variant.id,
currency_code: "usd",
rules: { region_id: region.id },
},
],
}
const response = await api.post(
`admin/price-lists/${priceList.id}/prices/batch/update`,
data,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.price_list.prices.length).toEqual(1)
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
prices: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
currency_code: "usd",
amount: 400,
}),
]),
})
)
})
})
describe("POST /admin/price-lists/:id/prices/batch/remove", () => {
it("should remove price list prices successfully", async () => {
const priceSet = await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
prices: [],
})
const [createdPriceList] = await pricingModule.createPriceLists([
{
title: "test price list",
description: "test",
prices: [
{
id: "price-to-remove",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
@@ -611,6 +504,13 @@ medusaIntegrationTestRunner({
region_id: region.id,
},
},
{
id: "price-to-update",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
rules: { region_id: region.id },
},
],
},
])
@@ -619,21 +519,122 @@ medusaIntegrationTestRunner({
{ id: [createdPriceList.id] },
{ relations: ["prices"] }
)
const priceIdToDelete = priceList.prices![0].id
const priceIdToDelete = priceList.prices?.find(
(p) => p.id === "price-to-remove"
)
const data = {
create: [
{
amount: 400,
variant_id: variant.id,
currency_code: "usd",
rules: { region_id: region.id },
},
],
update: [
{
id: "price-to-update",
amount: 500,
variant_id: variant.id,
currency_code: "usd",
rules: { region_id: region.id },
},
],
delete: [priceIdToDelete?.id],
}
const response = await api.post(
`/admin/price-lists/${priceList.id}/prices/batch/remove`,
{ ids: [priceIdToDelete] },
`admin/price-lists/${priceList.id}/prices/batch`,
data,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
prices: [],
})
expect(response.data).toEqual({
created: [
expect.objectContaining({
id: expect.any(String),
currency_code: "usd",
amount: 400,
}),
],
updated: [
expect.objectContaining({
id: "price-to-update",
currency_code: "usd",
amount: 500,
}),
],
deleted: {
ids: ["price-to-remove"],
object: "price",
deleted: true,
},
})
})
it("should remove all price list prices of a product", async () => {
const priceSet = await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
prices: [{ amount: 3000, currency_code: "usd" }],
})
const priceSet2 = await createVariantPriceSet({
container: appContainer,
variantId: variant2.id,
prices: [{ amount: 3000, currency_code: "usd" }],
})
const [createdPriceList] = await pricingModule.createPriceLists([
{
title: "test price list",
description: "test",
prices: [
{
id: "price-to-delete-1",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
rules: {
region_id: region.id,
},
},
{
id: "price-to-delete-2",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet2.id,
rules: { region_id: region.id },
},
],
},
])
const [priceList] = await pricingModule.listPriceLists(
{ id: [createdPriceList.id] },
{ relations: ["prices"] }
)
const data = { product_id: [product.id] }
const response = await api.post(
`admin/price-lists/${priceList.id}/prices/batch`,
data,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
created: [],
updated: [],
deleted: {
ids: ["price-to-delete-1", "price-to-delete-2"],
object: "price",
deleted: true,
},
})
})
})
})
@@ -0,0 +1,23 @@
import { CreatePriceListPricesWorkflowDTO } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
import { createPriceListPricesWorkflow } from "../workflows/create-price-list-prices"
export const createPriceListPricesWorkflowStepId =
"create-price-list-prices-workflow-step"
export const createPriceListPricesWorkflowStep = createStep(
createPriceListPricesWorkflowStepId,
async (data: CreatePriceListPricesWorkflowDTO[], { container }) => {
const { transaction, result: created } =
await createPriceListPricesWorkflow(container).run({ input: { data } })
return new StepResponse(created, transaction)
},
async (transaction, { container }) => {
if (!transaction) {
return
}
await createPriceListPricesWorkflow(container).cancel({ transaction })
}
)
@@ -41,7 +41,7 @@ export const createPriceListPricesStep = createStep(
)
return new StepResponse(
null,
createdPrices,
createdPrices.map((p) => p.id)
)
},
@@ -16,7 +16,7 @@ export const getExistingPriceListsPriceIdsStep = createStep(
const existingPrices = priceListIds.length
? await pricingModule.listPrices(
{ price_list_id: priceListIds },
{ relations: ["price_list"] }
{ relations: ["price_list"], take: null }
)
: []
@@ -1,9 +1,12 @@
export * from "./create-price-list-prices"
export * from "./create-price-list-prices-workflow"
export * from "./create-price-lists"
export * from "./delete-price-lists"
export * from "./get-existing-price-lists-price-ids"
export * from "./remove-price-list-prices"
export * from "./remove-price-list-prices-workflow"
export * from "./update-price-list-prices"
export * from "./update-price-list-prices-workflow"
export * from "./update-price-lists"
export * from "./validate-price-lists"
export * from "./validate-variant-price-links"
@@ -0,0 +1,22 @@
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
import { removePriceListPricesWorkflow } from "../workflows/remove-price-list-prices"
export const removePriceListPricesWorkflowStepId =
"remove-price-list-prices-workflow"
export const removePriceListPricesWorkflowStep = createStep(
removePriceListPricesWorkflowStepId,
async (ids: string[], { container }) => {
const { transaction, result: updated } =
await removePriceListPricesWorkflow(container).run({ input: { ids } })
return new StepResponse(updated, transaction)
},
async (transaction, { container }) => {
if (!transaction) {
return
}
await removePriceListPricesWorkflow(container).cancel({ transaction })
}
)
@@ -7,7 +7,7 @@ export const removePriceListPricesStep = createStep(
removePriceListPricesStepId,
async (ids: string[], { container }) => {
if (!ids.length) {
return new StepResponse(null, [])
return new StepResponse([], [])
}
const pricingModule = container.resolve<IPricingModuleService>(
@@ -19,12 +19,11 @@ export const removePriceListPricesStep = createStep(
{ relations: ["price_list"] }
)
await pricingModule.softDeletePrices(prices.map((price) => price.id))
const priceIds = prices.map((price) => price.id)
return new StepResponse(
null,
prices.map((price) => price.id)
)
await pricingModule.softDeletePrices(priceIds)
return new StepResponse(priceIds, priceIds)
},
async (ids, { container }) => {
if (!ids?.length) {
@@ -0,0 +1,23 @@
import { UpdatePriceListPricesWorkflowDTO } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
import { updatePriceListPricesWorkflow } from "../workflows/update-price-list-prices"
export const updatePriceListPricesWorkflowStepId =
"update-price-list-prices-workflow"
export const updatePriceListPricesWorkflowStep = createStep(
updatePriceListPricesWorkflowStepId,
async (data: UpdatePriceListPricesWorkflowDTO[], { container }) => {
const { transaction, result: updated } =
await updatePriceListPricesWorkflow(container).run({ input: { data } })
return new StepResponse(updated, transaction)
},
async (transaction, { container }) => {
if (!transaction) {
return
}
await updatePriceListPricesWorkflow(container).cancel({ transaction })
}
)
@@ -65,9 +65,11 @@ export const updatePriceListPricesStep = createStep(
})
}
await pricingModule.updatePriceListPrices(priceListPricesToUpdate)
const updatedPrices = await pricingModule.updatePriceListPrices(
priceListPricesToUpdate
)
return new StepResponse(null, dataBeforePriceUpdate)
return new StepResponse(updatedPrices, dataBeforePriceUpdate)
},
async (dataBeforePriceUpdate, { container }) => {
if (!dataBeforePriceUpdate?.length) {
@@ -10,7 +10,7 @@ export const validateVariantPriceLinksStep = createStep(
validateVariantPriceLinksStepId,
async (
data: {
prices: {
prices?: {
variant_id: string
}[]
}[],
@@ -0,0 +1,39 @@
import {
BatchPriceListPricesWorkflowDTO,
BatchPriceListPricesWorkflowResult,
} from "@medusajs/types"
import {
WorkflowData,
createWorkflow,
parallelize,
transform,
} from "@medusajs/workflows-sdk"
import { createPriceListPricesWorkflowStep } from "../steps/create-price-list-prices-workflow"
import { removePriceListPricesWorkflowStep } from "../steps/remove-price-list-prices-workflow"
import { updatePriceListPricesWorkflowStep } from "../steps/update-price-list-prices-workflow"
export const batchPriceListPricesWorkflowId = "batch-price-list-prices"
export const batchPriceListPricesWorkflow = createWorkflow(
batchPriceListPricesWorkflowId,
(
input: WorkflowData<{
data: BatchPriceListPricesWorkflowDTO
}>
): WorkflowData<BatchPriceListPricesWorkflowResult> => {
const createInput = transform({ input: input.data }, (data) => [
{ id: data.input.id, prices: data.input.create },
])
const updateInput = transform({ input: input.data }, (data) => [
{ id: data.input.id, prices: data.input.update },
])
const [created, updated, deleted] = parallelize(
createPriceListPricesWorkflowStep(createInput),
updatePriceListPricesWorkflowStep(updateInput),
removePriceListPricesWorkflowStep(input.data.delete)
)
return transform({ created, updated, deleted }, (data) => data)
}
)
@@ -1,14 +1,13 @@
import { CreatePriceListPricesWorkflowDTO } from "@medusajs/types"
import { PricingTypes } from "@medusajs/types/src"
import {
WorkflowData,
createWorkflow,
parallelize,
} from "@medusajs/workflows-sdk"
import {
createPriceListPricesStep,
validatePriceListsStep,
validateVariantPriceLinksStep,
} from "../steps"
import { createPriceListPricesStep } from "../steps/create-price-list-prices"
import { validatePriceListsStep } from "../steps/validate-price-lists"
import { validateVariantPriceLinksStep } from "../steps/validate-variant-price-links"
export const createPriceListPricesWorkflowId = "create-price-list-prices"
export const createPriceListPricesWorkflow = createWorkflow(
@@ -17,13 +16,13 @@ export const createPriceListPricesWorkflow = createWorkflow(
input: WorkflowData<{
data: CreatePriceListPricesWorkflowDTO[]
}>
): WorkflowData<void> => {
): WorkflowData<PricingTypes.PriceDTO[]> => {
const [_, variantPriceMap] = parallelize(
validatePriceListsStep(input.data),
validateVariantPriceLinksStep(input.data)
)
createPriceListPricesStep({
return createPriceListPricesStep({
data: input.data,
variant_price_map: variantPriceMap,
})
@@ -1,3 +1,4 @@
export * from "./batch-price-list-prices"
export * from "./create-price-list-prices"
export * from "./create-price-lists"
export * from "./delete-price-lists"
@@ -1,10 +1,10 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { removePriceListPricesStep } from "../steps"
import { removePriceListPricesStep } from "../steps/remove-price-list-prices"
export const removePriceListPricesWorkflowId = "remove-price-list-prices"
export const removePriceListPricesWorkflow = createWorkflow(
removePriceListPricesWorkflowId,
(input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
removePriceListPricesStep(input.ids)
(input: WorkflowData<{ ids: string[] }>): WorkflowData<string[]> => {
return removePriceListPricesStep(input.ids)
}
)
@@ -1,14 +1,13 @@
import { UpdatePriceListPricesWorkflowDTO } from "@medusajs/types"
import { PricingTypes } from "@medusajs/types/src"
import {
WorkflowData,
createWorkflow,
parallelize,
} from "@medusajs/workflows-sdk"
import {
updatePriceListPricesStep,
validatePriceListsStep,
validateVariantPriceLinksStep,
} from "../steps"
import { updatePriceListPricesStep } from "../steps/update-price-list-prices"
import { validatePriceListsStep } from "../steps/validate-price-lists"
import { validateVariantPriceLinksStep } from "../steps/validate-variant-price-links"
export const updatePriceListPricesWorkflowId = "update-price-list-prices"
export const updatePriceListPricesWorkflow = createWorkflow(
@@ -17,13 +16,13 @@ export const updatePriceListPricesWorkflow = createWorkflow(
input: WorkflowData<{
data: UpdatePriceListPricesWorkflowDTO[]
}>
): WorkflowData<void> => {
): WorkflowData<PricingTypes.PriceDTO[]> => {
const [_, variantPriceMap] = parallelize(
validatePriceListsStep(input.data),
validateVariantPriceLinksStep(input.data)
)
updatePriceListPricesStep({
return updatePriceListPricesStep({
data: input.data,
variant_price_map: variantPriceMap,
})
@@ -1,38 +0,0 @@
import { createPriceListPricesWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { getPriceList } from "../../../../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "../../../../query-config"
import { AdminPostPriceListsPriceListPricesBatchAddReq } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListPricesBatchAddReq>,
res: MedusaResponse
) => {
const { prices } = req.validatedBody
const id = req.params.id
const workflow = createPriceListPricesWorkflow(req.scope)
const { errors } = await workflow.run({
input: { data: [{ id, prices }] },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
})
res.status(200).json({ price_list: priceList })
}
@@ -1,37 +0,0 @@
import { removePriceListPricesWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { getPriceList } from "../../../../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "../../../../query-config"
import { AdminPostPriceListsPriceListPricesBatchRemoveReq } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListPricesBatchRemoveReq>,
res: MedusaResponse
) => {
const { ids } = req.validatedBody
const id = req.params.id
const workflow = removePriceListPricesWorkflow(req.scope)
const { errors } = await workflow.run({
input: { ids },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
})
res.status(200).json({ price_list: priceList })
}
@@ -0,0 +1,70 @@
import { batchPriceListPricesWorkflow } from "@medusajs/core-flows"
import { promiseAll } from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../types/routing"
import { fetchPriceListPriceIdsForProduct } from "../../../helpers"
import { listPrices } from "../../../queries"
import { adminPriceListPriceRemoteQueryFields } from "../../../query-config"
import { AdminBatchPriceListPricesType } from "../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminBatchPriceListPricesType>,
res: MedusaResponse
) => {
const id = req.params.id
const {
create = [],
update = [],
delete: deletePriceIds = [],
product_id: productIds = [],
} = req.validatedBody
const productPriceIds = await fetchPriceListPriceIdsForProduct(
id,
productIds,
req.scope
)
const priceIdsToDelete = [...deletePriceIds, ...productPriceIds]
const workflow = batchPriceListPricesWorkflow(req.scope)
const { result, errors } = await workflow.run({
input: {
data: {
id,
create,
update,
delete: priceIdsToDelete,
},
},
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const [created, updated] = await promiseAll([
listPrices(
result.created.map((c) => c.id),
req.scope,
adminPriceListPriceRemoteQueryFields
),
listPrices(
result.updated.map((c) => c.id),
req.scope,
adminPriceListPriceRemoteQueryFields
),
])
res.status(200).json({
created,
updated,
deleted: {
ids: priceIdsToDelete,
object: "price",
deleted: true,
},
})
}
@@ -1,38 +0,0 @@
import { updatePriceListPricesWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { getPriceList } from "../../../../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "../../../../query-config"
import { AdminPostPriceListPriceBatchUpdate } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListPriceBatchUpdate>,
res: MedusaResponse
) => {
const { prices } = req.validatedBody
const id = req.params.id
const workflow = updatePriceListPricesWorkflow(req.scope)
const { errors } = await workflow.run({
input: { data: [{ id, prices }] },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
})
res.status(200).json({ price_list: priceList })
}
@@ -6,37 +6,31 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { getPriceList } from "../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "../query-config"
import { AdminPostPriceListsPriceListReq } from "../validators"
import { fetchPriceList } from "../helpers"
import { AdminUpdatePriceListType } from "../validators"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const id = req.params.id
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: req.retrieveConfig.select!,
})
const price_list = await fetchPriceList(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ price_list: priceList })
res.status(200).json({ price_list })
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListReq>,
req: AuthenticatedMedusaRequest<AdminUpdatePriceListType>,
res: MedusaResponse
) => {
const id = req.params.id
const workflow = updatePriceListsWorkflow(req.scope)
const { errors } = await workflow.run({
input: { price_lists_data: [{ id, ...req.validatedBody }] },
input: { price_lists_data: [{ ...req.validatedBody, id }] },
throwOnError: false,
})
@@ -44,14 +38,13 @@ export const POST = async (
throw errors[0].error
}
const priceList = await getPriceList({
const price_list = await fetchPriceList(
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
})
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ price_list: priceList })
res.status(200).json({ price_list })
}
export const DELETE = async (
@@ -0,0 +1,79 @@
import { MedusaContainer } from "@medusajs/types"
import {
buildPriceListRules,
buildPriceSetPricesForCore,
ContainerRegistrationKeys,
isPresent,
MedusaError,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const fetchPriceList = async (
id: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "price_lists",
variables: {
filters: { id },
},
fields,
})
const [priceList] = await remoteQuery(queryObject)
if (!isPresent(priceList)) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Price list with id: ${id} was not found`
)
}
return transformPriceList(priceList)
}
export const transformPriceList = (priceList) => {
priceList.rules = buildPriceListRules(priceList.price_list_rules)
priceList.prices = buildPriceSetPricesForCore(priceList.prices)
delete priceList.price_list_rules
return priceList
}
export const fetchPriceListPriceIdsForProduct = async (
priceListId: string,
productIds: string[],
scope: MedusaContainer
): Promise<string[]> => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const priceSetIds: string[] = []
const variants = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "variants",
variables: { filters: { product_id: productIds } },
fields: ["price_set.id"],
})
)
for (const variant of variants) {
if (variant.price_set?.id) {
priceSetIds.push(variant.price_set.id)
}
}
const productPrices = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "prices",
variables: {
filters: { price_set_id: priceSetIds, price_list_id: priceListId },
},
fields: ["id"],
})
)
return productPrices.map((price) => price.id)
}
@@ -1,15 +1,15 @@
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformBody } from "../../utils/validate-body"
import { validateAndTransformQuery } from "../../utils/validate-query"
import * as QueryConfig from "./query-config"
import {
AdminBatchPriceListPrices,
AdminCreatePriceList,
AdminGetPriceListParams,
AdminGetPriceListPricesParams,
AdminGetPriceListsParams,
AdminGetPriceListsPriceListParams,
AdminPostPriceListPriceBatchUpdate,
AdminPostPriceListsPriceListPricesBatchAddReq,
AdminPostPriceListsPriceListPricesBatchRemoveReq,
AdminPostPriceListsPriceListReq,
AdminPostPriceListsReq,
AdminUpdatePriceList,
} from "./validators"
export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
@@ -22,9 +22,9 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/price-lists",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetPriceListsParams,
QueryConfig.adminListTransformQueryConfig
QueryConfig.listPriceListQueryConfig
),
],
},
@@ -32,37 +32,43 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/price-lists/:id",
middlewares: [
transformQuery(
AdminGetPriceListsPriceListParams,
QueryConfig.adminRetrieveTransformQueryConfig
validateAndTransformQuery(
AdminGetPriceListParams,
QueryConfig.retrivePriceListQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/price-lists",
middlewares: [transformBody(AdminPostPriceListsReq)],
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id",
middlewares: [transformBody(AdminPostPriceListsPriceListReq)],
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id/prices/batch/add",
middlewares: [transformBody(AdminPostPriceListsPriceListPricesBatchAddReq)],
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id/prices/batch/remove",
middlewares: [
transformBody(AdminPostPriceListsPriceListPricesBatchRemoveReq),
validateAndTransformBody(AdminCreatePriceList),
validateAndTransformQuery(
AdminGetPriceListPricesParams,
QueryConfig.retrivePriceListQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id/prices/batch/update",
middlewares: [transformBody(AdminPostPriceListPriceBatchUpdate)],
matcher: "/admin/price-lists/:id",
middlewares: [
validateAndTransformBody(AdminUpdatePriceList),
validateAndTransformQuery(
AdminGetPriceListPricesParams,
QueryConfig.retrivePriceListQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id/prices/batch",
middlewares: [
validateAndTransformBody(AdminBatchPriceListPrices),
validateAndTransformQuery(
AdminGetPriceListPricesParams,
QueryConfig.listPriceListPriceQueryConfig
),
],
},
]
@@ -7,14 +7,15 @@ import { AdminPriceListRemoteQueryDTO } from "../types"
export * from "./get-price-list"
export * from "./list-price-lists"
export * from "./list-prices"
export function buildPriceListResponse(
priceLists,
apiFields
): AdminPriceListRemoteQueryDTO[] {
for (const priceList of priceLists) {
priceList.rules = buildPriceListRules(priceList.price_list_rules || [])
priceList.prices = buildPriceSetPricesForCore(priceList.prices || [])
priceList.rules = buildPriceListRules(priceList.price_list_rules)
priceList.prices = buildPriceSetPricesForCore(priceList.prices)
}
return priceLists.map((priceList) => cleanResponseData(priceList, apiFields))
@@ -4,17 +4,14 @@ import {
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminPriceListRemoteQueryDTO } from "../types"
import { buildPriceListResponse } from "./"
export async function listPriceLists({
container,
remoteQueryFields,
apiFields,
variables,
}: {
container: MedusaContainer
remoteQueryFields: string[]
apiFields: string[]
variables: Record<string, any>
}): Promise<[AdminPriceListRemoteQueryDTO[], number]> {
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -26,11 +23,5 @@ export async function listPriceLists({
const { rows: priceLists, metadata } = await remoteQuery(queryObject)
if (!metadata.count) {
return [[], 0]
}
const sanitizedPriceLists = buildPriceListResponse(priceLists, apiFields)
return [sanitizedPriceLists, metadata.count]
return [priceLists, metadata.count]
}
@@ -0,0 +1,22 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const listPrices = (
ids: string[],
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "price",
variables: {
filters: { id: ids },
},
fields,
})
return remoteQuery(queryObject)
}
@@ -2,6 +2,20 @@ export enum PriceListRelations {
PRICES = "prices",
}
export const adminPriceListPriceRemoteQueryFields = [
"id",
"currency_code",
"amount",
"min_quantity",
"max_quantity",
"created_at",
"deleted_at",
"updated_at",
"price_set.variant.id",
"price_rules.value",
"price_rules.rule_type.rule_attribute",
]
export const adminPriceListRemoteQueryFields = [
"id",
"type",
@@ -13,56 +27,27 @@ export const adminPriceListRemoteQueryFields = [
"created_at",
"updated_at",
"deleted_at",
"prices.id",
"prices.currency_code",
"prices.amount",
"prices.min_quantity",
"prices.max_quantity",
"prices.created_at",
"prices.deleted_at",
"prices.updated_at",
"prices.price_set.variant.id",
"prices.price_rules.value",
"prices.price_rules.rule_type.rule_attribute",
"price_list_rules.price_list_rule_values.value",
"price_list_rules.rule_type.rule_attribute",
...adminPriceListPriceRemoteQueryFields.map((field) => `prices.${field}`),
]
export const defaultAdminPriceListFields = [
"id",
"type",
"description",
"title",
"status",
"starts_at",
"ends_at",
"rules",
"created_at",
"updated_at",
"prices.amount",
"prices.id",
"prices.currency_code",
"prices.amount",
"prices.min_quantity",
"prices.max_quantity",
"prices.variant_id",
"prices.rules",
]
export const retrivePriceListPriceQueryConfig = {
defaults: adminPriceListPriceRemoteQueryFields,
isList: false,
}
export const defaultAdminPriceListRelations = []
export const allowedAdminPriceListRelations = [PriceListRelations.PRICES]
export const adminListTransformQueryConfig = {
defaultLimit: 50,
defaultFields: defaultAdminPriceListFields,
defaultRelations: defaultAdminPriceListRelations,
allowedRelations: allowedAdminPriceListRelations,
export const listPriceListPriceQueryConfig = {
...retrivePriceListPriceQueryConfig,
isList: true,
}
export const adminRetrieveTransformQueryConfig = {
defaultFields: defaultAdminPriceListFields,
defaultRelations: defaultAdminPriceListRelations,
allowedRelations: allowedAdminPriceListRelations,
export const retrivePriceListQueryConfig = {
defaults: adminPriceListRemoteQueryFields,
isList: false,
}
export const listPriceListQueryConfig = {
...retrivePriceListQueryConfig,
isList: true,
}
@@ -1,42 +1,43 @@
import { createPriceListsWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { listPriceLists } from "./queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "./query-config"
import { AdminPostPriceListsReq } from "./validators"
import { fetchPriceList, transformPriceList } from "./helpers"
import { AdminCreatePriceListType } from "./validators"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { limit, offset } = req.validatedQuery
const [priceLists, count] = await listPriceLists({
container: req.scope,
apiFields: req.listConfig.select!,
remoteQueryFields: adminPriceListRemoteQueryFields,
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "price_list",
variables: {
filters: req.filterableFields,
order: req.listConfig.order,
skip: req.listConfig.skip,
take: req.listConfig.take,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: priceLists, metadata } = await remoteQuery(queryObject)
res.json({
count,
price_lists: priceLists,
count: metadata.count,
price_lists: priceLists.map((priceList) => transformPriceList(priceList)),
offset,
limit,
})
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListsReq>,
req: AuthenticatedMedusaRequest<AdminCreatePriceListType>,
res: MedusaResponse
) => {
const workflow = createPriceListsWorkflow(req.scope)
@@ -49,16 +50,11 @@ export const POST = async (
throw errors[0].error
}
const [[priceList]] = await listPriceLists({
container: req.scope,
apiFields: defaultAdminPriceListFields,
remoteQueryFields: adminPriceListRemoteQueryFields,
variables: {
filters: { id: result[0].id },
skip: 0,
take: 1,
},
})
const price_list = await fetchPriceList(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ price_list: priceList })
res.status(200).json({ price_list })
}
@@ -1,151 +1,77 @@
import { PriceListStatus, PriceListType } from "@medusajs/types"
import { Transform, Type } from "class-transformer"
import {
IsArray,
IsEnum,
IsInt,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { FindParams } from "../../../types/common"
import { transformOptionalDate } from "../../../utils/validators/date-transform"
import { z } from "zod"
import { createFindParams, createSelectParams } from "../../utils/validators"
export class AdminGetPriceListsParams extends FindParams {}
export class AdminGetPriceListsPriceListParams extends FindParams {}
export const AdminGetPriceListPricesParams = createSelectParams()
export const AdminGetPriceListsParams = createFindParams({
offset: 0,
limit: 50,
})
export class AdminPostPriceListsReq {
@IsString()
title: string
export const AdminGetPriceListParams = createFindParams({
offset: 0,
limit: 50,
})
@IsString()
description: string
export const AdminCreatePriceListPrice = z.object({
currency_code: z.string(),
amount: z.number(),
variant_id: z.string(),
min_quantity: z.number().optional(),
max_quantity: z.number().optional(),
rules: z.record(z.string(), z.string()).optional(),
})
@IsOptional()
@Transform(transformOptionalDate)
starts_at?: string
export type AdminCreatePriceListPriceType = z.infer<
typeof AdminCreatePriceListPrice
>
@IsOptional()
@Transform(transformOptionalDate)
ends_at?: string
export const AdminUpdatePriceListPrice = z.object({
id: z.string(),
currency_code: z.string().optional(),
amount: z.number().optional(),
variant_id: z.string(),
min_quantity: z.number().optional(),
max_quantity: z.number().optional(),
rules: z.record(z.string(), z.string()).optional(),
})
@IsOptional()
@IsEnum(PriceListStatus)
status?: PriceListStatus
export type AdminUpdatePriceListPriceType = z.infer<
typeof AdminUpdatePriceListPrice
>
@IsEnum(PriceListType)
type: PriceListType
export const AdminBatchPriceListPrices = z.object({
create: z.array(AdminCreatePriceListPrice).optional(),
update: z.array(AdminUpdatePriceListPrice).optional(),
delete: z.array(z.string()).optional(),
product_id: z.array(z.string()).optional(),
})
@IsArray()
@Type(() => AdminPriceListPricesCreateReq)
@ValidateNested({ each: true })
prices: AdminPriceListPricesCreateReq[]
export type AdminBatchPriceListPricesType = z.infer<
typeof AdminBatchPriceListPrices
>
@IsOptional()
@IsObject()
rules?: Record<string, string[]>
}
export const AdminCreatePriceList = z.object({
title: z.string(),
description: z.string(),
starts_at: z.string().optional(),
ends_at: z.string().optional(),
status: z.nativeEnum(PriceListStatus).optional(),
type: z.nativeEnum(PriceListType).optional(),
rules: z.record(z.string(), z.array(z.string())).optional(),
prices: z.array(AdminCreatePriceListPrice).optional(),
})
export class AdminPriceListPricesCreateReq {
@IsString()
currency_code: string
export type AdminCreatePriceListType = z.infer<typeof AdminCreatePriceList>
@IsInt()
amount: number
export const AdminUpdatePriceList = z.object({
title: z.string().optional(),
description: z.string().optional(),
starts_at: z.string().optional(),
ends_at: z.string().optional(),
status: z.nativeEnum(PriceListStatus).optional(),
type: z.nativeEnum(PriceListType).optional(),
rules: z.record(z.string(), z.array(z.string())).optional(),
})
@IsString()
variant_id: string
@IsOptional()
@IsInt()
min_quantity?: number
@IsOptional()
@IsInt()
max_quantity?: number
@IsOptional()
@IsObject()
rules?: Record<string, string>
}
export class AdminPostPriceListsPriceListReq {
@IsString()
@IsOptional()
title?: string
@IsString()
@IsOptional()
description?: string
@IsOptional()
@Transform(transformOptionalDate)
starts_at?: string
@IsOptional()
@Transform(transformOptionalDate)
ends_at?: string
@IsOptional()
@IsEnum(PriceListStatus)
status?: PriceListStatus
@IsOptional()
@IsEnum(PriceListType)
type?: PriceListType
@IsOptional()
@IsArray()
prices: AdminPriceListPricesCreateReq[]
@IsOptional()
@IsObject()
rules?: Record<string, string[]>
}
export class AdminPostPriceListsPriceListPricesBatchAddReq {
@IsOptional()
@IsArray()
prices: AdminPriceListPricesCreateReq[]
}
export class AdminPostPriceListPriceBatchUpdate {
@IsOptional()
@IsArray()
prices: AdminPostPriceListPriceUpdate[]
}
export class AdminPostPriceListsPriceListPricesBatchRemoveReq {
@IsArray()
@IsString({ each: true })
ids: string[]
}
export class AdminPostPriceListPriceUpdate {
@IsString()
id: string
@IsString()
variant_id: string
@IsOptional()
@IsString()
currency_code?: string
@IsOptional()
@IsInt()
amount?: number
@IsOptional()
@IsInt()
min_quantity?: number
@IsOptional()
@IsInt()
max_quantity?: number
@IsOptional()
@IsObject()
rules?: Record<string, string>
}
export type AdminUpdatePriceListType = z.infer<typeof AdminUpdatePriceList>
@@ -715,14 +715,13 @@ moduleIntegrationTestRunner({
describe("addRules", () => {
it("should add rules to existing price set", async () => {
console.log("1")
await service.addRules([
{
priceSetId: "price-set-1",
rules: [{ attribute: "region_id" }],
},
])
console.log("2")
const [priceSet] = await service.list(
{ id: ["price-set-1"] },
{ relations: ["rule_types"] }
+6
View File
@@ -39,5 +39,11 @@ export const joinerConfig: ModuleJoinerConfig = {
methodSuffix: "PriceLists",
},
},
{
name: ["price", "prices"],
args: {
methodSuffix: "Prices",
},
},
],
}
+36 -35
View File
@@ -2,6 +2,7 @@ import {
AddPricesDTO,
Context,
CreatePriceListRuleDTO,
CreatePriceRuleDTO,
CreatePricesDTO,
CreatePriceSetDTO,
DAL,
@@ -23,7 +24,7 @@ import {
groupBy,
InjectManager,
InjectTransactionManager,
isDefined, isPresent,
isPresent,
isString,
MedusaContext,
MedusaError,
@@ -44,12 +45,12 @@ import {
RuleType,
} from "@models"
import {PriceListService, RuleTypeService} from "@services"
import {validatePriceListDates} from "@utils"
import {entityNameToLinkableKeysMap, joinerConfig} from "../joiner-config"
import {PriceSetIdPrefix} from "../models/price-set"
import {PriceListIdPrefix} from "../models/price-list"
import {UpdatePriceSetInput} from "src/types/services"
import { PriceListService, RuleTypeService } from "@services"
import { validatePriceListDates } from "@utils"
import { UpdatePriceSetInput } from "src/types/services"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
import { PriceListIdPrefix } from "../models/price-list"
import { PriceSetIdPrefix } from "../models/price-set"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
@@ -373,7 +374,7 @@ export default class PricingModuleService<
...price,
price_set_id: priceSet.id,
price_rules: hasRulesInput ? rules : undefined,
rules_count: hasRulesInput ? rules.length : undefined
rules_count: hasRulesInput ? rules.length : undefined,
}
})
@@ -598,8 +599,10 @@ export default class PricingModuleService<
async updatePriceListPrices(
data: PricingTypes.UpdatePriceListPricesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
return await this.updatePriceListPrices_(data, sharedContext)
): Promise<PricingTypes.PriceDTO[]> {
const prices = await this.updatePriceListPrices_(data, sharedContext)
return await this.baseRepository_.serialize<PricingTypes.PriceDTO[]>(prices)
}
@InjectManager("baseRepository_")
@@ -614,8 +617,10 @@ export default class PricingModuleService<
async addPriceListPrices(
data: PricingTypes.AddPriceListPricesDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
return await this.addPriceListPrices_(data, sharedContext)
): Promise<PricingTypes.PriceDTO[]> {
const prices = await this.addPriceListPrices_(data, sharedContext)
return await this.baseRepository_.serialize<PricingTypes.PriceDTO[]>(prices)
}
@InjectManager("baseRepository_")
@@ -1141,7 +1146,7 @@ export default class PricingModuleService<
protected async updatePriceListPrices_(
data: PricingTypes.UpdatePriceListPricesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
): Promise<TPrice[]> {
const ruleTypeAttributes: string[] = []
const priceListIds: string[] = []
const priceIds: string[] = []
@@ -1233,6 +1238,10 @@ export default class PricingModuleService<
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
const pricesToUpdate: Partial<TPrice>[] = []
const priceRuleIdsToDelete: string[] = []
const priceRulesToCreate: CreatePriceRuleDTO[] = []
for (const { price_list_id: priceListId, prices } of data) {
const priceList = priceListMap.get(priceListId)
@@ -1243,43 +1252,37 @@ export default class PricingModuleService<
)
}
const priceRuleIdsToDelete: string[] = []
const priceRulesToCreate: PricingTypes.CreatePriceRuleDTO[] = []
const pricesToUpdate: Partial<TPrice>[] = []
for (const priceData of prices) {
const { rules, price_set_id, ...rest } = priceData
const { rules = {}, price_set_id, ...rest } = priceData
const price = priceMap.get(rest.id)!
const priceRules = price.price_rules!
if (!isDefined(rules)) {
continue
}
priceRulesToCreate.push(
...Object.entries(rules).map(([ruleAttribute, ruleValue]) => ({
price_set_id,
rule_type_id: ruleTypeMap.get(ruleAttribute)!.id,
value: ruleValue,
price_id: price.id,
}))
)
pricesToUpdate.push({
...rest,
rules_count: Object.keys(rules).length,
price_rules: Object.entries(rules).map(
([ruleAttribute, ruleValue]) => ({
price_set_id,
rule_type_id: ruleTypeMap.get(ruleAttribute)!.id,
value: ruleValue,
price_id: price.id,
})
),
} as unknown as TPrice)
priceRuleIdsToDelete.push(...priceRules.map((pr) => pr.id))
}
}
const [_deletedPriceRule, _createdPriceRule, updatedPrices] =
await promiseAll([
this.priceRuleService_.delete(priceRuleIdsToDelete),
this.priceRuleService_.create(priceRulesToCreate),
this.priceService_.update(pricesToUpdate),
])
}
return priceLists
return updatedPrices
}
@InjectTransactionManager("baseRepository_")
@@ -1294,7 +1297,7 @@ export default class PricingModuleService<
protected async addPriceListPrices_(
data: PricingTypes.AddPriceListPricesDTO[],
sharedContext: Context = {}
): Promise<PricingTypes.PriceListDTO[]> {
): Promise<TPrice[]> {
const ruleTypeAttributes: string[] = []
const priceListIds: string[] = []
const priceSetIds: string[] = []
@@ -1411,9 +1414,7 @@ export default class PricingModuleService<
pricesToCreate.push(...priceListPricesToCreate)
}
await this.priceService_.create(pricesToCreate, sharedContext)
return priceLists
return await this.priceService_.create(pricesToCreate, sharedContext)
}
@InjectTransactionManager("baseRepository_")
+8 -8
View File
@@ -1,3 +1,7 @@
import { FindConfig } from "../common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
import {
AddPriceListPricesDTO,
AddPricesDTO,
@@ -33,10 +37,6 @@ import {
UpdateRuleTypeDTO,
UpsertPriceSetDTO,
} from "./common"
import { FindConfig } from "../common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
/**
* The main service interface for the Pricing Module.
@@ -1734,7 +1734,7 @@ export interface IPricingModuleService extends IModuleService {
addPriceListPrices(
data: AddPriceListPricesDTO[],
sharedContext?: Context
): Promise<PriceListDTO[]>
): Promise<PriceDTO[]>
/**
* This method updates existing price list's prices.
@@ -1742,7 +1742,7 @@ export interface IPricingModuleService extends IModuleService {
* @param {UpdatePriceListPricesDTO[]} data - The attributes to update in a price list's prices. The price list's ID is specified
* in the `price_list_id` field.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<PriceListDTO[]>} The updated price list's prices.
* @returns {Promise<PriceDTO[]>} The updated price list's prices.
*
* @example
* const priceLists =
@@ -1763,7 +1763,7 @@ export interface IPricingModuleService extends IModuleService {
updatePriceListPrices(
data: UpdatePriceListPricesDTO[],
sharedContext?: Context
): Promise<PriceListDTO[]>
): Promise<PriceDTO[]>
/**
* This method is used to set the rules of a price list. Previous rules are removed.
@@ -1771,7 +1771,7 @@ export interface IPricingModuleService extends IModuleService {
* @param {SetPriceListRulesDTO} data - The rules to set for a price list. The price list is identified by the
* `price_list_id` property.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<PriceListDTO>} The updated price list.
* @returns {Promise<PriceDTO>} The updated price list's prices.
*
* @example
* const priceList =
+26 -10
View File
@@ -1,3 +1,4 @@
import { PricingTypes } from "../bundles"
import { PriceListStatus } from "./common"
export interface CreatePriceListPriceWorkflowDTO {
@@ -9,6 +10,16 @@ export interface CreatePriceListPriceWorkflowDTO {
rules?: Record<string, string>
}
export interface UpdatePriceListPriceWorkflowDTO {
id: string
variant_id: string
amount?: number
currency_code?: string
max_quantity?: number
min_quantity?: number
rules?: Record<string, string>
}
export interface CreatePriceListWorkflowInputDTO {
title: string
description: string
@@ -16,7 +27,7 @@ export interface CreatePriceListWorkflowInputDTO {
ends_at?: string | null
status?: PriceListStatus
rules?: Record<string, string[]>
prices: CreatePriceListPriceWorkflowDTO[]
prices?: CreatePriceListPriceWorkflowDTO[]
}
export interface UpdatePriceListWorkflowInputDTO {
@@ -31,15 +42,20 @@ export interface UpdatePriceListWorkflowInputDTO {
export interface UpdatePriceListPricesWorkflowDTO {
id: string
prices: {
id: string
variant_id: string
amount?: number
currency_code?: string
max_quantity?: number
min_quantity?: number
rules?: Record<string, string>
}[]
prices: UpdatePriceListPriceWorkflowDTO[]
}
export interface BatchPriceListPricesWorkflowDTO {
id: string
create: CreatePriceListPriceWorkflowDTO[]
update: UpdatePriceListPriceWorkflowDTO[]
delete: string[]
}
export interface BatchPriceListPricesWorkflowResult {
created: PricingTypes.PriceDTO[]
updated: PricingTypes.PriceDTO[]
deleted: string[]
}
export interface CreatePriceListPricesWorkflowDTO {
+26 -17
View File
@@ -7,9 +7,9 @@ import {
} from "@medusajs/types"
export function buildPriceListRules(
priceListRules: PriceListRuleDTO[]
): Record<string, string[]> {
return priceListRules.reduce((acc, curr) => {
priceListRules?: PriceListRuleDTO[]
): Record<string, string[]> | undefined {
return priceListRules?.reduce((acc, curr) => {
const ruleAttribute = curr.rule_type.rule_attribute
const ruleValues = curr.price_list_rule_values || []
@@ -20,9 +20,13 @@ export function buildPriceListRules(
}
export function buildPriceSetRules(
priceRules: PriceRuleDTO[]
): Record<string, string> {
return priceRules.reduce((acc, curr) => {
priceRules?: PriceRuleDTO[]
): Record<string, string> | undefined {
if (typeof priceRules === "undefined") {
return undefined
}
return priceRules?.reduce((acc, curr) => {
const ruleAttribute = curr.rule_type.rule_attribute
const ruleValue = curr.value
@@ -34,20 +38,24 @@ export function buildPriceSetRules(
export function buildPriceSetPricesForCore(
prices: (PriceDTO & {
price_set: PriceDTO["price_set"] & {
price_set?: PriceDTO["price_set"] & {
variant?: ProductVariantDTO
}
})[]
): Record<string, any>[] {
return prices.map((price) => {
const productVariant = (price.price_set as any).variant
const rules: Record<string, string> = price.price_rules
? buildPriceSetRules(price.price_rules)
: {}
return prices?.map((price) => {
const productVariant = (price.price_set as any)?.variant
const rules: Record<string, string> | undefined =
typeof price.price_rules === "undefined"
? undefined
: buildPriceSetRules(price.price_rules || [])
delete price.price_rules
delete price.price_set
return {
...price,
variant_id: productVariant?.id ?? null,
variant_id: productVariant?.id ?? undefined,
rules,
}
})
@@ -56,10 +64,11 @@ export function buildPriceSetPricesForCore(
export function buildPriceSetPricesForModule(
prices: PriceDTO[]
): UpdatePriceListPriceDTO[] {
return prices.map((price) => {
const rules: Record<string, string> = price.price_rules
? buildPriceSetRules(price.price_rules)
: {}
return prices?.map((price) => {
const rules: Record<string, string> | undefined =
typeof price.price_rules === "undefined"
? undefined
: buildPriceSetRules(price.price_rules || [])
return {
...price,