diff --git a/packages/core/e2e/product.e2e-spec.ts b/packages/core/e2e/product.e2e-spec.ts index 05b7d291b4..29a237cbe0 100644 --- a/packages/core/e2e/product.e2e-spec.ts +++ b/packages/core/e2e/product.e2e-spec.ts @@ -22,6 +22,7 @@ import { GetProductList, GetProductSimple, GetProductVariant, + GetProductVariantList, GetProductWithVariants, LanguageCode, ProductVariantFragment, @@ -336,6 +337,78 @@ describe('Product resolver', () => { }); }); + describe('productVariants list query', () => { + it('returns list', async () => { + const { productVariants } = await adminClient.query< + GetProductVariantList.Query, + GetProductVariantList.Variables + >(GET_PRODUCT_VARIANT_LIST, { + options: { + take: 3, + sort: { + name: SortOrder.ASC, + }, + }, + }); + + expect(productVariants.items).toEqual([ + { + id: 'T_34', + name: 'Bonsai Tree', + price: 1999, + sku: 'B01MXFLUSV', + }, + { + id: 'T_24', + name: 'Boxing Gloves', + price: 3304, + sku: 'B000ZYLPPU', + }, + { + id: 'T_19', + name: 'Camera Lens', + price: 10400, + sku: 'B0012UUP02', + }, + ]); + }); + + it('sort by price', async () => { + const { productVariants } = await adminClient.query< + GetProductVariantList.Query, + GetProductVariantList.Variables + >(GET_PRODUCT_VARIANT_LIST, { + options: { + take: 3, + sort: { + price: SortOrder.ASC, + }, + }, + }); + + expect(productVariants.items).toEqual([ + { + id: 'T_23', + name: 'Skipping Rope', + price: 799, + sku: 'B07CNGXVXT', + }, + { + id: 'T_20', + name: 'Tripod', + price: 1498, + sku: 'B00XI87KV8', + }, + { + id: 'T_32', + name: 'Spiky Cactus', + price: 1550, + sku: 'SC011001', + }, + ]); + }); + }); + describe('productVariant query', () => { it('by id', async () => { const { productVariant } = await adminClient.query< @@ -1261,3 +1334,17 @@ export const GET_PRODUCT_VARIANT = gql` } } `; + +export const GET_PRODUCT_VARIANT_LIST = gql` + query GetProductVariantLIST($options: ProductVariantListOptions) { + productVariants(options: $options) { + items { + id + name + sku + price + } + totalItems + } + } +`; diff --git a/packages/core/src/entity/product-variant/product-variant.entity.ts b/packages/core/src/entity/product-variant/product-variant.entity.ts index 2efd800e25..5c27a98602 100644 --- a/packages/core/src/entity/product-variant/product-variant.entity.ts +++ b/packages/core/src/entity/product-variant/product-variant.entity.ts @@ -70,8 +70,7 @@ export class ProductVariant currencyCode: CurrencyCode; @Calculated({ - relations: ['productVariantPrices'], - expression: 'productVariantPrices.price', + expression: 'productvariant_productVariantPrices.price', }) get price(): number { if (this.listPrice == null) { @@ -81,8 +80,7 @@ export class ProductVariant } @Calculated({ - relations: ['productVariantPrices'], - expression: 'productVariantPrices.price', + expression: 'productvariant_productVariantPrices.price', }) get priceWithTax(): number { if (this.listPrice == null) { diff --git a/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts b/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts index 1b8e34c013..74d35d49cb 100644 --- a/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts +++ b/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts @@ -113,11 +113,16 @@ export class ListQueryBuilder implements OnApplicationBootstrap { if (instruction) { const relations = instruction.relations || []; for (const relation of relations) { - const propertyPath = relation.includes('.') ? relation : `${alias}.${relation}`; - const relationAlias = relation.includes('.') - ? relation.split('.').reverse()[0] - : relation; - qb.innerJoinAndSelect(propertyPath, relationAlias); + const relationIsAlreadyJoined = qb.expressionMap.joinAttributes.find( + ja => ja.entityOrProperty === `${alias}.${relation}`, + ); + if (!relationIsAlreadyJoined) { + const propertyPath = relation.includes('.') ? relation : `${alias}.${relation}`; + const relationAlias = relation.includes('.') + ? relation.split('.').reverse()[0] + : relation; + qb.innerJoinAndSelect(propertyPath, relationAlias); + } } if (typeof instruction.query === 'function') { instruction.query(qb);