|
1 | 1 | import { db } from '@sim/db' |
2 | | -import { member, subscription as subscriptionTable, user, userStats } from '@sim/db/schema' |
| 2 | +import { member, user, userStats } from '@sim/db/schema' |
3 | 3 | import { createLogger } from '@sim/logger' |
4 | 4 | import { isOrgAdminRole } from '@sim/platform-authz/workspace' |
5 | | -import { and, eq, inArray } from 'drizzle-orm' |
| 5 | +import { and, eq } from 'drizzle-orm' |
6 | 6 | import { type NextRequest, NextResponse } from 'next/server' |
7 | 7 | import { |
8 | 8 | organizationMemberQuerySchema, |
9 | 9 | organizationParamsSchema, |
10 | 10 | } from '@/lib/api/contracts/organization' |
11 | 11 | import { getValidationErrorMessage } from '@/lib/api/server' |
12 | 12 | import { getSession } from '@/lib/auth' |
13 | | -import { getOrgMemberLedgerByUser } from '@/lib/billing/core/organization' |
14 | | -import { ENTITLED_SUBSCRIPTION_STATUSES } from '@/lib/billing/subscriptions/utils' |
| 13 | +import { getOrganizationMemberUsageSnapshot } from '@/lib/billing/core/organization' |
15 | 14 | import { withRouteHandler } from '@/lib/core/utils/with-route-handler' |
16 | 15 |
|
17 | 16 | const logger = createLogger('OrganizationMembersAPI') |
@@ -101,41 +100,18 @@ export const GET = withRouteHandler( |
101 | 100 | .leftJoin(userStats, eq(user.id, userStats.userId)) |
102 | 101 | .where(eq(member.organizationId, organizationId)) |
103 | 102 |
|
104 | | - // The billing period is the same for every member — it comes from |
105 | | - // whichever subscription covers them. Fetch once and attach to |
106 | | - // every row instead of calling `getUserUsageData` per-member, |
107 | | - // which would run an O(N) pooled query for each of N rows. |
108 | | - const [orgSub] = await db |
109 | | - .select({ |
110 | | - periodStart: subscriptionTable.periodStart, |
111 | | - periodEnd: subscriptionTable.periodEnd, |
| 103 | + const { billingPeriod, includeLegacyBaseline, usageByUser } = |
| 104 | + await getOrganizationMemberUsageSnapshot(organizationId, { |
| 105 | + userIds: base.length <= 1_000 ? base.map((row) => row.userId) : undefined, |
112 | 106 | }) |
113 | | - .from(subscriptionTable) |
114 | | - .where( |
115 | | - and( |
116 | | - eq(subscriptionTable.referenceId, organizationId), |
117 | | - inArray(subscriptionTable.status, ENTITLED_SUBSCRIPTION_STATUSES) |
118 | | - ) |
119 | | - ) |
120 | | - .limit(1) |
121 | | - |
122 | | - const billingPeriodStart = orgSub?.periodStart ?? null |
123 | | - const billingPeriodEnd = orgSub?.periodEnd ?? null |
124 | | - |
125 | | - // currentPeriodCost is only a baseline; add each member's attributed |
126 | | - // usage_log for the period (batched, one query) so the roster shows real |
127 | | - // usage rather than the frozen baseline. |
128 | | - const usageByUser = await getOrgMemberLedgerByUser( |
129 | | - organizationId, |
130 | | - billingPeriodStart && billingPeriodEnd |
131 | | - ? { start: billingPeriodStart, end: billingPeriodEnd } |
132 | | - : null |
133 | | - ) |
| 107 | + const billingPeriodStart = billingPeriod?.start ?? null |
| 108 | + const billingPeriodEnd = billingPeriod?.end ?? null |
134 | 109 |
|
135 | 110 | const membersWithUsage = base.map((row) => ({ |
136 | 111 | ...row, |
137 | 112 | currentPeriodCost: ( |
138 | | - Number(row.currentPeriodCost ?? 0) + (usageByUser.get(row.userId) ?? 0) |
| 113 | + (includeLegacyBaseline ? Number(row.currentPeriodCost ?? 0) : 0) + |
| 114 | + (usageByUser.get(row.userId) ?? 0) |
139 | 115 | ).toString(), |
140 | 116 | billingPeriodStart, |
141 | 117 | billingPeriodEnd, |
|
0 commit comments