fix(billing) - fix orphaned stripe subs (#20814)

Fix sentry issues
https://twenty-v7.sentry.io/issues/7203797925/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20assigned%3Ame&referrer=issue-stream

An orphaned sub is a not "canceled" stripe sub with no matching
workspace

- Clean all orphaned sub (script not included in this PR)
- Ensure to soft delete > cancel stripe sub > check for not active sub >
hard delete in every workspace deletion flow

Bonus : 
- Remove dead code
- Update doc on RLS (to improve AI chat knowledge)
This commit is contained in:
Etienne
2026-05-21 19:02:54 +02:00
committed by GitHub
parent 3c91f3f276
commit 0edd8d400c
15 changed files with 109 additions and 256 deletions
@@ -291,27 +291,49 @@ export class CleanerWorkspaceService {
});
if (workspaces.length !== 0) {
if (!dryRun) {
for (const workspace of workspaces) {
const userWorkspaces = await this.userWorkspaceRepository.find({
where: {
workspaceId: workspace.id,
},
withDeleted: true,
});
for (const workspace of workspaces) {
if (!isDefined(workspace.deletedAt)) {
this.logger.log(
`${dryRun ? 'DRY RUN - ' : ''}Soft deleting onboarding workspace ${workspace.id}`,
);
for (const userWorkspace of userWorkspaces) {
await this.workspaceService.handleRemoveWorkspaceMember(
workspace.id,
userWorkspace.userId,
);
if (!dryRun) {
const userWorkspaces = await this.userWorkspaceRepository.find({
where: {
workspaceId: workspace.id,
},
withDeleted: true,
});
for (const userWorkspace of userWorkspaces) {
await this.workspaceService.handleRemoveWorkspaceMember(
workspace.id,
userWorkspace.userId,
);
}
if (this.twentyConfigService.get('IS_BILLING_ENABLED')) {
await this.billingSubscriptionService.cancelSubscription(
workspace.id,
);
}
await this.workspaceService.deleteWorkspace(workspace.id, true);
}
} else {
if (this.twentyConfigService.get('IS_BILLING_ENABLED')) {
await this.billingSubscriptionService.deleteSubscriptions(
await this.billingSubscriptionService.assertSubscriptionCanceledOrNone(
workspace.id,
);
}
await this.workspaceRepository.delete(workspace.id);
this.logger.log(
`${dryRun ? 'DRY RUN - ' : ''}Hard deleting onboarding workspace ${workspace.id}`,
);
if (!dryRun) {
await this.workspaceService.deleteWorkspace(workspace.id);
}
}
}