Skip to content

Commit eb82aa9

Browse files
committed
fix: extra seats query returning wrong count for non-Seats subscriptions
1 parent db1a530 commit eb82aa9

4 files changed

Lines changed: 31 additions & 11 deletions

File tree

apps/builder/app/services/workspace-router.server.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "@webstudio-is/trpc-interface/index.server";
77
import { workspace as workspaceApi } from "@webstudio-is/project/index.server";
88
import { roles } from "@webstudio-is/trpc-interface/authorize";
9-
import { getPaidSeats } from "@webstudio-is/plans/index.server";
9+
import { getExtraPaidSeats } from "@webstudio-is/plans/index.server";
1010
import env from "~/env/env.server";
1111

1212
const Name = z.string().min(2).max(100);
@@ -318,15 +318,18 @@ export const workspaceRouter = router({
318318
.query(async ({ input, ctx }) => {
319319
try {
320320
const members = await workspaceApi.listMembers(input, ctx);
321-
const paidSeats = await getPaidSeats(members.owner.userId, ctx);
321+
const extraPaidSeats = await getExtraPaidSeats(
322+
members.owner.userId,
323+
ctx
324+
);
322325
return {
323326
success: true as const,
324327
data: {
325328
...members,
326329
// seatsIncluded = seats covered by the Team plan.
327-
// paidSeats = extra seats from the Seats subscription.
330+
// extraPaidSeats = extra seats from the Seats subscription.
328331
// Total capacity = included + extras.
329-
maxSeats: ctx.planFeatures.seatsIncluded + (paidSeats ?? 0),
332+
maxSeats: ctx.planFeatures.seatsIncluded + (extraPaidSeats ?? 0),
330333
},
331334
};
332335
} catch (error) {

packages/plans/src/index.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export * from "./index";
22
export {
33
getPlanInfo,
4-
getPaidSeats,
4+
getExtraPaidSeats,
55
getAuthorizationOwnerId,
66
parsePlansEnv,
77
parseProductMeta,

packages/plans/src/plan-client.server.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,30 @@ export const getPlanInfo = async (
206206
};
207207

208208
/**
209-
* Returns the Stripe subscription item quantity for the given user, derived
210-
* from the latest customer.subscription.updated/created event in TransactionLog.
211-
* Returns null when no subscription event exists yet (free plan, AppSumo, etc.).
209+
* Returns the extra-seat subscription quantity for the given user, derived
210+
* from the latest customer.subscription.updated/created event in TransactionLog
211+
* whose product is an extra-seat plan (maxSeatsPerWorkspace > 0, maxWorkspaces <= 1).
212+
*
213+
* Returns null when no matching event exists (free plan, no extra seats, etc.).
212214
*/
213-
export const getPaidSeats = async (
215+
export const getExtraPaidSeats = async (
214216
userId: string,
215217
context: { postgrest: PostgrestContext }
216218
): Promise<number | null> => {
219+
const productResult = await context.postgrest.client
220+
.from("Product")
221+
.select("id")
222+
.eq("name", "Seats");
223+
224+
if (productResult.error) {
225+
throw productResult.error;
226+
}
227+
228+
const seatProductIds = (productResult.data ?? []).map((p) => p.id);
229+
if (seatProductIds.length === 0) {
230+
return null;
231+
}
232+
217233
const result = await context.postgrest.client
218234
.from("TransactionLog")
219235
.select("eventData")
@@ -222,6 +238,7 @@ export const getPaidSeats = async (
222238
"customer.subscription.updated",
223239
"customer.subscription.created",
224240
])
241+
.in("productId", seatProductIds)
225242
.order("eventCreated", { ascending: false })
226243
.limit(1)
227244
.maybeSingle();

packages/project/src/db/workspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
NOTIFICATION_TTL_MS,
1212
} from "../shared/notification-schema";
1313
import { create as createNotification } from "./notification";
14-
import { getPaidSeats } from "@webstudio-is/plans/index.server";
14+
import { getExtraPaidSeats } from "@webstudio-is/plans/index.server";
1515

1616
export type Workspace = Database["public"]["Tables"]["Workspace"]["Row"];
1717

@@ -253,7 +253,7 @@ export const findMany = async (userId: string, context: AppContext) => {
253253
}
254254
const [memberCount, extraSeats] = await Promise.all([
255255
countAllMembers(ownerId, context),
256-
getPaidSeats(ownerId, context),
256+
getExtraPaidSeats(ownerId, context),
257257
]);
258258
// seatsIncluded is the number of non-owner member slots included in
259259
// the plan. The owner is not counted — consistent with the billing

0 commit comments

Comments
 (0)