diff --git a/integration-tests/api/__tests__/admin/price-list.js b/integration-tests/api/__tests__/admin/price-list.js index 7291a4f956..07027628a1 100644 --- a/integration-tests/api/__tests__/admin/price-list.js +++ b/integration-tests/api/__tests__/admin/price-list.js @@ -1,3 +1,4 @@ +const { PriceList, CustomerGroup } = require("@medusajs/medusa") const path = require("path") const setupServer = require("../../../helpers/setup-server") @@ -8,6 +9,9 @@ const { simpleProductFactory, simplePriceListFactory, } = require("../../factories") +const { + simpleCustomerGroupFactory, +} = require("../../factories/simple-customer-group-factory") const adminSeeder = require("../../helpers/admin-seeder") const customerSeeder = require("../../helpers/customer-seeder") const priceListSeeder = require("../../helpers/price-list-seeder") @@ -322,6 +326,48 @@ describe("/admin/price-lists", () => { ) expect(response.data.count).toEqual(1) }) + + it("lists only price lists with customer_group", async () => { + await customerSeeder(dbConnection) + + await simplePriceListFactory(dbConnection, { + id: "test-list-cgroup-1", + customer_groups: ["customer-group-1"], + }) + await simplePriceListFactory(dbConnection, { + id: "test-list-cgroup-2", + customer_groups: ["customer-group-2"], + }) + await simplePriceListFactory(dbConnection, { + id: "test-list-cgroup-3", + customer_groups: ["customer-group-3"], + }) + await simplePriceListFactory(dbConnection, { + id: "test-list-no-cgroup", + }) + + const api = useApi() + + const response = await api + .get( + `/admin/price-lists?customer_groups[]=customer-group-1,customer-group-2`, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.warn(err.response.data) + }) + + expect(response.status).toEqual(200) + expect(response.data.price_lists.length).toEqual(2) + expect(response.data.price_lists).toEqual([ + expect.objectContaining({ id: "test-list-cgroup-1" }), + expect.objectContaining({ id: "test-list-cgroup-2" }), + ]) + }) }) describe("POST /admin/price-lists/:id", () => { diff --git a/integration-tests/api/factories/simple-customer-group-factory.ts b/integration-tests/api/factories/simple-customer-group-factory.ts index 7afa566ff9..4d4f85a168 100644 --- a/integration-tests/api/factories/simple-customer-group-factory.ts +++ b/integration-tests/api/factories/simple-customer-group-factory.ts @@ -22,7 +22,7 @@ export const simpleCustomerGroupFactory = async ( data.id || `simple-customer-group-${Math.random() * 1000}` const c = manager.create(CustomerGroup, { id: customerGroupId, - name: data.name, + name: data.name || faker.company.companyName(), }) const group = await manager.save(c) diff --git a/integration-tests/api/factories/simple-price-list-factory.ts b/integration-tests/api/factories/simple-price-list-factory.ts index a20380d3b0..f8d5c9d9f0 100644 --- a/integration-tests/api/factories/simple-price-list-factory.ts +++ b/integration-tests/api/factories/simple-price-list-factory.ts @@ -7,6 +7,7 @@ import { } from "@medusajs/medusa" import faker from "faker" import { Connection } from "typeorm" +import { simpleCustomerGroupFactory } from "./simple-customer-group-factory" type ProductListPrice = { variant_id: string @@ -42,22 +43,10 @@ export const simplePriceListFactory = async ( let customerGroups = [] if (typeof data.customer_groups !== "undefined") { - await manager - .createQueryBuilder() - .insert() - .into(CustomerGroup) - .values( - data.customer_groups.map((group) => ({ - id: group, - name: faker.company.companyName(), - })) + customerGroups = await Promise.all( + data.customer_groups.map((group) => + simpleCustomerGroupFactory(connection, { id: group }) ) - .orIgnore() - .execute() - - customerGroups = await manager.findByIds( - CustomerGroup, - data.customer_groups ) } diff --git a/packages/medusa/src/api/routes/admin/price-lists/index.ts b/packages/medusa/src/api/routes/admin/price-lists/index.ts index 8c8ec7cc3e..4a5fbbddb0 100644 --- a/packages/medusa/src/api/routes/admin/price-lists/index.ts +++ b/packages/medusa/src/api/routes/admin/price-lists/index.ts @@ -11,7 +11,11 @@ export default (app) => { route.get("/:id", middlewares.wrap(require("./get-price-list").default)) - route.get("/", middlewares.wrap(require("./list-price-lists").default)) + route.get( + "/", + middlewares.normalizeQuery(), + middlewares.wrap(require("./list-price-lists").default) + ) route.get( "/:id/products", diff --git a/packages/medusa/src/repositories/price-list.ts b/packages/medusa/src/repositories/price-list.ts index 0eac64a2d2..37b3b9e468 100644 --- a/packages/medusa/src/repositories/price-list.ts +++ b/packages/medusa/src/repositories/price-list.ts @@ -2,22 +2,26 @@ import { groupBy, map } from "lodash" import { Brackets, EntityRepository, - FindManyOptions, Repository + FindManyOptions, + FindOperator, + Repository, } from "typeorm" import { PriceList } from "../models/price-list" -import { CustomFindOptions } from "../types/common" +import { CustomFindOptions, ExtendedFindConfig } from "../types/common" -type PriceListFindOptions = CustomFindOptions +type PriceListFindOptions = CustomFindOptions @EntityRepository(PriceList) export class PriceListRepository extends Repository { public async getFreeTextSearchResultsAndCount( q: string, options: PriceListFindOptions = { where: {} }, + groups?: FindOperator, relations: (keyof PriceList)[] = [] ): Promise<[PriceList[], number]> { options.where = options.where ?? {} - let qb = this.createQueryBuilder("price_list") + + const qb = this.createQueryBuilder("price_list") .leftJoinAndSelect("price_list.customer_groups", "customer_group") .select(["price_list.id"]) .where(options.where) @@ -31,6 +35,10 @@ export class PriceListRepository extends Repository { .skip(options.skip) .take(options.take) + if (groups) { + qb.andWhere("group.id IN (:...ids)", { ids: groups.value }) + } + const [results, count] = await qb.getManyAndCount() const price_lists = await this.findWithRelations( @@ -53,7 +61,6 @@ export class PriceListRepository extends Repository { } else { entities = await this.find(idsOrOptionsWithoutRelations) } - const groupedRelations: Record = {} for (const relation of relations) { const [topLevel] = relation.split(".") @@ -63,7 +70,6 @@ export class PriceListRepository extends Repository { groupedRelations[topLevel] = [relation] } } - const entitiesIds = entities.map(({ id }) => id) const entitiesIdsWithRelations = await Promise.all( Object.values(groupedRelations).map((relations: string[]) => { @@ -72,7 +78,7 @@ export class PriceListRepository extends Repository { relations: relations as string[], }) }) - ).then(entitiesIdsWithRelations => entitiesIdsWithRelations.flat()) + ).then((entitiesIdsWithRelations) => entitiesIdsWithRelations.flat()) const entitiesAndRelations = entitiesIdsWithRelations.concat(entities) const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id") @@ -87,9 +93,31 @@ export class PriceListRepository extends Repository { ): Promise { options.take = 1 - return (await this.findWithRelations( - relations, - options - ))?.pop() + return (await this.findWithRelations(relations, options))?.pop() + } + + async listAndCount( + query: ExtendedFindConfig, + groups?: FindOperator + ): Promise<[PriceList[], number]> { + const qb = this.createQueryBuilder("price_list") + .where(query.where) + .skip(query.skip) + .take(query.take) + + if (groups) { + qb.leftJoinAndSelect("price_list.customer_groups", "group").andWhere( + "group.id IN (:...ids)", + { ids: groups.value } + ) + } + + if (query.relations?.length) { + query.relations.forEach((rel) => { + qb.leftJoinAndSelect(`price_list.${rel}`, rel) + }) + } + + return await qb.getManyAndCount() } } diff --git a/packages/medusa/src/services/price-list.ts b/packages/medusa/src/services/price-list.ts index bb068a3ad4..c0eb750f7d 100644 --- a/packages/medusa/src/services/price-list.ts +++ b/packages/medusa/src/services/price-list.ts @@ -252,10 +252,18 @@ class PriceListService extends BaseService { selector: FilterablePriceListProps = {}, config: FindConfig = { skip: 0, take: 20 } ): Promise { - const priceListRepo = this.manager_.getCustomRepository(this.priceListRepo_) + return await this.atomicPhase_(async (manager: EntityManager) => { + const priceListRepo = manager.getCustomRepository(this.priceListRepo_) - const query = this.buildQuery_(selector, config) - return await priceListRepo.find(query) + const query = this.buildQuery_(selector, config) + + const groups = query.where.customer_groups + query.where.customer_groups = undefined + + const [priceLists] = await priceListRepo.listAndCount(query, groups) + + return priceLists + }) } /** @@ -268,19 +276,25 @@ class PriceListService extends BaseService { selector: FilterablePriceListProps = {}, config: FindConfig = { skip: 0, take: 20 } ): Promise<[PriceList[], number]> { - const priceListRepo = this.manager_.getCustomRepository(this.priceListRepo_) - const q = selector.q - const { relations, ...query } = this.buildQuery_(selector, config) + return await this.atomicPhase_(async (manager: EntityManager) => { + const priceListRepo = manager.getCustomRepository(this.priceListRepo_) + const q = selector.q + const { relations, ...query } = this.buildQuery_(selector, config) - if (q) { - delete query.where.q - return await priceListRepo.getFreeTextSearchResultsAndCount( - q, - query, - relations - ) - } - return await priceListRepo.findAndCount({ ...query, relations }) + const groups = query.where.customer_groups + delete query.where.customer_groups + + if (q) { + delete query.where.q + return await priceListRepo.getFreeTextSearchResultsAndCount( + q, + query, + groups, + relations + ) + } + return await priceListRepo.listAndCount({ ...query, relations }, groups) + }) } async upsertCustomerGroups_( diff --git a/packages/medusa/src/types/price-list.ts b/packages/medusa/src/types/price-list.ts index 42aae61aff..bee6195c88 100644 --- a/packages/medusa/src/types/price-list.ts +++ b/packages/medusa/src/types/price-list.ts @@ -9,7 +9,7 @@ import { ValidateNested, } from "class-validator" import { PriceList } from "../models/price-list" -import { DateComparisonOperator } from "./common" +import { DateComparisonOperator, FindConfig } from "./common" import { XorConstraint } from "./validators/xor" export enum PriceListType { @@ -39,6 +39,10 @@ export class FilterablePriceListProps { @IsOptional() name?: string + @IsOptional() + @IsString({ each: true }) + customer_groups?: string[] + @IsString() @IsOptional() description?: string