Parcourir la source

feat(server): Create entities for Orders flow

Michael Bromley il y a 7 ans
Parent
commit
37be0299ba

+ 3 - 8
docs/diagrams/full-class-diagram.puml

@@ -58,13 +58,9 @@ class OrderItem {
     order: Order
     productVariant: ProductVariant
 }
-class OrderItemUnit {
-    orderItem: OrderItem
-}
 class Adjustment {
-    orderItemUnit?: OrderItemUnit
-    orderItem?: OrderItem
-    order?: Order
+    target: OrderItem | Order
+    source: AdjustmentSource
 }
 enum AdjustmentType {
     Tax
@@ -103,11 +99,10 @@ Customer *-- "0..*" Order
 OrderItem - ProductVariant
 Order *-- OrderItem
 Order -- Channel
-OrderItem *-- OrderItemUnit
-OrderItemUnit o-- Adjustment
 OrderItem o-- Adjustment
 Order o-- Adjustment
 Adjustment - AdjustmentType
+Adjustment o-- AdjustmentSource
 AdjustmentSource - AdjustmentType
 AdjustmentSource o-- Channel
 Product o-- Channel

+ 28 - 0
docs/diagrams/order-state-diagram.puml

@@ -0,0 +1,28 @@
+@startuml
+!include theme.puml
+title Order State Diagram
+
+[*] --> AddingItems: AddProductVariantToOrder
+AddingItems: Customer is adding items to order
+AddingItems: All non-shipping Adjustments get applied here
+
+AddingItems --> ArrangingShipping:Next
+ArrangingShipping: Shipping destination is set
+ArrangingShipping: Shipping Adjustment added to Order
+
+ArrangingShipping --> AddingItems:Previous
+ArrangingShipping --> ArrangingPayment:Next
+ArrangingPayment: Payment provider is used to take payment
+
+ArrangingPayment --> ArrangingShipping:Previous
+ArrangingPayment --> OrderComplete:Next
+OrderComplete: Order is complete and ready to be processed
+
+OrderComplete --> Cancelled:Cancel
+Cancelled: Customer or Admin cancelled the order
+
+Cancelled --> [*]
+
+OrderComplete --> [*]
+
+@enduml

+ 1 - 1
server/package.json

@@ -26,7 +26,7 @@
     "@nestjs/testing": "5.3.8",
     "@nestjs/typeorm": "^5.2.2",
     "apollo-server-express": "^2.0.4",
-    "bcrypt": "^3.0.0",
+    "bcrypt": "^3.0.1",
     "body-parser": "^1.18.3",
     "cookie-session": "^2.0.0-beta.3",
     "express": "^4.16.3",

+ 11 - 0
server/src/api/common/graphql-config.service.ts

@@ -37,6 +37,17 @@ export class GraphqlConfigService implements GqlOptionsFactory {
                 DateTime: GraphQLDateTime,
                 Node: dummyResolveType,
                 PaginatedList: dummyResolveType,
+                AdjustmentTarget: {
+                    __resolveType(obj) {
+                        if (obj.hasOwnProperty('quantity')) {
+                            return 'OrderItem';
+                        }
+                        if (obj.hasOwnProperty('items')) {
+                            return 'Order';
+                        }
+                        return null;
+                    },
+                },
                 Upload: GraphQLUpload,
             },
             uploads: {

+ 27 - 0
server/src/entity/adjustment-source/adjustment-source.entity.ts

@@ -0,0 +1,27 @@
+import { DeepPartial } from 'shared/shared-types';
+import { Column, Entity, JoinTable, ManyToMany } from 'typeorm';
+
+import { ChannelAware } from '../../common/types/common-types';
+import { VendureEntity } from '../base/base.entity';
+import { Channel } from '../channel/channel.entity';
+
+export enum AdjustmentType {
+    Tax,
+    Shipping,
+    Promotion,
+}
+
+@Entity()
+export class AdjustmentSource extends VendureEntity implements ChannelAware {
+    constructor(input?: DeepPartial<AdjustmentSource>) {
+        super(input);
+    }
+
+    @Column() name: string;
+
+    @Column('varchar') type: AdjustmentType;
+
+    @ManyToMany(type => Channel)
+    @JoinTable()
+    channels: Channel[];
+}

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

@@ -0,0 +1,7 @@
+type AdjustmentSource implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    name: String!
+    type: String!
+}

+ 15 - 0
server/src/entity/adjustment/adjustment.entity.ts

@@ -0,0 +1,15 @@
+import { Column, Entity, ManyToOne, TableInheritance } from 'typeorm';
+
+import { AdjustmentSource, AdjustmentType } from '../adjustment-source/adjustment-source.entity';
+import { VendureEntity } from '../base/base.entity';
+
+@Entity()
+@TableInheritance({ column: { type: 'varchar', name: 'type' } })
+export abstract class Adjustment extends VendureEntity {
+    @Column('varchar') type: AdjustmentType;
+
+    @Column() amount: number;
+
+    @ManyToOne(type => AdjustmentSource)
+    source: AdjustmentSource;
+}

+ 11 - 0
server/src/entity/adjustment/adjustment.graphql

@@ -0,0 +1,11 @@
+type Adjustment implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    type: String!
+    amount: Int!
+    target: AdjustmentTarget!
+    source: AdjustmentSource!
+}
+
+union AdjustmentTarget = Order | OrderItem

