gristlabs_grist-core/app/gen-server/entity/BillingAccount.ts
Jarosław Sadziński d13b9b9019 (core) Billing for formula assistant
Summary:
Adding limits for AI calls and connecting those limits with a Stripe Account.

- New table in homedb called `limits`
- All calls to the AI are not routed through DocApi and measured.
- All products now contain a special key `assistantLimit`, with a default value 0
- Limit is reset every time the subscription has changed its period
- The billing page is updated with two new options that describe the AI plan
- There is a new popup that allows the user to upgrade to a higher plan
- Tiers are read directly from the Stripe product with a volume pricing model

Test Plan: Updated and added

Reviewers: georgegevoian, paulfitz

Reviewed By: georgegevoian

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3907
2023-07-10 13:24:08 +02:00

83 lines
3.2 KiB
TypeScript

import {BaseEntity, Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn} from 'typeorm';
import {BillingAccountManager} from 'app/gen-server/entity/BillingAccountManager';
import {Organization} from 'app/gen-server/entity/Organization';
import {Product} from 'app/gen-server/entity/Product';
import {nativeValues} from 'app/gen-server/lib/values';
import {Limit} from 'app/gen-server/entity/Limit';
// This type is for billing account status information. Intended for stuff
// like "free trial running out in N days".
export interface BillingAccountStatus {
stripeStatus?: string;
currentPeriodEnd?: string;
message?: string;
}
// A structure for billing options relevant to an external authority, for sites
// created outside of Grist's regular billing flow.
export interface ExternalBillingOptions {
authority: string; // The name of the external authority.
invoiceId?: string; // An id of an invoice or other external billing context.
}
/**
* This relates organizations to products. It holds any stripe information
* needed to be able to update and pay for the product that applies to the
* organization. It has a list of managers detailing which users have the
* right to view and edit these settings.
*/
@Entity({name: 'billing_accounts'})
export class BillingAccount extends BaseEntity {
@PrimaryGeneratedColumn()
public id: number;
@ManyToOne(type => Product)
@JoinColumn({name: 'product_id'})
public product: Product;
@Column({type: Boolean})
public individual: boolean;
// A flag for when all is well with the user's subscription.
// Probably shouldn't use this to drive whether service is provided or not.
// Strip recommends updating an end-of-service datetime every time payment
// is received, adding on a grace period of some days.
@Column({name: 'in_good_standing', type: Boolean, default: nativeValues.trueValue})
public inGoodStanding: boolean;
@Column({type: nativeValues.jsonEntityType, nullable: true})
public status: BillingAccountStatus;
@Column({name: 'stripe_customer_id', type: String, nullable: true})
public stripeCustomerId: string | null;
@Column({name: 'stripe_subscription_id', type: String, nullable: true})
public stripeSubscriptionId: string | null;
@Column({name: 'stripe_plan_id', type: String, nullable: true})
public stripePlanId: string | null;
@Column({name: 'external_id', type: String, nullable: true})
public externalId: string | null;
@Column({name: 'external_options', type: nativeValues.jsonEntityType, nullable: true})
public externalOptions: ExternalBillingOptions | null;
@OneToMany(type => BillingAccountManager, manager => manager.billingAccount)
public managers: BillingAccountManager[];
@OneToMany(type => Organization, org => org.billingAccount)
public orgs: Organization[];
@OneToMany(type => Limit, limit => limit.billingAccount)
public limits: Limit[];
// A calculated column that is true if it looks like there is a paid plan.
@Column({name: 'paid', type: 'boolean', insert: false, select: false})
public paid?: boolean;
// A calculated column summarizing whether active user is a manager of the billing account.
// (No @Column needed since calculation is done in javascript not sql)
public isManager?: boolean;
}