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 authored and GitHub committed 2024-03-18 14:14:28 +00:00
1 parent 84b8836cbf
commit 0c705d7bd4
28 files changed
+508 -205

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
feat(medusa): rework prices add/remove to batch spec
+7
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
@@ -259,7 +259,7 @@ medusaIntegrationTestRunner({
}) })
describe("POST /admin/price-lists", () => { 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({ await createVariantPriceSet({
container: appContainer, container: appContainer,
variantId: variant.id, 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({ await createVariantPriceSet({
container: appContainer, container: appContainer,
variantId: variant.id, variantId: variant.id,
@@ -456,7 +456,7 @@ medusaIntegrationTestRunner({
], ],
} }
const response = await api.post( let response = await api.post(
`admin/price-lists/${priceList.id}`, `admin/price-lists/${priceList.id}`,
data, data,
adminHeaders adminHeaders
@@ -466,14 +466,8 @@ medusaIntegrationTestRunner({
expect(response.data.price_list).toEqual( expect(response.data.price_list).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
title: "new price list name", title: "new price list name",
description: "new price list description", description: "new price list description",
type: "override",
status: "active",
starts_at: expect.any(String),
ends_at: expect.any(String),
rules: { rules: {
customer_group_id: [customerGroup.id], 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", () => { describe("POST /admin/price-lists/:id/prices/batch/add", () => {
it("should upsert price list prices successfully", async () => { it("should add price list prices successfully", async () => {
const priceSet = await createVariantPriceSet({ const priceSet = await createVariantPriceSet({
container: appContainer, container: appContainer,
variantId: variant.id, variantId: variant.id,
@@ -527,16 +551,11 @@ medusaIntegrationTestRunner({
currency_code: "usd", currency_code: "usd",
rules: { region_id: region.id }, rules: { region_id: region.id },
}, },
{
id: "test-price-id",
variant_id: variant.id,
amount: 200,
},
], ],
} }
const response = await api.post( const response = await api.post(
`admin/price-lists/${priceList.id}/prices`, `admin/price-lists/${priceList.id}/prices/batch/add`,
data, data,
adminHeaders adminHeaders
) )
@@ -555,7 +574,7 @@ medusaIntegrationTestRunner({
expect.objectContaining({ expect.objectContaining({
id: "test-price-id", id: "test-price-id",
currency_code: "usd", currency_code: "usd",
amount: 200, amount: 5000,
}), }),
]), ]),
}) })
@@ -563,8 +582,8 @@ medusaIntegrationTestRunner({
}) })
}) })
describe("DELETE /admin/price-lists/:id/prices", () => { describe("POST /admin/price-lists/:id/prices/batch/remove", () => {
it("should delete price list prices", async () => { it("should remove price list prices successfully", async () => {
const priceSet = await createVariantPriceSet({ const priceSet = await createVariantPriceSet({
container: appContainer, container: appContainer,
variantId: variant.id, variantId: variant.id,
@@ -577,7 +596,6 @@ medusaIntegrationTestRunner({
description: "test", description: "test",
prices: [ prices: [
{ {
id: "test-price-id",
amount: 5000, amount: 5000,
currency_code: "usd", currency_code: "usd",
price_set_id: priceSet.id, price_set_id: priceSet.id,
@@ -589,17 +607,21 @@ medusaIntegrationTestRunner({
}, },
]) ])
let response = await api.delete( const psmaIdToDelete = priceList.price_set_money_amounts![0].id
`/admin/price-lists/${priceList.id}/prices`,
{ ...adminHeaders, data: { ids: ["test-price-id"] } } const response = await api.post(
`/admin/price-lists/${priceList.id}/prices/batch/remove`,
{ ids: [psmaIdToDelete] },
adminHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data).toEqual({ expect(response.data.price_list).toEqual(
ids: ["test-price-id"], expect.objectContaining({
object: "price_list_prices", id: expect.any(String),
deleted: true, prices: [],
}) })
)
}) })
}) })
}) })
@@ -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)
}
}
)
@@ -1,20 +1,15 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { import {
CreatePriceListDTO, CreatePriceListDTO,
CreatePriceListWorkflowInputDTO, CreatePriceListsWorkflowStepDTO,
IPricingModuleService, IPricingModuleService,
} from "@medusajs/types" } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk" 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 createPriceListsStepId = "create-price-lists"
export const createPriceListsStep = createStep( export const createPriceListsStep = createStep(
createPriceListsStepId, createPriceListsStepId,
async (stepInput: WorkflowStepInput, { container }) => { async (stepInput: CreatePriceListsWorkflowStepDTO, { container }) => {
const { data, variant_price_map: variantPriceMap } = stepInput const { data, variant_price_map: variantPriceMap } = stepInput
const pricingModule = container.resolve<IPricingModuleService>( const pricingModule = container.resolve<IPricingModuleService>(
@@ -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)
}
)
@@ -1,6 +1,9 @@
export * from "./create-price-list-prices"
export * from "./create-price-lists" export * from "./create-price-lists"
export * from "./delete-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"
export * from "./update-price-list-prices"
export * from "./update-price-lists" export * from "./update-price-lists"
export * from "./upsert-price-list-prices" export * from "./upsert-price-list-prices"
export * from "./validate-price-lists" export * from "./validate-price-lists"
@@ -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)
}
}
)
@@ -8,20 +8,15 @@ import {
UpdatePriceListPriceDTO, UpdatePriceListPriceDTO,
UpdatePriceListPriceWorkflowDTO, UpdatePriceListPriceWorkflowDTO,
UpdatePriceListPricesDTO, UpdatePriceListPricesDTO,
UpdatePriceListWorkflowInputDTO, UpsertPriceListPricesWorkflowStepDTO,
} from "@medusajs/types" } from "@medusajs/types"
import { buildPriceSetPricesForModule, promiseAll } from "@medusajs/utils" import { buildPriceSetPricesForModule, promiseAll } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk" 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 upsertPriceListPricesStepId = "upsert-price-list-prices"
export const upsertPriceListPricesStep = createStep( export const upsertPriceListPricesStep = createStep(
upsertPriceListPricesStepId, upsertPriceListPricesStepId,
async (stepInput: WorkflowStepInput, { container }) => { async (stepInput: UpsertPriceListPricesWorkflowStepDTO, { container }) => {
const { data, variant_price_map: variantPriceSetMap } = stepInput const { data, variant_price_map: variantPriceSetMap } = stepInput
const priceListPricesToUpdate: UpdatePriceListPricesDTO[] = [] const priceListPricesToUpdate: UpdatePriceListPricesDTO[] = []
@@ -6,12 +6,13 @@ import {
} from "@medusajs/utils" } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk" import { StepResponse, createStep } from "@medusajs/workflows-sdk"
type WorkflowStepInput = Pick<UpdatePriceListWorkflowInputDTO, "prices">[]
export const validateVariantPriceLinksStepId = "validate-variant-price-links" export const validateVariantPriceLinksStepId = "validate-variant-price-links"
export const validateVariantPriceLinksStep = createStep( export const validateVariantPriceLinksStep = createStep(
validateVariantPriceLinksStepId, validateVariantPriceLinksStepId,
async (data: WorkflowStepInput, { container }) => { async (
data: Pick<UpdatePriceListWorkflowInputDTO, "prices">[],
{ container }
) => {
const remoteQuery = container.resolve( const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY ContainerRegistrationKeys.REMOTE_QUERY
) )
@@ -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,
})
}
)
@@ -1,12 +1,10 @@
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk" import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
import { deletePriceListsStep } from "../steps" import { deletePriceListsStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const deletePriceListsWorkflowId = "delete-price-lists" export const deletePriceListsWorkflowId = "delete-price-lists"
export const deletePriceListsWorkflow = createWorkflow( export const deletePriceListsWorkflow = createWorkflow(
deletePriceListsWorkflowId, deletePriceListsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => { (input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
return deletePriceListsStep(input.ids) return deletePriceListsStep(input.ids)
} }
) )
@@ -1,3 +1,4 @@
export * from "./create-price-list-prices"
export * from "./create-price-lists" export * from "./create-price-lists"
export * from "./delete-price-lists" export * from "./delete-price-lists"
export * from "./remove-price-list-prices" export * from "./remove-price-list-prices"
@@ -1,12 +1,10 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { removePriceListPricesStep } from "../steps" import { removePriceListPricesStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const removePriceListPricesWorkflowId = "remove-price-list-prices" export const removePriceListPricesWorkflowId = "remove-price-list-prices"
export const removePriceListPricesWorkflow = createWorkflow( export const removePriceListPricesWorkflow = createWorkflow(
removePriceListPricesWorkflowId, removePriceListPricesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => { (input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
removePriceListPricesStep(input.ids) removePriceListPricesStep(input.ids)
} }
) )
@@ -6,33 +6,46 @@ import {
transform, transform,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { import {
createPriceListPricesStep,
getExistingPriceListsPriceIdsStep,
removePriceListPricesStep,
updatePriceListsStep, updatePriceListsStep,
upsertPriceListPricesStep,
validatePriceListsStep, validatePriceListsStep,
validateVariantPriceLinksStep, validateVariantPriceLinksStep,
} from "../steps" } from "../steps"
type WorkflowInput = { price_lists_data: UpdatePriceListWorkflowInputDTO[] }
export const updatePriceListsWorkflowId = "update-price-lists" export const updatePriceListsWorkflowId = "update-price-lists"
export const updatePriceListsWorkflow = createWorkflow( export const updatePriceListsWorkflow = createWorkflow(
updatePriceListsWorkflowId, updatePriceListsWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => { (
input: WorkflowData<{ price_lists_data: UpdatePriceListWorkflowInputDTO[] }>
): WorkflowData<void> => {
const [priceListsMap, variantPriceMap] = parallelize( const [priceListsMap, variantPriceMap] = parallelize(
validatePriceListsStep(input.price_lists_data), validatePriceListsStep(input.price_lists_data),
validateVariantPriceLinksStep(input.price_lists_data) validateVariantPriceLinksStep(input.price_lists_data)
) )
const updatePricesInput = transform( const getPriceListPricesInput = transform({ priceListsMap }, (data) => ({
{ priceListsMap, variantPriceMap, input }, price_list_ids: Object.keys(data.priceListsMap),
(data) => ({ }))
data: data.input.price_lists_data,
price_lists_map: data.priceListsMap, const priceListPriceIdMap = getExistingPriceListsPriceIdsStep(
variant_price_map: data.variantPriceMap, 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) => { const updatePriceListInput = transform({ input }, (data) => {
return data.input.price_lists_data.map((priceListData) => { return data.input.price_lists_data.map((priceListData) => {
@@ -10,14 +10,14 @@ import {
validateVariantPriceLinksStep, validateVariantPriceLinksStep,
} from "../steps" } from "../steps"
type WorkflowInput = {
price_lists_data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
}
export const upsertPriceListPricesWorkflowId = "upsert-price-list-prices" export const upsertPriceListPricesWorkflowId = "upsert-price-list-prices"
export const upsertPriceListPricesWorkflow = createWorkflow( export const upsertPriceListPricesWorkflow = createWorkflow(
upsertPriceListPricesWorkflowId, upsertPriceListPricesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => { (
input: WorkflowData<{
price_lists_data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
}>
): WorkflowData<void> => {
const [_, variantPriceMap] = parallelize( const [_, variantPriceMap] = parallelize(
validatePriceListsStep(input.price_lists_data), validatePriceListsStep(input.price_lists_data),
validateVariantPriceLinksStep(input.price_lists_data) validateVariantPriceLinksStep(input.price_lists_data)
@@ -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 })
}
@@ -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 })
}
@@ -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,
})
}
@@ -2,12 +2,11 @@ import {
deletePriceListsWorkflow, deletePriceListsWorkflow,
updatePriceListsWorkflow, updatePriceListsWorkflow,
} from "@medusajs/core-flows" } from "@medusajs/core-flows"
import { MedusaError } from "@medusajs/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "../../../../types/routing" } from "../../../../types/routing"
import { listPriceLists } from "../queries" import { getPriceList } from "../queries"
import { import {
adminPriceListRemoteQueryFields, adminPriceListRemoteQueryFields,
defaultAdminPriceListFields, defaultAdminPriceListFields,
@@ -19,24 +18,13 @@ export const GET = async (
res: MedusaResponse res: MedusaResponse
) => { ) => {
const id = req.params.id const id = req.params.id
const [[priceList], count] = await listPriceLists({ const priceList = await getPriceList({
id,
container: req.scope, container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields, remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: req.retrieveConfig.select!, 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 }) res.status(200).json({ price_list: priceList })
} }
@@ -56,11 +44,11 @@ export const POST = async (
throw errors[0].error throw errors[0].error
} }
const [[priceList]] = await listPriceLists({ const priceList = await getPriceList({
id,
container: req.scope, container: req.scope,
remoteQueryFields: adminPriceListRemoteQueryFields, remoteQueryFields: adminPriceListRemoteQueryFields,
apiFields: defaultAdminPriceListFields, apiFields: defaultAdminPriceListFields,
variables: { filters: { id }, skip: 0, take: 1 },
}) })
res.status(200).json({ price_list: priceList }) res.status(200).json({ price_list: priceList })
@@ -3,10 +3,10 @@ import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware" import { authenticate } from "../../../utils/authenticate-middleware"
import * as QueryConfig from "./query-config" import * as QueryConfig from "./query-config"
import { import {
AdminDeletePriceListsPriceListPricesReq,
AdminGetPriceListsParams, AdminGetPriceListsParams,
AdminGetPriceListsPriceListParams, AdminGetPriceListsPriceListParams,
AdminPostPriceListsPriceListPricesReq, AdminPostPriceListsPriceListPricesBatchAddReq,
AdminPostPriceListsPriceListPricesBatchRemoveReq,
AdminPostPriceListsPriceListReq, AdminPostPriceListsPriceListReq,
AdminPostPriceListsReq, AdminPostPriceListsReq,
} from "./validators" } from "./validators"
@@ -49,12 +49,14 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
}, },
{ {
method: ["POST"], method: ["POST"],
matcher: "/admin/price-lists/:id/prices", matcher: "/admin/price-lists/:id/prices/batch/add",
middlewares: [transformBody(AdminPostPriceListsPriceListPricesReq)], middlewares: [transformBody(AdminPostPriceListsPriceListPricesBatchAddReq)],
}, },
{ {
method: ["DELETE"], method: ["POST"],
matcher: "/admin/price-lists/:id/prices", matcher: "/admin/price-lists/:id/prices/batch/remove",
middlewares: [transformBody(AdminDeletePriceListsPriceListPricesReq)], middlewares: [
transformBody(AdminPostPriceListsPriceListPricesBatchRemoveReq),
],
}, },
] ]
@@ -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
}
@@ -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 * 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))
}
@@ -1,12 +1,10 @@
import { MedusaContainer } from "@medusajs/types" import { MedusaContainer } from "@medusajs/types"
import { import {
ContainerRegistrationKeys, ContainerRegistrationKeys,
buildPriceListRules,
buildPriceSetPricesForCore,
remoteQueryObjectFromString, remoteQueryObjectFromString,
} from "@medusajs/utils" } from "@medusajs/utils"
import { cleanResponseData } from "../../../../utils/clean-response-data"
import { AdminPriceListRemoteQueryDTO } from "../types" import { AdminPriceListRemoteQueryDTO } from "../types"
import { buildPriceListResponse } from "./"
export async function listPriceLists({ export async function listPriceLists({
container, container,
@@ -32,16 +30,7 @@ export async function listPriceLists({
return [[], 0] return [[], 0]
} }
for (const priceList of priceLists) { const sanitizedPriceLists = buildPriceListResponse(priceLists, apiFields)
priceList.rules = buildPriceListRules(priceList.price_list_rules || [])
priceList.prices = buildPriceSetPricesForCore(
priceList.price_set_money_amounts || []
)
}
const sanitizedPriceLists: AdminPriceListRemoteQueryDTO[] = priceLists.map(
(priceList) => cleanResponseData(priceList, apiFields)
)
return [sanitizedPriceLists, metadata.count] return [sanitizedPriceLists, metadata.count]
} }
@@ -98,21 +98,20 @@ export class AdminPostPriceListsPriceListReq {
@IsOptional() @IsOptional()
@IsArray() @IsArray()
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[] prices: AdminPriceListPricesCreateReq[]
@IsOptional() @IsOptional()
@IsObject() @IsObject()
rules?: Record<string, string[]> rules?: Record<string, string[]>
} }
export class AdminPostPriceListsPriceListPricesReq { export class AdminPostPriceListsPriceListPricesBatchAddReq {
@IsOptional() @IsOptional()
@IsArray() @IsArray()
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[] prices: AdminPriceListPricesCreateReq[]
} }
export class AdminDeletePriceListsPriceListPricesReq { export class AdminPostPriceListsPriceListPricesBatchRemoveReq {
@IsOptional()
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
ids: string[] ids: string[]
+36 -1
View File
@@ -37,5 +37,40 @@ export interface UpdatePriceListWorkflowInputDTO {
ends_at?: string | null ends_at?: string | null
status?: PriceListStatus status?: PriceListStatus
rules?: Record<string, string[]> 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>
} }
@@ -1,3 +1,2 @@
export * from "./create-price-list" export * from "./create-price-list"
export * from "./update-price-list"
export * from "./remove-price-list" export * from "./remove-price-list"
@@ -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[]
}