Skip to content

Commit

Permalink
add historical data to financial metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
kaiomagalhaes committed Dec 13, 2023
1 parent f4a1051 commit fa09b20
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 29 deletions.
24 changes: 19 additions & 5 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2023-12-13 18:57:39 UTC using RuboCop version 1.56.2.
# on 2023-12-13 21:43:07 UTC using RuboCop version 1.56.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 4
# Offense count: 5
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 24
Max: 29

# Offense count: 4
# Offense count: 5
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 16
Max: 19

# Offense count: 1
# Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter:
Exclude:
- 'app/utils/analytics/finances/models/financial_statements_of_work.rb'

# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
Max: 124
25 changes: 25 additions & 0 deletions app/models/statement_of_work_financial_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: statement_of_work_financial_reports
#
# id :bigint not null, primary key
# end_date :datetime
# start_date :datetime
# total_executed_income :float
# created_at :datetime not null
# updated_at :datetime not null
# statement_of_work_id :integer not null
#
# Indexes
#
# index_sow_financial_reports_on_sow_id (statement_of_work_id)
#
class StatementOfWorkFinancialReport < ApplicationRecord
belongs_to :statement_of_work

scope :ending_on, lambda { |filter|
where(end_date: filter.to_date)
}
end
24 changes: 24 additions & 0 deletions app/tasks/update_statement_of_work_financial_reports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

class UpdateStatementOfWorkFinancialReports
def self.update!
initial_date = 2.years.ago.beginning_of_day.to_date
today = Time.zone.now.end_of_day.to_date

StatementOfWorkFinancialReport.destroy_all

Project.find(2).statement_of_works.active_in_period(initial_date, today).each do |statement_of_work|
start_date = [initial_date, statement_of_work.start_date].max.to_date

(start_date..today).each do |end_date|
previous_period_model_calculator =
Analytics::Finances::Calculators::CalculatorBuilder.build(statement_of_work,
start_date, end_date)
executed_income_to_start_date = previous_period_model_calculator.total_executed_income

StatementOfWorkFinancialReport.create!(statement_of_work:, start_date:,
end_date:, total_executed_income: executed_income_to_start_date)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ module Analytics
module Finances
module Calculators
class CalculatorBuilder
def self.build(statement_of_work, start_date, end_date)
def self.build(statement_of_work, start_date, end_date, executed_income_to_start_date = 0)
case statement_of_work.model
when 'maintenance'
MaintenanceCalculator.new(statement_of_work, start_date, end_date)
when 'time_and_materials'
TimeAndMaterialsCalculator.new(statement_of_work, start_date, end_date)
TimeAndMaterialsCalculator.new(statement_of_work, start_date, end_date, executed_income_to_start_date)
when 'fixed_bid'
FixedBidCalculator.new(statement_of_work, start_date, end_date)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def calculate!
financial_resource_assignment = financial_item_by_name(assignment.user.name, assignment.user.slug)

expected_income = add_expected_income(assigned_expected_income(assignment))

financial_resource_assignment.add_expected_income(expected_income)

executed_income = add_executed_income(assigned_executed_income(assignment))
Expand Down Expand Up @@ -76,6 +77,7 @@ def executed_hours(assignment)
def assignment_expected_cost(assignment)
start_date = assignment.start_date
end_date = assignment.end_date

work_days = ([start_date, @start_date].max...[end_date, @end_date].min).select do |date|
(1..5).cover?(date.wday)
end
Expand All @@ -84,7 +86,7 @@ def assignment_expected_cost(assignment)
salary = assignment.user.salary_on_date(work_day)

8 * (salary&.hourly_cost || 0)
end.sum
end.sum * assignment.coverage
end

