The derived dates are the minimum/maximum of the descendant's dates. As long as the work package is scheduled automatically, the derived and non derived dates should be equal althoug the derived dates will be null in case non descendants exists. But if the user decides to activate manual scheduling the dates can deviate. The derived dates can then help to display the deviation to the user. This commit however does not alter the front end. The dates are eager loaded on the work package index commit using the same mechanims already introduced for spent time. The patched cost attributes, which are also eager loaded, are now eager loaded in the core as well so the patch is removed. As those attributes are now part of the core (although in a different module) it no longer makes sense to rely on patching for this.pull/8555/head
parent
d14ca600c1
commit
655f021b92
@ -1,131 +0,0 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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. |
||||
#++ |
||||
|
||||
class WorkPackage::SpentTime |
||||
attr_accessor :user, |
||||
:work_package |
||||
|
||||
def initialize(user, work_package = nil) |
||||
@user = user |
||||
@work_package = work_package |
||||
end |
||||
|
||||
def scope |
||||
with_spent_hours_joined |
||||
end |
||||
|
||||
private |
||||
|
||||
def with_spent_hours_joined |
||||
query = join_descendants(wp_table) |
||||
query = join_time_entries(query) |
||||
|
||||
WorkPackage.joins(query.join_sources) |
||||
.group(:id) |
||||
end |
||||
|
||||
def join_descendants(select) |
||||
select |
||||
.outer_join(relations_table) |
||||
.on(relations_join_descendants_condition) |
||||
.outer_join(wp_descendants) |
||||
.on(hierarchy_and_allowed_condition) |
||||
end |
||||
|
||||
def join_time_entries(select) |
||||
join_condition = time_entries_table[:work_package_id] |
||||
.eq(wp_descendants[:id]) |
||||
.and(allowed_to_view_time_entries) |
||||
|
||||
select |
||||
.outer_join(time_entries_table) |
||||
.on(join_condition) |
||||
end |
||||
|
||||
def relations_from_and_type_matches_condition |
||||
relations_join_condition = relation_of_wp_and_hierarchy_condition |
||||
|
||||
non_hierarchy_type_columns.each do |type| |
||||
relations_join_condition = relations_join_condition.and(relations_table[type].eq(0)) |
||||
end |
||||
|
||||
relations_join_condition |
||||
end |
||||
|
||||
def relation_of_wp_and_hierarchy_condition |
||||
wp_table[:id].eq(relations_table[:from_id]).and(relations_table[:hierarchy].gteq(0)) |
||||
end |
||||
|
||||
def relations_join_descendants_condition |
||||
if work_package |
||||
relations_from_and_type_matches_condition |
||||
.and(wp_table[:id].eq(work_package.id)) |
||||
else |
||||
relations_from_and_type_matches_condition |
||||
end |
||||
end |
||||
|
||||
def allowed_to_view_work_packages |
||||
wp_descendants[:project_id].in(Project.allowed_to(user, :view_work_packages).select(:id).arel) |
||||
end |
||||
|
||||
def allowed_to_view_time_entries |
||||
time_entries_table[:id].in(TimeEntry.visible(user).select(:id).arel) |
||||
end |
||||
|
||||
def hierarchy_and_allowed_condition |
||||
self_or_descendant_condition |
||||
.and(allowed_to_view_work_packages) |
||||
end |
||||
|
||||
def self_or_descendant_condition |
||||
relations_table[:to_id].eq(wp_descendants[:id]) |
||||
end |
||||
|
||||
def non_hierarchy_type_columns |
||||
TypedDag::Configuration[WorkPackage].type_columns - [:hierarchy] |
||||
end |
||||
|
||||
def wp_table |
||||
@wp_table ||= WorkPackage.arel_table |
||||
end |
||||
|
||||
def relations_table |
||||
@relations || Relation.arel_table |
||||
end |
||||
|
||||
def wp_descendants |
||||
@wp_descendants ||= wp_table.alias('descendants') |
||||
end |
||||
|
||||
def time_entries_table |
||||
@time_entries_table ||= TimeEntry.arel_table |
||||
end |
||||
end |
@ -0,0 +1,95 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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 WorkPackages::DerivedDates |
||||
# Returns the maximum of the dates of all descendants (start and due date) |
||||
# No visibility check is applied so a user will always see the maximum regardless of his permission. |
||||
# |
||||
# The value can stem from either eager loading the value via |
||||
# WorkPackage.include_derived_dates in which case the work package has a |
||||
# derived_start_date attribute or it is loaded on calling the method. |
||||
def derived_start_date |
||||
derived_date('derived_start_date') |
||||
end |
||||
|
||||
# Returns the minimum of the dates of all descendants (start and due date) |
||||
# No visibility check is applied so a user will always see the minimum regardless of his permission. |
||||
# |
||||
# The value can stem from either eager loading the value via |
||||
# WorkPackage.include_derived_dates in which case the work package has a |
||||
# derived_due_date attribute or it is loaded on calling the method. |
||||
def derived_due_date |
||||
derived_date('derived_due_date') |
||||
end |
||||
|
||||
def derived_start_date=(date) |
||||
compute_derived_dates |
||||
@derived_dates[0] = date |
||||
end |
||||
|
||||
def derived_due_date=(date) |
||||
compute_derived_dates |
||||
@derived_dates[1] = date |
||||
end |
||||
|
||||
def reload(*) |
||||
@derived_dates = nil |
||||
super |
||||
end |
||||
|
||||
private |
||||
|
||||
def derived_date(key) |
||||
if attributes.key?(key) |
||||
attributes[key] |
||||
else |
||||
compute_derived_dates[key] |
||||
end |
||||
end |
||||
|
||||
def compute_derived_dates |
||||
@derived_dates ||= begin |
||||
attributes = %w[derived_start_date derived_due_date] |
||||
|
||||
values = if persisted? |
||||
WorkPackage |
||||
.from(WorkPackage.include_derived_dates.where(id: self)) |
||||
.pluck(*attributes.each { |a| Arel.sql(a) }) |
||||
.first || [] |
||||
else |
||||
[] |
||||
end |
||||
|
||||
attributes |
||||
.zip(values) |
||||
.to_h |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,79 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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. |
||||
#++ |
||||
|
||||
class WorkPackages::Scopes::IncludeSpentTime |
||||
class << self |
||||
def fetch(user, work_package = nil) |
||||
query = join_time_entries(user) |
||||
|
||||
scope = WorkPackage |
||||
.left_join_self_and_descendants(user, work_package) |
||||
.joins(query.join_sources) |
||||
.group(:id) |
||||
.select('SUM(time_entries.hours) AS hours') |
||||
|
||||
if work_package |
||||
scope.where(id: work_package.id) |
||||
else |
||||
scope |
||||
end |
||||
end |
||||
|
||||
protected |
||||
|
||||
def join_time_entries(user) |
||||
join_condition = time_entries_table[:work_package_id] |
||||
.eq(wp_descendants[:id]) |
||||
.and(allowed_to_view_time_entries(user)) |
||||
|
||||
wp_table |
||||
.outer_join(time_entries_table) |
||||
.on(join_condition) |
||||
end |
||||
|
||||
def allowed_to_view_time_entries(user) |
||||
time_entries_table[:id].in(TimeEntry.visible(user).select(:id).arel) |
||||
end |
||||
|
||||
def wp_table |
||||
@wp_table ||= WorkPackage.arel_table |
||||
end |
||||
|
||||
def wp_descendants |
||||
# Relies on a table called descendants to exist in the scope |
||||
# which is provided by left_join_self_and_descendants |
||||
@wp_descendants ||= wp_table.alias('descendants') |
||||
end |
||||
|
||||
def time_entries_table |
||||
@time_entries_table ||= TimeEntry.arel_table |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,99 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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. |
||||
#++ |
||||
|
||||
class WorkPackages::Scopes::LeftJoinSelfAndDescendants |
||||
class << self |
||||
def fetch(user, work_package = nil) |
||||
WorkPackage.joins(join_descendants(user, work_package).join_sources) |
||||
end |
||||
|
||||
private |
||||
|
||||
def join_descendants(user, work_package) |
||||
wp_table |
||||
.outer_join(relations_table) |
||||
.on(relations_join_descendants_condition(work_package)) |
||||
.outer_join(wp_descendants) |
||||
.on(hierarchy_and_allowed_condition(user)) |
||||
end |
||||
|
||||
def relations_from_and_type_matches_condition |
||||
relations_join_condition = relation_of_wp_and_hierarchy_condition |
||||
|
||||
non_hierarchy_type_columns.each do |type| |
||||
relations_join_condition = relations_join_condition.and(relations_table[type].eq(0)) |
||||
end |
||||
|
||||
relations_join_condition |
||||
end |
||||
|
||||
def relation_of_wp_and_hierarchy_condition |
||||
wp_table[:id].eq(relations_table[:from_id]).and(relations_table[:hierarchy].gteq(0)) |
||||
end |
||||
|
||||
def relations_join_descendants_condition(work_package) |
||||
if work_package |
||||
relations_from_and_type_matches_condition |
||||
.and(wp_table[:id].eq(work_package.id)) |
||||
else |
||||
relations_from_and_type_matches_condition |
||||
end |
||||
end |
||||
|
||||
def hierarchy_and_allowed_condition(user) |
||||
self_or_descendant_condition |
||||
.and(allowed_to_view_work_packages(user)) |
||||
end |
||||
|
||||
def allowed_to_view_work_packages(user) |
||||
wp_descendants[:project_id].in(Project.allowed_to(user, :view_work_packages).select(:id).arel) |
||||
end |
||||
|
||||
def self_or_descendant_condition |
||||
relations_table[:to_id].eq(wp_descendants[:id]) |
||||
end |
||||
|
||||
def non_hierarchy_type_columns |
||||
TypedDag::Configuration[WorkPackage].type_columns - [:hierarchy] |
||||
end |
||||
|
||||
def wp_table |
||||
@wp_table ||= WorkPackage.arel_table |
||||
end |
||||
|
||||
def relations_table |
||||
@relations || Relation.arel_table |
||||
end |
||||
|
||||
def wp_descendants |
||||
@wp_descendants ||= wp_table.alias('descendants') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,58 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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 WorkPackages::SpentTime |
||||
# Returns the total number of hours spent on this work package and its descendants. |
||||
# The result can be a subset of the actual spent time in cases where the user's permissions |
||||
# are limited, i.e. he lacks the view_time_entries and/or view_work_packages permission. |
||||
# |
||||
# Example: |
||||
# spent_hours => 0.0 |
||||
# spent_hours => 50.2 |
||||
# |
||||
# The value can stem from either eager loading the value via |
||||
# WorkPackage.include_spent_time in which case the work package has an |
||||
# #hours attribute or it is loaded on calling the method. |
||||
def spent_hours(user = User.current) |
||||
if respond_to?(:hours) |
||||
hours.to_f |
||||
else |
||||
compute_spent_hours(user) |
||||
end || 0.0 |
||||
end |
||||
|
||||
private |
||||
|
||||
def compute_spent_hours(user) |
||||
WorkPackage.include_spent_time(user, self) |
||||
.pluck(Arel.sql('SUM(hours)')) |
||||
.first |
||||
end |
||||
end |
@ -1,129 +0,0 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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 OpenProject::Costs::Patches::WorkPackageEagerLoadingPatch |
||||
def self.prepended(base) |
||||
class << base |
||||
prepend ClassMethods |
||||
end |
||||
end |
||||
|
||||
def self.join_costs(scope) |
||||
# The core adds a "LEFT OUTER JOIN time_entries" where the on clause |
||||
# allows all time entries to be joined if he has the :view_time_entries. |
||||
# Costs will add another "LEFT OUTER JOIN time_entries". The two joins |
||||
# may or may not include each other's rows depending on the user's and the project's permissions. |
||||
# This is caused by entries being joined if he has |
||||
# the :view_time_entries permission and additionally those which are |
||||
# his and for which he has the :view_own_time_entries permission. |
||||
# Because of that, entries may be joined twice. |
||||
# We therefore modify the core's join by placing it in a subquery similar to those of costs. |
||||
# |
||||
# This is very hacky. |
||||
# |
||||
# We also have to remove the sum calcualtion for time_entries.hours as |
||||
# the calculation is later on performed within the subquery added by |
||||
# LaborCosts. With it, we can use the value as it is calculated by the subquery. |
||||
time_join = core_with_joined_time(scope) |
||||
|
||||
reject_core_time_entries(scope) |
||||
|
||||
target_scope = new_scope_with_costs(scope, time_join) |
||||
|
||||
reject_core_descendants(target_scope) |
||||
reject_core_grouping(target_scope) |
||||
|
||||
target_scope |
||||
end |
||||
|
||||
def self.core_with_joined_time(scope) |
||||
time = scope.dup |
||||
|
||||
wp_table = WorkPackage.arel_table |
||||
|
||||
wp_table |
||||
.outer_join(time.arel.as('spent_time_hours')) |
||||
.on(wp_table[:id].eq(time.arel_table.alias('spent_time_hours')[:id])) |
||||
end |
||||
|
||||
def self.new_scope_with_costs(scope, time_join) |
||||
material_scope = work_package_material_scope(scope) |
||||
labor_scope = work_package_labor_scope(scope) |
||||
|
||||
scope |
||||
.joins(material_scope.arel.join_sources) |
||||
.joins(labor_scope.arel.join_sources) |
||||
.joins(time_join.join_sources) |
||||
.select(material_scope.select_values) |
||||
.select(labor_scope.select_values) |
||||
.select('spent_time_hours.hours') |
||||
end |
||||
|
||||
def self.work_package_material_scope(scope) |
||||
WorkPackage::MaterialCosts |
||||
.new |
||||
.add_to_work_package_collection(scope.dup) |
||||
end |
||||
|
||||
def self.work_package_labor_scope(scope) |
||||
WorkPackage::LaborCosts |
||||
.new |
||||
.add_to_work_package_collection(scope.dup) |
||||
end |
||||
|
||||
def self.reject_core_time_entries(scope) |
||||
scope.joins_values.reject! do |join| |
||||
join.is_a?(Arel::Nodes::OuterJoin) && |
||||
join.left.is_a?(Arel::Table) && |
||||
join.left.name == 'time_entries' |
||||
end |
||||
scope.select_values.reject! do |select| |
||||
select == "SUM(time_entries.hours) AS hours" |
||||
end |
||||
end |
||||
|
||||
def self.reject_core_descendants(scope) |
||||
scope.joins_values.reject! do |join| |
||||
join.is_a?(Arel::Nodes::OuterJoin) && |
||||
join.left.is_a?(Arel::Nodes::TableAlias) && |
||||
join.left.right == 'descendants' |
||||
end |
||||
end |
||||
|
||||
def self.reject_core_grouping(scope) |
||||
scope.group_values.reject! do |group| |
||||
group == :id |
||||
end |
||||
end |
||||
|
||||
module ClassMethods |
||||
def add_eager_loading(*args) |
||||
::OpenProject::Costs::Patches::WorkPackageEagerLoadingPatch.join_costs(super) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,153 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe WorkPackage, 'derived dates', type: :model do |
||||
let(:work_package) do |
||||
FactoryBot.create(:work_package) |
||||
end |
||||
let(:child_work_package) do |
||||
FactoryBot.create(:work_package, |
||||
project: work_package.project, |
||||
start_date: child_start_date, |
||||
due_date: child_due_date, |
||||
parent: work_package) |
||||
end |
||||
let(:child_work_package_in_other_project) do |
||||
FactoryBot.create(:work_package, |
||||
start_date: other_child_start_date, |
||||
due_date: other_child_due_date, |
||||
parent: work_package) |
||||
end |
||||
let(:child_start_date) { Date.today - 4.days } |
||||
let(:child_due_date) { Date.today + 6.days } |
||||
let(:other_child_start_date) { Date.today + 4.days } |
||||
let(:other_child_due_date) { Date.today + 10.days } |
||||
|
||||
let(:work_packages) { [work_package, child_work_package, child_work_package_in_other_project] } |
||||
|
||||
let(:role) do |
||||
FactoryBot.build(:role, |
||||
permissions: %i[view_work_packages]) |
||||
end |
||||
let(:user) do |
||||
FactoryBot.build(:user, |
||||
member_in_project: work_package.project, |
||||
member_through_role: role) |
||||
end |
||||
|
||||
before do |
||||
login_as user |
||||
work_packages |
||||
end |
||||
|
||||
shared_examples_for 'derived dates' do |
||||
context 'with all dates being set' do |
||||
it 'the derived_start_date is the minimum of both start and due date' do |
||||
expect(subject.derived_start_date).to eql child_start_date |
||||
end |
||||
|
||||
it 'the derived_due_date is the maximum of both start and due date' do |
||||
expect(subject.derived_due_date).to eql other_child_due_date |
||||
end |
||||
end |
||||
|
||||
context 'with the due dates being minimal (start date being nil)' do |
||||
let(:child_start_date) { nil } |
||||
let(:other_child_start_date) { nil } |
||||
|
||||
it 'the derived_start_date is the minumum of the due dates' do |
||||
expect(subject.derived_start_date).to eql child_due_date |
||||
end |
||||
|
||||
it 'the derived_due_date is the maximum of the due dates' do |
||||
expect(subject.derived_due_date).to eql other_child_due_date |
||||
end |
||||
end |
||||
|
||||
context 'with the start date being maximum (due date being nil)' do |
||||
let(:child_due_date) { nil } |
||||
let(:other_child_due_date) { nil } |
||||
|
||||
it 'the derived_start_date is the minimum of the start dates' do |
||||
expect(subject.derived_start_date).to eql child_start_date |
||||
end |
||||
|
||||
it 'has the derived_due_date is the maximum of the start dates' do |
||||
expect(subject.derived_due_date).to eql other_child_start_date |
||||
end |
||||
end |
||||
|
||||
context 'with child dates being nil' do |
||||
let(:child_start_date) { nil } |
||||
let(:child_due_date) { nil } |
||||
let(:other_child_start_date) { nil } |
||||
let(:other_child_due_date) { nil } |
||||
|
||||
it 'is nil' do |
||||
expect(subject.derived_start_date).to be_nil |
||||
end |
||||
end |
||||
|
||||
context 'without children' do |
||||
let(:work_packages) { [work_package] } |
||||
|
||||
it 'is nil' do |
||||
expect(subject.derived_start_date).to be_nil |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'for a work_package loaded individually' do |
||||
subject { work_package } |
||||
|
||||
it_behaves_like 'derived dates' |
||||
end |
||||
|
||||
context 'for a work package that had derived dates loaded' do |
||||
subject { WorkPackage.include_derived_dates.first } |
||||
|
||||
it_behaves_like 'derived dates' |
||||
end |
||||
|
||||
context 'for an unpersisted work_package' do |
||||
let(:work_package) { WorkPackage.new } |
||||
let(:work_packages) { [] } |
||||
|
||||
subject { work_package } |
||||
|
||||
it 'the derived_start_date is nil' do |
||||
expect(subject.derived_start_date).to be_nil |
||||
end |
||||
|
||||
it 'the derived_due_date is nil' do |
||||
expect(subject.derived_due_date).to be_nil |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue