From 80cde2d222b067ce4ae985d4bd2d87e9a9328016 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 17:38:49 -0700 Subject: [PATCH 1/9] [#4183] HourlyDeliverable#total (budget) should only include labor budget. --- app/models/hourly_deliverable.rb | 3 +-- test/unit/hourly_deliverable_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/hourly_deliverable.rb b/app/models/hourly_deliverable.rb index f32d372..91afde2 100644 --- a/app/models/hourly_deliverable.rb +++ b/app/models/hourly_deliverable.rb @@ -19,8 +19,7 @@ class HourlyDeliverable < Deliverable return 0 if contract.billable_rate.blank? return 0 if labor_budgets.count == 0 && overhead_budgets.count == 0 - hours = labor_budgets.sum(:hours) + overhead_budgets.sum(:hours) - return contract.billable_rate * hours + return contract.billable_rate * labor_budgets.sum(:hours) end # Block setting the total on HourlyDeliverables diff --git a/test/unit/hourly_deliverable_test.rb b/test/unit/hourly_deliverable_test.rb index 63856d1..611d762 100644 --- a/test/unit/hourly_deliverable_test.rb +++ b/test/unit/hourly_deliverable_test.rb @@ -20,13 +20,13 @@ class HourlyDeliverableTest < ActiveSupport::TestCase assert_equal 0, d.total end - should "multiply the total number of budgeted hours by the contract billable rate" do + should "multiply the total number of labor budget hours by the contract billable rate" do contract = Contract.generate!(:billable_rate => 100.0) d = HourlyDeliverable.generate!(:contract => contract) d.labor_budgets << LaborBudget.generate!(:hours => 10) d.overhead_budgets << OverheadBudget.generate!(:hours => 20) - assert_equal 100.0 * 30, d.total + assert_equal 100.0 * 10, d.total end end From 5d09a035c569828c695594e23c383baf59d1c3cd Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 17:43:00 -0700 Subject: [PATCH 2/9] [#4183] Updated HourlyDeliverable#profit_budget test to match the example. --- test/unit/hourly_deliverable_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/hourly_deliverable_test.rb b/test/unit/hourly_deliverable_test.rb index 611d762..2117799 100644 --- a/test/unit/hourly_deliverable_test.rb +++ b/test/unit/hourly_deliverable_test.rb @@ -52,7 +52,7 @@ class HourlyDeliverableTest < ActiveSupport::TestCase context "#profit_budget" do setup do - @contract = Contract.generate!(:billable_rate => 100.0) + @contract = Contract.generate!(:billable_rate => 150.0) @deliverable = HourlyDeliverable.generate!(:contract => @contract) end @@ -63,12 +63,12 @@ class HourlyDeliverableTest < ActiveSupport::TestCase end should "be the total minus the sum of all of the budgets' amounts" do - LaborBudget.generate!(:deliverable => @deliverable, :hours => 10, :budget => 2000) - LaborBudget.generate!(:deliverable => @deliverable, :hours => 5, :budget => 1000) - OverheadBudget.generate!(:deliverable => @deliverable, :hours => 15, :budget => 2000) + LaborBudget.generate!(:deliverable => @deliverable, :hours => 5, :budget => 250) + LaborBudget.generate!(:deliverable => @deliverable, :hours => 5, :budget => 250) + OverheadBudget.generate!(:deliverable => @deliverable, :hours => 3, :budget => 225) - assert_equal 30 * 100, @deliverable.total - assert_equal 3000 - (2000 + 1000 + 2000), @deliverable.profit_budget + assert_equal 1500, @deliverable.total + assert_equal 1500 - (225 + 250 + 250), @deliverable.profit_budget end end end From de42a3212199750e83c498b823da45e19e74adcb Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 17:49:50 -0700 Subject: [PATCH 3/9] [#4183] Implemented FixedDeliverable#total_spent --- app/models/fixed_deliverable.rb | 5 +++++ test/unit/fixed_deliverable_test.rb | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/models/fixed_deliverable.rb b/app/models/fixed_deliverable.rb index a856bc0..5432ada 100644 --- a/app/models/fixed_deliverable.rb +++ b/app/models/fixed_deliverable.rb @@ -15,6 +15,11 @@ class FixedDeliverable < Deliverable read_attribute(:total) || 0.0 end + # Fixed deliverables are always 100% spent + def total_spent + total + end + # The amount of profit that is budgeted for this deliverable. # Profit = Total - ( Labor + Overhead + Fixed + Markup ) def profit_budget diff --git a/test/unit/fixed_deliverable_test.rb b/test/unit/fixed_deliverable_test.rb index f46cc01..86f610a 100644 --- a/test/unit/fixed_deliverable_test.rb +++ b/test/unit/fixed_deliverable_test.rb @@ -21,4 +21,10 @@ class FixedDeliverableTest < ActiveSupport::TestCase assert_equal 0, FixedDeliverable.generate(:total => nil).profit_budget end end + + context "#total_spent" do + should "equal the budgeted total" do + assert_equal 1000, FixedDeliverable.generate(:total => 1_000).total_spent + end + end end From 597062fdf890b1a55879edb81941ae010996d0e0 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:00:35 -0700 Subject: [PATCH 4/9] [#4183] Implemented FixedDeliverable#profit_left (was called profit_spent). --- app/models/fixed_deliverable.rb | 6 +++++ test/unit/fixed_deliverable_test.rb | 37 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/app/models/fixed_deliverable.rb b/app/models/fixed_deliverable.rb index 5432ada..c944114 100644 --- a/app/models/fixed_deliverable.rb +++ b/app/models/fixed_deliverable.rb @@ -26,6 +26,12 @@ class FixedDeliverable < Deliverable budgets = labor_budget_total + overhead_budget_total (total || 0.0) - budgets end + + # The amount of money remaining after expenses have been taken out + # Profit left = Total - Labor spent - Overhead spent + def profit_left + total_spent - labor_budget_spent - overhead_spent + end # Hardcoded value used as a wrapper for the old Budget plugin API. # diff --git a/test/unit/fixed_deliverable_test.rb b/test/unit/fixed_deliverable_test.rb index 86f610a..b480d76 100644 --- a/test/unit/fixed_deliverable_test.rb +++ b/test/unit/fixed_deliverable_test.rb @@ -27,4 +27,41 @@ class FixedDeliverableTest < ActiveSupport::TestCase assert_equal 1000, FixedDeliverable.generate(:total => 1_000).total_spent end end + + context "#profit_left" do + should "be the total_spent minus the labor budget spent minus the overhead budget spent" do + configure_overhead_plugin + + @project = Project.generate! + @developer = User.generate! + @manager = User.generate! + @role = Role.generate! + User.add_to_project(@developer, @project, @role) + User.add_to_project(@manager, @project, @role) + @rate = Rate.generate!(:project => @project, + :user => @developer, + :date_in_effect => Date.yesterday, + :amount => 55) + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.yesterday, + :amount => 75) + + @deliverable_1 = FixedDeliverable.generate!(:total => 2000) + @deliverable_1.issues << @issue1 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue1, :project => @project, + :activity => @billable_activity, + :user => @developer) + TimeEntry.generate!(:hours => 4, :issue => @issue1, :project => @project, + :activity => @non_billable_activity, + :user => @manager) + + # Check intermediate values + assert_equal 825, @deliverable_1.labor_budget_spent + assert_equal 300, @deliverable_1.overhead_spent + + assert_equal 875, @deliverable_1.profit_left + + end + end end From cd3ba5e5e53a2d5635ec389df6c74b8a2858a862 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:11:02 -0700 Subject: [PATCH 5/9] [#4183] Implemented HourlyDeliverable#total_spent. --- app/models/hourly_deliverable.rb | 16 ++++++++++++++++ test/unit/hourly_deliverable_test.rb | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/models/hourly_deliverable.rb b/app/models/hourly_deliverable.rb index 91afde2..e183114 100644 --- a/app/models/hourly_deliverable.rb +++ b/app/models/hourly_deliverable.rb @@ -21,6 +21,22 @@ class HourlyDeliverable < Deliverable return contract.billable_rate * labor_budgets.sum(:hours) end + + # Total amount to be billed on the deliverable, using the total time logged + # and the contract rate + def total_spent + return 0 if contract.nil? + return 0 if contract.billable_rate.blank? + return 0 unless self.issues.count > 0 + + time_logs = self.issues.collect(&:time_entries).flatten + hours = time_logs.inject(0) {|total, time_entry| + total += time_entry.hours if time_entry.billable? + total + } + + return hours * contract.billable_rate + end # Block setting the total on HourlyDeliverables def total=(v) diff --git a/test/unit/hourly_deliverable_test.rb b/test/unit/hourly_deliverable_test.rb index 2117799..d1e5ffc 100644 --- a/test/unit/hourly_deliverable_test.rb +++ b/test/unit/hourly_deliverable_test.rb @@ -29,6 +29,26 @@ class HourlyDeliverableTest < ActiveSupport::TestCase assert_equal 100.0 * 10, d.total end end + + context "#total_spent" do + should "be equal to the number of hours used multipled by the contract rate" do + configure_overhead_plugin + + contract = Contract.generate!(:billable_rate => 150.0) + @project = Project.generate! + @developer = User.generate! + @role = Role.generate! + User.add_to_project(@developer, @project, @role) + + d = HourlyDeliverable.generate!(:contract => contract) + d.issues << @issue1 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue1, :project => @project, + :activity => @billable_activity, + :user => @developer) + + assert_equal 2250, d.total_spent + end + end context "#total=" do should "not write any attributes" do From 1f22b203b657c6ceb32211970998818cedbd2514 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:15:29 -0700 Subject: [PATCH 6/9] [#4183] Implemented HourlyDeliverable#profit_left. --- app/models/hourly_deliverable.rb | 6 +++++ test/unit/hourly_deliverable_test.rb | 38 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/app/models/hourly_deliverable.rb b/app/models/hourly_deliverable.rb index e183114..ddaa8c5 100644 --- a/app/models/hourly_deliverable.rb +++ b/app/models/hourly_deliverable.rb @@ -53,4 +53,10 @@ class HourlyDeliverable < Deliverable budgets = labor_budget_total + overhead_budget_total (total || 0.0) - budgets end + + # The amount of money remaining after expenses have been taken out + # Profit left = Total - Labor spent - Overhead spent + def profit_left + total_spent - labor_budget_spent - overhead_spent + end end diff --git a/test/unit/hourly_deliverable_test.rb b/test/unit/hourly_deliverable_test.rb index d1e5ffc..9c51191 100644 --- a/test/unit/hourly_deliverable_test.rb +++ b/test/unit/hourly_deliverable_test.rb @@ -91,4 +91,42 @@ class HourlyDeliverableTest < ActiveSupport::TestCase assert_equal 1500 - (225 + 250 + 250), @deliverable.profit_budget end end + + context "#profit_left" do + should "be equal to the total to bill (total_spent) minus the labor budget spent minus the overhead spent" do + configure_overhead_plugin + + contract = Contract.generate!(:billable_rate => 150.0) + @project = Project.generate! + @developer = User.generate! + @manager = User.generate! + @role = Role.generate! + User.add_to_project(@developer, @project, @role) + User.add_to_project(@manager, @project, @role) + @rate = Rate.generate!(:project => @project, + :user => @developer, + :date_in_effect => Date.yesterday, + :amount => 55) + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.yesterday, + :amount => 75) + + @deliverable_1 = HourlyDeliverable.generate!(:contract => contract) + @deliverable_1.issues << @issue1 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue1, :project => @project, + :activity => @billable_activity, + :user => @developer) + TimeEntry.generate!(:hours => 4, :issue => @issue1, :project => @project, + :activity => @non_billable_activity, + :user => @manager) + + # Check intermediate values + assert_equal 825, @deliverable_1.labor_budget_spent + assert_equal 300, @deliverable_1.overhead_spent + + assert_equal 1125, @deliverable_1.profit_left + end + end + end From 850d6adb543619a4dde1ced1d3acad438ce63766 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:19:58 -0700 Subject: [PATCH 7/9] [#4183] Updated contract tests from the Deliverable calculation changes. --- test/unit/contract_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/contract_test.rb b/test/unit/contract_test.rb index a269d92..bd17446 100644 --- a/test/unit/contract_test.rb +++ b/test/unit/contract_test.rb @@ -163,7 +163,7 @@ class ContractTest < ActiveSupport::TestCase LaborBudget.generate!(:deliverable => @deliverable_2, :hours => 10) OverheadBudget.generate!(:deliverable => @deliverable_2, :hours => 20) - assert_equal 10_000 + (30 * 100), contract.total_budget + assert_equal 10_000 + (10 * 100), contract.total_budget end end @@ -180,8 +180,8 @@ class ContractTest < ActiveSupport::TestCase OverheadBudget.generate!(:deliverable => @deliverable_2, :hours => 20) assert_equal 10_000 - 4000, @deliverable_1.profit_budget - assert_equal (30 * 100.0) - 0, @deliverable_2.profit_budget - assert_equal 9000, contract.profit_budget + assert_equal (10 * 100.0) - 0, @deliverable_2.profit_budget + assert_equal 7000, contract.profit_budget end end From 361b971100a567892c4dfbeda3c84c8aef7414ce Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:25:00 -0700 Subject: [PATCH 8/9] [#4183] Implemented Contract#total_spent --- app/models/contract.rb | 7 ++++++- test/unit/contract_test.rb | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/models/contract.rb b/app/models/contract.rb index 9ddb58f..ec7a6d0 100644 --- a/app/models/contract.rb +++ b/app/models/contract.rb @@ -38,7 +38,7 @@ class Contract < ActiveRecord::Base define_method(mthd) { "TODO in later release" } end - [:total_spent, + [ :profit_spent ].each do |mthd| define_method(mthd) { "TODO" } @@ -81,6 +81,11 @@ class Contract < ActiveRecord::Base deliverables.inject(0) {|total, deliverable| total += deliverable.total } end + # OPTIMIZE: N+1 + def total_spent + deliverables.inject(0) {|total, deliverable| total += deliverable.total_spent } + end + # OPTIMIZE: N+1 def profit_budget deliverables.inject(0) {|total, deliverable| total += deliverable.profit_budget } diff --git a/test/unit/contract_test.rb b/test/unit/contract_test.rb index bd17446..beeeca1 100644 --- a/test/unit/contract_test.rb +++ b/test/unit/contract_test.rb @@ -185,4 +185,28 @@ class ContractTest < ActiveSupport::TestCase end end + + context "#total_spent" do + should "sum all of the total spents on the Deliverables" do + configure_overhead_plugin + + contract = Contract.generate!(:billable_rate => 150.0) + + @project = Project.generate! + @developer = User.generate! + @role = Role.generate! + User.add_to_project(@developer, @project, @role) + + contract.deliverables << @deliverable_1 = FixedDeliverable.generate!(:total => 10_000) + contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!(:contract => contract) + @deliverable_2.issues << @issue1 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue1, :project => @project, + :activity => @billable_activity, + :user => @developer) + + assert_equal 10_000, @deliverable_1.total_spent + assert_equal 2250, @deliverable_2.total_spent + assert_equal 12_250, contract.total_spent + end + end end From 41e221119c96ec37f6ea2ea7dd8dc1c082114621 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Tue, 13 Jul 2010 18:32:35 -0700 Subject: [PATCH 9/9] [#4183] Implemented Contract#profit_left. --- app/models/contract.rb | 12 +++++----- test/unit/contract_test.rb | 48 +++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/app/models/contract.rb b/app/models/contract.rb index ec7a6d0..ba23fdf 100644 --- a/app/models/contract.rb +++ b/app/models/contract.rb @@ -38,12 +38,6 @@ class Contract < ActiveRecord::Base define_method(mthd) { "TODO in later release" } end - [ - :profit_spent - ].each do |mthd| - define_method(mthd) { "TODO" } - end - # OPTIMIZE: N+1 def labor_budget deliverables.inject(0) {|total, deliverable| total += deliverable.labor_budget_total } @@ -91,6 +85,12 @@ class Contract < ActiveRecord::Base deliverables.inject(0) {|total, deliverable| total += deliverable.profit_budget } end + # OPTIMIZE: N+1 + def profit_left + deliverables.inject(0) {|total, deliverable| total += deliverable.profit_left } + end + alias_method :profit_spent, :profit_left + PaymentTerms = { :net_0 => :text_payment_terms_net_0, :net_15 => :text_payment_terms_net_15, diff --git a/test/unit/contract_test.rb b/test/unit/contract_test.rb index beeeca1..bb02ef0 100644 --- a/test/unit/contract_test.rb +++ b/test/unit/contract_test.rb @@ -208,5 +208,51 @@ class ContractTest < ActiveSupport::TestCase assert_equal 2250, @deliverable_2.total_spent assert_equal 12_250, contract.total_spent end - end + end + + context "#profit_left" do + should "sum all of the profit left on all of the Deliverables" do + configure_overhead_plugin + + contract = Contract.generate!(:billable_rate => 150.0) + + @project = Project.generate! + @developer = User.generate! + @manager = User.generate! + @role = Role.generate! + User.add_to_project(@developer, @project, @role) + User.add_to_project(@manager, @project, @role) + @rate = Rate.generate!(:project => @project, + :user => @developer, + :date_in_effect => Date.yesterday, + :amount => 55) + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.yesterday, + :amount => 75) + + contract.deliverables << @deliverable_1 = FixedDeliverable.generate!(:total => 2000) + @deliverable_1.issues << @issue1 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue1, :project => @project, + :activity => @billable_activity, + :user => @developer) + TimeEntry.generate!(:hours => 4, :issue => @issue1, :project => @project, + :activity => @non_billable_activity, + :user => @manager) + + contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!(:contract => contract) + @deliverable_2.issues << @issue2 = Issue.generate_for_project!(@project) + TimeEntry.generate!(:hours => 15, :issue => @issue2, :project => @project, + :activity => @billable_activity, + :user => @developer) + TimeEntry.generate!(:hours => 4, :issue => @issue2, :project => @project, + :activity => @non_billable_activity, + :user => @manager) + + assert_equal 875, @deliverable_1.profit_left + assert_equal 1125, @deliverable_2.profit_left + assert_equal 2000, contract.profit_left + end + + end end