OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/app/models/work_package/scheduling_rules.rb

98 lines
3.3 KiB

#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module WorkPackage::SchedulingRules
extend ActiveSupport::Concern
def reschedule_after(date)
WorkPackages::RescheduleService
.new(user: User.current,
work_package: self)
.call(date)
end
# Calculates the minimum date that
# will not violate the precedes relations (max(due date, start date) + delay)
# of this work package or its ancestors
# e.g.
# AP(due_date: 2017/07/24, delay: 1)-precedes-A
# |
# parent
# |
# BP(due_date: 2017/07/22, delay: 2)-precedes-B
# |
# parent
# |
# CP(due_date: 2017/07/25, delay: 2)-precedes-C
#
# Then soonest_start for:
# C is 2017/07/27
# B is 2017/07/25
# A is 2017/07/25
def soonest_start
# Using a hand crafted union here instead of the alternative
# Relation.from_work_package_or_ancestors(self).follows
# as the performance of the above would be several orders of magnitude worse on MySql
sql = Relation.connection.unprepared_statement do
"((#{ancestors_follows_relations.to_sql}) UNION (#{own_follows_relations.to_sql})) AS relations"
end
@soonest_start ||=
Relation.from(sql)
.map(&:successor_soonest_start)
.compact
.max
end
# Returns the time scheduled for this work package.
#
# Example:
# Start Date: 2/26/09, Due Date: 3/04/09, duration => 7
# Start Date: 2/26/09, Due Date: 2/26/09, duration => 1
# Start Date: 2/26/09, Due Date: - , duration => 1
# Start Date: - , Due Date: 2/26/09, duration => 1
def duration
if start_date && due_date
due_date - start_date + 1
else
1
end
end
private
def ancestors_follows_relations
Relation.where(from_id: self.ancestors_relations.select(:from_id)).follows
end
def own_follows_relations
Relation.where(from_id: self.id).follows
end
end