Class: TaskJuggler::TimeSheetRecord

Inherits:
Object
  • Object
show all
Includes:
MessageHandler
Defined in:
lib/taskjuggler/TimeSheets.rb

Overview

This class holds the work related bits of a time sheet that are specific to a single Task. This can be an existing Task or a new one identified by it’s ID String. For effort based task, it stores the remaining effort, for other task the expected end date. For all tasks it stores the completed work during the reporting time frame.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MessageHandler

#critical, #debug, #error, #fatal, #info, #warning

Constructor Details

#initialize(timeSheet, task) ⇒ TimeSheetRecord

Returns a new instance of TimeSheetRecord.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/taskjuggler/TimeSheets.rb', line 29

def initialize(timeSheet, task)
  # This is a reference to a Task object for existing tasks or an ID as
  # String for new tasks.
  @task = task
  # Add the new TimeSheetRecord to the TimeSheet it belongs to.
  (@timeSheet = timeSheet) << self
  # Work done will be measured in time slots.
  @work = nil
  # Remaining work will be measured in time slots.
  @remaining = nil
  @expectedEnd = nil
  # For new task, we also need to store the name.
  @name = nil
  # Reference to the JournalEntry object that holds the status for this
  # record.
  @status = nil
  @priority = 0
  @sourceFileInfo = nil
end

Instance Attribute Details

#expectedEndObject

Returns the value of attribute expectedEnd.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def expectedEnd
  @expectedEnd
end

#nameObject

Returns the value of attribute name.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def name
  @name
end

#priorityObject

Returns the value of attribute priority.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def priority
  @priority
end

#remainingObject

Returns the value of attribute remaining.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def remaining
  @remaining
end

#sourceFileInfoObject

Returns the value of attribute sourceFileInfo.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def sourceFileInfo
  @sourceFileInfo
end

#statusObject

Returns the value of attribute status.



26
27
28
# File 'lib/taskjuggler/TimeSheets.rb', line 26

def status
  @status
end

#taskObject (readonly)

Returns the value of attribute task.



25
26
27
# File 'lib/taskjuggler/TimeSheets.rb', line 25

def task
  @task
end

#workObject

Returns the value of attribute work.



25
26
27
# File 'lib/taskjuggler/TimeSheets.rb', line 25

def work
  @work
end

Instance Method Details

#actualEndObject

The reported expected end of the task.



227
228
229
# File 'lib/taskjuggler/TimeSheets.rb', line 227

def actualEnd
  @expectedEnd
end

#actualRemainingObject

The reporting remaining effort in days.



211
212
213
214
# File 'lib/taskjuggler/TimeSheets.rb', line 211

def actualRemaining
  project = @timeSheet.resource.project
  project.convertToDailyLoad(@remaining * project['scheduleGranularity'])
end

#actualWorkPercentObject

The reported work in % (0.0 - 100.0) of the average working time.



194
195
196
# File 'lib/taskjuggler/TimeSheets.rb', line 194

def actualWorkPercent
  (@work.to_f / @timeSheet.totalGrossWorkingSlots) * 100.0
end

#checkObject

Perform all kinds of consistency checks.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/taskjuggler/TimeSheets.rb', line 62

def check
  scIdx = @timeSheet.scenarioIdx
  taskId = @task.is_a?(Task) ? @task.fullId : @task
  # All TimeSheetRecords must have a 'work' attribute.
  if @work.nil?
    error('ts_no_work',
          "The time sheet record for task #{taskId} must " +
          "have a 'work' attribute to specify how much was done " +
          "for this task during the reported period.")
  end
  if @task.is_a?(Task)
    # This is already known tasks.
    if @task['effort', scIdx] > 0
      unless @remaining
        error('ts_no_remaining',
              "The time sheet record for task #{taskId} must " +
              "have a 'remaining' attribute to specify how much " +
              "effort is left for this task.")
      end
    else
      unless @expectedEnd
        error('ts_no_expected_end',
              "The time sheet record for task #{taskId} must " +
              "have an 'end' attribute to specify the expected end " +
              "of this task.")
      end
    end
  else
    # This is for new tasks.
    if @remaining.nil? && @expectedEnd.nil?
      error('ts_no_rem_or_end',
            "New task #{taskId} requires either a 'remaining' or a " +
            "'end' attribute.")
    end
  end

  if @work >= @timeSheet.daysToSlots(1) && @status.nil?
    error('ts_no_status_work',
          "You must specify a status for task #{taskId}. It was worked " +
          "on for a day or more.")
  end

  if @status
    if @status.headline.empty?
      error('ts_no_headline',
            "You must provide a headline for the status of " +
            "task #{taskId}")
    end
    if @status.summary &&
      @status.summary.richText.inputText == "A summary text\n"
      error('ts_default_summary',
            "You must change the default summary text of the status " +
            "for task #{taskId}.")
    end
    if @status.alertLevel > 0 && @status.summary.nil? &&
       @status.details.nil?
      error('ts_alert1_more_details',
            "Task #{taskId} has an elevated alert level and must " +
            "have a summary or details section.")
    end
    if @status.alertLevel > 1 && @status.details.nil?
      error('ts_alert2_more_details',
            "Task #{taskId} has a high alert level and must have " +
            "a details section.")
    end
  end