+ 16 - 0
server/src/entity/adjustment/order-adjustment.entity.ts

@@ -0,0 +1,16 @@
+import { DeepPartial } from 'shared/shared-types';
+import { ChildEntity, ManyToOne } from 'typeorm';
+
+import { Order } from '../order/order.entity';
+
+import { Adjustment } from './adjustment.entity';
+
+@ChildEntity()
+export class OrderAdjustment extends Adjustment {
+    constructor(input?: DeepPartial<OrderAdjustment>) {
+        super(input);
+    }
+
+    @ManyToOne(type => Order)
+    target: Order;
+}

+ 16 - 0
server/src/entity/adjustment/order-item-adjustment.entity.ts

@@ -0,0 +1,16 @@
+import { DeepPartial } from 'shared/shared-types';
+import { ChildEntity, ManyToOne } from 'typeorm';
+
+import { OrderItem } from '../order-item/order-item.entity';
+
+import { Adjustment } from './adjustment.entity';
+
+@ChildEntity()
+export class OrderItemAdjustment extends Adjustment {
+    constructor(input?: DeepPartial<OrderItemAdjustment>) {
+        super(input);
+    }
+
+    @ManyToOne(type => OrderItem)
+    target: OrderItem;
+}

+ 4 - 0
server/src/entity/customer/customer.entity.ts

