Skip to content

Commit

Permalink
Savings investing rates (#19)
Browse files Browse the repository at this point in the history
* Add route, controller, and form for saving types

* Migrate savings rate

* Migrate savings rate

* Add savings/investing and render on dashboard

* lint

* Update factory and test suite

* Lint
  • Loading branch information
neb417 authored Nov 18, 2023
1 parent 850d640 commit 38b2106
Show file tree
Hide file tree
Showing 36 changed files with 778 additions and 32 deletions.
6 changes: 6 additions & 0 deletions app/controllers/concerns/dashboard_builder.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# frozen_string_literal: true

module DashboardBuilder
extend ActiveSupport::Concern
include TaxedIncome
include TotalCost
include SaveIncome

def build_dashboard_variables!
@incomes = Income.order_by_type
@fixed_expenses = FixedExpense.get_ordered
@savings_rate = SavingsRate.savings
@investing_rate = SavingsRate.investing
build_taxed_income_vars!
build_savings_vars!
build_total_cost_vars!
Rails.logger.debug "\n *** Building Vars!!\n "
end
end
39 changes: 39 additions & 0 deletions app/controllers/concerns/save_income.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module SaveIncome
extend ActiveSupport::Concern
include TaxedIncome

def build_savings_vars!
@hourly_saving = hourly_saving
@hourly_invest = hourly_investing
@salary_saving = salary_saving
@salary_invest = salary_investing
end

def salary_investing
SavingsCalculator.new(tax_on_salary, set_invest_rate)
end

def salary_saving
SavingsCalculator.new(tax_on_salary, set_save_rate)
end

def hourly_investing
SavingsCalculator.new(tax_on_hourly, set_invest_rate)
end

def hourly_saving
SavingsCalculator.new(tax_on_hourly, set_save_rate)
end

private

def set_save_rate
SavingsRate.savings.rate
end

def set_invest_rate
SavingsRate.investing.rate
end
end
10 changes: 7 additions & 3 deletions app/controllers/incomes_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class IncomesController < ApplicationController
include DashboardBuilder
include TotalCost
include SaveIncome

before_action :set_income, only: %i[show edit update destroy]

Expand Down Expand Up @@ -42,7 +43,6 @@ def update
respond_to do |format|
if @income.update_from_dashboard(params: params)
build_dashboard_variables!
format.html { redirect_to root_path, notice: "Income was successfully updated." }
format.turbo_stream
else
format.html { render :edit, status: :unprocessable_entity }
Expand Down Expand Up @@ -93,13 +93,17 @@ def income_params
params.require(:income).permit(:income_type, :rate, :hours, :weekly_income)
end

def build_locals(income)
def build_locals(taxed_income)
income = taxed_income.income
build_total_cost_vars!
build_savings_vars!
{
total_annual_cost: @total_annual_cost,
total_monthly_cost: @total_monthly_cost,
total_bi_weekly_cost: @total_bi_weekly_cost,
income: income
income: taxed_income,
investing_amount: income.is_hourly? ? @hourly_invest : @salary_invest,
savings_amount: income.is_hourly? ? @hourly_saving : @salary_saving
}
end
end
74 changes: 74 additions & 0 deletions app/controllers/savings_rates_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
class SavingsRatesController < ApplicationController
include DashboardBuilder

before_action :set_savings_rate, only: %i[show edit update destroy]

# GET /savings_rates or /savings_rates.json
def index
@savings_rates = SavingsRate.all
end

# GET /savings_rates/1 or /savings_rates/1.json
def show
end

# GET /savings_rates/new
def new
@savings_rate = SavingsRate.new
end

# GET /savings_rates/1/edit
def edit
end

# POST /savings_rates or /savings_rates.json
def create
@savings_rate = SavingsRate.new(savings_rate_params)

respond_to do |format|
if @savings_rate.save
format.html { redirect_to savings_rate_url(@savings_rate), notice: "Savings rate was successfully created." }
format.json { render :show, status: :created, location: @savings_rate }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @savings_rate.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /savings_rates/1 or /savings_rates/1.json
def update
respond_to do |format|
if @savings_rate.update_from_dashboard(params: savings_rate_params)
# calculate new savings amounts for both hourly and salary and new guilt free
build_dashboard_variables!
format.turbo_stream
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @savings_rate.errors, status: :unprocessable_entity }
end
end
end

# DELETE /savings_rates/1 or /savings_rates/1.json
def destroy
@savings_rate.destroy

respond_to do |format|
format.html { redirect_to savings_rates_url, notice: "Savings rate was successfully destroyed." }
format.json { head :no_content }
end
end

private

# Use callbacks to share common setup or constraints between actions.
def set_savings_rate
@savings_rate = SavingsRate.find(params[:id])
end

# Only allow a list of trusted parameters through.
def savings_rate_params
params.require(:savings_rate).permit(:name, :rate)
end
end
2 changes: 2 additions & 0 deletions app/helpers/savings_rates_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module SavingsRatesHelper
end
31 changes: 31 additions & 0 deletions app/models/savings_rate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# == Schema Information
#
# Table name: savings_rates
#
# id :bigint not null, primary key
# name :string
# rate :float
# created_at :datetime not null
# updated_at :datetime not null
#
class SavingsRate < ApplicationRecord
SAVING_TYPES = %w[savings investing]
validates :name, presence: true, inclusion: SAVING_TYPES

