##// END OF EJS Templates
Fixed: custom field displayed as deleted in the issue history even if no value was set....
Jean-Philippe Lang -
r695:1b2e80545a20
parent child
Show More
@@ -1,166 +1,168
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Issue < ActiveRecord::Base
19 19 belongs_to :project
20 20 belongs_to :tracker
21 21 belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
22 22 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 23 belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
24 24 belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
25 25 belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
26 26 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
27 27
28 28 has_many :journals, :as => :journalized, :dependent => :destroy
29 29 has_many :attachments, :as => :container, :dependent => :destroy
30 30 has_many :time_entries, :dependent => :nullify
31 31 has_many :custom_values, :dependent => :delete_all, :as => :customized
32 32 has_many :custom_fields, :through => :custom_values
33 33 has_and_belongs_to_many :changesets, :order => "revision ASC"
34 34
35 35 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
36 36 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
37 37
38 38 acts_as_watchable
39 39 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
40 40 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
41 41
42 42 validates_presence_of :subject, :description, :priority, :tracker, :author, :status
43 43 validates_length_of :subject, :maximum => 255
44 44 validates_inclusion_of :done_ratio, :in => 0..100
45 45 validates_associated :custom_values, :on => :update
46 46
47 47 # set default status for new issues
48 48 def before_validation
49 49 self.status = IssueStatus.default if status.nil?
50 50 end
51 51
52 52 def validate
53 53 if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
54 54 errors.add :due_date, :activerecord_error_not_a_date
55 55 end
56 56
57 57 if self.due_date and self.start_date and self.due_date < self.start_date
58 58 errors.add :due_date, :activerecord_error_greater_than_start_date
59 59 end
60 60
61 61 if start_date && soonest_start && start_date < soonest_start
62 62 errors.add :start_date, :activerecord_error_invalid
63 63 end
64 64
65 65 # validate assignment
66 66 if assigned_to && !assignable_users.include?(assigned_to)
67 67 errors.add :assigned_to_id, :activerecord_error_invalid
68 68 end
69 69 end
70 70
71 71 def before_create
72 72 # default assignment based on category
73 73 if assigned_to.nil? && category && category.assigned_to
74 74 self.assigned_to = category.assigned_to
75 75 end
76 76 end
77 77
78 78 def before_save
79 79 if @current_journal
80 80 # attributes changes
81 81 (Issue.column_names - %w(id description)).each {|c|
82 82 @current_journal.details << JournalDetail.new(:property => 'attr',
83 83 :prop_key => c,
84 84 :old_value => @issue_before_change.send(c),
85 85 :value => send(c)) unless send(c)==@issue_before_change.send(c)
86 86 }
87 87 # custom fields changes
88 88 custom_values.each {|c|
89 next if (@custom_values_before_change[c.custom_field_id]==c.value ||
90 (@custom_values_before_change[c.custom_field_id].blank? && c.value.blank?))
89 91 @current_journal.details << JournalDetail.new(:property => 'cf',
90 92 :prop_key => c.custom_field_id,
91 93 :old_value => @custom_values_before_change[c.custom_field_id],
92 :value => c.value) unless @custom_values_before_change[c.custom_field_id]==c.value
94 :value => c.value)
93 95 }
94 96 @current_journal.save unless @current_journal.details.empty? and @current_journal.notes.empty?
95 97 end
96 98 end
97 99
98 100 def after_save
99 101 # Update start/due dates of following issues
100 102 relations_from.each(&:set_issue_to_dates)
101 103
102 104 # Close duplicates if the issue was closed
103 105 if @issue_before_change && !@issue_before_change.closed? && self.closed?
104 106 duplicates.each do |duplicate|
105 107 # Don't re-close it if it's already closed
106 108 next if duplicate.closed?
107 109 # Same user and notes
108 110 duplicate.init_journal(@current_journal.user, @current_journal.notes)
109 111 duplicate.update_attribute :status, self.status
110 112 end
111 113 end
112 114 end
113 115
114 116 def custom_value_for(custom_field)
115 117 self.custom_values.each {|v| return v if v.custom_field_id == custom_field.id }
116 118 return nil
117 119 end
118 120
119 121 def init_journal(user, notes = "")
120 122 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
121 123 @issue_before_change = self.clone
122 124 @custom_values_before_change = {}
123 125 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
124 126 @current_journal
125 127 end
126 128
127 129 # Return true if the issue is closed, otherwise false
128 130 def closed?
129 131 self.status.is_closed?
130 132 end
131 133
132 134 # Users the issue can be assigned to
133 135 def assignable_users
134 136 project.members.select {|m| m.role.assignable?}.collect {|m| m.user}
135 137 end
136 138
137 139 def spent_hours
138 140 @spent_hours ||= time_entries.sum(:hours) || 0
139 141 end
140 142
141 143 def relations
142 144 (relations_from + relations_to).sort
143 145 end
144 146
145 147 def all_dependent_issues
146 148 dependencies = []
147 149 relations_from.each do |relation|
148 150 dependencies << relation.issue_to
149 151 dependencies += relation.issue_to.all_dependent_issues
150 152 end
151 153 dependencies
152 154 end
153 155
154 156 # Returns an array of the duplicate issues
155 157 def duplicates
156 158 relations.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.other_issue(self)}
157 159 end
158 160
159 161 def duration
160 162 (start_date && due_date) ? due_date - start_date : 0
161 163 end
162 164
163 165 def soonest_start
164 166 @soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
165 167 end
166 168 end
General Comments 0
You need to be logged in to leave comments. Login now