瀏覽代碼

feat(core): Implement deletion of TaxCategory

Relates to #262
Michael Bromley 6 年之前
父節點
當前提交
b263b8b61f

+ 7 - 0
packages/common/src/generated-types.ts

@@ -1801,6 +1801,8 @@ export type Mutation = {
   createTaxCategory: TaxCategory,
   /** Update an existing TaxCategory */
   updateTaxCategory: TaxCategory,
+  /** Deletes a TaxCategory */
+  deleteTaxCategory: DeletionResponse,
   /** Create a new TaxRate */
   createTaxRate: TaxRate,
   /** Update an existing TaxRate */
@@ -2154,6 +2156,11 @@ export type MutationUpdateTaxCategoryArgs = {
 };
 
 
+export type MutationDeleteTaxCategoryArgs = {
+  id: Scalars['ID']
+};
+
+
 export type MutationCreateTaxRateArgs = {
   input: CreateTaxRateInput
 };

+ 74 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -1804,6 +1804,8 @@ export type Mutation = {
     createTaxCategory: TaxCategory;
     /** Update an existing TaxCategory */
     updateTaxCategory: TaxCategory;
+    /** Deletes a TaxCategory */
+    deleteTaxCategory: DeletionResponse;
     /** Create a new TaxRate */
     createTaxRate: TaxRate;
     /** Update an existing TaxRate */
@@ -2091,6 +2093,10 @@ export type MutationUpdateTaxCategoryArgs = {
     input: UpdateTaxCategoryInput;
 };
 
+export type MutationDeleteTaxCategoryArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationCreateTaxRateArgs = {
     input: CreateTaxRateInput;
 };
@@ -5091,6 +5097,44 @@ export type UpdateStockMutation = { __typename?: 'Mutation' } & {
     updateProductVariants: Array<Maybe<{ __typename?: 'ProductVariant' } & VariantWithStockFragment>>;
 };
 
+export type GetTaxCategoryListQueryVariables = {};
+
+export type GetTaxCategoryListQuery = { __typename?: 'Query' } & {
+    taxCategories: Array<{ __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>>;
+};
+
+export type GetTaxCategoryQueryVariables = {
+    id: Scalars['ID'];
+};
+
+export type GetTaxCategoryQuery = { __typename?: 'Query' } & {
+    taxCategory: Maybe<{ __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>>;
+};
+
+export type CreateTaxCategoryMutationVariables = {
+    input: CreateTaxCategoryInput;
+};
+
+export type CreateTaxCategoryMutation = { __typename?: 'Mutation' } & {
+    createTaxCategory: { __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>;
+};
+
+export type UpdateTaxCategoryMutationVariables = {
+    input: UpdateTaxCategoryInput;
+};
+
+export type UpdateTaxCategoryMutation = { __typename?: 'Mutation' } & {
+    updateTaxCategory: { __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>;
+};
+
+export type DeleteTaxCategoryMutationVariables = {
+    id: Scalars['ID'];
+};
+
+export type DeleteTaxCategoryMutation = { __typename?: 'Mutation' } & {
+    deleteTaxCategory: { __typename?: 'DeletionResponse' } & Pick<DeletionResponse, 'result' | 'message'>;
+};
+
 export type GetTaxRatesQueryVariables = {};
 
 export type GetTaxRatesQuery = { __typename?: 'Query' } & {
@@ -6316,6 +6360,36 @@ export namespace UpdateStock {
     export type UpdateProductVariants = VariantWithStockFragment;
 }
 
+export namespace GetTaxCategoryList {
+    export type Variables = GetTaxCategoryListQueryVariables;
+    export type Query = GetTaxCategoryListQuery;
+    export type TaxCategories = NonNullable<GetTaxCategoryListQuery['taxCategories'][0]>;
+}
+
+export namespace GetTaxCategory {
+    export type Variables = GetTaxCategoryQueryVariables;
+    export type Query = GetTaxCategoryQuery;
+    export type TaxCategory = NonNullable<GetTaxCategoryQuery['taxCategory']>;
+}
+
+export namespace CreateTaxCategory {
+    export type Variables = CreateTaxCategoryMutationVariables;
+    export type Mutation = CreateTaxCategoryMutation;
+    export type CreateTaxCategory = CreateTaxCategoryMutation['createTaxCategory'];
+}
+
+export namespace UpdateTaxCategory {
+    export type Variables = UpdateTaxCategoryMutationVariables;
+    export type Mutation = UpdateTaxCategoryMutation;
+    export type UpdateTaxCategory = UpdateTaxCategoryMutation['updateTaxCategory'];
+}
+
+export namespace DeleteTaxCategory {
+    export type Variables = DeleteTaxCategoryMutationVariables;
+    export type Mutation = DeleteTaxCategoryMutation;
+    export type DeleteTaxCategory = DeleteTaxCategoryMutation['deleteTaxCategory'];
+}
+
 export namespace GetTaxRates {
     export type Variables = GetTaxRatesQueryVariables;
     export type Query = GetTaxRatesQuery;

+ 172 - 0
packages/core/e2e/tax-category.e2e-spec.ts

@@ -0,0 +1,172 @@
+import { createTestEnvironment } from '@vendure/testing';
+import gql from 'graphql-tag';
+import path from 'path';
+
+import { initialData } from '../../../e2e-common/e2e-initial-data';
+import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
+
+import {
+    CreateTaxCategory,
+    DeleteTaxCategory,
+    DeletionResult,
+    GetTaxCategory,
+    GetTaxCategoryList,
+    UpdateTaxCategory,
+} from './graphql/generated-e2e-admin-types';
+import { sortById } from './utils/test-order-utils';
+
+describe('TaxCategory resolver', () => {
+    const { server, adminClient, shopClient } = createTestEnvironment(testConfig);
+
+    beforeAll(async () => {
+        await server.init({
+            initialData,
+            productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
+            customerCount: 2,
+        });
+        await adminClient.asSuperAdmin();
+    }, TEST_SETUP_TIMEOUT_MS);
+
+    afterAll(async () => {
+        await server.destroy();
+    });
+
+    it('taxCategories', async () => {
+        const { taxCategories } = await adminClient.query<GetTaxCategoryList.Query>(GET_TAX_CATEGORY_LIST);
+
+        expect(taxCategories.sort(sortById)).toEqual([
+            { id: 'T_1', name: 'Standard Tax' },
+            { id: 'T_2', name: 'Reduced Tax' },
+            { id: 'T_3', name: 'Zero Tax' },
+        ]);
+    });
+
+    it('taxCategory', async () => {
+        const { taxCategory } = await adminClient.query<GetTaxCategory.Query, GetTaxCategory.Variables>(
+            GET_TAX_CATEGORY,
+            {
+                id: 'T_2',
+            },
+        );
+
+        expect(taxCategory).toEqual({
+            id: 'T_2',
+            name: 'Reduced Tax',
+        });
+    });
+
+    it('createTaxCategory', async () => {
+        const { createTaxCategory } = await adminClient.query<
+            CreateTaxCategory.Mutation,
+            CreateTaxCategory.Variables
+        >(CREATE_TAX_CATEGORY, {
+            input: {
+                name: 'New Category',
+            },
+        });
+
+        expect(createTaxCategory).toEqual({
+            id: 'T_4',
+            name: 'New Category',
+        });
+    });
+
+    it('updateCategory', async () => {
+        const { updateTaxCategory } = await adminClient.query<
+            UpdateTaxCategory.Mutation,
+            UpdateTaxCategory.Variables
+        >(UPDATE_TAX_CATEGORY, {
+            input: {
+                id: 'T_4',
+                name: 'New Category Updated',
+            },
+        });
+
+        expect(updateTaxCategory).toEqual({
+            id: 'T_4',
+            name: 'New Category Updated',
+        });
+    });
+
+    describe('deletion', () => {
+        it('cannot delete if used by a TaxRate', async () => {
+            const { deleteTaxCategory } = await adminClient.query<
+                DeleteTaxCategory.Mutation,
+                DeleteTaxCategory.Variables
+            >(DELETE_TAX_CATEGORY, {
+                id: 'T_2',
+            });
+
+            expect(deleteTaxCategory.result).toBe(DeletionResult.NOT_DELETED);
+            expect(deleteTaxCategory.message).toBe(
+                `Cannot remove TaxCategory "Reduced Tax" as it is referenced by 5 TaxRates`,
+            );
+        });
+
+        it('can delete if not used by TaxRate', async () => {
+            const { deleteTaxCategory } = await adminClient.query<
+                DeleteTaxCategory.Mutation,
+                DeleteTaxCategory.Variables
+            >(DELETE_TAX_CATEGORY, {
+                id: 'T_4',
+            });
+
+            expect(deleteTaxCategory.result).toBe(DeletionResult.DELETED);
+            expect(deleteTaxCategory.message).toBeNull();
+
+            const { taxCategory } = await adminClient.query<GetTaxCategory.Query, GetTaxCategory.Variables>(
+                GET_TAX_CATEGORY,
+                {
+                    id: 'T_4',
+                },
+            );
+
+            expect(taxCategory).toBeNull();
+        });
+    });
+});
+
+const GET_TAX_CATEGORY_LIST = gql`
+    query GetTaxCategoryList {
+        taxCategories {
+            id
+            name
+        }
+    }
+`;
+
+const GET_TAX_CATEGORY = gql`
+    query GetTaxCategory($id: ID!) {
+        taxCategory(id: $id) {
+            id
+            name
+        }
+    }
+`;
+
+const CREATE_TAX_CATEGORY = gql`
+    mutation CreateTaxCategory($input: CreateTaxCategoryInput!) {
+        createTaxCategory(input: $input) {
+            id
+            name
+        }
+    }
+`;
+
+const UPDATE_TAX_CATEGORY = gql`
+    mutation UpdateTaxCategory($input: UpdateTaxCategoryInput!) {
+        updateTaxCategory(input: $input) {
+            id
+            name
+        }
+    }
+`;
+
+const DELETE_TAX_CATEGORY = gql`
+    mutation DeleteTaxCategory($id: ID!) {
+        deleteTaxCategory(id: $id) {
+            result
+            message
+        }
+    }
+`;

+ 11 - 0
packages/core/src/api/resolvers/admin/tax-category.resolver.ts

@@ -1,6 +1,8 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
+    DeletionResponse,
     MutationCreateTaxCategoryArgs,
+    MutationDeleteTaxCategoryArgs,
     MutationUpdateTaxCategoryArgs,
     Permission,
     QueryTaxCategoryArgs,
@@ -42,4 +44,13 @@ export class TaxCategoryResolver {
     async updateTaxCategory(@Args() args: MutationUpdateTaxCategoryArgs): Promise<TaxCategory> {
         return this.taxCategoryService.update(args.input);
     }
+
+    @Mutation()
+    @Allow(Permission.DeleteSettings)
+    async deleteTaxCategory(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationDeleteTaxCategoryArgs,
+    ): Promise<DeletionResponse> {
+        return this.taxCategoryService.delete(ctx, args.id);
+    }
 }

+ 3 - 0
packages/core/src/api/schema/admin-api/tax-category.api.graphql

@@ -9,6 +9,9 @@ type Mutation {
 
     "Update an existing TaxCategory"
     updateTaxCategory(input: UpdateTaxCategoryInput!): TaxCategory!
+
+    "Deletes a TaxCategory"
+    deleteTaxCategory(id: ID!): DeletionResponse!
 }
 
 input CreateTaxCategoryInput {

+ 1 - 0
packages/core/src/i18n/messages/en.json

@@ -8,6 +8,7 @@
     "cannot-create-sales-for-active-order": "Cannot create a Sale for an Order which is still active",
     "cannot-move-collection-into-self": "Cannot move a Collection into itself",
     "cannot-remove-option-group-due-to-variants": "Cannot remove ProductOptionGroup \"{ code }\" as it is used by {count, plural, one {1 ProductVariant} other {# ProductVariants}}",
+    "cannot-remove-tax-category-due-to-tax-rates": "Cannot remove TaxCategory \"{ name }\" as it is referenced by {count, plural, one {1 TaxRate} other {# TaxRates}}",
     "cannot-transition-order-from-to": "Cannot transition Order from \"{ fromState }\" to \"{ toState }\"",
     "cannot-transition-payment-from-to": "Cannot transition Payment from \"{ fromState }\" to \"{ toState }\"",
     "cannot-transition-refund-from-to": "Cannot transition Refund from \"{ fromState }\" to \"{ toState }\"",

+ 39 - 1
packages/core/src/service/services/tax-category.service.ts

@@ -1,12 +1,20 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
-import { CreateTaxCategoryInput, UpdateTaxCategoryInput } from '@vendure/common/lib/generated-types';
+import {
+    CreateTaxCategoryInput,
+    DeletionResponse,
+    DeletionResult,
+    UpdateTaxCategoryInput,
+} from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 import { Connection } from 'typeorm';
 
+import { RequestContext } from '../../api/common/request-context';
 import { EntityNotFoundError } from '../../common/error/errors';
 import { assertFound } from '../../common/utils';
 import { TaxCategory } from '../../entity/tax-category/tax-category.entity';
+import { TaxRate } from '../../entity/tax-rate/tax-rate.entity';
+import { getEntityOrThrow } from '../helpers/utils/get-entity-or-throw';
 import { patchEntity } from '../helpers/utils/patch-entity';
 
 @Injectable()
@@ -36,4 +44,34 @@ export class TaxCategoryService {
         await this.connection.getRepository(TaxCategory).save(updatedTaxCategory, { reload: false });
         return assertFound(this.findOne(taxCategory.id));
     }
+
+    async delete(ctx: RequestContext, id: ID): Promise<DeletionResponse> {
+        const taxCategory = await getEntityOrThrow(this.connection, TaxCategory, id);
+        const dependentRates = await this.connection
+            .getRepository(TaxRate)
+            .count({ where: { category: id } });
+
+        if (0 < dependentRates) {
+            const message = ctx.translate('error.cannot-remove-tax-category-due-to-tax-rates', {
+                name: taxCategory.name,
+                count: dependentRates,
+            });
+            return {
+                result: DeletionResult.NOT_DELETED,
+                message,
+            };
+        }
+
+        try {
+            await this.connection.getRepository(TaxCategory).remove(taxCategory);
+            return {
+                result: DeletionResult.DELETED,
+            };
+        } catch (e) {
+            return {
+                result: DeletionResult.NOT_DELETED,
+                message: e.toString(),
+            };
+        }
+    }
 }

+ 6 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -1804,6 +1804,8 @@ export type Mutation = {
     createTaxCategory: TaxCategory;
     /** Update an existing TaxCategory */
     updateTaxCategory: TaxCategory;
+    /** Deletes a TaxCategory */
+    deleteTaxCategory: DeletionResponse;
     /** Create a new TaxRate */
     createTaxRate: TaxRate;
     /** Update an existing TaxRate */
@@ -2091,6 +2093,10 @@ export type MutationUpdateTaxCategoryArgs = {
     input: UpdateTaxCategoryInput;
 };
 
+export type MutationDeleteTaxCategoryArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationCreateTaxRateArgs = {
     input: CreateTaxRateInput;
 };

文件差異過大導致無法顯示
+ 0 - 0
schema-admin.json


部分文件因文件數量過多而無法顯示