Skip to content

Commit 242e510

Browse files
geroplroboquat
authored andcommitted
[server] BillingMode: Render names of Chargebee Team Plans/Subscriptions if that blocks UBP
1 parent 4976dcb commit 242e510

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

components/gitpod-protocol/src/billing-mode.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ interface Chargebee {
6464
paid?: boolean;
6565

6666
canUpgradeToUBB?: boolean;
67+
68+
/** Name of team(s) that block switching to usage-based */
69+
teamNames?: string[];
6770
}
6871

6972
/** Session is handld with new usage-based logic */

components/server/ee/src/billing/billing-mode.spec.db.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class BillingModeSpec {
157157
function user(): User {
158158
return {
159159
id: userId,
160+
name: `user-${userId}`,
160161
creationDate,
161162
identities: [],
162163
};
@@ -312,6 +313,7 @@ class BillingModeSpec {
312313
expectation: {
313314
mode: "chargebee",
314315
canUpgradeToUBB: true,
316+
teamNames: ["Team Subscription 'Team Unleashed' (owner: user-123)"],
315317
},
316318
},
317319
{
@@ -328,6 +330,7 @@ class BillingModeSpec {
328330
expectation: {
329331
mode: "chargebee",
330332
canUpgradeToUBB: true,
333+
teamNames: ["Team Subscription 'Team Unleashed' (owner: user-123)"],
331334
},
332335
},
333336
{
@@ -461,6 +464,7 @@ class BillingModeSpec {
461464
expectation: {
462465
mode: "chargebee",
463466
paid: true,
467+
teamNames: ["team-123"],
464468
},
465469
},
466470
// team: transition chargebee -> UBB
@@ -475,6 +479,7 @@ class BillingModeSpec {
475479
expectation: {
476480
mode: "chargebee",
477481
canUpgradeToUBB: true,
482+
teamNames: ["team-123"],
478483
},
479484
},
480485
{

components/server/ee/src/billing/billing-mode.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,16 @@ export class BillingModesImpl implements BillingModes {
110110

111111
// 2. Any personal subscriptions?
112112
// Chargebee takes precedence
113-
function isTeamSubscription(s: Subscription): boolean {
114-
return !!Plans.getById(s.planId)?.team;
113+
function isPersonalSubscription(s: Subscription): boolean {
114+
return !Plans.getById(s.planId)?.team;
115+
}
116+
function isOldTeamSubscription(s: Subscription): boolean {
117+
return !!Plans.getById(s.planId)?.team && !s.teamMembershipId;
115118
}
116119
const cbSubscriptions = await this.subscriptionSvc.getActivePaidSubscription(user.id, now);
117-
const cbTeamSubscriptions = cbSubscriptions.filter((s) => isTeamSubscription(s));
120+
const cbTeamSubscriptions = cbSubscriptions.filter((s) => isOldTeamSubscription(s));
118121
const cbPersonalSubscriptions = cbSubscriptions.filter(
119-
(s) => !isTeamSubscription(s) && s.planId !== Plans.FREE_OPEN_SOURCE.chargebeeId,
122+
(s) => isPersonalSubscription(s) && s.planId !== Plans.FREE_OPEN_SOURCE.chargebeeId,
120123
);
121124
const cbOwnedTeamSubscriptions = (
122125
await this.teamSubscriptionDb.findTeamSubscriptions({ userId: user.id })
@@ -166,15 +169,50 @@ export class BillingModesImpl implements BillingModes {
166169
return usageBased();
167170
}
168171
if (hasCbPaidTeamMembership || hasCbPaidTeamSeat || canUpgradeToUBB) {
169-
// TODO(gpl): Q: How to test the free-tier, then? A: Make sure you have no CB paid seats anymore
170-
// For that we could add a new field here, which lists all seats that are "blocking" you, and display them in the UI somewhere.
171-
return { mode: "chargebee", canUpgradeToUBB: true }; // UBB is enabled, but no seat nor subscription yet.
172+
// Q: How to test the free-tier, then? A: Make sure you have no CB paid seats anymore
173+
// For that we lists all Team Subscriptions/Team Memberships that are "blocking" you, and display them in the UI somewhere.
174+
const result: BillingMode = { mode: "chargebee", canUpgradeToUBB }; // UBB is enabled, but no seat nor subscription yet.
175+
176+
const teamNames = [];
177+
for (const tm of teamsModes) {
178+
if (tm.mode === "chargebee" && tm.teamNames) {
179+
teamNames.push(`Team Membership: ${tm.teamNames}`);
180+
}
181+
}
182+
const tsOwners = await Promise.all(cbTeamSubscriptions.map((s) => this.mapTeamSubscriptionToOwnerName(s)));
183+
for (const owner of tsOwners) {
184+
if (!owner) {
185+
continue;
186+
}
187+
const [ts, ownerName] = owner;
188+
teamNames.push(`Team Subscription '${Plans.getById(ts.planId)?.name}' (owner: ${ownerName})`);
189+
}
190+
if (teamNames.length > 0) {
191+
result.teamNames = teamNames;
192+
}
193+
194+
return result;
172195
}
173196

174197
// UBB free tier
175198
return usageBased();
176199
}
177200

201+
protected async mapTeamSubscriptionToOwnerName(s: Subscription): Promise<[TeamSubscription, string] | undefined> {
202+
if (!s || !s.teamSubscriptionSlotId) {
203+
return undefined;
204+
}
205+
const ts = await this.teamSubscriptionDb.findTeamSubscriptionBySlotId(s.teamSubscriptionSlotId!);
206+
if (!ts) {
207+
return undefined;
208+
}
209+
const user = await this.userDB.findUserById(ts.userId);
210+
if (!user) {
211+
return undefined;
212+
}
213+
return [ts, user.name || user.fullName || "---"];
214+
}
215+
178216
async getBillingModeForTeam(team: Team, _now: Date): Promise<BillingMode> {
179217
if (!this.config.enablePayment) {
180218
// Payment is not enabled. E.g. Self-Hosted.
@@ -200,7 +238,7 @@ export class BillingModesImpl implements BillingModes {
200238
return { mode: "chargebee", canUpgradeToUBB: isUsageBasedBillingEnabled };
201239
}
202240

203-
return { mode: "chargebee", paid: true };
241+
return { mode: "chargebee", teamNames: [team.name], paid: true };
204242
}
205243

206244
// 2. UBB enabled at all?

0 commit comments

Comments
 (0)