@@ -5,6 +5,7 @@ import { Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm';
 import { Address } from '../address/address.entity';
 import { VendureEntity } from '../base/base.entity';
 import { CustomCustomerFields } from '../custom-entity-fields';
+import { Order } from '../order/order.entity';
 import { User } from '../user/user.entity';
 
 @Entity()
@@ -26,6 +27,9 @@ export class Customer extends VendureEntity implements HasCustomFields {
     @OneToMany(type => Address, address => address.customer)
     addresses: Address[];
 
+    @OneToMany(type => Order, order => order.customer)
+    orders: Order[];
+
     @OneToOne(type => User, { eager: true })
     @JoinColumn()
     user?: User;

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

@@ -1,4 +1,8 @@
 import { Address } from './address/address.entity';
+import { AdjustmentSource } from './adjustment-source/adjustment-source.entity';
+import { Adjustment } from './adjustment/adjustment.entity';
+import { OrderAdjustment } from './adjustment/order-adjustment.entity';
+import { OrderItemAdjustment } from './adjustment/order-item-adjustment.entity';
 import { Administrator } from './administrator/administrator.entity';
 import { Asset } from './asset/asset.entity';
 import { Channel } from './channel/channel.entity';
@@ -7,6 +11,8 @@ import { FacetValueTranslation } from './facet-value/facet-value-translation.ent
 import { FacetValue } from './facet-value/facet-value.entity';
 import { FacetTranslation } from './facet/facet-translation.entity';
 import { Facet } from './facet/facet.entity';
+import { OrderItem } from './order-item/order-item.entity';
+import { Order } from './order/order.entity';
 import { ProductOptionGroupTranslation } from './product-option-group/product-option-group-translation.entity';
 import { ProductOptionGroup } from './product-option-group/product-option-group.entity';
 import { ProductOptionTranslation } from './product-option/product-option-translation.entity';
@@ -25,6 +31,8 @@ import { User } from './user/user.entity';
  */
 export const coreEntitiesMap = {
     Address,
+    Adjustment,
+    AdjustmentSource,
     Administrator,
     Asset,
     Channel,
@@ -33,12 +41,16 @@ export const coreEntitiesMap = {
     FacetTranslation,
     FacetValue,
     FacetValueTranslation,
+    Order,
+    OrderAdjustment,
+    OrderItem,
+    OrderItemAdjustment,
     Product,
-    ProductTranslation,
     ProductOption,
-    ProductOptionTranslation,
     ProductOptionGroup,
     ProductOptionGroupTranslation,
+    ProductOptionTranslation,
+    ProductTranslation,
     ProductVariant,
     ProductVariantPrice,
     ProductVariantTranslation,

+ 27 - 0
server/src/entity/order-item/order-item.entity.ts

@@ -0,0 +1,27 @@
+import { DeepPartial } from 'shared/shared-types';
+import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
+
+import { OrderItemAdjustment } from '../adjustment/order-item-adjustment.entity';
+import { VendureEntity } from '../base/base.entity';
+import { Order } from '../order/order.entity';
+import { ProductVariant } from '../product-variant/product-variant.entity';
+
+@Entity()
+export class OrderItem extends VendureEntity {
+    constructor(input?: DeepPartial<OrderItem>) {
+        super(input);
+    }
+
+    @ManyToOne(type => ProductVariant)
+    productVariant: ProductVariant;
+
+    @Column() unitPrice: number;
+
+    @Column() quantity: number;
+
+    @OneToMany(type => OrderItemAdjustment, adjustment => adjustment.target)
+    adjustments: OrderItemAdjustment[];
+
+    @ManyToOne(type => Order, order => order.items)
+    order: Order;
+}

+ 10 - 0
server/src/entity/order-item/order-item.graphql

@@ -0,0 +1,10 @@
+type OrderItem implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    productVariant: ProductVariant!
+    unitPrice: Int!
+    quantity: Int!
+    adjustments: [Adjustment!]!
+    order: Order!
+}

+ 25 - 0
server/src/entity/order/order.entity.ts

@@ -0,0 +1,25 @@
+import { DeepPartial } from 'shared/shared-types';
+import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
+
+import { OrderAdjustment } from '../adjustment/order-adjustment.entity';
+import { VendureEntity } from '../base/base.entity';
+import { Customer } from '../customer/customer.entity';
+import { OrderItem } from '../order-item/order-item.entity';
+
+@Entity()
+export class Order extends VendureEntity {
+    constructor(input?: DeepPartial<Order>) {
+        super(input);
+    }
+
+    @Column() code: string;
+
+    @ManyToOne(type => Customer)
+    customer: Customer;
+
+    @OneToMany(type => OrderItem, item => item.order)
+    items: OrderItem[];
+
+    @OneToMany(type => OrderAdjustment, adjustment => adjustment.target)
+    adjustments: OrderAdjustment[];
+}

+ 9 - 0
server/src/entity/order/order.graphql

@@ -0,0 +1,9 @@
+type Order implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    code: String!
+    customer: Customer!
+    items: [OrderItem!]!
+    adjustments: [Adjustment!]!
+}

+ 5 - 1
server/src/entity/session/session.entity.ts

@@ -2,6 +2,7 @@ import { DeepPartial } from 'shared/shared-types';
 import { Column, Entity, Index, ManyToOne } from 'typeorm';
 
 import { VendureEntity } from '../base/base.entity';
+import { Order } from '../order/order.entity';
 import { User } from '../user/user.entity';
 
 @Entity()
@@ -15,7 +16,10 @@ export class Session extends VendureEntity {
     token: string;
 
     @ManyToOne(type => User)
-    user: User;
+    user?: User;
+
+    @ManyToOne(type => Order)
+    activeOrder?: Order;
 
     @Column() expires: Date;
 

+ 7 - 22
server/yarn.lock

@@ -899,12 +899,12 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
-bcrypt@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.0.tgz#0cd38983f45143aa5a6122c9660d0b7ec3a33fb0"
+bcrypt@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.1.tgz#2ea12883330bf5f81926f70475cf21bdd0c2f8ff"
   dependencies:
-    nan "2.10.0"
-    node-pre-gyp "0.10.2"
+    nan "2.11.0"
+    node-pre-gyp "0.11.0"
 
 bignumber.js@4.1.0:
   version "4.1.0"
@@ -3930,7 +3930,7 @@ nan@2.10.0, nan@^2.9.2:
   version "2.10.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
 
-nan@^2.11.0:
+nan@2.11.0, nan@^2.11.0:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099"
 
@@ -3954,7 +3954,7 @@ natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
 
-needle@^2.2.0, needle@^2.2.1:
+needle@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d"
   dependencies:
@@ -4025,21 +4025,6 @@ node-notifier@^5.2.1:
     shellwords "^0.1.1"
     which "^1.3.0"
 
-node-pre-gyp@0.10.2:
-  version "0.10.2"
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.2.tgz#e8945c20ef6795a20aac2b44f036eb13cf5146e3"
-  dependencies:
-    detect-libc "^1.0.2"
-    mkdirp "^0.5.1"
-    needle "^2.2.0"
-    nopt "^4.0.1"
-    npm-packlist "^1.1.6"
-    npmlog "^4.0.2"
-    rc "^1.2.7"
-    rimraf "^2.6.1"
-    semver "^5.3.0"
-    tar "^4"
-
 node-pre-gyp@0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"