Browse Source

feat(server): Make adjustment operations configurable

Michael Bromley 7 years ago
parent
commit
c6b79fe959

File diff suppressed because it is too large
+ 0 - 0
schema.json


+ 0 - 5
server/src/common/types/asset-type.graphql

@@ -1,5 +0,0 @@
-enum AssetType {
-    IMAGE,
-    VIDEO,
-    BINARY
-}

+ 0 - 6
server/src/common/types/auth-types.ts

@@ -1,6 +0,0 @@
-import { Permission } from 'shared/generated-types';
-
-export interface JwtPayload {
-    identifier: string;
-    roles: Permission[];
-}

+ 0 - 6
server/src/common/types/common-types.graphql

@@ -12,12 +12,6 @@ interface Node {
     id: ID!
 }
 
-enum AdjustmentType {
-    TAX
-    PROMOTION
-    SHIPPING
-}
-
 enum SortOrder {
     ASC
     DESC

+ 0 - 27
server/src/config/adjustment/adjustment-actions.ts

@@ -1,27 +0,0 @@
-import { AdjustmentOperation, AdjustmentType } from 'shared/generated-types';
-
-import { OrderItem } from '../../entity/order-item/order-item.entity';
-import { Order } from '../../entity/order/order.entity';
-
-export type AdjustmentActionArgType = 'percentage' | 'money';
-export type AdjustmentActionArg = { name: string; type: AdjustmentActionArgType; value?: string };
-export type AdjustmentActionCalculation<T extends OrderItem | Order> = (
-    target: T,
-    args: { [argName: string]: any },
-    context: any,
-) => number;
-
-export interface AdjustmentActionConfig<T extends OrderItem | Order> extends AdjustmentOperation {
-    args: AdjustmentActionArg[];
-    calculate: AdjustmentActionCalculation<T>;
-}
-
-export const orderPercentageDiscount: AdjustmentActionConfig<Order> = {
-    type: AdjustmentType.PROMOTION,
-    code: 'order_percentage_discount',
-    args: [{ name: 'discount', type: 'percentage' }],
-    calculate(target, args) {
-        return target.price * args.discount;
-    },
-    description: 'Discount order by { discount }%',
-};

+ 31 - 0
server/src/config/adjustment/adjustment-types.ts

@@ -0,0 +1,31 @@
+import { AdjustmentOperation, AdjustmentOperationTarget } from 'shared/generated-types';
+
+import { OrderItem } from '../../entity/order-item/order-item.entity';
+import { Order } from '../../entity/order/order.entity';
+
+export type AdjustmentActionArgType = 'percentage' | 'money';
+export type AdjustmentActionArg = { name: string; type: AdjustmentActionArgType; value?: string };
+export type AdjustmentActionCalculation<T extends OrderItem | Order> = (
+    target: T,
+    args: { [argName: string]: any },
+    context: any,
+) => number;
+
+export interface AdjustmentActionConfig<T extends OrderItem | Order> extends AdjustmentOperation {
+    args: AdjustmentActionArg[];
+    calculate: AdjustmentActionCalculation<T>;
+}
+
+export type AdjustmentConditionArgType = 'int' | 'money' | 'string' | 'datetime';
+export type AdjustmentConditionArg = { name: string; type: AdjustmentConditionArgType };
+export type AdjustmentConditionPredicate<T extends OrderItem | Order> = (
+    target: T,
+    args: { [argName: string]: any },
+    context: any,
+) => boolean;
+
+export interface AdjustmentConditionConfig<T extends OrderItem | Order> extends AdjustmentOperation {
+    target: T extends Order ? AdjustmentOperationTarget.ORDER : AdjustmentOperationTarget.ORDER_ITEM;
+    args: AdjustmentConditionArg[];
+    predicate: AdjustmentConditionPredicate<T>;
+}

+ 18 - 0
server/src/config/adjustment/default-adjustment-actions.ts

@@ -0,0 +1,18 @@
+import { AdjustmentOperationTarget, AdjustmentType } from 'shared/generated-types';
+
+import { Order } from '../../entity/order/order.entity';
+
+import { AdjustmentActionConfig } from './adjustment-types';
+
+export const orderPercentageDiscount: AdjustmentActionConfig<Order> = {
+    type: AdjustmentType.PROMOTION,
+    target: AdjustmentOperationTarget.ORDER,
+    code: 'order_percentage_discount',
+    args: [{ name: 'discount', type: 'percentage' }],
+    calculate(target, args) {
+        return target.price * args.discount;
+    },
+    description: 'Discount order by { discount }%',
+};
+
+export const defaultAdjustmentActions = [orderPercentageDiscount];

+ 17 - 13
server/src/config/adjustment/adjustment-conditions.ts → server/src/config/adjustment/default-adjustment-conditions.ts

@@ -1,23 +1,13 @@
-import { AdjustmentOperation, AdjustmentType } from 'shared/generated-types';
+import { AdjustmentOperationTarget, AdjustmentType } from 'shared/generated-types';
 
 import { OrderItem } from '../../entity/order-item/order-item.entity';
 import { Order } from '../../entity/order/order.entity';
 
-export type AdjustmentConditionArgType = 'int' | 'money' | 'string' | 'datetime';
-export type AdjustmentConditionArg = { name: string; type: AdjustmentConditionArgType };
-export type AdjustmentConditionPredicate<T extends OrderItem | Order> = (
-    target: T,
-    args: { [argName: string]: any },
-    context: any,
-) => boolean;
-
-export interface AdjustmentConditionConfig<T extends OrderItem | Order> extends AdjustmentOperation {
-    args: AdjustmentConditionArg[];
-    predicate: AdjustmentConditionPredicate<T>;
-}
+import { AdjustmentConditionConfig } from './adjustment-types';
 
 export const minimumOrderAmount: AdjustmentConditionConfig<Order> = {
     type: AdjustmentType.PROMOTION,
+    target: AdjustmentOperationTarget.ORDER,
     code: 'minimum_order_amount',
     args: [{ name: 'amount', type: 'money' }],
     predicate(target: Order, args) {
@@ -28,6 +18,7 @@ export const minimumOrderAmount: AdjustmentConditionConfig<Order> = {
 
 export const dateRange: AdjustmentConditionConfig<Order> = {
     type: AdjustmentType.PROMOTION,
+    target: AdjustmentOperationTarget.ORDER,
     code: 'date_range',
     args: [{ name: 'start', type: 'datetime' }, { name: 'end', type: 'datetime' }],
     predicate(target: Order, args) {
@@ -36,3 +27,16 @@ export const dateRange: AdjustmentConditionConfig<Order> = {
     },
     description: 'If Order placed between { start } and { end }',
 };
+
+export const atLeastNOfProduct: AdjustmentConditionConfig<OrderItem> = {
+    type: AdjustmentType.PROMOTION,
+    target: AdjustmentOperationTarget.ORDER_ITEM,
+    code: 'at_least_n_of_product',
+    args: [{ name: 'minimum', type: 'int' }],
+    predicate(target: OrderItem, args) {
+        return target.quantity >= args.minimum;
+    },
+    description: 'Buy at least { minimum } of any product',
+};
+
+export const defaultAdjustmentConditions = [minimumOrderAmount, dateRange, atLeastNOfProduct];

+ 9 - 0
server/src/config/config.service.ts

@@ -7,6 +7,7 @@ import { ConnectionOptions } from 'typeorm';
 
 import { ReadOnlyRequired } from '../common/types/common-types';
 
+import { AdjustmentActionConfig, AdjustmentConditionConfig } from './adjustment/adjustment-types';
 import { AssetNamingStrategy } from './asset-naming-strategy/asset-naming-strategy';
 import { AssetPreviewStrategy } from './asset-preview-strategy/asset-preview-strategy';
 import { AssetStorageStrategy } from './asset-storage-strategy/asset-storage-strategy';
@@ -76,6 +77,14 @@ export class ConfigService implements VendureConfig {
         return this.activeConfig.uploadMaxFileSize;
     }
 
+    get adjustmentConditions(): Array<AdjustmentConditionConfig<any>> {
+        return this.activeConfig.adjustmentConditions;
+    }
+
+    get adjustmentActions(): Array<AdjustmentActionConfig<any>> {
+        return this.activeConfig.adjustmentActions;
+    }
+
     get customFields(): CustomFields {
         return this.activeConfig.customFields;
     }

+ 4 - 0
server/src/config/default-config.ts

@@ -4,6 +4,8 @@ import { CustomFields } from 'shared/shared-types';
 
 import { ReadOnlyRequired } from '../common/types/common-types';
 
+import { defaultAdjustmentActions } from './adjustment/default-adjustment-actions';
+import { defaultAdjustmentConditions } from './adjustment/default-adjustment-conditions';
 import { DefaultAssetNamingStrategy } from './asset-naming-strategy/default-asset-naming-strategy';
 import { NoAssetPreviewStrategy } from './asset-preview-strategy/no-asset-preview-strategy';
 import { NoAssetStorageStrategy } from './asset-storage-strategy/no-asset-storage-strategy';
@@ -37,6 +39,8 @@ export const defaultConfig: ReadOnlyRequired<VendureConfig> = {
         type: 'mysql',
     },
     uploadMaxFileSize: 20971520,
+    adjustmentConditions: defaultAdjustmentConditions,
+    adjustmentActions: defaultAdjustmentActions,
     customFields: {
         Address: [],
         Customer: [],

+ 11 - 0
server/src/config/vendure-config.ts

@@ -6,6 +6,7 @@ import { ConnectionOptions } from 'typeorm';
 
 import { ReadOnlyRequired } from '../common/types/common-types';
 
+import { AdjustmentActionConfig, AdjustmentConditionConfig } from './adjustment/adjustment-types';
 import { AssetNamingStrategy } from './asset-naming-strategy/asset-naming-strategy';
 import { AssetPreviewStrategy } from './asset-preview-strategy/asset-preview-strategy';
 import { AssetStorageStrategy } from './asset-storage-strategy/asset-storage-strategy';
@@ -108,6 +109,16 @@ export interface VendureConfig {
      * The connection options used by TypeORM to connect to the database.
      */
     dbConnectionOptions: ConnectionOptions;
+    /**
+     * An array of adjustment conditions which can be used to construct AdjustmentSources
+     * (promotions, taxes and shipping).
+     */
+    adjustmentConditions?: Array<AdjustmentConditionConfig<any>>;
+    /**
+     * An array of adjustment actions which can be used to construct AdjustmentSources
+     * (promotions, taxes and shipping).
+     */
+    adjustmentActions?: Array<AdjustmentActionConfig<any>>;
     /**
      * Defines custom fields which can be used to extend the built-in entities.
      */

+ 12 - 0
server/src/entity/adjustment-source/adjustment-source.graphql

@@ -8,6 +8,17 @@ type AdjustmentSource implements Node {
     actions: [AdjustmentOperation!]!
 }
 
+enum AdjustmentType {
+    TAX
+    PROMOTION
+    SHIPPING
+}
+
+enum AdjustmentOperationTarget {
+    ORDER
+    ORDER_ITEM
+}
+
 type AdjustmentArg {
     name: String!
     type: String!
@@ -16,6 +27,7 @@ type AdjustmentArg {
 
 type AdjustmentOperation {
     type: AdjustmentType!
+    target: AdjustmentOperationTarget!
     code: String!
     args: [AdjustmentArg!]!
     description: String!

+ 6 - 0
server/src/entity/asset/asset.graphql

@@ -8,6 +8,12 @@ type Asset implements Node {
     preview: String!
 }
 
+enum AssetType {
+    IMAGE,
+    VIDEO,
+    BINARY
+}
+
 input CreateAssetInput {
     file: Upload!
 }

+ 10 - 10
server/src/service/providers/adjustment-source.service.ts

@@ -20,14 +20,10 @@ import { assertFound } from '../../common/utils';
 import {
     AdjustmentActionArgType,
     AdjustmentActionConfig,
-    orderPercentageDiscount,
-} from '../../config/adjustment/adjustment-actions';
-import {
     AdjustmentConditionArgType,
     AdjustmentConditionConfig,
-    dateRange,
-    minimumOrderAmount,
-} from '../../config/adjustment/adjustment-conditions';
+} from '../../config/adjustment/adjustment-types';
+import { ConfigService } from '../../config/config.service';
 import {
     AdjustmentOperationValues,
     AdjustmentSource,
@@ -43,10 +39,13 @@ export class AdjustmentSourceService {
     availableConditions: Array<AdjustmentConditionConfig<any>> = [];
     availableActions: Array<AdjustmentActionConfig<any>> = [];
 
-    constructor(@InjectConnection() private connection: Connection, private channelService: ChannelService) {
-        // TODO: get from config
-        this.availableConditions = [minimumOrderAmount, dateRange];
-        this.availableActions = [orderPercentageDiscount];
+    constructor(
+        @InjectConnection() private connection: Connection,
+        private configService: ConfigService,
+        private channelService: ChannelService,
+    ) {
+        this.availableConditions = this.configService.adjustmentConditions;
+        this.availableActions = this.configService.adjustmentActions;
     }
 
     findAll(
@@ -148,6 +147,7 @@ export class AdjustmentSourceService {
             const match = this.getAdjustmentOperationByCode(type, v.code);
             return {
                 type: match.type,
+                target: match.target,
                 code: v.code,
                 args: match.args.map((args, i) => ({
                     ...args,

+ 12 - 0
shared/generated-types.ts

@@ -77,6 +77,7 @@ export interface AdjustmentSource extends Node {
 
 export interface AdjustmentOperation {
     type: AdjustmentType;
+    target: AdjustmentOperationTarget;
     code: string;
     args: AdjustmentArg[];
     description: string;
@@ -1087,6 +1088,11 @@ export enum AdjustmentType {
     SHIPPING = 'SHIPPING',
 }
 
+export enum AdjustmentOperationTarget {
+    ORDER = 'ORDER',
+    ORDER_ITEM = 'ORDER_ITEM',
+}
+
 export enum SortOrder {
     ASC = 'ASC',
     DESC = 'DESC',
@@ -1586,12 +1592,18 @@ export namespace AdjustmentSourceResolvers {
 export namespace AdjustmentOperationResolvers {
     export interface Resolvers<Context = any> {
         type?: TypeResolver<AdjustmentType, any, Context>;
+        target?: TargetResolver<AdjustmentOperationTarget, any, Context>;
         code?: CodeResolver<string, any, Context>;
         args?: ArgsResolver<AdjustmentArg[], any, Context>;
         description?: DescriptionResolver<string, any, Context>;
     }
 
     export type TypeResolver<R = AdjustmentType, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type TargetResolver<R = AdjustmentOperationTarget, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
     export type CodeResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type ArgsResolver<R = AdjustmentArg[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type DescriptionResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;

Some files were not shown because too many files changed in this diff