From 0c705d7bd41a768c48017ae95b3c8414d96c6acb Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Mon, 18 Mar 2024 15:14:28 +0100 Subject: [PATCH] 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" --- .changeset/eight-candles-smoke.md | 5 ++ .changeset/pretty-bugs-smoke.md | 7 ++ .../price-lists/admin/price-lists.spec.ts | 80 ++++++++++------- .../steps/create-price-list-prices.ts | 61 +++++++++++++ .../price-list/steps/create-price-lists.ts | 9 +- .../get-existing-price-lists-price-ids.ts | 32 +++++++ .../core-flows/src/price-list/steps/index.ts | 3 + .../steps/update-price-list-prices.ts | 85 +++++++++++++++++++ .../steps/upsert-price-list-prices.ts | 9 +- .../steps/validate-variant-price-links.ts | 7 +- .../workflows/create-price-list-prices.ts | 31 +++++++ .../workflows/delete-price-lists.ts | 4 +- .../src/price-list/workflows/index.ts | 1 + .../workflows/remove-price-list-prices.ts | 4 +- .../workflows/update-price-lists.ts | 37 +++++--- .../workflows/upsert-price-list-prices.ts | 10 +-- .../[id]/prices/batch/add/route.ts | 38 +++++++++ .../[id]/prices/batch/remove/route.ts | 37 ++++++++ .../admin/price-lists/[id]/prices/route.ts | 68 --------------- .../api-v2/admin/price-lists/[id]/route.ts | 22 ++--- .../api-v2/admin/price-lists/middlewares.ts | 16 ++-- .../price-lists/queries/get-price-list.ts | 40 +++++++++ .../api-v2/admin/price-lists/queries/index.ts | 22 +++++ .../price-lists/queries/list-price-lists.ts | 15 +--- .../api-v2/admin/price-lists/validators.ts | 9 +- packages/types/src/pricing/workflows.ts | 37 +++++++- .../types/src/workflow/price-list/index.ts | 1 - .../workflow/price-list/update-price-list.ts | 25 ------ 28 files changed, 509 insertions(+), 206 deletions(-) create mode 100644 .changeset/eight-candles-smoke.md create mode 100644 .changeset/pretty-bugs-smoke.md create mode 100644 packages/core-flows/src/price-list/steps/create-price-list-prices.ts create mode 100644 packages/core-flows/src/price-list/steps/get-existing-price-lists-price-ids.ts create mode 100644 packages/core-flows/src/price-list/steps/update-price-list-prices.ts create mode 100644 packages/core-flows/src/price-list/workflows/create-price-list-prices.ts create mode 100644 packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/add/route.ts create mode 100644 packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/remove/route.ts delete mode 100644 packages/medusa/src/api-v2/admin/price-lists/[id]/prices/route.ts create mode 100644 packages/medusa/src/api-v2/admin/price-lists/queries/get-price-list.ts delete mode 100644 packages/types/src/workflow/price-list/update-price-list.ts diff --git a/.changeset/eight-candles-smoke.md b/.changeset/eight-candles-smoke.md new file mode 100644 index 0000000000..b30bed98f0 --- /dev/null +++ b/.changeset/eight-candles-smoke.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +feat(medusa): rework prices add/remove to batch spec diff --git a/.changeset/pretty-bugs-smoke.md b/.changeset/pretty-bugs-smoke.md new file mode 100644 index 0000000000..cba3fd5240 --- /dev/null +++ b/.changeset/pretty-bugs-smoke.md @@ -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 diff --git a/integration-tests/modules/__tests__/price-lists/admin/price-lists.spec.ts b/integration-tests/modules/__tests__/price-lists/admin/price-lists.spec.ts index fd40b24127..3cbb452779 100644 --- a/integration-tests/modules/__tests__/price-lists/admin/price-lists.spec.ts +++ b/integration-tests/modules/__tests__/price-lists/admin/price-lists.spec.ts @@ -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: [], + }) + ) }) }) }) diff --git a/packages/core-flows/src/price-list/steps/create-price-list-prices.ts b/packages/core-flows/src/price-list/steps/create-price-list-prices.ts new file mode 100644 index 0000000000..b9663192a7 --- /dev/null +++ b/packages/core-flows/src/price-list/steps/create-price-list-prices.ts @@ -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( + 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( + ModuleRegistrationName.PRICING + ) + + if (createdIds.length) { + await pricingModule.removePrices(createdIds) + } + } +) diff --git a/packages/core-flows/src/price-list/steps/create-price-lists.ts b/packages/core-flows/src/price-list/steps/create-price-lists.ts index dadd198d17..6c4d0f7c01 100644 --- a/packages/core-flows/src/price-list/steps/create-price-lists.ts +++ b/packages/core-flows/src/price-list/steps/create-price-lists.ts @@ -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 -} - 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( diff --git a/packages/core-flows/src/price-list/steps/get-existing-price-lists-price-ids.ts b/packages/core-flows/src/price-list/steps/get-existing-price-lists-price-ids.ts new file mode 100644 index 0000000000..8884f8c95a --- /dev/null +++ b/packages/core-flows/src/price-list/steps/get-existing-price-lists-price-ids.ts @@ -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 = {} + const pricingModule = container.resolve( + 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) + } +) diff --git a/packages/core-flows/src/price-list/steps/index.ts b/packages/core-flows/src/price-list/steps/index.ts index fcef10dc22..c542e3e0f4 100644 --- a/packages/core-flows/src/price-list/steps/index.ts +++ b/packages/core-flows/src/price-list/steps/index.ts @@ -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" diff --git a/packages/core-flows/src/price-list/steps/update-price-list-prices.ts b/packages/core-flows/src/price-list/steps/update-price-list-prices.ts new file mode 100644 index 0000000000..43f1c9a7f1 --- /dev/null +++ b/packages/core-flows/src/price-list/steps/update-price-list-prices.ts @@ -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( + 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() + 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( + ModuleRegistrationName.PRICING + ) + + if (dataBeforePriceUpdate.length) { + await pricingModule.updatePriceListPrices(dataBeforePriceUpdate) + } + } +) diff --git a/packages/core-flows/src/price-list/steps/upsert-price-list-prices.ts b/packages/core-flows/src/price-list/steps/upsert-price-list-prices.ts index 5204c953dd..ec3894cd8f 100644 --- a/packages/core-flows/src/price-list/steps/upsert-price-list-prices.ts +++ b/packages/core-flows/src/price-list/steps/upsert-price-list-prices.ts @@ -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[] - variant_price_map: Record -} - 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[] = [] diff --git a/packages/core-flows/src/price-list/steps/validate-variant-price-links.ts b/packages/core-flows/src/price-list/steps/validate-variant-price-links.ts index 9178beb7a9..c195a81120 100644 --- a/packages/core-flows/src/price-list/steps/validate-variant-price-links.ts +++ b/packages/core-flows/src/price-list/steps/validate-variant-price-links.ts @@ -6,12 +6,13 @@ import { } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" -type WorkflowStepInput = Pick[] - export const validateVariantPriceLinksStepId = "validate-variant-price-links" export const validateVariantPriceLinksStep = createStep( validateVariantPriceLinksStepId, - async (data: WorkflowStepInput, { container }) => { + async ( + data: Pick[], + { container } + ) => { const remoteQuery = container.resolve( ContainerRegistrationKeys.REMOTE_QUERY ) diff --git a/packages/core-flows/src/price-list/workflows/create-price-list-prices.ts b/packages/core-flows/src/price-list/workflows/create-price-list-prices.ts new file mode 100644 index 0000000000..050d148279 --- /dev/null +++ b/packages/core-flows/src/price-list/workflows/create-price-list-prices.ts @@ -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 => { + const [_, variantPriceMap] = parallelize( + validatePriceListsStep(input.data), + validateVariantPriceLinksStep(input.data) + ) + + createPriceListPricesStep({ + data: input.data, + variant_price_map: variantPriceMap, + }) + } +) diff --git a/packages/core-flows/src/price-list/workflows/delete-price-lists.ts b/packages/core-flows/src/price-list/workflows/delete-price-lists.ts index 70d03a1871..608c607418 100644 --- a/packages/core-flows/src/price-list/workflows/delete-price-lists.ts +++ b/packages/core-flows/src/price-list/workflows/delete-price-lists.ts @@ -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): WorkflowData => { + (input: WorkflowData<{ ids: string[] }>): WorkflowData => { return deletePriceListsStep(input.ids) } ) diff --git a/packages/core-flows/src/price-list/workflows/index.ts b/packages/core-flows/src/price-list/workflows/index.ts index 479dc8368d..ffa4e400a3 100644 --- a/packages/core-flows/src/price-list/workflows/index.ts +++ b/packages/core-flows/src/price-list/workflows/index.ts @@ -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" diff --git a/packages/core-flows/src/price-list/workflows/remove-price-list-prices.ts b/packages/core-flows/src/price-list/workflows/remove-price-list-prices.ts index c048e9abff..955d06a20f 100644 --- a/packages/core-flows/src/price-list/workflows/remove-price-list-prices.ts +++ b/packages/core-flows/src/price-list/workflows/remove-price-list-prices.ts @@ -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): WorkflowData => { + (input: WorkflowData<{ ids: string[] }>): WorkflowData => { removePriceListPricesStep(input.ids) } ) diff --git a/packages/core-flows/src/price-list/workflows/update-price-lists.ts b/packages/core-flows/src/price-list/workflows/update-price-lists.ts index 62453d8bf1..0b6fb4b51d 100644 --- a/packages/core-flows/src/price-list/workflows/update-price-lists.ts +++ b/packages/core-flows/src/price-list/workflows/update-price-lists.ts @@ -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): WorkflowData => { + ( + input: WorkflowData<{ price_lists_data: UpdatePriceListWorkflowInputDTO[] }> + ): WorkflowData => { 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) => { diff --git a/packages/core-flows/src/price-list/workflows/upsert-price-list-prices.ts b/packages/core-flows/src/price-list/workflows/upsert-price-list-prices.ts index 3fb4f409ea..b56a91b45b 100644 --- a/packages/core-flows/src/price-list/workflows/upsert-price-list-prices.ts +++ b/packages/core-flows/src/price-list/workflows/upsert-price-list-prices.ts @@ -10,14 +10,14 @@ import { validateVariantPriceLinksStep, } from "../steps" -type WorkflowInput = { - price_lists_data: Pick[] -} - export const upsertPriceListPricesWorkflowId = "upsert-price-list-prices" export const upsertPriceListPricesWorkflow = createWorkflow( upsertPriceListPricesWorkflowId, - (input: WorkflowData): WorkflowData => { + ( + input: WorkflowData<{ + price_lists_data: Pick[] + }> + ): WorkflowData => { const [_, variantPriceMap] = parallelize( validatePriceListsStep(input.price_lists_data), validateVariantPriceLinksStep(input.price_lists_data) diff --git a/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/add/route.ts b/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/add/route.ts new file mode 100644 index 0000000000..5241eefcba --- /dev/null +++ b/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/add/route.ts @@ -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, + 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 }) +} diff --git a/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/remove/route.ts b/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/remove/route.ts new file mode 100644 index 0000000000..1d75cc7a8a --- /dev/null +++ b/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/batch/remove/route.ts @@ -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, + 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 }) +} diff --git a/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/route.ts b/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/route.ts deleted file mode 100644 index c80183e59c..0000000000 --- a/packages/medusa/src/api-v2/admin/price-lists/[id]/prices/route.ts +++ /dev/null @@ -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, - 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, - 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, - }) -} diff --git a/packages/medusa/src/api-v2/admin/price-lists/[id]/route.ts b/packages/medusa/src/api-v2/admin/price-lists/[id]/route.ts index 31bba5eb48..52fe009c50 100644 --- a/packages/medusa/src/api-v2/admin/price-lists/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/price-lists/[id]/route.ts @@ -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 }) diff --git a/packages/medusa/src/api-v2/admin/price-lists/middlewares.ts b/packages/medusa/src/api-v2/admin/price-lists/middlewares.ts index 78a2f6ae1c..777348c617 100644 --- a/packages/medusa/src/api-v2/admin/price-lists/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/price-lists/middlewares.ts @@ -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), + ], }, ] diff --git a/packages/medusa/src/api-v2/admin/price-lists/queries/get-price-list.ts b/packages/medusa/src/api-v2/admin/price-lists/queries/get-price-list.ts new file mode 100644 index 0000000000..5a75fa0cbf --- /dev/null +++ b/packages/medusa/src/api-v2/admin/price-lists/queries/get-price-list.ts @@ -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 { + 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 +} diff --git a/packages/medusa/src/api-v2/admin/price-lists/queries/index.ts b/packages/medusa/src/api-v2/admin/price-lists/queries/index.ts index 56ce79ca4c..4dc752b843 100644 --- a/packages/medusa/src/api-v2/admin/price-lists/queries/index.ts +++ b/packages/medusa/src/api-v2/admin/price-lists/queries/index.ts @@ -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)) +} diff --git a/packages/medusa/src/api-v2/admin/price-lists/queries/list-price-lists.ts b/packages/medusa/src/api-v2/admin/price-lists/queries/list-price-lists.ts index 8d6fe3b81d..b08bd7aa41 100644 --- a/packages/medusa/src/api-v2/admin/price-lists/queries/list-price-lists.ts +++ b/packages/medusa/src/api-v2/admin/price-lists/queries/list-price-lists.ts @@ -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] } diff --git a/packages/medusa/src/api-v2/admin/price-lists/validators.ts b/packages/medusa/src/api-v2/admin/price-lists/validators.ts index 879d4d34e8..0c2772c740 100644 --- a/packages/medusa/src/api-v2/admin/price-lists/validators.ts +++ b/packages/medusa/src/api-v2/admin/price-lists/validators.ts @@ -98,21 +98,20 @@ export class AdminPostPriceListsPriceListReq { @IsOptional() @IsArray() - prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[] + prices: AdminPriceListPricesCreateReq[] @IsOptional() @IsObject() rules?: Record } -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[] diff --git a/packages/types/src/pricing/workflows.ts b/packages/types/src/pricing/workflows.ts index d6ca3d42b1..f03d1c34d7 100644 --- a/packages/types/src/pricing/workflows.ts +++ b/packages/types/src/pricing/workflows.ts @@ -37,5 +37,40 @@ export interface UpdatePriceListWorkflowInputDTO { ends_at?: string | null status?: PriceListStatus rules?: Record - prices?: (UpdatePriceListPriceWorkflowDTO | CreatePriceListPriceWorkflowDTO)[] + prices?: CreatePriceListPriceWorkflowDTO[] +} + +export interface UpdatePriceListPricesWorkflowDTO { + id: string + prices: UpdatePriceListPriceWorkflowDTO[] +} + +export interface CreatePriceListPricesWorkflowDTO { + id: string + prices: CreatePriceListPriceWorkflowDTO[] +} + +export interface UpdatePriceListPriceWorkflowDTO { + data: Pick[] + variant_price_map: Record +} + +export interface UpdatePriceListPriceWorkflowStepDTO { + data?: UpdatePriceListPricesWorkflowDTO[] + variant_price_map: Record +} + +export interface UpsertPriceListPricesWorkflowStepDTO { + data: Pick[] + variant_price_map: Record +} + +export interface CreatePriceListsWorkflowStepDTO { + data: CreatePriceListWorkflowInputDTO[] + variant_price_map: Record +} + +export interface CreatePriceListPricesWorkflowStepDTO { + data: (Pick & { id: string })[] + variant_price_map: Record } diff --git a/packages/types/src/workflow/price-list/index.ts b/packages/types/src/workflow/price-list/index.ts index 3649befbea..15b7306909 100644 --- a/packages/types/src/workflow/price-list/index.ts +++ b/packages/types/src/workflow/price-list/index.ts @@ -1,3 +1,2 @@ export * from "./create-price-list" -export * from "./update-price-list" export * from "./remove-price-list" diff --git a/packages/types/src/workflow/price-list/update-price-list.ts b/packages/types/src/workflow/price-list/update-price-list.ts deleted file mode 100644 index e2d23cd86f..0000000000 --- a/packages/types/src/workflow/price-list/update-price-list.ts +++ /dev/null @@ -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[] -}