From 5add5ea695248dc03d2344243c634af081588083 Mon Sep 17 00:00:00 2001
From: Etienne <45695613+etiennejouan@users.noreply.github.com>
Date: Tue, 21 Jul 2026 19:04:10 +0200
Subject: [PATCH] fix(billing) - expand billing sub uniqueness constraint
(#23123)
fixes :
https://discord.com/channels/1130383047699738754/1528956737363771497
---
...-statuses-to-billing-subscription-index.ts | 45 +++++++++++++++++++
.../instance-commands.constant.ts | 2 +
.../entities/billing-subscription.entity.ts | 2 +-
3 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-instance-command-slow-1784650048045-add-statuses-to-billing-subscription-index.ts
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-instance-command-slow-1784650048045-add-statuses-to-billing-subscription-index.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-instance-command-slow-1784650048045-add-statuses-to-billing-subscription-index.ts
new file mode 100644
index 0000000000..2e257e3389
--- /dev/null
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-instance-command-slow-1784650048045-add-statuses-to-billing-subscription-index.ts
@@ -0,0 +1,45 @@
+import { DataSource, QueryRunner } from 'typeorm';
+
+import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
+import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
+
+@RegisteredInstanceCommand('2.23.0', 1784650048045, { type: 'slow' })
+export class AddStatusesToBillingSubscriptionIndexSlowInstanceCommand
+ implements SlowInstanceCommand
+{
+ async runDataMigration(_dataSource: DataSource): Promise {}
+
+ public async up(queryRunner: QueryRunner): Promise {
+ const tableExists = await queryRunner.query(
+ `SELECT 1 FROM pg_tables WHERE schemaname = 'core' AND tablename = 'billingSubscription'`,
+ );
+
+ if (tableExists.length === 0) {
+ return;
+ }
+
+ await queryRunner.query(
+ 'DROP INDEX "core"."IDX_BILLING_SUBSCRIPTION_WORKSPACE_ID_UNIQUE"',
+ );
+ await queryRunner.query(
+ `CREATE UNIQUE INDEX "IDX_BILLING_SUBSCRIPTION_WORKSPACE_ID_UNIQUE" ON "core"."billingSubscription" ("workspaceId") WHERE status IN ('trialing', 'active', 'past_due', 'incomplete', 'incomplete_expired', 'unpaid', 'paused')`,
+ );
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ const tableExists = await queryRunner.query(
+ `SELECT 1 FROM pg_tables WHERE schemaname = 'core' AND tablename = 'billingSubscription'`,
+ );
+
+ if (tableExists.length === 0) {
+ return;
+ }
+
+ await queryRunner.query(
+ 'DROP INDEX "core"."IDX_BILLING_SUBSCRIPTION_WORKSPACE_ID_UNIQUE"',
+ );
+ await queryRunner.query(
+ `CREATE UNIQUE INDEX "IDX_BILLING_SUBSCRIPTION_WORKSPACE_ID_UNIQUE" ON "core"."billingSubscription" ("workspaceId") WHERE status IN ('trialing', 'active', 'past_due')`,
+ );
+ }
+}
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts
index bfce8a2a28..22b8283614 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts
@@ -118,6 +118,7 @@ import { AddKanbanAndCalendarWidgetViewTypesFastInstanceCommand } from './2-23/2
import { WidenViewCalendarIntegrityConstraintFastInstanceCommand } from './2-23/2-23-instance-command-fast-1784620626406-widen-view-calendar-integrity-constraint';
import { BackfillCreatedWorkspaceActivationStatusSlowInstanceCommand } from './2-23/2-23-instance-command-slow-1784286705000-backfill-created-workspace-activation-status';
import { UnlistUnclaimedNpmApplicationRegistrationsSlowInstanceCommand } from './2-23/2-23-instance-command-slow-1784322591746-unlist-unclaimed-npm-application-registrations';
+import { AddStatusesToBillingSubscriptionIndexSlowInstanceCommand } from './2-23/2-23-instance-command-slow-1784650048045-add-statuses-to-billing-subscription-index';
export const INSTANCE_COMMANDS = [
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
@@ -238,4 +239,5 @@ export const INSTANCE_COMMANDS = [
WidenViewCalendarIntegrityConstraintFastInstanceCommand,
AddAutoUpgradeToApplicationFastInstanceCommand,
AddSdkClientCoreChecksumToApplicationFastInstanceCommand,
+ AddStatusesToBillingSubscriptionIndexSlowInstanceCommand,
];
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 0931752220..9690d2b1f3 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
@@ -50,7 +50,7 @@ registerEnumType(SubscriptionInterval, { name: 'SubscriptionInterval' });
@Entity({ name: 'billingSubscription', schema: 'core' })
@Index('IDX_BILLING_SUBSCRIPTION_WORKSPACE_ID_UNIQUE', ['workspaceId'], {
unique: true,
- where: `status IN ('trialing', 'active', 'past_due')`,
+ where: `status IN ('trialing', 'active', 'past_due', 'incomplete', 'incomplete_expired', 'unpaid', 'paused')`,
})
@ObjectType('BillingSubscription')
export class BillingSubscriptionEntity extends WorkspaceRelatedEntity {