diff --git a/app/views/deliverables/_deliverable_summary_row.html.erb b/app/views/deliverables/_deliverable_summary_row.html.erb index b700b2c..0948f04 100644 --- a/app/views/deliverables/_deliverable_summary_row.html.erb +++ b/app/views/deliverables/_deliverable_summary_row.html.erb @@ -8,8 +8,8 @@ <%= content_tag(:td, h(deliverable.subject), :class => 'subject') %> <%= content_tag(:td, number_to_currency(deliverable.budget || 0.0, :precision => 0), :class => 'budget') if allowed_management? %> <%= content_tag(:td, number_to_currency(deliverable.labor_budget || 0.0, :precision => 0), :class => 'budget') if allowed_management? %> - <%= content_tag(:td, number_to_currency(deliverable.spent, :precision => 0), :class => 'spent') if allowed_management? %> + <%= content_tag(:td, number_to_currency(deliverable.labor_budget_spent, :precision => 0), :class => 'spent') if allowed_management? %> <%= content_tag(:td, format_date(deliverable.due), :class => 'due_date') %> <%= content_tag(:td, progress_bar(deliverable.progress, :width => '100%', :class => 'done_ratio')) %> <%= Redmine::Hook.call_hook(:plugin_budget_view_deliverable_summary_row, { :deliverable => deliverable }) %> - \ No newline at end of file + diff --git a/init.rb b/init.rb index 50078f9..ce0bb3f 100644 --- a/init.rb +++ b/init.rb @@ -3,11 +3,15 @@ require 'redmine' # Patches to the Redmine core. require 'dispatcher' require 'overhead_deliverable_patch' +require 'overhead_hourly_deliverable_patch' +require 'overhead_fixed_deliverable_patch' require 'overhead_issue_patch' require 'overhead_time_entry_patch' require 'overhead_time_entry_activity_patch' Dispatcher.to_prepare do Deliverable.send(:include, OverheadDeliverablePatch) + HourlyDeliverable.send(:include, OverheadHourlyDeliverablePatch) + FixedDeliverable.send(:include, OverheadFixedDeliverablePatch) Issue.send(:include, OverheadIssuePatch) TimeEntry.send(:include, OverheadTimeEntryPatch) TimeEntryActivity.send(:include, OverheadTimeEntryActivityPatch) diff --git a/lib/overhead_fixed_deliverable_patch.rb b/lib/overhead_fixed_deliverable_patch.rb new file mode 100644 index 0000000..1f05b32 --- /dev/null +++ b/lib/overhead_fixed_deliverable_patch.rb @@ -0,0 +1,32 @@ +require_dependency 'deliverable' +require_dependency 'fixed_deliverable' + +module OverheadFixedDeliverablePatch + def self.included(base) + base.send(:include, InstanceMethods) + + base.class_eval do + unloadable + end + end + + module InstanceMethods + # Amount of "billable" money spent on issues. Similar to +spent+ + # but only billable time. + def labor_budget_spent + return 0.0 if self.fixed_cost.nil? + return self.fixed_cost unless self.issues.size > 0 + + # Get all timelogs assigned + time_logs = self.issues.collect(&:time_entries).flatten + + return fixed_cost + time_logs.collect {|time_log| + if time_log.billable? + time_log.cost + else + 0.0 + end + }.sum + end + end +end diff --git a/lib/overhead_hourly_deliverable_patch.rb b/lib/overhead_hourly_deliverable_patch.rb new file mode 100644 index 0000000..f26cee9 --- /dev/null +++ b/lib/overhead_hourly_deliverable_patch.rb @@ -0,0 +1,33 @@ +require_dependency 'deliverable' +require_dependency 'hourly_deliverable' + +module OverheadHourlyDeliverablePatch + def self.included(base) + base.send(:include, InstanceMethods) + + base.class_eval do + unloadable + end + end + + module InstanceMethods + # Amount of "billable" money spent on issues. Similar to +spent+ + # but only billable time. + def labor_budget_spent + return 0.0 unless self.issues.size > 0 + total = 0.0 + + # Get all timelogs assigned + time_logs = self.issues.collect(&:time_entries).flatten + + return time_logs.collect {|time_log| + if time_log.billable? + time_log.cost + else + 0.0 + end + }.sum + + end + end +end diff --git a/spec/lib/overhead_fixed_deliverable_patch_spec.rb b/spec/lib/overhead_fixed_deliverable_patch_spec.rb new file mode 100644 index 0000000..bca8543 --- /dev/null +++ b/spec/lib/overhead_fixed_deliverable_patch_spec.rb @@ -0,0 +1,39 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe FixedDeliverable, '#labor_budget_spent' do + def mock_issues_and_time_entries + @deliverable.should_receive(:issues).at_least(:once).and_return do + issue1 = mock_model(Issue, :time_entries => [ + mock_model(TimeEntry, :cost => 100, :billable? => true), + mock_model(TimeEntry, :cost => 200, :billable? => false), + mock_model(TimeEntry, :cost => 50, :billable? => true) + ]) + issue2 = mock_model(Issue, :time_entries => [ + mock_model(TimeEntry, :cost => 1000, :billable? => true), + mock_model(TimeEntry, :cost => 2000, :billable? => false), + mock_model(TimeEntry, :cost => 5000, :billable? => true) + ]) + [issue1, issue2] + end + end + + before(:each) do + @deliverable = FixedDeliverable.new :fixed_cost => 0 + end + + it 'should be 0 if there are no assigned issues' do + @deliverable.should_receive(:issues).and_return([]) + @deliverable.labor_budget_spent.should eql(0) + end + + it 'should total all billable time entries' do + mock_issues_and_time_entries + @deliverable.labor_budget_spent.should eql(6150.0) + end + + it 'should total the billable time entries to the fixed_cost' do + mock_issues_and_time_entries + @deliverable.should_receive(:fixed_cost).at_least(:once).and_return(5000) + @deliverable.labor_budget_spent.should eql(11_150.0) + end +end diff --git a/spec/lib/overhead_hourly_deliverable_patch_spec.rb b/spec/lib/overhead_hourly_deliverable_patch_spec.rb new file mode 100644 index 0000000..863d7cc --- /dev/null +++ b/spec/lib/overhead_hourly_deliverable_patch_spec.rb @@ -0,0 +1,28 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe HourlyDeliverable, '#labor_budget_spent' do + it 'should be 0 if there are no assigned issues' do + deliverable = HourlyDeliverable.new + deliverable.should_receive(:issues).and_return([]) + deliverable.labor_budget_spent.should eql(0.0) + end + + it 'should total all billable time entries' do + deliverable = HourlyDeliverable.new + deliverable.should_receive(:issues).at_least(:once).and_return do + issue1 = mock_model(Issue, :time_entries => [ + mock_model(TimeEntry, :cost => 100, :billable? => true), + mock_model(TimeEntry, :cost => 200, :billable? => false), + mock_model(TimeEntry, :cost => 50, :billable? => true) + ]) + issue2 = mock_model(Issue, :time_entries => [ + mock_model(TimeEntry, :cost => 1000, :billable? => true), + mock_model(TimeEntry, :cost => 2000, :billable? => false), + mock_model(TimeEntry, :cost => 5000, :billable? => true) + ]) + [issue1, issue2] + end + + deliverable.labor_budget_spent.should eql(6150.0) + end +end