comparison app/models/version.rb @ 1338:25603efa57b5

Merge from live branch
author Chris Cannam
date Thu, 20 Jun 2013 13:14:14 +0100
parents 433d4f72a19b
children 622f24f53b42 261b3d9a4903
comparison
equal deleted inserted replaced
1209:1b1138f6f55e 1338:25603efa57b5
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
31 validates_uniqueness_of :name, :scope => [:project_id] 31 validates_uniqueness_of :name, :scope => [:project_id]
32 validates_length_of :name, :maximum => 60 32 validates_length_of :name, :maximum => 60
33 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true 33 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
34 validates_inclusion_of :status, :in => VERSION_STATUSES 34 validates_inclusion_of :status, :in => VERSION_STATUSES
35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS 35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
36 36 validate :validate_version
37 named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}} 37
38 named_scope :open, :conditions => {:status => 'open'} 38 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
39 named_scope :visible, lambda {|*args| { :include => :project, 39 scope :open, where(:status => 'open')
40 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } } 40 scope :visible, lambda {|*args|
41 includes(:project).where(Project.allowed_to_condition(args.first || User.current, :view_issues))
42 }
41 43
42 safe_attributes 'name', 44 safe_attributes 'name',
43 'description', 45 'description',
44 'effective_date', 46 'effective_date',
45 'due_date', 47 'due_date',
76 @estimated_hours ||= fixed_issues.leaves.sum(:estimated_hours).to_f 78 @estimated_hours ||= fixed_issues.leaves.sum(:estimated_hours).to_f
77 end 79 end
78 80
79 # Returns the total reported time for this version 81 # Returns the total reported time for this version
80 def spent_hours 82 def spent_hours
81 @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f 83 @spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f
82 end 84 end
83 85
84 def closed? 86 def closed?
85 status == 'closed' 87 status == 'closed'
86 end 88 end
89 status == 'open' 91 status == 'open'
90 end 92 end
91 93
92 # Returns true if the version is completed: due date reached and no open issues 94 # Returns true if the version is completed: due date reached and no open issues
93 def completed? 95 def completed?
94 effective_date && (effective_date <= Date.today) && (open_issues_count == 0) 96 effective_date && (effective_date < Date.today) && (open_issues_count == 0)
95 end 97 end
96 98
97 def behind_schedule? 99 def behind_schedule?
98 if completed_pourcent == 100 100 if completed_pourcent == 100
99 return false 101 return false
131 effective_date && (effective_date < Date.today) && (open_issues_count > 0) 133 effective_date && (effective_date < Date.today) && (open_issues_count > 0)
132 end 134 end
133 135
134 # Returns assigned issues count 136 # Returns assigned issues count
135 def issues_count 137 def issues_count
136 @issue_count ||= fixed_issues.count 138 load_issue_counts
139 @issue_count
137 end 140 end
138 141
139 # Returns the total amount of open issues for this version. 142 # Returns the total amount of open issues for this version.
140 def open_issues_count 143 def open_issues_count
141 @open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status) 144 load_issue_counts
145 @open_issues_count
142 end 146 end
143 147
144 # Returns the total amount of closed issues for this version. 148 # Returns the total amount of closed issues for this version.
145 def closed_issues_count 149 def closed_issues_count
146 @closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status) 150 load_issue_counts
151 @closed_issues_count
147 end 152 end
148 153
149 def wiki_page 154 def wiki_page
150 if project.wiki && !wiki_page_title.blank? 155 if project.wiki && !wiki_page_title.blank?
151 @wiki_page ||= project.wiki.find_page(wiki_page_title) 156 @wiki_page ||= project.wiki.find_page(wiki_page_title)
157 162
158 def to_s_with_project 163 def to_s_with_project
159 "#{project} - #{name}" 164 "#{project} - #{name}"
160 end 165 end
161 166
162 # Versions are sorted by effective_date and "Project Name - Version name" 167 # Versions are sorted by effective_date and name
163 # Those with no effective_date are at the end, sorted by "Project Name - Version name" 168 # Those with no effective_date are at the end, sorted by name
164 def <=>(version) 169 def <=>(version)
165 if self.effective_date 170 if self.effective_date
166 if version.effective_date 171 if version.effective_date
167 if self.effective_date == version.effective_date 172 if self.effective_date == version.effective_date
168 "#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}" 173 name == version.name ? id <=> version.id : name <=> version.name
169 else 174 else
170 self.effective_date <=> version.effective_date 175 self.effective_date <=> version.effective_date
171 end 176 end
172 else 177 else
173 -1 178 -1
174 end 179 end
175 else 180 else
176 if version.effective_date 181 if version.effective_date
177 1 182 1
178 else 183 else
179 "#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}" 184 name == version.name ? id <=> version.id : name <=> version.name
180 end 185 end
181 end 186 end
182 end 187 end
188
189 def self.fields_for_order_statement(table=nil)
190 table ||= table_name
191 ["(CASE WHEN #{table}.effective_date IS NULL THEN 1 ELSE 0 END)", "#{table}.effective_date", "#{table}.name", "#{table}.id"]
192 end
193
194 scope :sorted, order(fields_for_order_statement)
183 195
184 # Returns the sharings that +user+ can set the version to 196 # Returns the sharings that +user+ can set the version to
185 def allowed_sharings(user = User.current) 197 def allowed_sharings(user = User.current)
186 VERSION_SHARINGS.select do |s| 198 VERSION_SHARINGS.select do |s|
187 if sharing == s 199 if sharing == s
202 end 214 end
203 end 215 end
204 216
205 private 217 private
206 218
219 def load_issue_counts
220 unless @issue_count
221 @open_issues_count = 0
222 @closed_issues_count = 0
223 fixed_issues.count(:all, :group => :status).each do |status, count|
224 if status.is_closed?
225 @closed_issues_count += count
226 else
227 @open_issues_count += count
228 end
229 end
230 @issue_count = @open_issues_count + @closed_issues_count
231 end
232 end
233
207 # Update the issue's fixed versions. Used if a version's sharing changes. 234 # Update the issue's fixed versions. Used if a version's sharing changes.
208 def update_issues_from_sharing_change 235 def update_issues_from_sharing_change
209 if sharing_changed? 236 if sharing_changed?
210 if VERSION_SHARINGS.index(sharing_was).nil? || 237 if VERSION_SHARINGS.index(sharing_was).nil? ||
211 VERSION_SHARINGS.index(sharing).nil? || 238 VERSION_SHARINGS.index(sharing).nil? ||
240 @issues_progress[open] ||= begin 267 @issues_progress[open] ||= begin
241 progress = 0 268 progress = 0
242 if issues_count > 0 269 if issues_count > 0
243 ratio = open ? 'done_ratio' : 100 270 ratio = open ? 'done_ratio' : 100
244 271
245 done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}", 272 done = fixed_issues.open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f
246 :include => :status,
247 :conditions => ["is_closed = ?", !open]).to_f
248 progress = done / (estimated_average * issues_count) 273 progress = done / (estimated_average * issues_count)
249 end 274 end
250 progress 275 progress
251 end 276 end
252 end 277 end
278
279 def validate_version
280 if effective_date.nil? && @attributes['effective_date'].present?
281 errors.add :effective_date, :not_a_date
282 end
283 end
253 end 284 end