Explorar el Código

feat(server): Implement CustomerGroup entity

Michael Bromley hace 7 años
padre
commit
ff495fca58

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
schema.json


+ 2 - 2
server/mock-data/mock-data.service.ts

@@ -203,9 +203,9 @@ export class MockDataService {
                 password: 'test',
             };
 
-            const customer: Customer | void = await this.client
+            const customer: { id: string; emailAddress: string } | void = await this.client
                 .query(query1, variables1)
-                .then((data: any) => data.createCustomer as Customer, err => this.log(err));
+                .then((data: any) => data.createCustomer, err => this.log(err));
 
             if (customer) {
                 const query2 = gql`

+ 2 - 0
server/src/api/api.module.ts

@@ -19,6 +19,7 @@ import { AuthResolver } from './resolvers/auth.resolver';
 import { ChannelResolver } from './resolvers/channel.resolver';
 import { ConfigResolver } from './resolvers/config.resolver';
 import { CountryResolver } from './resolvers/country.resolver';
+import { CustomerGroupResolver } from './resolvers/customer-group.resolver';
 import { CustomerResolver } from './resolvers/customer.resolver';
 import { FacetResolver } from './resolvers/facet.resolver';
 import { OrderResolver } from './resolvers/order.resolver';
@@ -37,6 +38,7 @@ const exportedProviders = [
     CountryResolver,
     FacetResolver,
     CustomerResolver,
+    CustomerGroupResolver,
     OrderResolver,
     ProductOptionResolver,
     ProductResolver,

+ 65 - 0
server/src/api/resolvers/customer-group.resolver.ts

@@ -0,0 +1,65 @@
+import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
+import {
+    AddCustomersToGroupMutationArgs,
+    CreateCustomerGroupMutationArgs,
+    CustomerGroupQueryArgs,
+    Permission,
+    RemoveCustomersFromGroupMutationArgs,
+    UpdateCustomerGroupMutationArgs,
+} from 'shared/generated-types';
+
+import { CustomerGroup } from '../../entity/customer-group/customer-group.entity';
+import { CustomerGroupService } from '../../service/providers/customer-group.service';
+import { Allow } from '../common/auth-guard';
+import { Decode } from '../common/id-interceptor';
+import { RequestContext } from '../common/request-context';
+import { Ctx } from '../common/request-context.decorator';
+
+@Resolver('CustomerGroup')
+export class CustomerGroupResolver {
+    constructor(private customerGroupService: CustomerGroupService) {}
+
+    @Query()
+    @Allow(Permission.ReadCustomer)
+    customerGroups(@Ctx() ctx: RequestContext): Promise<CustomerGroup[]> {
+        return this.customerGroupService.findAll();
+    }
+
+    @Query()
+    @Allow(Permission.ReadCustomer)
+    async customerGroup(
+        @Ctx() ctx: RequestContext,
+        @Args() args: CustomerGroupQueryArgs,
+    ): Promise<CustomerGroup | undefined> {
+        return this.customerGroupService.findOne(args.id);
+    }
+
+    @Mutation()
+    @Allow(Permission.CreateCustomer)
+    @Decode('customerIds')
+    async createCustomerGroup(@Args() args: CreateCustomerGroupMutationArgs): Promise<CustomerGroup> {
+        return this.customerGroupService.create(args.input);
+    }
+
+    @Mutation()
+    @Allow(Permission.UpdateCustomer)
+    async updateCustomerGroup(@Args() args: UpdateCustomerGroupMutationArgs): Promise<CustomerGroup> {
+        return this.customerGroupService.update(args.input);
+    }
+
+    @Mutation()
+    @Allow(Permission.UpdateCustomer)
+    @Decode('customerGroupId', 'customerIds')
+    async addCustomersToGroup(@Args() args: AddCustomersToGroupMutationArgs): Promise<CustomerGroup> {
+        return this.customerGroupService.addCustomersToGroup(args);
+    }
+
+    @Mutation()
+    @Allow(Permission.UpdateCustomer)
+    @Decode('customerGroupId', 'customerIds')
+    async removeCustomersFromGroup(
+        @Args() args: RemoveCustomersFromGroupMutationArgs,
+    ): Promise<CustomerGroup> {
+        return this.customerGroupService.removeCustomersFromGroup(args);
+    }
+}

+ 15 - 0
server/src/api/types/customer-group.api.graphql

@@ -0,0 +1,15 @@
+type Query {
+    customerGroups: [CustomerGroup!]!
+    customerGroup(id: ID!): CustomerGroup
+}
+
+type Mutation {
+    "Create a new CustomerGroup"
+    createCustomerGroup(input: CreateCustomerGroupInput!): CustomerGroup!
+    "Update an existing CustomerGroup"
+    updateCustomerGroup(input: UpdateCustomerGroupInput!): CustomerGroup!
+    "Add Customers to a CustomerGroup"
+    addCustomersToGroup(customerGroupId: ID!, customerIds: [ID!]!): CustomerGroup!
+    "Remove Customers from a CustomerGroup"
+    removeCustomersFromGroup(customerGroupId: ID!, customerIds: [ID!]!): CustomerGroup!
+}

+ 17 - 0
server/src/entity/customer-group/customer-group.entity.ts

@@ -0,0 +1,17 @@
+import { DeepPartial } from 'shared/shared-types';
+import { Column, Entity, ManyToMany } from 'typeorm';
+
+import { VendureEntity } from '../base/base.entity';
+import { Customer } from '../customer/customer.entity';
+
+@Entity()
+export class CustomerGroup extends VendureEntity {
+    constructor(input?: DeepPartial<CustomerGroup>) {
+        super(input);
+    }
+
+    @Column() name: string;
+
+    @ManyToMany(type => Customer)
+    customers: Customer[];
+}

+ 16 - 0
server/src/entity/customer-group/customer-group.graphql

@@ -0,0 +1,16 @@
+type CustomerGroup implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    name: String!
+}
+
+input CreateCustomerGroupInput {
+    name: String!
+    customerIds: [ID!]
+}
+
+input UpdateCustomerGroupInput {
+    id: ID!
+    name: String
+}

+ 6 - 1
server/src/entity/customer/customer.entity.ts

@@ -1,10 +1,11 @@
 import { DeepPartial } from 'shared/shared-types';
 import { HasCustomFields } from 'shared/shared-types';
-import { Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm';
+import { Column, Entity, JoinColumn, JoinTable, ManyToMany, OneToMany, OneToOne } from 'typeorm';
 
 import { Address } from '../address/address.entity';
 import { VendureEntity } from '../base/base.entity';
 import { CustomCustomerFields } from '../custom-entity-fields';
+import { CustomerGroup } from '../customer-group/customer-group.entity';
 import { Order } from '../order/order.entity';
 import { User } from '../user/user.entity';
 
@@ -24,6 +25,10 @@ export class Customer extends VendureEntity implements HasCustomFields {
     @Column({ unique: true })
     emailAddress: string;
 
+    @ManyToMany(type => CustomerGroup)
+    @JoinTable()
+    groups: CustomerGroup[];
+
     @OneToMany(type => Address, address => address.customer)
     addresses: Address[];
 

+ 2 - 0
server/src/entity/entities.ts

@@ -4,6 +4,7 @@ import { Administrator } from './administrator/administrator.entity';
 import { Asset } from './asset/asset.entity';
 import { Channel } from './channel/channel.entity';
 import { Country } from './country/country.entity';
+import { CustomerGroup } from './customer-group/customer-group.entity';
 import { Customer } from './customer/customer.entity';
 import { FacetValueTranslation } from './facet-value/facet-value-translation.entity';
 import { FacetValue } from './facet-value/facet-value.entity';
@@ -40,6 +41,7 @@ export const coreEntitiesMap = {
     Channel,
     Country,
     Customer,
+    CustomerGroup,
     Facet,
     FacetTranslation,
     FacetValue,

+ 76 - 0
server/src/service/providers/customer-group.service.ts

@@ -0,0 +1,76 @@
+import { Injectable } from '@nestjs/common';
+import { InjectConnection } from '@nestjs/typeorm';
+import {
+    AddCustomersToGroupMutationArgs,
+    CreateCustomerGroupInput,
+    RemoveCustomersFromGroupMutationArgs,
+    UpdateCustomerGroupInput,
+} from 'shared/generated-types';
+import { ID } from 'shared/shared-types';
+import { unique } from 'shared/unique';
+import { Connection } from 'typeorm';
+
+import { assertFound } from '../../common/utils';
+import { CustomerGroup } from '../../entity/customer-group/customer-group.entity';
+import { Customer } from '../../entity/customer/customer.entity';
+import { I18nError } from '../../i18n/i18n-error';
+import { patchEntity } from '../helpers/patch-entity';
+
+@Injectable()
+export class CustomerGroupService {
+    constructor(@InjectConnection() private connection: Connection) {}
+
+    findAll(): Promise<CustomerGroup[]> {
+        return this.connection.getRepository(CustomerGroup).find({});
+    }
+
+    findOne(customerGroupId: ID): Promise<CustomerGroup | undefined> {
+        return this.connection.getRepository(CustomerGroup).findOne(customerGroupId);
+    }
+
+    async create(input: CreateCustomerGroupInput): Promise<CustomerGroup> {
+        const customerGroup = new CustomerGroup(input);
+        if (input.customerIds) {
+            customerGroup.customers = await this.getCustomersFromIds(input.customerIds);
+        }
+        const newCustomerGroup = await this.connection.getRepository(CustomerGroup).save(customerGroup);
+        return assertFound(this.findOne(newCustomerGroup.id));
+    }
+
+    async update(input: UpdateCustomerGroupInput): Promise<CustomerGroup> {
+        const customerGroup = await this.getCustomerGroupOrThrow(input.id);
+        const updatedCustomerGroup = patchEntity(customerGroup, input);
+        await this.connection.getRepository(CustomerGroup).save(updatedCustomerGroup);
+        return assertFound(this.findOne(customerGroup.id));
+    }
+
+    async addCustomersToGroup(input: AddCustomersToGroupMutationArgs): Promise<CustomerGroup> {
+        const countries = await this.getCustomersFromIds(input.customerIds);
+        const customerGroup = await this.getCustomerGroupOrThrow(input.customerGroupId);
+        const customers = unique(customerGroup.customers.concat(countries), 'id');
+        customerGroup.customers = customers;
+        await this.connection.getRepository(CustomerGroup).save(customerGroup);
+        return customerGroup;
+    }
+
+    async removeCustomersFromGroup(input: RemoveCustomersFromGroupMutationArgs): Promise<CustomerGroup> {
+        const customerGroup = await this.getCustomerGroupOrThrow(input.customerGroupId);
+        customerGroup.customers = customerGroup.customers.filter(
+            customer => !input.customerIds.includes(customer.id as string),
+        );
+        await this.connection.getRepository(CustomerGroup).save(customerGroup);
+        return customerGroup;
+    }
+
+    private async getCustomerGroupOrThrow(id: ID): Promise<CustomerGroup> {
+        const customerGroup = await this.findOne(id);
+        if (!customerGroup) {
+            throw new I18nError(`error.entity-with-id-not-found`, { entityName: 'CustomerGroup', id });
+        }
+        return customerGroup;
+    }
+
+    private getCustomersFromIds(ids: ID[]): Promise<Customer[]> {
+        return this.connection.getRepository(Customer).findByIds(ids);
+    }
+}

+ 2 - 0
server/src/service/service.module.ts

@@ -12,6 +12,7 @@ import { AssetService } from './providers/asset.service';
 import { AuthService } from './providers/auth.service';
 import { ChannelService } from './providers/channel.service';
 import { CountryService } from './providers/country.service';
+import { CustomerGroupService } from './providers/customer-group.service';
 import { CustomerService } from './providers/customer.service';
 import { FacetValueService } from './providers/facet-value.service';
 import { FacetService } from './providers/facet.service';
@@ -31,6 +32,7 @@ const exportedProviders = [
     AuthService,
     ChannelService,
     CountryService,
+    CustomerGroupService,
     CustomerService,
     FacetService,
     FacetValueService,

+ 117 - 0
shared/generated-types.ts

@@ -50,6 +50,8 @@ export interface Query {
     config: Config;
     countries: CountryList;
     country?: Country | null;
+    customerGroups: CustomerGroup[];
+    customerGroup?: CustomerGroup | null;
     customers: CustomerList;
     customer?: Customer | null;
     facets: FacetList;
@@ -183,6 +185,13 @@ export interface Country extends Node {
     enabled: boolean;
 }
 
+export interface CustomerGroup extends Node {
+    id: string;
+    createdAt: DateTime;
+    updatedAt: DateTime;
+    name: string;
+}
+
 export interface CustomerList extends PaginatedList {
     items: Customer[];
     totalItems: number;
@@ -457,6 +466,10 @@ export interface Mutation {
     createChannel: Channel;
     createCountry: Country;
     updateCountry: Country;
+    createCustomerGroup: CustomerGroup;
+    updateCustomerGroup: CustomerGroup;
+    addCustomersToGroup: CustomerGroup;
+    removeCustomersFromGroup: CustomerGroup;
     createCustomer: Customer;
     createCustomerAddress: Address;
     createFacet: Facet;
@@ -783,6 +796,16 @@ export interface UpdateCountryInput {
     enabled?: boolean | null;
 }
 
+export interface CreateCustomerGroupInput {
+    name: string;
+    customerIds?: string[] | null;
+}
+
+export interface UpdateCustomerGroupInput {
+    id: string;
+    name?: string | null;
+}
+
 export interface CreateCustomerInput {
     firstName: string;
     lastName: string;
@@ -1034,6 +1057,9 @@ export interface CountriesQueryArgs {
 export interface CountryQueryArgs {
     id: string;
 }
+export interface CustomerGroupQueryArgs {
+    id: string;
+}
 export interface CustomersQueryArgs {
     options?: CustomerListOptions | null;
 }
@@ -1112,6 +1138,20 @@ export interface CreateCountryMutationArgs {
 export interface UpdateCountryMutationArgs {
     input: UpdateCountryInput;
 }
+export interface CreateCustomerGroupMutationArgs {
+    input: CreateCustomerGroupInput;
+}
+export interface UpdateCustomerGroupMutationArgs {
+    input: UpdateCustomerGroupInput;
+}
+export interface AddCustomersToGroupMutationArgs {
+    customerGroupId: string;
+    customerIds: string[];
+}
+export interface RemoveCustomersFromGroupMutationArgs {
+    customerGroupId: string;
+    customerIds: string[];
+}
 export interface CreateCustomerMutationArgs {
     input: CreateCustomerInput;
     password?: string | null;
@@ -1452,6 +1492,8 @@ export namespace QueryResolvers {
         config?: ConfigResolver<Config, any, Context>;
         countries?: CountriesResolver<CountryList, any, Context>;
         country?: CountryResolver<Country | null, any, Context>;
+        customerGroups?: CustomerGroupsResolver<CustomerGroup[], any, Context>;
+        customerGroup?: CustomerGroupResolver<CustomerGroup | null, any, Context>;
         customers?: CustomersResolver<CustomerList, any, Context>;
         customer?: CustomerResolver<Customer | null, any, Context>;
         facets?: FacetsResolver<FacetList, any, Context>;
@@ -1568,6 +1610,21 @@ export namespace QueryResolvers {
         id: string;
     }
 
+    export type CustomerGroupsResolver<R = CustomerGroup[], Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
+    export type CustomerGroupResolver<R = CustomerGroup | null, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        CustomerGroupArgs
+    >;
+    export interface CustomerGroupArgs {
+        id: string;
+    }
+
     export type CustomersResolver<R = CustomerList, Parent = any, Context = any> = Resolver<
         R,
         Parent,
@@ -1992,6 +2049,20 @@ export namespace CountryResolvers {
     export type EnabledResolver<R = boolean, Parent = any, Context = any> = Resolver<R, Parent, Context>;
 }
 
+export namespace CustomerGroupResolvers {
+    export interface Resolvers<Context = any> {
+        id?: IdResolver<string, any, Context>;
+        createdAt?: CreatedAtResolver<DateTime, any, Context>;
+        updatedAt?: UpdatedAtResolver<DateTime, any, Context>;
+        name?: NameResolver<string, any, Context>;
+    }
+
+    export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type CreatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type UpdatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+}
+
 export namespace CustomerListResolvers {
     export interface Resolvers<Context = any> {
         items?: ItemsResolver<Customer[], any, Context>;
@@ -2773,6 +2844,10 @@ export namespace MutationResolvers {
         createChannel?: CreateChannelResolver<Channel, any, Context>;
         createCountry?: CreateCountryResolver<Country, any, Context>;
         updateCountry?: UpdateCountryResolver<Country, any, Context>;
+        createCustomerGroup?: CreateCustomerGroupResolver<CustomerGroup, any, Context>;
+        updateCustomerGroup?: UpdateCustomerGroupResolver<CustomerGroup, any, Context>;
+        addCustomersToGroup?: AddCustomersToGroupResolver<CustomerGroup, any, Context>;
+        removeCustomersFromGroup?: RemoveCustomersFromGroupResolver<CustomerGroup, any, Context>;
         createCustomer?: CreateCustomerResolver<Customer, any, Context>;
         createCustomerAddress?: CreateCustomerAddressResolver<Address, any, Context>;
         createFacet?: CreateFacetResolver<Facet, any, Context>;
@@ -2912,6 +2987,48 @@ export namespace MutationResolvers {
         input: UpdateCountryInput;
     }
 
+    export type CreateCustomerGroupResolver<R = CustomerGroup, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        CreateCustomerGroupArgs
+    >;
+    export interface CreateCustomerGroupArgs {
+        input: CreateCustomerGroupInput;
+    }
+
+    export type UpdateCustomerGroupResolver<R = CustomerGroup, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        UpdateCustomerGroupArgs
+    >;
+    export interface UpdateCustomerGroupArgs {
+        input: UpdateCustomerGroupInput;
+    }
+
+    export type AddCustomersToGroupResolver<R = CustomerGroup, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        AddCustomersToGroupArgs
+    >;
+    export interface AddCustomersToGroupArgs {
+        customerGroupId: string;
+        customerIds: string[];
+    }
+
+    export type RemoveCustomersFromGroupResolver<R = CustomerGroup, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        RemoveCustomersFromGroupArgs
+    >;
+    export interface RemoveCustomersFromGroupArgs {
+        customerGroupId: string;
+        customerIds: string[];
+    }
+
     export type CreateCustomerResolver<R = Customer, Parent = any, Context = any> = Resolver<
         R,
         Parent,

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio