Merge branch '6441-contract-and-deliverable-status'

This commit is contained in:
Eric Davis
2011-08-10 16:12:11 -07:00
29 changed files with 1258 additions and 53 deletions
+44 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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!
+7
View File
@@ -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') %>
+67 -7
View File
@@ -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>
+3 -6
View File
@@ -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') %>
+1 -1
View File
@@ -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) %>
+12
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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."
+10
View File
@@ -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
+6
View File
@@ -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
+127
View File
@@ -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
+23 -5
View File
@@ -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
+2
View File
@@ -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
+241 -1
View File
@@ -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
+8 -2
View File
@@ -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)
@@ -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')
@@ -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
@@ -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
+9
View File
@@ -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!
+48
View File
@@ -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