@@ -56,20 +56,23 @@ export function calculateDailyBudget(params: {
5656 // This prevents spending everything before a small income and starving the next period
5757 const minWindowDays = 14 ;
5858
59+ // Clamp day to last day of month so e.g. expected_day=31 in April resolves to 30
60+ const clampToMonth = ( day : number ) => Math . min ( resolveDay ( day ) , daysInMonth ) ;
61+
5962 // Collect all income events with absolute day positions
6063 const incomeEvents : { absDay : number ; amount : number } [ ] = [ ] ;
6164 for ( const i of unreceivedIncomes ) {
62- const d = resolveDay ( i . expectedDay ) ;
63- if ( d > today && d <= daysInMonth ) {
65+ const d = clampToMonth ( i . expectedDay ) ;
66+ if ( d > today ) {
6467 incomeEvents . push ( { absDay : d , amount : i . amount } ) ;
6568 }
6669 }
6770
6871 // Add next month's income events if window extends past month end
6972 if ( allIncomes ) {
70- const allDays = allIncomes . map ( ( i ) => resolveDay ( i . expectedDay ) ) . sort ( ( a , b ) => a - b ) ;
71- for ( const d of allDays ) {
72- incomeEvents . push ( { absDay : daysInMonth + d , amount : allIncomes . find ( ( i ) => resolveDay ( i . expectedDay ) === d ) ! . amount } ) ;
73+ for ( const i of allIncomes ) {
74+ const d = clampToMonth ( i . expectedDay ) ;
75+ incomeEvents . push ( { absDay : daysInMonth + d , amount : i . amount } ) ;
7376 }
7477 }
7578 incomeEvents . sort ( ( a , b ) => a . absDay - b . absDay ) ;
@@ -80,16 +83,18 @@ export function calculateDailyBudget(params: {
8083 const totalDays = endAbsDay - today ;
8184 if ( totalDays <= 0 ) return { dailyBudget : 0 , tightestSegment : null , segmentCount : 0 } ;
8285
83- // Collect obligations in window with absolute due day
86+ // Collect obligations in window with absolute due day (clamp to last day of month)
8487 const obligationEvents : { absDay : number ; amount : number } [ ] = [ ] ;
8588 for ( const b of unpaidBills ) {
86- if ( b . dueDay > today && b . dueDay <= Math . min ( daysInMonth , endAbsDay ) ) {
87- obligationEvents . push ( { absDay : b . dueDay , amount : b . amount } ) ;
89+ const d = clampToMonth ( b . dueDay ) ;
90+ if ( d > today && d <= endAbsDay ) {
91+ obligationEvents . push ( { absDay : d , amount : b . amount } ) ;
8892 }
8993 }
9094 for ( const d of debts ) {
91- if ( d . dueDay > today && d . dueDay <= Math . min ( daysInMonth , endAbsDay ) && d . amount > 0 ) {
92- obligationEvents . push ( { absDay : d . dueDay , amount : d . amount } ) ;
95+ const day = clampToMonth ( d . dueDay ) ;
96+ if ( day > today && day <= endAbsDay && d . amount > 0 ) {
97+ obligationEvents . push ( { absDay : day , amount : d . amount } ) ;
9398 }
9499 }
95100 if ( endAbsDay > daysInMonth ) {
0 commit comments