feat: Add DiscountConditions (#1230)
* feat: Add DiscountCondition entity + Join table per relation (#1146) * feat: Convert DiscountService to TypeScript (#1149) * feat: Add DiscountRepository + bulk insert and remove (#1156) * feat: Add `conditions` to payload in `POST /discounts` and `POST /discounts/:id` (#1170) * feat: Add DiscountRuleCondition entity * fix relation * fix join key * Add discount rule condition repo * add join table per relation * Convert DiscountService to TypeScript * feat: Add DiscountConditionRepository * Add migration + remove use of valid_for * revert changes to files, not done yet * init work on create discount endpoint * Add conditions to create discount endpoint * Add conditions to update discount endpoint * Add unique constraint to discount condition * integration tests passing * fix imports of models * fix tests (excluding totals calculations) * Fix commented code * add unique constraint on discount condition * Add generic way of generating retrieve configs * Requested changes + ExactlyOne validator * Remove isLocal flag from error handler * Use postgres error constant * remove commented code * feat: Add `isValidForProduct` to check if Discount is valid for a given Product (#1172) * feat: Add `canApplyForCustomer` to check if Discount is valid for customer groups (#1212) * feat: Add `calculateDiscountForLineItem` (#1224) * feat: Adds discount condition test factory (#1228) * Remove use of valid_for * Tests passing * Remove valid_for form relations * Add integration tests for applying discounts to cart
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { CustomerGroup } from "./customer-group"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
|
||||
@Entity()
|
||||
export class DiscountConditionCustomerGroup {
|
||||
@PrimaryColumn()
|
||||
customer_group_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
condition_id: string
|
||||
|
||||
@ManyToOne(() => CustomerGroup, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "customer_group_id" })
|
||||
customer_group?: CustomerGroup
|
||||
|
||||
@ManyToOne(() => DiscountCondition, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "condition_id" })
|
||||
discount_condition?: DiscountCondition
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition_customer_group
|
||||
* title: "Product Tag Discount Condition"
|
||||
* description: "Associates a discount condition with a customer group"
|
||||
* x-resourceId: discount_condition_customer_group
|
||||
* properties:
|
||||
* customer_group_id:
|
||||
* description: "The id of the Product Tag"
|
||||
* type: string
|
||||
* condition_id:
|
||||
* description: "The id of the Discount Condition"
|
||||
* type: string
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
import { ProductCollection } from "./product-collection"
|
||||
|
||||
@Entity()
|
||||
export class DiscountConditionProductCollection {
|
||||
@PrimaryColumn()
|
||||
product_collection_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
condition_id: string
|
||||
|
||||
@ManyToOne(() => ProductCollection, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "product_collection_id" })
|
||||
product_collection?: ProductCollection
|
||||
|
||||
@ManyToOne(() => DiscountCondition, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "condition_id" })
|
||||
discount_condition?: DiscountCondition
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition_product_collection
|
||||
* title: "Product Collection Discount Condition"
|
||||
* description: "Associates a discount condition with a product collection"
|
||||
* x-resourceId: discount_condition_product_collection
|
||||
* properties:
|
||||
* product_collection_id:
|
||||
* description: "The id of the Product Collection"
|
||||
* type: string
|
||||
* condition_id:
|
||||
* description: "The id of the Discount Condition"
|
||||
* type: string
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
import { ProductTag } from "./product-tag"
|
||||
|
||||
@Entity()
|
||||
export class DiscountConditionProductTag {
|
||||
@PrimaryColumn()
|
||||
product_tag_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
condition_id: string
|
||||
|
||||
@ManyToOne(() => ProductTag, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "product_tag_id" })
|
||||
product_tag?: ProductTag
|
||||
|
||||
@ManyToOne(() => DiscountCondition, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "condition_id" })
|
||||
discount_condition?: DiscountCondition
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition_product_tag
|
||||
* title: "Product Tag Discount Condition"
|
||||
* description: "Associates a discount condition with a product tag"
|
||||
* x-resourceId: discount_condition_product_tag
|
||||
* properties:
|
||||
* product_tag_id:
|
||||
* description: "The id of the Product Tag"
|
||||
* type: string
|
||||
* condition_id:
|
||||
* description: "The id of the Discount Condition"
|
||||
* type: string
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
import { ProductType } from "./product-type"
|
||||
|
||||
@Entity()
|
||||
export class DiscountConditionProductType {
|
||||
@PrimaryColumn()
|
||||
product_type_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
condition_id: string
|
||||
|
||||
@ManyToOne(() => ProductType, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "product_type_id" })
|
||||
product_type?: ProductType
|
||||
|
||||
@ManyToOne(() => DiscountCondition, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "condition_id" })
|
||||
discount_condition?: DiscountCondition
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition_product_type
|
||||
* title: "Product Type Discount Condition"
|
||||
* description: "Associates a discount condition with a product type"
|
||||
* x-resourceId: discount_condition_product
|
||||
* properties:
|
||||
* product_type_id:
|
||||
* description: "The id of the Product Type"
|
||||
* type: string
|
||||
* condition_id:
|
||||
* description: "The id of the Discount Condition"
|
||||
* type: string
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
import { Product } from "./product"
|
||||
|
||||
@Entity()
|
||||
export class DiscountConditionProduct {
|
||||
@PrimaryColumn()
|
||||
product_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
condition_id: string
|
||||
|
||||
@ManyToOne(() => Product, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "product_id" })
|
||||
product?: Product
|
||||
|
||||
@ManyToOne(() => DiscountCondition, { onDelete: "CASCADE" })
|
||||
@JoinColumn({ name: "condition_id" })
|
||||
discount_condition?: DiscountCondition
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition_product
|
||||
* title: "Product Discount Condition"
|
||||
* description: "Associates a discount condition with a product"
|
||||
* x-resourceId: discount_condition_product
|
||||
* properties:
|
||||
* product_id:
|
||||
* description: "The id of the Product"
|
||||
* type: string
|
||||
* condition_id:
|
||||
* description: "The id of the Discount Condition"
|
||||
* type: string
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -0,0 +1,186 @@
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { ulid } from "ulid"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { CustomerGroup } from "./customer-group"
|
||||
import { DiscountRule } from "./discount-rule"
|
||||
import { Product } from "./product"
|
||||
import { ProductCollection } from "./product-collection"
|
||||
import { ProductTag } from "./product-tag"
|
||||
import { ProductType } from "./product-type"
|
||||
|
||||
export enum DiscountConditionType {
|
||||
PRODUCTS = "products",
|
||||
PRODUCT_TYPES = "product_types",
|
||||
PRODUCT_COLLECTIONS = "product_collections",
|
||||
PRODUCT_TAGS = "product_tags",
|
||||
CUSTOMER_GROUPS = "customer_groups",
|
||||
}
|
||||
|
||||
export enum DiscountConditionOperator {
|
||||
IN = "in",
|
||||
NOT_IN = "not_in",
|
||||
}
|
||||
|
||||
@Entity()
|
||||
@Unique("dctypeuniq", ["type", "operator", "discount_rule_id"])
|
||||
export class DiscountCondition {
|
||||
@PrimaryColumn()
|
||||
id: string
|
||||
|
||||
@DbAwareColumn({
|
||||
type: "enum",
|
||||
enum: DiscountConditionType,
|
||||
})
|
||||
type: DiscountConditionType
|
||||
|
||||
@DbAwareColumn({
|
||||
type: "enum",
|
||||
enum: DiscountConditionOperator,
|
||||
})
|
||||
operator: DiscountConditionOperator
|
||||
|
||||
@Index()
|
||||
@Column()
|
||||
discount_rule_id: string
|
||||
|
||||
@ManyToOne(() => DiscountRule, (dr) => dr.conditions)
|
||||
@JoinColumn({ name: "discount_rule_id" })
|
||||
discount_rule: DiscountRule
|
||||
|
||||
@ManyToMany(() => Product)
|
||||
@JoinTable({
|
||||
name: "discount_condition_product",
|
||||
joinColumn: {
|
||||
name: "condition_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "product_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
products: Product[]
|
||||
|
||||
@ManyToMany(() => ProductType)
|
||||
@JoinTable({
|
||||
name: "discount_condition_product_type",
|
||||
joinColumn: {
|
||||
name: "condition_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "product_type_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
product_types: ProductType[]
|
||||
|
||||
@ManyToMany(() => ProductTag)
|
||||
@JoinTable({
|
||||
name: "discount_condition_product_tag",
|
||||
joinColumn: {
|
||||
name: "condition_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "product_tag_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
product_tags: ProductTag[]
|
||||
|
||||
@ManyToMany(() => ProductCollection)
|
||||
@JoinTable({
|
||||
name: "discount_condition_product_collection",
|
||||
joinColumn: {
|
||||
name: "condition_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "product_collection_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
product_collections: ProductCollection[]
|
||||
|
||||
@ManyToMany(() => CustomerGroup)
|
||||
@JoinTable({
|
||||
name: "discount_condition_customer_group",
|
||||
joinColumn: {
|
||||
name: "condition_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "customer_group_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
customer_groups: CustomerGroup[]
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DeleteDateColumn({ type: resolveDbType("timestamptz") })
|
||||
deleted_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert() {
|
||||
const id = ulid()
|
||||
this.id = `discon_${id}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema discount_condition
|
||||
* title: "Discount Condition"
|
||||
* description: "Holds rule conditions for when a discount is applicable"
|
||||
* x-resourceId: discount_condition
|
||||
* properties:
|
||||
* id:
|
||||
* description: "The id of the Discount Condition. Will be prefixed by `discon_`."
|
||||
* type: string
|
||||
* type:
|
||||
* description: "The type of the Condition"
|
||||
* type: string
|
||||
* enum:
|
||||
* - products
|
||||
* - product_types
|
||||
* - product_collections
|
||||
* - product_tags
|
||||
* - customer_groups
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* update_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -1,19 +1,16 @@
|
||||
import {
|
||||
Entity,
|
||||
BeforeInsert,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Index,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
OneToMany,
|
||||
PrimaryColumn,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { ulid } from "ulid"
|
||||
import { resolveDbType, DbAwareColumn } from "../utils/db-aware-column"
|
||||
|
||||
import { Product } from "./product"
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountCondition } from "./discount-condition"
|
||||
|
||||
export enum DiscountRuleType {
|
||||
FIXED = "fixed",
|
||||
@@ -50,19 +47,8 @@ export class DiscountRule {
|
||||
})
|
||||
allocation: AllocationType
|
||||
|
||||
@ManyToMany(() => Product, { cascade: true })
|
||||
@JoinTable({
|
||||
name: "discount_rule_products",
|
||||
joinColumn: {
|
||||
name: "discount_rule_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "product_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
valid_for: Product[]
|
||||
@OneToMany(() => DiscountCondition, (conditions) => conditions.discount_rule)
|
||||
conditions: DiscountCondition[]
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
@@ -111,11 +97,11 @@ export class DiscountRule {
|
||||
* enum:
|
||||
* - total
|
||||
* - item
|
||||
* valid_for:
|
||||
* description: "A set of Products that the discount can be used for."
|
||||
* conditions:
|
||||
* description: "A set of conditions that can be used to limit when the discount can be used"
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/product"
|
||||
* $ref: "#/components/schemas/discount_condition"
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import {
|
||||
Entity,
|
||||
BeforeInsert,
|
||||
DeleteDateColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
Column,
|
||||
PrimaryColumn,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
JoinTable,
|
||||
JoinColumn,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm"
|
||||
import { ulid } from "ulid"
|
||||
import { resolveDbType, DbAwareColumn } from "../utils/db-aware-column"
|
||||
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||
import { DiscountRule } from "./discount-rule"
|
||||
import { Region } from "./region"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user