diff --git a/app/helpers/contracts_helper.rb b/app/helpers/contracts_helper.rb index fcbb441..f9cde35 100644 --- a/app/helpers/contracts_helper.rb +++ b/app/helpers/contracts_helper.rb @@ -9,10 +9,16 @@ module ContractsHelper def format_budget_for_deliverable(deliverable, spent, total, options={}) extra_css_class = options[:class] || '' - + if total > 0 || spent > 0 - content_tag(:td, h(format_value_field_for_contracts(spent)), :class => 'spent-amount ' + extra_css_class) + - content_tag(:td, h(format_value_field_for_contracts(total)), :class => 'total-amount white ' + extra_css_class) + spent_css_classes = 'spent-amount' + spent_css_classes << " #{overage_class(spent, total)}" + spent_css_classes << ' ' << extra_css_class + total_css_classes = 'total-amount white' + total_css_classes << ' ' << extra_css_class + + content_tag(:td, h(format_value_field_for_contracts(spent)), :class => spent_css_classes) + + content_tag(:td, h(format_value_field_for_contracts(total)), :class => total_css_classes) else content_tag(:td, '----', :colspan => '2', :class => 'no-value ' + extra_css_class) end @@ -61,12 +67,16 @@ module ContractsHelper def show_budget_field(object, spent_field, total_field, options={}) formatter = options[:format] || :number_to_currency - spent_content = send(formatter, object.send(spent_field)) - total_content = send(formatter, object.send(total_field)) + spent_value = object.send(spent_field) + total_value = object.send(total_field) + spent_content = send(formatter, spent_value) + total_content = send(formatter, total_value) + reverse_overage = spent_field.to_s.match(/profit/i) + show_field(object, spent_field, options.merge(:raw => true, :wrap_in_td => false)) do - content_tag(:td, h(spent_content), :class => 'spent') + + content_tag(:td, h(spent_content), :class => "spent #{overage_class(spent_value, total_value, :reverse => reverse_overage)}") + content_tag(:td, h(total_content), :class => 'budget') end end @@ -164,4 +174,27 @@ module ContractsHelper return '' unless (1..5).include?(version) image_tag("todo#{version}.png", :plugin => 'redmine_contracts', :title => "Coming in release #{version}. #{message}") end + + # Overage occurs when spent is negative or spent is greater than budget + # + # :reverse - spent is reverse, it is overage when it's less than budget + def overage?(spent, budget, options={}) + return false unless spent && budget + return true if spent < 0 + + if options[:reverse] + spent.to_f < budget.to_f + else + spent.to_f > budget.to_f + end + end + + def overage_class(spent, budget, options={}) + if overage?(spent, budget, options) + 'overage' + else + '' + end + end + end diff --git a/app/views/deliverables/_details_row.html.erb b/app/views/deliverables/_details_row.html.erb index c10b10d..125f118 100644 --- a/app/views/deliverables/_details_row.html.erb +++ b/app/views/deliverables/_details_row.html.erb @@ -52,18 +52,22 @@ <%= l(:field_labor) %><%= release(3, "Deliverable lightbox") %> - <%= h(format_value_field_for_contracts(deliverable.labor_budget_spent(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.labor_budget_spent(validated_period))) %> + <%= h(format_value_field_for_contracts(deliverable.labor_budget_total(validated_period))) %> - <%= h(format_value_field_for_contracts(deliverable.labor_hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.labor_budget_hours(validated_period))) %> <%= l(:text_short_hours) %> + <%= h(format_value_field_for_contracts(deliverable.labor_hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.labor_budget_hours(validated_period))) %> <%= l(:text_short_hours) %> <%= l(:field_overhead) %><%= release(3, "Deliverable lightbox") %> - <%= h(format_value_field_for_contracts(deliverable.overhead_spent(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.overhead_spent(validated_period))) %> + <%= h(format_value_field_for_contracts(deliverable.overhead_budget_total(validated_period))) %> - <%= h(format_value_field_for_contracts(deliverable.overhead_hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.overhead_budget_hours(validated_period))) %> <%= l(:text_short_hours) %> + <%= h(format_value_field_for_contracts(deliverable.overhead_hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.overhead_budget_hours(validated_period))) %> <%= l(:text_short_hours) %> @@ -71,7 +75,9 @@ <% next if fixed_budget.blank_record? %> <%= h(fixed_budget.title) %> - <%= h(format_value_field_for_contracts(fixed_budget.budget_spent)) %> + + <%= h(format_value_field_for_contracts(fixed_budget.budget_spent)) %> + <%= h(format_value_field_for_contracts(fixed_budget.budget)) %> @@ -80,7 +86,9 @@ <% if show_markup_for?(deliverable, validated_period) %> <%= l(:field_markup) %> - <%= h(format_value_field_for_contracts(deliverable.fixed_markup_budget_total_spent(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.fixed_markup_budget_total_spent(validated_period))) %> + <%= h(format_value_field_for_contracts(deliverable.fixed_markup_budget_total(validated_period))) %> @@ -88,17 +96,24 @@ <%= l(:field_profit) %> - <%= h(format_value_field_for_contracts(deliverable.profit_left(validated_period))) %> - <%= h(format_value_field_for_contracts(deliverable.profit_budget(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.profit_left(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.profit_budget(validated_period))) %> <%= l(:field_total) %>: - <%= h(format_value_field_for_contracts(deliverable.total_spent(validated_period))) %> + + + <%= h(format_value_field_for_contracts(deliverable.total_spent(validated_period))) %> + + <%= h(format_value_field_for_contracts(deliverable.total(validated_period))) %> - <%= h(format_value_field_for_contracts(deliverable.hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.estimated_hour_budget_total(validated_period))) %> <%= l(:text_short_hours) %> + + <%= h(format_value_field_for_contracts(deliverable.hours_spent_total(validated_period))) %>/<%= h(format_value_field_for_contracts(deliverable.estimated_hour_budget_total(validated_period))) %> <%= l(:text_short_hours) %> diff --git a/assets/stylesheets/redmine_contracts.css b/assets/stylesheets/redmine_contracts.css index fae9acd..4d81f15 100644 --- a/assets/stylesheets/redmine_contracts.css +++ b/assets/stylesheets/redmine_contracts.css @@ -27,6 +27,7 @@ html>body .tabular li {overflow:hidden;} /* End tabular */ a.contract-delete {color: red; } +.overage { color: #A40000; } /* Positioning */ diff --git a/test/integration/contracts_show_test.rb b/test/integration/contracts_show_test.rb index c558712..f39e34f 100644 --- a/test/integration/contracts_show_test.rb +++ b/test/integration/contracts_show_test.rb @@ -448,6 +448,54 @@ class ContractsShowTest < ActionController::IntegrationTest end end + should "show overages in red" do + configure_overhead_plugin + + @manager = User.generate! + + @deliverable1 = HourlyDeliverable.generate!(:contract => @contract, :manager => @manager) + @issue1 = Issue.generate_for_project!(@project) + @time_entry1 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @billable_activity, + :spent_on => Date.today, + :hours => 10, + :user => @manager) + @time_entry2 = TimeEntry.generate!(:issue => @issue1, + :project => @project, + :activity => @non_billable_activity, + :spent_on => Date.today, + :hours => 5, + :user => @manager) + @deliverable1.issues << @issue1 + @rate = Rate.generate!(:project => @project, + :user => @manager, + :date_in_effect => Date.yesterday, + :amount => 100) + + assert_equal 1, @deliverable1.issues.count + + visit_contract_page(@contract) + + # Overages on: + assert_select '.overage', :count => 14 + assert_select '.contract-labor .overage', :count => 2 + assert_select '.contract-overhead .overage', :count => 2 + assert_select '.contract-profit .overage' + assert_select '.contract-estimated-hour.total .overage' + + assert_select '#deliverables .spent-amount.labor.overage' + assert_select '#deliverables .spent-amount.overhead.overage' + assert_select '#deliverables .labor_budget_spent.overage' + assert_select '#deliverables .overhead_budget_spent.overage' + assert_select '#deliverables .profit_spent.overage' + assert_select '#deliverables .labor_hours .overage' + assert_select '#deliverables .overhead_hours .overage' + assert_select '#deliverables .total_hours .overage' + + end + + should "show an alert if there is orphaned time or issues" do configure_overhead_plugin