Merge branch '6574-budgets-by-activity'
This commit is contained in:
2
Rakefile
2
Rakefile
@@ -6,7 +6,7 @@ Dir[File.expand_path(File.dirname(__FILE__)) + "/lib/tasks/**/*.rake"].sort.each
|
||||
RedminePluginSupport::Base.setup do |plugin|
|
||||
plugin.project_name = 'redmine_contracts'
|
||||
plugin.default_task = [:test]
|
||||
plugin.tasks = [:db, :doc, :release, :clean, :test, :stats, :metrics]
|
||||
plugin.tasks = [:db, :doc, :release, :clean, :test, :stats]
|
||||
# TODO: gem not getting this automaticly
|
||||
plugin.redmine_root = File.expand_path(File.dirname(__FILE__) + '/../../../')
|
||||
end
|
||||
|
||||
@@ -11,9 +11,9 @@ class Deliverable < ActiveRecord::Base
|
||||
has_many :fixed_budgets
|
||||
has_many :issues, :dependent => :nullify
|
||||
|
||||
accepts_nested_attributes_for :labor_budgets
|
||||
accepts_nested_attributes_for :overhead_budgets
|
||||
accepts_nested_attributes_for :fixed_budgets
|
||||
accepts_nested_attributes_for :labor_budgets, :allow_destroy => true
|
||||
accepts_nested_attributes_for :overhead_budgets, :allow_destroy => true
|
||||
accepts_nested_attributes_for :fixed_budgets, :allow_destroy => true
|
||||
|
||||
# Validations
|
||||
validates_presence_of :title
|
||||
@@ -31,7 +31,8 @@ class Deliverable < ActiveRecord::Base
|
||||
delegate "open?", :to => :contract, :prefix => true, :allow_nil => true
|
||||
delegate "closed?", :to => :contract, :prefix => true, :allow_nil => true
|
||||
delegate "locked?", :to => :contract, :prefix => true, :allow_nil => true
|
||||
|
||||
delegate :project, :to => :contract, :allow_nil => true
|
||||
|
||||
# Callbacks
|
||||
before_destroy :block_on_locked_contracts
|
||||
before_destroy :block_on_closed_contracts
|
||||
@@ -280,6 +281,11 @@ class Deliverable < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
# Required attribute for AAJ's JournalFormatter
|
||||
def name
|
||||
title
|
||||
end
|
||||
|
||||
# Accessors from the budget plugin that need to be wrapped
|
||||
def subject
|
||||
warn "[DEPRECATION] Deliverable#subject is deprecated. Please use Deliverable#title instead."
|
||||
|
||||
@@ -3,8 +3,10 @@ class LaborBudget < ActiveRecord::Base
|
||||
|
||||
# Associations
|
||||
belongs_to :deliverable
|
||||
belongs_to :time_entry_activity
|
||||
|
||||
# Validations
|
||||
validates_presence_of :time_entry_activity_id
|
||||
|
||||
# Accessors
|
||||
include DollarizedAttribute
|
||||
|
||||
@@ -3,8 +3,10 @@ class OverheadBudget < ActiveRecord::Base
|
||||
|
||||
# Associations
|
||||
belongs_to :deliverable
|
||||
belongs_to :time_entry_activity
|
||||
|
||||
# Validations
|
||||
validates_presence_of :time_entry_activity_id
|
||||
|
||||
# Accessors
|
||||
include DollarizedAttribute
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
<th colspan="2"><%= l(:field_labor) %></th>
|
||||
<th colspan="2"><%= l(:field_overhead) %></th>
|
||||
<th colspan="2"><%= l(:field_fixed) %></th>
|
||||
<th colspan="2"><%= l(:field_total) %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -135,6 +136,7 @@
|
||||
<%= format_budget_for_deliverable(deliverable, deliverable.labor_budget_spent, deliverable.labor_budget_total, :class => 'labor') %>
|
||||
<%= format_budget_for_deliverable(deliverable, deliverable.overhead_spent, deliverable.overhead_budget_total, :class => 'overhead') %>
|
||||
<%= format_budget_for_deliverable(deliverable, deliverable.fixed_budget_total_spent, deliverable.fixed_budget_total, :class => 'fixed') %>
|
||||
<%= format_budget_for_deliverable(deliverable, deliverable.total_spent, deliverable.total, :class => 'total') %>
|
||||
<% end %>
|
||||
|
||||
<tr id="deliverable_details_<%= h(deliverable.id) %>" class="ign">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<% validated_period = validate_period(deliverable, period) %>
|
||||
|
||||
<td colspan="11" class="deliverable_details_outer_wrapper_<%= h(deliverable.id) %>">
|
||||
<td colspan="13" class="deliverable_details_outer_wrapper_<%= h(deliverable.id) %>">
|
||||
|
||||
<div class="expanded">
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
<script type="text/html" id="labor-budget-template">
|
||||
<% form.fields_for :labor_budgets, resource.labor_budgets.new(:id => 0) do |labor_budget| %>
|
||||
<%= render :partial => 'labor_budget_form', :locals => {:labor_budget => labor_budget} %>
|
||||
<% end %>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="overhead-budget-template">
|
||||
<% form.fields_for :overhead_budgets, resource.overhead_budgets.new(:id => 0) do |overhead_budget| %>
|
||||
<%= render :partial => 'overhead_budget_form', :locals => {:overhead_budget => overhead_budget} %>
|
||||
<% end %>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="fixed-budget-template">
|
||||
<% form.fields_for :fixed_budgets, resource.fixed_budgets.new(:id => 0) do |fixed_budget| %>
|
||||
<%= render :partial => 'fixed_budget_form', :locals => {:fixed_budget => fixed_budget} %>
|
||||
<% end %>
|
||||
</script>
|
||||
|
||||
<% form.inputs :name => label, :class => "deliverable-finances #{fieldset_class}" do %>
|
||||
|
||||
<li style="display:none;" id='retainer-finances-message'>
|
||||
@@ -7,54 +25,22 @@
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_labor)) %>
|
||||
<table id="deliverable-labor" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :labor_budgets, labor_budgets do |labor_budget| %>
|
||||
<%= labor_budget.hidden_field(:year) %>
|
||||
<%= labor_budget.hidden_field(:month) %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= release(3, "Select field for the Time Entry Activity in a td") %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= labor_budget.text_field(:hours, :value => format_deliverable_value_fields(labor_budget.object.hours), :class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= labor_budget.text_field(:budget, :value => format_deliverable_value_fields(labor_budget.object.budget), :class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<%= release(3, "Green Add button for multiple records") %>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<% form.fields_for :labor_budgets, labor_budgets.sort_by(&:id) do |labor_budget| %>
|
||||
<%= render :partial => 'labor_budget_form', :locals => {:labor_budget => labor_budget} %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_overhead)) %>
|
||||
<table id="deliverable-overhead" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :overhead_budgets, overhead_budgets do |overhead_budget| %>
|
||||
<%= overhead_budget.hidden_field(:year) %>
|
||||
<%= overhead_budget.hidden_field(:month) %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= release(3, "Select field for the Time Entry Activity in a td") %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= overhead_budget.text_field(:hours, :value => format_deliverable_value_fields(overhead_budget.object.hours),:class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= overhead_budget.text_field(:budget, :value => format_deliverable_value_fields(overhead_budget.object.budget), :class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<%= release(3, "Green Add button for multiple records") %>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<% form.fields_for :overhead_budgets, overhead_budgets.sort_by(&:id) do |overhead_budget| %>
|
||||
<%= render :partial => 'overhead_budget_form', :locals => {:overhead_budget => overhead_budget} %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
@@ -62,34 +48,9 @@
|
||||
<div id="deliverable-fixed" class="fixed-item-form">
|
||||
<label for="contract_discount">Fixed</label>
|
||||
|
||||
<% form.fields_for :fixed_budgets, fixed_budgets do |fixed_budget| %>
|
||||
<%= fixed_budget.hidden_field(:year) %>
|
||||
<%= fixed_budget.hidden_field(:month) %>
|
||||
|
||||
<p class="inline-hints"><%= fixed_budget.label(:title, l(:field_title))%>
|
||||
<%= fixed_budget.text_field(:title) %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:budget, l(:field_budget))%> <%= l(:text_dollar_sign) %>
|
||||
<%= fixed_budget.text_field(:budget, :value => format_deliverable_value_fields(fixed_budget.object.budget), :class => 'financial') %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:markup, l(:field_markup)) %> <%= l(:field_discount_hint) %>
|
||||
<%= fixed_budget.text_field(:markup, :value => format_deliverable_value_fields_as_dollar_or_percent(fixed_budget.object.markup), :class => 'financial') %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:paid, l(:field_paid)) %>
|
||||
<%= fixed_budget.check_box(:paid) %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints" style="display:none;"><%= fixed_budget.label(:description, l(:field_description), :for => "fixed-description#{fixed_budget.object.object_id}")%></p>
|
||||
<%= fixed_budget.text_area(:description, :class => 'wiki-edit', :rows => '5', :id => "fixed-description#{fixed_budget.object.object_id}") %>
|
||||
<% form.fields_for :fixed_budgets, fixed_budgets.sort_by(&:id) do |fixed_budget| %>
|
||||
<%= render :partial => 'fixed_budget_form', :locals => {:fixed_budget => fixed_budget} %>
|
||||
<%= wikitoolbar_for "fixed-description#{fixed_budget.object.object_id}" %>
|
||||
|
||||
<p><%= release(3, "Green Add button for multiple records") %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
||||
33
app/views/deliverables/_fixed_budget_form.html.erb
Normal file
33
app/views/deliverables/_fixed_budget_form.html.erb
Normal file
@@ -0,0 +1,33 @@
|
||||
<div class="fixed-budget-form">
|
||||
<%= fixed_budget.hidden_field(:id) unless fixed_budget.object.new_record? %>
|
||||
<%= fixed_budget.hidden_field(:year) %>
|
||||
<%= fixed_budget.hidden_field(:month) %>
|
||||
|
||||
<p class="inline-hints"><%= fixed_budget.label(:title, l(:field_title))%>
|
||||
<%= fixed_budget.text_field(:title) %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:budget, l(:field_budget))%> <%= l(:text_dollar_sign) %>
|
||||
<%= fixed_budget.text_field(:budget, :value => format_deliverable_value_fields(fixed_budget.object.budget), :class => 'financial') %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:markup, l(:field_markup)) %> <%= l(:field_discount_hint) %>
|
||||
<%= fixed_budget.text_field(:markup, :value => format_deliverable_value_fields_as_dollar_or_percent(fixed_budget.object.markup), :class => 'financial') %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints">
|
||||
<%= fixed_budget.label(:paid, l(:field_paid)) %>
|
||||
<%= fixed_budget.check_box(:paid) %>
|
||||
</p>
|
||||
|
||||
<p class="inline-hints" style="display:none;"><%= fixed_budget.label(:description, l(:field_description), :for => "fixed-description#{fixed_budget.object.object_id}")%></p>
|
||||
<%= fixed_budget.text_area(:description, :class => 'wiki-edit', :rows => '5', :id => "fixed-description#{fixed_budget.object.object_id}") %>
|
||||
|
||||
<p class="inline-hints add-fixed">
|
||||
<%= fixed_budget.hidden_field "_destroy", :class=> "delete-flag" %>
|
||||
<%= link_to_function(l(:button_delete), 'deleteDeliverableFinance(this)', :class => 'delete icon icon-del') %>
|
||||
<%= link_to_function(l(:button_add), 'addNewDeliverableFixedItem()', :class => 'add icon icon-add', :style => 'display:none;') %>
|
||||
</p>
|
||||
</div>
|
||||
@@ -1,6 +1,7 @@
|
||||
<%= javascript_tag("var i18nStartDateEmpty = '#{l(:text_start_date_empty)}'") %>
|
||||
<%= javascript_tag("var i18nEndDateEmpty = '#{l(:text_end_date_empty)}'") %>
|
||||
<%= javascript_tag("var i18nChangedPeriodMessage = '#{l(:text_changed_period_message)}'") %>
|
||||
<%= javascript_tag("var i18nAreYouSure = '#{l(:text_are_you_sure)}'") %>
|
||||
|
||||
<% if resource.locked? || resource.closed? || resource.contract_locked? || resource.contract_closed? %>
|
||||
<div class="error_msg">
|
||||
@@ -56,7 +57,7 @@
|
||||
<% if resource.retainer? && resource.respond_to?(:months) %>
|
||||
<% if resource.months.present? %>
|
||||
<% resource.months.each do |month| %>
|
||||
<%= render :partial => 'finance_form', :locals => {:form => form, :labor_budgets => resource.labor_budgets_for_date(month), :overhead_budgets => resource.overhead_budgets_for_date(month), :fixed_budgets => resource.fixed_budgets_for_date(month), :label => l(:text_deliverable_finances_date, :date => month.strftime("%B, %Y")), :fieldset_class => 'date-' + month.strftime('%Y-%m') } %>
|
||||
<%= render :partial => 'finance_form', :locals => {:resource => resource, :form => form, :labor_budgets => resource.labor_budgets_for_date(month), :overhead_budgets => resource.overhead_budgets_for_date(month), :fixed_budgets => resource.fixed_budgets_for_date(month), :label => l(:text_deliverable_finances_date, :date => month.strftime("%B, %Y")), :fieldset_class => 'date-' + month.strftime('%Y-%m') } %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= content_tag(:p, l(:text_missing_period), :class => 'nodata') %>
|
||||
|
||||
23
app/views/deliverables/_labor_budget_form.html.erb
Normal file
23
app/views/deliverables/_labor_budget_form.html.erb
Normal file
@@ -0,0 +1,23 @@
|
||||
<tr class="labor-budget-form">
|
||||
<td>
|
||||
<%= labor_budget.hidden_field(:id) unless labor_budget.object.new_record? %>
|
||||
<%= labor_budget.hidden_field(:year) %>
|
||||
<%= labor_budget.hidden_field(:month) %>
|
||||
|
||||
<%= labor_budget.label(:time_entry_activity_id, :class => "hidden") %>
|
||||
<%= labor_budget.select(:time_entry_activity_id, options_from_collection_for_select(@project.billable_activities, :id, :name, labor_budget.object.time_entry_activity_id), {:include_blank => false}, {:class => 'financial'}) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= labor_budget.text_field(:hours, :value => format_deliverable_value_fields(labor_budget.object.hours), :class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= labor_budget.text_field(:budget, :value => format_deliverable_value_fields(labor_budget.object.budget), :class => 'financial') %>
|
||||
</td>
|
||||
<td class="add-labor">
|
||||
<%= labor_budget.hidden_field "_destroy", :class=> "delete-flag" %>
|
||||
<%= link_to_function(l(:button_delete), 'deleteDeliverableFinance(this)', :class => 'delete icon icon-del') %>
|
||||
<%= link_to_function(l(:button_add), 'addNewDeliverableLaborItem()', :class => 'add icon icon-add', :style => 'display:none;') %>
|
||||
</td>
|
||||
</tr>
|
||||
22
app/views/deliverables/_overhead_budget_form.html.erb
Normal file
22
app/views/deliverables/_overhead_budget_form.html.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<tr class="overhead-budget-form">
|
||||
<td>
|
||||
<%= overhead_budget.hidden_field(:year) %>
|
||||
<%= overhead_budget.hidden_field(:month) %>
|
||||
|
||||
<%= overhead_budget.label(:time_entry_activity_id, :class => "hidden") %>
|
||||
<%= overhead_budget.select(:time_entry_activity_id, options_from_collection_for_select(@project.non_billable_activities, :id, :name, overhead_budget.object.time_entry_activity_id), {:include_blank => false}, {:class => 'financial'}) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= overhead_budget.text_field(:hours, :value => format_deliverable_value_fields(overhead_budget.object.hours),:class => 'financial') %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= overhead_budget.text_field(:budget, :value => format_deliverable_value_fields(overhead_budget.object.budget), :class => 'financial') %>
|
||||
</td>
|
||||
<td class="add-overhead">
|
||||
<%= overhead_budget.hidden_field "_destroy", :class=> "delete-flag" %>
|
||||
<%= link_to_function(l(:button_delete), 'deleteDeliverableFinance(this)', :class => 'delete icon icon-del') %>
|
||||
<%= link_to_function(l(:button_add), 'addNewDeliverableOverheadItem()', :class => 'add icon icon-add', :style => 'display:none;') %>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -68,6 +68,88 @@ jQuery(function($) {
|
||||
}
|
||||
},
|
||||
|
||||
showDeliverableAddButtons = function() {
|
||||
var laborLinks = $('table.deliverable_finance_table .add-labor a.add')
|
||||
if (laborLinks.length == 0) {
|
||||
// No link, add a blank form
|
||||
addNewDeliverableLaborItem();
|
||||
} else {
|
||||
laborLinks.hide().last().show();
|
||||
}
|
||||
var overheadLinks = $('table.deliverable_finance_table .add-overhead a.add')
|
||||
if (overheadLinks.length == 0) {
|
||||
// No link, add a blank form
|
||||
addNewDeliverableOverheadItem();
|
||||
} else {
|
||||
overheadLinks.hide().last().show();
|
||||
}
|
||||
var fixedLinks = $('#deliverable-fixed .fixed-budget-form .add-fixed a.add')
|
||||
if (fixedLinks.length == 0) {
|
||||
// No link, add a blank form
|
||||
addNewDeliverableFixedItem();
|
||||
} else {
|
||||
fixedLinks.hide().last().show();
|
||||
}
|
||||
},
|
||||
|
||||
addNewDeliverableLaborItem = function() {
|
||||
addNewDeliverableFinance('#labor-budget-template',
|
||||
'#deliverable-labor tbody',
|
||||
$("tr.labor-budget-form").size(),
|
||||
'<tr class="labor-budget-form">');
|
||||
},
|
||||
|
||||
addNewDeliverableOverheadItem = function() {
|
||||
addNewDeliverableFinance('#overhead-budget-template',
|
||||
'#deliverable-overhead tbody',
|
||||
$("tr.overhead-budget-form").size(),
|
||||
'<tr class="overhead-budget-form">');
|
||||
},
|
||||
|
||||
addNewDeliverableFixedItem = function() {
|
||||
addNewDeliverableFinance('#fixed-budget-template',
|
||||
'#deliverable-fixed.fixed-item-form',
|
||||
$("div.fixed-budget-form").size(),
|
||||
'<div class="fixed-budget-form">');
|
||||
},
|
||||
|
||||
addNewDeliverableFinance = function(templateSelector, appendTemplateTo, countOfExisting, wrapperElement) {
|
||||
var t = $(templateSelector).tmpl({});
|
||||
if (t.length > 0) {
|
||||
var recordLocation = countOfExisting + 1; // increments the Rails [n] placeholder
|
||||
var newContent = t.html().replace(/\[0\]/g, "[" + recordLocation + "]");
|
||||
// New ids for textareas for the jsToolBar to attach to
|
||||
newContent = newContent.replace(/fixed-description\d*/g, "fixed-description" + Math.floor(Math.random() * 100000000))
|
||||
var newItem = $(wrapperElement).html(newContent)
|
||||
|
||||
newItem.appendTo(appendTemplateTo);
|
||||
newItem.find("textarea.wiki-edit").each(function () {
|
||||
attachWikiToolbar(this.id);
|
||||
});
|
||||
showDeliverableAddButtons();
|
||||
}
|
||||
},
|
||||
|
||||
// Set the deleted flag for Rails and move it out of the row into
|
||||
// a hidden table
|
||||
deleteDeliverableFinance = function(deleteLink) {
|
||||
if (confirm(i18nAreYouSure)) {
|
||||
$(deleteLink).parent().find('.delete-flag').val('1')
|
||||
if ($('#deleted-finances').length == 0) {
|
||||
$(deleteLink).
|
||||
closest("form").
|
||||
append($("<table style='display:none' id='deleted-finances'></table>"));
|
||||
}
|
||||
$('#deleted-finances').append(
|
||||
$(deleteLink). // <a>
|
||||
parent(). // <td>
|
||||
parent().hide()
|
||||
); // <tr>
|
||||
showDeliverableAddButtons();
|
||||
}
|
||||
},
|
||||
|
||||
showDeliverableAddButtons();
|
||||
toggleSpecificDeliverableFields($('form.deliverable'));
|
||||
|
||||
$('select#deliverable_type').change(function() {
|
||||
@@ -125,3 +207,9 @@ jQuery(function($) {
|
||||
|
||||
}
|
||||
})(jQuery);
|
||||
|
||||
// Global functions outside of jQuery scoping
|
||||
function attachWikiToolbar(id) {
|
||||
var jsToolBarInstance = new jsToolBar($(id));
|
||||
jsToolBarInstance.draw();
|
||||
}
|
||||
|
||||
1
assets/javascripts/jquery.tmpl.min.js
vendored
Normal file
1
assets/javascripts/jquery.tmpl.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -26,6 +26,7 @@ html>body .tabular li {overflow:hidden;}
|
||||
.tabular li.hidden { height: 0; padding: 0; margin: 0; }
|
||||
/* End tabular */
|
||||
|
||||
.hidden { display: none; }
|
||||
a.contract-delete {color: red; }
|
||||
.overage { color: #A40000; }
|
||||
|
||||
@@ -521,3 +522,5 @@ input.financial{
|
||||
#edit_contract_1 li#contract_billable_rate_input div{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.fixed-budget-form {padding: 5px 0;}
|
||||
|
||||
@@ -100,3 +100,4 @@ en:
|
||||
text_deliverable_closed_warning: "This deliverable is closed and cannot be saved without changing it's status to Open."
|
||||
text_contract_locked_warning: "This contract is locked and cannot be saved without changing it's status to Open."
|
||||
text_contract_closed_warning: "This contract is closed and cannot be saved without changing it's status to Open."
|
||||
field_time_entry_activity: "Activity"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class AddTimeEntryActivityIdToLaborBudgets < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :labor_budgets, :time_entry_activity_id, :integer
|
||||
add_index :labor_budgets, :time_entry_activity_id
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :labor_budgets, :time_entry_activity_id
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
class AddTimeEntryActivityIdToOverheadBudgets < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :overhead_budgets, :time_entry_activity_id, :integer
|
||||
add_index :overhead_budgets, :time_entry_activity_id
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :overhead_budgets, :time_entry_activity_id
|
||||
end
|
||||
end
|
||||
@@ -103,7 +103,8 @@ module RedmineContracts
|
||||
if old_deliverable['total_hours'].present? || old_deliverable['cost_per_hour'].present?
|
||||
deliverable.labor_budgets << LaborBudget.new(:deliverable => deliverable,
|
||||
:budget => @total_cost,
|
||||
:hours => old_deliverable['total_hours'])
|
||||
:hours => old_deliverable['total_hours'],
|
||||
:time_entry_activity => first_billable_activity(project))
|
||||
end
|
||||
else
|
||||
@total_cost = 0
|
||||
@@ -170,7 +171,8 @@ module RedmineContracts
|
||||
|
||||
deliverable.overhead_budgets << OverheadBudget.new(:deliverable => deliverable,
|
||||
:budget => old_deliverable['overhead'],
|
||||
:hours => hours.to_f.round(2))
|
||||
:hours => hours.to_f.round(2),
|
||||
:time_entry_activity => first_non_billable_activity(deliverable.project))
|
||||
elsif old_deliverable['overhead_percent'].present?
|
||||
overhead = total * (old_deliverable['overhead_percent'].to_f / 100)
|
||||
if @overhead_rate != 0
|
||||
@@ -181,7 +183,8 @@ module RedmineContracts
|
||||
|
||||
deliverable.overhead_budgets << OverheadBudget.new(:deliverable => deliverable,
|
||||
:budget => overhead,
|
||||
:hours => hours.to_f.round(2))
|
||||
:hours => hours.to_f.round(2),
|
||||
:time_entry_activity => first_non_billable_activity(deliverable.project))
|
||||
|
||||
end
|
||||
end
|
||||
@@ -228,5 +231,13 @@ module RedmineContracts
|
||||
def self.append_old_deliverable_to_notes(old_deliverable, new_deliverable)
|
||||
new_deliverable.notes += "Converted data:\n<pre>" + old_deliverable.pretty_inspect + "</pre>"
|
||||
end
|
||||
|
||||
def self.first_billable_activity(project)
|
||||
project.billable_activities.first || TimeEntryActivity.first
|
||||
end
|
||||
|
||||
def self.first_non_billable_activity(project)
|
||||
project.non_billable_activities.first || TimeEntryActivity.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,6 +8,11 @@ module RedmineContracts
|
||||
# * :detail => Detail about the journal change
|
||||
#
|
||||
def helper_issues_show_detail_after_setting(context = { })
|
||||
# This will be skipped in ChiliProject 2.x because
|
||||
# acts_as_journalized overrides the prop_key with the label
|
||||
# 'deliverable_id' becomes 'Deliverable' (i18n)
|
||||
#
|
||||
# register_on_journal_formatter is used for ChiliProject 2.x support
|
||||
# TODO Later: Overwritting the caller is bad juju
|
||||
if context[:detail].prop_key == 'deliverable_id'
|
||||
context[:detail].reload
|
||||
|
||||
@@ -9,6 +9,7 @@ module RedmineContracts
|
||||
return stylesheet_link_tag("redmine_contracts", :plugin => "redmine_contracts", :media => "screen") +
|
||||
|
||||
javascript_include_tag('jquery-1.4.2.min.js', :plugin => 'redmine_contracts') +
|
||||
javascript_include_tag('jquery.tmpl.min.js', :plugin => 'redmine_contracts') +
|
||||
javascript_tag('jQuery.noConflict();') +
|
||||
javascript_include_tag('contracts.js', :plugin => 'redmine_contracts')
|
||||
|
||||
|
||||
@@ -12,6 +12,17 @@ module RedmineContracts
|
||||
delegate :title, :to => :deliverable, :prefix => true, :allow_nil => true
|
||||
delegate :contract, :to => :deliverable, :allow_nil => true
|
||||
|
||||
# ChiliProject 2.x support for acts_as_journalized.
|
||||
# Used to format the journal details on the Issue page
|
||||
#
|
||||
# See RedmineContracts::Hooks::HelperIssuesShowDetailAfterSettingHook
|
||||
# for <2.x and Redmine version
|
||||
#
|
||||
# TODO: Will not support permissions or custom code in the formatter.
|
||||
if Issue.respond_to?(:register_on_journal_formatter)
|
||||
register_on_journal_formatter(:named_association, 'deliverable_id')
|
||||
end
|
||||
|
||||
def contract_name
|
||||
contract.try(:name)
|
||||
end
|
||||
|
||||
@@ -16,6 +16,27 @@ module RedmineContracts
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def billable_activities
|
||||
activities_sorted_by_billable[:billable]
|
||||
end
|
||||
|
||||
def non_billable_activities
|
||||
activities_sorted_by_billable[:non_billable]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def activities_sorted_by_billable
|
||||
split_activities = activities.partition do |activity|
|
||||
activity.billable?
|
||||
end
|
||||
|
||||
{
|
||||
:billable => split_activities.first,
|
||||
:non_billable => split_activities.second
|
||||
}
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -261,7 +261,52 @@ class ContractsShowTest < ActionController::IntegrationTest
|
||||
assert_select "td.fixed.spent-amount", :text => /1,000/
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
should "show the total budget for a Deliverable" do
|
||||
@manager = User.generate!
|
||||
|
||||
@deliverable1 = FixedDeliverable.generate!(:contract => @contract, :manager => @manager, :total => '5404')
|
||||
|
||||
visit_contract_page(@contract)
|
||||
assert_select "table#deliverables" do
|
||||
assert_select "td.total.total-amount", :text => /5,404/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show the total spent for a Deliverable" do
|
||||
configure_overhead_plugin
|
||||
@manager = User.generate!
|
||||
@contract.billable_rate = 200
|
||||
assert @contract.save
|
||||
|
||||
@deliverable1 = HourlyDeliverable.generate!(:contract => @contract, :manager => @manager, :total => '1504')
|
||||
|
||||
@issue1 = Issue.generate_for_project!(@project)
|
||||
@time_entry1 = TimeEntry.generate!(:issue => @issue1,
|
||||
:project => @project,
|
||||
:activity => @billable_activity,
|
||||
:spent_on => Date.today,
|
||||
:hours => 15,
|
||||
:user => @manager)
|
||||
|
||||
@rate = Rate.generate!(:project => @project,
|
||||
:user => @manager,
|
||||
:date_in_effect => Date.yesterday,
|
||||
:amount => 100)
|
||||
|
||||
@deliverable1.issues << @issue1
|
||||
|
||||
assert_equal 1, @deliverable1.issues.count
|
||||
|
||||
visit_contract_page(@contract)
|
||||
assert_select "table#deliverables" do
|
||||
# Using the contract billable rate and not the user rate because it's income, not an expense
|
||||
assert_select "td.total.spent-amount", :text => /3,000/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show each fixed budget item in the details for the Deliverable" do
|
||||
@manager = User.generate!
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
@hourly_deliverable = HourlyDeliverable.generate!(:contract => @contract, :manager => @manager, :title => 'An Hourly')
|
||||
|
||||
@user = User.generate_user_with_permission_to_manage_budget(:project => @project)
|
||||
configure_overhead_plugin
|
||||
|
||||
login_as(@user.login, 'contracts')
|
||||
end
|
||||
@@ -420,10 +421,12 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
# * labor hidden month
|
||||
# * labor hours
|
||||
# * labor amount
|
||||
# * labor deleted (hidden)
|
||||
# * overhead hidden year
|
||||
# * overhead hidden month
|
||||
# * overhead hours
|
||||
# * overhead amount
|
||||
# * overhead deleted (hidden)
|
||||
# * fixed hidden year
|
||||
# * fixed hidden month
|
||||
# * fixed title
|
||||
@@ -431,9 +434,10 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
# * fixed markup
|
||||
# * fixed paid checkbox
|
||||
# * fixed paid hidden field
|
||||
# * fixed deleted (hidden)
|
||||
# * total (hidden)
|
||||
assert_select ".date-2010-01" do
|
||||
assert_select "input", :count => 16
|
||||
assert_select "input", :count => 19
|
||||
assert_select "textarea.wiki-edit", :count => 1 # Fixed description
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
@project = Project.generate!(:identifier => 'main')
|
||||
@contract = Contract.generate!(:project => @project)
|
||||
@user = User.generate_user_with_permission_to_manage_budget(:project => @project)
|
||||
configure_overhead_plugin
|
||||
|
||||
login_as(@user.login, 'contracts')
|
||||
end
|
||||
@@ -231,11 +232,13 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
within("#deliverable-labor") do
|
||||
select @billable_activity.name, :from => 'Activity'
|
||||
fill_in "hrs", :with => '20'
|
||||
fill_in "$", :with => '$2,000'
|
||||
end
|
||||
|
||||
within("#deliverable-overhead") do
|
||||
select @non_billable_activity.name, :from => 'Activity'
|
||||
fill_in "hrs", :with => '10'
|
||||
fill_in "$", :with => '$1,000'
|
||||
end
|
||||
@@ -258,11 +261,13 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
@labor_budget = @deliverable.labor_budgets.first
|
||||
assert_equal 20, @labor_budget.hours
|
||||
assert_equal 2000.0, @labor_budget.budget
|
||||
assert_equal @billable_activity, @labor_budget.time_entry_activity
|
||||
|
||||
assert_equal 1, @deliverable.overhead_budgets.count
|
||||
@overhead_budget = @deliverable.overhead_budgets.first
|
||||
assert_equal 10, @overhead_budget.hours
|
||||
assert_equal 1000.0, @overhead_budget.budget
|
||||
assert_equal @non_billable_activity, @overhead_budget.time_entry_activity
|
||||
|
||||
assert_equal 1, @deliverable.fixed_budgets.count
|
||||
@fixed_budget = @deliverable.fixed_budgets.first
|
||||
|
||||
@@ -92,6 +92,12 @@ class ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
begin
|
||||
require 'ruby_gc_test_patch'
|
||||
include RubyGcTestPatch
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
def configure_overhead_plugin
|
||||
@custom_field = TimeEntryActivityCustomField.generate!
|
||||
Setting['plugin_redmine_overhead'] = {
|
||||
|
||||
@@ -2,6 +2,9 @@ require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class LaborBudgetTest < ActiveSupport::TestCase
|
||||
should_belong_to :deliverable
|
||||
should_belong_to :time_entry_activity
|
||||
|
||||
should_validate_presence_of :time_entry_activity_id
|
||||
|
||||
context "#budget=" do
|
||||
should "strip dollar signs when writing" do
|
||||
|
||||
@@ -2,6 +2,9 @@ require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class OverheadBudgetTest < ActiveSupport::TestCase
|
||||
should_belong_to :deliverable
|
||||
should_belong_to :time_entry_activity
|
||||
|
||||
should_validate_presence_of :time_entry_activity_id
|
||||
|
||||
context "#budget=" do
|
||||
should "strip dollar signs when writing" do
|
||||
|
||||
Reference in New Issue
Block a user