feat(core-flows,medusa,types): create/update workflows to create and update PriceList prices (#6711)

what:

- create and update workflows for price list prices
- update price list endpoint does a "set"
This commit is contained in:
Riqwan Thamir
2024-03-18 15:14:28 +01:00
committed by GitHub
parent 84b8836cbf
commit 0c705d7bd4
28 changed files with 509 additions and 206 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
feat(medusa): rework prices add/remove to batch spec

View File

@@ -0,0 +1,7 @@
---
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
"@medusajs/types": patch
---
feat(core-flows,medusa,types): split upsert workflow to create and update

View File

@@ -259,7 +259,7 @@ medusaIntegrationTestRunner({
})
describe("POST /admin/price-lists", () => {
it("should create price list and money amounts", async () => {
it("should create price list and prices successfully", async () => {
await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
@@ -422,7 +422,7 @@ medusaIntegrationTestRunner({
)
})
it("should update price lists successfully", async () => {
it("should update price lists and set prices successfully", async () => {
await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
@@ -456,7 +456,7 @@ medusaIntegrationTestRunner({
],
}
const response = await api.post(
let response = await api.post(
`admin/price-lists/${priceList.id}`,
data,
adminHeaders
@@ -466,14 +466,8 @@ medusaIntegrationTestRunner({
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
title: "new price list name",
description: "new price list description",
type: "override",
status: "active",
starts_at: expect.any(String),
ends_at: expect.any(String),
rules: {
customer_group_id: [customerGroup.id],
},
@@ -492,11 +486,41 @@ medusaIntegrationTestRunner({
],
})
)
// Updating prices should remove existing prices and create new ones
response = await api.post(
`admin/price-lists/${priceList.id}`,
{
prices: [
{
amount: 600,
variant_id: variant.id,
currency_code: "usd",
rules: { region_id: region.id },
},
],
},
adminHeaders
)
expect(response.data.price_list).toEqual(
expect.objectContaining({
prices: [
expect.objectContaining({
id: expect.any(String),
currency_code: "usd",
amount: 600,
variant_id: variant.id,
rules: { region_id: region.id },
}),
],
})
)
})
})
describe("POST /admin/price-lists/:id/prices", () => {
it("should upsert price list prices successfully", async () => {
describe("POST /admin/price-lists/:id/prices/batch/add", () => {
it("should add price list prices successfully", async () => {
const priceSet = await createVariantPriceSet({
container: appContainer,
variantId: variant.id,
@@ -527,16 +551,11 @@ medusaIntegrationTestRunner({
currency_code: "usd",
rules: { region_id: region.id },
},
{
id: "test-price-id",
variant_id: variant.id,
amount: 200,
},
],
}
const response = await api.post(
`admin/price-lists/${priceList.id}/prices`,
`admin/price-lists/${priceList.id}/prices/batch/add`,
data,
adminHeaders
)
@@ -555,7 +574,7 @@ medusaIntegrationTestRunner({
expect.objectContaining({
id: "test-price-id",
currency_code: "usd",
amount: 200,
amount: 5000,
}),
]),
})
@@ -563,8 +582,8 @@ medusaIntegrationTestRunner({
})
})
describe("DELETE /admin/price-lists/:id/prices", () => {
it("should delete price list prices", async () => {
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,
@@ -577,7 +596,6 @@ medusaIntegrationTestRunner({
description: "test",
prices: [
{
id: "test-price-id",
amount: 5000,
currency_code: "usd",
price_set_id: priceSet.id,
@@ -589,17 +607,21 @@ medusaIntegrationTestRunner({
},
])
let response = await api.delete(
`/admin/price-lists/${priceList.id}/prices`,
{ ...adminHeaders, data: { ids: ["test-price-id"] } }
const psmaIdToDelete = priceList.price_set_money_amounts![0].id
const response = await api.post(
`/admin/price-lists/${priceList.id}/prices/batch/remove`,
{ ids: [psmaIdToDelete] },
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
ids: ["test-price-id"],
object: "price_list_prices",
deleted: true,
})
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
prices: [],
})
)
})
})
})

View File

@@ -0,0 +1,61 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
AddPriceListPricesDTO,
CreatePriceListPriceDTO,
CreatePriceListPricesWorkflowStepDTO,
IPricingModuleService,
} from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const createPriceListPricesStepId = "create-price-list-prices"
export const createPriceListPricesStep = createStep(
createPriceListPricesStepId,
async (stepInput: CreatePriceListPricesWorkflowStepDTO, { container }) => {
const { data, variant_price_map: variantPriceSetMap } = stepInput
const priceListPricesToCreate: AddPriceListPricesDTO[] = []
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
for (const createPriceListPricesData of data) {
const { prices = [], id } = createPriceListPricesData
const pricesToAdd: CreatePriceListPriceDTO[] = []
for (const price of prices) {
pricesToAdd.push({
...price,
price_set_id: variantPriceSetMap[price.variant_id!],
})
}
if (pricesToAdd.length) {
priceListPricesToCreate.push({
price_list_id: id,
prices: pricesToAdd,
})
}
}
const createdPrices = await pricingModule.addPriceListPrices(
priceListPricesToCreate
)
return new StepResponse(
null,
createdPrices.map((p) => p.id)
)
},
async (createdIds, { container }) => {
if (!createdIds?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
if (createdIds.length) {
await pricingModule.removePrices(createdIds)
}
}
)

View File

@@ -1,20 +1,15 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
CreatePriceListDTO,
CreatePriceListWorkflowInputDTO,
CreatePriceListsWorkflowStepDTO,
IPricingModuleService,
} from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
type WorkflowStepInput = {
data: CreatePriceListWorkflowInputDTO[]
variant_price_map: Record<string, string>
}
export const createPriceListsStepId = "create-price-lists"
export const createPriceListsStep = createStep(
createPriceListsStepId,
async (stepInput: WorkflowStepInput, { container }) => {
async (stepInput: CreatePriceListsWorkflowStepDTO, { container }) => {
const { data, variant_price_map: variantPriceMap } = stepInput
const pricingModule = container.resolve<IPricingModuleService>(

View File

@@ -0,0 +1,32 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const getExistingPriceListsPriceIdsStepId =
"get-existing-price-lists-prices"
export const getExistingPriceListsPriceIdsStep = createStep(
getExistingPriceListsPriceIdsStepId,
async (data: { price_list_ids: string[] }, { container }) => {
const { price_list_ids: priceListIds = [] } = data
const priceListPriceIdsMap: Record<string, string[]> = {}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
const existingPrices = priceListIds.length
? await pricingModule.listPriceSetMoneyAmounts(
{ price_list_id: priceListIds },
{ relations: ["price_list"] }
)
: []
for (const price of existingPrices) {
const priceListId = price.price_list!.id
const prices = priceListPriceIdsMap[priceListId] || []
priceListPriceIdsMap[priceListId] = prices.concat(price.id)
}
return new StepResponse(priceListPriceIdsMap)
}
)

View File

@@ -1,6 +1,9 @@
export * from "./create-price-list-prices"
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 "./update-price-list-prices"
export * from "./update-price-lists"
export * from "./upsert-price-list-prices"
export * from "./validate-price-lists"

View File

@@ -0,0 +1,85 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
IPricingModuleService,
PriceSetMoneyAmountDTO,
UpdatePriceListPriceDTO,
UpdatePriceListPricesDTO,
UpdatePriceListPriceWorkflowStepDTO,
} from "@medusajs/types"
import { buildPriceSetPricesForModule } from "@medusajs/utils"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
export const updatePriceListPricesStepId = "update-price-list-prices"
export const updatePriceListPricesStep = createStep(
updatePriceListPricesStepId,
async (stepInput: UpdatePriceListPriceWorkflowStepDTO, { container }) => {
const { data = [], variant_price_map: variantPriceSetMap } = stepInput
const priceListPricesToUpdate: UpdatePriceListPricesDTO[] = []
const priceIds: string[] = []
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
for (const priceListData of data) {
const pricesToUpdate: UpdatePriceListPriceDTO[] = []
const { prices = [], id } = priceListData
for (const price of prices) {
pricesToUpdate.push({
...price,
price_set_id: variantPriceSetMap[price.variant_id!],
})
if (price.id) {
priceIds.push(price.id)
}
}
priceListPricesToUpdate.push({
price_list_id: id,
prices: pricesToUpdate,
})
}
const existingPrices = priceIds.length
? await pricingModule.listPriceSetMoneyAmounts(
{ id: priceIds },
{ relations: ["price_list"] }
)
: []
const priceListPsmaMap = new Map<string, PriceSetMoneyAmountDTO[]>()
const dataBeforePriceUpdate: UpdatePriceListPricesDTO[] = []
for (const price of existingPrices) {
const priceListId = price.price_list!.id
const psmas = priceListPsmaMap.get(priceListId) || []
priceListPsmaMap.set(priceListId, psmas)
}
for (const [priceListId, psmas] of Object.entries(priceListPsmaMap)) {
dataBeforePriceUpdate.push({
price_list_id: priceListId,
prices: buildPriceSetPricesForModule(psmas),
})
}
await pricingModule.updatePriceListPrices(priceListPricesToUpdate)
return new StepResponse(null, dataBeforePriceUpdate)
},
async (dataBeforePriceUpdate, { container }) => {
if (!dataBeforePriceUpdate?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
if (dataBeforePriceUpdate.length) {
await pricingModule.updatePriceListPrices(dataBeforePriceUpdate)
}
}
)

View File

@@ -8,20 +8,15 @@ import {
UpdatePriceListPriceDTO,
UpdatePriceListPriceWorkflowDTO,
UpdatePriceListPricesDTO,
UpdatePriceListWorkflowInputDTO,
UpsertPriceListPricesWorkflowStepDTO,
} from "@medusajs/types"
import { buildPriceSetPricesForModule, promiseAll } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
type WorkflowStepInput = {
data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
variant_price_map: Record<string, string>
}
export const upsertPriceListPricesStepId = "upsert-price-list-prices"
export const upsertPriceListPricesStep = createStep(
upsertPriceListPricesStepId,
async (stepInput: WorkflowStepInput, { container }) => {
async (stepInput: UpsertPriceListPricesWorkflowStepDTO, { container }) => {
const { data, variant_price_map: variantPriceSetMap } = stepInput
const priceListPricesToUpdate: UpdatePriceListPricesDTO[] = []

View File

@@ -6,12 +6,13 @@ import {
} from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
type WorkflowStepInput = Pick<UpdatePriceListWorkflowInputDTO, "prices">[]
export const validateVariantPriceLinksStepId = "validate-variant-price-links"
export const validateVariantPriceLinksStep = createStep(
validateVariantPriceLinksStepId,
async (data: WorkflowStepInput, { container }) => {
async (
data: Pick<UpdatePriceListWorkflowInputDTO, "prices">[],
{ container }
) => {
const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY
)

View File

@@ -0,0 +1,31 @@
import { CreatePriceListPricesWorkflowDTO } from "@medusajs/types"
import {
WorkflowData,
createWorkflow,
parallelize,
} from "@medusajs/workflows-sdk"
import {
createPriceListPricesStep,
validatePriceListsStep,
validateVariantPriceLinksStep,
} from "../steps"
export const createPriceListPricesWorkflowId = "create-price-list-prices"
export const createPriceListPricesWorkflow = createWorkflow(
createPriceListPricesWorkflowId,
(
input: WorkflowData<{
data: CreatePriceListPricesWorkflowDTO[]
}>
): WorkflowData<void> => {
const [_, variantPriceMap] = parallelize(
validatePriceListsStep(input.data),
validateVariantPriceLinksStep(input.data)
)
createPriceListPricesStep({
data: input.data,
variant_price_map: variantPriceMap,
})
}
)

View File

@@ -1,12 +1,10 @@
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
import { deletePriceListsStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const deletePriceListsWorkflowId = "delete-price-lists"
export const deletePriceListsWorkflow = createWorkflow(
deletePriceListsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
(input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
return deletePriceListsStep(input.ids)
}
)

View File

@@ -1,3 +1,4 @@
export * from "./create-price-list-prices"
export * from "./create-price-lists"
export * from "./delete-price-lists"
export * from "./remove-price-list-prices"

View File

@@ -1,12 +1,10 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { removePriceListPricesStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const removePriceListPricesWorkflowId = "remove-price-list-prices"
export const removePriceListPricesWorkflow = createWorkflow(
removePriceListPricesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
(input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
removePriceListPricesStep(input.ids)
}
)

View File

@@ -6,33 +6,46 @@ import {
transform,
} from "@medusajs/workflows-sdk"
import {
createPriceListPricesStep,
getExistingPriceListsPriceIdsStep,
removePriceListPricesStep,
updatePriceListsStep,
upsertPriceListPricesStep,
validatePriceListsStep,
validateVariantPriceLinksStep,
} from "../steps"
type WorkflowInput = { price_lists_data: UpdatePriceListWorkflowInputDTO[] }
export const updatePriceListsWorkflowId = "update-price-lists"
export const updatePriceListsWorkflow = createWorkflow(
updatePriceListsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
(
input: WorkflowData<{ price_lists_data: UpdatePriceListWorkflowInputDTO[] }>
): WorkflowData<void> => {
const [priceListsMap, variantPriceMap] = parallelize(
validatePriceListsStep(input.price_lists_data),
validateVariantPriceLinksStep(input.price_lists_data)
)
const updatePricesInput = transform(
{ priceListsMap, variantPriceMap, input },
(data) => ({
data: data.input.price_lists_data,
price_lists_map: data.priceListsMap,
variant_price_map: data.variantPriceMap,
})
const getPriceListPricesInput = transform({ priceListsMap }, (data) => ({
price_list_ids: Object.keys(data.priceListsMap),
}))
const priceListPriceIdMap = getExistingPriceListsPriceIdsStep(
getPriceListPricesInput
)
upsertPriceListPricesStep(updatePricesInput)
const removePriceListPricesInput = transform(
{ priceListPriceIdMap },
(data) => Object.values(data.priceListPriceIdMap).flat(1)
)
removePriceListPricesStep(removePriceListPricesInput)
const updatePricesInput = transform({ variantPriceMap, input }, (data) => ({
data: data.input.price_lists_data,
variant_price_map: data.variantPriceMap,
}))
createPriceListPricesStep(updatePricesInput)
const updatePriceListInput = transform({ input }, (data) => {
return data.input.price_lists_data.map((priceListData) => {

View File

@@ -10,14 +10,14 @@ import {
validateVariantPriceLinksStep,
} from "../steps"
type WorkflowInput = {
price_lists_data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
}
export const upsertPriceListPricesWorkflowId = "upsert-price-list-prices"
export const upsertPriceListPricesWorkflow = createWorkflow(
upsertPriceListPricesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
(
input: WorkflowData<{
price_lists_data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
}>
): WorkflowData<void> => {
const [_, variantPriceMap] = parallelize(
validatePriceListsStep(input.price_lists_data),
validateVariantPriceLinksStep(input.price_lists_data)

View File

@@ -0,0 +1,38 @@
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 })
}

View File

@@ -0,0 +1,37 @@
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 })
}

View File

@@ -1,68 +0,0 @@
import {
removePriceListPricesWorkflow,
upsertPriceListPricesWorkflow,
} from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../types/routing"
import { listPriceLists } from "../../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
} from "../../query-config"
import {
AdminDeletePriceListsPriceListPricesReq,
AdminPostPriceListsPriceListPricesReq,
} from "../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListPricesReq>,
res: MedusaResponse
) => {
const { prices } = req.validatedBody
const id = req.params.id
const workflow = upsertPriceListPricesWorkflow(req.scope)
const { errors } = await workflow.run({
input: {
price_lists_data: [{ id, prices }],
},
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const [[priceList]] = await listPriceLists({
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
variables: { filters: { id }, skip: 0, take: 1 },
})
res.status(200).json({ price_list: priceList })
}
export const DELETE = async (
req: AuthenticatedMedusaRequest<AdminDeletePriceListsPriceListPricesReq>,
res: MedusaResponse
) => {
const { ids } = req.validatedBody
const workflow = removePriceListPricesWorkflow(req.scope)
const { errors } = await workflow.run({
input: { ids },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({
ids,
object: "price_list_prices",
deleted: true,
})
}

View File

@@ -2,12 +2,11 @@ import {
deletePriceListsWorkflow,
updatePriceListsWorkflow,
} from "@medusajs/core-flows"
import { MedusaError } from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { listPriceLists } from "../queries"
import { getPriceList } from "../queries"
import {
adminPriceListRemoteQueryFields,
defaultAdminPriceListFields,
@@ -19,24 +18,13 @@ export const GET = async (
res: MedusaResponse
) => {
const id = req.params.id
const [[priceList], count] = await listPriceLists({
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: req.retrieveConfig.select!,
variables: {
filters: { id },
skip: 0,
take: 1,
},
})
if (count === 0) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Price list with id: ${id} was not found`
)
}
res.status(200).json({ price_list: priceList })
}
@@ -56,11 +44,11 @@ export const POST = async (
throw errors[0].error
}
const [[priceList]] = await listPriceLists({
const priceList = await getPriceList({
id,
container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields,
variables: { filters: { id }, skip: 0, take: 1 },
})
res.status(200).json({ price_list: priceList })

View File

@@ -3,10 +3,10 @@ import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import * as QueryConfig from "./query-config"
import {
AdminDeletePriceListsPriceListPricesReq,
AdminGetPriceListsParams,
AdminGetPriceListsPriceListParams,
AdminPostPriceListsPriceListPricesReq,
AdminPostPriceListsPriceListPricesBatchAddReq,
AdminPostPriceListsPriceListPricesBatchRemoveReq,
AdminPostPriceListsPriceListReq,
AdminPostPriceListsReq,
} from "./validators"
@@ -49,12 +49,14 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
},
{
method: ["POST"],
matcher: "/admin/price-lists/:id/prices",
middlewares: [transformBody(AdminPostPriceListsPriceListPricesReq)],
matcher: "/admin/price-lists/:id/prices/batch/add",
middlewares: [transformBody(AdminPostPriceListsPriceListPricesBatchAddReq)],
},
{
method: ["DELETE"],
matcher: "/admin/price-lists/:id/prices",
middlewares: [transformBody(AdminDeletePriceListsPriceListPricesReq)],
method: ["POST"],
matcher: "/admin/price-lists/:id/prices/batch/remove",
middlewares: [
transformBody(AdminPostPriceListsPriceListPricesBatchRemoveReq),
],
},
]

View File

@@ -0,0 +1,40 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
MedusaError,
isPresent,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminPriceListRemoteQueryDTO } from "../types"
import { buildPriceListResponse } from "./"
export async function getPriceList({
id,
container,
remoteQueryFields,
apiFields,
}: {
id: string
container: MedusaContainer
remoteQueryFields: string[]
apiFields: string[]
}): Promise<AdminPriceListRemoteQueryDTO> {
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "price_list",
fields: remoteQueryFields,
variables: { id },
})
const priceLists = await remoteQuery(queryObject)
const [sanitizedPriceList] = buildPriceListResponse(priceLists, apiFields)
if (!isPresent(sanitizedPriceList)) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Price list with id: ${id} was not found`
)
}
return sanitizedPriceList
}

View File

@@ -1 +1,23 @@
import {
buildPriceListRules,
buildPriceSetPricesForCore,
} from "@medusajs/utils"
import { cleanResponseData } from "../../../../utils/clean-response-data"
import { AdminPriceListRemoteQueryDTO } from "../types"
export * from "./get-price-list"
export * from "./list-price-lists"
export function buildPriceListResponse(
priceLists,
apiFields
): AdminPriceListRemoteQueryDTO[] {
for (const priceList of priceLists) {
priceList.rules = buildPriceListRules(priceList.price_list_rules || [])
priceList.prices = buildPriceSetPricesForCore(
priceList.price_set_money_amounts || []
)
}
return priceLists.map((priceList) => cleanResponseData(priceList, apiFields))
}

View File

@@ -1,12 +1,10 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
buildPriceListRules,
buildPriceSetPricesForCore,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { cleanResponseData } from "../../../../utils/clean-response-data"
import { AdminPriceListRemoteQueryDTO } from "../types"
import { buildPriceListResponse } from "./"
export async function listPriceLists({
container,
@@ -32,16 +30,7 @@ export async function listPriceLists({
return [[], 0]
}
for (const priceList of priceLists) {
priceList.rules = buildPriceListRules(priceList.price_list_rules || [])
priceList.prices = buildPriceSetPricesForCore(
priceList.price_set_money_amounts || []
)
}
const sanitizedPriceLists: AdminPriceListRemoteQueryDTO[] = priceLists.map(
(priceList) => cleanResponseData(priceList, apiFields)
)
const sanitizedPriceLists = buildPriceListResponse(priceLists, apiFields)
return [sanitizedPriceLists, metadata.count]
}

View File

@@ -98,21 +98,20 @@ export class AdminPostPriceListsPriceListReq {
@IsOptional()
@IsArray()
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[]
prices: AdminPriceListPricesCreateReq[]
@IsOptional()
@IsObject()
rules?: Record<string, string[]>
}
export class AdminPostPriceListsPriceListPricesReq {
export class AdminPostPriceListsPriceListPricesBatchAddReq {
@IsOptional()
@IsArray()
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[]
prices: AdminPriceListPricesCreateReq[]
}
export class AdminDeletePriceListsPriceListPricesReq {
@IsOptional()
export class AdminPostPriceListsPriceListPricesBatchRemoveReq {
@IsArray()
@IsString({ each: true })
ids: string[]

View File

@@ -37,5 +37,40 @@ export interface UpdatePriceListWorkflowInputDTO {
ends_at?: string | null
status?: PriceListStatus
rules?: Record<string, string[]>
prices?: (UpdatePriceListPriceWorkflowDTO | CreatePriceListPriceWorkflowDTO)[]
prices?: CreatePriceListPriceWorkflowDTO[]
}
export interface UpdatePriceListPricesWorkflowDTO {
id: string
prices: UpdatePriceListPriceWorkflowDTO[]
}
export interface CreatePriceListPricesWorkflowDTO {
id: string
prices: CreatePriceListPriceWorkflowDTO[]
}
export interface UpdatePriceListPriceWorkflowDTO {
data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
variant_price_map: Record<string, string>
}
export interface UpdatePriceListPriceWorkflowStepDTO {
data?: UpdatePriceListPricesWorkflowDTO[]
variant_price_map: Record<string, string>
}
export interface UpsertPriceListPricesWorkflowStepDTO {
data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
variant_price_map: Record<string, string>
}
export interface CreatePriceListsWorkflowStepDTO {
data: CreatePriceListWorkflowInputDTO[]
variant_price_map: Record<string, string>
}
export interface CreatePriceListPricesWorkflowStepDTO {
data: (Pick<CreatePriceListWorkflowInputDTO, "prices"> & { id: string })[]
variant_price_map: Record<string, string>
}

View File

@@ -1,3 +1,2 @@
export * from "./create-price-list"
export * from "./update-price-list"
export * from "./remove-price-list"

View File

@@ -1,25 +0,0 @@
import { CreatePriceListRules, PriceListStatus } from "../../pricing"
import { UpdateProductVariantPricesInputDTO } from "../product"
export type PriceListVariantPriceDTO = UpdateProductVariantPricesInputDTO & {
variant_id?: string
price_set_id?: string
}
export interface UpdatePriceListWorkflowDTO {
id: string
name?: string
starts_at?: Date
ends_at?: Date
status?: PriceListStatus
rules?: CreatePriceListRules
prices?: PriceListVariantPriceDTO[]
customer_groups?: {
id: string
}[]
}
export interface UpdatePriceListWorkflowInputDTO {
price_lists: UpdatePriceListWorkflowDTO[]
}