Просмотр исходного кода

feat(core): Create entities & fields needed for stock control

Relates to #81. No logic is implemented yet.
Michael Bromley 6 лет назад
Родитель
Сommit
aace38f416

+ 1 - 1
package.json

@@ -3,7 +3,7 @@
   "version": "0.1.0-alpha.17",
   "private": true,
   "scripts": {
-    "core:watch": "concurrently -n tsc,gulp \"cd packages/core && yarn tsc:watch\" \"cd packages/core && yarn gulp:watch\"",
+    "core:watch": "concurrently -n tsc,gulp,common \"cd packages/core && yarn tsc:watch\" \"cd packages/core && yarn gulp:watch\" \"cd packages/common && yarn watch\"",
     "bootstrap": "lerna bootstrap",
     "docs:watch": "concurrently -n docgen,hugo,webpack -c green,blue,cyan \"yarn generate-graphql-docs && yarn generate-typescript-docs -w\" \"cd docs && hugo server\" \"cd docs && yarn webpack -w\"",
     "docs:build": "yarn generate-graphql-docs && yarn generate-typescript-docs && cd docs && yarn webpack --prod && node build.js && hugo",

+ 80 - 1
packages/common/src/generated-shop-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-04-25T20:45:59+02:00
+// Generated in 2019-05-02T11:33:31+02:00
 export type Maybe<T> = T | null;
 
 export interface OrderListOptions {
@@ -768,6 +768,13 @@ export enum Permission {
     DeleteSettings = 'DeleteSettings',
 }
 
+export enum StockMovementType {
+    ADJUSTMENT = 'ADJUSTMENT',
+    SALE = 'SALE',
+    CANCELLATION = 'CANCELLATION',
+    RETURN = 'RETURN',
+}
+
 export enum DeletionResult {
     DELETED = 'DELETED',
     NOT_DELETED = 'NOT_DELETED',
@@ -1675,6 +1682,22 @@ export interface AssetList extends PaginatedList {
     totalItems: number;
 }
 
+export interface Cancellation extends Node {
+    id: string;
+
+    createdAt: DateTime;
+
+    updatedAt: DateTime;
+
+    productVariant: ProductVariant;
+
+    type: StockMovementType;
+
+    quantity: number;
+
+    orderLine: OrderLine;
+}
+
 export interface CountryList extends PaginatedList {
     items: Country[];
 
@@ -1708,6 +1731,8 @@ export interface GlobalSettings {
 
     availableLanguages: LanguageCode[];
 
+    trackInventory: boolean;
+
     serverConfig: ServerConfig;
 
     customFields?: Maybe<Json>;
@@ -1761,12 +1786,44 @@ export interface PromotionList extends PaginatedList {
     totalItems: number;
 }
 
+export interface Return extends Node {
+    id: string;
+
+    createdAt: DateTime;
+
+    updatedAt: DateTime;
+
+    productVariant: ProductVariant;
+
+    type: StockMovementType;
+
+    quantity: number;
+
+    orderItem: OrderItem;
+}
+
 export interface RoleList extends PaginatedList {
     items: Role[];
 
     totalItems: number;
 }
 
+export interface Sale extends Node {
+    id: string;
+
+    createdAt: DateTime;
+
+    updatedAt: DateTime;
+
+    productVariant: ProductVariant;
+
+    type: StockMovementType;
+
+    quantity: number;
+
+    orderLine: OrderLine;
+}
+
 export interface SearchReindexResponse {
     success: boolean;
 
@@ -1781,6 +1838,26 @@ export interface ShippingMethodList extends PaginatedList {
     totalItems: number;
 }
 
+export interface StockAdjustment extends Node {
+    id: string;
+
+    createdAt: DateTime;
+
+    updatedAt: DateTime;
+
+    productVariant: ProductVariant;
+
+    type: StockMovementType;
+
+    quantity: number;
+}
+
+export interface StockMovementList {
+    items: StockMovement[];
+
+    totalItems: number;
+}
+
 export interface TaxRateList extends PaginatedList {
     items: TaxRate[];
 
@@ -1912,3 +1989,5 @@ export interface ResetPasswordMutationArgs {
 
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
+
+export type StockMovement = StockAdjustment | Sale | Cancellation | Return;

Разница между файлами не показана из-за своего большого размера
+ 379 - 261
packages/common/src/generated-types.ts


+ 15 - 0
packages/core/src/api/config/configure-graphql-module.ts

@@ -1,5 +1,6 @@
 import { DynamicModule } from '@nestjs/common';
 import { GqlModuleOptions, GraphQLModule, GraphQLTypesLoader } from '@nestjs/graphql';
+import { StockMovementType } from '@vendure/common/lib/generated-types';
 import { GraphQLUpload } from 'apollo-server-core';
 import { extendSchema, printSchema } from 'graphql';
 import { GraphQLDateTime } from 'graphql-iso-date';
@@ -82,6 +83,20 @@ async function createGraphQLOptions(
                     return value.hasOwnProperty('value') ? 'SinglePrice' : 'PriceRange';
                 },
             },
+            StockMovement: {
+                __resolveType(value: any) {
+                    switch (value.type) {
+                        case StockMovementType.ADJUSTMENT:
+                            return 'StockAdjustment';
+                        case StockMovementType.SALE:
+                            return 'Sale';
+                        case StockMovementType.CANCELLATION:
+                            return 'Cancellation';
+                        case StockMovementType.RETURN:
+                            return 'Return';
+                    }
+                },
+            },
         },
         uploads: {
             maxFileSize: configService.assetOptions.uploadMaxFileSize,

+ 1 - 0
packages/core/src/api/schema/admin-api/global-settings.api.graphql

@@ -8,4 +8,5 @@ type Mutation {
 
 input UpdateGlobalSettingsInput {
     availableLanguages: [LanguageCode!]
+    trackInventory: Boolean
 }

+ 13 - 0
packages/core/src/api/schema/admin-api/product.api.graphql

@@ -32,6 +32,15 @@ type Product {
 
 type ProductVariant {
     enabled: Boolean!
+    stockOnHand: Int!
+    trackInventory: Boolean!
+    stockMovements(options: StockMovementListOptions): StockMovementList!
+}
+
+input StockMovementListOptions {
+    type: StockMovementType
+    skip: Int
+    take: Int
 }
 
 # generated by generateListOptions function
@@ -76,6 +85,8 @@ input CreateProductVariantInput {
     optionIds: [ID!]
     featuredAssetId: ID
     assetIds: [ID!]
+    stockOnHand: Int
+    trackInventory: Boolean
 }
 
 input UpdateProductVariantInput {
@@ -88,4 +99,6 @@ input UpdateProductVariantInput {
     price: Int
     featuredAssetId: ID
     assetIds: [ID!]
+    stockOnHand: Int
+    trackInventory: Boolean
 }

+ 1 - 0
packages/core/src/api/schema/type/global-settings.type.graphql

@@ -3,6 +3,7 @@ type GlobalSettings {
     createdAt: DateTime!
     updatedAt: DateTime!
     availableLanguages: [LanguageCode!]!
+    trackInventory: Boolean!
     serverConfig: ServerConfig!
 }
 

+ 61 - 0
packages/core/src/api/schema/type/stock-movement.type.graphql

@@ -0,0 +1,61 @@
+enum StockMovementType {
+    ADJUSTMENT
+    SALE
+    CANCELLATION
+    RETURN
+}
+
+# interface StockMovement {
+#     id: ID!
+#     createdAt: DateTime!
+#     updatedAt: DateTime!
+#     productVariant: ProductVariant!
+#     type: StockMovementType!
+#     quantity: Int!
+# }
+
+type StockAdjustment implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    productVariant: ProductVariant!
+    type: StockMovementType!
+    quantity: Int!
+}
+
+type Sale implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    productVariant: ProductVariant!
+    type: StockMovementType!
+    quantity: Int!
+    orderLine: OrderLine!
+}
+
+type Cancellation implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    productVariant: ProductVariant!
+    type: StockMovementType!
+    quantity: Int!
+    orderLine: OrderLine!
+}
+
+type Return implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    productVariant: ProductVariant!
+    type: StockMovementType!
+    quantity: Int!
+    orderItem: OrderItem!
+}
+
+union StockMovement = StockAdjustment | Sale | Cancellation | Return
+
+type StockMovementList {
+    items: [StockMovement!]!
+    totalItems: Int!
+}

+ 13 - 3
packages/core/src/entity/entities.ts

@@ -33,6 +33,11 @@ import { AnonymousSession } from './session/anonymous-session.entity';
 import { AuthenticatedSession } from './session/authenticated-session.entity';
 import { Session } from './session/session.entity';
 import { ShippingMethod } from './shipping-method/shipping-method.entity';
+import { Cancellation } from './stock-movement/cancellation.entity';
+import { Return } from './stock-movement/return.entity';
+import { Sale } from './stock-movement/sale.entity';
+import { StockAdjustment } from './stock-movement/stock-adjustment.entity';
+import { StockMovement } from './stock-movement/stock-movement.entity';
 import { TaxCategory } from './tax-category/tax-category.entity';
 import { TaxRate } from './tax-rate/tax-rate.entity';
 import { User } from './user/user.entity';
@@ -47,7 +52,10 @@ export const coreEntitiesMap = {
     AnonymousSession,
     Asset,
     AuthenticatedSession,
+    Cancellation,
     Channel,
+    Collection,
+    CollectionTranslation,
     Country,
     CountryTranslation,
     Customer,
@@ -58,13 +66,11 @@ export const coreEntitiesMap = {
     FacetValueTranslation,
     GlobalSettings,
     Order,
-    OrderLine,
     OrderItem,
+    OrderLine,
     Payment,
     PaymentMethod,
     Product,
-    Collection,
-    CollectionTranslation,
     ProductOption,
     ProductOptionGroup,
     ProductOptionGroupTranslation,
@@ -74,9 +80,13 @@ export const coreEntitiesMap = {
     ProductVariantPrice,
     ProductVariantTranslation,
     Promotion,
+    Return,
     Role,
+    Sale,
     Session,
     ShippingMethod,
+    StockAdjustment,
+    StockMovement,
     TaxCategory,
     TaxRate,
     User,

+ 8 - 0
packages/core/src/entity/global-settings/global-settings.entity.ts

@@ -14,6 +14,14 @@ export class GlobalSettings extends VendureEntity implements HasCustomFields {
     @Column('simple-array')
     availableLanguages: LanguageCode[];
 
+    /**
+     * Specifies the default value for inventory tracking for ProductVariants.
+     * Can be overridden per ProductVariant, but this value determines the default
+     * if not otherwise specified.
+     */
+    @Column({ default: false })
+    trackInventory: boolean;
+
     @Column(type => CustomGlobalSettingsFields)
     customFields: CustomGlobalSettingsFields;
 }

+ 10 - 0
packages/core/src/entity/product-variant/product-variant.entity.ts

@@ -10,6 +10,7 @@ import { CustomProductVariantFields } from '../custom-entity-fields';
 import { FacetValue } from '../facet-value/facet-value.entity';
 import { ProductOption } from '../product-option/product-option.entity';
 import { Product } from '../product/product.entity';
+import { StockMovement } from '../stock-movement/stock-movement.entity';
 import { TaxCategory } from '../tax-category/tax-category.entity';
 import { TaxRate } from '../tax-rate/tax-rate.entity';
 
@@ -91,6 +92,15 @@ export class ProductVariant extends VendureEntity implements Translatable, HasCu
     @Column({ nullable: true })
     productId: number;
 
+    @Column({ default: 0 })
+    stockOnHand: number;
+
+    @Column()
+    trackInventory: boolean;
+
+    @OneToMany(type => StockMovement, stockMovement => stockMovement.productVariant)
+    stockMovements: StockMovement[];
+
     @ManyToMany(type => ProductOption)
     @JoinTable()
     options: ProductOption[];

+ 16 - 0
packages/core/src/entity/stock-movement/cancellation.entity.ts

@@ -0,0 +1,16 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity, ManyToOne } from 'typeorm';
+
+import { OrderLine } from '../order-line/order-line.entity';
+
+import { StockMovement } from './stock-movement.entity';
+
+@ChildEntity()
+export class Cancellation extends StockMovement {
+    constructor(input: DeepPartial<Cancellation>) {
+        super(input);
+    }
+
+    @ManyToOne(type => OrderLine)
+    orderLine: OrderLine;
+}

+ 11 - 0
packages/core/src/entity/stock-movement/return.entity.ts

@@ -0,0 +1,11 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity } from 'typeorm';
+
+import { StockMovement } from './stock-movement.entity';
+
+@ChildEntity()
+export class Return extends StockMovement {
+    constructor(input: DeepPartial<Return>) {
+        super(input);
+    }
+}

+ 16 - 0
packages/core/src/entity/stock-movement/sale.entity.ts

@@ -0,0 +1,16 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity, ManyToOne } from 'typeorm';
+
+import { OrderLine } from '../order-line/order-line.entity';
+
+import { StockMovement } from './stock-movement.entity';
+
+@ChildEntity()
+export class Sale extends StockMovement {
+    constructor(input: DeepPartial<Sale>) {
+        super(input);
+    }
+
+    @ManyToOne(type => OrderLine)
+    orderLine: OrderLine;
+}

+ 16 - 0
packages/core/src/entity/stock-movement/stock-adjustment.entity.ts

@@ -0,0 +1,16 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity, ManyToOne } from 'typeorm';
+
+import { OrderItem } from '../order-item/order-item.entity';
+
+import { StockMovement } from './stock-movement.entity';
+
+@ChildEntity()
+export class StockAdjustment extends StockMovement {
+    constructor(input: DeepPartial<StockAdjustment>) {
+        super(input);
+    }
+
+    @ManyToOne(type => OrderItem)
+    orderItem: OrderItem;
+}

+ 24 - 0
packages/core/src/entity/stock-movement/stock-movement.entity.ts

@@ -0,0 +1,24 @@
+import { Column, Entity, ManyToOne, TableInheritance } from 'typeorm';
+
+import { VendureEntity } from '../base/base.entity';
+import { ProductVariant } from '../product-variant/product-variant.entity';
+
+/**
+ * @description
+ * A StockMovement is created whenever stock of a particular ProductVariant goes in
+ * or out.
+ *
+ * @docsCategory entities
+ */
+@Entity()
+@TableInheritance({ column: { type: 'varchar', name: 'type' } })
+export abstract class StockMovement extends VendureEntity {
+    @Column({ nullable: false })
+    type: string;
+
+    @ManyToOne(type => ProductVariant, variant => variant.stockMovements)
+    productVariant: ProductVariant;
+
+    @Column()
+    quantity: number;
+}

+ 5 - 0
packages/core/src/service/services/product-variant.service.ts

@@ -28,6 +28,7 @@ import { getEntityOrThrow } from '../helpers/utils/get-entity-or-throw';
 import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { FacetValueService } from './facet-value.service';
+import { GlobalSettingsService } from './global-settings.service';
 import { TaxCategoryService } from './tax-category.service';
 import { TaxRateService } from './tax-rate.service';
 import { ZoneService } from './zone.service';
@@ -46,6 +47,7 @@ export class ProductVariantService {
         private translatableSaver: TranslatableSaver,
         private eventBus: EventBus,
         private listQueryBuilder: ListQueryBuilder,
+        private globalSettingsService: GlobalSettingsService,
     ) {}
 
     findOne(ctx: RequestContext, productVariantId: ID): Promise<Translated<ProductVariant> | undefined> {
@@ -155,6 +157,9 @@ export class ProductVariantService {
                 if (input.facetValueIds) {
                     variant.facetValues = await this.facetValueService.findByIds(input.facetValueIds);
                 }
+                if (input.trackInventory == null) {
+                    variant.trackInventory = (await this.globalSettingsService.getSettings()).trackInventory;
+                }
                 variant.product = product;
                 variant.taxCategory = { id: input.taxCategoryId } as any;
                 await this.assetUpdater.updateEntityAssets(variant, input);

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
schema-admin.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
schema-shop.json


Разница между файлами не показана из-за своего большого размера
+ 262 - 482
schema.json


Некоторые файлы не были показаны из-за большого количества измененных файлов