Merge branch '6441-contract-and-deliverable-status'
This commit is contained in:
@@ -1,12 +1,52 @@
|
||||
module ContractsHelper
|
||||
def setup_nested_deliverable_records(deliverable)
|
||||
returning(deliverable) do |d|
|
||||
d.labor_budgets.build if d.labor_budgets.empty?
|
||||
d.overhead_budgets.build if d.overhead_budgets.empty?
|
||||
d.fixed_budgets.build if d.fixed_budgets.empty?
|
||||
deliverable.labor_budgets.build if deliverable.labor_budgets.empty?
|
||||
deliverable.overhead_budgets.build if deliverable.overhead_budgets.empty?
|
||||
deliverable.fixed_budgets.build if deliverable.fixed_budgets.empty?
|
||||
deliverable
|
||||
end
|
||||
|
||||
def group_contracts_by_status(contracts)
|
||||
grouped_contracts = contracts.inject({}) do |grouped, contract|
|
||||
grouped[contract.status] ||= []
|
||||
grouped[contract.status] << contract
|
||||
grouped
|
||||
end
|
||||
grouped_contracts["open"] ||= []
|
||||
grouped_contracts["locked"] ||= []
|
||||
grouped_contracts["closed"] ||= []
|
||||
grouped_contracts
|
||||
end
|
||||
|
||||
def grouped_deliverable_options_for_select(project, selected_key=nil)
|
||||
project.contracts.all(:include => :deliverables).inject("") do |html, contract|
|
||||
if contract.closed? && !contract.includes_deliverable_id?(selected_key)
|
||||
html
|
||||
else
|
||||
html << content_tag(:optgroup,
|
||||
deliverable_options_for_contract(contract, selected_key).join("\n"),
|
||||
:label => h(contract.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deliverable_options_for_contract(contract, selected_key)
|
||||
contract.deliverables.collect do |deliverable|
|
||||
deliverable_option(deliverable, selected_key)
|
||||
end
|
||||
end
|
||||
|
||||
def deliverable_option(deliverable, selected_key)
|
||||
option_attributes = {}
|
||||
option_attributes[:value] = h(deliverable.id)
|
||||
option_attributes[:selected] = "selected" if selected_key.to_i == deliverable.id
|
||||
option_attributes[:disabled] = "disabled" if (deliverable.locked? || deliverable.contract_locked?) && selected_key.to_i != deliverable.id
|
||||
|
||||
return "" if deliverable.closed? && option_attributes[:selected].blank? # Skip unselected, closed
|
||||
|
||||
content_tag(:option, h(deliverable.title), option_attributes)
|
||||
end
|
||||
|
||||
# Simple helper to show the values of a field on an object in a standard format
|
||||
#
|
||||
# <p>
|
||||
|
||||
+72
-9
@@ -17,7 +17,9 @@ class Contract < ActiveRecord::Base
|
||||
validates_presence_of :start_date
|
||||
validates_presence_of :end_date
|
||||
validates_inclusion_of :discount_type, :in => %w($ %), :allow_blank => true, :allow_nil => true
|
||||
validates_inclusion_of :status, :in => ["open","locked","closed"], :allow_blank => true, :allow_nil => true
|
||||
validate :start_and_end_date_are_valid
|
||||
validate_on_update :validate_status_changes
|
||||
|
||||
# Accessors
|
||||
attr_accessible :name
|
||||
@@ -33,15 +35,49 @@ class Contract < ActiveRecord::Base
|
||||
attr_accessible :po_number
|
||||
attr_accessible :client_point_of_contact
|
||||
attr_accessible :details
|
||||
attr_accessible :status
|
||||
|
||||
named_scope :by_name, {:order => "#{Contract.table_name}.name ASC"}
|
||||
|
||||
[:status, :contract_type,
|
||||
named_scope :with_status, lambda {|statuses|
|
||||
{
|
||||
:conditions => ["#{Contract.table_name}.status IN (?)", statuses]
|
||||
}
|
||||
}
|
||||
|
||||
[:contract_type,
|
||||
:discount_spent, :discount_budget
|
||||
].each do |mthd|
|
||||
define_method(mthd) { "TODO in later release" }
|
||||
end
|
||||
|
||||
def status
|
||||
read_attribute(:status) || "open"
|
||||
end
|
||||
|
||||
def lock!
|
||||
update_attribute(:status, "locked")
|
||||
end
|
||||
|
||||
def close!
|
||||
update_attribute(:status, "closed")
|
||||
end
|
||||
|
||||
def open?
|
||||
self.status == "open"
|
||||
end
|
||||
|
||||
def locked?
|
||||
self.status == "locked"
|
||||
end
|
||||
|
||||
def closed?
|
||||
self.status == "closed"
|
||||
end
|
||||
|
||||
def includes_deliverable_id?(deliverable_id)
|
||||
deliverable_ids.include?(deliverable_id.to_i)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Labor Methods
|
||||
# ------------------------------------------------------------
|
||||
@@ -229,6 +265,7 @@ class Contract < ActiveRecord::Base
|
||||
|
||||
def after_initialize
|
||||
self.executed = false unless self.executed.present?
|
||||
self.status = "open" unless self.status.present?
|
||||
end
|
||||
|
||||
# Are the start_date and end_date valid?
|
||||
@@ -238,6 +275,30 @@ class Contract < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def valid_status_change?
|
||||
change_to_status_only? || changing_to_the_open_status? || changing_from_the_open_status?
|
||||
end
|
||||
|
||||
def change_to_status_only?
|
||||
["status"] == changes.keys
|
||||
end
|
||||
|
||||
def changing_to_the_open_status?
|
||||
changes["status"].present? && "open" == changes["status"].second
|
||||
end
|
||||
|
||||
def changing_from_the_open_status?
|
||||
changes["status"].present? && "open" == changes["status"].first
|
||||
end
|
||||
|
||||
# TODO: duplicated on Deliverable, refactor after one more duplication
|
||||
def validate_status_changes
|
||||
return if valid_status_change?
|
||||
|
||||
errors.add_to_base(:cant_update_locked_contract) if locked?
|
||||
errors.add_to_base(:cant_update_closed_contract) if closed?
|
||||
end
|
||||
|
||||
# Currency amount of time that is logged to the project or to issues
|
||||
# that are not assigned to a Deliverable
|
||||
def orphaned_time
|
||||
@@ -252,15 +313,17 @@ class Contract < ActiveRecord::Base
|
||||
end
|
||||
|
||||
if Rails.env.test?
|
||||
generator_for :name, :method => :next_name
|
||||
generator_for :name, :start => "Contract 0000"
|
||||
generator_for :executed => true
|
||||
generator_for(:start_date) { Date.yesterday }
|
||||
generator_for(:end_date) { Date.tomorrow }
|
||||
|
||||
def self.next_name
|
||||
@last_name ||= 'Contract 0000'
|
||||
@last_name.succ!
|
||||
end
|
||||
generator_for :discount, ''
|
||||
generator_for :details, ''
|
||||
generator_for :discount_note, ''
|
||||
generator_for :client_point_of_contact, ''
|
||||
generator_for :client_ap_contact_information, ''
|
||||
generator_for :po_number, ''
|
||||
generator_for :status, 'open'
|
||||
|
||||
end
|
||||
|
||||
@@ -270,5 +333,5 @@ class Contract < ActiveRecord::Base
|
||||
def summarize_associated_values(records, value_method)
|
||||
records.inject(0) {|total, record| total += record.send(value_method)}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
+102
-2
@@ -19,15 +19,27 @@ class Deliverable < ActiveRecord::Base
|
||||
validates_presence_of :title
|
||||
validates_presence_of :type
|
||||
validates_presence_of :manager
|
||||
validates_inclusion_of :status, :in => ["open","locked","closed"], :allow_blank => true, :allow_nil => true
|
||||
validate_on_update :validate_status_changes
|
||||
validate :validate_contract_status
|
||||
|
||||
# Accessors
|
||||
include DollarizedAttribute
|
||||
dollarized_attribute :total
|
||||
|
||||
delegate :name, :to => :contract, :prefix => true, :allow_nil => true
|
||||
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
|
||||
|
||||
# Callbacks
|
||||
|
||||
before_destroy :block_on_locked_contracts
|
||||
before_destroy :block_on_closed_contracts
|
||||
|
||||
def after_initialize
|
||||
self.status = "open" unless self.status.present?
|
||||
end
|
||||
|
||||
# Register callbacks here, on new records the class isn't set so class-specific
|
||||
# callbacks don't fire.
|
||||
def after_save
|
||||
@@ -37,6 +49,11 @@ class Deliverable < ActiveRecord::Base
|
||||
end
|
||||
|
||||
named_scope :by_title, {:order => "#{Deliverable.table_name}.title ASC"}
|
||||
named_scope :with_status, lambda {|statuses|
|
||||
{
|
||||
:conditions => ["#{Deliverable.table_name}.status IN (?)", statuses]
|
||||
}
|
||||
}
|
||||
|
||||
def short_type
|
||||
''
|
||||
@@ -51,6 +68,88 @@ class Deliverable < ActiveRecord::Base
|
||||
nil
|
||||
end
|
||||
|
||||
def lock!
|
||||
update_attribute(:status, "locked")
|
||||
end
|
||||
|
||||
def close!
|
||||
update_attribute(:status, "closed")
|
||||
end
|
||||
|
||||
def open?
|
||||
self.status == "open"
|
||||
end
|
||||
|
||||
def locked?
|
||||
self.status == "locked"
|
||||
end
|
||||
|
||||
def closed?
|
||||
self.status == "closed"
|
||||
end
|
||||
|
||||
def editable?
|
||||
(new_record? || open?)
|
||||
end
|
||||
|
||||
def valid_status_change?
|
||||
change_to_status_only? || changing_to_the_open_status? || changing_from_the_open_status?
|
||||
end
|
||||
|
||||
def change_to_status_only?
|
||||
["status"] == changes.keys
|
||||
end
|
||||
|
||||
def changing_to_the_open_status?
|
||||
changes["status"].present? && "open" == changes["status"].second
|
||||
end
|
||||
|
||||
def changing_from_the_open_status?
|
||||
changes["status"].present? && "open" == changes["status"].first
|
||||
end
|
||||
|
||||
# TODO: duplicated on Contract, refactor after one more duplication
|
||||
def validate_status_changes
|
||||
return if valid_status_change?
|
||||
|
||||
errors.add_to_base(:cant_update_locked_deliverable) if locked?
|
||||
errors.add_to_base(:cant_update_closed_deliverable) if closed?
|
||||
end
|
||||
|
||||
def validate_contract_status
|
||||
return if contract_open?
|
||||
return if change_to_status_only?
|
||||
|
||||
if contract_locked?
|
||||
if new_record?
|
||||
errors.add_to_base(:cant_create_deliverable_on_locked_contract)
|
||||
else
|
||||
errors.add_to_base(:cant_update_locked_contract)
|
||||
end
|
||||
end
|
||||
|
||||
if contract_closed?
|
||||
if new_record?
|
||||
errors.add_to_base(:cant_create_deliverable_on_closed_contract)
|
||||
else
|
||||
errors.add_to_base(:cant_update_closed_contract)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# No operation method, useful to clean up logic with an optional message
|
||||
# for documentation
|
||||
def noop(message="")
|
||||
end
|
||||
|
||||
def block_on_locked_contracts
|
||||
!contract_locked?
|
||||
end
|
||||
|
||||
def block_on_closed_contracts
|
||||
!contract_closed?
|
||||
end
|
||||
|
||||
def to_s
|
||||
title
|
||||
end
|
||||
@@ -214,7 +313,8 @@ class Deliverable < ActiveRecord::Base
|
||||
|
||||
if Rails.env.test?
|
||||
generator_for :title, :method => :next_title
|
||||
|
||||
generator_for :status, 'open'
|
||||
|
||||
def self.next_title
|
||||
@last_title ||= 'Deliverable 0000'
|
||||
@last_title.succ!
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<% if resource.locked? || resource.closed? %>
|
||||
<div class="error_msg">
|
||||
<p><%= resource.locked? ? l(:text_contract_locked_warning) : l(:text_contract_closed_warning) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="box tabular">
|
||||
<% form.inputs :name => l(:text_general_legend) do %>
|
||||
<%= form.input :name, :required => true %>
|
||||
<%= form.input :status, :required => true, :collection => [["Open","open"],["Locked","locked"],["Closed","closed"]] %>
|
||||
<%= form.input :account_executive, :required => true, :collection => @project.users.sort %>
|
||||
<li class="boolean optional">
|
||||
<%= label('contract', 'executed') %>
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
</div>
|
||||
|
||||
<% if collection.empty? %>
|
||||
<% if group_contracts_by_status(collection)["open"].empty? %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% else %>
|
||||
<table class="list" cellspacing="0" border="0" cellpadding="0" id="contracts">
|
||||
<table class="list open" cellspacing="0" border="0" cellpadding="0" id="contracts">
|
||||
<thead>
|
||||
<th><%= l(:field_id) %></th>
|
||||
<th><%= l(:field_name) %></th>
|
||||
@@ -26,11 +26,11 @@
|
||||
<th><%= l(:field_end_date) %></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% collection.each do |contract| %>
|
||||
<% group_contracts_by_status(collection)["open"].each do |contract| %>
|
||||
<% content_tag_for(:tr, contract, :class => cycle('','odd')) do %>
|
||||
<td class="id"><%= link_to(h(contract.id), contract_path(@project, contract)) %></td>
|
||||
<td class="name"><%= link_to(h(contract.name), contract_path(@project, contract)) %></td>
|
||||
<td><%= release(5, "Contract Status") %></td>
|
||||
<td class="status"><%= h(contract.status) %></td>
|
||||
<td><%= release(5, "Contract Type") %></td>
|
||||
<td class="account-executive"><%= h contract.account_executive.name %></td>
|
||||
<td class="total-budget"><%= h(format_value_field_for_contracts(contract.total_budget)) %></td>
|
||||
@@ -42,11 +42,71 @@
|
||||
<% end %>
|
||||
|
||||
<div class="title-bar">
|
||||
<h2>Inactive Contracts</h2>
|
||||
<h2>Locked Contracts</h2>
|
||||
</div>
|
||||
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<p><%= release(5, "Contract Status. Split contracts by active and inactive") %></p>
|
||||
<% if group_contracts_by_status(collection)["locked"].empty? %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% else %>
|
||||
<table class="list locked" cellspacing="0" border="0" cellpadding="0" id="contracts">
|
||||
<thead>
|
||||
<th><%= l(:field_id) %></th>
|
||||
<th><%= l(:field_name) %></th>
|
||||
<th><%= l(:field_status) %></th>
|
||||
<th><%= l(:field_type) %></th>
|
||||
<th><%= l(:field_account_executive_short) %></th>
|
||||
<th><%= l(:field_total_budget) %></th>
|
||||
<th><%= l(:field_end_date) %></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% group_contracts_by_status(collection)["locked"].each do |contract| %>
|
||||
<% content_tag_for(:tr, contract, :class => cycle('','odd')) do %>
|
||||
<td class="id"><%= link_to(h(contract.id), contract_path(@project, contract)) %></td>
|
||||
<td class="name"><%= link_to(h(contract.name), contract_path(@project, contract)) %></td>
|
||||
<td class="status"><%= h(contract.status) %></td>
|
||||
<td><%= release(5, "Contract Type") %></td>
|
||||
<td class="account-executive"><%= h contract.account_executive.name %></td>
|
||||
<td class="total-budget"><%= h(format_value_field_for_contracts(contract.total_budget)) %></td>
|
||||
<td class="end-date"><%= h format_date(contract.end_date) %></td>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<div class="title-bar">
|
||||
<h2>Closed Contracts</h2>
|
||||
</div>
|
||||
|
||||
<% if group_contracts_by_status(collection)["closed"].empty? %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% else %>
|
||||
<table class="list closed" cellspacing="0" border="0" cellpadding="0" id="contracts">
|
||||
<thead>
|
||||
<th><%= l(:field_id) %></th>
|
||||
<th><%= l(:field_name) %></th>
|
||||
<th><%= l(:field_status) %></th>
|
||||
<th><%= l(:field_type) %></th>
|
||||
<th><%= l(:field_account_executive_short) %></th>
|
||||
<th><%= l(:field_total_budget) %></th>
|
||||
<th><%= l(:field_end_date) %></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% group_contracts_by_status(collection)["closed"].each do |contract| %>
|
||||
<% content_tag_for(:tr, contract, :class => cycle('','odd')) do %>
|
||||
<td class="id"><%= link_to(h(contract.id), contract_path(@project, contract)) %></td>
|
||||
<td class="name"><%= link_to(h(contract.name), contract_path(@project, contract)) %></td>
|
||||
<td class="status"><%= h(contract.status) %></td>
|
||||
<td><%= release(5, "Contract Type") %></td>
|
||||
<td class="account-executive"><%= h contract.account_executive.name %></td>
|
||||
<td class="total-budget"><%= h(format_value_field_for_contracts(contract.total_budget)) %></td>
|
||||
<td class="end-date"><%= h format_date(contract.end_date) %></td>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
|
||||
<div class="c_overview">
|
||||
<table class="left">
|
||||
<tr class="contract-status">
|
||||
<%# show_field(resource, :status, :html_options => {:class => 'contract-status'}) %>
|
||||
<td colspan="2"><%= release(5, "Contract status") %></td>
|
||||
</tr>
|
||||
<%= show_field(resource, :status, :html_options => {:class => 'contract-status'}) %>
|
||||
<%= show_field(resource, :account_executive, :html_options => {:class => 'contract-account-manager'}) %>
|
||||
<tr class="contract-type">
|
||||
<%# show_field(resource, :contract_type, :html_options => {:class => 'contract-type'}) %>
|
||||
@@ -105,7 +102,7 @@
|
||||
<div class="actions">
|
||||
<a href="#TODO-release-2"><%= release(2, "CSV") %></a>
|
||||
<a href="#TODO-release?"><%= release(5, "View All/Pagination") %></a>
|
||||
<%= link_to(l(:button_add_new), new_contract_deliverable_path(@project, resource), :id => 'new-deliverable') %>
|
||||
<%= link_to(l(:button_add_new), new_contract_deliverable_path(@project, resource), :id => 'new-deliverable') if resource.open? %>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
@@ -133,7 +130,7 @@
|
||||
<td width="10%" class="arrow end-date"><span><%= h format_date(deliverable.end_date) %></span></td>
|
||||
<td width="2%" class="type"><%= h deliverable.short_type %></td>
|
||||
<td width="25%" class="title"><%= h deliverable.title %></td>
|
||||
<td width="15%"><%= release(5, "Deliverable status") %></td>
|
||||
<td width="15%" class="status"><%= h deliverable.status %></td>
|
||||
<td width="15%" class="manager"><%= h deliverable.manager.try(:name) %></td>
|
||||
<%= 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') %>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<%= link_to(l(:button_edit), edit_contract_deliverable_path(@project, contract, deliverable), :class => 'icon icon-edit') %>
|
||||
<%= link_to(l(:button_delete), contract_deliverable_path(@project, contract, deliverable), :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
|
||||
<%= link_to(l(:button_delete), contract_deliverable_path(@project, contract, deliverable), :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') if contract.open? %>
|
||||
</div>
|
||||
|
||||
<%= textilizable(deliverable, :notes) %>
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
<%= javascript_tag("var i18nEndDateEmpty = '#{l(:text_end_date_empty)}'") %>
|
||||
<%= javascript_tag("var i18nChangedPeriodMessage = '#{l(:text_changed_period_message)}'") %>
|
||||
|
||||
<% if resource.locked? || resource.closed? || resource.contract_locked? || resource.contract_closed? %>
|
||||
<div class="error_msg">
|
||||
<% if resource.contract_locked? || resource.contract_closed? %>
|
||||
<p><%= resource.contract_locked? ? l(:text_contract_locked_warning) : l(:text_contract_closed_warning) %></p>
|
||||
<% end %>
|
||||
<% if resource.locked? || resource.closed? %>
|
||||
<p><%= resource.locked? ? l(:text_deliverable_locked_warning) : l(:text_deliverable_closed_warning) %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="box tabular">
|
||||
<% form.inputs :name => l(:text_deliverable_details_legend), :id => 'deliverable-details' do %>
|
||||
<%# Used by jquery to check if this is a new or existing record %>
|
||||
@@ -19,6 +30,7 @@
|
||||
</li>
|
||||
<%= form.input :type, :as => :hidden, :class => 'type' %>
|
||||
<% end %>
|
||||
<%= form.input :status, :required => true, :collection => [["Open","open"],["Locked","locked"],["Closed","closed"]] %>
|
||||
<%= form.input :manager, :required => true, :collection => @project.users.sort %>
|
||||
|
||||
<%= form.input :start_date, :as => :string, :input_html => {:size => 10, :class => 'start-date', :id => 'deliverable_start_date'}, :hint => calendar_for('deliverable_start_date') %>
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<% if project.module_enabled?(:contracts) && User.current.allowed_to?(:assign_deliverable_to_issue, project) %>
|
||||
<p>
|
||||
<%= label_tag(:deliverable_id, l(:field_deliverable)) %>
|
||||
<% options = project.contracts.inject([]) {|data, contract|
|
||||
data << [contract.name, contract.deliverables.collect {|d| [d.title, d.id]} ]
|
||||
} %>
|
||||
|
||||
<%= select_tag('deliverable_id',
|
||||
content_tag('option', l(:label_no_change_option), :value => '') +
|
||||
content_tag('option', l(:label_none), :value => 'none') +
|
||||
grouped_options_for_select(options)) %>
|
||||
grouped_deliverable_options_for_select(project)) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<% if project.module_enabled?(:contracts) && User.current.allowed_to?(:assign_deliverable_to_issue, project) %>
|
||||
<p>
|
||||
<% options = project.contracts.inject([]) {|data, contract|
|
||||
data << [contract.name, contract.deliverables.collect {|d| [d.title, d.id]} ]
|
||||
} %>
|
||||
<%= form.select(:deliverable_id, grouped_options_for_select(options, issue.deliverable_id), {:include_blank => true}) %>
|
||||
<%= form.select(:deliverable_id, grouped_deliverable_options_for_select(project, issue.deliverable_id), {:include_blank => true}) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
+19
-1
@@ -1,4 +1,19 @@
|
||||
en:
|
||||
activerecord:
|
||||
errors:
|
||||
messages:
|
||||
cant_create_time_on_object: "Can't create a time entry on a %{reason} %{thing}"
|
||||
cant_assign_to_closed_deliverable: "Can't assign issue to a closed deliverable"
|
||||
cant_assign_to_locked_deliverable: "Can't assign issue to a locked deliverable"
|
||||
cant_assign_to_closed_contract: "Can't assign issue to a closed contract"
|
||||
cant_assign_to_locked_contract: "Can't assign issue to a locked contract"
|
||||
cant_update_locked_deliverable: "Can't update a locked deliverable"
|
||||
cant_update_closed_deliverable: "Can't update a closed deliverable"
|
||||
cant_update_locked_contract: "Can't update a locked contract"
|
||||
cant_update_closed_contract: "Can't update a closed contract"
|
||||
cant_create_deliverable_on_locked_contract: "Can't create a deliverable on a locked contract"
|
||||
cant_create_deliverable_on_closed_contract: "Can't create a deliverable on a closed contract"
|
||||
|
||||
field_end_date: End Date
|
||||
field_executed: Executed
|
||||
text_contracts: Contracts
|
||||
@@ -81,4 +96,7 @@ en:
|
||||
text_error_message_orphaned_time: "There is {{amount}} worth of time clocked to issues that are not assigned to any deliverables."
|
||||
text_error_message_update_orphaned_time: "Please update the orphaned issues."
|
||||
field_estimated: Estimated
|
||||
|
||||
text_deliverable_locked_warning: "This deliverable is locked and cannot be saved without changing it's status to Open."
|
||||
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."
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class AddStatusToContracts < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :contracts, :status, :string
|
||||
add_index :contracts, :status
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :contracts, :status
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
class AddStatusToDeliverables < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :deliverables, :status, :string
|
||||
add_index :deliverables, :status
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :deliverables, :status
|
||||
end
|
||||
end
|
||||
@@ -62,6 +62,9 @@ end
|
||||
|
||||
require 'dispatcher'
|
||||
Dispatcher.to_prepare :redmine_contracts do
|
||||
|
||||
require_dependency 'time_entry'
|
||||
TimeEntry.send(:include, RedmineContracts::Patches::TimeEntryPatch)
|
||||
gem 'inherited_resources', :version => '1.0.6'
|
||||
require_dependency 'inherited_resources'
|
||||
require_dependency 'inherited_resources/base'
|
||||
@@ -99,6 +102,9 @@ Dispatcher.to_prepare :redmine_contracts do
|
||||
unless Query.available_columns.collect(&:name).include?(:contract_name)
|
||||
Query.add_available_column(QueryColumn.new(:contract_name, :sortable => "#{Contract.table_name}.name", :groupable => 'contracts.name'))
|
||||
end
|
||||
|
||||
require_dependency 'application_controller'
|
||||
ApplicationController.send(:helper, :contracts)
|
||||
end
|
||||
|
||||
require 'redmine_contracts/hooks/view_layouts_base_html_head_hook'
|
||||
|
||||
@@ -15,6 +15,24 @@ module RedmineContracts
|
||||
def contract_name
|
||||
contract.try(:name)
|
||||
end
|
||||
|
||||
validate :validate_deliverable_status
|
||||
validate :validate_contract_status
|
||||
|
||||
def validate_deliverable_status
|
||||
if deliverable.present? && changes["deliverable_id"].present?
|
||||
errors.add_to_base(:cant_assign_to_closed_deliverable) if deliverable.closed?
|
||||
errors.add_to_base(:cant_assign_to_locked_deliverable) if deliverable.locked?
|
||||
end
|
||||
end
|
||||
|
||||
def validate_contract_status
|
||||
if deliverable.present? && changes["deliverable_id"].present? && contract.present?
|
||||
errors.add_to_base(:cant_assign_to_closed_contract) if contract.closed?
|
||||
errors.add_to_base(:cant_assign_to_locked_contract) if contract.locked?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
module RedmineContracts
|
||||
module Patches
|
||||
module TimeEntryPatch
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
unloadable
|
||||
|
||||
validate :validate_deliverable_status
|
||||
validate :validate_contract_status
|
||||
|
||||
def validate_deliverable_status
|
||||
if issue.present? && issue.deliverable.present?
|
||||
errors.add_to_base("#{l(:"activerecord.errors.messages.cant_create_time_on_object", :reason => 'locked', :thing => 'deliverable')}") if issue.deliverable.locked?
|
||||
errors.add_to_base("#{l(:"activerecord.errors.messages.cant_create_time_on_object", :reason => 'closed', :thing => 'deliverable')}") if issue.deliverable.closed?
|
||||
end
|
||||
end
|
||||
|
||||
def validate_contract_status
|
||||
if issue.present? && issue.deliverable.present? && issue.deliverable.contract.present?
|
||||
errors.add_to_base("#{l(:"activerecord.errors.messages.cant_create_time_on_object", :reason => 'locked', :thing => 'contract')}") if issue.deliverable.contract.locked?
|
||||
errors.add_to_base("#{l(:"activerecord.errors.messages.cant_create_time_on_object", :reason => 'closed', :thing => 'contract')}") if issue.deliverable.contract.closed?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -54,6 +54,133 @@ class ContractsEditTest < ActionController::IntegrationTest
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert_equal "An updated name", @contract.reload.name
|
||||
end
|
||||
|
||||
context "locked contract" do
|
||||
setup do
|
||||
assert @contract.lock!
|
||||
end
|
||||
|
||||
should "block edits" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
fill_in "Name", :with => 'An updated name'
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/edit'
|
||||
|
||||
assert_not_equal "An updated name", @contract.reload.name
|
||||
end
|
||||
|
||||
should "block edits even when the status is changed to closed" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
fill_in "Name", :with => 'An updated name'
|
||||
select "Closed", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/edit'
|
||||
|
||||
assert_not_equal "An updated name", @contract.reload.name
|
||||
assert @contract.reload.locked?
|
||||
end
|
||||
|
||||
should "be allowed to change the status from locked to open" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
select "Open", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @contract.reload.open?
|
||||
end
|
||||
|
||||
should "be allowed to change the status from locked to closed" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
select "Closed", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @contract.reload.closed?
|
||||
end
|
||||
end
|
||||
|
||||
context "closed contract" do
|
||||
setup do
|
||||
assert @contract.close!
|
||||
end
|
||||
|
||||
should "block edits" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
fill_in "Name", :with => 'An updated name'
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/edit'
|
||||
|
||||
assert_not_equal "An updated name", @contract.reload.name
|
||||
end
|
||||
|
||||
should "block edits weven when the status is changed to locked" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
fill_in "Name", :with => 'An updated name'
|
||||
select "Locked", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/edit'
|
||||
|
||||
assert_not_equal "An updated name", @contract.reload.name
|
||||
assert @contract.reload.closed?
|
||||
end
|
||||
|
||||
should "be allowed to change the status from closed to open" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
select "Open", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @contract.reload.open?
|
||||
end
|
||||
|
||||
should "be allowed to change the status from closed to locked" do
|
||||
visit_contract_page(@contract)
|
||||
click_link 'Update'
|
||||
assert_response :success
|
||||
|
||||
select "Locked", :from => "Status"
|
||||
click_button 'Save Contract'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @contract.reload.locked?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,8 +5,10 @@ class ContractsListTest < ActionController::IntegrationTest
|
||||
|
||||
def setup
|
||||
@project = Project.generate!(:identifier => 'main')
|
||||
@contract = Contract.generate!(:project => @project)
|
||||
@contract2 = Contract.generate!(:project => @project)
|
||||
@contract = Contract.generate!(:project => @project, :name => 'Contract1').reload
|
||||
@contract2 = Contract.generate!(:project => @project, :name => 'Contract2').reload
|
||||
@contract_locked = Contract.generate!(:project => @project, :status => 'locked', :name => 'LockedContract').reload
|
||||
@contract_closed = Contract.generate!(:project => @project, :status => 'closed', :name => 'ClosedContract').reload
|
||||
|
||||
@other_project = Project.generate!(:identifier => 'other')
|
||||
@other_contract = Contract.generate!(:project => @other_project)
|
||||
@@ -44,10 +46,10 @@ class ContractsListTest < ActionController::IntegrationTest
|
||||
visit_contracts_for_project(@project)
|
||||
end
|
||||
|
||||
should "list all contracts for the project" do
|
||||
should "list all contracts for the project grouped by status" do
|
||||
visit_contracts_for_project(@project)
|
||||
|
||||
assert_select "table#contracts" do
|
||||
assert_select "table#contracts.open" do
|
||||
[@contract, @contract2].each do |contract|
|
||||
assert_select "td.id", :text => /#{contract.id}/
|
||||
assert_select "td.name", :text => /#{contract.name}/
|
||||
@@ -56,7 +58,23 @@ class ContractsListTest < ActionController::IntegrationTest
|
||||
assert_select "td.total-budget"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_select "table#contracts.locked" do
|
||||
assert_select "td.id", :text => /#{@contract_locked.id}/
|
||||
assert_select "td.name", :text => /#{@contract_locked.name}/
|
||||
assert_select "td.account-executive", :text => /#{@contract_locked.account_executive.name}/
|
||||
assert_select "td.end-date", :text => /#{format_date(@contract_locked.end_date)}/
|
||||
assert_select "td.total-budget"
|
||||
end
|
||||
|
||||
assert_select "table#contracts.closed" do
|
||||
assert_select "td.id", :text => /#{@contract_closed.id}/
|
||||
assert_select "td.name", :text => /#{@contract_closed.name}/
|
||||
assert_select "td.account-executive", :text => /#{@contract_closed.account_executive.name}/
|
||||
assert_select "td.end-date", :text => /#{format_date(@contract_closed.end_date)}/
|
||||
assert_select "td.total-budget"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "not list contracts from other projects" do
|
||||
|
||||
@@ -52,6 +52,7 @@ class ContractsNewTest < ActionController::IntegrationTest
|
||||
fill_in "Start", :with => '2010-01-01'
|
||||
fill_in "End Date", :with => '2010-12-31'
|
||||
select "Net 30", :from => "Payment Terms"
|
||||
select "Locked", :from => "Status"
|
||||
|
||||
click_button "Save Contract"
|
||||
|
||||
@@ -64,6 +65,7 @@ class ContractsNewTest < ActionController::IntegrationTest
|
||||
assert_equal '2010-01-01', @contract.start_date.to_s
|
||||
assert_equal '2010-12-31', @contract.end_date.to_s
|
||||
assert_equal 'Net 30', @contract.payment_term.name
|
||||
assert_equal "locked", @contract.status
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
@manager = User.generate!
|
||||
@role = Role.generate!
|
||||
User.add_to_project(@manager, @project, @role)
|
||||
@fixed_deliverable = FixedDeliverable.generate!(:contract => @contract, :manager => @manager, :title => 'The Title')
|
||||
@fixed_deliverable = FixedDeliverable.generate!(:contract => @contract, :manager => @manager, :title => 'The Title', :notes => "", :feature_sign_off => false, :warranty_sign_off => false)
|
||||
@hourly_deliverable = HourlyDeliverable.generate!(:contract => @contract, :manager => @manager, :title => 'An Hourly')
|
||||
|
||||
@user = User.generate_user_with_permission_to_manage_budget(:project => @project)
|
||||
@@ -49,6 +49,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
select "Locked", :from => "Status"
|
||||
check "Feature Sign Off"
|
||||
check "Warranty Sign Off"
|
||||
end
|
||||
@@ -61,6 +62,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
assert_equal "FixedDeliverable", @fixed_deliverable.reload.type
|
||||
assert @fixed_deliverable.reload.warranty_sign_off?
|
||||
assert @fixed_deliverable.reload.feature_sign_off?
|
||||
assert_equal "locked", @fixed_deliverable.reload.status
|
||||
|
||||
end
|
||||
|
||||
@@ -78,6 +80,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
select "Locked", :from => "Status"
|
||||
check "Feature Sign Off"
|
||||
check "Warranty Sign Off"
|
||||
end
|
||||
@@ -101,6 +104,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
assert_equal "HourlyDeliverable", @hourly_deliverable.reload.type
|
||||
assert @hourly_deliverable.reload.warranty_sign_off?
|
||||
assert @hourly_deliverable.reload.feature_sign_off?
|
||||
assert_equal "locked", @hourly_deliverable.reload.status
|
||||
|
||||
assert_equal 1, @hourly_deliverable.labor_budgets.count
|
||||
@labor_budget = @hourly_deliverable.labor_budgets.first
|
||||
@@ -471,4 +475,240 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
assert_equal 3, @retainer_deliverable.fixed_budgets.count
|
||||
assert_equal [600, nil, nil], @retainer_deliverable.fixed_budgets.collect(&:budget)
|
||||
end
|
||||
|
||||
context "locked deliverable" do
|
||||
setup do
|
||||
assert @fixed_deliverable.lock!
|
||||
end
|
||||
|
||||
should "block edits to locked deliverables" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
end
|
||||
|
||||
should "block edits to locked deliverables even when status changes to closed" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
select "Closed", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
assert @fixed_deliverable.reload.locked?
|
||||
end
|
||||
|
||||
should "be allowed to change the status on a locked deliverables to open" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Open", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.open?
|
||||
end
|
||||
|
||||
should "be allowed to change the status on a locked deliverables to closed" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Closed", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.closed?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "closed deliverable" do
|
||||
setup do
|
||||
assert @fixed_deliverable.close!
|
||||
end
|
||||
|
||||
should "block edits to closed deliverables" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
end
|
||||
|
||||
should "block edits to closed deliverables even when the status is changed to locked" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
select "Locked", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
assert @fixed_deliverable.reload.closed?
|
||||
end
|
||||
|
||||
should "be allowed to change the status on a closed deliverables to open" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Open", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.open?
|
||||
end
|
||||
|
||||
should "be allowed to change the status on a closed deliverables to Locked" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Locked", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.locked?
|
||||
end
|
||||
end
|
||||
|
||||
context "a Deliverable on a locked Contract" do
|
||||
setup do
|
||||
assert @contract.lock!
|
||||
end
|
||||
|
||||
should "be blocked from editing" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
end
|
||||
|
||||
should "allow status only changes" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Locked", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.locked?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "a Deliverable on a closed Contract" do
|
||||
setup do
|
||||
assert @contract.close!
|
||||
end
|
||||
|
||||
should "be blocked from editing" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'An updated title'
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_not_equal "An updated title", @fixed_deliverable.reload.title
|
||||
end
|
||||
|
||||
should "allow status only changes" do
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@fixed_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
|
||||
within("#deliverable-details") do
|
||||
select "Locked", :from => "Status"
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
assert @fixed_deliverable.reload.locked?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -74,6 +74,7 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'A New Deliverable'
|
||||
select "Fixed", :from => "Type"
|
||||
select "Locked", :from => "Status"
|
||||
select @manager.name, :from => "Manager"
|
||||
fill_in "Start", :with => '2010-01-01'
|
||||
fill_in "End Date", :with => '2010-12-31'
|
||||
@@ -95,6 +96,7 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
assert_equal '2010-12-31', @deliverable.end_date.to_s
|
||||
assert_equal @manager, @deliverable.manager
|
||||
assert_equal 1000.0, @deliverable.total.to_f
|
||||
assert_equal "locked", @deliverable.status
|
||||
end
|
||||
|
||||
should "create a new Hourly deliverable" do
|
||||
@@ -109,6 +111,7 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'A New Deliverable'
|
||||
select "Hourly", :from => "Type"
|
||||
select "Locked", :from => "Status"
|
||||
select @manager.name, :from => "Manager"
|
||||
fill_in "Start", :with => '2010-01-01'
|
||||
fill_in "End Date", :with => '2010-12-31'
|
||||
@@ -128,7 +131,8 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
assert_equal '2010-01-01', @deliverable.start_date.to_s
|
||||
assert_equal '2010-12-31', @deliverable.end_date.to_s
|
||||
assert_equal @manager, @deliverable.manager
|
||||
|
||||
assert_equal "locked", @deliverable.status
|
||||
|
||||
end
|
||||
|
||||
should "create a new Retainer deliverable" do
|
||||
@@ -143,6 +147,7 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
within("#deliverable-details") do
|
||||
fill_in "Title", :with => 'A New Deliverable'
|
||||
select "Retainer", :from => "Type"
|
||||
select "Locked", :from => "Status"
|
||||
select @manager.name, :from => "Manager"
|
||||
fill_in "Start", :with => '2010-01-01'
|
||||
fill_in "End Date", :with => '2010-12-31'
|
||||
@@ -171,7 +176,8 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
assert_equal '2010-01-01', @deliverable.start_date.to_s
|
||||
assert_equal '2010-12-31', @deliverable.end_date.to_s
|
||||
assert_equal @manager, @deliverable.manager
|
||||
|
||||
assert_equal "locked", @deliverable.status
|
||||
|
||||
# Budget items, one per month
|
||||
labor_budgets = @deliverable.labor_budgets
|
||||
assert_equal 12, labor_budgets.length
|
||||
|
||||
@@ -24,10 +24,10 @@ class RedmineContracts::Hooks::ControllerIssuesEditBeforeSaveTest < ActionContro
|
||||
context "for a new issue" do
|
||||
setup do
|
||||
visit_project(@project)
|
||||
click_link "New issue"
|
||||
end
|
||||
|
||||
should "set the issue's deliverable" do
|
||||
click_link "New issue"
|
||||
fill_in "Subject", :with => 'Hook test'
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Create"
|
||||
@@ -38,6 +38,70 @@ class RedmineContracts::Hooks::ControllerIssuesEditBeforeSaveTest < ActionContro
|
||||
|
||||
end
|
||||
|
||||
should "not allow setting a locked Deliverable" do
|
||||
assert @deliverable2.lock!
|
||||
click_link "New issue"
|
||||
|
||||
fill_in "Subject", :with => 'Hook test'
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
assert_no_difference("Issue.count") do
|
||||
click_button "Create"
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
assert_equal nil, Issue.last.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow setting a closed Deliverable" do
|
||||
assert @deliverable2.close!
|
||||
click_link "New issue"
|
||||
|
||||
fill_in "Subject", :with => 'Hook test'
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
assert_no_difference("Issue.count") do
|
||||
click_button "Create"
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
assert_equal nil, Issue.last.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow setting a Deliverable on a locked Contract" do
|
||||
assert @contract2.lock!
|
||||
click_link "New issue"
|
||||
|
||||
fill_in "Subject", :with => 'Hook test'
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
assert_no_difference("Issue.count") do
|
||||
click_button "Create"
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
assert_equal nil, Issue.last.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow setting a Deliverable on a closed Contract" do
|
||||
assert @contract2.close!
|
||||
click_link "New issue"
|
||||
|
||||
fill_in "Subject", :with => 'Hook test'
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
assert_no_difference("Issue.count") do
|
||||
click_button "Create"
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
assert_equal nil, Issue.last.deliverable
|
||||
|
||||
end
|
||||
|
||||
context "with no permission to Assign Deliverable" do
|
||||
should "not allow setting the Deliverable (force HTTP request)" do
|
||||
@role.permissions.delete(:assign_deliverable_to_issue)
|
||||
@@ -69,6 +133,75 @@ class RedmineContracts::Hooks::ControllerIssuesEditBeforeSaveTest < ActionContro
|
||||
|
||||
end
|
||||
|
||||
should "not allow updating to a locked deliverable" do
|
||||
assert @deliverable2.lock!
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
|
||||
@issue.reload
|
||||
assert_equal nil, @issue.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow updating to a closed deliverable" do
|
||||
assert @deliverable2.close!
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
|
||||
@issue.reload
|
||||
assert_equal nil, @issue.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow updating to a deliverable on a locked contract" do
|
||||
assert @contract2.lock!
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
|
||||
@issue.reload
|
||||
assert_equal nil, @issue.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "not allow updating to a deliverable on a closed contract" do
|
||||
assert @contract2.close!
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
|
||||
@issue.reload
|
||||
assert_equal nil, @issue.deliverable
|
||||
|
||||
end
|
||||
|
||||
should "allow updating an issue, even if the deliverable is locked as long as the deliverable isn't changed" do
|
||||
select @deliverable2.title, :from => "Deliverable"
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
|
||||
@issue.reload
|
||||
assert_equal @deliverable2, @issue.deliverable
|
||||
|
||||
# Now normal update after locking
|
||||
assert @deliverable2.lock!
|
||||
fill_in "Subject", :with => 'Change subject'
|
||||
click_button "Submit"
|
||||
|
||||
assert_response :success
|
||||
@issue.reload
|
||||
assert_equal "Change subject", @issue.subject
|
||||
assert_equal @deliverable2, @issue.deliverable
|
||||
|
||||
end
|
||||
|
||||
context "with no permission to Assign Deliverable" do
|
||||
should "not allow setting the Deliverable (force HTTP request)" do
|
||||
@role.permissions.delete(:assign_deliverable_to_issue)
|
||||
|
||||
+3
-3
@@ -19,15 +19,15 @@ class RedmineContracts::Hooks::HelperIssuesShowDetailAfterSettingHookTest < Acti
|
||||
# Set first
|
||||
@issue.init_journal(@manager)
|
||||
@issue.deliverable = @deliverable1
|
||||
@issue.save!
|
||||
@issue.save! && @issue.reload
|
||||
# Change
|
||||
@issue.init_journal(@manager)
|
||||
@issue.deliverable = @deliverable2
|
||||
@issue.save!
|
||||
@issue.save! && @issue.reload
|
||||
# Unset
|
||||
@issue.init_journal(@manager)
|
||||
@issue.deliverable = nil
|
||||
@issue.save!
|
||||
@issue.save! && @issue.reload
|
||||
|
||||
login_as('manager', 'existing')
|
||||
|
||||
|
||||
+40
@@ -11,14 +11,25 @@ class RedmineContracts::Hooks::ViewIssuesBulkEditDetailsBottomHookTest < ActionC
|
||||
@issue3 = Issue.generate_for_project!(@project)
|
||||
@contract1 = Contract.generate!(:project => @project)
|
||||
@contract2 = Contract.generate!(:project => @project)
|
||||
@locked_contract = Contract.generate!(:project => @project)
|
||||
@closed_contract = Contract.generate!(:project => @project)
|
||||
|
||||
@manager = User.generate!(:login => 'manager', :password => 'existing', :password_confirmation => 'existing')
|
||||
@role = Role.generate!(:permissions => [:view_issues, :edit_issues])
|
||||
User.add_to_project(@manager, @project, @role)
|
||||
@deliverable1 = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'The Title')
|
||||
@deliverable2 = FixedDeliverable.generate!(:contract => @contract2, :manager => @manager, :title => 'The Title')
|
||||
@locked_deliverable = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'Locked Deliverable', :status => 'locked')
|
||||
@closed_deliverable = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'Closed Deliverable', :status => 'closed')
|
||||
@deliverable1_on_locked_contract = FixedDeliverable.generate!(:contract => @locked_contract, :manager => @manager, :title => 'Deliverable 1 on locked contract')
|
||||
@deliverable2_on_locked_contract = FixedDeliverable.generate!(:contract => @locked_contract, :manager => @manager, :title => 'Deliverable 2 on locked contract')
|
||||
@deliverable_on_closed_contract = FixedDeliverable.generate!(:contract => @closed_contract, :manager => @manager, :title => 'Deliverable on closed contract')
|
||||
@issue.deliverable = @deliverable1
|
||||
|
||||
# Set contract statuses now that all deliverables are created
|
||||
assert @locked_contract.lock!
|
||||
assert @closed_contract.close!
|
||||
|
||||
login_as('manager', 'existing')
|
||||
end
|
||||
|
||||
@@ -42,6 +53,35 @@ class RedmineContracts::Hooks::ViewIssuesBulkEditDetailsBottomHookTest < ActionC
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "disable all locked deliverables" do
|
||||
assert_select "select#deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@locked_deliverable.title}/
|
||||
end
|
||||
end
|
||||
|
||||
should "disable all deliverables on locked contracts" do
|
||||
assert_select "select#deliverable_id" do
|
||||
assert_select "optgroup[label=?]", @locked_contract.name do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1_on_locked_contract.title}/
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable2_on_locked_contract.title}/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "not show closed deliverables" do
|
||||
assert_select "select#deliverable_id" do
|
||||
assert_select "option", :text => /#{@closed_deliverable.title}/, :count => 0
|
||||
end
|
||||
end
|
||||
|
||||
should "not show deliverables on closed contracts" do
|
||||
assert_select "select#deliverable_id" do
|
||||
assert_select "optgroup[label=?]", @closed_contract.name, :count => 0
|
||||
assert_select "option", :text => /#{@deliverable_on_closed_contract.title}/, :count => 0
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "with no permission to Assign Deliverable" do
|
||||
|
||||
+86
-2
@@ -9,13 +9,25 @@ class RedmineContracts::Hooks::ViewIssuesFormDetailsBottomTest < ActionControlle
|
||||
@issue = Issue.generate_for_project!(@project)
|
||||
@contract1 = Contract.generate!(:project => @project)
|
||||
@contract2 = Contract.generate!(:project => @project)
|
||||
@locked_contract = Contract.generate!(:project => @project)
|
||||
@closed_contract = Contract.generate!(:project => @project)
|
||||
|
||||
@manager = User.generate!(:login => 'manager', :password => 'existing', :password_confirmation => 'existing')
|
||||
@role = Role.generate!(:permissions => [:view_issues, :edit_issues])
|
||||
User.add_to_project(@manager, @project, @role)
|
||||
@deliverable1 = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'The Title')
|
||||
@deliverable2 = FixedDeliverable.generate!(:contract => @contract2, :manager => @manager, :title => 'The Title')
|
||||
@deliverable1 = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'Deliverable1')
|
||||
@deliverable2 = FixedDeliverable.generate!(:contract => @contract2, :manager => @manager, :title => 'Deliverable2')
|
||||
@locked_deliverable = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'Locked Deliverable', :status => 'locked')
|
||||
@closed_deliverable = FixedDeliverable.generate!(:contract => @contract1, :manager => @manager, :title => 'Closed Deliverable', :status => 'closed')
|
||||
@deliverable1_on_locked_contract = FixedDeliverable.generate!(:contract => @locked_contract, :manager => @manager, :title => 'Deliverable 1 on locked contract')
|
||||
@deliverable2_on_locked_contract = FixedDeliverable.generate!(:contract => @locked_contract, :manager => @manager, :title => 'Deliverable 2 on locked contract')
|
||||
@deliverable_on_closed_contract = FixedDeliverable.generate!(:contract => @closed_contract, :manager => @manager, :title => 'Deliverable on closed contract')
|
||||
@issue.deliverable = @deliverable1
|
||||
assert @issue.save
|
||||
|
||||
# Set contract statuses now that all deliverables are created
|
||||
assert @locked_contract.lock!
|
||||
assert @closed_contract.close!
|
||||
|
||||
login_as('manager', 'existing')
|
||||
end
|
||||
@@ -39,6 +51,78 @@ class RedmineContracts::Hooks::ViewIssuesFormDetailsBottomTest < ActionControlle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "disable all locked deliverables" do
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@locked_deliverable.title}/
|
||||
end
|
||||
end
|
||||
|
||||
should "disable all deliverables on locked contracts" do
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "optgroup[label=?]", @locked_contract.name do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1_on_locked_contract.title}/
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable2_on_locked_contract.title}/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "not show closed deliverables" do
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option", :text => /#{@closed_deliverable.title}/, :count => 0
|
||||
end
|
||||
end
|
||||
|
||||
should "not show deliverables on closed contracts" do
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "optgroup[label=?]", @closed_contract.name, :count => 0
|
||||
assert_select "option", :text => /#{@deliverable_on_closed_contract.title}/, :count => 0
|
||||
end
|
||||
end
|
||||
|
||||
should "show the assigned deliverable as an option, even if it's locked" do
|
||||
@deliverable1.lock!
|
||||
visit_issue_page(@issue)
|
||||
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1.title}/, :count => 0 # Not disabled
|
||||
assert_select "option", :text => /#{@deliverable1.title}/, :count => 1 # Present
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show the assigned deliverable as an option, even if it's closed" do
|
||||
@deliverable1.close!
|
||||
visit_issue_page(@issue)
|
||||
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1.title}/, :count => 0 # Not disabled
|
||||
assert_select "option", :text => /#{@deliverable1.title}/, :count => 1 # Present
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show the assigned deliverable as an option, even if it's contract is locked" do
|
||||
@contract1.lock!
|
||||
visit_issue_page(@issue)
|
||||
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1.title}/, :count => 0 # Not disabled
|
||||
assert_select "option", :text => /#{@deliverable1.title}/, :count => 1 # Present
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show the assigned deliverable as an option, even if it's contract is closed" do
|
||||
@contract1.close!
|
||||
visit_issue_page(@issue)
|
||||
|
||||
assert_select "select#issue_deliverable_id" do
|
||||
assert_select "option[disabled=disabled]", :text => /#{@deliverable1.title}/, :count => 0 # Not disabled
|
||||
assert_select "option", :text => /#{@deliverable1.title}/, :count => 1 # Present
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
context "with no permission to Assign Deliverable" do
|
||||
|
||||
@@ -17,6 +17,9 @@ class ContractTest < ActiveSupport::TestCase
|
||||
should_allow_values_for :discount_type, "$", "%", nil, ''
|
||||
should_not_allow_values_for :discount_type, ["amount", "percent", "bar"]
|
||||
|
||||
should_allow_values_for :status, "", nil, 'open', 'locked', 'closed'
|
||||
should_not_allow_values_for :status, "other", "things", "1"
|
||||
|
||||
context "end_date" do
|
||||
should "be after start_date" do
|
||||
@contract = Contract.new(:start_date => Date.today, :end_date => Date.yesterday)
|
||||
@@ -32,6 +35,12 @@ class ContractTest < ActiveSupport::TestCase
|
||||
assert_equal false, @contract.executed
|
||||
end
|
||||
|
||||
should "default status to open" do
|
||||
@contract = Contract.new
|
||||
|
||||
assert_equal "open", @contract.status
|
||||
end
|
||||
|
||||
context "#labor_budget" do
|
||||
should "sum all of the labor budgets of the Deliverables" do
|
||||
contract = Contract.generate!
|
||||
|
||||
@@ -12,6 +12,13 @@ class DeliverableTest < ActiveSupport::TestCase
|
||||
should_validate_presence_of :type
|
||||
should_validate_presence_of :manager
|
||||
|
||||
should_allow_values_for :status, "", nil, 'open', 'locked', 'closed'
|
||||
should_not_allow_values_for :status, "other", "things", "1"
|
||||
|
||||
should "default status to open" do
|
||||
assert_equal "open", Deliverable.new.status
|
||||
end
|
||||
|
||||
context "#total=" do
|
||||
should "strip dollar signs when writing" do
|
||||
d = Deliverable.new
|
||||
@@ -35,4 +42,45 @@ class DeliverableTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
context "with a locked contract" do
|
||||
should "block creating a new deliverable" do
|
||||
contract = Contract.generate!(:status => "locked")
|
||||
deliverable = FixedDeliverable.spawn(:contract => contract)
|
||||
|
||||
assert !deliverable.valid?
|
||||
assert deliverable.errors.on_base.include?("Can't create a deliverable on a locked contract")
|
||||
end
|
||||
|
||||
should "block deleting a deliverable" do
|
||||
contract = Contract.generate!
|
||||
deliverable = FixedDeliverable.generate!(:contract => contract).reload
|
||||
assert contract.lock!
|
||||
|
||||
assert_no_difference("Deliverable.count") do
|
||||
deliverable.destroy
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
context "with a closed contract" do
|
||||
should "block creating a new deliverable" do
|
||||
contract = Contract.generate!(:status => "closed")
|
||||
deliverable = FixedDeliverable.spawn(:contract => contract)
|
||||
|
||||
assert !deliverable.valid?
|
||||
assert deliverable.errors.on_base.include?("Can't create a deliverable on a closed contract")
|
||||
end
|
||||
|
||||
should "block deleting a deliverable" do
|
||||
contract = Contract.generate!
|
||||
deliverable = FixedDeliverable.generate!(:contract => contract).reload
|
||||
assert contract.close!
|
||||
|
||||
assert_no_difference("Deliverable.count") do
|
||||
deliverable.destroy
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,6 +19,9 @@ class RedmineContracts::Hooks::ViewIssuesShowDetailsBottomTest < ActionControlle
|
||||
@controller ||= ApplicationController.new
|
||||
@controller.class.send(:include, ::Redmine::I18n)
|
||||
@controller.response ||= ActionController::TestResponse.new
|
||||
def @controller.api_request?
|
||||
false
|
||||
end
|
||||
# Hack to support render_on
|
||||
@controller.instance_variable_set('@template', template)
|
||||
@controller.response = response
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
require File.dirname(__FILE__) + '/../../../../test_helper'
|
||||
|
||||
class RedmineContracts::Patches::TimeEntryTest < ActionController::TestCase
|
||||
|
||||
def setup
|
||||
@project = Project.generate!
|
||||
@contract = Contract.generate!(:project => @project, :status => 'open')
|
||||
@deliverable = FixedDeliverable.generate!(:contract => @contract, :status => 'open').reload
|
||||
@issue = Issue.generate_for_project!(@project, :deliverable => @deliverable).reload
|
||||
assert_equal @deliverable, @issue.deliverable
|
||||
@user = User.generate!
|
||||
@role = Role.generate!
|
||||
User.add_to_project(@user, @project, @role)
|
||||
@activity = TimeEntryActivity.generate!
|
||||
end
|
||||
|
||||
def create_time_entry
|
||||
@issue.reload
|
||||
@time_entry = TimeEntry.create(:issue => @issue,
|
||||
:project => @project,
|
||||
:spent_on => Date.today,
|
||||
:activity => @activity,
|
||||
:hours => 10,
|
||||
:user => @user)
|
||||
end
|
||||
|
||||
def assert_error_about_locked_deliverable(time_entry)
|
||||
assert_equal "Can't create a time entry on a locked deliverable", time_entry.errors.on_base
|
||||
end
|
||||
|
||||
def assert_error_about_locked_contract(time_entry)
|
||||
assert_equal "Can't create a time entry on a locked contract", time_entry.errors.on_base
|
||||
end
|
||||
|
||||
def assert_error_about_closed_deliverable(time_entry)
|
||||
assert_equal "Can't create a time entry on a closed deliverable", time_entry.errors.on_base
|
||||
end
|
||||
|
||||
def assert_error_about_closed_contract(time_entry)
|
||||
assert_equal "Can't create a time entry on a closed contract", time_entry.errors.on_base
|
||||
end
|
||||
|
||||
should "allow logging time to an issue on an open deliverable, open contract" do
|
||||
assert_difference("TimeEntry.count") { create_time_entry }
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a locked deliverable, open contract" do
|
||||
assert @deliverable.lock!
|
||||
assert @deliverable.locked?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert_error_about_locked_deliverable(@time_entry)
|
||||
end
|
||||
|
||||
should "block logging time to an issue on an open deliverable, locked contract" do
|
||||
assert @contract.lock!
|
||||
assert @contract.locked?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert_error_about_locked_contract(@time_entry)
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a locked deliverable, locked contract" do
|
||||
assert @deliverable.lock!
|
||||
assert @deliverable.locked?
|
||||
assert @contract.lock!
|
||||
assert @contract.locked?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a locked deliverable")
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a locked contract")
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a closed deliverable, open contract" do
|
||||
assert @deliverable.close!
|
||||
assert @deliverable.closed?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert_error_about_closed_deliverable(@time_entry)
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a closed deliverable, locked contract" do
|
||||
assert @deliverable.close!
|
||||
assert @deliverable.closed?
|
||||
assert @contract.lock!
|
||||
assert @contract.locked?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a closed deliverable")
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a locked contract")
|
||||
end
|
||||
|
||||
should "block logging time to an issue on an open deliverable, closed contract" do
|
||||
assert @contract.close!
|
||||
assert @contract.closed?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert_error_about_closed_contract(@time_entry)
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a locked deliverable, closed contract" do
|
||||
assert @deliverable.lock!
|
||||
assert @deliverable.locked?
|
||||
assert @contract.close!
|
||||
assert @contract.closed?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a locked deliverable")
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a closed contract")
|
||||
end
|
||||
|
||||
should "block logging time to an issue on a closed deliverable, closed contract" do
|
||||
assert @deliverable.close!
|
||||
assert @deliverable.closed?
|
||||
assert @contract.close!
|
||||
assert @contract.closed?
|
||||
|
||||
assert_no_difference("TimeEntry.count") { create_time_entry }
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a closed deliverable")
|
||||
assert @time_entry.errors.on_base.include?("Can't create a time entry on a closed contract")
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user