feat: order editing data model (#2184)

**What**
- add order editing entities
- add repositories
- add a feature flag for the order editing feature
- add the migrations file

RESOLVES CORE-490
This commit is contained in:
Frane Polić
2022-09-13 18:40:21 +02:00
committed by GitHub
parent a94d9816fe
commit f2eb942b41
7 changed files with 353 additions and 4 deletions

View File

@@ -0,0 +1,10 @@
import { FlagSettings } from "../../types/feature-flags"
const OrderEditingFeatureFlag: FlagSettings = {
key: "order_editing",
default_val: false,
env_key: "MEDUSA_FF_ORDER_EDITING",
description: "[WIP] Enable the order editing feature",
}
export default OrderEditingFeatureFlag

View File

@@ -0,0 +1,53 @@
import { MigrationInterface, QueryRunner } from "typeorm"
import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing"
export const featureFlag = OrderEditingFeatureFlag.key
export class orderEditing1663059812399 implements MigrationInterface {
name = "orderEditing1663059812399"
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "order_item_change_type_enum" AS ENUM('item_add', 'item_remove', 'item_update')`
)
await queryRunner.query(
`CREATE TABLE "order_item_change" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP WITH TIME ZONE, "type" "order_item_change_type_enum" NOT NULL, "order_edit_id" character varying NOT NULL, "original_line_item_id" character varying, "line_item_id" character varying, CONSTRAINT "UQ_da93cee3ca0dd50a5246268c2e9" UNIQUE ("order_edit_id", "line_item_id"), CONSTRAINT "UQ_5b7a99181e4db2ea821be0b6196" UNIQUE ("order_edit_id", "original_line_item_id"), CONSTRAINT "REL_5f9688929761f7df108b630e64" UNIQUE ("line_item_id"), CONSTRAINT "PK_d6eb138f77ffdee83567b85af0c" PRIMARY KEY ("id"))`
)
await queryRunner.query(
`CREATE TABLE "order_edit" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP WITH TIME ZONE, "order_id" character varying NOT NULL, "internal_note" character varying, "created_by" character varying NOT NULL, "requested_by" character varying, "requested_at" TIMESTAMP WITH TIME ZONE, "confirmed_by" character varying, "confirmed_at" TIMESTAMP WITH TIME ZONE, "declined_by" character varying, "declined_reason" character varying, "declined_at" TIMESTAMP WITH TIME ZONE, "canceled_by" character varying, "canceled_at" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_58ab6acf2e84b4e827f5f846f7a" PRIMARY KEY ("id"))`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" ADD CONSTRAINT "FK_44feeebb258bf4cfa4cc4202281" FOREIGN KEY ("order_edit_id") REFERENCES "order_edit"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" ADD CONSTRAINT "FK_b4d53b8d03c9f5e7d4317e818d9" FOREIGN KEY ("original_line_item_id") REFERENCES "line_item"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" ADD CONSTRAINT "FK_5f9688929761f7df108b630e64a" FOREIGN KEY ("line_item_id") REFERENCES "line_item"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
)
await queryRunner.query(
`ALTER TABLE "order_edit" ADD CONSTRAINT "FK_1f3a251488a91510f57e1bf93cd" FOREIGN KEY ("order_id") REFERENCES "order"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "order_edit" DROP CONSTRAINT "FK_1f3a251488a91510f57e1bf93cd"`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" DROP CONSTRAINT "FK_5f9688929761f7df108b630e64a"`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" DROP CONSTRAINT "FK_b4d53b8d03c9f5e7d4317e818d9"`
)
await queryRunner.query(
`ALTER TABLE "order_item_change" DROP CONSTRAINT "FK_44feeebb258bf4cfa4cc4202281"`
)
await queryRunner.query(`DROP TABLE "order_edit"`)
await queryRunner.query(`DROP TABLE "order_item_change"`)
await queryRunner.query(`DROP TYPE "order_item_change_type_enum"`)
}
}

View File

@@ -0,0 +1,157 @@
import { BeforeInsert, Column, JoinColumn, ManyToOne, OneToMany } from "typeorm"
import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing"
import { FeatureFlagEntity } from "../utils/feature-flag-decorators"
import { resolveDbType } from "../utils/db-aware-column"
import { OrderItemChange } from "./order-item-change"
import { SoftDeletableEntity } from "../interfaces"
import { generateEntityId } from "../utils"
import { LineItem } from "./line-item"
import { Order } from "./order"
@FeatureFlagEntity(OrderEditingFeatureFlag.key)
export class OrderEdit extends SoftDeletableEntity {
@Column()
order_id: string
@ManyToOne(() => Order, (o) => o.edits)
@JoinColumn({ name: "order_id" })
order: Order
@OneToMany(() => OrderItemChange, (oic) => oic.order_edit, {
cascade: true,
})
changes: OrderItemChange[]
@Column({ nullable: true })
internal_note?: string
@Column()
created_by: string // customer or user ID
@Column({ nullable: true })
requested_by?: string // customer or user ID
@Column({ type: resolveDbType("timestamptz"), nullable: true })
requested_at?: Date
@Column({ nullable: true })
confirmed_by?: string // customer or user ID
@Column({ type: resolveDbType("timestamptz"), nullable: true })
confirmed_at?: Date
@Column({ nullable: true })
declined_by?: string // customer or user ID
@Column({ nullable: true })
declined_reason?: string
@Column({ type: resolveDbType("timestamptz"), nullable: true })
declined_at?: Date
@Column({ nullable: true })
canceled_by?: string
@Column({ type: resolveDbType("timestamptz"), nullable: true })
canceled_at?: Date
// Computed
subtotal: number
discount_total?: number
tax_total: number
total: number
difference_due: number
items: LineItem[]
@BeforeInsert()
private beforeInsert(): void {
this.id = generateEntityId(this.id, "oe")
}
}
/**
* @schema order_edit
* title: "Order Edit"
* description: "Order edit keeps track of order items changes."
* x-resourceId: order_edit
* required:
* - order_id
* - order
* - changes
* - created_by
* properties:
* id:
* type: string
* description: The order edit's ID
* example: oe_01G8TJSYT9M6AVS5N4EMNFS1EK
* order_id:
* type: string
* description: The ID of the order that is edited
* example: order_01G2SG30J8C85S4A5CHM2S1NS2
* order:
* description: Order object
* $ref: "#/components/schemas/order"
* changes:
* type: array
* description: Line item changes array.
* items:
* $ref: "#/components/schemas/order_item_changes"
* internal_note:
* description: "An optional note with additional details about the order edit."
* type: string
* example: Included two more items B to the order.
* created_by:
* type: string
* description: "The unique identifier of the user or customer who created the order edit."
* requested_by:
* type: string
* description: "The unique identifier of the user or customer who requested the order edit."
* requested_at:
* type: string
* description: "The date with timezone at which the edit was requested."
* format: date-time
* confirmed_by:
* type: string
* description: "The unique identifier of the user or customer who confirmed the order edit."
* confirmed_at:
* type: string
* description: "The date with timezone at which the edit was confirmed."
* format: date-time
* declined_by:
* type: string
* description: "The unique identifier of the user or customer who declined the order edit."
* declined_at:
* type: string
* description: "The date with timezone at which the edit was declined."
* format: date-time
* declined_reason:
* description: "An optional note why the order edit is declined."
* type: string
* subtotal:
* type: integer
* description: The subtotal for line items computed from changes.
* example: 8000
* discount_total:
* type: integer
* description: The total of discount
* example: 800
* tax_total:
* type: integer
* description: The total of tax
* example: 0
* total:
* type: integer
* description: The total amount of the edited order.
* example: 8200
* difference_due:
* type: integer
* description: The difference between the total amount of the order and total amount of edited order.
* example: 8200
* items:
* type: array
* description: Computed line items from the changes.
* items:
* $ref: "#/components/schemas/line_item"
*/

View File

@@ -0,0 +1,102 @@
import {
BeforeInsert,
Column,
JoinColumn,
ManyToOne,
OneToOne,
Unique,
} from "typeorm"
import { SoftDeletableEntity } from "../interfaces"
import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing"
import { FeatureFlagEntity } from "../utils/feature-flag-decorators"
import { generateEntityId } from "../utils"
import { DbAwareColumn } from "../utils/db-aware-column"
import { OrderEdit } from "./order-edit"
import { LineItem } from "./line-item"
export enum OrderEditItemChangeType {
ITEM_ADD = "item_add",
ITEM_REMOVE = "item_remove",
ITEM_UPDATE = "item_update",
}
@FeatureFlagEntity(OrderEditingFeatureFlag.key)
@Unique(["order_edit_id", "original_line_item_id"])
@Unique(["order_edit_id", "line_item_id"])
export class OrderItemChange extends SoftDeletableEntity {
@DbAwareColumn({
type: "enum",
enum: OrderEditItemChangeType,
})
type: OrderEditItemChangeType
@Column()
order_edit_id: string
@ManyToOne(() => OrderEdit, (oe) => oe.changes)
@JoinColumn({ name: "order_edit_id" })
order_edit: OrderEdit
@Column({ nullable: true })
original_line_item_id?: string
@ManyToOne(() => LineItem, { nullable: true })
@JoinColumn({ name: "original_line_item_id" })
original_line_item?: LineItem
@Column({ nullable: true })
line_item_id?: string
@OneToOne(() => LineItem, { nullable: true })
@JoinColumn({ name: "line_item_id" })
line_item?: LineItem
@BeforeInsert()
private beforeInsert(): void {
this.id = generateEntityId(this.id, "oic")
}
}
/**
* @schema order_item_change
* title: "Order Item Change"
* description: "Represents an order edit item change"
* x-resourceId: order_item_change
* required:
* - type
* - order_edit_id
* properties:
* id:
* type: string
* description: The order item change's ID
* example: oic_01G8TJSYT9M6AVS5N4EMNFS1EK
* type:
* type: string
* description: The order's status
* enum:
* - item_add
* - item_remove
* - item_update
* order_edit_id:
* type: string
* description: The ID of the order edit
* example: oe_01G2SG30J8C85S4A5CHM2S1NS2
* order_edit:
* description: Order edit object
* $ref: "#/components/schemas/order_edit"
* original_line_item_id:
* type: string
* description: The ID of the original line item in the order
* example: item_01G8ZC9GWT6B2GP5FSXRXNFNGN
* original_line_item:
* description: Original line item object.
* $ref: "#/components/schemas/line_item"
* line_item_id:
* type: string
* description: The ID of the cloned line item.
* example: item_01G8ZC9GWT6B2GP5FSXRXNFNGN
* line_item:
* description: Line item object.
* $ref: "#/components/schemas/line_item"
*/

View File

@@ -42,6 +42,8 @@ import { ShippingMethod } from "./shipping-method"
import { Swap } from "./swap"
import { generateEntityId } from "../utils/generate-entity-id"
import { manualAutoIncrement } from "../utils/manual-auto-increment"
import { OrderEdit } from "./order-edit"
import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing"
export enum OrderStatus {
PENDING = "pending",
@@ -208,6 +210,14 @@ export class Order extends BaseEntity {
@JoinColumn({ name: "draft_order_id" })
draft_order: DraftOrder
@FeatureFlagDecorators(OrderEditingFeatureFlag.key, [
OneToMany(
() => OrderEdit,
(oe) => oe.order
),
])
edits: OrderEdit[]
@OneToMany(() => LineItem, (lineItem) => lineItem.order, {
cascade: ["insert"],
})
@@ -406,25 +416,25 @@ export class Order extends BaseEntity {
* description: The returns associated with the order. Available if the relation `returns` is expanded.
* items:
* type: object
* description: A return object.
* description: A return object.
* claims:
* type: array
* description: The claims associated with the order. Available if the relation `claims` is expanded.
* items:
* type: object
* description: A claim order object.
* description: A claim order object.
* refunds:
* type: array
* description: The refunds associated with the order. Available if the relation `refunds` is expanded.
* items:
* type: object
* description: A refund object.
* description: A refund object.
* swaps:
* type: array
* description: The swaps associated with the order. Available if the relation `swaps` is expanded.
* items:
* type: object
* description: A swap object.
* description: A swap object.
* draft_order_id:
* type: string
* description: The ID of the draft order this order is associated with.
@@ -437,6 +447,11 @@ export class Order extends BaseEntity {
* description: The line items that belong to the order. Available if the relation `items` is expanded.
* items:
* $ref: "#/components/schemas/line_item"
* edits:
* type: array
* description: [EXPERIMENTAL] Order edits done on the order. Available if the relation `edits` is expanded.
* items:
* $ref: "#/components/schemas/order_edit"
* gift_card_transactions:
* type: array
* description: The gift card transactions used in the order. Available if the relation `gift_card_transactions` is expanded.

View File

@@ -0,0 +1,6 @@
import { EntityRepository, Repository } from "typeorm"
import { OrderEdit } from "../models/order-edit"
@EntityRepository(OrderEdit)
export class OrderEditRepository extends Repository<OrderEdit> {}

View File

@@ -0,0 +1,6 @@
import { EntityRepository, Repository } from "typeorm"
import { OrderItemChange } from "../models/order-item-change"
@EntityRepository(OrderItemChange)
export class OrderItemChangeRepository extends Repository<OrderItemChange> {}