mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-18 20:59:39 +02:00
fix: subcategories are not properly handled for budget allocations (#1844)
* fix: `allocated_spending` logic * fix: subcategories exceeding parent limit * refactor: budget allocations and max allocation logic * feat: add stream for budget category form validation * feat: update uncategorized value via stream, refactor confirm button with stream * fix: ensure live updates for parent & sibling budgets in Turbo Stream * fix: lint issues
This commit is contained in:
parent
077694bbde
commit
fb6c6fa6bb
7 changed files with 60 additions and 13 deletions
|
@ -133,10 +133,10 @@ class Budget < ApplicationRecord
|
|||
end
|
||||
|
||||
# =============================================================================
|
||||
# Budget allocations: How much user has budgeted for all categories combined
|
||||
# Budget allocations: How much user has budgeted for all parent categories combined
|
||||
# =============================================================================
|
||||
def allocated_spending
|
||||
budget_categories.sum(:budgeted_spending)
|
||||
budget_categories.reject { |bc| bc.subcategory? }.sum(&:budgeted_spending)
|
||||
end
|
||||
|
||||
def allocated_percent
|
||||
|
|
|
@ -79,4 +79,29 @@ class BudgetCategory < ApplicationRecord
|
|||
|
||||
segments
|
||||
end
|
||||
|
||||
def siblings
|
||||
budget.budget_categories.select { |bc| bc.category.parent_id == category.parent_id && bc.id != id }
|
||||
end
|
||||
|
||||
def max_allocation
|
||||
return nil unless subcategory?
|
||||
|
||||
parent_budget = budget.budget_categories.find { |bc| bc.category.id == category.parent_id }&.budgeted_spending
|
||||
siblings_budget = siblings.sum(&:budgeted_spending)
|
||||
|
||||
[ parent_budget - siblings_budget, 0 ].max
|
||||
end
|
||||
|
||||
def subcategories
|
||||
return BudgetCategory.none unless category.parent_id.nil?
|
||||
|
||||
budget.budget_categories
|
||||
.joins(:category)
|
||||
.where(categories: { parent_id: category.id })
|
||||
end
|
||||
|
||||
def subcategory?
|
||||
category.parent_id.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<% currency = Money::Currency.new(budget_category.budget.currency) %>
|
||||
|
||||
<div class="w-full flex gap-3">
|
||||
<div id=<%= dom_id(budget_category, :form) %> class="w-full flex gap-3">
|
||||
<div class="w-1 h-3 rounded-xl mt-1" style="background-color: <%= budget_category.category.color %>"></div>
|
||||
|
||||
<div class="text-sm mr-3">
|
||||
|
@ -22,6 +22,7 @@
|
|||
step: currency.step,
|
||||
id: dom_id(budget_category, :budgeted_spending),
|
||||
min: 0,
|
||||
max: budget_category.max_allocation,
|
||||
data: { auto_submit_form_target: "auto" } %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
11
app/views/budget_categories/_confirm_button.html.erb
Normal file
11
app/views/budget_categories/_confirm_button.html.erb
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div id="<%= dom_id(budget, :confirm_button) %>">
|
||||
<% if budget.allocations_valid? %>
|
||||
<%= link_to "Confirm",
|
||||
budget_path(budget),
|
||||
class: "block btn btn--primary w-full text-center" %>
|
||||
<% else %>
|
||||
<span class="block btn btn--secondary w-full text-center text-gray-400 cursor-not-allowed">
|
||||
Confirm
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<% budget_category = budget.uncategorized_budget_category %>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<div id="<%= dom_id(budget, :uncategorized_budget_category_form) %>" class="flex gap-3">
|
||||
<div class="w-1 h-3 rounded-xl mt-1" style="background-color: <%= budget_category.category.color %>"></div>
|
||||
|
||||
<div class="text-sm mr-3">
|
||||
|
|
|
@ -45,15 +45,7 @@
|
|||
<%= render "budget_categories/uncategorized_budget_category_form", budget: @budget %>
|
||||
</div>
|
||||
|
||||
<% if @budget.allocations_valid? %>
|
||||
<%= link_to "Confirm",
|
||||
budget_path(@budget),
|
||||
class: "block btn btn--primary w-full text-center" %>
|
||||
<% else %>
|
||||
<span class="block btn btn--secondary w-full text-center text-gray-400 cursor-not-allowed">
|
||||
Confirm
|
||||
</span>
|
||||
<% end %>
|
||||
<%= render "budget_categories/confirm_button", budget: @budget %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1 +1,19 @@
|
|||
<%= turbo_stream.replace dom_id(@budget, :allocation_progress), partial: "budget_categories/allocation_progress", locals: { budget: @budget } %>
|
||||
|
||||
<%= turbo_stream.replace dom_id(@budget, :uncategorized_budget_category_form), partial: "budget_categories/uncategorized_budget_category_form", locals: { budget: @budget } %>
|
||||
|
||||
<%= turbo_stream.replace dom_id(@budget, :confirm_button), partial: "budget_categories/confirm_button", locals: { budget: @budget } %>
|
||||
|
||||
|
||||
<% if @budget_category.subcategory? %>
|
||||
<%# Update sibling subcategories when a subcategory changes %>
|
||||
<% @budget_category.siblings.each do |sibling| %>
|
||||
<%= turbo_stream.update dom_id(sibling, :form), partial: "budget_categories/budget_category_form", locals: { budget_category: sibling } %>
|
||||
<% end %>
|
||||
|
||||
<% else %>
|
||||
<%# Update all subcategories when a parent category changes %>
|
||||
<% @budget_category.subcategories.each do |subcategory| %>
|
||||
<%= turbo_stream.update dom_id(subcategory, :form), partial: "budget_categories/budget_category_form", locals: { budget_category: subcategory } %>
|
||||
<% end %>
|
||||
<% end %>
|
Loading…
Add table
Add a link
Reference in a new issue