lib/taskjuggler/ResourceScenario.rb in taskjuggler-3.5.0 vs lib/taskjuggler/ResourceScenario.rb in taskjuggler-3.6.0

- old
+ new

@@ -1,12 +1,12 @@ #!/usr/bin/env ruby -w # encoding: UTF-8 # # = ResourceScenario.rb -- The TaskJuggler III Project Management Software # -# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 -# by Chris Schlaeger <chris@linux.com> +# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 +# by Chris Schlaeger <cs@taskjuggler.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # @@ -24,18 +24,11 @@ # nil: Value has not been determined yet. # Task: A reference to a Task object # Bit 0: Reserved # Bit 1: 0: Work time (as defined by working hours) # 1: No work time (as defined by working hours) - # Bit 2 - 5: 0: No holiday or leave time - # 1: Public holiday (holiday) - # 2: Annual leave - # 3: Special leave - # 4: Sick leave - # 5: unpaid leave - # 6: blocked for other projects - # 7 - 15: Reserved + # Bit 2 - 5: See Leave class for acutal values. # Bit 6 - 7: Reserved # Bit 8: 0: No global override # 1: Override global setting # The scoreboard is only created when needed to save memory for projects # which read-in the coporate employee database but only need a small @@ -71,12 +64,10 @@ # This method must be called at the beginning of each scheduling run. It # initializes variables used during the scheduling process. def prepareScheduling @effort = 0 initScoreboard if @property.leaf? - - setDirectReports end # The criticalness of a resource is a measure for the probabilty that all # allocations can be fullfilled. The smaller the value, the more likely # will the tasks get the resource. A value above 1.0 means that @@ -95,21 +86,39 @@ @alloctdeffort / freeSlots end end def setDirectReports - # Only leaf resources have reporting lines. - return unless @property.leaf? + @managers.map! do |managerId| + # First we need to map 'managerId' to an existing Resource. + if (manager = @project.resource(managerId)).nil? + error('resource_id_expected', "#{managerId} is not a defined " + + 'resource.', @sourceFileInfo) + end + unless manager.leaf? + error('manager_is_group', + "Resource #{@property.fullId} has group #{manager.fullId} " + + "assigned as manager. Managers must be leaf resources.") + end + if manager == @property + error('manager_is_self', + "Resource #{@property.fullId} cannot manage itself.") + end - # The 'directreports' attribute is the reverse link for the 'managers' - # attribute. In contrast to the 'managers' attribute, the - # 'directreports' list has no duplicate entries. - @managers.each do |manager| - unless manager['directreports', @scenarioIdx].include?(@property) - manager['directreports', @scenarioIdx] << @property + # Only leaf resources have reporting lines. + if @property.leaf? + # The 'directreports' attribute is the reverse link for the 'managers' + # attribute. In contrast to the 'managers' attribute, the + # 'directreports' list has no duplicate entries. + unless manager['directreports', @scenarioIdx].include?(@property) + manager['directreports', @scenarioIdx] << @property + end end + + manager end + @managers.uniq! end def setReports return unless @directreports.empty? @@ -117,21 +126,10 @@ r.setReports_i(@scenarioIdx, [ @property ]) end end def preScheduleCheck - @managers.each do |manager| - unless manager.leaf? - error('manager_is_group', - "Resource #{@property.fullId} has group #{manager.fullId} " + - "assigned as manager. Managers must be leaf resources.") - end - if manager == @property - error('manager_is_self', - "Resource #{@property.fullId} cannot manage itself.") - end - end end # This method does some housekeeping work after the scheduling is # completed. It's meant to be called for top-level resources and then # recursively descends into all child resources. @@ -385,11 +383,15 @@ @property.kids.each do |resource| resource.query_headcount(@scenarioIdx, query) headcount += query.to_num end else - headcount += @efficiency.round + if employed?(@project.dateToIdx(query.start)) + # We only count headcount that is employed at the start date of the + # query interval. + headcount += @efficiency.round + end end query.sortable = query.numerical = headcount query.string = query.numberFormat.format(headcount) end @@ -890,9 +892,19 @@ if @shifts && @shifts.assigned?(sbIdx) return @shifts.onShift?(sbIdx) else @workinghours.onShift?(sbIdx) end + end + + def employed?(sbIdx) + initScoreboard unless @scoreboard + + val = @scoreboard[sbIdx] + return true unless val.is_a?(Fixnum) + + leave_type = (val >> 2) & 0xF + leave_type < Leave::Types[:unemployed] end # Returns true if the resource or any of its children is allocated during # the period specified with _startIdx_ and _endIdx_. If task is not nil # only allocations to this tasks are respected.