def self.savings
find_by(name: "savings")
end

def self.investing
find_by(name: "investing")
end

def display_rate
(rate * 100)
end

def update_from_dashboard(params:)
rate = params[:rate].to_f / 100
update(rate: rate)
end
end
52 changes: 52 additions & 0 deletions app/services/savings_calculator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

class SavingsCalculator
attr_reader :saving_amount,
:annual_saving,
:biannual_saving,
:quarterly_saving,
:monthly_saving,
:bi_weekly_saving,
:weekly_saving,
:daily_saving

def initialize(income_type, saving_rate)
@annual_income = income_type.annual_income
@saving_rate = saving_rate
@annual_saving = calculate_savings
@bi_weekly_saving = calculate_bi_weekly_saving
@daily_saving = calculate_daily_saving
@weekly_saving = calculate_weekly_saving
@monthly_saving = calculate_monthly_saving
@quarterly_saving = calculate_quarterly_saving
@biannual_saving = calculate_biannual_saving
end

def calculate_savings
@annual_income * @saving_rate
end

def calculate_daily_saving
@annual_saving / 365
end

def calculate_weekly_saving
@annual_saving / 52
end

def calculate_bi_weekly_saving
@annual_saving / 26
end

def calculate_monthly_saving
@annual_saving / 12
end

def calculate_quarterly_saving
@annual_saving / 4
end

def calculate_biannual_saving
@annual_saving / 2
end
end
11 changes: 10 additions & 1 deletion app/views/budget/_hourly_budget.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
<%= turbo_frame_tag "hourly_budget" do %>
<div id="hourly_budget" class="py-4">

<strong>Hourly</strong>
<%= render partial: "budget/budget_headings" %>
<%= render partial: "shared/budget", locals: { total_annual_cost: total_annual_cost, total_monthly_cost: total_monthly_cost, total_bi_weekly_cost: total_bi_weekly_cost, income: income } %>
<%= render partial: "shared/budget",
locals: {
total_annual_cost: total_annual_cost,
total_monthly_cost: total_monthly_cost,
total_bi_weekly_cost: total_bi_weekly_cost,
income: income,
investing_amount: investing_amount,
savings_amount: savings_amount
} %>
</div>
<% end %>
11 changes: 10 additions & 1 deletion app/views/budget/_salary_budget.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
<div id="salary_budget" class="py-4">
<strong>Salary</strong>
<%= render partial: "budget/budget_headings" %>
<%= render partial: "shared/budget", locals: { total_annual_cost: total_annual_cost, total_monthly_cost: total_monthly_cost, total_bi_weekly_cost: total_bi_weekly_cost, income: income } %>
<% Rails.logger.debug "*** Salary_budget #{savings_amount.daily_saving}" %>
<%= render partial: "shared/budget",
locals: {
total_annual_cost: total_annual_cost,
total_monthly_cost: total_monthly_cost,
total_bi_weekly_cost: total_bi_weekly_cost,
income: income,
investing_amount: investing_amount,
savings_amount: savings_amount
} %>
</div>
<% end %>
50 changes: 44 additions & 6 deletions app/views/dashboard/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,45 @@

<div class="primary-card">
<div class="mx-auto py-6 sm:px-6 lg:px-8">
<h3>Final Budget</h3>
<div class="pt-4">
<%= render partial: "components/income_switch" %>
<div class="grid grid-cols-3">
<h3>Final Budget</h3>
<div>Savings Rate %</div>
<div>Investing Rate %</div>
</div>
<div class="grid grid-cols-3">
<div class="pt-4">
<%= render partial: "components/income_switch" %>
</div>

<div class="">
<%= form_with model: @savings_rate, local: true do |form| %>
<div class="flex">
<%= 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"%>
</div>
<% end %>
</div>

<div class="">
<%= form_with model: @investing_rate, local: true do |form| %>
<div class="flex">
<%= 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"%>
</div>
<% end %>
</div>

</div>
<div id="final_income">
<%= render partial: "budget/salary_budget", locals: { total_annual_cost: @total_annual_cost, total_monthly_cost: @total_monthly_cost, total_bi_weekly_cost: @total_bi_weekly_cost, income: @salary_taxed } %>
<%= render partial: "budget/salary_budget",
locals: {
total_annual_cost: @total_annual_cost,
total_monthly_cost: @total_monthly_cost,
total_bi_weekly_cost: @total_bi_weekly_cost, income: @salary_taxed,
investing_amount: @salary_invest,
savings_amount: @salary_saving
}
%>
</div>
</div>
</div>
Expand All @@ -32,7 +65,8 @@

<div class="pt-4">
<%= turbo_frame_tag "taxed_incomes" do %>
<%= render partial: "shared/taxed_incomes", locals: { salary_taxed: @salary_taxed, hourly_taxed: @hourly_taxed} %>
<%= render partial: "shared/taxed_incomes",
locals: { salary_taxed: @salary_taxed, hourly_taxed: @hourly_taxed} %>
<% end %>
</div>
</div>
Expand All @@ -50,7 +84,11 @@
<% end %>

<%= turbo_frame_tag "total_costs" do %>
<%= render 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 } %>
<%= render 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 } %>
<% end %>
</div>
</div>
Expand Down
Loading

0 comments on commit 38b2106

Please sign in to comment.