Feature #6687 » keep_original_dates_and_estimations_on_parent_issue.diff
| app/models/issue.rb (copia de trabajo) | ||
|---|---|---|
| 27 | 27 |
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' |
| 28 | 28 |
belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id' |
| 29 | 29 |
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' |
| 30 |
belongs_to :priority_on_children, :class_name => 'IssuePriority', :foreign_key => 'priority_id_on_children' |
|
| 30 | 31 | |
| 31 | 32 |
has_many :journals, :as => :journalized, :dependent => :destroy |
| 32 | 33 |
has_many :visible_journals, |
| ... | ... | |
| 64 | 65 |
delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true |
| 65 | 66 | |
| 66 | 67 |
validates_presence_of :subject, :priority, :project, :tracker, :author, :status |
| 68 |
validates_presence_of :priority_on_children, :if => Proc.new{|i| !i.leaf?}
|
|
| 67 | 69 | |
| 68 | 70 |
validates_length_of :subject, :maximum => 255 |
| 69 | 71 |
validates_inclusion_of :done_ratio, :in => 0..100 |
| ... | ... | |
| 413 | 415 |
return if attrs.empty? |
| 414 | 416 | |
| 415 | 417 |
unless leaf? |
| 416 |
attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
|
|
| 418 |
attrs.reject! {|k,v| %w(done_ratio).include?(k)}
|
|
| 417 | 419 |
end |
| 418 | 420 | |
| 419 | 421 |
if attrs['parent_issue_id'].present? |
| ... | ... | |
| 556 | 558 |
errors.add :tracker_id, :inclusion |
| 557 | 559 |
end |
| 558 | 560 |
end |
| 559 | ||
| 561 |
|
|
| 562 |
if estimated_hours && estimated_hours_on_children && estimated_hours < estimated_hours_on_children |
|
| 563 |
errors.add :estimated_hours, :less_than_children_estimations |
|
| 564 |
end |
|
| 565 |
|
|
| 560 | 566 |
# Checks parent issue assignment |
| 561 | 567 |
if @invalid_parent_issue_id.present? |
| 562 | 568 |
errors.add :parent_issue_id, :invalid |
| ... | ... | |
| 573 | 579 |
errors.add :parent_issue_id, :invalid |
| 574 | 580 |
end |
| 575 | 581 |
end |
| 582 |
if errors[:parent_issue_id].empty? |
|
| 583 |
# Parent valid, avoid estimated hours on children overflow estimated hours on parent |
|
| 584 |
ests_on_parent = @parent_issue.estimated_hours_on_children || 0 |
|
| 585 |
ests_on_parent -= estimated_hours_was unless estimated_hours_was.nil? || (parent_id != @parent_issue.id) |
|
| 586 |
if @parent_issue.estimated_hours.nil? && !estimated_hours.nil? |
|
| 587 |
errors.add :estimated_hours, :no_parent_estimations |
|
| 588 |
elsif !estimated_hours.nil? && @parent_issue.estimated_hours < ests_on_parent + estimated_hours |
|
| 589 |
errors.add :estimated_hours, :greater_than_parent_estimations |
|
| 590 |
end |
|
| 591 |
end |
|
| 576 | 592 |
end |
| 577 | 593 |
end |
| 578 | 594 | |
| ... | ... | |
| 1200 | 1216 | |
| 1201 | 1217 |
def recalculate_attributes_for(issue_id) |
| 1202 | 1218 |
if issue_id && p = Issue.find_by_id(issue_id) |
| 1203 |
# priority = highest priority of children |
|
| 1219 |
# priority in children = highest priority of children
|
|
| 1204 | 1220 |
if priority_position = p.children.maximum("#{IssuePriority.table_name}.position", :joins => :priority)
|
| 1205 |
p.priority = IssuePriority.find_by_position(priority_position) |
|
| 1221 |
p.priority_on_children = IssuePriority.find_by_position(priority_position)
|
|
| 1206 | 1222 |
end |
| 1207 | 1223 | |
| 1208 |
# start/due dates = lowest/highest dates of children |
|
| 1209 |
p.start_date = p.children.minimum(:start_date) |
|
| 1210 |
p.due_date = p.children.maximum(:due_date) |
|
| 1211 |
if p.start_date && p.due_date && p.due_date < p.start_date
|
|
| 1212 |
p.start_date, p.due_date = p.due_date, p.start_date
|
|
| 1224 |
# start/due dates on children = lowest/highest dates of children
|
|
| 1225 |
p.start_date_on_children = p.children.minimum(:start_date)
|
|
| 1226 |
p.due_date_on_children = p.children.maximum(:due_date)
|
|
| 1227 |
if p.start_date_on_children && p.due_date_on_children && p.due_date_on_children < p.start_date_on_children
|
|
| 1228 |
p.start_date_on_children, p.due_date_on_children = p.due_date_on_children, p.start_date_on_children
|
|
| 1213 | 1229 |
end |
| 1214 | ||
| 1230 |
|
|
| 1231 |
p_children = Issue.where(:parent_id => p.id) |
|
| 1232 |
|
|
| 1215 | 1233 |
# done ratio = weighted average ratio of leaves |
| 1216 | 1234 |
unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio |
| 1217 |
leaves_count = p.leaves.count
|
|
| 1235 |
leaves_count = p_children.count # 1
|
|
| 1218 | 1236 |
if leaves_count > 0 |
| 1219 |
average = p.leaves.average(:estimated_hours).to_f
|
|
| 1237 |
average = p_children.average(:estimated_hours).to_f # 5
|
|
| 1220 | 1238 |
if average == 0 |
| 1221 | 1239 |
average = 1 |
| 1222 | 1240 |
end |
| 1223 |
done = p.leaves.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
|
|
| 1224 |
progress = done / (average * leaves_count) |
|
| 1241 |
done = p_children.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
|
|
| 1242 |
progress = done / p.estimated_hours#(average * leaves_count)
|
|
| 1225 | 1243 |
p.done_ratio = progress.round |
| 1226 | 1244 |
end |
| 1227 | 1245 |
end |
| 1228 | 1246 | |
| 1229 |
# estimate = sum of leaves estimates |
|
| 1230 |
p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
|
|
| 1231 |
p.estimated_hours = nil if p.estimated_hours == 0.0 |
|
| 1247 |
# estimate on children = sum of leaves estimates
|
|
| 1248 |
p.estimated_hours_on_children = p_children.sum(:estimated_hours).to_f
|
|
| 1249 |
p.estimated_hours_on_children = nil if p.estimated_hours == 0.0
|
|
| 1232 | 1250 | |
| 1233 | 1251 |
# ancestors will be recursively updated |
| 1234 | 1252 |
p.save(:validate => false) |
| app/views/issues/show.html.erb (copia de trabajo) | ||
|---|---|---|
| 35 | 35 |
<%= issue_fields_rows do |rows| |
| 36 | 36 |
rows.left l(:field_status), h(@issue.status.name), :class => 'status' |
| 37 | 37 |
rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority' |
| 38 |
rows.left l(:field_priority_on_children), h(@issue.priority_on_children.name), :class => 'priority-on-children' unless @issue.leaf? |
|
| 38 | 39 | |
| 39 | 40 |
unless @issue.disabled_core_fields.include?('assigned_to_id')
|
| 40 | 41 |
rows.left l(:field_assigned_to), avatar(@issue.assigned_to, :size => "14").to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to' |
| ... | ... | |
| 48 | 49 | |
| 49 | 50 |
unless @issue.disabled_core_fields.include?('start_date')
|
| 50 | 51 |
rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date' |
| 52 |
rows.right l(:field_start_date_on_children), format_date(@issue.start_date_on_children), :class => 'start-date-on-children' unless @issue.leaf? |
|
| 51 | 53 |
end |
| 52 | 54 |
unless @issue.disabled_core_fields.include?('due_date')
|
| 53 | 55 |
rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date' |
| 56 |
rows.right l(:field_due_date_on_children), format_date(@issue.due_date_on_children), :class => 'due-date-on-children' unless @issue.leaf? |
|
| 54 | 57 |
end |
| 55 | 58 |
unless @issue.disabled_core_fields.include?('done_ratio')
|
| 56 | 59 |
rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress'
|
| ... | ... | |
| 59 | 62 |
unless @issue.estimated_hours.nil? |
| 60 | 63 |
rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours' |
| 61 | 64 |
end |
| 65 |
unless @issue.estimated_hours_on_children.nil? |
|
| 66 |
rows.right l(:field_estimated_hours_on_children), l_hours(@issue.estimated_hours_on_children), :class => 'estimated-hours_on_children' unless @issue.leaf? |
|
| 67 |
end |
|
| 62 | 68 |
end |
| 63 | 69 |
if User.current.allowed_to?(:view_time_entries, @project) |
| 64 | 70 |
rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-"), :class => 'spent-time'
|
| app/views/issues/_attributes.html.erb (copia de trabajo) | ||
|---|---|---|
| 11 | 11 |
<% end %> |
| 12 | 12 | |
| 13 | 13 |
<% if @issue.safe_attribute? 'priority_id' %> |
| 14 |
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p>
|
|
| 14 |
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true} %></p>
|
|
| 15 | 15 |
<% end %> |
| 16 | 16 | |
| 17 | 17 |
<% if @issue.safe_attribute? 'assigned_to_id' %> |
| ... | ... | |
| 47 | 47 |
<% end %> |
| 48 | 48 | |
| 49 | 49 |
<% if @issue.safe_attribute? 'start_date' %> |
| 50 |
<p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('start_date') %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
|
| 50 |
<p><%= f.text_field :start_date, :size => 10, :required => @issue.required_attribute?('start_date') %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
|
| 51 | 51 |
<% end %> |
| 52 | 52 | |
| 53 | 53 |
<% if @issue.safe_attribute? 'due_date' %> |
| 54 |
<p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('due_date') %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
|
| 54 |
<p><%= f.text_field :due_date, :size => 10, :required => @issue.required_attribute?('due_date') %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
|
| 55 | 55 |
<% end %> |
| 56 | 56 | |
| 57 | 57 |
<% if @issue.safe_attribute? 'estimated_hours' %> |
| 58 |
<p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %></p>
|
|
| 58 |
<p><%= f.text_field :estimated_hours, :size => 3, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %></p>
|
|
| 59 | 59 |
<% end %> |
| 60 | 60 | |
| 61 | 61 |
<% if @issue.safe_attribute?('done_ratio') && @issue.leaf? && Issue.use_field_for_done_ratio? %>
|
| config/locales/en.yml (copia de trabajo) | ||
|---|---|---|
| 129 | 129 |
not_same_project: "doesn't belong to the same project" |
| 130 | 130 |
circular_dependency: "This relation would create a circular dependency" |
| 131 | 131 |
cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks" |
| 132 |
|
|
| 133 |
models: |
|
| 134 |
# Overrides default messages |
|
| 135 |
issue: |
|
| 136 |
attributes: |
|
| 137 |
estimated_hours: |
|
| 138 |
greater_than_parent_estimations: "is over the parent issue's estimation" |
|
| 139 |
less_than_children_estimations: "is under the estimations of subtasks" |
|
| 140 |
no_parent_estimations: "can't be present if parent issue's estimation is undefined" |
|
| 132 | 141 | |
| 133 | 142 |
actionview_instancetag_blank_option: Please select |
| 134 | 143 | |
| ... | ... | |
| 332 | 341 |
field_timeout: "Timeout (in seconds)" |
| 333 | 342 |
field_board_parent: Parent forum |
| 334 | 343 |
field_private_notes: Private notes |
| 344 |
field_due_date_on_children: Due date on subtasks |
|
| 345 |
field_start_date_on_children: Start date on subtasks |
|
| 346 |
field_priority_on_children: Highest priority on subtasks |
|
| 347 |
field_estimated_hours_on_children: Estimated hours on subtasks |
|
| 335 | 348 | |
| 336 | 349 |
setting_app_title: Application title |
| 337 | 350 |
setting_app_subtitle: Application subtitle |
| config/locales/es.yml (copia de trabajo) | ||
|---|---|---|
| 141 | 141 | |
| 142 | 142 |
models: |
| 143 | 143 |
# Overrides default messages |
| 144 | ||
| 144 |
issue: |
|
| 145 |
attributes: |
|
| 146 |
estimated_hours: |
|
| 147 |
greater_than_parent_estimations: "sobrepasa estimaciones de la tarea padre" |
|
| 148 |
less_than_children_estimations: "bajo estimaciones de subtareas" |
|
| 149 |
no_parent_estimations: "no puede estar presente si tarea padre no lo tiene definido" |
|
| 145 | 150 |
attributes: |
| 146 | 151 |
# Overrides model and default messages. |
| 147 | ||
| 152 |
|
|
| 148 | 153 |
direction: ltr |
| 149 | 154 |
date: |
| 150 | 155 |
formats: |
| ... | ... | |
| 275 | 280 |
field_done_ratio: "% Realizado" |
| 276 | 281 |
field_downloads: Descargas |
| 277 | 282 |
field_due_date: Fecha fin |
| 283 |
field_due_date_on_children: Fecha fin en subtareas |
|
| 278 | 284 |
field_effective_date: Fecha |
| 279 | 285 |
field_estimated_hours: Tiempo estimado |
| 286 |
field_estimated_hours_on_children: Tiempo estimado en subtareas |
|
| 280 | 287 |
field_field_format: Formato |
| 281 | 288 |
field_filename: Fichero |
| 282 | 289 |
field_filesize: Tamaño |
| ... | ... | |
| 315 | 322 |
field_port: Puerto |
| 316 | 323 |
field_possible_values: Valores posibles |
| 317 | 324 |
field_priority: Prioridad |
| 325 |
field_priority_on_children: Prioridad más alta en subtareas |
|
| 318 | 326 |
field_project: Proyecto |
| 319 | 327 |
field_redirect_existing_links: Redireccionar enlaces existentes |
| 320 | 328 |
field_regexp: Expresión regular |
| ... | ... | |
| 322 | 330 |
field_searchable: Incluir en las búsquedas |
| 323 | 331 |
field_spent_on: Fecha |
| 324 | 332 |
field_start_date: Fecha de inicio |
| 333 |
field_start_date_on_children: Fecha de inicio de subtareas |
|
| 325 | 334 |
field_start_page: Página principal |
| 326 | 335 |
field_status: Estado |
| 327 | 336 |
field_subject: Asunto |
| db/migrate/20130222154546_add_due_date_on_children_and_priority_id_on_children_and_start_date_on_children_and_estimated_hours_on_children_to_issue.rb (revisión: 0) | ||
|---|---|---|
| 1 |
class AddDueDateOnChildrenAndPriorityIdOnChildrenAndStartDateOnChildrenAndEstimatedHoursOnChildrenToIssue < ActiveRecord::Migration |
|
| 2 |
def change |
|
| 3 |
add_column :issues, :due_date_on_children, :date |
|
| 4 |
add_column :issues, :start_date_on_children, :date |
|
| 5 |
add_column :issues, :priority_id_on_children, :integer, :default => 0, :null => false |
|
| 6 |
add_column :issues, :estimated_hours_on_children, :float |
|
| 7 |
add_index :issues, :priority_id_on_children |
|
| 8 |
Issue.all.each do |i| |
|
| 9 |
# Trigger update_parent_attributes |
|
| 10 |
i.update_attributes(:parent_id => i.parent_id) |
|
| 11 |
end |
|
| 12 |
end |
|
| 13 |
end |
|