Skip to content

Commit

Permalink
Admin views financial report
Browse files Browse the repository at this point in the history
  • Loading branch information
jonallured committed Feb 10, 2024
1 parent 76b3f59 commit 4875ea5
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 0 deletions.
4 changes: 4 additions & 0 deletions app/controllers/financial_reports_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class FinancialReportsController < ApplicationController
layout "wide"
expose(:financial_report) { FinancialReport.new(params[:year].to_i) }
end
62 changes: 62 additions & 0 deletions app/models/financial_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
class FinancialReport
def self.months_for_year(year)
start_on = Date.parse("#{year}-01-01")
end_on = Date.parse("#{year}-12-01")
(start_on..end_on).uniq(&:month)
end

attr_reader :year

def initialize(year = Time.now.year)
@year = year
end

def prev_year
@year - 1
end

def next_year
@year + 1
end

def checking_data
AccountData.new(FinancialAccount.wf_checking, @year)
end

def savings_data
AccountData.new(FinancialAccount.wf_savings, @year)
end

class AccountData
def initialize(account, year)
@account = account
@year = year
end

def starting_amounts
statements.map(&:starting_amount_cents)
end

def ending_amounts
statements.map(&:ending_amount_cents)
end

def net_amounts
statements.map(&:net_amount_cents)
end

private

def compute_statements
account_statements = @account.financial_statements.for_year(@year)
FinancialReport.months_for_year(@year).map do |date|
statement_for_period = account_statements.find { |statement| statement.period_start_on == date }
statement_for_period || NullStatement.instance
end
end

def statements
@statements ||= compute_statements
end
end
end
4 changes: 4 additions & 0 deletions app/models/financial_statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ class FinancialStatement < ApplicationRecord
validates_uniqueness_of :period_start_on, scope: :financial_account

scope :for_year, ->(year) { where("extract(year from period_start_on) = ?", year) }

def net_amount_cents
ending_amount_cents - starting_amount_cents
end
end
12 changes: 12 additions & 0 deletions app/models/null_statement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class NullStatement
include Singleton

def starting_amount_cents
end

def ending_amount_cents
end

def net_amount_cents
end
end
1 change: 1 addition & 0 deletions app/views/dashboard/show.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
%h1 Dashboard
%p= link_to "Artsy Viewer", artsy_viewer_path
%p= link_to "Cybertail", cybertail_path
%p= link_to "Financial Report", financial_report_path(year: Time.now.year)
%p= link_to "Fairing Direball", faring_direball_path
%p= link_to "Reading List", reading_list_path(year: Time.now.year)
%p= link_to "Root", root_path
Expand Down
5 changes: 5 additions & 0 deletions app/views/financial_reports/_amount_cents.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- if amount_cents
%td.text-right{class: "#{amount_cents < 0 && 'text-purple'}"}
= number_to_currency(amount_cents / 100.0, negative_format: "(%u%n)")
- else
%td.text-right -
66 changes: 66 additions & 0 deletions app/views/financial_reports/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
- content_for :header do
%nav.flex.justify-between.items-center.py-6.border-b-8.border-dark-gray
%p.m-0= link_to "prev", financial_report_path(financial_report.prev_year)
%h1.m-0= financial_report.year
%p.m-0= link_to "next", financial_report_path(financial_report.next_year)

- checking_data = financial_report.checking_data
- savings_data = financial_report.savings_data

%h2 Checking

%table.text-sm.table-fixed
%thead
%tr
%th.text-left(class="w-[70px]") Month
%th.text-right(class="w-[120px]") Jan
%th.text-right(class="w-[120px]") Feb
%th.text-right(class="w-[120px]") Mar
%th.text-right(class="w-[120px]") Apr
%th.text-right(class="w-[120px]") May
%th.text-right(class="w-[120px]") Jun
%th.text-right(class="w-[120px]") Jul
%th.text-right(class="w-[120px]") Aug
%th.text-right(class="w-[120px]") Sep
%th.text-right(class="w-[120px]") Oct
%th.text-right(class="w-[120px]") Nov
%th.text-right(class="w-[120px]") Dec
%tbody
%tr
%td.text-left Starting
= render partial: "amount_cents", collection: checking_data.starting_amounts
%tr
%td.text-left Ending
= render partial: "amount_cents", collection: checking_data.ending_amounts
%tr
%td.text-left Net
= render partial: "amount_cents", collection: checking_data.net_amounts

%h2 Savings

