feat(medusa): Sales Channels model (#1782)
**What** - added `SalesChannel` entity - added `SalesChannel` repository - added `SalesChannel` relations to the order, cart and store entities - added a migrations file **How** - introduced entities and relations under a new feature flag "sales-channels" Fixes CORE-271 Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
1 parent
9a14b84e58
commit
f9c3218aac
10 files changed
+184
-13
No files matched your search
@@ -0,0 +1,10 @@
|
|||||||
|
import { FlagSettings } from "../../types/feature-flags"
|
||||||
|
|
||||||
|
const SalesChannelFeatureFlag: FlagSettings = {
|
||||||
|
key: "sales_channels",
|
||||||
|
default_val: false,
|
||||||
|
env_key: "MEDUSA_FF_SALES_CHANNELS",
|
||||||
|
description: "[WIP] Enable the sales channels feature",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SalesChannelFeatureFlag
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||||
|
|
||||||
|
export const featureFlag = "sales_channels"
|
||||||
|
|
||||||
|
export class salesChannel1656949291839 implements MigrationInterface {
|
||||||
|
name = "salesChannel1656949291839"
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "sales_channel" ("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, "name" character varying NOT NULL, "description" character varying, "is_disabled" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_d1eb0b923ea5a0eb1e0916191f1" PRIMARY KEY ("id"))`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "product_sales_channel" ("product_id" character varying NOT NULL, "sales_channel_id" character varying NOT NULL, CONSTRAINT "PK_fd29b6a8bd641052628dee19583" PRIMARY KEY ("product_id", "sales_channel_id"))`
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_5a4d5e1e60f97633547821ec8d" ON "product_sales_channel" ("product_id") `
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_37341bad297fe5cca91f921032" ON "product_sales_channel" ("sales_channel_id") `
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "cart" ADD "sales_channel_id" character varying`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "order" ADD "sales_channel_id" character varying`
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "store" ADD "default_sales_channel_id" character varying`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "store" ADD CONSTRAINT "UQ_61b0f48cccbb5f41c750bac7286" UNIQUE ("default_sales_channel_id")`
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "cart" ADD CONSTRAINT "FK_a2bd3c26f42e754b9249ba78fd6" FOREIGN KEY ("sales_channel_id") REFERENCES "sales_channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "order" ADD CONSTRAINT "FK_6ff7e874f01b478c115fdd462eb" FOREIGN KEY ("sales_channel_id") REFERENCES "sales_channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "store" ADD CONSTRAINT "FK_61b0f48cccbb5f41c750bac7286" FOREIGN KEY ("default_sales_channel_id") REFERENCES "sales_channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "product_sales_channel" ADD CONSTRAINT "FK_5a4d5e1e60f97633547821ec8d6" FOREIGN KEY ("product_id") REFERENCES "product"("id") ON DELETE CASCADE ON UPDATE CASCADE`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "product_sales_channel" ADD CONSTRAINT "FK_37341bad297fe5cca91f921032b" FOREIGN KEY ("sales_channel_id") REFERENCES "sales_channel"("id") ON DELETE CASCADE ON UPDATE CASCADE`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "product_sales_channel" DROP CONSTRAINT "FK_37341bad297fe5cca91f921032b"`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "product_sales_channel" DROP CONSTRAINT "FK_5a4d5e1e60f97633547821ec8d6"`
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "store" DROP COLUMN "default_sales_channel_id"`
|
||||||
|
)
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "order" DROP COLUMN "sales_channel_id"`
|
||||||
|
)
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" DROP COLUMN "sales_channel_id"`)
|
||||||
|
await queryRunner.query(`DROP TABLE "product_sales_channel"`)
|
||||||
|
await queryRunner.query(`DROP TABLE "sales_channel"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@ import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
|||||||
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
import { User } from "./user"
|
import { User } from "./user"
|
||||||
import { RequestQueryFields, Selector } from "../types/common"
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class BatchJob extends SoftDeletableEntity {
|
export class BatchJob extends SoftDeletableEntity {
|
||||||
|
|||||||
@@ -107,6 +107,11 @@ import { Region } from "./region"
|
|||||||
import { ShippingMethod } from "./shipping-method"
|
import { ShippingMethod } from "./shipping-method"
|
||||||
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
|
import {
|
||||||
|
FeatureFlagColumn,
|
||||||
|
FeatureFlagDecorators,
|
||||||
|
} from "../utils/feature-flag-decorators"
|
||||||
|
import { SalesChannel } from "./sales-channel"
|
||||||
|
|
||||||
export enum CartType {
|
export enum CartType {
|
||||||
DEFAULT = "default",
|
DEFAULT = "default",
|
||||||
@@ -230,6 +235,15 @@ export class Cart extends SoftDeletableEntity {
|
|||||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown>
|
metadata: Record<string, unknown>
|
||||||
|
|
||||||
|
@FeatureFlagColumn("sales_channels", { type: "varchar", nullable: true })
|
||||||
|
sales_channel_id: string | null
|
||||||
|
|
||||||
|
@FeatureFlagDecorators("sales_channels", [
|
||||||
|
ManyToOne(() => SalesChannel),
|
||||||
|
JoinColumn({ name: "sales_channel_id" }),
|
||||||
|
])
|
||||||
|
sales_channel: SalesChannel
|
||||||
|
|
||||||
shipping_total?: number
|
shipping_total?: number
|
||||||
discount_total?: number
|
discount_total?: number
|
||||||
tax_total?: number | null
|
tax_total?: number | null
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ import { Return } from "./return"
|
|||||||
import { ShippingMethod } from "./shipping-method"
|
import { ShippingMethod } from "./shipping-method"
|
||||||
import { Swap } from "./swap"
|
import { Swap } from "./swap"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
|
import {
|
||||||
|
FeatureFlagColumn,
|
||||||
|
FeatureFlagDecorators,
|
||||||
|
} from "../utils/feature-flag-decorators"
|
||||||
|
import { SalesChannel } from "./sales-channel"
|
||||||
|
|
||||||
export enum OrderStatus {
|
export enum OrderStatus {
|
||||||
PENDING = "pending",
|
PENDING = "pending",
|
||||||
@@ -225,6 +230,15 @@ export class Order extends BaseEntity {
|
|||||||
@Column({ type: "varchar", nullable: true })
|
@Column({ type: "varchar", nullable: true })
|
||||||
external_id: string | null
|
external_id: string | null
|
||||||
|
|
||||||
|
@FeatureFlagColumn("sales_channels", { type: "varchar", nullable: true })
|
||||||
|
sales_channel_id: string | null
|
||||||
|
|
||||||
|
@FeatureFlagDecorators("sales_channels", [
|
||||||
|
ManyToOne(() => SalesChannel),
|
||||||
|
JoinColumn({ name: "sales_channel_id" }),
|
||||||
|
])
|
||||||
|
sales_channel: SalesChannel
|
||||||
|
|
||||||
// Total fields
|
// Total fields
|
||||||
shipping_total: number
|
shipping_total: number
|
||||||
discount_total: number
|
discount_total: number
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
|
OneToOne,
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
import { DbAwareColumn } from "../utils/db-aware-column"
|
import { DbAwareColumn } from "../utils/db-aware-column"
|
||||||
import { Image } from "./image"
|
import { Image } from "./image"
|
||||||
@@ -20,6 +21,11 @@ import { ProductVariant } from "./product-variant"
|
|||||||
import { ShippingProfile } from "./shipping-profile"
|
import { ShippingProfile } from "./shipping-profile"
|
||||||
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
|
import {
|
||||||
|
FeatureFlagColumn,
|
||||||
|
FeatureFlagDecorators,
|
||||||
|
} from "../utils/feature-flag-decorators"
|
||||||
|
import { SalesChannel } from "./sales-channel"
|
||||||
|
|
||||||
export enum ProductStatus {
|
export enum ProductStatus {
|
||||||
DRAFT = "draft",
|
DRAFT = "draft",
|
||||||
@@ -66,19 +72,12 @@ export class Product extends SoftDeletableEntity {
|
|||||||
@Column({ type: "text", nullable: true })
|
@Column({ type: "text", nullable: true })
|
||||||
thumbnail: string | null
|
thumbnail: string | null
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(() => ProductOption, (productOption) => productOption.product)
|
||||||
() => ProductOption,
|
|
||||||
(productOption) => productOption.product
|
|
||||||
)
|
|
||||||
options: ProductOption[]
|
options: ProductOption[]
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(() => ProductVariant, (variant) => variant.product, {
|
||||||
() => ProductVariant,
|
cascade: true,
|
||||||
(variant) => variant.product,
|
})
|
||||||
{
|
|
||||||
cascade: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
variants: ProductVariant[]
|
variants: ProductVariant[]
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@@ -150,6 +149,22 @@ export class Product extends SoftDeletableEntity {
|
|||||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
|
|
||||||
|
@FeatureFlagDecorators("sales_channels", [
|
||||||
|
ManyToMany(() => SalesChannel, { cascade: true }),
|
||||||
|
JoinTable({
|
||||||
|
name: "product_sales_channel",
|
||||||
|
joinColumn: {
|
||||||
|
name: "product_id",
|
||||||
|
referencedColumnName: "id",
|
||||||
|
},
|
||||||
|
inverseJoinColumn: {
|
||||||
|
name: "sales_channel_id",
|
||||||
|
referencedColumnName: "id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
sales_channels: SalesChannel[]
|
||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert(): void {
|
private beforeInsert(): void {
|
||||||
if (this.id) return
|
if (this.id) return
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { BeforeInsert, Column } from "typeorm"
|
||||||
|
|
||||||
|
import { SoftDeletableEntity } from "../interfaces"
|
||||||
|
import { FeatureFlagEntity } from "../utils/feature-flag-decorators"
|
||||||
|
import { resolveDbType } from "../utils/db-aware-column"
|
||||||
|
import { generateEntityId } from "../utils"
|
||||||
|
|
||||||
|
@FeatureFlagEntity("sales_channels")
|
||||||
|
export class SalesChannel extends SoftDeletableEntity {
|
||||||
|
@Column()
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@Column({ type: "varchar", nullable: true })
|
||||||
|
description: string | null
|
||||||
|
|
||||||
|
@Column({ default: false })
|
||||||
|
is_disabled: boolean
|
||||||
|
|
||||||
|
// @Column({ type: resolveDbType("timestamptz"), nullable: true })
|
||||||
|
// disabled_at: Date | null
|
||||||
|
|
||||||
|
@BeforeInsert()
|
||||||
|
private beforeInsert(): void {
|
||||||
|
this.id = generateEntityId(this.id, "sc")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,18 @@ import {
|
|||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OneToOne,
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
import { BaseEntity } from "../interfaces/models/base-entity"
|
import { BaseEntity } from "../interfaces/models/base-entity"
|
||||||
import { DbAwareColumn } from "../utils/db-aware-column"
|
import { DbAwareColumn } from "../utils/db-aware-column"
|
||||||
|
|
||||||
import { Currency } from "./currency"
|
import { Currency } from "./currency"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
|
import { SalesChannel } from "./sales-channel"
|
||||||
|
import {
|
||||||
|
FeatureFlagColumn,
|
||||||
|
FeatureFlagDecorators,
|
||||||
|
} from "../utils/feature-flag-decorators"
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Store extends BaseEntity {
|
export class Store extends BaseEntity {
|
||||||
@@ -51,6 +57,15 @@ export class Store extends BaseEntity {
|
|||||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown>
|
metadata: Record<string, unknown>
|
||||||
|
|
||||||
|
@FeatureFlagColumn("sales_channels", { nullable: true })
|
||||||
|
default_sales_channel_id: string
|
||||||
|
|
||||||
|
@FeatureFlagDecorators("sales_channels", [
|
||||||
|
OneToOne(() => SalesChannel),
|
||||||
|
JoinColumn({ name: "default_sales_channel_id" }),
|
||||||
|
])
|
||||||
|
default_sales_channel: SalesChannel
|
||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert(): void {
|
private beforeInsert(): void {
|
||||||
this.id = generateEntityId(this.id, "store")
|
this.id = generateEntityId(this.id, "store")
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from "typeorm"
|
||||||
|
import { SalesChannel } from "../models/sales-channel"
|
||||||
|
|
||||||
|
@EntityRepository(SalesChannel)
|
||||||
|
export class SalesChannelRepository extends Repository<SalesChannel> {}
|
||||||
@@ -7,7 +7,7 @@ import { FlagRouter } from "./flag-router"
|
|||||||
|
|
||||||
export function FeatureFlagColumn(
|
export function FeatureFlagColumn(
|
||||||
featureFlag: string,
|
featureFlag: string,
|
||||||
columnOptions: ColumnOptions
|
columnOptions: ColumnOptions = {}
|
||||||
): PropertyDecorator {
|
): PropertyDecorator {
|
||||||
const featureFlagRouter = getFeatureFlagRouter()
|
const featureFlagRouter = getFeatureFlagRouter()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user