From 785f67000a379f724dc570d463922ad0bf8d66f8 Mon Sep 17 00:00:00 2001 From: Benjamin Randolph <104036158+neb417@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:09:55 -0700 Subject: [PATCH] Federal tax bracket radio buttons (#48) * Add radio buttons to dashboard * add route to submit form when button selected. * Render updated figures. * Refactor radio buttons. * Lint. * Add prys. * Update factories. * Lint. --- Gemfile | 4 +- Gemfile.lock | 14 ++- app/controllers/concerns/stream.rb | 20 +--- app/controllers/concerns/taxed_income.rb | 9 +- app/controllers/dashboard_controller.rb | 6 ++ app/services/federal_tax_calculator.rb | 7 +- app/views/dashboard/_index.html.erb | 99 ++++++++++++++++++++ app/views/dashboard/index.html.erb | 84 +---------------- config/routes.rb | 1 + spec/factories/federal_tax_brackets.rb | 6 +- spec/services/federal_tax_calculator_spec.rb | 3 +- 11 files changed, 139 insertions(+), 114 deletions(-) create mode 100644 app/views/dashboard/_index.html.erb diff --git a/Gemfile b/Gemfile index cc840dd..7e9c186 100644 --- a/Gemfile +++ b/Gemfile @@ -60,7 +60,9 @@ group :development, :test do gem "faker" gem "launchy" gem "orderly" - gem "pry" + gem "pry-byebug" + gem "pry-rescue" + gem "pry-rails" gem "rspec-rails" gem "shoulda-matchers" gem "simplecov" diff --git a/Gemfile.lock b/Gemfile.lock index 985be7e..32f36b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,6 +76,7 @@ GEM bootsnap (1.16.0) msgpack (~> 1.2) builder (3.2.4) + byebug (11.1.3) capybara (3.38.0) addressable matrix @@ -109,6 +110,7 @@ GEM importmap-rails (1.1.6) actionpack (>= 6.0.0) railties (>= 6.0.0) + interception (0.5) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) @@ -166,6 +168,14 @@ GEM pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) + pry-byebug (3.10.1) + byebug (~> 11.0) + pry (>= 0.13, < 0.15) + pry-rails (0.3.9) + pry (>= 0.10.4) + pry-rescue (1.6.0) + interception (>= 0.5) + pry (>= 0.12.0) public_suffix (5.0.1) puma (5.6.5) nio4r (~> 2.0) @@ -315,7 +325,9 @@ DEPENDENCIES net-http orderly pg (~> 1.1) - pry + pry-byebug + pry-rails + pry-rescue puma (~> 5.0) rails (~> 7.0.5) redis (~> 4.0) diff --git a/app/controllers/concerns/stream.rb b/app/controllers/concerns/stream.rb index 1497ad8..8ecb8d0 100644 --- a/app/controllers/concerns/stream.rb +++ b/app/controllers/concerns/stream.rb @@ -30,25 +30,7 @@ def switch_to_salary def render_dashboard [ - turbo_stream.replace("hourly_budget", - partial: "budget/salary_budget", - locals: build_locals(tax_on_salary)), - turbo_stream.replace("salary_budget", - partial: "budget/hourly_budget", - locals: build_locals(tax_on_hourly)), - turbo_stream.replace("total_costs", - partial: "shared/total_costs", - locals: { - total_annual_cost: @total_annual_cost, - total_monthly_cost: @total_monthly_cost, - total_bi_weekly_cost: @total_bi_weekly_cost - }), - turbo_stream.replace("taxed_incomes", - partial: "shared/taxed_incomes", - locals: { - salary_taxed: @salary_taxed, - hourly_taxed: @hourly_taxed - }) + turbo_stream.replace("primary_frame", partial: "dashboard/index") ] end end diff --git a/app/controllers/concerns/taxed_income.rb b/app/controllers/concerns/taxed_income.rb index a1e59cc..d577c7b 100644 --- a/app/controllers/concerns/taxed_income.rb +++ b/app/controllers/concerns/taxed_income.rb @@ -8,8 +8,6 @@ def build_income_tax_variables! @hourly_taxed = build_income_tax_object(income: hourly_income) end - private - def salary_income @salary_income = Income.find_by(income_type: "Salary").weekly_income * 52 end @@ -18,8 +16,13 @@ def hourly_income @hourly_income = Income.find_by(income_type: "Hourly").weekly_income * 52 end + def federal_tax_table_type_id + filter = params[:federal_tax_table_type_id].present? ? {id: params[:federal_tax_table_type_id]} : {filing_status: "single"} + @federal_tax_table_type_id = FederalTaxTableType.find_by(filter).id + end + def build_income_tax_object(income:) - federal_tax = FederalTaxCalculator.call(income: income) + federal_tax = FederalTaxCalculator.call(income: income, federal_tax_table_type_id: federal_tax_table_type_id) fica_tax = FicaTaxCalculator.call(income: income) state_tax = StateTaxCalculator.call(income: income) net_income = income - (fica_tax + federal_tax + state_tax) diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index d069d72..189a80d 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,7 +1,13 @@ class DashboardController < ApplicationController include DashboardBuilder + include Stream def index build_dashboard_variables! end + + def switch_taxable_income + build_dashboard_variables! + render_respond_to(render_dashboard) + end end diff --git a/app/services/federal_tax_calculator.rb b/app/services/federal_tax_calculator.rb index 4fc3eca..aa658e8 100644 --- a/app/services/federal_tax_calculator.rb +++ b/app/services/federal_tax_calculator.rb @@ -1,8 +1,9 @@ class FederalTaxCalculator include Callable - def initialize(income:) + def initialize(income:, federal_tax_table_type_id:) self.income = income + self.federal_tax_table_type_id = federal_tax_table_type_id end def call @@ -11,10 +12,10 @@ def call private - attr_accessor :income + attr_accessor :income, :federal_tax_table_type_id def calculate - bracket = FederalTaxBracket.where("bottom_range_cents <= ?", income.fractional).order(:bottom_range_cents).last + bracket = FederalTaxBracket.where(federal_tax_table_type_id: federal_tax_table_type_id).where("bottom_range_cents <= ?", income.fractional).order(:bottom_range_cents).last taxable_at_bracket_rate = Money.new(income - bracket.bottom_range) rated = bracket.rate * taxable_at_bracket_rate rated + bracket.cumulative diff --git a/app/views/dashboard/_index.html.erb b/app/views/dashboard/_index.html.erb new file mode 100644 index 0000000..7766b83 --- /dev/null +++ b/app/views/dashboard/_index.html.erb @@ -0,0 +1,99 @@ +<%= turbo_frame_tag "primary_frame" do %> +
+
+
+

Budget Calculator

+
+
+ +
+
+
+

Final Budget

+
Savings Rate %
+
Investing Rate %
+
+
+
+ <%= render partial: "components/income_switch" %> +
+ +
+ <%= form_with model: @savings_rate, local: true do |form| %> +
+ <%= form.number_field :rate, in: 0.00..50.00, step: 0.25, value: @savings_rate.display_rate, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-1/4 h-7" %> + <%= form.submit "Save", class:"btn btn-primary ml-4 mt-2 py-0.5 my-auto"%> +
+ <% end %> +
+ +
+ <%= form_with model: @investing_rate, local: true do |form| %> +
+ <%= form.number_field :rate, in: 0.00..50.00, step: 0.25, value: @investing_rate.display_rate, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-1/4 h-7" %> + <%= form.submit "Invest", class:"btn btn-primary ml-4 mt-2 py-0.5 my-auto"%> +
+ <% end %> +
+
+ +
+ Filing status + <%= form_with url: switch_taxable_income_path, local: true do |form| %> +
+ <% FederalTaxTableType.all.each do |table| %> +
+ <%= form.radio_button :federal_tax_table_type_id, table.id, checked: table.id == @federal_tax_table_type_id, onchange: "this.form.requestSubmit()" %> + <%= form.label :federal_tax_table_type_id, table.filing_status.humanize.capitalize %> +
+ <% end %> +
+ <% end %> +
+ +
+ <%= render partial: "budget/salary_budget", + locals: { + income: @salary_taxed, + total_cost: @total_cost, + investing_amount: @salary_invest, + savings_amount: @salary_saving, + guilt_free: @guilt_free_salary + } + %> +
+
+
+ +
+
+ <%= turbo_frame_tag "income_header_frame" do %> +

Income

+ <%= link_to "New Income", new_income_path, data: { turbo_frame: :incomes }, class: "btn btn-primary" %> + <% end %> + +
+ <%= render "incomes/index" %> +
+ +
+ <%= render partial: "shared/taxed_incomes", + locals: { salary_taxed: @salary_taxed, hourly_taxed: @hourly_taxed } %> +
+
+
+ +
+
+ <%= turbo_frame_tag "fixed_expense_header_frame" do %> +

Fixed Expenses

+ <%= link_to "New Fixed Expense", new_fixed_expense_path, data: { turbo_frame: :fixed_expenses }, class: "btn btn-primary" %> + <% end %> + + <%= render "fixed_expenses/index" %> + + <%= render partial: "shared/total_costs", locals: { total_cost: @total_cost } %> +
+
+
+<% end %> diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index 29aaf00..5ff9740 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -1,83 +1 @@ -
-
-
-

Budget Calculator

-
-
- -
-
-
-

Final Budget

-
Savings Rate %
-
Investing Rate %
-
-
-
- <%= render partial: "components/income_switch" %> -
- -
- <%= form_with model: @savings_rate, local: true do |form| %> -
- <%= form.number_field :rate, in: 0.00..50.00, step: 0.25, value: @savings_rate.display_rate, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-1/4 h-7" %> - <%= form.submit "Save", class:"btn btn-primary ml-4 mt-2 py-0.5 my-auto"%> -
- <% end %> -
- -
- <%= form_with model: @investing_rate, local: true do |form| %> -
- <%= form.number_field :rate, in: 0.00..50.00, step: 0.25, value: @investing_rate.display_rate, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-1/4 h-7" %> - <%= form.submit "Invest", class:"btn btn-primary ml-4 mt-2 py-0.5 my-auto"%> -
- <% end %> -
- -
-
- <%= render partial: "budget/salary_budget", - locals: { - income: @salary_taxed, - total_cost: @total_cost, - investing_amount: @salary_invest, - savings_amount: @salary_saving, - guilt_free: @guilt_free_salary - } - %> -
-
-
- -
-
- <%= turbo_frame_tag "income_header_frame" do %> -

Income

- <%= link_to "New Income", new_income_path, data: { turbo_frame: :incomes }, class: "btn btn-primary" %> - <% end %> - -
- <%= render "incomes/index" %> -
- -
- <%= render partial: "shared/taxed_incomes", - locals: { salary_taxed: @salary_taxed, hourly_taxed: @hourly_taxed } %> -
-
-
- -
-
- <%= turbo_frame_tag "fixed_expense_header_frame" do %> -

Fixed Expenses

- <%= link_to "New Fixed Expense", new_fixed_expense_path, data: { turbo_frame: :fixed_expenses }, class: "btn btn-primary" %> - <% end %> - - <%= render "fixed_expenses/index" %> - - <%= render partial: "shared/total_costs", locals: { total_cost: @total_cost } %> -
-
-
+<%= render partial: "dashboard/index" %> diff --git a/config/routes.rb b/config/routes.rb index 0fca9f2..c83a1ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,4 +5,5 @@ root "dashboard#index" resources :incomes post "/income_switch", to: "incomes#income_switch" + post "/switch_taxable_income", to: "dashboard#switch_taxable_income", as: "switch_taxable_income" end diff --git a/spec/factories/federal_tax_brackets.rb b/spec/factories/federal_tax_brackets.rb index 08daadc..8564003 100644 --- a/spec/factories/federal_tax_brackets.rb +++ b/spec/factories/federal_tax_brackets.rb @@ -52,9 +52,9 @@ end trait :with_all_tiers do - after :create do |_record| - create(:federal_tax_bracket, :tier_2) - create(:federal_tax_bracket, :tier_3) + after :create do |record| + create(:federal_tax_bracket, :tier_2, federal_tax_table_type: record.federal_tax_table_type) + create(:federal_tax_bracket, :tier_3, federal_tax_table_type: record.federal_tax_table_type) end end # diff --git a/spec/services/federal_tax_calculator_spec.rb b/spec/services/federal_tax_calculator_spec.rb index 0ab8377..65e5008 100644 --- a/spec/services/federal_tax_calculator_spec.rb +++ b/spec/services/federal_tax_calculator_spec.rb @@ -5,7 +5,8 @@ RSpec.describe FederalTaxCalculator, type: :service do subject(:service) do described_class.call( - income: salary_income.rate + income: salary_income.rate, + federal_tax_table_type_id: tax_brackets.federal_tax_table_type_id ) end