|
|
|
@ -5,20 +5,22 @@ module Backlogs |
|
|
|
|
def self.included(base) # :nodoc: |
|
|
|
|
base.extend(ClassMethods) |
|
|
|
|
base.send(:include, InstanceMethods) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base.class_eval do |
|
|
|
|
unloadable |
|
|
|
|
|
|
|
|
|
alias_method_chain :move_to_project_without_transaction, :autolink |
|
|
|
|
alias_method_chain :recalculate_attributes_for, :remaining_hours |
|
|
|
|
alias_method_chain :is_descendant_of?, :new_record |
|
|
|
|
alias_method_chain :is_ancestor_of?, :new_record |
|
|
|
|
before_validation :backlogs_before_validation |
|
|
|
|
after_save :backlogs_after_save |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module ClassMethods |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module InstanceMethods |
|
|
|
|
def move_to_project_without_transaction_with_autolink(new_project, new_tracker = nil, options = {}) |
|
|
|
|
newissue = move_to_project_without_transaction_without_autolink(new_project, new_tracker, options) |
|
|
|
@ -29,33 +31,49 @@ module Backlogs |
|
|
|
|
relation.issue_to = newissue |
|
|
|
|
relation.save |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newissue |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def journalized_update_attributes!(attribs) |
|
|
|
|
self.init_journal(User.current) |
|
|
|
|
return self.update_attributes!(attribs) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def journalized_update_attributes(attribs) |
|
|
|
|
self.init_journal(User.current) |
|
|
|
|
return self.update_attributes(attribs) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def journalized_update_attribute(attrib, v) |
|
|
|
|
self.init_journal(User.current) |
|
|
|
|
self.update_attribute(attrib, v) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_story? |
|
|
|
|
return Story.trackers.include?(self.tracker_id) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_task? |
|
|
|
|
return (self.parent_id && self.tracker_id == Task.tracker) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_descendant_of_with_new_record?(other) |
|
|
|
|
if self.new_record? |
|
|
|
|
self.parent && (self.parent == other || self.parent.is_decendant_of?(other)) |
|
|
|
|
else |
|
|
|
|
is_descendant_of_without_new_record?(other) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def is_ancestor_of_with_new_record?(other) |
|
|
|
|
if self.new_record? |
|
|
|
|
false |
|
|
|
|
else |
|
|
|
|
is_ancestor_of_without_new_record?(other) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def story |
|
|
|
|
if self.is_story? |
|
|
|
|
return self |
|
|
|
@ -63,13 +81,13 @@ module Backlogs |
|
|
|
|
return self.ancestors.find_by_tracker_id(Story.trackers) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def blocks |
|
|
|
|
# return issues that I block that aren't closed |
|
|
|
|
return [] if closed? |
|
|
|
|
relations_from.collect {|ir| ir.relation_type == 'blocks' && !ir.issue_to.closed? ? ir.issue_to : nil}.compact |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def blockers |
|
|
|
|
# return issues that block me |
|
|
|
|
return [] if closed? |
|
|
|
@ -78,23 +96,23 @@ module Backlogs |
|
|
|
|
|
|
|
|
|
def velocity_based_estimate |
|
|
|
|
return nil if !self.is_story? || ! self.story_points || self.story_points <= 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dpp = self.project.scrum_statistics.info[:average_days_per_point] |
|
|
|
|
return nil if ! dpp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Integer(self.story_points * dpp) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def recalculate_attributes_for_with_remaining_hours(issue_id) |
|
|
|
|
recalculate_attributes_for_without_remaining_hours(issue_id) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if issue_id && p = Issue.find_by_id(issue_id) |
|
|
|
|
if p.left != (p.right + 1) # this node has children |
|
|
|
|
p.update_attribute(:remaining_hours, p.leaves.sum(:remaining_hours).to_f) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def backlogs_before_validation |
|
|
|
|
if self.tracker_id == Task.tracker |
|
|
|
|
self.estimated_hours = self.remaining_hours if self.estimated_hours.blank? && ! self.remaining_hours.blank? |
|
|
|
@ -108,9 +126,9 @@ module Backlogs |
|
|
|
|
## Normally one of the _before_save hooks ought to take |
|
|
|
|
## care of this, but appearantly neither root_id nor |
|
|
|
|
## parent_id are set at that point |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
touched_sprints = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.is_story? |
|
|
|
|
# raw sql here because it's efficient and not |
|
|
|
|
# doing so causes an update loop when Issue calls |
|
|
|
@ -125,7 +143,7 @@ module Backlogs |
|
|
|
|
|
|
|
|
|
touched_sprints = [self.fixed_version_id, self.fixed_version_id_was].compact.uniq |
|
|
|
|
touched_sprints = touched_sprints.collect{|s| Sprint.find(s)}.compact |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elsif not Task.tracker.nil? |
|
|
|
|
begin |
|
|
|
|
story = self.story |
|
|
|
@ -136,7 +154,7 @@ module Backlogs |
|
|
|
|
touched_sprints = [self.root_id, self.root_id_was].compact.uniq.collect{|s| Story.find(s).fixed_version}.compact |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
touched_sprints.each {|sprint| |
|
|
|
|
sprint.touch_burndown |
|
|
|
|
} |
|
|
|
|