diff --git a/app/models/deliverable.rb b/app/models/deliverable.rb index 554efd1..d1fa66f 100644 --- a/app/models/deliverable.rb +++ b/app/models/deliverable.rb @@ -279,32 +279,44 @@ class Deliverable < ActiveRecord::Base end # Total amount spent ($) for a given activity - def spent_for_activity(activity) + def spent_for_activity(activity, options={}) + period = options[:period] || nil issues.all.inject(0.0) do |all_issues_total, issue| - all_issues_total += issue.time_entries.all(:conditions => {:activity_id => activity.id}).sum(&:cost) + conditions = ARCondition.new(["activity_id IN (?)", activity.id]) + if period.present? + conditions.add(["tyear = ? AND tmonth = ?", period.year, period.month]) + end + all_issues_total += issue.time_entries.all(:conditions => conditions.conditions).sum(&:cost) all_issues_total end end # Total hours spent for a given activity - def hours_spent_for_activity(activity) + def hours_spent_for_activity(activity, options={}) issue_ids = issues.collect(&:id) TimeEntry.sum(:hours, :conditions => ["#{TimeEntry.table_name}.issue_id IN (?) AND activity_id IN (?)", issue_ids, activity.id]) end # Total budget ($) for a given activity - def budget_for_activity(activity) - labor = labor_budgets.sum(:budget, - :conditions => ["#{LaborBudget.table_name}.time_entry_activity_id IN (?)", activity.id]) - overhead = overhead_budgets.sum(:budget, - :conditions => ["#{OverheadBudget.table_name}.time_entry_activity_id IN (?)", activity.id]) - + def budget_for_activity(activity, options={}) + period = options[:period] || nil + labor_conditions = ARCondition.new(["#{LaborBudget.table_name}.time_entry_activity_id IN (?)", activity.id]) + overhead_conditions = ARCondition.new(["#{OverheadBudget.table_name}.time_entry_activity_id IN (?)", activity.id]) + if period.present? + labor_conditions.add(["#{LaborBudget.table_name}.year = ?", period.year]) + labor_conditions.add(["#{LaborBudget.table_name}.month = ?", period.month]) + overhead_conditions.add(["#{OverheadBudget.table_name}.year = ?", period.year]) + overhead_conditions.add(["#{OverheadBudget.table_name}.month = ?", period.month]) + end + + labor = labor_budgets.sum(:budget, :conditions => labor_conditions.conditions) + overhead = overhead_budgets.sum(:budget, :conditions => overhead_conditions.conditions) labor.to_f + overhead.to_f end # Total budget (hours) a given activity - def hours_budget_for_activity(activity) + def hours_budget_for_activity(activity, options={}) labor = labor_budgets.sum(:hours, :conditions => ["#{LaborBudget.table_name}.time_entry_activity_id IN (?)", activity.id]) overhead = overhead_budgets.sum(:hours, diff --git a/app/views/deliverables/_finances.html.erb b/app/views/deliverables/_finances.html.erb index da9c08d..6c8d09b 100644 --- a/app/views/deliverables/_finances.html.erb +++ b/app/views/deliverables/_finances.html.erb @@ -37,17 +37,17 @@ <%= h(activity.name) %> - - <%= number_to_currency(deliverable.spent_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + + <%= number_to_currency(deliverable.spent_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - <%= number_to_currency(deliverable.budget_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + <%= number_to_currency(deliverable.budget_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - - <%= number_with_precision(deliverable.hours_spent_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + + <%= number_with_precision(deliverable.hours_spent_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - <%= number_with_precision(deliverable.hours_budget_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + <%= number_with_precision(deliverable.hours_budget_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> <% end %> @@ -87,17 +87,17 @@ <%= h(activity.name) %> - - <%= number_to_currency(deliverable.spent_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + + <%= number_to_currency(deliverable.spent_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - <%= number_to_currency(deliverable.budget_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + <%= number_to_currency(deliverable.budget_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - - <%= number_with_precision(deliverable.hours_spent_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + + <%= number_with_precision(deliverable.hours_spent_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> - <%= number_with_precision(deliverable.hours_budget_for_activity(activity), :precision => Deliverable::ViewPrecision) %> + <%= number_with_precision(deliverable.hours_budget_for_activity(activity, :period => period), :precision => Deliverable::ViewPrecision) %> <% end %> diff --git a/test/integration/deliverable_finances_test.rb b/test/integration/deliverable_finances_test.rb index f6db682..52444aa 100644 --- a/test/integration/deliverable_finances_test.rb +++ b/test/integration/deliverable_finances_test.rb @@ -205,4 +205,92 @@ class DeliverableFinancesShowTest < ActionController::IntegrationTest end end end + + context "for a request for a different period" do + setup do + @period = "2010-02" + visit "/projects/#{@project.id}/contracts/#{@contract.id}/deliverables/#{@deliverable1.id}/finances?period=#{@period}" + + assert_response :success + + # All work done in setup() is past this period, only these items should show up. + Rate.generate!(:project => @deliverable1.project, + :user => @manager, + :date_in_effect => Date.new(2010, 2, 15), + :amount => 45) + # 2 hours of $45 billable work + create_issue_with_time_for_deliverable(@deliverable1, { + :activity => @billable_activity, + :user => @manager, + :hours => 2, + :spent_on => Date.new(2010, 2,15), + :skip_rate => true, + :issue_category => @category_on_billable + }) + # 1 hour of $45 billable work with no category + create_issue_with_time_for_deliverable(@deliverable1, { + :activity => @billable_activity, + :user => @manager, + :hours => 1, + :spent_on => Date.new(2010, 2,15), + :skip_rate => true, + :issue_category => nil + }) + # 3 hours of $45 nonbillable work + create_issue_with_time_for_deliverable(@deliverable1, { + :activity => @non_billable_activity, + :user => @manager, + :hours => 3, + :spent_on => Date.new(2010, 2, 15), + :skip_rate => true, + :issue_category => @category_on_non_billable + }) + + end + + should "calculate activity values based on the period only" do + # Labor + assert_select "table#deliverable-labor-activities" do + assert_select "tr.labor" do + assert_select "td", :text => /#{@billable_activity.name}/ + assert_select "td.spent-amount", :text => /\$135/ # 3 * $45 + assert_select "td.total-amount", :text => /\$100/ # 1 month + assert_select "td.spent-hours", :text => /3/ + assert_select "td.total-deliverable-hours", :text => /10/ # 1 month + end + + assert_select "tr.summary-row.labor" do + assert_select "td", :text => /Totals/ + assert_select "td.spent-amount", :text => /\$135/ + assert_select "td.total-amount", :text => /\$100/ + assert_select "td.spent-hours", :text => /3/ + assert_select "td.total-deliverable-hours", :text => /10/ + end + + end + + # Overhead + assert_select "table#deliverable-overhead-activities" do + assert_select "tr.overhead" do + assert_select "td", :text => /#{@non_billable_activity.name}/ + assert_select "td.spent-amount", :text => /\$135/ + assert_select "td.total-amount", :text => /\$200/ + assert_select "td.spent-hours", :text => /3/ + assert_select "td.total-deliverable-hours", :text => /10/ # 3 month retainer * 10 + end + + assert_select "tr.summary-row.overhead" do + assert_select "td", :text => /Totals/ + assert_select "td.spent-amount", :text => /\$135/ + assert_select "td.total-amount", :text => /\$200/ + assert_select "td.spent-hours", :text => /5/ + assert_select "td.total-deliverable-hours", :text => /10/ + end + end + + end + + should "calculate user values based on the period only" + should "calculate issue category values based on the period only" + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 5373422..c0ae688 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -131,18 +131,20 @@ class ActiveSupport::TestCase amount = options[:amount] || 100 hours = options[:hours] || 2 issue_category = options[:issue_category] + skip_rate = options[:skip_rate] || false + spent_on = options[:spent_on] || Date.today issue = Issue.generate_for_project!(project, :category_id => issue_category.try(:id)) time_entry = TimeEntry.generate!(:issue => issue, :project => project, :activity => activity, - :spent_on => Date.today, + :spent_on => spent_on, :hours => hours, :user => user) rate = Rate.generate!(:project => project, :user => user, :date_in_effect => Date.yesterday, - :amount => amount) + :amount => amount) unless skip_rate deliverable.issues << issue issue end diff --git a/test/unit/deliverable_test.rb b/test/unit/deliverable_test.rb index 75ea378..fe868c3 100644 --- a/test/unit/deliverable_test.rb +++ b/test/unit/deliverable_test.rb @@ -141,6 +141,31 @@ class DeliverableTest < ActiveSupport::TestCase assert_equal 500.0, @deliverable.spent_for_activity(@billable_activity).to_f end + + should "return the total amount spent for an activity during the period" do + configure_overhead_plugin + create_contract_and_deliverable + Rate.generate!(:project => @deliverable.project, + :user => @manager, + :date_in_effect => Date.new(2010, 2, 15), + :amount => 45) + create_issue_with_time_for_deliverable(@deliverable, { + :activity => @billable_activity, + :user => @manager, + :hours => 5, + :amount => 100 + }) + create_issue_with_time_for_deliverable(@deliverable, { + :activity => @billable_activity, + :user => @manager, + :hours => 2, + :spent_on => Date.new(2010, 2,15), + :skip_rate => true, + :issue_category => @category_on_billable + }) + + assert_equal 90.0, @deliverable.spent_for_activity(@billable_activity, :period => Date.new(2010,2,1)).to_f + end end context "#budget_for_activity" do @@ -153,7 +178,17 @@ class DeliverableTest < ActiveSupport::TestCase assert_equal 600.0, @deliverable.budget_for_activity(@billable_activity).to_f # 200 * 3 months (retainer) end - + + should "return the total amount budgeted for an activity during the period" do + configure_overhead_plugin + create_contract_and_deliverable + @deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10, :time_entry_activity => @billable_activity) + @deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10, :time_entry_activity => @billable_activity) + @deliverable.save! + + assert_equal 200.0, @deliverable.budget_for_activity(@billable_activity, :period => Date.new(2010,2,1)).to_f # 200 * 1 months (retainer) + end + end context "#hours_spent_for_activity" do @@ -170,7 +205,8 @@ class DeliverableTest < ActiveSupport::TestCase assert_equal 5.0, @deliverable.hours_spent_for_activity(@billable_activity).to_f end - + + should "return the total hours spent for an activity during the period" end context "#hours_budget_for_activity" do @@ -183,7 +219,8 @@ class DeliverableTest < ActiveSupport::TestCase assert_equal 60.0, @deliverable.hours_budget_for_activity(@billable_activity).to_f # 20 * 3 months (retainer) end - + + should "return the total hours budgeted for an activity during the period" do end context "#users_with_billable_time" do