%table.text-sm.table-fixed
%thead
%tr
%th.text-left(class="w-[70px]") Month
%th.text-right(class="w-[120px]") Jan
%th.text-right(class="w-[120px]") Feb
%th.text-right(class="w-[120px]") Mar
%th.text-right(class="w-[120px]") Apr
%th.text-right(class="w-[120px]") May
%th.text-right(class="w-[120px]") Jun
%th.text-right(class="w-[120px]") Jul
%th.text-right(class="w-[120px]") Aug
%th.text-right(class="w-[120px]") Sep
%th.text-right(class="w-[120px]") Oct
%th.text-right(class="w-[120px]") Nov
%th.text-right(class="w-[120px]") Dec
%tbody
%tr
%td.text-left Starting
= render partial: "amount_cents", collection: savings_data.starting_amounts
%tr
%td.text-left Ending
= render partial: "amount_cents", collection: savings_data.ending_amounts
%tr
%td.text-left Net
= render partial: "amount_cents", collection: savings_data.net_amounts
7 changes: 7 additions & 0 deletions app/views/layouts/wide.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
!!!
%html
%head
= render partial: "layouts/shared_head"
= yield :head
%body.bg-off-black.text-off-white.selection:bg-pink.p-4.md:p-0.mx-8
= render partial: "layouts/shared_main"
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
get "cybertail", to: "cybertail#index"
get "dashboard", to: "dashboard#show"
get "faring_direball", to: "faring_direball#index"
get "financial_reports/:year", to: "financial_reports#show", as: :financial_report
get "reading-list/:year", to: "reading_list#index", as: :reading_list
get "today", to: "today#show"
get "wishlist", to: "wishlist#index"
Expand Down
128 changes: 128 additions & 0 deletions spec/system/financial_reports/admin_views_financial_report_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require "rails_helper"

describe "Admin views financial reports" do
include_context "admin password matches"

before do
FactoryBot.create(:usb_checking)
FactoryBot.create(:wf_checking)
FactoryBot.create(:wf_savings)
end

let!(:last_year) { Date.today - 1.year }

scenario "with no statements for that year" do
visit "/financial_reports/#{last_year}"

header = page.find("header nav h1")
expect(header.text).to eq last_year.year.to_s

prev_link, next_link = page.all("header nav a").to_a

expect(prev_link.text).to eq "prev"
expect(prev_link["href"]).to end_with((last_year.year - 1).to_s)

expect(next_link.text).to eq "next"
expect(next_link["href"]).to end_with((last_year.year + 1).to_s)

page.all("table").to_a.each do |table|
month_row, starting_row, ending_row, net_row = table.all("tr").to_a

expect(month_row.all("th").map(&:text)).to eq %w[
Month Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
]

expect(starting_row.all("td").map(&:text)).to eq %w[
Starting - - - - - - - - - - - -
]

expect(ending_row.all("td").map(&:text)).to eq %w[
Ending - - - - - - - - - - - -
]

expect(net_row.all("td").map(&:text)).to eq %w[
Net - - - - - - - - - - - -
]
end
end

scenario "with one statement in the middle of that year" do
FactoryBot.create(
:financial_statement,
financial_account: FinancialAccount.wf_checking,
period_start_on: Date.parse("#{last_year.year}-05-01"),
starting_amount_cents: 1_034_89,
ending_amount_cents: 99_00
)

visit "/financial_reports/#{last_year}"

checking_table = page.all("table").to_a.first
_month_tr, starting_tr, ending_tr, net_tr = checking_table.all("tr").to_a

may_starting_td = starting_tr.all("td").to_a[5]
expect(may_starting_td.text).to eq "$1,034.89"

may_ending_td = ending_tr.all("td").to_a[5]
expect(may_ending_td.text).to eq "$99.00"

may_net_td = net_tr.all("td").to_a[5]
expect(may_net_td.text).to eq "($935.89)"
end

scenario "with statements for every month of that year" do
FinancialReport.months_for_year(last_year.year).each do |date|
FactoryBot.create(
:financial_statement,
financial_account: FinancialAccount.wf_checking,
period_start_on: date,
starting_amount_cents: 1_034_89,
ending_amount_cents: 99_00
)

FactoryBot.create(
:financial_statement,
financial_account: FinancialAccount.wf_savings,
period_start_on: date,
starting_amount_cents: 99_00,
ending_amount_cents: 1_034_89
)
end

visit "/financial_reports/#{last_year}"

checking_table, savings_table = page.all("table").to_a

within checking_table do
_month_tr, starting_tr, ending_tr, net_tr = checking_table.all("tr").to_a

_starting_label, *starting_tds = starting_tr.all("td").to_a
expect(starting_tds.count).to eq 12
expect(starting_tds.map(&:text).uniq).to eq ["$1,034.89"]

_ending_label, *ending_tds = ending_tr.all("td").to_a
expect(ending_tds.count).to eq 12
expect(ending_tds.map(&:text).uniq).to eq ["$99.00"]

_net_label, *net_tds = net_tr.all("td").to_a
expect(net_tds.count).to eq 12
expect(net_tds.map(&:text).uniq).to eq ["($935.89)"]
end

within savings_table do
_month_tr, starting_tr, ending_tr, net_tr = savings_table.all("tr").to_a

_starting_label, *starting_tds = starting_tr.all("td").to_a
expect(starting_tds.count).to eq 12
expect(starting_tds.map(&:text).uniq).to eq ["$99.00"]

_ending_label, *ending_tds = ending_tr.all("td").to_a
expect(ending_tds.count).to eq 12
expect(ending_tds.map(&:text).uniq).to eq ["$1,034.89"]

_net_label, *net_tds = net_tr.all("td").to_a
expect(net_tds.count).to eq 12
expect(net_tds.map(&:text).uniq).to eq ["$935.89"]
end
end
end

0 comments on commit 4875ea5

Please sign in to comment.