11import { db } from '@sim/db'
22import { member , subscription , userStats } from '@sim/db/schema'
3- import { and , eq , sql } from 'drizzle-orm'
3+ import { and , eq , inArray , sql } from 'drizzle-orm'
44import type Stripe from 'stripe'
55import { DEFAULT_OVERAGE_THRESHOLD } from '@/lib/billing/constants'
66import { calculateSubscriptionOverage , getPlanPricing } from '@/lib/billing/core/billing'
@@ -178,8 +178,9 @@ export async function checkAndBillOverageThreshold(userId: string): Promise<void
178178 : Math . floor ( Date . now ( ) / 1000 )
179179 const billingPeriod = new Date ( periodEnd * 1000 ) . toISOString ( ) . slice ( 0 , 7 )
180180
181- const timestamp = Date . now ( )
182- const idempotencyKey = `threshold-overage:${ customerId } :${ stripeSubscriptionId } :${ billingPeriod } :${ timestamp } `
181+ const amountCents = Math . round ( amountToBill * 100 )
182+ const totalOverageCents = Math . round ( currentOverage * 100 )
183+ const idempotencyKey = `threshold-overage:${ customerId } :${ stripeSubscriptionId } :${ billingPeriod } :${ totalOverageCents } :${ amountCents } `
183184
184185 logger . info ( 'Creating threshold overage invoice' , {
185186 userId,
@@ -190,7 +191,7 @@ export async function checkAndBillOverageThreshold(userId: string): Promise<void
190191 } )
191192
192193 try {
193- const cents = Math . round ( amountToBill * 100 )
194+ const cents = amountCents
194195
195196 const invoiceId = await createAndFinalizeOverageInvoice ( stripe , {
196197 customerId,
@@ -199,7 +200,7 @@ export async function checkAndBillOverageThreshold(userId: string): Promise<void
199200 description : `Threshold overage billing – ${ billingPeriod } ` ,
200201 itemDescription : `Usage overage ($${ amountToBill . toFixed ( 2 ) } )` ,
201202 metadata : {
202- type : 'threshold_overage ' ,
203+ type : 'overage_threshold_billing ' ,
203204 userId,
204205 subscriptionId : stripeSubscriptionId ,
205206 billingPeriod,
@@ -288,22 +289,22 @@ export async function checkAndBillOrganizationOverageThreshold(
288289 return
289290 }
290291
291- let totalTeamUsage = 0
292- let totalBilledOverage = 0
292+ let totalTeamUsage = parseDecimal ( ownerStatsLock [ 0 ] . currentPeriodCost )
293+ const totalBilledOverage = parseDecimal ( ownerStatsLock [ 0 ] . billedOverageThisPeriod )
293294
294- for ( const m of members ) {
295- const memberStats = await tx
295+ const nonOwnerIds = members . filter ( ( m ) => m . userId !== owner . userId ) . map ( ( m ) => m . userId )
296+
297+ if ( nonOwnerIds . length > 0 ) {
298+ const memberStatsRows = await tx
296299 . select ( {
300+ userId : userStats . userId ,
297301 currentPeriodCost : userStats . currentPeriodCost ,
298- billedOverageThisPeriod : userStats . billedOverageThisPeriod ,
299302 } )
300303 . from ( userStats )
301- . where ( eq ( userStats . userId , m . userId ) )
302- . limit ( 1 )
304+ . where ( inArray ( userStats . userId , nonOwnerIds ) )
303305
304- if ( memberStats . length > 0 ) {
305- totalTeamUsage += parseDecimal ( memberStats [ 0 ] . currentPeriodCost )
306- totalBilledOverage += parseDecimal ( memberStats [ 0 ] . billedOverageThisPeriod )
306+ for ( const stats of memberStatsRows ) {
307+ totalTeamUsage += parseDecimal ( stats . currentPeriodCost )
307308 }
308309 }
309310
@@ -342,9 +343,10 @@ export async function checkAndBillOrganizationOverageThreshold(
342343 ? Math . floor ( orgSubscription . periodEnd . getTime ( ) / 1000 )
343344 : Math . floor ( Date . now ( ) / 1000 )
344345 const billingPeriod = new Date ( periodEnd * 1000 ) . toISOString ( ) . slice ( 0 , 7 )
345- const timestamp = Date . now ( )
346+ const amountCents = Math . round ( amountToBill * 100 )
347+ const totalOverageCents = Math . round ( currentOverage * 100 )
346348
347- const idempotencyKey = `threshold-overage-org:${ customerId } :${ stripeSubscriptionId } :${ billingPeriod } :${ timestamp } `
349+ const idempotencyKey = `threshold-overage-org:${ customerId } :${ stripeSubscriptionId } :${ billingPeriod } :${ totalOverageCents } : ${ amountCents } `
348350
349351 logger . info ( 'Creating organization threshold overage invoice' , {
350352 organizationId,
@@ -353,7 +355,7 @@ export async function checkAndBillOrganizationOverageThreshold(
353355 } )
354356
355357 try {
356- const cents = Math . round ( amountToBill * 100 )
358+ const cents = amountCents
357359
358360 const invoiceId = await createAndFinalizeOverageInvoice ( stripe , {
359361 customerId,
@@ -362,7 +364,7 @@ export async function checkAndBillOrganizationOverageThreshold(
362364 description : `Team threshold overage billing – ${ billingPeriod } ` ,
363365 itemDescription : `Team usage overage ($${ amountToBill . toFixed ( 2 ) } )` ,
364366 metadata : {
365- type : 'threshold_overage_organization ' ,
367+ type : 'overage_threshold_billing_org ' ,
366368 organizationId,
367369 subscriptionId : stripeSubscriptionId ,
368370 billingPeriod,
0 commit comments