@@ -313,9 +313,9 @@ def calculate_balance_history(
313313 ) -> dict :
314314 """Calculate historical HSA balance for charting.
315315
316- Works backwards from current balance to derive historical balances.
317- This ensures the chart shows an upward trajectory toward the current balance,
318- even when historical expenses are added after setting the current balance .
316+ Uses actual hsa_balance_history records for balance values, ensuring
317+ the chart shows accurate contribution and balance progression.
318+ Cumulative expenses are calculated separately from HSA expense records .
319319
320320 Args:
321321 user_id: The user's ID
@@ -326,59 +326,67 @@ def calculate_balance_history(
326326 """
327327 current_balance = self .get_balance (user_id )
328328
329- # Get all HSA transactions ordered by date
330- transactions = self .conn .execute ('''
331- SELECT date_of_service, amount_outflow, amount_inflow
329+ # Get HSA balance history records ordered by date
330+ history_records = self .conn .execute ('''
331+ SELECT date(recorded_at) as record_date, new_balance
332+ FROM hsa_balance_history
333+ WHERE user_id = ?
334+ ORDER BY recorded_at ASC
335+ ''' , (user_id ,)).fetchall ()
336+
337+ # Get all HSA expenses for cumulative expense calculation
338+ expenses = self .conn .execute ('''
339+ SELECT date_of_service, amount_outflow
332340 FROM expenses
333341 WHERE user_id = ? AND category = 'HSA'
334342 ORDER BY date_of_service ASC
335343 ''' , (user_id ,)).fetchall ()
336344
337- if not transactions :
345+ # Build cumulative expenses by date
346+ expense_by_date : dict = {}
347+ cumulative = 0.0
348+ for expense in expenses :
349+ date_str = expense ['date_of_service' ]
350+ outflow = float (expense ['amount_outflow' ]) if expense ['amount_outflow' ] else 0
351+ cumulative += outflow
352+ expense_by_date [date_str ] = cumulative
353+
354+ # If no history records, return current balance only
355+ if not history_records :
356+ total_expenses = cumulative if expenses else 0
338357 return {
339358 'dates' : [current_date ],
340359 'balances' : [current_balance ],
341- 'cumulative_expenses' : [0 ]
360+ 'cumulative_expenses' : [total_expenses ]
342361 }
343362
344- # Calculate the total net effect of all transactions
345- # net_effect = sum(inflow) - sum(outflow)
346- # This represents total reimbursements drawn from the HSA
347- total_net_effect = 0.0
348- total_outflow = 0.0
349- for transaction in transactions :
350- outflow = float (transaction ['amount_outflow' ]) if transaction ['amount_outflow' ] else 0
351- inflow = float (transaction ['amount_inflow' ]) if transaction ['amount_inflow' ] else 0
352- total_net_effect += (inflow - outflow )
353- total_outflow += outflow
354-
355- # Work backwards: starting_balance = current_balance - net_effect_of_all_transactions
356- # This gives us what the balance would have been before any transactions
357- starting_balance = current_balance - total_net_effect
358-
359363 dates : List [str ] = []
360364 balances : List [float ] = []
361365 cumulative_expenses : List [float ] = []
362366
363- balance_at_point = starting_balance
364- cumulative_total = 0.0
367+ # Add data points from balance history
368+ for record in history_records :
369+ date_str = record ['record_date' ]
370+ balance = float (record ['new_balance' ]) if record ['new_balance' ] else 0
365371
366- for transaction in transactions :
367- date_str = transaction ['date_of_service' ]
368- outflow = float (transaction ['amount_outflow' ]) if transaction ['amount_outflow' ] else 0
369- inflow = float (transaction ['amount_inflow' ]) if transaction ['amount_inflow' ] else 0
370-
371- # Record the balance BEFORE this transaction is applied
372+ # Only add if date not already present (avoid duplicates)
372373 if not dates or dates [- 1 ] != date_str :
373374 dates .append (date_str )
374- balances .append (balance_at_point )
375- cumulative_expenses .append (cumulative_total )
376-
377- # Apply the transaction effect
378- balance_at_point += (inflow - outflow )
379- cumulative_total += outflow
380-
381- # Add current date as the final point with the actual current balance
375+ balances .append (balance )
376+
377+ # Find cumulative expenses up to this date
378+ cumulative_at_date = 0.0
379+ for exp_date , exp_cumulative in expense_by_date .items ():
380+ if exp_date <= date_str :
381+ cumulative_at_date = exp_cumulative
382+ else :
383+ break
384+ cumulative_expenses .append (cumulative_at_date )
385+ else :
386+ # Update the balance for the same date to the latest value
387+ balances [- 1 ] = balance
388+
389+ # Add current date as the final point if not already present
382390 if current_date not in dates :
383391 # Find correct chronological position
384392 insert_idx = len (dates )
@@ -387,21 +395,17 @@ def calculate_balance_history(
387395 insert_idx = i
388396 break
389397
390- # Calculate cumulative expenses up to current date
398+ # Get cumulative expenses up to current date
391399 cumulative_at_current = 0.0
392- for transaction in transactions :
393- if transaction ['date_of_service' ] > current_date :
400+ for exp_date , exp_cumulative in expense_by_date .items ():
401+ if exp_date <= current_date :
402+ cumulative_at_current = exp_cumulative
403+ else :
394404 break
395- outflow = float (transaction ['amount_outflow' ]) if transaction ['amount_outflow' ] else 0
396- cumulative_at_current += outflow
397405
398406 dates .insert (insert_idx , current_date )
399407 balances .insert (insert_idx , current_balance )
400408 cumulative_expenses .insert (insert_idx , cumulative_at_current )
401- else :
402- # If current_date matches a transaction date, update that balance to current_balance
403- idx = dates .index (current_date )
404- balances [idx ] = current_balance
405409
406410 return {
407411 'dates' : dates ,
0 commit comments