Skip to content

Commit f8cd118

Browse files
committed
fix: revert unrelated refactors from referral migration
- Revert org-billing.ts to main branch version (expiration bucket tracking for orgs was not part of the migration plan) - Make breakdownByExpiration and principalsByExpiration optional in CreditBalance interface (only needed for user balance, not orgs) - Add CreditBalanceWithExpiration type for code requiring full balance - Add guard in usage-display.tsx for optional expiration fields
1 parent 0d7f29a commit f8cd118

File tree

3 files changed

+42
-20
lines changed

3 files changed

+42
-20
lines changed

packages/billing/src/balance-calculator.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ export interface CreditBalance {
2828
netBalance: number
2929
breakdown: Record<GrantType, number>
3030
principals: Record<GrantType, number>
31-
// Split by expiration for UI display
31+
// Split by expiration for UI display (optional - only populated for user balance, not org balance)
32+
breakdownByExpiration?: Record<CreditExpirationBucket, Record<GrantType, number>>
33+
principalsByExpiration?: Record<CreditExpirationBucket, Record<GrantType, number>>
34+
}
35+
36+
/**
37+
* CreditBalance with required expiration bucket fields.
38+
* Returned by createInitialCreditBalance() and calculateUsageAndBalance().
39+
*/
40+
export interface CreditBalanceWithExpiration extends CreditBalance {
3241
breakdownByExpiration: Record<CreditExpirationBucket, Record<GrantType, number>>
3342
principalsByExpiration: Record<CreditExpirationBucket, Record<GrantType, number>>
3443
}
@@ -37,7 +46,7 @@ export interface CreditBalance {
3746
* Creates an initialized CreditBalance object with all values set to 0.
3847
* Used by both user and organization balance calculations to avoid code duplication.
3948
*/
40-
export function createInitialCreditBalance(): CreditBalance {
49+
export function createInitialCreditBalance(): CreditBalanceWithExpiration {
4150
const breakdown: Record<GrantType, number> = {} as Record<GrantType, number>
4251
const principals: Record<GrantType, number> = {} as Record<GrantType, number>
4352

packages/billing/src/org-billing.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import { GRANT_PRIORITIES } from '@codebuff/common/constants/grant-priorities'
2+
import { GrantTypeValues } from '@codebuff/common/types/grant'
23
import db from '@codebuff/internal/db'
34
import * as schema from '@codebuff/internal/db/schema'
45
import { withAdvisoryLockTransaction } from '@codebuff/internal/db/transaction'
56
import { env } from '@codebuff/internal/env'
67
import { stripeServer } from '@codebuff/internal/util/stripe'
78
import { and, asc, gt, isNull, or, eq } from 'drizzle-orm'
89

9-
import {
10-
consumeFromOrderedGrants,
11-
createInitialCreditBalance,
12-
} from './balance-calculator'
10+
import { consumeFromOrderedGrants } from './balance-calculator'
1311

1412
import type {
1513
CreditBalance,
16-
CreditExpirationBucket,
1714
CreditUsageAndBalance,
1815
CreditConsumptionResult,
1916
} from './balance-calculator'
@@ -187,8 +184,29 @@ export async function calculateOrganizationUsageAndBalance(
187184
// Get all relevant grants for the organization
188185
const grants = await getOrderedActiveOrganizationGrants(withDefaults)
189186

190-
// Initialize balance structure with all values set to 0
191-
const balance = createInitialCreditBalance()
187+
// Initialize breakdown and principals with all grant types set to 0
188+
const initialBreakdown: Record<GrantType, number> = {} as Record<
189+
GrantType,
190+
number
191+
>
192+
const initialPrincipals: Record<GrantType, number> = {} as Record<
193+
GrantType,
194+
number
195+
>
196+
197+
for (const type of GrantTypeValues) {
198+
initialBreakdown[type] = 0
199+
initialPrincipals[type] = 0
200+
}
201+
202+
// Initialize balance structure
203+
const balance: CreditBalance = {
204+
totalRemaining: 0,
205+
totalDebt: 0,
206+
netBalance: 0,
207+
breakdown: initialBreakdown,
208+
principals: initialPrincipals,
209+
}
192210

193211
// Calculate both metrics in one pass
194212
let usageThisCycle = 0
@@ -211,17 +229,9 @@ export async function calculateOrganizationUsageAndBalance(
211229
// Add to balance if grant is currently active
212230
if (!grant.expires_at || grant.expires_at > now) {
213231
balance.principals[grantType] += grant.principal
214-
215-
// Classify by expiration: renewable if expires_at is set, nonRenewable if null
216-
const bucket: CreditExpirationBucket = grant.expires_at
217-
? 'renewable'
218-
: 'nonRenewable'
219-
balance.principalsByExpiration[bucket][grantType] += grant.principal
220-
221232
if (grant.balance > 0) {
222233
totalPositiveBalance += grant.balance
223234
balance.breakdown[grantType] += grant.balance
224-
balance.breakdownByExpiration[bucket][grantType] += grant.balance
225235
} else if (grant.balance < 0) {
226236
totalDebt += Math.abs(grant.balance)
227237
}

web/src/app/profile/components/usage-display.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,13 @@ export const UsageDisplay = ({
246246
if (isLoading) {
247247
return <UsageDisplaySkeleton />
248248
}
249-
const { totalDebt, netBalance } = balance
249+
const { totalDebt, netBalance, breakdownByExpiration, principalsByExpiration } = balance
250250

251-
// Get expiration buckets from balance (data-driven, not hardcoded)
252-
const { breakdownByExpiration, principalsByExpiration } = balance
251+
// Handle case where expiration buckets might not be provided (e.g., org balance)
252+
// This should not happen for user balance, but TypeScript requires the check
253+
if (!breakdownByExpiration || !principalsByExpiration) {
254+
return <UsageDisplaySkeleton />
255+
}
253256

254257
const renewableTypes = getTypesInBucket('renewable', principalsByExpiration, breakdownByExpiration)
255258
const nonRenewableTypes = getTypesInBucket('nonRenewable', principalsByExpiration, breakdownByExpiration)

0 commit comments

Comments
 (0)