feat(payment): Add migration (#6509)

This commit is contained in:
Oli Juhl
2024-02-27 19:05:52 +01:00
committed by GitHub
parent b3d826497b
commit 70aeb602c9
8 changed files with 390 additions and 65 deletions

View File

@@ -141,6 +141,7 @@ moduleIntegrationTestRunner({
])
.catch((e) => e)
// TODO: Change error thrown by Mikro for BigNumber fields
expect(error.message).toContain(
"Value for PaymentCollection.amount is required, 'undefined' found"
)
@@ -462,6 +463,8 @@ moduleIntegrationTestRunner({
}),
payment_session: {
id: expect.any(String),
updated_at: expect.any(Date),
created_at: expect.any(Date),
currency_code: "usd",
amount: 100,
raw_amount: { value: "100", precision: 20 },

View File

@@ -0,0 +1,244 @@
import { generatePostgresAlterColummnIfExistStatement } from "@medusajs/utils"
import { Migration } from "@mikro-orm/migrations"
export class Migration20240225134525 extends Migration {
async up(): Promise<void> {
const paymentCollectionExists = await this.execute(
`SELECT * FROM information_schema.tables where table_name = 'payment_collection' and table_schema = 'public';`
)
if (paymentCollectionExists.length) {
this.addSql(`
${generatePostgresAlterColummnIfExistStatement(
"payment_collection",
["type", "created_by"],
"DROP NOT NULL"
)}
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "completed_at" TIMESTAMPTZ NULL;
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
ALTER TABLE IF EXISTS "payment_provider" ADD COLUMN IF NOT EXISTS "is_enabled" BOOLEAN NOT NULL DEFAULT TRUE;
ALTER TABLE IF EXISTS "payment_session" ADD COLUMN IF NOT EXISTS "payment_collection_id" TEXT NOT NULL;
ALTER TABLE IF EXISTS "payment_session" ADD COLUMN IF NOT EXISTS "payment_authorized_at" TIMESTAMPTZ NULL;
ALTER TABLE IF EXISTS "payment_session" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
ALTER TABLE IF EXISTS "payment" ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMPTZ NULL;
ALTER TABLE IF EXISTS "payment" ADD COLUMN IF NOT EXISTS "payment_collection_id" TEXT NOT NULL;
ALTER TABLE IF EXISTS "payment" ADD COLUMN IF NOT EXISTS "provider_id" TEXT NOT NULL;
ALTER TABLE IF EXISTS "payment" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
ALTER TABLE IF EXISTS "capture" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
ALTER TABLE IF EXISTS "refund" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
CREATE TABLE IF NOT EXISTS "capture" (
"id" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"payment_id" TEXT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"created_by" TEXT NULL,
CONSTRAINT "capture_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment_method_token" (
"id" TEXT NOT NULL,
"provider_id" TEXT NOT NULL,
"data" JSONB NULL,
"name" TEXT NOT NULL,
"type_detail" TEXT NULL,
"description_detail" TEXT NULL,
"metadata" JSONB NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
CONSTRAINT "payment_method_token_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment_collection_payment_providers" (
"payment_collection_id" TEXT NOT NULL,
"payment_provider_id" TEXT NOT NULL,
CONSTRAINT "payment_collection_payment_providers_pkey" PRIMARY KEY ("payment_collection_id", "payment_provider_id")
);
ALTER TABLE IF EXISTS "payment_collection_payment_providers"
ADD CONSTRAINT "payment_collection_payment_providers_payment_coll_aa276_foreign" FOREIGN KEY ("payment_collection_id") REFERENCES "payment_collection" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "payment_collection_payment_providers"
ADD CONSTRAINT "payment_collection_payment_providers_payment_provider_id_foreign" FOREIGN KEY ("payment_provider_id") REFERENCES "payment_provider" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "capture"
ADD CONSTRAINT "capture_payment_id_foreign" FOREIGN KEY ("payment_id") REFERENCES "payment" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "refund"
ADD CONSTRAINT "refund_payment_id_foreign" FOREIGN KEY ("payment_id") REFERENCES "payment" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
CREATE INDEX IF NOT EXISTS "IDX_payment_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_payment_collection_id" ON "payment" ("payment_collection_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_method_token_deleted_at" ON "payment_method_token" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_provider_id" ON "payment" ("provider_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_collection_region_id" ON "payment_collection" ("region_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_collection_deleted_at" ON "payment_collection" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_refund_payment_id" ON "refund" ("payment_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_refund_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_capture_payment_id" ON "capture" ("payment_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_capture_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_session_payment_collection_id" ON "payment_session" ("payment_collection_id") WHERE "deleted_at" IS NULL;
`)
} else {
this.addSql(`
CREATE TABLE IF NOT EXISTS "payment_collection" (
"id" TEXT NOT NULL,
"currency_code" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"region_id" TEXT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
"completed_at" TIMESTAMPTZ NULL,
"status" TEXT CHECK ("status" IN ('not_paid', 'awaiting', 'authorized', 'partially_authorized', 'canceled')) NOT NULL DEFAULT 'not_paid',
"metadata" JSONB NULL,
CONSTRAINT "payment_collection_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment_method_token" (
"id" TEXT NOT NULL,
"provider_id" TEXT NOT NULL,
"data" JSONB NULL,
"name" TEXT NOT NULL,
"type_detail" TEXT NULL,
"description_detail" TEXT NULL,
"metadata" JSONB NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
CONSTRAINT "payment_method_token_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment_provider" (
"id" TEXT NOT NULL,
"is_enabled" BOOLEAN NOT NULL DEFAULT TRUE,
CONSTRAINT "payment_provider_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment_collection_payment_providers" (
"payment_collection_id" TEXT NOT NULL,
"payment_provider_id" TEXT NOT NULL,
CONSTRAINT "payment_collection_payment_providers_pkey" PRIMARY KEY ("payment_collection_id", "payment_provider_id")
);
CREATE TABLE IF NOT EXISTS "payment_session" (
"id" TEXT NOT NULL,
"currency_code" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"provider_id" TEXT NOT NULL,
"data" JSONB NOT NULL,
"status" TEXT CHECK ("status" IN ('authorized', 'pending', 'requires_more', 'error', 'canceled')) NOT NULL DEFAULT 'pending',
"authorized_at" TIMESTAMPTZ NULL,
"payment_collection_id" TEXT NOT NULL,
"metadata" JSONB NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
CONSTRAINT "payment_session_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payment" (
"id" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"currency_code" TEXT NOT NULL,
"provider_id" TEXT NOT NULL,
"cart_id" TEXT NULL,
"order_id" TEXT NULL,
"order_edit_id" TEXT NULL,
"customer_id" TEXT NULL,
"data" JSONB NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
"captured_at" TIMESTAMPTZ NULL,
"canceled_at" TIMESTAMPTZ NULL,
"payment_collection_id" TEXT NOT NULL,
"session_id" TEXT NOT NULL,
"metadata" JSONB NULL,
CONSTRAINT "payment_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "refund" (
"id" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"payment_id" TEXT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
"created_by" TEXT NULL,
"metadata" JSONB NULL,
CONSTRAINT "refund_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "capture" (
"id" TEXT NOT NULL,
"amount" NUMERIC NOT NULL,
"raw_amount" JSONB NOT NULL,
"payment_id" TEXT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deleted_at" TIMESTAMPTZ NULL,
"created_by" TEXT NULL,
"metadata" JSONB NULL,
CONSTRAINT "capture_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "IDX_payment_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_payment_collection_id" ON "payment" ("payment_collection_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_method_token_deleted_at" ON "payment_method_token" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_provider_id" ON "payment" ("provider_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_collection_region_id" ON "payment_collection" ("region_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_collection_deleted_at" ON "payment_collection" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_refund_payment_id" ON "refund" ("payment_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_refund_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_capture_payment_id" ON "capture" ("payment_id") WHERE "deleted_at" IS NULL;
CREATE INDEX IF NOT EXISTS "IDX_capture_deleted_at" ON "payment" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "IDX_payment_session_payment_collection_id" ON "payment_session" ("payment_collection_id") WHERE "deleted_at" IS NULL;
ALTER TABLE IF EXISTS "payment_collection_payment_providers"
ADD CONSTRAINT "payment_collection_payment_providers_payment_coll_aa276_foreign" FOREIGN KEY ("payment_collection_id") REFERENCES "payment_collection" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "payment_collection_payment_providers"
ADD CONSTRAINT "payment_collection_payment_providers_payment_provider_id_foreign" FOREIGN KEY ("payment_provider_id") REFERENCES "payment_provider" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "payment_session"
ADD CONSTRAINT "payment_session_payment_collection_id_foreign" FOREIGN KEY ("payment_collection_id") REFERENCES "payment_collection" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "payment"
ADD CONSTRAINT "payment_payment_collection_id_foreign" FOREIGN KEY ("payment_collection_id") REFERENCES "payment_collection" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "capture"
ADD CONSTRAINT "capture_payment_id_foreign" FOREIGN KEY ("payment_id") REFERENCES "payment" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE IF EXISTS "refund"
ADD CONSTRAINT "refund_payment_id_foreign" FOREIGN KEY ("payment_id") REFERENCES "payment" ("id") ON UPDATE CASCADE ON DELETE CASCADE;
`)
}
}
}

View File

@@ -1,4 +1,9 @@
import { generateEntityId } from "@medusajs/utils"
import { BigNumberRawValue } from "@medusajs/types"
import {
BigNumber,
MikroOrmBigNumberProperty,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
@@ -19,11 +24,11 @@ export default class Capture {
@PrimaryKey({ columnType: "text" })
id: string
@Property({
columnType: "numeric",
serializer: Number,
})
amount: number
@MikroOrmBigNumberProperty()
amount: BigNumber | number
@Property({ columnType: "jsonb" })
raw_amount: BigNumberRawValue
@ManyToOne(() => Payment, {
onDelete: "cascade",
@@ -39,6 +44,21 @@ export default class Capture {
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({
columnType: "timestamptz",
nullable: true,
index: "IDX_capture_deleted_at",
})
deleted_at: Date | null = null
@Property({ columnType: "text", nullable: true })
created_by: string | null = null

View File

@@ -1,3 +1,11 @@
import { BigNumberRawValue, DAL } from "@medusajs/types"
import {
BigNumber,
DALUtils,
MikroOrmBigNumberProperty,
PaymentCollectionStatus,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
@@ -6,23 +14,15 @@ import {
Enum,
Filter,
ManyToMany,
OneToMany,
OnInit,
OneToMany,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { DAL } from "@medusajs/types"
import {
DALUtils,
generateEntityId,
optionalNumericSerializer,
PaymentCollectionStatus,
} from "@medusajs/utils"
import Payment from "./payment"
import PaymentProvider from "./payment-provider"
import PaymentSession from "./payment-session"
import Payment from "./payment"
type OptionalPaymentCollectionProps = "status" | DAL.EntityDateColumns
@@ -37,27 +37,11 @@ export default class PaymentCollection {
@Property({ columnType: "text" })
currency_code: string
@Property({
columnType: "numeric",
serializer: Number,
})
amount: number
@MikroOrmBigNumberProperty()
amount: BigNumber | number
// TODO: make this computed properties
// @Property({
// columnType: "numeric",
// nullable: true,
// serializer: optionalNumericSerializer,
// })
// authorized_amount: number | null = null
//
// @Property({
// columnType: "numeric",
// nullable: true,
// serializer: optionalNumericSerializer,
// })
// refunded_amount: number | null = null
@Property({ columnType: "jsonb" })
raw_amount: BigNumberRawValue
@Property({ columnType: "text", index: "IDX_payment_collection_region_id" })
region_id: string
@@ -109,6 +93,9 @@ export default class PaymentCollection {
})
payments = new Collection<Payment>(this)
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "pay_col")

View File

@@ -1,3 +1,4 @@
import { generateEntityId } from "@medusajs/utils"
import {
BeforeCreate,
Entity,
@@ -6,8 +7,6 @@ import {
Property,
} from "@mikro-orm/core"
import { generateEntityId } from "@medusajs/utils"
@Entity({ tableName: "payment_method_token" })
export default class PaymentMethodToken {
@PrimaryKey({ columnType: "text" })
@@ -28,6 +27,28 @@ export default class PaymentMethodToken {
@Property({ columnType: "text", nullable: true })
description_detail: string | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({
columnType: "timestamptz",
nullable: true,
index: "IDX_payment_metod_token_deleted_at",
})
deleted_at: Date | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null

View File

@@ -1,3 +1,10 @@
import { BigNumberRawValue } from "@medusajs/types"
import {
BigNumber,
generateEntityId,
MikroOrmBigNumberProperty,
PaymentSessionStatus,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
@@ -9,16 +16,8 @@ import {
PrimaryKey,
Property,
} from "@mikro-orm/core"
import {
BigNumber,
generateEntityId,
MikroOrmBigNumberProperty,
PaymentSessionStatus,
} from "@medusajs/utils"
import { BigNumberRawValue } from "@medusajs/types"
import PaymentCollection from "./payment-collection"
import Payment from "./payment"
import PaymentCollection from "./payment-collection"
@Entity({ tableName: "payment_session" })
export default class PaymentSession {
@@ -56,6 +55,7 @@ export default class PaymentSession {
authorized_at: Date | null = null
@ManyToOne({
entity: () => PaymentCollection,
index: "IDX_payment_session_payment_collection_id",
fieldName: "payment_collection_id",
onDelete: "cascade",
@@ -70,6 +70,28 @@ export default class PaymentSession {
})
payment?: Payment | null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({
columnType: "timestamptz",
nullable: true,
index: "IDX_payment_session_deleted_at",
})
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "payses")

View File

@@ -1,3 +1,10 @@
import { BigNumberRawValue, DAL } from "@medusajs/types"
import {
BigNumber,
DALUtils,
MikroOrmBigNumberProperty,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
@@ -5,20 +12,17 @@ import {
Entity,
Filter,
ManyToOne,
OnInit,
OneToMany,
OneToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { DAL } from "@medusajs/types"
import { DALUtils, generateEntityId } from "@medusajs/utils"
import Refund from "./refund"
import Capture from "./capture"
import PaymentSession from "./payment-session"
import PaymentCollection from "./payment-collection"
import PaymentSession from "./payment-session"
import Refund from "./refund"
type OptionalPaymentProps = DAL.EntityDateColumns
@@ -30,11 +34,11 @@ export default class Payment {
@PrimaryKey({ columnType: "text" })
id: string
@Property({
columnType: "numeric",
serializer: Number,
})
amount: number
@MikroOrmBigNumberProperty()
amount: BigNumber | number
@Property({ columnType: "jsonb" })
raw_amount: BigNumberRawValue
@Property({ columnType: "text" })
currency_code: string
@@ -57,6 +61,9 @@ export default class Payment {
@Property({ columnType: "jsonb", nullable: true })
data: Record<string, unknown> | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",

View File

@@ -1,3 +1,9 @@
import { BigNumberRawValue } from "@medusajs/types"
import {
BigNumber,
MikroOrmBigNumberProperty,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
@@ -6,8 +12,6 @@ import {
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { generateEntityId } from "@medusajs/utils"
import Payment from "./payment"
@Entity({ tableName: "refund" })
@@ -15,11 +19,11 @@ export default class Refund {
@PrimaryKey({ columnType: "text" })
id: string
@Property({
columnType: "numeric",
serializer: Number,
})
amount: number
@MikroOrmBigNumberProperty()
amount: BigNumber | number
@Property({ columnType: "jsonb" })
raw_amount: BigNumberRawValue
@ManyToOne(() => Payment, {
onDelete: "cascade",
@@ -35,9 +39,26 @@ export default class Refund {
})
created_at: Date
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({
columnType: "timestamptz",
nullable: true,
index: "IDX_refund_deleted_at",
})
deleted_at: Date | null = null
@Property({ columnType: "text", nullable: true })
created_by: string | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "ref")