diff --git a/app/models/contract.rb b/app/models/contract.rb index 87e5de6..e734b7a 100644 --- a/app/models/contract.rb +++ b/app/models/contract.rb @@ -48,6 +48,22 @@ class Contract < ActiveRecord::Base read_attribute(:status) || "open" end + def lock! + update_attribute(:status, "locked") + end + + def close! + update_attribute(:status, "closed") + end + + def locked? + self.status == "locked" + end + + def closed? + self.status == "closed" + end + # ------------------------------------------------------------ # Labor Methods # ------------------------------------------------------------ diff --git a/app/models/deliverable.rb b/app/models/deliverable.rb index fa93bf9..e52aeb6 100644 --- a/app/models/deliverable.rb +++ b/app/models/deliverable.rb @@ -55,6 +55,22 @@ class Deliverable < ActiveRecord::Base nil end + def lock! + update_attribute(:status, "locked") + end + + def close! + update_attribute(:status, "closed") + end + + def locked? + self.status == "locked" + end + + def closed? + self.status == "closed" + end + def to_s title end diff --git a/config/locales/en.yml b/config/locales/en.yml index 661837d..0001f08 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,10 @@ en: + activerecord: + errors: + messages: + cant_to_closed_deliverable: "Can't create a time entry on a closed deliverable" + cant_to_closed_contract: "Can't create a time entry on a closed contract" + field_end_date: End Date field_executed: Executed text_contracts: Contracts diff --git a/init.rb b/init.rb index 19e673c..eb3c616 100644 --- a/init.rb +++ b/init.rb @@ -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' diff --git a/lib/redmine_contracts/patches/time_entry_patch.rb b/lib/redmine_contracts/patches/time_entry_patch.rb new file mode 100644 index 0000000..58f9d69 --- /dev/null +++ b/lib/redmine_contracts/patches/time_entry_patch.rb @@ -0,0 +1,36 @@ +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(:cant_to_closed_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(:cant_to_closed_contract) if issue.deliverable.contract.closed? + end + end + + end + end + + module ClassMethods + end + + module InstanceMethods + end + end + end +end diff --git a/test/unit/lib/redmine_contracts/patches/time_entry_patch_test.rb b/test/unit/lib/redmine_contracts/patches/time_entry_patch_test.rb new file mode 100644 index 0000000..d844c15 --- /dev/null +++ b/test/unit/lib/redmine_contracts/patches/time_entry_patch_test.rb @@ -0,0 +1,109 @@ +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_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 "allow logging time to an issue on a locked deliverable, open contract" do + assert @deliverable.lock! + assert @deliverable.locked? + + assert_difference("TimeEntry.count") { create_time_entry } + end + + should "allow logging time to an issue on an open deliverable, locked contract" do + assert @contract.lock! + assert @contract.locked? + + assert_difference("TimeEntry.count") { create_time_entry } + end + + should "allow 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_difference("TimeEntry.count") { create_time_entry } + 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_error_about_closed_deliverable(@time_entry) + 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_error_about_closed_contract(@time_entry) + 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