def assignment_executed_cost(assignment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,12 @@ def months_difference
month_diff
end

def assigned_executed_income(assignment)
assignment_value(assignment)
def assigned_executed_income(_assignment)
0
end

def assigned_expected_income(assignment)
assignment_value(assignment)
end

def assignment_value(assignment)
(assignment.coverage / @statement_of_work.requirements.sum(&:coverage)) * @statement_of_work.total_revenue
def assigned_expected_income(_assignment)
0
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ module Analytics
module Finances
module Calculators
class TimeAndMaterialsCalculator < FinancesCalculator
def initialize(statement_of_work, start_date, end_date)
attr_reader :total_executed_income, :total_expected_income

def initialize(statement_of_work, start_date, end_date, executed_income_to_start_date = 0)
@total_executed_income = 0
@total_expected_income = 0
@executed_income_to_start_date = executed_income_to_start_date

super(statement_of_work, start_date, end_date)
end
Expand All @@ -15,11 +18,10 @@ def income_limit
@statement_of_work.total_revenue
end

attr_reader :total_executed_income, :total_expected_income

def add_executed_income(income)
if @total_executed_income + income > income_limit
income_to_add = income_limit - @total_executed_income
income_to_add = 0
if (@total_executed_income + income + @executed_income_to_start_date) >= income_limit
income_to_add = [income_limit - @total_executed_income - @executed_income_to_start_date, 0].max
@total_executed_income = income_limit
else
income_to_add = income
Expand All @@ -30,8 +32,8 @@ def add_executed_income(income)
end

def add_expected_income(income)
if @total_expected_income + income > income_limit
income_to_add = income_limit - @total_expected_income
if @total_expected_income + income + @executed_income_to_start_date > income_limit
income_to_add = [income_limit - @total_expected_income - @executed_income_to_start_date, 0].max
@total_expected_income = income_limit
else
income_to_add = income
Expand Down
6 changes: 4 additions & 2 deletions app/utils/analytics/finances/models/financial_projects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ module Models
class FinancialProjects < FinancialReport
def calculate!
projects.each do |project|
finances = Analytics::Finances::Models::FinancialStatementsOfWork.new(project, @start_date, @end_date)
finances = Analytics::Finances::Models::FinancialStatementsOfWork.new(project, @start_date, @end_date,
false)

add_executed_income(finances.total_executed_income)
add_expected_income(finances.total_expected_income)
add_executed_cost(finances.total_executed_cost)
add_expected_cost(finances.total_expected_cost)

finances.financial_items.each do |financial_item|
existing_item = financial_item_by_name(project.name, project.slug)
existing_item = financial_item_by_name(financial_item.name, project.slug)
existing_item.merge(financial_item)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,47 @@ module Analytics
module Finances
module Models
class FinancialStatementsOfWork < FinancialReport
def initialize(project, start_date, end_date)
def initialize(project, start_date, end_date, resource_level = true)
@project = project
@resource_level = resource_level
super(start_date, end_date)
end

def calculate!
@project.statement_of_works.active_in_period(@start_date, @end_date).each do |statement_of_work|
model_calculator = Calculators::CalculatorBuilder.build(statement_of_work, @start_date, @end_date)
statement_of_work_start_date = statement_of_work.start_date.to_datetime

executed_income_to_start_date = 0
if @start_date > statement_of_work_start_date
report_to_date = StatementOfWorkFinancialReport.where(statement_of_work:).ending_on(@start_date - 1.day).first
executed_income_to_start_date = report_to_date&.total_executed_income || 0
end

model_calculator = Calculators::CalculatorBuilder.build(statement_of_work, @start_date, @end_date,
executed_income_to_start_date)

add_executed_income(model_calculator.total_executed_income)
add_expected_income(model_calculator.total_expected_income)
add_executed_cost(model_calculator.total_executed_cost)
add_expected_cost(model_calculator.total_expected_cost)

model_calculator.financial_items.each do |financial_item|
existing_item = financial_item_by_name(financial_item.name, financial_item.slug)
name = financial_item_name(financial_item, statement_of_work)
existing_item = financial_item_by_name(name, financial_item.slug)
existing_item.merge(financial_item)
end
end
end

def financial_item_name(financial_item, statement_of_work)
return financial_item.name if @resource_level

sow_name(statement_of_work)
end

def sow_name(sow)
"#{sow.project.name} - #{sow.name} #{sow.start_date.strftime('%m/%d/%y')} - #{sow.end_date.strftime('%m/%d/%y')}"
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class CreateStatementOfWorkFinancialReports < ActiveRecord::Migration[7.0]
def change
create_table :statement_of_work_financial_reports do |t|
# Remove index: true from here
t.integer :statement_of_work_id, null: false
t.datetime :start_date
t.datetime :end_date
t.float :total_executed_income

t.timestamps
end

# Add the index after the create_table block
# You can provide a custom shorter name if needed
add_index :statement_of_work_financial_reports, :statement_of_work_id, name: 'index_sow_financial_reports_on_sow_id'
end
end
12 changes: 11 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions spec/factories/statement_of_work_financial_reports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: statement_of_work_financial_reports
#
# id :bigint not null, primary key
# end_date :datetime
# start_date :datetime
# total_executed_income :float
# created_at :datetime not null
# updated_at :datetime not null
# statement_of_work_id :integer not null
#
# Indexes
#
# index_sow_financial_reports_on_sow_id (statement_of_work_id)
#
FactoryBot.define do
factory :statement_of_work_financial_report do
statement_of_work { nil }
start_date { '2023-12-13 19:57:44' }
end_date { '2023-12-13 19:57:44' }
total_executed_income { 1.5 }
end
end
23 changes: 23 additions & 0 deletions spec/models/statement_of_work_financial_report_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: statement_of_work_financial_reports
#
# id :bigint not null, primary key
# end_date :datetime
# start_date :datetime
# total_executed_income :float
# created_at :datetime not null
# updated_at :datetime not null
# statement_of_work_id :integer not null
#
# Indexes
#
# index_sow_financial_reports_on_sow_id (statement_of_work_id)
#
require 'rails_helper'

RSpec.describe StatementOfWorkFinancialReport, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

0 comments on commit fa09b20

Please sign in to comment.