diff --git a/packages/twenty-server/src/database/typeorm/core/core.datasource.ts b/packages/twenty-server/src/database/typeorm/core/core.datasource.ts index 1f12bb4cd1..0c052261c7 100644 --- a/packages/twenty-server/src/database/typeorm/core/core.datasource.ts +++ b/packages/twenty-server/src/database/typeorm/core/core.datasource.ts @@ -18,9 +18,15 @@ export const typeORMCoreModuleOptions: TypeOrmModuleOptions = { migrationsRun: false, migrationsTableName: '_typeorm_migrations', metadataTableName: '_typeorm_generated_columns_and_materialized_views', - migrations: [ - `${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/*{.ts,.js}`, - ], + migrations: + process.env.IS_BILLING_ENABLED === 'true' + ? [ + `${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/common/*{.ts,.js}`, + `${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/billing/*{.ts,.js}`, + ] + : [ + `${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/common/*{.ts,.js}`, + ], ssl: process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true' ? { diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1710926613773-addIntervalToBillingSubscription.ts b/packages/twenty-server/src/database/typeorm/core/migrations/1710926613773-addIntervalToBillingSubscription.ts deleted file mode 100644 index 441cf54d07..0000000000 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1710926613773-addIntervalToBillingSubscription.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddIntervalToBillingSubscription1710926613773 - implements MigrationInterface -{ - name = 'AddIntervalToBillingSubscription1710926613773'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ADD "interval" character varying`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" DROP COLUMN "interval"`, - ); - } -} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1708535112230-addBillingCoreTables.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1708535112230-addBillingCoreTables.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1708535112230-addBillingCoreTables.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/billing/1708535112230-addBillingCoreTables.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1709233666080-updateBillingCoreTables.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1709233666080-updateBillingCoreTables.ts similarity index 81% rename from packages/twenty-server/src/database/typeorm/core/migrations/1709233666080-updateBillingCoreTables.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/billing/1709233666080-updateBillingCoreTables.ts index fea0f40c44..ac841b4fe3 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1709233666080-updateBillingCoreTables.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1709233666080-updateBillingCoreTables.ts @@ -15,6 +15,9 @@ export class UpdateBillingCoreTables1709233666080 await queryRunner.query( `ALTER TABLE "core"."billingSubscriptionItem" ADD CONSTRAINT "IndexOnBillingSubscriptionIdAndStripeProductIdUnique" UNIQUE ("billingSubscriptionId", "stripeProductId")`, ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`, + ); } public async down(queryRunner: QueryRunner): Promise { @@ -27,5 +30,8 @@ export class UpdateBillingCoreTables1709233666080 await queryRunner.query( `ALTER TABLE "core"."billingSubscriptionItem" DROP COLUMN "stripeSubscriptionItemId"`, ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`, + ); } } diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1709914564361-updateBillingSubscription.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1709914564361-updateBillingSubscription.ts similarity index 53% rename from packages/twenty-server/src/database/typeorm/core/migrations/1709914564361-updateBillingSubscription.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/billing/1709914564361-updateBillingSubscription.ts index 0505b688b9..003ac10425 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1709914564361-updateBillingSubscription.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1709914564361-updateBillingSubscription.ts @@ -18,9 +18,27 @@ export class UpdateBillingSubscription1709914564361 await queryRunner.query( `ALTER TABLE "core"."billingSubscription" ADD CONSTRAINT "FK_4abfb70314c18da69e1bee1954d" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE text`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingSubscription_status_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE "core"."billingSubscription_status_enum" USING "status"::"core"."billingSubscription_status_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" SET NOT NULL`, + ); } public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" DROP NOT NULL`, + ); await queryRunner.query( `ALTER TABLE "core"."billingSubscription" DROP CONSTRAINT "FK_4abfb70314c18da69e1bee1954d"`, ); @@ -33,5 +51,17 @@ export class UpdateBillingSubscription1709914564361 await queryRunner.query( `ALTER TABLE "core"."billingSubscription" ADD CONSTRAINT "FK_4abfb70314c18da69e1bee1954d" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE text`, + ); + await queryRunner.query( + `DROP TYPE "core"."billingSubscription_status_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE character varying`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`, + ); } } diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/billing/1710926613773-addIntervalToBillingSubscription.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1710926613773-addIntervalToBillingSubscription.ts new file mode 100644 index 0000000000..5d2a4dcfaa --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1710926613773-addIntervalToBillingSubscription.ts @@ -0,0 +1,34 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIntervalToBillingSubscription1710926613773 + implements MigrationInterface +{ + name = 'AddIntervalToBillingSubscription1710926613773'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "interval" character varying`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE text`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingSubscription_interval_enum" AS ENUM('day', 'month', 'week', 'year')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE "core"."billingSubscription_interval_enum" USING "interval"::"core"."billingSubscription_interval_enum"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE text`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "interval"`, + ); + await queryRunner.query( + `DROP TYPE "core"."billingSubscription_interval_enum"`, + ); + } +} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1732098580545-addBillingEntitlementTable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1732098580545-addBillingEntitlementTable.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1732098580545-addBillingEntitlementTable.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/billing/1732098580545-addBillingEntitlementTable.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/billing/1733397937967-addNewBillingStripeTables.ts b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1733397937967-addNewBillingStripeTables.ts new file mode 100644 index 0000000000..2f9905ca36 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/billing/1733397937967-addNewBillingStripeTables.ts @@ -0,0 +1,187 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddNewBillingStripeTables1733397937967 + implements MigrationInterface +{ + name = 'AddNewBillingStripeTables1733397937967'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "core"."billingCustomer" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "deletedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "workspaceId" uuid NOT NULL, "stripeCustomerId" character varying NOT NULL, CONSTRAINT "UQ_b35a0ef2e2f0d40101dd7f161b9" UNIQUE ("stripeCustomerId"), CONSTRAINT "IndexOnWorkspaceIdAndStripeCustomerIdUnique" UNIQUE ("workspaceId", "stripeCustomerId"), CONSTRAINT "PK_5fffcd69bf722c297a3d5c3f3bc" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingMeter_status_enum" AS ENUM('ACTIVE', 'INACTIVE')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingMeter_eventtimewindow_enum" AS ENUM('DAY', 'HOUR')`, + ); + await queryRunner.query( + `CREATE TABLE "core"."billingMeter" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "deletedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "stripeMeterId" character varying NOT NULL, "displayName" character varying NOT NULL, "eventName" character varying NOT NULL, "status" "core"."billingMeter_status_enum" NOT NULL, "customerMapping" jsonb NOT NULL, "eventTimeWindow" "core"."billingMeter_eventtimewindow_enum", "valueSettings" jsonb NOT NULL, CONSTRAINT "UQ_340c08c4e5dd33cf963cbb133ae" UNIQUE ("stripeMeterId"), CONSTRAINT "PK_0bba5f7d2e3713332a0138ea1b3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "core"."billingProduct" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "deletedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "active" boolean NOT NULL, "description" text, "name" character varying NOT NULL, "taxCode" text, "images" jsonb NOT NULL DEFAULT '[]', "marketingFeatures" jsonb NOT NULL DEFAULT '[]', "stripeProductId" character varying NOT NULL, "defaultStripePriceId" text, "metadata" jsonb NOT NULL DEFAULT '{}', "unitLabel" text, "url" text, CONSTRAINT "UQ_1ba1ba118792aa9eec92f132e82" UNIQUE ("stripeProductId"), CONSTRAINT "PK_8bb3c7be66db8e05476808b0ca7" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_taxbehavior_enum" AS ENUM('EXCLUSIVE', 'INCLUSIVE', 'UNSPECIFIED')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_type_enum" AS ENUM('ONE_TIME', 'RECURRING')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_billingscheme_enum" AS ENUM('PER_UNIT', 'TIERED')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_tiersmode_enum" AS ENUM('GRADUATED', 'VOLUME')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_usagetype_enum" AS ENUM('METERED', 'LICENSED')`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingPrice_interval_enum" AS ENUM('day', 'month', 'week', 'year')`, + ); + await queryRunner.query( + `CREATE TABLE "core"."billingPrice" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "deletedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "stripePriceId" character varying NOT NULL, "active" boolean NOT NULL, "stripeProductId" character varying NOT NULL, "currency" character varying NOT NULL, "nickname" text, "taxBehavior" "core"."billingPrice_taxbehavior_enum" NOT NULL, "type" "core"."billingPrice_type_enum" NOT NULL, "billingScheme" "core"."billingPrice_billingscheme_enum" NOT NULL, "currencyOptions" jsonb, "tiers" jsonb, "recurring" jsonb, "transformQuantity" jsonb, "tiersMode" "core"."billingPrice_tiersmode_enum", "unitAmountDecimal" text, "unitAmount" numeric, "stripeMeterId" character varying, "usageType" "core"."billingPrice_usagetype_enum" NOT NULL, "interval" "core"."billingPrice_interval_enum", CONSTRAINT "UQ_f66d20a329f5f4b9d12afeae7d0" UNIQUE ("stripePriceId"), CONSTRAINT "PK_13927aef8d4e68e176a61c33d89" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" ADD "stripeSubscriptionId" character varying`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" ADD "metadata" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" ADD "billingThresholds" jsonb`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "cancelAtPeriodEnd" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "currency" character varying NOT NULL DEFAULT 'USD'`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "currentPeriodEnd" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "currentPeriodStart" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "metadata" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "cancelAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "canceledAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "automaticTax" jsonb`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "cancellationDetails" jsonb`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingSubscription_collectionmethod_enum" AS ENUM('CHARGE_AUTOMATICALLY', 'SEND_INVOICE')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "collectionMethod" "core"."billingSubscription_collectionmethod_enum" NOT NULL DEFAULT 'CHARGE_AUTOMATICALLY'`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "endedAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "trialStart" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ADD "trialEnd" TIMESTAMP WITH TIME ZONE`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."billingCustomer" ADD CONSTRAINT "FK_53c2ef50e9611082f83d760897d" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."billingPrice" ADD CONSTRAINT "FK_4d57ee4dbfc8b4075eb24026fca" FOREIGN KEY ("stripeProductId") REFERENCES "core"."billingProduct"("stripeProductId") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingPrice" ADD CONSTRAINT "FK_c8b4375b7bf8724ba54065372e1" FOREIGN KEY ("stripeMeterId") REFERENCES "core"."billingMeter"("stripeMeterId") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."billingPrice" DROP CONSTRAINT "FK_c8b4375b7bf8724ba54065372e1"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingPrice" DROP CONSTRAINT "FK_4d57ee4dbfc8b4075eb24026fca"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingCustomer" DROP CONSTRAINT "FK_53c2ef50e9611082f83d760897d"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "trialEnd"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "trialStart"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "endedAt"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "collectionMethod"`, + ); + await queryRunner.query( + `DROP TYPE "core"."billingSubscription_collectionmethod_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "cancellationDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "automaticTax"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "canceledAt"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "cancelAt"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "metadata"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "currentPeriodStart"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "currentPeriodEnd"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "currency"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" DROP COLUMN "cancelAtPeriodEnd"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" DROP COLUMN "billingThresholds"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" DROP COLUMN "metadata"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscriptionItem" DROP COLUMN "stripeSubscriptionId"`, + ); + await queryRunner.query(`DROP TABLE "core"."billingPrice"`); + await queryRunner.query(`DROP TYPE "core"."billingPrice_interval_enum"`); + await queryRunner.query(`DROP TYPE "core"."billingPrice_usagetype_enum"`); + await queryRunner.query(`DROP TYPE "core"."billingPrice_tiersmode_enum"`); + await queryRunner.query( + `DROP TYPE "core"."billingPrice_billingscheme_enum"`, + ); + await queryRunner.query(`DROP TYPE "core"."billingPrice_type_enum"`); + await queryRunner.query(`DROP TYPE "core"."billingPrice_taxbehavior_enum"`); + await queryRunner.query(`DROP TABLE "core"."billingProduct"`); + await queryRunner.query(`DROP TABLE "core"."billingMeter"`); + await queryRunner.query( + `DROP TYPE "core"."billingMeter_eventtimewindow_enum"`, + ); + await queryRunner.query(`DROP TYPE "core"."billingMeter_status_enum"`); + await queryRunner.query(`DROP TABLE "core"."billingCustomer"`); + } +} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1700324860820-setupCoreTables.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1700324860820-setupCoreTables.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1700324860820-setupCoreTables.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1700324860820-setupCoreTables.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1700654387203-addAllowImpersonationToWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1700654387203-addAllowImpersonationToWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1700654387203-addAllowImpersonationToWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1700654387203-addAllowImpersonationToWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1701194529853-addFeatureFlags.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1701194529853-addFeatureFlags.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1701194529853-addFeatureFlags.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1701194529853-addFeatureFlags.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1702479005171-addSubscriptionStatusOnWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1702479005171-addSubscriptionStatusOnWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1702479005171-addSubscriptionStatusOnWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1702479005171-addSubscriptionStatusOnWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1704825571702-addPasswordResetToken.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1704825571702-addPasswordResetToken.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1704825571702-addPasswordResetToken.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1704825571702-addPasswordResetToken.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1706613419989-addDefaultAvatarUrlToUser.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1706613419989-addDefaultAvatarUrlToUser.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1706613419989-addDefaultAvatarUrlToUser.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1706613419989-addDefaultAvatarUrlToUser.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1707778127558-addUserWorkspaces.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1707778127558-addUserWorkspaces.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1707778127558-addUserWorkspaces.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1707778127558-addUserWorkspaces.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1709314035408-updateUserWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1709314035408-updateUserWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1709314035408-updateUserWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1709314035408-updateUserWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1709680520888-updateUserWorkspaceColumnConstraints.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1709680520888-updateUserWorkspaceColumnConstraints.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1709680520888-updateUserWorkspaceColumnConstraints.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1709680520888-updateUserWorkspaceColumnConstraints.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1711557405330-addMissingMigration.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1711557405330-addMissingMigration.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1711557405330-addMissingMigration.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1711557405330-addMissingMigration.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1711624086253-updateRefreshTokenTable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1711624086253-updateRefreshTokenTable.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1711624086253-updateRefreshTokenTable.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1711624086253-updateRefreshTokenTable.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1711633823798-useTimestampWithTZ.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1711633823798-useTimestampWithTZ.ts similarity index 81% rename from packages/twenty-server/src/database/typeorm/core/migrations/1711633823798-useTimestampWithTZ.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1711633823798-useTimestampWithTZ.ts index d2937d222f..97bcbbcf9b 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1711633823798-useTimestampWithTZ.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1711633823798-useTimestampWithTZ.ts @@ -10,12 +10,7 @@ export class UseTimestampWithTZ1711633823798 implements MigrationInterface { await queryRunner.query( `ALTER TABLE "core"."featureFlag" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`, ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`, - ); + await queryRunner.query( `ALTER TABLE "core"."workspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`, ); @@ -43,12 +38,7 @@ export class UseTimestampWithTZ1711633823798 implements MigrationInterface { await queryRunner.query( `ALTER TABLE "core"."featureFlag" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`, ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`, - ); + await queryRunner.query( `ALTER TABLE "core"."workspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`, ); diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1715593226719-updateInconsistentUserConstraint.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1715593226719-updateInconsistentUserConstraint.ts similarity index 72% rename from packages/twenty-server/src/database/typeorm/core/migrations/1715593226719-updateInconsistentUserConstraint.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1715593226719-updateInconsistentUserConstraint.ts index ace85a0c54..b098881e0a 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1715593226719-updateInconsistentUserConstraint.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1715593226719-updateInconsistentUserConstraint.ts @@ -9,12 +9,7 @@ export class UpdateInconsistentUserConstraint1715593226719 await queryRunner.query( `ALTER TABLE "core"."user" DROP CONSTRAINT "FK_2ec910029395fa7655621c88908"`, ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE text`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE text`, - ); + await queryRunner.query( `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" TYPE text`, ); @@ -30,12 +25,6 @@ export class UpdateInconsistentUserConstraint1715593226719 await queryRunner.query( `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" TYPE character varying`, ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE character varying`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE character varying`, - ); await queryRunner.query( `ALTER TABLE "core"."user" ADD CONSTRAINT "FK_2ec910029395fa7655621c88908" FOREIGN KEY ("defaultWorkspaceId") REFERENCES "core"."workspace"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, ); diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1717425967770-addKeyValuePairTable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1717425967770-addKeyValuePairTable.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1717425967770-addKeyValuePairTable.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1717425967770-addKeyValuePairTable.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1717688588972-addKeyValuePairTableUniqueContraints.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1717688588972-addKeyValuePairTableUniqueContraints.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1717688588972-addKeyValuePairTableUniqueContraints.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1717688588972-addKeyValuePairTableUniqueContraints.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1717751631669-addPostgresCredentials.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1717751631669-addPostgresCredentials.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1717751631669-addPostgresCredentials.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1717751631669-addPostgresCredentials.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1719327438923-useEnumForSubscriptionStatusInterval.ts similarity index 51% rename from packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1719327438923-useEnumForSubscriptionStatusInterval.ts index c2894f0fd4..440858104d 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1719327438923-useEnumForSubscriptionStatusInterval.ts @@ -6,21 +6,6 @@ export class UseEnumForSubscriptionStatusInterval1719327438923 name = 'UseEnumForSubscriptionStatusInterval1719327438923'; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "core"."billingSubscription_status_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE "core"."billingSubscription_status_enum" USING "status"::"core"."billingSubscription_status_enum"`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" SET NOT NULL`, - ); - await queryRunner.query( - `CREATE TYPE "core"."billingSubscription_interval_enum" AS ENUM('day', 'month', 'week', 'year')`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE "core"."billingSubscription_interval_enum" USING "interval"::"core"."billingSubscription_interval_enum"`, - ); await queryRunner.query( `CREATE TYPE "core"."workspace_subscriptionstatus_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, ); @@ -45,17 +30,5 @@ export class UseEnumForSubscriptionStatusInterval1719327438923 await queryRunner.query( `DROP TYPE "core"."workspace_subscriptionstatus_enum"`, ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE text`, - ); - await queryRunner.query( - `DROP TYPE "core"."billingSubscription_interval_enum"`, - ); - await queryRunner.query( - `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE text`, - ); - await queryRunner.query( - `DROP TYPE "core"."billingSubscription_status_enum"`, - ); } } diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1721139150487-addKeyValuePairType.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1721139150487-addKeyValuePairType.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1721139150487-addKeyValuePairType.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1721139150487-addKeyValuePairType.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1721656106498-migrateKeyValueTypeToJsonb.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1721656106498-migrateKeyValueTypeToJsonb.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1721656106498-migrateKeyValueTypeToJsonb.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1721656106498-migrateKeyValueTypeToJsonb.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1721738579379-DeletePasswordResetToken.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1721738579379-DeletePasswordResetToken.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1721738579379-DeletePasswordResetToken.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1721738579379-DeletePasswordResetToken.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1722256203539-addActivationStatus.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203539-addActivationStatus.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1722256203539-addActivationStatus.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203539-addActivationStatus.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1722256203540-updateActivationStatusEnum copy.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203540-updateActivationStatusEnum copy.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1722256203540-updateActivationStatusEnum copy.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203540-updateActivationStatusEnum copy.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1722256203541-updateActivationStatusEnumPendingCreation.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203541-updateActivationStatusEnumPendingCreation.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1722256203541-updateActivationStatusEnumPendingCreation.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1722256203541-updateActivationStatusEnumPendingCreation.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1724056827317-addInvitation.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1724056827317-addInvitation.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1724056827317-addInvitation.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1724056827317-addInvitation.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1724173430043-introduceMetadataVersionAndDatasourceOnWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1724173430043-introduceMetadataVersionAndDatasourceOnWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1724173430043-introduceMetadataVersionAndDatasourceOnWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1724173430043-introduceMetadataVersionAndDatasourceOnWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1726849473832-addUniqueConstraintOnUsers.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1726849473832-addUniqueConstraintOnUsers.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1726849473832-addUniqueConstraintOnUsers.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1726849473832-addUniqueConstraintOnUsers.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1727181198403-addWorkspaceSSOIdentityProvider.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1727181198403-addWorkspaceSSOIdentityProvider.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1727181198403-addWorkspaceSSOIdentityProvider.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1727181198403-addWorkspaceSSOIdentityProvider.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1728314605995-add_typeormGeneratedColumnsAndMaterializedViews.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1728314605995-add_typeormGeneratedColumnsAndMaterializedViews.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1728314605995-add_typeormGeneratedColumnsAndMaterializedViews.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1728314605995-add_typeormGeneratedColumnsAndMaterializedViews.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1728986317196-addIsPublicInviteLinkEnabledOnWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1728986317196-addIsPublicInviteLinkEnabledOnWorkspace.ts similarity index 100% rename from packages/twenty-server/src/database/typeorm/core/migrations/1728986317196-addIsPublicInviteLinkEnabledOnWorkspace.ts rename to packages/twenty-server/src/database/typeorm/core/migrations/common/1728986317196-addIsPublicInviteLinkEnabledOnWorkspace.ts diff --git a/packages/twenty-server/src/database/typeorm/typeorm.service.ts b/packages/twenty-server/src/database/typeorm/typeorm.service.ts index 2f69306092..025fbe2b0a 100644 --- a/packages/twenty-server/src/database/typeorm/typeorm.service.ts +++ b/packages/twenty-server/src/database/typeorm/typeorm.service.ts @@ -3,7 +3,11 @@ import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; +import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingEntitlement } from 'src/engine/core-modules/billing/entities/billing-entitlement.entity'; +import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity'; +import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity'; +import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; @@ -36,6 +40,10 @@ export class TypeORMService implements OnModuleInit, OnModuleDestroy { FeatureFlagEntity, BillingSubscription, BillingSubscriptionItem, + BillingMeter, + BillingCustomer, + BillingProduct, + BillingPrice, BillingEntitlement, PostgresCredentials, WorkspaceSSOIdentityProvider, diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts index 182b71a404..0664c74682 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts @@ -3,7 +3,11 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { BillingController } from 'src/engine/core-modules/billing/billing.controller'; import { BillingResolver } from 'src/engine/core-modules/billing/billing.resolver'; +import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingEntitlement } from 'src/engine/core-modules/billing/entities/billing-entitlement.entity'; +import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity'; +import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity'; +import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { BillingWorkspaceMemberListener } from 'src/engine/core-modules/billing/listeners/billing-workspace-member.listener'; @@ -27,6 +31,10 @@ import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/doma [ BillingSubscription, BillingSubscriptionItem, + BillingCustomer, + BillingProduct, + BillingPrice, + BillingMeter, BillingEntitlement, Workspace, UserWorkspace, diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-customer.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-customer.entity.ts new file mode 100644 index 0000000000..f1b237b7cc --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-customer.entity.ts @@ -0,0 +1,65 @@ +import { ObjectType } from '@nestjs/graphql'; + +import { IDField } from '@ptc-org/nestjs-query-graphql'; +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + PrimaryGeneratedColumn, + Relation, + Unique, + UpdateDateColumn, +} from 'typeorm'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { BillingEntitlement } from 'src/engine/core-modules/billing/entities/billing-entitlement.entity'; +import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; + +@Entity({ name: 'billingCustomer', schema: 'core' }) +@ObjectType('billingCustomer') +@Unique('IndexOnWorkspaceIdAndStripeCustomerIdUnique', [ + 'workspaceId', + 'stripeCustomerId', +]) +export class BillingCustomer { + @IDField(() => UUIDScalarType) + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: true, type: 'timestamptz' }) + deletedAt?: Date; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @ManyToOne(() => Workspace, (workspace) => workspace.billingCustomers, { + onDelete: 'CASCADE', + }) + @JoinColumn() + workspace: Relation; + + @Column({ nullable: false, type: 'uuid' }) + workspaceId: string; + + @Column({ nullable: false, unique: true }) + stripeCustomerId: string; + + @OneToMany( + () => BillingSubscription, + (billingSubscription) => billingSubscription.billingCustomer, + ) + billingSubscriptions: Relation; + + @OneToMany( + () => BillingEntitlement, + (billingEntitlement) => billingEntitlement.billingCustomer, + ) + billingEntitlements: Relation; +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-entitlement.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-entitlement.entity.ts index 4bb831fdcb..927281f3ef 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-entitlement.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-entitlement.entity.ts @@ -14,6 +14,7 @@ import { } from 'typeorm'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/billing-entitlement-key.enum'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @Entity({ name: 'billingEntitlement', schema: 'core' }) @@ -53,4 +54,17 @@ export class BillingEntitlement { @Column({ nullable: true, type: 'timestamptz' }) deletedAt?: Date; + @ManyToOne( + () => BillingCustomer, + (billingCustomer) => billingCustomer.billingEntitlements, + { + onDelete: 'CASCADE', + createForeignKeyConstraints: false, // TODO: remove this once the customer table is populated + }, + ) + @JoinColumn({ + referencedColumnName: 'stripeCustomerId', + name: 'stripeCustomerId', + }) + billingCustomer: Relation; } diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-meter.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-meter.entity.ts new file mode 100644 index 0000000000..2c153e6085 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-meter.entity.ts @@ -0,0 +1,61 @@ +import Stripe from 'stripe'; +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity'; +import { BillingMeterEventTimeWindow } from 'src/engine/core-modules/billing/enums/billing-meter-event-time-window.enum'; +import { BillingMeterStatus } from 'src/engine/core-modules/billing/enums/billing-meter-status.enum'; + +@Entity({ name: 'billingMeter', schema: 'core' }) +export class BillingMeter { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: true, type: 'timestamptz' }) + deletedAt?: Date; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @Column({ nullable: false, unique: true }) + stripeMeterId: string; + + @Column({ nullable: false }) + displayName: string; + + @Column({ nullable: false }) + eventName: string; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(BillingMeterStatus), + }) + status: BillingMeterStatus; + + @Column({ nullable: false, type: 'jsonb' }) + customerMapping: Stripe.Billing.Meter.CustomerMapping; + + @Column({ + nullable: true, + type: 'enum', + enum: Object.values(BillingMeterEventTimeWindow), + }) + eventTimeWindow: BillingMeterEventTimeWindow | null; + + @OneToMany(() => BillingPrice, (billingPrice) => billingPrice.billingMeter) + billingPrices: Relation; + + @Column({ nullable: false, type: 'jsonb' }) + valueSettings: Stripe.Billing.Meter.ValueSettings; +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-price.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-price.entity.ts new file mode 100644 index 0000000000..51e53b8dff --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-price.entity.ts @@ -0,0 +1,139 @@ +import { Field } from '@nestjs/graphql'; + +import Stripe from 'stripe'; +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity'; +import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity'; +import { BillingPriceBillingScheme } from 'src/engine/core-modules/billing/enums/billing-price-billing-scheme.enum'; +import { BillingPriceTaxBehavior } from 'src/engine/core-modules/billing/enums/billing-price-tax-behavior.enum'; +import { BillingPriceTiersMode } from 'src/engine/core-modules/billing/enums/billing-price-tiers-mode.enum'; +import { BillingPriceType } from 'src/engine/core-modules/billing/enums/billing-price-type.enum'; +import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/billing-subscription-interval.enum'; +import { BillingUsageType } from 'src/engine/core-modules/billing/enums/billing-usage-type.enum'; + +@Entity({ name: 'billingPrice', schema: 'core' }) +export class BillingPrice { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: true, type: 'timestamptz' }) + deletedAt?: Date; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @Column({ nullable: false, unique: true }) + stripePriceId: string; + + @Column({ nullable: false }) + active: boolean; + + @Column({ nullable: false }) + stripeProductId: string; + + @Column({ nullable: false }) + currency: string; + + @Column({ nullable: true, type: 'text' }) + nickname: string | null; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(BillingPriceTaxBehavior), + }) + taxBehavior: BillingPriceTaxBehavior; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(BillingPriceType), + }) + type: BillingPriceType; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(BillingPriceBillingScheme), + }) + billingScheme: BillingPriceBillingScheme; + + @Column({ nullable: true, type: 'jsonb' }) + currencyOptions: Stripe.Price.CurrencyOptions | null; + + @Column({ nullable: true, type: 'jsonb' }) + tiers: Stripe.Price.Tier[] | null; + + @Column({ nullable: true, type: 'jsonb' }) + recurring: Stripe.Price.Recurring | null; + + @Column({ nullable: true, type: 'jsonb' }) + transformQuantity: Stripe.Price.TransformQuantity | null; + + @Column({ + nullable: true, + type: 'enum', + enum: Object.values(BillingPriceTiersMode), + }) + tiersMode: BillingPriceTiersMode | null; + + @Column({ nullable: true, type: 'text' }) + unitAmountDecimal: string | null; + + @Column({ nullable: true, type: 'numeric' }) + unitAmount: number | null; + + @Column({ nullable: true, type: 'text' }) + stripeMeterId: string | null; + + @Field(() => BillingUsageType) + @Column({ + type: 'enum', + enum: Object.values(BillingUsageType), + nullable: false, + }) + usageType: BillingUsageType; + + @Field(() => SubscriptionInterval, { nullable: true }) + @Column({ + type: 'enum', + enum: Object.values(SubscriptionInterval), + nullable: true, + }) + interval: SubscriptionInterval | null; + + @ManyToOne( + () => BillingProduct, + (billingProduct) => billingProduct.billingPrices, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ + referencedColumnName: 'stripeProductId', + name: 'stripeProductId', + }) + billingProduct: Relation; + + @ManyToOne(() => BillingMeter, (billingMeter) => billingMeter.billingPrices, { + nullable: true, + }) + @JoinColumn({ + referencedColumnName: 'stripeMeterId', + name: 'stripeMeterId', + }) + billingMeter: Relation; +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-product.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-product.entity.ts new file mode 100644 index 0000000000..7019d38ec1 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-product.entity.ts @@ -0,0 +1,67 @@ +import { registerEnumType } from '@nestjs/graphql'; + +import Stripe from 'stripe'; +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity'; +import { BillingUsageType } from 'src/engine/core-modules/billing/enums/billing-usage-type.enum'; +import { BillingProductMetadata } from 'src/engine/core-modules/billing/types/billing-product-metadata.type'; +registerEnumType(BillingUsageType, { name: 'BillingUsageType' }); +@Entity({ name: 'billingProduct', schema: 'core' }) +export class BillingProduct { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: true, type: 'timestamptz' }) + deletedAt?: Date; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @Column({ nullable: false }) + active: boolean; + + @Column({ nullable: true, type: 'text' }) + description: string | null; + + @Column({ nullable: false }) + name: string; + + @Column({ nullable: true, type: 'text' }) + taxCode: string | null; + + @Column({ nullable: false, type: 'jsonb', default: [] }) + images: string[]; + + @Column({ nullable: false, type: 'jsonb', default: [] }) + marketingFeatures: Stripe.Product.MarketingFeature[]; + + @Column({ nullable: false, unique: true }) + stripeProductId: string; + + @Column({ nullable: true, type: 'text' }) + defaultStripePriceId: string | null; + + @Column({ nullable: false, type: 'jsonb', default: {} }) + metadata: BillingProductMetadata; + + @OneToMany(() => BillingPrice, (billingPrice) => billingPrice.billingProduct) + billingPrices: Relation; + + @Column({ nullable: true, type: 'text' }) + unitLabel: string | null; + + @Column({ nullable: true, type: 'text' }) + url: string | null; +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription-item.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription-item.entity.ts index 47f24a59e7..785759d104 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription-item.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription-item.entity.ts @@ -1,3 +1,4 @@ +import Stripe from 'stripe'; import { Column, CreateDateColumn, @@ -10,7 +11,6 @@ import { } from 'typeorm'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; - @Entity({ name: 'billingSubscriptionItem', schema: 'core' }) @Unique('IndexOnBillingSubscriptionIdAndStripeProductIdUnique', [ 'billingSubscriptionId', @@ -36,6 +36,15 @@ export class BillingSubscriptionItem { @Column({ nullable: false }) billingSubscriptionId: string; + @Column({ nullable: true }) + stripeSubscriptionId: string; + + @Column({ nullable: false, type: 'jsonb', default: {} }) + metadata: Stripe.Metadata; + + @Column({ nullable: true, type: 'jsonb' }) + billingThresholds: Stripe.SubscriptionItem.BillingThresholds; + @ManyToOne( () => BillingSubscription, (billingSubscription) => billingSubscription.billingSubscriptionItems, @@ -52,8 +61,8 @@ export class BillingSubscriptionItem { stripePriceId: string; @Column({ nullable: false }) - stripeSubscriptionItemId: string; + stripeSubscriptionItemId: string; //TODO: add unique @Column({ nullable: false }) - quantity: number; + quantity: number; //TODO: add nullable and modify stripe service } diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts index 711c3f5275..e80d97bd27 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts @@ -15,7 +15,9 @@ import { } from 'typeorm'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; +import { BillingSubscriptionCollectionMethod } from 'src/engine/core-modules/billing/enums/billing-subscription-collection-method.enum'; import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/billing-subscription-interval.enum'; import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -75,4 +77,73 @@ export class BillingSubscription { (billingSubscriptionItem) => billingSubscriptionItem.billingSubscription, ) billingSubscriptionItems: Relation; + + @ManyToOne( + () => BillingCustomer, + (billingCustomer) => billingCustomer.billingSubscriptions, + { + nullable: false, + createForeignKeyConstraints: false, + }, + ) + @JoinColumn({ + referencedColumnName: 'stripeCustomerId', + name: 'stripeCustomerId', + }) + billingCustomer: Relation; //let's see if it works + + @Column({ nullable: false, default: false }) + cancelAtPeriodEnd: boolean; + + @Column({ nullable: false, default: 'USD' }) + currency: string; + + @Column({ + nullable: false, + type: 'timestamptz', + default: () => 'CURRENT_TIMESTAMP', + }) + currentPeriodEnd: Date; + + @Column({ + nullable: false, + type: 'timestamptz', + default: () => 'CURRENT_TIMESTAMP', + }) + currentPeriodStart: Date; + + @Column({ nullable: false, type: 'jsonb', default: {} }) + metadata: Stripe.Metadata; + + @Column({ nullable: true, type: 'timestamptz' }) + cancelAt: Date | null; + + @Column({ + nullable: true, + type: 'timestamptz', + }) + canceledAt: Date | null; + + @Column({ nullable: true, type: 'jsonb' }) + automaticTax: Stripe.Subscription.AutomaticTax | null; + + @Column({ nullable: true, type: 'jsonb' }) + cancellationDetails: Stripe.Subscription.CancellationDetails | null; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(BillingSubscriptionCollectionMethod), + default: BillingSubscriptionCollectionMethod.CHARGE_AUTOMATICALLY, + }) + collectionMethod: BillingSubscriptionCollectionMethod; + + @Column({ nullable: true, type: 'timestamptz' }) + endedAt: Date | null; + + @Column({ nullable: true, type: 'timestamptz' }) + trialStart: Date | null; + + @Column({ nullable: true, type: 'timestamptz' }) + trialEnd: Date | null; } diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-event-time-window.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-event-time-window.enum.ts new file mode 100644 index 0000000000..b450429221 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-event-time-window.enum.ts @@ -0,0 +1,4 @@ +export enum BillingMeterEventTimeWindow { + DAY = 'DAY', + HOUR = 'HOUR', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-status.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-status.enum.ts new file mode 100644 index 0000000000..81e906e4fe --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-meter-status.enum.ts @@ -0,0 +1,4 @@ +export enum BillingMeterStatus { + ACTIVE = 'ACTIVE', + INACTIVE = 'INACTIVE', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts new file mode 100644 index 0000000000..2f8aa1eb12 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts @@ -0,0 +1,4 @@ +export enum BillingPlanKey { + BASE_PLAN = 'BASE_PLAN', + PRO_PLAN = 'PRO_PLAN', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-billing-scheme.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-billing-scheme.enum.ts new file mode 100644 index 0000000000..f733e00a90 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-billing-scheme.enum.ts @@ -0,0 +1,4 @@ +export enum BillingPriceBillingScheme { + PER_UNIT = 'PER_UNIT', + TIERED = 'TIERED', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tax-behavior.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tax-behavior.enum.ts new file mode 100644 index 0000000000..5dea6f701d --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tax-behavior.enum.ts @@ -0,0 +1,5 @@ +export enum BillingPriceTaxBehavior { + EXCLUSIVE = 'EXCLUSIVE', + INCLUSIVE = 'INCLUSIVE', + UNSPECIFIED = 'UNSPECIFIED', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tiers-mode.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tiers-mode.enum.ts new file mode 100644 index 0000000000..2d54ac8740 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-tiers-mode.enum.ts @@ -0,0 +1,4 @@ +export enum BillingPriceTiersMode { + GRADUATED = 'GRADUATED', + VOLUME = 'VOLUME', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-type.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-type.enum.ts new file mode 100644 index 0000000000..efc2973535 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-price-type.enum.ts @@ -0,0 +1,4 @@ +export enum BillingPriceType { + ONE_TIME = 'ONE_TIME', + RECURRING = 'RECURRING', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-subscription-collection-method.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-subscription-collection-method.enum.ts new file mode 100644 index 0000000000..7dc79588ee --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-subscription-collection-method.enum.ts @@ -0,0 +1,4 @@ +export enum BillingSubscriptionCollectionMethod { + CHARGE_AUTOMATICALLY = 'CHARGE_AUTOMATICALLY', + SEND_INVOICE = 'SEND_INVOICE', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-usage-type.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-usage-type.enum.ts new file mode 100644 index 0000000000..a181b1a0ed --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-usage-type.enum.ts @@ -0,0 +1,4 @@ +export enum BillingUsageType { + METERED = 'METERED', + LICENSED = 'LICENSED', +} diff --git a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts index a91446b1c5..5843a4f36b 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts @@ -2,10 +2,11 @@ import { Logger, Scope } from '@nestjs/common'; import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service'; import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service'; -import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; +import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; export type UpdateSubscriptionJobData = { workspaceId: string }; @Processor({ @@ -17,15 +18,18 @@ export class UpdateSubscriptionJob { constructor( private readonly billingSubscriptionService: BillingSubscriptionService, - private readonly userWorkspaceService: UserWorkspaceService, private readonly stripeService: StripeService, + private readonly twentyORMManager: TwentyORMManager, ) {} @Process(UpdateSubscriptionJob.name) async handle(data: UpdateSubscriptionJobData): Promise { - const workspaceMembersCount = await this.userWorkspaceService.getUserCount( - data.workspaceId, - ); + const workspaceMemberRepository = + await this.twentyORMManager.getRepository( + 'workspaceMember', + ); + + const workspaceMembersCount = await workspaceMemberRepository.count(); if (!workspaceMembersCount || workspaceMembersCount <= 0) { return; diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook.service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook.service.ts index fc6c7d2b4c..d458061247 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook.service.ts @@ -12,6 +12,7 @@ import { BillingEntitlement } from 'src/engine/core-modules/billing/entities/bil import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/billing-entitlement-key.enum'; +import { BillingSubscriptionCollectionMethod } from 'src/engine/core-modules/billing/enums/billing-subscription-collection-method.enum'; import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum'; import { Workspace, @@ -54,6 +55,30 @@ export class BillingWebhookService { stripeSubscriptionId: data.object.id, status: data.object.status as SubscriptionStatus, interval: data.object.items.data[0].plan.interval, + cancelAtPeriodEnd: data.object.cancel_at_period_end, + currency: data.object.currency.toUpperCase(), + currentPeriodEnd: new Date(data.object.current_period_end * 1000), + currentPeriodStart: new Date(data.object.current_period_start * 1000), + metadata: data.object.metadata, + collectionMethod: + data.object.collection_method.toUpperCase() as BillingSubscriptionCollectionMethod, + automaticTax: data.object.automatic_tax ?? undefined, + cancellationDetails: data.object.cancellation_details ?? undefined, + endedAt: data.object.ended_at + ? new Date(data.object.ended_at * 1000) + : undefined, + trialStart: data.object.trial_start + ? new Date(data.object.trial_start * 1000) + : undefined, + trialEnd: data.object.trial_end + ? new Date(data.object.trial_end * 1000) + : undefined, + cancelAt: data.object.cancel_at + ? new Date(data.object.cancel_at * 1000) + : undefined, + canceledAt: data.object.canceled_at + ? new Date(data.object.canceled_at * 1000) + : undefined, }, { conflictPaths: ['stripeSubscriptionId'], @@ -70,10 +95,13 @@ export class BillingWebhookService { data.object.items.data.map((item) => { return { billingSubscriptionId: billingSubscription.id, + stripeSubscriptionId: data.object.id, stripeProductId: item.price.product as string, stripePriceId: item.price.id, stripeSubscriptionItemId: item.id, quantity: item.quantity, + metadata: item.metadata, + billingThresholds: item.billing_thresholds ?? undefined, }; }), { diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts index 27cc61bb4a..6477519878 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts @@ -22,7 +22,10 @@ export class BillingService { return this.environmentService.get('IS_BILLING_ENABLED'); } - async hasWorkspaceActiveSubscriptionOrFreeAccess(workspaceId: string) { + async hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement( + workspaceId: string, + entitlementKey?: BillingEntitlementKey, + ) { const isBillingEnabled = this.isBillingEnabled(); if (!isBillingEnabled) { @@ -39,6 +42,13 @@ export class BillingService { return true; } + if (entitlementKey) { + return this.billingSubscriptionService.getWorkspaceEntitlementByKey( + workspaceId, + entitlementKey, + ); + } + const currentBillingSubscription = await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow( { workspaceId }, @@ -53,20 +63,4 @@ export class BillingService { ].includes(currentBillingSubscription.status) ); } - - async verifyWorkspaceEntitlement( - workspaceId: string, - entitlementKey: BillingEntitlementKey, - ) { - const isBillingEnabled = this.isBillingEnabled(); - - if (!isBillingEnabled) { - return true; - } - - return this.billingSubscriptionService.getWorkspaceEntitlementByKey( - workspaceId, - entitlementKey, - ); - } } diff --git a/packages/twenty-server/src/engine/core-modules/billing/types/billing-product-metadata.type.ts b/packages/twenty-server/src/engine/core-modules/billing/types/billing-product-metadata.type.ts new file mode 100644 index 0000000000..5cbe7cf08e --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/types/billing-product-metadata.type.ts @@ -0,0 +1,7 @@ +import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum'; +import { BillingUsageType } from 'src/engine/core-modules/billing/enums/billing-usage-type.enum'; + +export type BillingProductMetadata = { + planKey: BillingPlanKey; + priceUsageBased: BillingUsageType; +}; diff --git a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts index e7974a6fbf..61db91a457 100644 --- a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts @@ -24,11 +24,11 @@ import { LLMTracingDriver } from 'src/engine/core-modules/llm-tracing/interfaces import { CacheStorageType } from 'src/engine/core-modules/cache-storage/types/cache-storage-type.enum'; import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces'; +import { AssertOrWarn } from 'src/engine/core-modules/environment/decorators/assert-or-warn.decorator'; import { CastToBoolean } from 'src/engine/core-modules/environment/decorators/cast-to-boolean.decorator'; import { CastToLogLevelArray } from 'src/engine/core-modules/environment/decorators/cast-to-log-level-array.decorator'; import { CastToPositiveNumber } from 'src/engine/core-modules/environment/decorators/cast-to-positive-number.decorator'; import { CastToStringArray } from 'src/engine/core-modules/environment/decorators/cast-to-string-array.decorator'; -import { AssertOrWarn } from 'src/engine/core-modules/environment/decorators/assert-or-warn.decorator'; import { IsAWSRegion } from 'src/engine/core-modules/environment/decorators/is-aws-region.decorator'; import { IsDuration } from 'src/engine/core-modules/environment/decorators/is-duration.decorator'; import { IsStrictlyLowerThan } from 'src/engine/core-modules/environment/decorators/is-strictly-lower-than.decorator'; diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 8b17532317..5d4cddd808 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -27,7 +27,7 @@ export class OnboardingService { private async isSubscriptionIncompleteOnboardingStatus(user: User) { const hasSubscription = - await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccess( + await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement( user.defaultWorkspaceId, ); diff --git a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts index 7f01265f7d..cc6e90287d 100644 --- a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts +++ b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts @@ -55,7 +55,7 @@ export class SSOService { ); } const isSSOBillingEnabled = - await this.billingService.verifyWorkspaceEntitlement( + await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement( workspaceId, this.featureLookUpKey, ); diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts index e353174e6f..0380f4243a 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts @@ -13,6 +13,7 @@ import { import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; +import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingEntitlement } from 'src/engine/core-modules/billing/entities/billing-entitlement.entity'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @@ -42,6 +43,9 @@ registerEnumType(WorkspaceActivationStatus, { @UnPagedRelation('billingEntitlements', () => BillingEntitlement, { nullable: true, }) +@UnPagedRelation('billingCustomers', () => BillingCustomer, { + nullable: true, +}) export class Workspace { @IDField(() => UUIDScalarType) @PrimaryGeneratedColumn('uuid') @@ -121,6 +125,12 @@ export class Workspace { ) billingSubscriptions: Relation; + @OneToMany( + () => BillingCustomer, + (billingCustomer) => billingCustomer.workspace, + ) + billingCustomers: Relation; + @OneToMany( () => BillingEntitlement, (billingEntitlement) => billingEntitlement.workspace, diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts index cfd1db2c53..d7f9cfe042 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts @@ -133,7 +133,11 @@ export class WorkspaceResolver { @ResolveField(() => BillingSubscription, { nullable: true }) async currentBillingSubscription( @Parent() workspace: Workspace, - ): Promise { + ): Promise { + if (!this.environmentService.get('IS_BILLING_ENABLED')) { + return; + } + return this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow( { workspaceId: workspace.id }, );