@@ -0,0 +1,37 | |||||
|
1 | <% form_tag({:controller => 'projects', :action => 'save_activities', :id => @project}, :class => "tabular") do %> | |||
|
2 | ||||
|
3 | <table class="list"> | |||
|
4 | <tr> | |||
|
5 | <th><%= l(:field_name) %></th> | |||
|
6 | <th><%= l(:enumeration_system_activity) %></th> | |||
|
7 | <% TimeEntryActivity.new.available_custom_fields.each do |value| %> | |||
|
8 | <th><%= h value.name %></th> | |||
|
9 | <% end %> | |||
|
10 | <th style="width:15%;"><%= l(:field_active) %></th> | |||
|
11 | </tr> | |||
|
12 | ||||
|
13 | <% @project.activities(true).each do |enumeration| %> | |||
|
14 | <% fields_for "enumerations[#{enumeration.id}]", enumeration do |ff| %> | |||
|
15 | <tr class="<%= cycle('odd', 'even') %>"> | |||
|
16 | <td> | |||
|
17 | <%= ff.hidden_field :parent_id, :value => enumeration.id unless enumeration.project %> | |||
|
18 | <%= h(enumeration) %> | |||
|
19 | </td> | |||
|
20 | <td align="center" style="width:15%;"><%= image_tag('true.png') unless enumeration.project %></td> | |||
|
21 | <% enumeration.custom_field_values.each do |value| %> | |||
|
22 | <td align="center"> | |||
|
23 | <%= custom_field_tag "enumerations[#{enumeration.id}]", value %> | |||
|
24 | </td> | |||
|
25 | <% end %> | |||
|
26 | <td align="center" style="width:15%;"> | |||
|
27 | <%= ff.check_box :active %> | |||
|
28 | </td> | |||
|
29 | </tr> | |||
|
30 | <% end %> | |||
|
31 | <% end %> | |||
|
32 | </table> | |||
|
33 | ||||
|
34 | <%= submit_tag l(:button_save) %> | |||
|
35 | <% end %> | |||
|
36 | <%= button_to l(:button_reset), {:controller => 'projects', :action => 'reset_activities', :id => @project}, :method => :delete, :confirm => l(:text_are_you_sure), :style => "position: relative; top: -20px; left: 60px;" %> | |||
|
37 |
@@ -231,6 +231,24 class ProjectsController < ApplicationController | |||||
231 | end |
|
231 | end | |
232 | @versions = @project.versions.sort |
|
232 | @versions = @project.versions.sort | |
233 | end |
|
233 | end | |
|
234 | ||||
|
235 | def save_activities | |||
|
236 | if request.post? && params[:enumerations] | |||
|
237 | params[:enumerations].each do |id, activity| | |||
|
238 | @project.update_or_build_time_entry_activity(id, activity) | |||
|
239 | end | |||
|
240 | @project.save | |||
|
241 | end | |||
|
242 | ||||
|
243 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project | |||
|
244 | end | |||
|
245 | ||||
|
246 | def reset_activities | |||
|
247 | @project.time_entry_activities.each do |time_entry_activity| | |||
|
248 | time_entry_activity.destroy | |||
|
249 | end | |||
|
250 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project | |||
|
251 | end | |||
234 |
|
252 | |||
235 | def list_files |
|
253 | def list_files | |
236 | sort_init 'filename', 'asc' |
|
254 | sort_init 'filename', 'asc' |
@@ -29,7 +29,8 module ProjectsHelper | |||||
29 | {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural}, |
|
29 | {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural}, | |
30 | {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki}, |
|
30 | {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki}, | |
31 | {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository}, |
|
31 | {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository}, | |
32 | {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural} |
|
32 | {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural}, | |
|
33 | {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities} | |||
33 | ] |
|
34 | ] | |
34 | tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)} |
|
35 | tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)} | |
35 | end |
|
36 | end |
@@ -25,7 +25,7 class Enumeration < ActiveRecord::Base | |||||
25 | before_destroy :check_integrity |
|
25 | before_destroy :check_integrity | |
26 |
|
26 | |||
27 | validates_presence_of :name |
|
27 | validates_presence_of :name | |
28 | validates_uniqueness_of :name, :scope => [:type] |
|
28 | validates_uniqueness_of :name, :scope => [:type, :project_id] | |
29 | validates_length_of :name, :maximum => 30 |
|
29 | validates_length_of :name, :maximum => 30 | |
30 |
|
30 | |||
31 | # Backwards compatiblity named_scopes. |
|
31 | # Backwards compatiblity named_scopes. | |
@@ -58,7 +58,7 class Enumeration < ActiveRecord::Base | |||||
58 | end |
|
58 | end | |
59 | # End backwards compatiblity named_scopes |
|
59 | # End backwards compatiblity named_scopes | |
60 |
|
60 | |||
61 | named_scope :all, :order => 'position' |
|
61 | named_scope :all, :order => 'position', :conditions => { :project_id => nil } | |
62 |
|
62 | |||
63 | named_scope :active, lambda { |
|
63 | named_scope :active, lambda { | |
64 | { |
|
64 | { | |
@@ -134,6 +134,32 class Enumeration < ActiveRecord::Base | |||||
134 | def self.get_subclasses |
|
134 | def self.get_subclasses | |
135 | @@subclasses[Enumeration] |
|
135 | @@subclasses[Enumeration] | |
136 | end |
|
136 | end | |
|
137 | ||||
|
138 | # Does the +new+ Hash override the previous Enumeration? | |||
|
139 | def self.overridding_change?(new, previous) | |||
|
140 | if (same_active_state?(new['active'], previous.active)) && same_custom_values?(new,previous) | |||
|
141 | return false | |||
|
142 | else | |||
|
143 | return true | |||
|
144 | end | |||
|
145 | end | |||
|
146 | ||||
|
147 | # Does the +new+ Hash have the same custom values as the previous Enumeration? | |||
|
148 | def self.same_custom_values?(new, previous) | |||
|
149 | previous.custom_field_values.each do |custom_value| | |||
|
150 | if custom_value.value != new["custom_field_values"][custom_value.custom_field_id.to_s] | |||
|
151 | return false | |||
|
152 | end | |||
|
153 | end | |||
|
154 | ||||
|
155 | return true | |||
|
156 | end | |||
|
157 | ||||
|
158 | # Are the new and previous fields equal? | |||
|
159 | def self.same_active_state?(new, previous) | |||
|
160 | new = (new == "1" ? true : false) | |||
|
161 | return new == previous | |||
|
162 | end | |||
137 |
|
163 | |||
138 | private |
|
164 | private | |
139 | def check_integrity |
|
165 | def check_integrity |
@@ -20,7 +20,12 class Project < ActiveRecord::Base | |||||
20 | STATUS_ACTIVE = 1 |
|
20 | STATUS_ACTIVE = 1 | |
21 | STATUS_ARCHIVED = 9 |
|
21 | STATUS_ARCHIVED = 9 | |
22 |
|
22 | |||
23 | has_many :time_entry_activities, :conditions => {:active => true } # Specific overidden Activities |
|
23 | # Specific overidden Activities | |
|
24 | has_many :time_entry_activities do | |||
|
25 | def active | |||
|
26 | find(:all, :conditions => {:active => true}) | |||
|
27 | end | |||
|
28 | end | |||
24 | has_many :members, :include => :user, :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}" |
|
29 | has_many :members, :include => :user, :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}" | |
25 | has_many :member_principals, :class_name => 'Member', |
|
30 | has_many :member_principals, :class_name => 'Member', | |
26 | :include => :principal, |
|
31 | :include => :principal, | |
@@ -156,14 +161,37 class Project < ActiveRecord::Base | |||||
156 | statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))" |
|
161 | statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))" | |
157 | end |
|
162 | end | |
158 |
|
163 | |||
159 |
# Returns |
|
164 | # Returns the Systemwide and project specific activities | |
160 | def activities |
|
165 | def activities(include_inactive=false) | |
161 | overridden_activity_ids = self.time_entry_activities.collect(&:parent_id) |
|
166 | if include_inactive | |
|
167 | return all_activities | |||
|
168 | else | |||
|
169 | return active_activities | |||
|
170 | end | |||
|
171 | end | |||
162 |
|
172 | |||
163 | if overridden_activity_ids.empty? |
|
173 | # Will build a new Project specific Activity or update an existing one | |
164 | return TimeEntryActivity.active |
|
174 | def update_or_build_time_entry_activity(id, activity_hash) | |
|
175 | if activity_hash.respond_to?(:has_key?) && activity_hash.has_key?('parent_id') | |||
|
176 | self.build_time_entry_activity_if_needed(activity_hash) | |||
165 | else |
|
177 | else | |
166 | return system_activities_and_project_overrides |
|
178 | activity = project.time_entry_activities.find_by_id(id.to_i) | |
|
179 | activity.update_attributes(activity_hash) if activity | |||
|
180 | end | |||
|
181 | end | |||
|
182 | ||||
|
183 | # Builds new activity | |||
|
184 | def build_time_entry_activity_if_needed(activity) | |||
|
185 | # Only new override activities are built | |||
|
186 | if activity['parent_id'] | |||
|
187 | ||||
|
188 | parent_activity = TimeEntryActivity.find(activity['parent_id']) | |||
|
189 | activity['name'] = parent_activity.name | |||
|
190 | activity['position'] = parent_activity.position | |||
|
191 | ||||
|
192 | if Enumeration.overridding_change?(activity, parent_activity) | |||
|
193 | self.time_entry_activities.build(activity) | |||
|
194 | end | |||
167 | end |
|
195 | end | |
168 | end |
|
196 | end | |
169 |
|
197 | |||
@@ -459,11 +487,41 private | |||||
459 | @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten |
|
487 | @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten | |
460 | end |
|
488 | end | |
461 |
|
489 | |||
462 |
# Returns |
|
490 | # Returns all the active Systemwide and project specific activities | |
463 | def system_activities_and_project_overrides |
|
491 | def active_activities | |
464 | return TimeEntryActivity.active. |
|
492 | overridden_activity_ids = self.time_entry_activities.active.collect(&:parent_id) | |
465 | find(:all, |
|
493 | ||
466 | :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + |
|
494 | if overridden_activity_ids.empty? | |
467 |
|
|
495 | return TimeEntryActivity.active | |
|
496 | else | |||
|
497 | return system_activities_and_project_overrides | |||
|
498 | end | |||
|
499 | end | |||
|
500 | ||||
|
501 | # Returns all the Systemwide and project specific activities | |||
|
502 | # (inactive and active) | |||
|
503 | def all_activities | |||
|
504 | overridden_activity_ids = self.time_entry_activities.collect(&:parent_id) | |||
|
505 | ||||
|
506 | if overridden_activity_ids.empty? | |||
|
507 | return TimeEntryActivity.all | |||
|
508 | else | |||
|
509 | return system_activities_and_project_overrides(true) | |||
|
510 | end | |||
|
511 | end | |||
|
512 | ||||
|
513 | # Returns the systemwide active activities merged with the project specific overrides | |||
|
514 | def system_activities_and_project_overrides(include_inactive=false) | |||
|
515 | if include_inactive | |||
|
516 | return TimeEntryActivity.all. | |||
|
517 | find(:all, | |||
|
518 | :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + | |||
|
519 | self.time_entry_activities | |||
|
520 | else | |||
|
521 | return TimeEntryActivity.active. | |||
|
522 | find(:all, | |||
|
523 | :conditions => ["id NOT IN (?)", self.time_entry_activities.active.collect(&:parent_id)]) + | |||
|
524 | self.time_entry_activities.active | |||
|
525 | end | |||
468 | end |
|
526 | end | |
469 | end |
|
527 | end |
@@ -830,3 +830,4 en: | |||||
830 | enumeration_issue_priorities: Issue priorities |
|
830 | enumeration_issue_priorities: Issue priorities | |
831 | enumeration_doc_categories: Document categories |
|
831 | enumeration_doc_categories: Document categories | |
832 | enumeration_activities: Activities (time tracking) |
|
832 | enumeration_activities: Activities (time tracking) | |
|
833 | enumeration_system_activity: System Activity |
@@ -200,6 +200,11 ActionController::Routing::Routes.draw do |map| | |||||
200 | project_actions.connect 'projects/:id/files/new', :action => 'add_file' |
|
200 | project_actions.connect 'projects/:id/files/new', :action => 'add_file' | |
201 | project_actions.connect 'projects/:id/versions/new', :action => 'add_version' |
|
201 | project_actions.connect 'projects/:id/versions/new', :action => 'add_version' | |
202 | project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category' |
|
202 | project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category' | |
|
203 | project_actions.connect 'projects/:id/activities/save', :action => 'save_activities' | |||
|
204 | end | |||
|
205 | ||||
|
206 | projects.with_options :conditions => {:method => :delete} do |project_actions| | |||
|
207 | project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities' | |||
203 | end |
|
208 | end | |
204 | end |
|
209 | end | |
205 |
|
210 |
@@ -59,6 +59,7 Redmine::AccessControl.map do |map| | |||||
59 | map.permission :view_time_entries, :timelog => [:details, :report] |
|
59 | map.permission :view_time_entries, :timelog => [:details, :report] | |
60 | map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member |
|
60 | map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member | |
61 | map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin |
|
61 | map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin | |
|
62 | map.permission :manage_project_activities, {:projects => [:save_activities, :reset_activities]}, :require => :member | |||
62 | end |
|
63 | end | |
63 |
|
64 | |||
64 | map.project_module :news do |map| |
|
65 | map.project_module :news do |map| |
@@ -89,3 +89,9 custom_values_015: | |||||
89 | customized_id: 10 |
|
89 | customized_id: 10 | |
90 | id: 15 |
|
90 | id: 15 | |
91 | value: true |
|
91 | value: true | |
|
92 | custom_values_016: | |||
|
93 | customized_type: Enumeration | |||
|
94 | custom_field_id: 7 | |||
|
95 | customized_id: 11 | |||
|
96 | id: 16 | |||
|
97 | value: true |
@@ -46,6 +46,7 roles_001: | |||||
46 | - :browse_repository |
|
46 | - :browse_repository | |
47 | - :manage_repository |
|
47 | - :manage_repository | |
48 | - :view_changesets |
|
48 | - :view_changesets | |
|
49 | - :manage_project_activities | |||
49 |
|
50 | |||
50 | position: 1 |
|
51 | position: 1 | |
51 | roles_002: |
|
52 | roles_002: | |
@@ -174,4 +175,4 roles_005: | |||||
174 | - :view_changesets |
|
175 | - :view_changesets | |
175 |
|
176 | |||
176 | position: 5 |
|
177 | position: 5 | |
177 | No newline at end of file |
|
178 |
@@ -24,7 +24,7 class ProjectsController; def rescue_action(e) raise e end; end | |||||
24 | class ProjectsControllerTest < ActionController::TestCase |
|
24 | class ProjectsControllerTest < ActionController::TestCase | |
25 | fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, |
|
25 | fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, | |
26 | :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, |
|
26 | :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, | |
27 | :attachments |
|
27 | :attachments, :custom_fields, :custom_values | |
28 |
|
28 | |||
29 | def setup |
|
29 | def setup | |
30 | @controller = ProjectsController.new |
|
30 | @controller = ProjectsController.new | |
@@ -555,7 +555,134 class ProjectsControllerTest < ActionController::TestCase | |||||
555 | assert_response :success |
|
555 | assert_response :success | |
556 | assert_template 'show' |
|
556 | assert_template 'show' | |
557 | end |
|
557 | end | |
|
558 | ||||
|
559 | def test_reset_activities_routing | |||
|
560 | assert_routing({:method => :delete, :path => 'projects/64/reset_activities'}, | |||
|
561 | :controller => 'projects', :action => 'reset_activities', :id => '64') | |||
|
562 | end | |||
|
563 | ||||
|
564 | def test_reset_activities | |||
|
565 | @request.session[:user_id] = 2 # manager | |||
|
566 | project_activity = TimeEntryActivity.new({ | |||
|
567 | :name => 'Project Specific', | |||
|
568 | :parent => TimeEntryActivity.find(:first), | |||
|
569 | :project => Project.find(1), | |||
|
570 | :active => true | |||
|
571 | }) | |||
|
572 | assert project_activity.save | |||
|
573 | project_activity_two = TimeEntryActivity.new({ | |||
|
574 | :name => 'Project Specific Two', | |||
|
575 | :parent => TimeEntryActivity.find(:last), | |||
|
576 | :project => Project.find(1), | |||
|
577 | :active => true | |||
|
578 | }) | |||
|
579 | assert project_activity_two.save | |||
|
580 | ||||
|
581 | delete :reset_activities, :id => 1 | |||
|
582 | assert_response :redirect | |||
|
583 | assert_redirected_to 'projects/ecookbook/settings/activities' | |||
|
584 | ||||
|
585 | assert_nil TimeEntryActivity.find_by_id(project_activity.id) | |||
|
586 | assert_nil TimeEntryActivity.find_by_id(project_activity_two.id) | |||
|
587 | end | |||
558 |
|
588 | |||
|
589 | def test_save_activities_routing | |||
|
590 | assert_routing({:method => :post, :path => 'projects/64/activities/save'}, | |||
|
591 | :controller => 'projects', :action => 'save_activities', :id => '64') | |||
|
592 | end | |||
|
593 | ||||
|
594 | def test_save_activities_to_override_system_activities | |||
|
595 | @request.session[:user_id] = 2 # manager | |||
|
596 | billable_field = TimeEntryActivityCustomField.find_by_name("Billable") | |||
|
597 | ||||
|
598 | post :save_activities, :id => 1, :enumerations => { | |||
|
599 | "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate | |||
|
600 | "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value | |||
|
601 | "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value | |||
|
602 | "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes | |||
|
603 | } | |||
|
604 | ||||
|
605 | assert_response :redirect | |||
|
606 | assert_redirected_to 'projects/ecookbook/settings/activities' | |||
|
607 | ||||
|
608 | # Created project specific activities... | |||
|
609 | project = Project.find('ecookbook') | |||
|
610 | ||||
|
611 | # ... Design | |||
|
612 | design = project.time_entry_activities.find_by_name("Design") | |||
|
613 | assert design, "Project activity not found" | |||
|
614 | ||||
|
615 | assert_equal 9, design.parent_id # Relate to the system activity | |||
|
616 | assert_not_equal design.parent.id, design.id # Different records | |||
|
617 | assert_equal design.parent.name, design.name # Same name | |||
|
618 | assert !design.active? | |||
|
619 | ||||
|
620 | # ... Development | |||
|
621 | development = project.time_entry_activities.find_by_name("Development") | |||
|
622 | assert development, "Project activity not found" | |||
|
623 | ||||
|
624 | assert_equal 10, development.parent_id # Relate to the system activity | |||
|
625 | assert_not_equal development.parent.id, development.id # Different records | |||
|
626 | assert_equal development.parent.name, development.name # Same name | |||
|
627 | assert development.active? | |||
|
628 | assert_equal "0", development.custom_value_for(billable_field).value | |||
|
629 | ||||
|
630 | # ... Inactive Activity | |||
|
631 | previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity") | |||
|
632 | assert previously_inactive, "Project activity not found" | |||
|
633 | ||||
|
634 | assert_equal 14, previously_inactive.parent_id # Relate to the system activity | |||
|
635 | assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records | |||
|
636 | assert_equal previously_inactive.parent.name, previously_inactive.name # Same name | |||
|
637 | assert previously_inactive.active? | |||
|
638 | assert_equal "1", previously_inactive.custom_value_for(billable_field).value | |||
|
639 | ||||
|
640 | # ... QA | |||
|
641 | assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified" | |||
|
642 | end | |||
|
643 | ||||
|
644 | def test_save_activities_will_update_project_specific_activities | |||
|
645 | @request.session[:user_id] = 2 # manager | |||
|
646 | ||||
|
647 | project_activity = TimeEntryActivity.new({ | |||
|
648 | :name => 'Project Specific', | |||
|
649 | :parent => TimeEntryActivity.find(:first), | |||
|
650 | :project => Project.find(1), | |||
|
651 | :active => true | |||
|
652 | }) | |||
|
653 | assert project_activity.save | |||
|
654 | project_activity_two = TimeEntryActivity.new({ | |||
|
655 | :name => 'Project Specific Two', | |||
|
656 | :parent => TimeEntryActivity.find(:last), | |||
|
657 | :project => Project.find(1), | |||
|
658 | :active => true | |||
|
659 | }) | |||
|
660 | assert project_activity_two.save | |||
|
661 | ||||
|
662 | ||||
|
663 | post :save_activities, :id => 1, :enumerations => { | |||
|
664 | project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate | |||
|
665 | project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate | |||
|
666 | } | |||
|
667 | ||||
|
668 | assert_response :redirect | |||
|
669 | assert_redirected_to 'projects/ecookbook/settings/activities' | |||
|
670 | ||||
|
671 | # Created project specific activities... | |||
|
672 | project = Project.find('ecookbook') | |||
|
673 | assert_equal 2, project.time_entry_activities.count | |||
|
674 | ||||
|
675 | activity_one = project.time_entry_activities.find_by_name(project_activity.name) | |||
|
676 | assert activity_one, "Project activity not found" | |||
|
677 | assert_equal project_activity.id, activity_one.id | |||
|
678 | assert !activity_one.active? | |||
|
679 | ||||
|
680 | activity_two = project.time_entry_activities.find_by_name(project_activity_two.name) | |||
|
681 | assert activity_two, "Project activity not found" | |||
|
682 | assert_equal project_activity_two.id, activity_two.id | |||
|
683 | assert !activity_two.active? | |||
|
684 | end | |||
|
685 | ||||
559 | # A hook that is manually registered later |
|
686 | # A hook that is manually registered later | |
560 | class ProjectBasedTemplate < Redmine::Hook::ViewListener |
|
687 | class ProjectBasedTemplate < Redmine::Hook::ViewListener | |
561 | def view_layouts_base_html_head(context) |
|
688 | def view_layouts_base_html_head(context) |
@@ -344,10 +344,17 class ProjectTest < ActiveSupport::TestCase | |||||
344 | end |
|
344 | end | |
345 |
|
345 | |||
346 | def test_activities_should_handle_nils |
|
346 | def test_activities_should_handle_nils | |
|
347 | overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.find(:first)}) | |||
347 | TimeEntryActivity.delete_all |
|
348 | TimeEntryActivity.delete_all | |
348 |
|
349 | |||
|
350 | # No activities | |||
349 | project = Project.find(1) |
|
351 | project = Project.find(1) | |
350 | assert project.activities.empty? |
|
352 | assert project.activities.empty? | |
|
353 | ||||
|
354 | # No system, one overridden | |||
|
355 | assert overridden_activity.save! | |||
|
356 | project.reload | |||
|
357 | assert_equal [overridden_activity], project.activities | |||
351 | end |
|
358 | end | |
352 |
|
359 | |||
353 | def test_activities_should_override_system_activities_with_project_activities |
|
360 | def test_activities_should_override_system_activities_with_project_activities | |
@@ -360,6 +367,14 class ProjectTest < ActiveSupport::TestCase | |||||
360 | assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden" |
|
367 | assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden" | |
361 | end |
|
368 | end | |
362 |
|
369 | |||
|
370 | def test_activities_should_include_inactive_activities_if_specified | |||
|
371 | project = Project.find(1) | |||
|
372 | overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.find(:first), :active => false}) | |||
|
373 | assert overridden_activity.save! | |||
|
374 | ||||
|
375 | assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found" | |||
|
376 | end | |||
|
377 | ||||
363 | context "Project#copy" do |
|
378 | context "Project#copy" do | |
364 | setup do |
|
379 | setup do | |
365 | ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests |
|
380 | ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests |
General Comments 0
You need to be logged in to leave comments.
Login now