1111import { MeterUsageBreakdown , QuotaMetric } from '../../../src/types/usage' ;
1212import { MeteringService , meteringService } from './meteringService' ;
1313import { TieredPricingCalculator } from './tieredPricingCalculator' ;
14+ import { useInvoiceStore } from '../../../src/store/invoiceStore' ;
15+ import { useSubscriptionStore } from '../../../src/store/subscriptionStore' ;
16+ import { InvoiceStatus } from '../../../src/types/invoice' ;
17+ import { buildBillingPeriod } from '../../../src/utils/invoice' ;
1418
1519export interface UsageBillingCloseEntry {
1620 userId : string ;
@@ -46,7 +50,7 @@ export class UsageBillingCloseCron {
4650
4751 start ( ) : void {
4852 if ( this . intervalHandle ) return ;
49- this . intervalHandle = setInterval ( ( ) => this . runOnce ( ) , this . intervalMs ) ;
53+ this . intervalHandle = setInterval ( ( ) => { this . runOnce ( ) . catch ( console . error ) ; } , this . intervalMs ) ;
5054 if ( this . intervalHandle . unref ) this . intervalHandle . unref ( ) ;
5155 }
5256
@@ -58,13 +62,15 @@ export class UsageBillingCloseCron {
5862 }
5963
6064 /** Closes the current period for every registered account and resets it. */
61- runOnce ( ) : UsageBillingCloseReport {
65+ async runOnce ( ) : Promise < UsageBillingCloseReport > {
6266 const entries : UsageBillingCloseEntry [ ] = [ ] ;
6367
6468 for ( const account of this . accounts ) {
6569 const unitsUsed = this . service . getCurrentPeriodConsumption ( account . userId , account . metricType ) ;
6670 const priced = account . calculator . calculate ( unitsUsed ) ;
6771 const includedUnits = priced . lines . find ( ( l ) => l . tier . unitPrice === 0 ) ?. unitsInTier ?? 0 ;
72+ const billableUnits = Math . max ( 0 , unitsUsed - includedUnits ) ;
73+ const amount = priced . totalAmount ;
6874
6975 entries . push ( {
7076 userId : account . userId ,
@@ -73,12 +79,30 @@ export class UsageBillingCloseCron {
7379 metric : account . metric ,
7480 unitsUsed,
7581 includedUnits,
76- billableUnits : Math . max ( 0 , unitsUsed - includedUnits ) ,
77- amount : priced . totalAmount ,
82+ billableUnits,
83+ amount,
7884 } ,
7985 } ) ;
8086
8187 this . service . resetPeriod ( account . userId , account . metricType ) ;
88+
89+ if ( amount > 0 ) {
90+ const sub = useSubscriptionStore . getState ( ) . subscriptions . find ( s => s . id === account . userId ) ;
91+ if ( sub ) {
92+ try {
93+ const period = buildBillingPeriod ( sub ) ;
94+ const invoiceStore = useInvoiceStore . getState ( ) ;
95+ const invoice = await invoiceStore . generateInvoiceFromSubscription ( {
96+ subscription : sub ,
97+ period,
98+ notes : `Usage overage for ${ account . metricType } (${ billableUnits } billable units)`
99+ } ) ;
100+ await invoiceStore . updateInvoiceStatus ( invoice . id , InvoiceStatus . DRAFT ) ;
101+ } catch ( err ) {
102+ console . error ( 'Failed to generate usage invoice' , err ) ;
103+ }
104+ }
105+ }
82106 }
83107
84108 return { closedAt : new Date ( ) . toISOString ( ) , entries } ;
0 commit comments