end

#planEndObject

The planned end of the task.



232
233
234
# File 'lib/taskjuggler/TimeSheets.rb', line 232

def planEnd
  @task['end', @timeSheet.scenarioIdx]
end

#planRemainingObject

The remaining effort according to the plan.



217
218
219
220
221
222
223
224
# File 'lib/taskjuggler/TimeSheets.rb', line 217

def planRemaining
  resource = @timeSheet.resource
  project = resource.project
  scenarioIdx = @timeSheet.scenarioIdx
  startIdx = project.dateToIdx(project['now'])
  endIdx = project.dateToIdx(@task['end', scenarioIdx])
  @task.getEffectiveWork(scenarioIdx, startIdx, endIdx, resource)
end

#planWorkPercentObject

The planned work in % (0.0 - 100.0) of the average working time.



199
200
201
202
203
204
205
206
207
208
# File 'lib/taskjuggler/TimeSheets.rb', line 199

def planWorkPercent
  resource = @timeSheet.resource
  project = resource.project
  scenarioIdx = @timeSheet.scenarioIdx
  startIdx = project.dateToIdx(@timeSheet.interval.start)
  endIdx = project.dateToIdx(@timeSheet.interval.end)
  (@timeSheet.resource.getAllocatedSlots(scenarioIdx, startIdx, endIdx,
                                         @task).to_f /
   @timeSheet.totalGrossWorkingSlots) * 100.0
end

#taskIdObject



189
190
191
# File 'lib/taskjuggler/TimeSheets.rb', line 189

def taskId
  @task.is_a?(Task) ? @task.fullId : task
end

#warnOnDelta(startIdx, endIdx) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/taskjuggler/TimeSheets.rb', line 130

def warnOnDelta(startIdx, endIdx)
  # Ignore personal entries.
  return unless @task

  resource = @timeSheet.resource
  if @task.is_a?(String)
    # A resource has requested a new Task to be created.
    warning('ts_res_new_task',
            "#{resource.name} is requesting a new task:\n" +
            "  ID: #{@task}\n" +
            "  Name: #{@name}\n" +
            "  Work: #{@timeSheet.slotsToDays(@work)}d  " +
            (@remaining ?
             "Remaining: #{@timeSheet.slotsToDays(@remaining)}d" :
             "End: #{@end.to_s}"))
    return
  end

  scenarioIdx = @timeSheet.scenarioIdx
  project = resource.project
  plannedWork = @task.getEffectiveWork(scenarioIdx, startIdx, endIdx,
                                       resource)
  # Convert the @work slots into a daily load.
  work = project.convertToDailyLoad(@work * project['scheduleGranularity'])

  if work != plannedWork
    warning('ts_res_work_delta',
            "#{resource.name} worked " +
            "#{work < plannedWork ? 'less' : 'more'} " +
            "on #{@task.fullId}\n" +
            "#{work}d instead of #{plannedWork}d")
  end
  if @task['effort', scenarioIdx] > 0
    startIdx = endIdx
    endIdx = project.dateToIdx(@task['end', scenarioIdx])
    remainingWork = @task.getEffectiveWork(scenarioIdx, startIdx, endIdx,
                                           resource)
    # Convert the @remaining slots into a daily load.
    remaining = project.convertToDailyLoad(@remaining *
                                           project['scheduleGranularity'])
    if remaining != remainingWork
      warning('ts_res_remain_delta',
              "#{resource.name} requests " +
              "#{remaining < remainingWork ? 'less' : 'more'} " +
              "remaining effort for task #{@task.fullId}\n" +
              "#{remaining}d instead of #{remainingWork}d")
    end
  else
    if @expectedEnd != @task['end', scenarioIdx]
      warning('ts_res_end_delta',
              "#{resource.name} requests " +
              "#{@expectedEnd < @task['end', scenarioIdx] ?
                'earlier' : 'later'} end (#{@expectedEnd}) for task " +
              "#{@task.fullId}. Planned end is " +
              "#{@task['end', scenarioIdx]}.")
    end
  end
end