From 0a1bb9169df738fd5057ba49cd57f5882bfcdb6b Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Thu, 9 Sep 2010 14:22:25 -0700 Subject: [PATCH] [#4420] Hook up Retainer's total_spent and profit_left for periods. --- app/models/fixed_deliverable.rb | 2 +- app/models/hourly_deliverable.rb | 6 +- app/models/retainer_deliverable.rb | 34 +++++++ test/unit/retainer_deliverable_test.rb | 133 ++++++++++++++++++++++++- 4 files changed, 168 insertions(+), 7 deletions(-) diff --git a/app/models/fixed_deliverable.rb b/app/models/fixed_deliverable.rb index f1c5034..0c2fd03 100644 --- a/app/models/fixed_deliverable.rb +++ b/app/models/fixed_deliverable.rb @@ -16,7 +16,7 @@ class FixedDeliverable < Deliverable end # Fixed deliverables are always 100% spent - def total_spent + def total_spent(date=nil) total end diff --git a/app/models/hourly_deliverable.rb b/app/models/hourly_deliverable.rb index 11601a6..5fb3792 100644 --- a/app/models/hourly_deliverable.rb +++ b/app/models/hourly_deliverable.rb @@ -24,7 +24,7 @@ class HourlyDeliverable < Deliverable # Total amount to be billed on the deliverable, using the total time logged # and the contract rate - def total_spent + def total_spent(date=nil) return 0 if contract.nil? return 0 if contract.billable_rate.blank? return 0 unless self.issues.count > 0 @@ -56,7 +56,7 @@ class HourlyDeliverable < Deliverable # 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 + def profit_left(date=nil) + total_spent(date) - labor_budget_spent(date) - overhead_spent(date) end end diff --git a/app/models/retainer_deliverable.rb b/app/models/retainer_deliverable.rb index 5b558cf..b06f094 100644 --- a/app/models/retainer_deliverable.rb +++ b/app/models/retainer_deliverable.rb @@ -111,6 +111,40 @@ class RetainerDeliverable < HourlyDeliverable end end + def total_spent(date=nil) + if date + if within_date_range?(date) + # TODO: duplicated on HourlyDeliverable#total_spent + return 0 if contract.nil? + return 0 if contract.billable_rate.blank? + return 0 unless self.issues.count > 0 + + issue_ids = self.issues.collect(&:id) + if issue_ids.present? + time_logs = TimeEntry.all(:conditions => ["#{Issue.table_name}.id IN (:issue_ids) AND tyear = (:year) AND tmonth = (:month)", + {:issue_ids => issue_ids, + :year => date.year, + :month => date.month} + ], + :include => :issue) + end + time_logs ||= [] + hours = time_logs.inject(0) {|total, time_entry| + total += time_entry.hours if time_entry.billable? + total + } + + return hours * contract.billable_rate + + else + 0 # outside of range + end + else + super + end + + end + # TODO: stolen directly from redmine_overhead but with a block option def labor_budget_spent_with_filter(&block) return 0.0 unless self.issues.size > 0 diff --git a/test/unit/retainer_deliverable_test.rb b/test/unit/retainer_deliverable_test.rb index 577f91a..7ace8a5 100644 --- a/test/unit/retainer_deliverable_test.rb +++ b/test/unit/retainer_deliverable_test.rb @@ -271,7 +271,71 @@ class RetainerDeliverableTest < ActiveSupport::TestCase end end - # context "#profit_left" + # (Labor used * contract rate) - (labor used * time rate) - (overhead used * time rate) + context "#profit_left" do + setup do + @project = Project.generate! + @contract = Contract.generate!(:billable_rate => 200, :project => @project) + @deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract) + @deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10) + @deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10) + @deliverable.save! + + @manager = User.generate! + @role = Role.generate! + User.add_to_project(@manager, @project, @role) + + configure_overhead_plugin + + @issue1 = Issue.generate_for_project!(@project) + @time_entry1 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @billable_activity, + :spent_on => Date.new(2010,1,2), + :hours => 10, + :user => @manager) + @time_entry2 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @non_billable_activity, + :spent_on => Date.new(2010,2,1), + :hours => 20, + :user => @manager) + + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.new(2010,1,1), + :amount => 100) + + @deliverable.issues << @issue1 + + + end + + context "with a empty period" do + should "use all periods" do + assert_equal (10 * 200) - (10 * 100) - (20 * 100), @deliverable.profit_left(nil) + end + end + + context "with a period out of the retainer range" do + should "filter the records periods" do + assert_equal 0, @deliverable.profit_left(Date.new(2011,1,1)) + end + end + + context "with an invalid period" do + should "return 0" do + assert_equal 0, @deliverable.profit_left('1') + end + end + + context "with a period in the retainer range" do + should "filter the records" do + assert_equal (0 * 200) - (0 * 100) - (20 * 100), @deliverable.profit_left(Date.new(2010,2,1)) + end + end + end + context "#profit_budget" do setup do @@ -311,8 +375,71 @@ class RetainerDeliverableTest < ActiveSupport::TestCase end - # context "#total_spent" - + context "#total_spent" do + setup do + @project = Project.generate! + @contract = Contract.generate!(:billable_rate => 200, :project => @project) + @deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract) + @deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10) + @deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10) + @deliverable.save! + + @manager = User.generate! + @role = Role.generate! + User.add_to_project(@manager, @project, @role) + + configure_overhead_plugin + + @issue1 = Issue.generate_for_project!(@project) + @time_entry1 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @billable_activity, + :spent_on => Date.new(2010,1,2), + :hours => 10, + :user => @manager) + @time_entry2 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @billable_activity, + :spent_on => Date.new(2010,2,1), + :hours => 20, + :user => @manager) + + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.new(2010,1,1), + :amount => 100) + + @deliverable.issues << @issue1 + + + end + + context "with a empty period" do + should "use all periods" do + # Labor used * contract rate + assert_equal (10+20) * 200, @deliverable.total_spent(nil) + end + end + + context "with a period out of the retainer range" do + should "filter the records periods" do + assert_equal 0, @deliverable.total_spent(Date.new(2011,1,1)) + end + end + + context "with an invalid period" do + should "return 0" do + assert_equal 0, @deliverable.total_spent('1') + end + end + + context "with a period in the retainer range" do + should "filter the records" do + assert_equal 20 * 200, @deliverable.total_spent(Date.new(2010,2,1)) + end + end + end + context "#total" do setup do @contract = Contract.generate!(:billable_rate => 100)