mirror of
https://github.com/we-promise/sure.git
synced 2026-04-21 21:14:17 +00:00
Fix budget category totals to account for refunds and reimbursements (#824)
* Fix budget category totals to net refunds against expenses
Budget spending calculations now subtract refunds (negative transactions
classified as income) from expense totals in the same category. Previously,
refunds were excluded entirely, causing budgets to show gross spending
instead of net spending.
Fixes #314
* Handle missing git binary in commit_sha initializer
Rescues Errno::ENOENT when git is not installed, falling back to
BUILD_COMMIT_SHA env var or "unknown". Fixes crash in Docker
development containers that lack git.
* Revert "Handle missing git binary in commit_sha initializer"
This reverts commit 7e58458faa.
* Subtract uncategorized refunds from overall budget spending
Uncategorized refunds were not being netted against actual_spending
because the synthetic uncategorized category has no persisted ID and
wasn't matched by the budget_categories ID set. Now checks for
category.uncategorized? in addition to the ID lookup.
* perf: optimize budget category actual spending calculation
This commit is contained in:
@@ -155,11 +155,14 @@ class Budget < ApplicationRecord
|
||||
end
|
||||
|
||||
def actual_spending
|
||||
expense_totals.total
|
||||
[ expense_totals.total - refunds_in_expense_categories, 0 ].max
|
||||
end
|
||||
|
||||
def budget_category_actual_spending(budget_category)
|
||||
expense_totals.category_totals.find { |ct| ct.category.id == budget_category.category.id }&.total || 0
|
||||
cat_id = budget_category.category_id
|
||||
expense = expense_totals_by_category[cat_id]&.total || 0
|
||||
refund = income_totals_by_category[cat_id]&.total || 0
|
||||
[ expense - refund, 0 ].max
|
||||
end
|
||||
|
||||
def category_median_monthly_expense(category)
|
||||
@@ -235,6 +238,14 @@ class Budget < ApplicationRecord
|
||||
end
|
||||
|
||||
private
|
||||
def refunds_in_expense_categories
|
||||
expense_category_ids = budget_categories.map(&:category_id).to_set
|
||||
income_totals.category_totals
|
||||
.reject { |ct| ct.category.subcategory? }
|
||||
.select { |ct| expense_category_ids.include?(ct.category.id) || ct.category.uncategorized? }
|
||||
.sum(&:total)
|
||||
end
|
||||
|
||||
def income_statement
|
||||
@income_statement ||= family.income_statement
|
||||
end
|
||||
@@ -246,4 +257,12 @@ class Budget < ApplicationRecord
|
||||
def income_totals
|
||||
@income_totals ||= family.income_statement.income_totals(period: period)
|
||||
end
|
||||
|
||||
def expense_totals_by_category
|
||||
@expense_totals_by_category ||= expense_totals.category_totals.index_by { |ct| ct.category.id }
|
||||
end
|
||||
|
||||
def income_totals_by_category
|
||||
@income_totals_by_category ||= income_totals.category_totals.index_by { |ct| ct.category.id }
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user