Skip to content

Commit

Permalink
(core) add users.options.isConsultant flag, and omit such users from …
Browse files Browse the repository at this point in the history
…billing

Summary:
This adds an optional `isConsultant` flag to `users.options`, and an endpoint that allows the support user to turn it on or off. Users marked as consultants are not counted as billable members. Follows the example of existing `allowGoogleLogin` option.

Billable members are counted when members are added or removed from a site. Changing the `isConsultant` flag has no immediate or retroactive effect on billing. The number of users in stripe is now set unconditionally, rather than only when it has changed.

Notifications to billing managers are not aware of this billing nuance, but continue to report user counts that include consultants. The notifications link users to the billing page.

Test Plan: extended test

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: anaisconce, jarek

Differential Revision: https://phab.getgrist.com/D3362
  • Loading branch information
paulfitz committed Apr 11, 2022
1 parent 782bb44 commit 14f7e30
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 2 deletions.
2 changes: 1 addition & 1 deletion app/client/ui/BillingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class BillingPage extends Disposable {
] : null,
moneyPlan.amount ? [
makeSummaryFeature([`Your team site has `, `${sub.userCount}`,
` member${sub.userCount > 1 ? 's' : ''}`]),
` member${sub.userCount !== 1 ? 's' : ''}`]),
tier ? this.buildAppSumoPlanNotes(discountName!) : null,
// Currently the subtotal is misleading and scary when tiers are in effect.
// In this case, for now, just report what will be invoiced.
Expand Down
11 changes: 11 additions & 0 deletions app/common/UserAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ export interface Document extends DocumentProperties {
export interface UserOptions {
// Whether signing in with Google is allowed. Defaults to true if unset.
allowGoogleLogin?: boolean;
// Whether user is a consultant. Consultant users can be added to sites
// without being counted for billing. Defaults to false if unset.
isConsultant?: boolean;
}

export interface PermissionDelta {
Expand Down Expand Up @@ -312,6 +315,7 @@ export interface UserAPI {
getUserProfile(): Promise<FullUser>;
updateUserName(name: string): Promise<void>;
updateAllowGoogleLogin(allowGoogleLogin: boolean): Promise<void>;
updateIsConsultant(userId: number, isConsultant: boolean): Promise<void>;
getWorker(key: string): Promise<string>;
getWorkerAPI(key: string): Promise<DocWorkerAPI>;
getBillingAPI(): BillingAPI;
Expand Down Expand Up @@ -608,6 +612,13 @@ export class UserAPIImpl extends BaseAPI implements UserAPI {
});
}

public async updateIsConsultant(userId: number, isConsultant: boolean): Promise<void> {
await this.request(`${this._url}/api/profile/isConsultant`, {
method: 'POST',
body: JSON.stringify({userId, isConsultant})
});
}

public async getWorker(key: string): Promise<string> {
const json = await this.requestJson(`${this._url}/api/worker/${key}`, {
method: 'GET',
Expand Down
19 changes: 19 additions & 0 deletions app/gen-server/ApiServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,25 @@ export class ApiServer {
res.sendStatus(200);
}));

this._app.post('/api/profile/isConsultant', expressWrap(async (req, res) => {
const userId = getAuthorizedUserId(req);
if (userId !== this._dbManager.getSupportUserId()) {
throw new ApiError('Only support user can enable/disable isConsultant', 401);
}
const isConsultant: boolean | undefined = req.body.isConsultant;
const targetUserId: number | undefined = req.body.userId;
if (isConsultant === undefined) {
throw new ApiError('Missing body param: isConsultant', 400);
}
if (targetUserId === undefined) {
throw new ApiError('Missing body param: targetUserId', 400);
}
await this._dbManager.updateUserOptions(targetUserId, {
isConsultant
});
res.sendStatus(200);
}));

// GET /api/profile/apikey
// Get user's apiKey
this._app.get('/api/profile/apikey', expressWrap(async (req, res) => {
Expand Down
2 changes: 1 addition & 1 deletion app/gen-server/lib/HomeDBManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4185,7 +4185,7 @@ function getFrom(queryBuilder: SelectQueryBuilder<any>): string {
}

// Flatten a map of users per role into a simple list of users.
function removeRole(usersWithRoles: Map<roles.NonGuestRole, User[]>) {
export function removeRole(usersWithRoles: Map<roles.NonGuestRole, User[]>) {
return flatten([...usersWithRoles.values()]);
}

Expand Down

0 comments on commit 14f7e30

Please sign in to comment.