diff --git a/.changeset/sunny-buckets-chew.md b/.changeset/sunny-buckets-chew.md new file mode 100644 index 0000000000..2234112446 --- /dev/null +++ b/.changeset/sunny-buckets-chew.md @@ -0,0 +1,7 @@ +--- +"@medusajs/dashboard": minor +"@medusajs/types": minor +"@medusajs/medusa": minor +--- + +fix(dashboard,medusa,types): improve performance for price list prices retrieval diff --git a/integration-tests/http/__tests__/price-list/admin/price-list.spec.ts b/integration-tests/http/__tests__/price-list/admin/price-list.spec.ts index 06b9c3e2ed..56890f708b 100644 --- a/integration-tests/http/__tests__/price-list/admin/price-list.spec.ts +++ b/integration-tests/http/__tests__/price-list/admin/price-list.spec.ts @@ -14,6 +14,7 @@ medusaIntegrationTestRunner({ env: {}, testSuite: ({ dbConnection, getContainer, api }) => { let pricelist1 + let pricelist1Prices let pricelist2 let region1 let product1 @@ -93,6 +94,10 @@ medusaIntegrationTestRunner({ ) ).data.price_list + pricelist1Prices = await api + .get(`/admin/price-lists/${pricelist1.id}/prices`, adminHeaders) + .then((res) => res.data.prices) + pricelist2 = ( await api.post( "/admin/price-lists", @@ -134,7 +139,7 @@ medusaIntegrationTestRunner({ }) const response = await api.post( - "/admin/price-lists", + "/admin/price-lists?fields=prices.*,prices.price_set.*,prices.price_set.variant.*", payload, adminHeaders ) @@ -189,34 +194,12 @@ medusaIntegrationTestRunner({ status: pricelist1.status, starts_at: pricelist1.starts_at, ends_at: pricelist1.ends_at, - prices: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - amount: 100, - currency_code: "usd", - // BREAKING: Min and max quantity are returned as string - min_quantity: 1, - max_quantity: 100, - variant_id: product1.variants[0].id, - created_at: expect.any(String), - updated_at: expect.any(String), - // BREAKING: `variant` and `variants` are not returned as part of the prices - }), - expect.objectContaining({ - id: expect.any(String), - amount: 80, - currency_code: "usd", - min_quantity: 101, - max_quantity: 500, - variant_id: product1.variants[0].id, - created_at: expect.any(String), - updated_at: expect.any(String), - }), - ]), created_at: expect.any(String), updated_at: expect.any(String), }) ) + // BREAKING: Prices are not returned as part of the price list if not specified in fields + expect(response.data.price_list).not.toHaveProperty("prices") }) it("returns a list of price lists", async () => { @@ -367,22 +350,6 @@ medusaIntegrationTestRunner({ status: "draft", starts_at: "2022-09-01T00:00:00.000Z", ends_at: "2022-12-31T00:00:00.000Z", - prices: expect.arrayContaining([ - expect.objectContaining({ - amount: 100, - currency_code: "usd", - id: expect.any(String), - max_quantity: 100, - min_quantity: 1, - }), - expect.objectContaining({ - amount: 80, - currency_code: "usd", - id: expect.any(String), - max_quantity: 500, - min_quantity: 101, - }), - ]), rules: { "customer.groups.id": [customerGroup1.id], }, @@ -393,11 +360,12 @@ medusaIntegrationTestRunner({ }) it("updates the amount and currency of a price in the price list", async () => { + const priceToUpdate = pricelist1Prices.find((p) => p.amount === 80) const payload = { // BREAKING: Updates of prices happen through the batch endpoint, and doing it through the price list update endpoint is no longer supported update: [ { - id: pricelist1.prices.find((p) => p.amount === 80).id, + id: priceToUpdate.id, amount: 250, currency_code: "eur", variant_id: product1.variants[0].id, @@ -411,27 +379,29 @@ medusaIntegrationTestRunner({ adminHeaders ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices).toEqual([ - expect.objectContaining({ - amount: 100, - currency_code: "usd", - id: expect.any(String), - max_quantity: 100, - min_quantity: 1, - }), - expect.objectContaining({ - amount: 250, - currency_code: "eur", - id: expect.any(String), - max_quantity: 500, - min_quantity: 101, - }), - ]) + expect(response.data.prices).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + amount: 100, + currency_code: "usd", + id: expect.any(String), + max_quantity: 100, + min_quantity: 1, + }), + expect.objectContaining({ + amount: 250, + currency_code: "eur", + id: expect.any(String), + max_quantity: 500, + min_quantity: 101, + }), + ]) + ) }) }) @@ -470,13 +440,13 @@ medusaIntegrationTestRunner({ adminHeaders ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices.length).toEqual(5) - expect(response.data.price_list.prices).toEqual( + expect(response.data.prices.length).toEqual(5) + expect(response.data.prices).toEqual( expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), @@ -484,7 +454,12 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 1, max_quantity: 100, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), @@ -492,13 +467,23 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 101, max_quantity: 500, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), amount: 45, currency_code: "usd", - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), min_quantity: 1001, max_quantity: 2000, }), @@ -506,7 +491,12 @@ medusaIntegrationTestRunner({ id: expect.any(String), amount: 35, currency_code: "usd", - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), min_quantity: 2001, max_quantity: 3000, }), @@ -514,7 +504,12 @@ medusaIntegrationTestRunner({ id: expect.any(String), amount: 25, currency_code: "usd", - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), min_quantity: 3001, max_quantity: 4000, }), @@ -551,13 +546,13 @@ medusaIntegrationTestRunner({ ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices.length).toEqual(2) - expect(response.data.price_list.prices).toEqual( + expect(response.data.prices.length).toEqual(2) + expect(response.data.prices).toEqual( expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), @@ -565,7 +560,12 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 1, max_quantity: 100, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), @@ -573,7 +573,12 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 101, max_quantity: 500, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), ]) ) @@ -605,13 +610,13 @@ medusaIntegrationTestRunner({ adminHeaders ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices.length).toEqual(4) - expect(response.data.price_list.prices).toEqual( + expect(response.data.prices.length).toEqual(4) + expect(response.data.prices).toEqual( expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), @@ -619,7 +624,12 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 1, max_quantity: 100, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), @@ -627,20 +637,40 @@ medusaIntegrationTestRunner({ currency_code: "usd", min_quantity: 101, max_quantity: 500, - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), amount: 100, currency_code: "eur", - rules: { region_id: region1.id }, - variant_id: product1.variants[0].id, + price_rules: expect.arrayContaining([ + expect.objectContaining({ + attribute: "region_id", + value: region1.id, + }), + ]), + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), expect.objectContaining({ id: expect.any(String), amount: 200, currency_code: "eur", - variant_id: product1.variants[0].id, + price_set: expect.objectContaining({ + id: expect.any(String), + variant: { + id: product1.variants[0].id, + }, + }), }), ]) ) @@ -680,41 +710,48 @@ medusaIntegrationTestRunner({ ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices.length).toEqual(0) + expect(response.data.prices.length).toEqual(0) }) }) describe("DELETE /admin/price-lists/:id/prices/batch", () => { // BREAKING: The batch method signature changed it("Deletes several prices associated with a price list", async () => { + const priceToDelete = pricelist1Prices.find((p) => p.amount === 100) await api.post( `/admin/price-lists/${pricelist1.id}/prices/batch`, { - delete: [pricelist1.prices[0].id], + delete: [priceToDelete.id], }, adminHeaders ) const response = await api.get( - `/admin/price-lists/${pricelist1.id}`, + `/admin/price-lists/${pricelist1.id}/prices`, adminHeaders ) expect(response.status).toEqual(200) - expect(response.data.price_list.prices).toEqual([ - expect.objectContaining({ - amount: 80, - currency_code: "usd", - min_quantity: 101, - max_quantity: 500, - variant_id: product1.variants[0].id, - }), - ]) + expect(response.data.prices).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + amount: 80, + currency_code: "usd", + min_quantity: 101, + max_quantity: 500, + price_set: expect.objectContaining({ + variant: expect.objectContaining({ + id: product1.variants[0].id, + }), + }), + }), + ]) + ) }) }) 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 645f1e88ff..43a1d8000f 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 @@ -99,7 +99,10 @@ medusaIntegrationTestRunner({ }, ]) - let response = await api.get(`/admin/price-lists`, adminHeaders) + let response = await api.get( + `/admin/price-lists?fields=prices.*,prices.price_rules.*,prices.price_set.*,prices.price_set.variant.*`, + adminHeaders + ) expect(response.status).toEqual(200) expect(response.data.count).toEqual(1) @@ -130,9 +133,16 @@ medusaIntegrationTestRunner({ updated_at: expect.any(String), deleted_at: null, price_set_id: expect.any(String), + price_list_id: expect.any(String), + title: null, rules: { region_id: region.id, }, + rules_count: 1, + raw_amount: expect.objectContaining({ + value: "5000", + precision: 20, + }), }, ], }, @@ -258,7 +268,7 @@ medusaIntegrationTestRunner({ ]) let response = await api.get( - `/admin/price-lists/${priceList.id}`, + `/admin/price-lists/${priceList.id}?fields=prices.*,prices.price_rules.*,prices.price_set.*,prices.price_set.variant.*`, adminHeaders ) @@ -288,8 +298,15 @@ medusaIntegrationTestRunner({ variant_id: variant.id, created_at: expect.any(String), updated_at: expect.any(String), + price_list_id: expect.any(String), price_set_id: expect.any(String), deleted_at: null, + rules_count: 1, + raw_amount: expect.objectContaining({ + value: "5000", + precision: 20, + }), + title: null, rules: { region_id: region.id, }, @@ -355,7 +372,7 @@ medusaIntegrationTestRunner({ } const response = await api.post( - `admin/price-lists`, + `admin/price-lists?fields=prices.*,prices.price_rules.*,prices.price_set.*,prices.price_set.variant.*`, data, adminHeaders ) @@ -388,6 +405,12 @@ medusaIntegrationTestRunner({ updated_at: expect.any(String), deleted_at: null, price_set_id: expect.any(String), + rules_count: 1, + raw_amount: expect.objectContaining({ + value: "400", + precision: 20, + }), + title: null, rules: { region_id: region.id, }, diff --git a/packages/admin/dashboard/src/hooks/api/price-lists.tsx b/packages/admin/dashboard/src/hooks/api/price-lists.tsx index 1a4e2ac4fc..ccceaa32ad 100644 --- a/packages/admin/dashboard/src/hooks/api/price-lists.tsx +++ b/packages/admin/dashboard/src/hooks/api/price-lists.tsx @@ -14,7 +14,12 @@ import { customerGroupsQueryKeys } from "./customer-groups" import { productsQueryKeys } from "./products" const PRICE_LISTS_QUERY_KEY = "price-lists" as const +const PRICE_LIST_PRICES_QUERY_KEY = "price-list-prices" as const + export const priceListsQueryKeys = queryKeysFactory(PRICE_LISTS_QUERY_KEY) +export const priceListPricesQueryKeys = queryKeysFactory( + PRICE_LIST_PRICES_QUERY_KEY +) export const usePriceList = ( id: string, @@ -31,7 +36,7 @@ export const usePriceList = ( ) => { const { data, ...rest } = useQuery({ queryFn: () => sdk.admin.priceList.retrieve(id, query), - queryKey: priceListsQueryKeys.detail(id), + queryKey: priceListsQueryKeys.detail(id, query), ...options, }) @@ -124,6 +129,25 @@ export const useDeletePriceList = ( }) } +export const usePriceListPrices = ( + id: string, + query?: HttpTypes.AdminPriceListPriceListParams, + options?: UseQueryOptions< + HttpTypes.AdminPriceListPriceListResponse, + FetchError, + HttpTypes.AdminPriceListPriceListResponse, + QueryKey + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => sdk.admin.priceList.prices(id, query), + queryKey: priceListPricesQueryKeys.detail(id, query), + ...options, + }) + + return { ...data, ...rest } +} + export const useBatchPriceListPrices = ( id: string, query?: HttpTypes.AdminPriceListParams, @@ -141,6 +165,9 @@ export const useBatchPriceListPrices = ( queryKey: priceListsQueryKeys.detail(id), }) queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() }) + queryClient.invalidateQueries({ + queryKey: priceListPricesQueryKeys.detail(id), + }) options?.onSuccess?.(data, variables, context) }, diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/components/price-list-general-section/price-list-general-section.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/components/price-list-general-section/price-list-general-section.tsx index 05df8606a5..d3158e8bf2 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-detail/components/price-list-general-section/price-list-general-section.tsx +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-detail/components/price-list-general-section/price-list-general-section.tsx @@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" import { useDeletePriceListAction } from "../../../common/hooks/use-delete-price-list-action" import { getPriceListStatus } from "../../../common/utils" +import { usePriceListPrices } from "../../../../../hooks/api" type PriceListGeneralSectionProps = { priceList: HttpTypes.AdminPriceList @@ -15,8 +16,13 @@ export const PriceListGeneralSection = ({ priceList, }: PriceListGeneralSectionProps) => { const { t } = useTranslation() - - const overrideCount = priceList.prices?.length || 0 + const { + count: overrideCount, + isLoading, + error, + } = usePriceListPrices(priceList.id, { + limit: 1, + }) const { color, text } = getPriceListStatus(t, priceList) @@ -77,9 +83,11 @@ export const PriceListGeneralSection = ({ {t("priceLists.fields.priceOverrides.label")} - - {overrideCount || "-"} - + {!isLoading && !error && ( + + {overrideCount || "-"} + + )} ) diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/price-count-cell.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/price-count-cell.tsx new file mode 100644 index 0000000000..72a2a386e5 --- /dev/null +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/price-count-cell.tsx @@ -0,0 +1,19 @@ +import { usePriceListPrices } from "../../../../../hooks/api/price-lists" +import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell" +import { TextCell } from "../../../../../components/table/table-cells/common/text-cell" + +type PriceCountCellProps = { + priceListId: string +} + +export const PriceCountCell = ({ priceListId }: PriceCountCellProps) => { + const { count, isLoading } = usePriceListPrices(priceListId, { + limit: 1, + }) + + if (isLoading) { + return + } + + return 0 ? count.toString() : "-"} /> +} diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/use-pricing-table-columns.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/use-pricing-table-columns.tsx index d301d577db..0d2a53e50c 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/use-pricing-table-columns.tsx +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-list/components/price-list-list-table/use-pricing-table-columns.tsx @@ -4,12 +4,10 @@ import { useMemo } from "react" import { useTranslation } from "react-i18next" import { StatusCell } from "../../../../../components/table/table-cells/common/status-cell" -import { - TextCell, - TextHeader, -} from "../../../../../components/table/table-cells/common/text-cell" +import { TextHeader } from "../../../../../components/table/table-cells/common/text-cell" import { getPriceListStatus } from "../../../common/utils" import { PriceListListTableActions } from "./price-list-list-table-actions" +import { PriceCountCell } from "./price-count-cell" const columnHelper = createColumnHelper() @@ -30,9 +28,10 @@ export const usePricingTableColumns = () => { return {text} }, }), - columnHelper.accessor("prices", { + columnHelper.display({ + id: "price_overrides", header: t("priceLists.fields.priceOverrides.header"), - cell: (info) => , + cell: ({ row }) => , }), columnHelper.display({ id: "actions", diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-prices-add/price-list-prices-add.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-prices-add/price-list-prices-add.tsx index f883114b03..9a527e7101 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-prices-add/price-list-prices-add.tsx +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-prices-add/price-list-prices-add.tsx @@ -7,7 +7,10 @@ import { PriceListPricesAddForm } from "./components/price-list-prices-add-form" export const PriceListProductsAdd = () => { const { id } = useParams<{ id: string }>() - const { price_list, isPending, isError, error } = usePriceList(id!) + const { price_list, isPending, isError, error } = usePriceList(id!, { + fields: + "*prices,prices.price_set.variant.id,prices.price_rules.attribute,prices.price_rules.value", + }) const { currencies, regions, pricePreferences, isReady } = usePriceListCurrencyData() diff --git a/packages/admin/dashboard/src/routes/price-lists/price-list-prices-edit/price-list-prices-edit.tsx b/packages/admin/dashboard/src/routes/price-lists/price-list-prices-edit/price-list-prices-edit.tsx index 60c8e87c1c..12746c4f87 100644 --- a/packages/admin/dashboard/src/routes/price-lists/price-list-prices-edit/price-list-prices-edit.tsx +++ b/packages/admin/dashboard/src/routes/price-lists/price-list-prices-edit/price-list-prices-edit.tsx @@ -10,7 +10,9 @@ export const PriceListPricesEdit = () => { const [searchParams] = useSearchParams() const ids = searchParams.get("ids[]") - const { price_list, isLoading, isError, error } = usePriceList(id!) + const { price_list, isLoading, isError, error } = usePriceList(id!, { + fields: "*prices,prices.price_set.variant.id,prices.price_rules.attribute,prices.price_rules.value", + }) const productIds = ids?.split(",") const { diff --git a/packages/core/js-sdk/src/admin/price-list.ts b/packages/core/js-sdk/src/admin/price-list.ts index b83a386b55..b605979457 100644 --- a/packages/core/js-sdk/src/admin/price-list.ts +++ b/packages/core/js-sdk/src/admin/price-list.ts @@ -19,24 +19,24 @@ export class PriceList { * This method retrieves a price list. It sends a request to the * [Get Price List](https://docs.medusajs.com/v2/api/admin#price-lists_getpricelistsid) * API route. - * + * * @param id - The price list's ID. * @param query - Configure the fields to retrieve in the price list. * @param headers - Headers to pass in the request * @returns The price list's details. - * + * * @example * To retrieve a price list by its ID: - * + * * ```ts * sdk.admin.priceList.retrieve("plist_123") * .then(({ price_list }) => { * console.log(price_list) * }) * ``` - * + * * To specify the fields and relations to retrieve: - * + * * ```ts * sdk.admin.priceList.retrieve("plist_123", { * fields: "id,*prices" @@ -45,7 +45,7 @@ export class PriceList { * console.log(price_list) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/v2/api/store#select-fields-and-relations). */ async retrieve( @@ -64,27 +64,27 @@ export class PriceList { } /** - * This method retrieves a paginated list of price lists. It sends a request to the + * This method retrieves a paginated list of price lists. It sends a request to the * [List Price Lists](https://docs.medusajs.com/v2/api/admin#price-lists_getpricelists) API route. - * + * * @param query - Filters and pagination configurations. * @param headers - Headers to pass in the request. * @returns The paginated list of price lists. - * + * * @example * To retrieve the list of price lists: - * + * * ```ts * sdk.admin.priceList.list() * .then(({ price_lists, count, limit, offset }) => { * console.log(price_lists) * }) * ``` - * + * * To configure the pagination, pass the `limit` and `offset` query parameters. - * + * * For example, to retrieve only 10 items and skip 10 items: - * + * * ```ts * sdk.admin.priceList.list({ * limit: 10, @@ -94,10 +94,10 @@ export class PriceList { * console.log(price_lists) * }) * ``` - * + * * Using the `fields` query parameter, you can specify the fields and relations to retrieve * in each price list: - * + * * ```ts * sdk.admin.priceList.list({ * fields: "id,*prices" @@ -106,7 +106,7 @@ export class PriceList { * console.log(price_lists) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/v2/api/store#select-fields-and-relations). */ async list( @@ -127,12 +127,12 @@ export class PriceList { * This method creates a price list. It sends a request to the * [Create Price List](https://docs.medusajs.com/v2/api/admin#price-lists_postpricelists) * API route. - * + * * @param body - The details of the price list to create. * @param query - Configure the fields to retrieve in the price list. * @param headers - Headers to pass in the request * @returns The price list's details. - * + * * @example * sdk.admin.priceList.create({ * title: "My Price List", @@ -170,16 +170,16 @@ export class PriceList { } /** - * This method updates a price list. It sends a request to the + * This method updates a price list. It sends a request to the * [Update Price List](https://docs.medusajs.com/v2/api/admin#price-lists_postpricelistsid) * API route. - * + * * @param id - The price list's ID. * @param body - The data to update in the price list. * @param query - Configure the fields to retrieve in the price list. * @param headers - Headers to pass in the request * @returns The price list's details. - * + * * @example * sdk.admin.priceList.update("plist_123", { * title: "My Price List", @@ -209,11 +209,11 @@ export class PriceList { * This method deletes a price list. It sends a request to the * [Delete Price List](https://docs.medusajs.com/v2/api/admin#price-lists_deletepricelistsid) * API route. - * + * * @param id - The price list's ID. * @param headers - Headers to pass in the request * @returns The deletion's details. - * + * * @example * sdk.admin.priceList.delete("plist_123") * .then(({ deleted }) => { @@ -234,13 +234,13 @@ export class PriceList { * This method manages the prices of a price list to create, update, or delete them. * It sends a request to the [Manage Prices](https://docs.medusajs.com/v2/api/admin#price-lists_postpricelistsidpricesbatch) * API route. - * + * * @param id - The price list's ID. * @param body - The prices to create, update, or delete. * @param query - Configure the fields to retrieve in the price list. * @param headers - Headers to pass in the request * @returns The price list's details. - * + * * @example * sdk.admin.priceList.batchPrices("plist_123", { * create: [{ @@ -279,17 +279,47 @@ export class PriceList { ) } + /** + * This method retrieves the prices of a price list. It sends a request to the + * [Get Prices](https://docs.medusajs.com/v2/api/admin#price-lists_getpricelistsidprices) + * API route. + * + * @param id - The price list's ID. + * @param query - Configure the fields to retrieve in the price list. + * @param headers - Headers to pass in the request + * @returns The price list's prices. + * + * @example + * sdk.admin.priceList.prices("plist_123") + * .then(({ prices }) => { + * console.log(prices) + * }) + */ + async prices( + id: string, + query?: HttpTypes.AdminPriceListPriceListParams, + headers?: ClientHeaders + ) { + return this.client.fetch( + `/admin/price-lists/${id}/prices`, + { + method: "GET", + headers, + query, + } + ) + } /** * This method removes products from a price list. It sends a request to the * [Remove Product](https://docs.medusajs.com/v2/api/admin#price-lists_postpricelistsidproducts) * API route. - * + * * @param id - The price list's ID. * @param body - The details of the products to remove. * @param query - Configure the fields to retrieve in the price list. * @param headers - Headers to pass in the request * @returns The price list's details. - * + * * @example * sdk.admin.priceList.linkProducts("plist_123", { * remove: ["prod_123"] diff --git a/packages/core/types/src/http/price-list/admin/queries.ts b/packages/core/types/src/http/price-list/admin/queries.ts index 937bf50411..09ea08965c 100644 --- a/packages/core/types/src/http/price-list/admin/queries.ts +++ b/packages/core/types/src/http/price-list/admin/queries.ts @@ -32,3 +32,7 @@ export interface AdminPriceListListParams } export interface AdminPriceListParams extends SelectParams {} + +export interface AdminPriceListPriceListParams + extends FindParams, + BaseFilterable {} diff --git a/packages/core/types/src/http/price-list/admin/responses.ts b/packages/core/types/src/http/price-list/admin/responses.ts index e78292a2b5..c3738f4b0b 100644 --- a/packages/core/types/src/http/price-list/admin/responses.ts +++ b/packages/core/types/src/http/price-list/admin/responses.ts @@ -38,3 +38,8 @@ export interface AdminPriceListBatchResponse { deleted: boolean } } + +export interface AdminPriceListPriceListResponse + extends PaginatedResponse<{ + prices: AdminPrice[] + }> {} diff --git a/packages/medusa/src/api/admin/price-lists/[id]/prices/batch/route.ts b/packages/medusa/src/api/admin/price-lists/[id]/prices/batch/route.ts index 4a74ae6d1c..3bc7071a5c 100644 --- a/packages/medusa/src/api/admin/price-lists/[id]/prices/batch/route.ts +++ b/packages/medusa/src/api/admin/price-lists/[id]/prices/batch/route.ts @@ -4,7 +4,7 @@ import { MedusaResponse, } from "@medusajs/framework/http" import { listPrices } from "../../../queries" -import { adminPriceListPriceRemoteQueryFields } from "../../../query-config" +import { adminPriceListPriceQueryFields } from "../../../query-config" import { BatchMethodRequest, HttpTypes } from "@medusajs/framework/types" import { AdminCreatePriceListPriceType, @@ -44,12 +44,12 @@ export const POST = async ( listPrices( result.created.map((c) => c.id), req.scope, - adminPriceListPriceRemoteQueryFields + adminPriceListPriceQueryFields ), listPrices( result.updated.map((c) => c.id), req.scope, - adminPriceListPriceRemoteQueryFields + adminPriceListPriceQueryFields ), ]) diff --git a/packages/medusa/src/api/admin/price-lists/[id]/prices/route.ts b/packages/medusa/src/api/admin/price-lists/[id]/prices/route.ts new file mode 100644 index 0000000000..0f00214058 --- /dev/null +++ b/packages/medusa/src/api/admin/price-lists/[id]/prices/route.ts @@ -0,0 +1,26 @@ +import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework" +import { HttpTypes } from "@medusajs/types" +import { ContainerRegistrationKeys } from "@medusajs/framework/utils" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) + const result = await query.graph({ + entity: "price", + fields: req.queryConfig.fields, + filters: { + ...req.filterableFields, + price_list_id: req.params.id, + }, + pagination: req.queryConfig.pagination, + }) + + res.status(200).json({ + prices: result.data, + count: result.metadata?.count ?? 0, + offset: result.metadata?.skip ?? 0, + limit: result.metadata?.take ?? 0, + }) +} diff --git a/packages/medusa/src/api/admin/price-lists/middlewares.ts b/packages/medusa/src/api/admin/price-lists/middlewares.ts index 26331e7677..30e3a9a7ad 100644 --- a/packages/medusa/src/api/admin/price-lists/middlewares.ts +++ b/packages/medusa/src/api/admin/price-lists/middlewares.ts @@ -10,6 +10,7 @@ import { AdminCreatePriceList, AdminCreatePriceListPrice, AdminGetPriceListParams, + AdminGetPriceListPriceParams, AdminGetPriceListPricesParams, AdminGetPriceListsParams, AdminRemoveProductsPriceList, @@ -44,7 +45,7 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [ middlewares: [ validateAndTransformBody(AdminCreatePriceList), validateAndTransformQuery( - AdminGetPriceListPricesParams, + AdminGetPriceListsParams, QueryConfig.retrivePriceListQueryConfig ), ], @@ -55,7 +56,7 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [ middlewares: [ validateAndTransformBody(AdminUpdatePriceList), validateAndTransformQuery( - AdminGetPriceListPricesParams, + AdminGetPriceListParams, QueryConfig.retrivePriceListQueryConfig ), ], @@ -71,6 +72,16 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["GET"], + matcher: "/admin/price-lists/:id/prices", + middlewares: [ + validateAndTransformQuery( + AdminGetPriceListPricesParams, + QueryConfig.listPriceListPriceQueryConfig + ), + ], + }, { method: ["POST"], matcher: "/admin/price-lists/:id/prices/batch", @@ -82,7 +93,7 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [ createBatchBody(AdminCreatePriceListPrice, AdminUpdatePriceListPrice) ), validateAndTransformQuery( - AdminGetPriceListPricesParams, + AdminGetPriceListPriceParams, QueryConfig.listPriceListPriceQueryConfig ), ], diff --git a/packages/medusa/src/api/admin/price-lists/query-config.ts b/packages/medusa/src/api/admin/price-lists/query-config.ts index 9c34e6fc6c..7b1511617a 100644 --- a/packages/medusa/src/api/admin/price-lists/query-config.ts +++ b/packages/medusa/src/api/admin/price-lists/query-config.ts @@ -2,7 +2,8 @@ export enum PriceListRelations { PRICES = "prices", } -export const adminPriceListPriceRemoteQueryFields = [ +// Note: renamed to avoid referencing remoteQuery which is legacy +export const adminPriceListPriceQueryFields = [ "id", "currency_code", "amount", @@ -29,11 +30,10 @@ export const adminPriceListRemoteQueryFields = [ "deleted_at", "price_list_rules.value", "price_list_rules.attribute", - ...adminPriceListPriceRemoteQueryFields.map((field) => `prices.${field}`), ] export const retrivePriceListPriceQueryConfig = { - defaults: adminPriceListPriceRemoteQueryFields, + defaults: adminPriceListPriceQueryFields, isList: false, } diff --git a/packages/medusa/src/api/admin/price-lists/validators.ts b/packages/medusa/src/api/admin/price-lists/validators.ts index 7da286727f..c5af548322 100644 --- a/packages/medusa/src/api/admin/price-lists/validators.ts +++ b/packages/medusa/src/api/admin/price-lists/validators.ts @@ -7,7 +7,12 @@ import { } from "../../utils/validators" import { applyAndAndOrOperators } from "../../utils/common-validators" -export const AdminGetPriceListPricesParams = createSelectParams() +export const AdminGetPriceListPriceParams = createSelectParams() + +export const AdminGetPriceListPricesParams = createFindParams({ + offset: 0, + limit: 50, +}) export const AdminGetPriceListsParamsFields = z.object({ q: z.string().optional(),