Merge pull request #283 from finnlabs/fix/time_entry_sums

Fix/time entry sums
pull/6827/head
Oliver Günther 8 years ago committed by GitHub
commit 7c9d2298b2
  1. 2
      .travis.yml
  2. 4
      app/models/cost_scopes.rb
  3. 2
      app/models/labor_budget_item.rb
  4. 2
      app/models/time_entry/time_entry_scopes.rb
  5. 2
      app/models/work_package/labor_costs.rb
  6. 35
      lib/open_project/costs/engine.rb
  7. 3
      spec/models/work_package/cost_eager_loading_spec.rb

@ -59,7 +59,7 @@ env:
- COVERAGE=true - COVERAGE=true
matrix: matrix:
- "TEST_SUITE=plugins:spec DB=mysql GROUP_SIZE=1 GROUP=1" - "TEST_SUITE=plugins:specs DB=mysql GROUP_SIZE=1 GROUP=1"
- "TEST_SUITE=plugins:cucumber DB=mysql GROUP_SIZE=1 GROUP=1" - "TEST_SUITE=plugins:cucumber DB=mysql GROUP_SIZE=1 GROUP=1"
- "TEST_SUITE=npm" - "TEST_SUITE=npm"

@ -3,13 +3,13 @@ module CostScopes
base_module.class_eval do base_module.class_eval do
def self.extended(base_class) def self.extended(base_class)
base_class.class_eval do base_class.class_eval do
scope :visible, -> (*args) { scope :visible, ->(*args) {
user = args.first || User.current user = args.first || User.current
with_visible_entries_on self, user: user, project: args[1] with_visible_entries_on self, user: user, project: args[1]
} }
scope :visible_costs, -> (*args) { scope :visible_costs, ->(*args) {
user = args.first || User.current user = args.first || User.current
with_visible_costs_on self, user: user, project: args[1] with_visible_costs_on self, user: user, project: args[1]

@ -36,7 +36,7 @@ class LaborBudgetItem < ActiveRecord::Base
table = self.arel_table table = self.arel_table
view_allowed = Project.allowed_to(user, :view_hourly_rates).select(:id) view_allowed = Project.allowed_to(user, :view_hourly_rates).select(:id)
view_own_allowed = Project.allowed_to(user, :view_own_hourly_rates).select(:id) view_own_allowed = Project.allowed_to(user, :view_own_hourly_rate).select(:id)
view_or_view_own = table[:project_id] view_or_view_own = table[:project_id]
.in(view_allowed.arel) .in(view_allowed.arel)

@ -14,7 +14,7 @@ class TimeEntry
table = self.arel_table table = self.arel_table
view_allowed = Project.allowed_to(user, :view_hourly_rates).select(:id) view_allowed = Project.allowed_to(user, :view_hourly_rates).select(:id)
view_own_allowed = Project.allowed_to(user, :view_own_hourly_rates).select(:id) view_own_allowed = Project.allowed_to(user, :view_own_hourly_rate).select(:id)
scope.where view_or_view_own(table, view_allowed, view_own_allowed, user) scope.where view_or_view_own(table, view_allowed, view_own_allowed, user)
end end

@ -17,7 +17,7 @@ class WorkPackage
end end
def sum_subselect(base_scope) def sum_subselect(base_scope)
super.project('SUM(hours) hours') super
end end
end end
end end

@ -356,27 +356,34 @@ module OpenProject::Costs
end end
def self.join_costs(scope) def self.join_costs(scope)
material = WorkPackage::MaterialCosts.new
labor = WorkPackage::LaborCosts.new
# The core adds a "LEFT OUTER JOIN time_entries" where the on clause # 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. # allows all time entries to be joined if he has the :view_time_entries.
# Because the cost scopes add another "LEFT OUTER JOIN time_entries" # Costs will add another "LEFT OUTER JOIN time_entries". The two joins
# where the on clause allows all time entries to be joined if he has # 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 # the :view_time_entries permission and additionally those which are
# his and for which he has the :view_own_time_entries permission. # his and for which he has the :view_own_time_entries permission.
# Because costs join includes the values of the core, entries are joined twice. # Because of that, entries may be joined twice.
# We therefore have to remove core's join. # We therefore modify the core's join by placing it in a subquery similar to those of costs.
# #
# This is very hacky. # This is very hacky.
# #
# We also have to remove the sum calcualtion for time_entries.hours as # We also have to remove the sum calcualtion for time_entries.hours as
# the calculation is later on performed within the subquery added by # 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. # LaborCosts. With it, we can use the value as it is calculated by the subquery.
material = WorkPackage::MaterialCosts.new
labor = WorkPackage::LaborCosts.new
time = scope.dup
wp_table = WorkPackage.arel_table
time_join = wp_table
.outer_join(time.arel.as('spent_time_hours'))
.on(wp_table[:id].eq(time.arel_table.alias('spent_time_hours')[:id]))
scope.joins_values.reject! do |join| scope.joins_values.reject! do |join|
join.is_a?(Arel::Nodes::OuterJoin) && join.is_a?(Arel::Nodes::OuterJoin) &&
join.left.is_a?(Arel::Table) && join.left.is_a?(Arel::Table) &&
join.left.name == 'time_entries' join.left.name == 'time_entries'
end end
scope.select_values.reject! do |select| scope.select_values.reject! do |select|
select == "SUM(time_entries.hours) AS hours" select == "SUM(time_entries.hours) AS hours"
@ -385,16 +392,18 @@ module OpenProject::Costs
material_scope = material.add_to_work_package_collection(scope.dup) material_scope = material.add_to_work_package_collection(scope.dup)
labor_scope = labor.add_to_work_package_collection(scope.dup) labor_scope = labor.add_to_work_package_collection(scope.dup)
target_scope = scope.joins(material_scope.join_sources) target_scope = scope
.joins(material_scope.join_sources)
.joins(labor_scope.join_sources) .joins(labor_scope.join_sources)
.joins(time_join.join_sources)
.select(material_scope.select_values) .select(material_scope.select_values)
.select(labor_scope.select_values) .select(labor_scope.select_values)
.select('time_entries.hours') .select('spent_time_hours.hours')
target_scope.joins_values.reject! do |join| target_scope.joins_values.reject! do |join|
join.is_a?(Arel::Nodes::OuterJoin) && join.is_a?(Arel::Nodes::OuterJoin) &&
join.left.is_a?(Arel::Nodes::TableAlias) && join.left.is_a?(Arel::Nodes::TableAlias) &&
join.left.right == 'descendants' join.left.right == 'descendants'
end end
target_scope.group_values.reject! do |group| target_scope.group_values.reject! do |group|

@ -58,7 +58,6 @@ describe WorkPackage, 'cost eager loading', type: :model do
user: user, user: user,
work_package: work_package, work_package: work_package,
project: project) project: project)
end end
let(:time_entry1) do let(:time_entry1) do
FactoryGirl.create(:time_entry, FactoryGirl.create(:time_entry,
@ -84,9 +83,9 @@ describe WorkPackage, 'cost eager loading', type: :model do
context "combining core's and cost's eager loading" do context "combining core's and cost's eager loading" do
let(:scope) do let(:scope) do
scope = WorkPackage scope = WorkPackage
.include_spent_hours(user) .include_spent_hours(user)
.select('work_packages.*')
.where(id: [work_package.id]) .where(id: [work_package.id])
OpenProject::Costs::Engine::EagerLoadedCosts.join_costs(scope) OpenProject::Costs::Engine::EagerLoadedCosts.join_costs(scope)

Loading…
Cancel
Save