fix: prevent billingPortal creation if no active subscription (#9701)
Billing portal is created in settings/billing page even if subscription is canceled, causing server internal error. -> Skip back end request Bonus : display settings/billing page with disabled button even if subscription is canceled --------- Co-authored-by: etiennejouan <jouan.etienne@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -79,18 +79,16 @@ export class BillingPortalWorkspaceService {
|
||||
workspace: Workspace,
|
||||
returnUrlPath?: string,
|
||||
) {
|
||||
const currentSubscription =
|
||||
await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
|
||||
{
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
);
|
||||
const lastSubscription = await this.billingSubscriptionRepository.findOne({
|
||||
where: { workspaceId: workspace.id },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
if (!currentSubscription) {
|
||||
if (!lastSubscription) {
|
||||
throw new Error('Error: missing subscription');
|
||||
}
|
||||
|
||||
const stripeCustomerId = currentSubscription.stripeCustomerId;
|
||||
const stripeCustomerId = lastSubscription.stripeCustomerId;
|
||||
|
||||
if (!stripeCustomerId) {
|
||||
throw new Error('Error: missing stripeCustomerId');
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField, UnPagedRelation } from '@ptc-org/nestjs-query-graphql';
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared';
|
||||
import {
|
||||
Column,
|
||||
@ -15,9 +15,6 @@ 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';
|
||||
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
@ -30,15 +27,6 @@ registerEnumType(WorkspaceActivationStatus, {
|
||||
|
||||
@Entity({ name: 'workspace', schema: 'core' })
|
||||
@ObjectType('Workspace')
|
||||
@UnPagedRelation('billingSubscriptions', () => BillingSubscription, {
|
||||
nullable: true,
|
||||
})
|
||||
@UnPagedRelation('billingEntitlements', () => BillingEntitlement, {
|
||||
nullable: true,
|
||||
})
|
||||
@UnPagedRelation('billingCustomers', () => BillingCustomer, {
|
||||
nullable: true,
|
||||
})
|
||||
export class Workspace {
|
||||
@IDField(() => UUIDScalarType)
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
@ -17,8 +21,6 @@ import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.r
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
|
||||
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
|
||||
|
||||
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
|
||||
import { Workspace } from './workspace.entity';
|
||||
@ -28,6 +30,7 @@ import { WorkspaceService } from './services/workspace.service';
|
||||
@Module({
|
||||
imports: [
|
||||
TypeORMModule,
|
||||
TypeOrmModule.forFeature([BillingSubscription], 'core'),
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [
|
||||
DomainManagerModule,
|
||||
|
||||
@ -7,8 +7,10 @@ import {
|
||||
ResolveField,
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
|
||||
@ -63,6 +65,8 @@ export class WorkspaceResolver {
|
||||
private readonly fileService: FileService,
|
||||
private readonly billingSubscriptionService: BillingSubscriptionService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
@InjectRepository(BillingSubscription, 'core')
|
||||
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
||||
) {}
|
||||
|
||||
@Query(() => Workspace)
|
||||
@ -159,6 +163,19 @@ export class WorkspaceResolver {
|
||||
return this.workspaceService.deleteWorkspace(id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [BillingSubscription])
|
||||
async billingSubscriptions(
|
||||
@Parent() workspace: Workspace,
|
||||
): Promise<BillingSubscription[] | undefined> {
|
||||
try {
|
||||
return this.billingSubscriptionRepository.find({
|
||||
where: { workspaceId: workspace.id },
|
||||
});
|
||||
} catch (error) {
|
||||
workspaceGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ResolveField(() => BillingSubscription, { nullable: true })
|
||||
async currentBillingSubscription(
|
||||
@Parent() workspace: Workspace,
|
||||
|
||||
Reference in New Issue
Block a user