From ff1ae905fba235bb3703ea57b167a97241e4e1da Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Thu, 23 Sep 2021 17:03:49 +0200 Subject: [PATCH] fix(core): Soft-delete variants when a product is soft-deleted Fixes #1096 --- packages/core/e2e/product.e2e-spec.ts | 31 ++++++++++++++++--- .../services/product-variant.service.ts | 18 +++++++---- .../src/service/services/product.service.ts | 5 +++ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/core/e2e/product.e2e-spec.ts b/packages/core/e2e/product.e2e-spec.ts index 3eb6fe7220..34b3665ce7 100644 --- a/packages/core/e2e/product.e2e-spec.ts +++ b/packages/core/e2e/product.e2e-spec.ts @@ -1617,7 +1617,7 @@ describe('Product resolver', () => { describe('deletion', () => { let allProducts: GetProductList.Items[]; - let productToDelete: GetProductList.Items; + let productToDelete: GetProductWithVariants.Product; beforeAll(async () => { const result = await adminClient.query( @@ -1634,24 +1634,45 @@ describe('Product resolver', () => { }); it('deletes a product', async () => { - productToDelete = allProducts[0]; + const { product } = await adminClient.query< + GetProductWithVariants.Query, + GetProductWithVariants.Variables + >(GET_PRODUCT_WITH_VARIANTS, { + id: allProducts[0].id, + }); const result = await adminClient.query( DELETE_PRODUCT, - { id: productToDelete.id }, + { id: product!.id }, ); expect(result.deleteProduct).toEqual({ result: DeletionResult.DELETED }); + + productToDelete = product!; }); it('cannot get a deleted product', async () => { - const result = await adminClient.query< + const { product } = await adminClient.query< GetProductWithVariants.Query, GetProductWithVariants.Variables >(GET_PRODUCT_WITH_VARIANTS, { id: productToDelete.id, }); - expect(result.product).toBe(null); + expect(product).toBe(null); + }); + + // https://github.com/vendure-ecommerce/vendure/issues/1096 + it('variants of deleted product are also deleted', async () => { + for (const variant of productToDelete.variants) { + const { productVariant } = await adminClient.query< + GetProductVariant.Query, + GetProductVariant.Variables + >(GET_PRODUCT_VARIANT, { + id: variant.id, + }); + + expect(productVariant).toBe(null); + } }); it('deleted product omitted from list', async () => { diff --git a/packages/core/src/service/services/product-variant.service.ts b/packages/core/src/service/services/product-variant.service.ts index 8c27eab09e..b50aceca88 100644 --- a/packages/core/src/service/services/product-variant.service.ts +++ b/packages/core/src/service/services/product-variant.service.ts @@ -102,7 +102,10 @@ export class ProductVariantService { findOne(ctx: RequestContext, productVariantId: ID): Promise | undefined> { const relations = ['product', 'product.featuredAsset', 'taxCategory']; return this.connection - .findOneInChannel(ctx, ProductVariant, productVariantId, ctx.channelId, { relations }) + .findOneInChannel(ctx, ProductVariant, productVariantId, ctx.channelId, { + relations, + where: { deletedAt: null }, + }) .then(async result => { if (result) { return translateDeep(await this.applyChannelPriceAndTax(result, ctx), ctx.languageCode, [ @@ -498,11 +501,14 @@ export class ProductVariantService { return this.connection.getRepository(ctx, ProductVariantPrice).save(variantPrice); } - async softDelete(ctx: RequestContext, id: ID): Promise { - const variant = await this.connection.getEntityOrThrow(ctx, ProductVariant, id); - variant.deletedAt = new Date(); - await this.connection.getRepository(ctx, ProductVariant).save(variant, { reload: false }); - this.eventBus.publish(new ProductVariantEvent(ctx, [variant], 'deleted')); + async softDelete(ctx: RequestContext, id: ID | ID[]): Promise { + const ids = Array.isArray(id) ? id : [id]; + const variants = await this.connection.getRepository(ctx, ProductVariant).findByIds(ids); + for (const variant of variants) { + variant.deletedAt = new Date(); + } + await this.connection.getRepository(ctx, ProductVariant).save(variants, { reload: false }); + this.eventBus.publish(new ProductVariantEvent(ctx, variants, 'deleted')); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/product.service.ts b/packages/core/src/service/services/product.service.ts index 79bebf36dc..907fba4d13 100644 --- a/packages/core/src/service/services/product.service.ts +++ b/packages/core/src/service/services/product.service.ts @@ -220,10 +220,15 @@ export class ProductService { async softDelete(ctx: RequestContext, productId: ID): Promise { const product = await this.connection.getEntityOrThrow(ctx, Product, productId, { channelId: ctx.channelId, + relations: ['variants'], }); product.deletedAt = new Date(); await this.connection.getRepository(ctx, Product).save(product, { reload: false }); this.eventBus.publish(new ProductEvent(ctx, product, 'deleted')); + await this.productVariantService.softDelete( + ctx, + product.variants.map(v => v.id), + ); return { result: DeletionResult.DELETED, };