taskjuggler 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. data/CHANGELOG +30 -0
  2. data/README.rdoc +1 -0
  3. data/data/tjp.vim +13 -9
  4. data/lib/taskjuggler/Allocation.rb +2 -2
  5. data/lib/taskjuggler/AttributeBase.rb +10 -0
  6. data/lib/taskjuggler/Attributes.rb +2 -8
  7. data/lib/taskjuggler/Journal.rb +20 -19
  8. data/lib/taskjuggler/PTNProxy.rb +14 -1
  9. data/lib/taskjuggler/ProjectFileParser.rb +3 -0
  10. data/lib/taskjuggler/ProjectFileScanner.rb +54 -44
  11. data/lib/taskjuggler/PropertyList.rb +32 -0
  12. data/lib/taskjuggler/PropertyTreeNode.rb +4 -0
  13. data/lib/taskjuggler/ResourceScenario.rb +53 -20
  14. data/lib/taskjuggler/RichText/Document.rb +7 -5
  15. data/lib/taskjuggler/RichText/Scanner.rb +38 -38
  16. data/lib/taskjuggler/RichText/TOCEntry.rb +1 -1
  17. data/lib/taskjuggler/TaskScenario.rb +78 -62
  18. data/lib/taskjuggler/TextParser.rb +4 -3
  19. data/lib/taskjuggler/TextParser/MacroTable.rb +3 -1
  20. data/lib/taskjuggler/TextParser/Scanner.rb +73 -58
  21. data/lib/taskjuggler/Tj3Config.rb +1 -1
  22. data/lib/taskjuggler/TjpSyntaxRules.rb +215 -67
  23. data/lib/taskjuggler/apps/Tj3Client.rb +1 -1
  24. data/lib/taskjuggler/daemon/ReportServer.rb +8 -1
  25. data/lib/taskjuggler/reports/ExportRE.rb +46 -0
  26. data/lib/taskjuggler/reports/MspXmlRE.rb +409 -0
  27. data/lib/taskjuggler/reports/Report.rb +29 -4
  28. data/lib/taskjuggler/reports/ReportBase.rb +0 -8
  29. data/lib/taskjuggler/reports/TableReport.rb +13 -3
  30. data/lib/taskjuggler/reports/TaskListRE.rb +1 -0
  31. data/lib/taskjuggler/reports/TjpExportRE.rb +1 -1
  32. data/manual/Rich_Text_Attributes +2 -2
  33. data/manual/Tutorial +15 -11
  34. data/manual/html/Day_To_Day_Juggling.html +1 -1
  35. data/manual/html/Getting_Started.html +1 -1
  36. data/manual/html/How_To_Contribute.html +1 -1
  37. data/manual/html/Installation.html +1 -1
  38. data/manual/html/Intro.html +1 -1
  39. data/manual/html/Reporting_Bugs.html +1 -1
  40. data/manual/html/Rich_Text_Attributes.html +1 -1
  41. data/manual/html/Software.html +1 -1
  42. data/manual/html/TaskJuggler_2x_Migration.html +1 -1
  43. data/manual/html/TaskJuggler_Internals.html +1 -1
  44. data/manual/html/The_TaskJuggler_Syntax.html +1 -1
  45. data/manual/html/Tutorial.html +5 -2
  46. data/manual/html/account.html +1 -1
  47. data/manual/html/account.task.html +1 -1
  48. data/manual/html/accountprefix.html +1 -1
  49. data/manual/html/accountreport.html +9 -3
  50. data/manual/html/accountroot.html +1 -1
  51. data/manual/html/active.html +1 -1
  52. data/manual/html/adopt.task.html +3 -3
  53. data/manual/html/aggregate.html +1 -1
  54. data/manual/html/alert.html +1 -1
  55. data/manual/html/alertlevels.html +1 -1
  56. data/manual/html/allocate.html +5 -2
  57. data/manual/html/alphabet.html +1 -1
  58. data/manual/html/alternative.html +1 -1
  59. data/manual/html/author.html +1 -1
  60. data/manual/html/balance.html +1 -1
  61. data/manual/html/booking.resource.html +1 -1
  62. data/manual/html/booking.task.html +1 -1
  63. data/manual/html/caption.html +1 -1
  64. data/manual/html/cellcolor.column.html +1 -1
  65. data/manual/html/celltext.column.html +43 -4
  66. data/manual/html/center.html +1 -1
  67. data/manual/html/charge.html +1 -1
  68. data/manual/html/chargeset.html +2 -2
  69. data/manual/html/columnid.html +6 -6
  70. data/manual/html/columns.html +1 -1
  71. data/manual/html/complete.html +1 -1
  72. data/manual/html/copyright.html +1 -1
  73. data/manual/html/credits.html +1 -1
  74. data/manual/html/currency.html +1 -1
  75. data/manual/html/currencyformat.html +1 -1
  76. data/manual/html/dailymax.html +1 -1
  77. data/manual/html/dailymin.html +1 -1
  78. data/manual/html/dailyworkinghours.html +1 -1
  79. data/manual/html/date.extend.html +1 -1
  80. data/manual/html/date.html +2 -2
  81. data/manual/html/definitions.html +1 -1
  82. data/manual/html/depends.html +1 -1
  83. data/manual/html/details.html +1 -1
  84. data/manual/html/disabled.html +1 -1
  85. data/manual/html/duration.html +1 -1
  86. data/manual/html/efficiency.html +1 -1
  87. data/manual/html/effort.html +1 -1
  88. data/manual/html/email.html +1 -1
  89. data/manual/html/enabled.html +1 -1
  90. data/manual/html/end.column.html +1 -1
  91. data/manual/html/end.html +1 -1
  92. data/manual/html/end.limit.html +1 -1
  93. data/manual/html/end.report.html +9 -3
  94. data/manual/html/end.timesheet.html +1 -1
  95. data/manual/html/endcredit.html +1 -1
  96. data/manual/html/epilog.html +1 -1
  97. data/manual/html/export.html +73 -5
  98. data/manual/html/extend.html +1 -1
  99. data/manual/html/fail.html +1 -1
  100. data/manual/html/fdl.html +1 -1
  101. data/manual/html/flags.account.html +1 -1
  102. data/manual/html/flags.html +1 -1
  103. data/manual/html/flags.journalentry.html +1 -1
  104. data/manual/html/flags.report.html +1 -1
  105. data/manual/html/flags.resource.html +1 -1
  106. data/manual/html/flags.statussheet.html +1 -1
  107. data/manual/html/flags.task.html +1 -1
  108. data/manual/html/flags.timesheet.html +1 -1
  109. data/manual/html/fontcolor.column.html +1 -1
  110. data/manual/html/footer.html +1 -1
  111. data/manual/html/formats.export.html +72 -0
  112. data/manual/html/formats.html +3 -3
  113. data/manual/html/functions.html +3 -3
  114. data/manual/html/gapduration.html +2 -2
  115. data/manual/html/gaplength.html +1 -1
  116. data/manual/html/halign.center.html +1 -1
  117. data/manual/html/halign.column.html +1 -1
  118. data/manual/html/halign.left.html +1 -1
  119. data/manual/html/halign.right.html +1 -1
  120. data/manual/html/hasalert.html +1 -1
  121. data/manual/html/header.html +1 -1
  122. data/manual/html/headline.html +1 -1
  123. data/manual/html/height.html +1 -1
  124. data/manual/html/hideaccount.html +1 -1
  125. data/manual/html/hidejournalentry.html +1 -1
  126. data/manual/html/hidereport.html +1 -1
  127. data/manual/html/hideresource.html +1 -1
  128. data/manual/html/hidetask.html +1 -1
  129. data/manual/html/icalreport.html +1 -1
  130. data/manual/html/include.macro.html +1 -1
  131. data/manual/html/include.project.html +1 -1
  132. data/manual/html/include.properties.html +1 -1
  133. data/manual/html/index.html +1 -1
  134. data/manual/html/inherit.extend.html +1 -1
  135. data/manual/html/interval1.html +2 -2
  136. data/manual/html/interval2.html +2 -2
  137. data/manual/html/interval3.html +2 -2
  138. data/manual/html/interval4.html +2 -2
  139. data/manual/html/isactive.html +1 -1
  140. data/manual/html/ischildof.html +1 -1
  141. data/manual/html/isdependencyof.html +1 -1
  142. data/manual/html/isdutyof.html +1 -1
  143. data/manual/html/isfeatureof.html +1 -1
  144. data/manual/html/isleaf.html +1 -1
  145. data/manual/html/ismilestone.html +1 -1
  146. data/manual/html/isongoing.html +1 -1
  147. data/manual/html/isresource.html +1 -1
  148. data/manual/html/isresponsibilityof.html +1 -1
  149. data/manual/html/istask.html +1 -1
  150. data/manual/html/journalattributes.html +1 -1
  151. data/manual/html/journalentry.html +1 -1
  152. data/manual/html/journalmode.html +11 -3
  153. data/manual/html/leaveallowance.html +1 -1
  154. data/manual/html/leaves.html +1 -1
  155. data/manual/html/left.html +1 -1
  156. data/manual/html/length.html +1 -1
  157. data/manual/html/limits.allocate.html +1 -1
  158. data/manual/html/limits.html +1 -1
  159. data/manual/html/limits.resource.html +1 -1
  160. data/manual/html/limits.task.html +1 -1
  161. data/manual/html/listitem.column.html +1 -1
  162. data/manual/html/listtype.column.html +1 -1
  163. data/manual/html/loadunit.html +2 -2
  164. data/manual/html/logicalexpression.html +1 -1
  165. data/manual/html/logicalflagexpression.html +1 -1
  166. data/manual/html/macro.html +1 -1
  167. data/manual/html/managers.html +1 -1
  168. data/manual/html/mandatory.html +1 -1
  169. data/manual/html/maxend.html +1 -1
  170. data/manual/html/maximum.html +1 -1
  171. data/manual/html/maxstart.html +1 -1
  172. data/manual/html/milestone.html +1 -1
  173. data/manual/html/minend.html +1 -1
  174. data/manual/html/minimum.html +1 -1
  175. data/manual/html/minstart.html +1 -1
  176. data/manual/html/monthlymax.html +1 -1
  177. data/manual/html/monthlymin.html +1 -1
  178. data/manual/html/navbar.html +5 -1
  179. data/manual/html/navigator.html +1 -1
  180. data/manual/html/newtask.html +1 -1
  181. data/manual/html/nikureport.html +1 -1
  182. data/manual/html/note.task.html +1 -1
  183. data/manual/html/now.html +1 -1
  184. data/manual/html/numberformat.html +1 -1
  185. data/manual/html/onend.html +1 -1
  186. data/manual/html/onstart.html +1 -1
  187. data/manual/html/opennodes.html +1 -1
  188. data/manual/html/overtime.booking.html +1 -1
  189. data/manual/html/period.column.html +1 -1
  190. data/manual/html/period.limit.html +1 -1
  191. data/manual/html/period.report.html +1 -1
  192. data/manual/html/period.task.html +1 -1
  193. data/manual/html/persistent.html +1 -1
  194. data/manual/html/precedes.html +1 -1
  195. data/manual/html/priority.html +1 -1
  196. data/manual/html/priority.timesheet.html +1 -1
  197. data/manual/html/project.html +1 -1
  198. data/manual/html/projectid.html +1 -1
  199. data/manual/html/projectid.task.html +1 -1
  200. data/manual/html/projectids.html +1 -1
  201. data/manual/html/projection.html +1 -1
  202. data/manual/html/prolog.html +1 -1
  203. data/manual/html/properties.html +1 -1
  204. data/manual/html/purge.html +8 -5
  205. data/manual/html/rate.html +1 -1
  206. data/manual/html/rate.resource.html +1 -1
  207. data/manual/html/reference.extend.html +1 -1
  208. data/manual/html/remaining.html +1 -1
  209. data/manual/html/replace.html +1 -1
  210. data/manual/html/reportprefix.html +1 -1
  211. data/manual/html/resource.html +1 -1
  212. data/manual/html/resourceattributes.html +1 -1
  213. data/manual/html/resourceprefix.html +1 -1
  214. data/manual/html/resourcereport.html +9 -3
  215. data/manual/html/resourceroot.html +1 -1
  216. data/manual/html/resources.limit.html +1 -1
  217. data/manual/html/responsible.html +1 -1
  218. data/manual/html/richtext.extend.html +1 -1
  219. data/manual/html/right.html +1 -1
  220. data/manual/html/rollupaccount.html +1 -1
  221. data/manual/html/rollupresource.html +1 -1
  222. data/manual/html/rolluptask.html +1 -1
  223. data/manual/html/scale.column.html +1 -1
  224. data/manual/html/scenario.html +1 -1
  225. data/manual/html/scenario.ical.html +1 -1
  226. data/manual/html/scenarios.export.html +1 -1
  227. data/manual/html/scenarios.html +1 -1
  228. data/manual/html/scenariospecific.extend.html +1 -1
  229. data/manual/html/scheduled.html +2 -2
  230. data/manual/html/scheduling.html +1 -1
  231. data/manual/html/select.html +1 -1
  232. data/manual/html/selfcontained.html +1 -1
  233. data/manual/html/shift.allocate.html +1 -1
  234. data/manual/html/shift.html +1 -1
  235. data/manual/html/shift.resource.html +1 -1
  236. data/manual/html/shift.task.html +1 -1
  237. data/manual/html/shift.timesheet.html +1 -1
  238. data/manual/html/shifts.allocate.html +1 -1
  239. data/manual/html/shifts.resource.html +1 -1
  240. data/manual/html/shifts.task.html +1 -1
  241. data/manual/html/shorttimeformat.html +1 -1
  242. data/manual/html/sloppy.booking.html +1 -1
  243. data/manual/html/sloppy.projection.html +1 -1
  244. data/manual/html/sortaccounts.html +1 -1
  245. data/manual/html/sortjournalentries.html +1 -1
  246. data/manual/html/sortresources.html +1 -1
  247. data/manual/html/sorttasks.html +1 -1
  248. data/manual/html/start.column.html +1 -1
  249. data/manual/html/start.html +1 -1
  250. data/manual/html/start.limit.html +1 -1
  251. data/manual/html/start.report.html +1 -1
  252. data/manual/html/startcredit.html +1 -1
  253. data/manual/html/status.statussheet.html +1 -1
  254. data/manual/html/status.timesheet.html +1 -1
  255. data/manual/html/statussheet.html +1 -1
  256. data/manual/html/statussheetreport.html +1 -1
  257. data/manual/html/strict.projection.html +1 -1
  258. data/manual/html/summary.html +1 -1
  259. data/manual/html/supplement.html +1 -1
  260. data/manual/html/supplement.resource.html +1 -1
  261. data/manual/html/supplement.task.html +1 -1
  262. data/manual/html/tagfile.html +1 -1
  263. data/manual/html/task.html +1 -1
  264. data/manual/html/task.statussheet.html +1 -1
  265. data/manual/html/task.timesheet.html +1 -1
  266. data/manual/html/taskattributes.html +1 -1
  267. data/manual/html/taskprefix.html +1 -1
  268. data/manual/html/taskreport.html +9 -3
  269. data/manual/html/taskroot.export.html +104 -0
  270. data/manual/html/taskroot.html +3 -3
  271. data/manual/html/text.extend.html +3 -3
  272. data/manual/html/textreport.html +9 -3
  273. data/manual/html/timeformat.html +1 -1
  274. data/manual/html/timeoff.nikureport.html +1 -1
  275. data/manual/html/timesheet.html +1 -1
  276. data/manual/html/timesheetreport.html +1 -1
  277. data/manual/html/timezone.export.html +1 -1
  278. data/manual/html/timezone.html +1 -1
  279. data/manual/html/timezone.report.html +1 -1
  280. data/manual/html/timezone.shift.html +1 -1
  281. data/manual/html/timingresolution.html +1 -1
  282. data/manual/html/title.column.html +1 -1
  283. data/manual/html/title.html +1 -1
  284. data/manual/html/toc.html +203 -189
  285. data/manual/html/tooltip.column.html +1 -1
  286. data/manual/html/tracereport.html +9 -3
  287. data/manual/html/trackingscenario.html +1 -1
  288. data/manual/html/treelevel.html +1 -1
  289. data/manual/html/vacation.html +1 -1
  290. data/manual/html/vacation.resource.html +1 -1
  291. data/manual/html/vacation.shift.html +1 -1
  292. data/manual/html/warn.html +1 -1
  293. data/manual/html/weeklymax.html +1 -1
  294. data/manual/html/weeklymin.html +1 -1
  295. data/manual/html/weekstartsmonday.html +1 -1
  296. data/manual/html/weekstartssunday.html +1 -1
  297. data/manual/html/width.column.html +1 -1
  298. data/manual/html/width.html +1 -1
  299. data/manual/html/work.html +1 -1
  300. data/manual/html/workinghours.project.html +1 -1
  301. data/manual/html/workinghours.resource.html +1 -1
  302. data/manual/html/workinghours.shift.html +1 -1
  303. data/manual/html/yearlyworkingdays.html +1 -1
  304. data/test/TestSuite/CSV-Reports/project-1.tji +0 -6
  305. data/test/TestSuite/CSV-Reports/refs/alert.csv +0 -7
  306. data/test/TestSuite/CSV-Reports/refs/celltext.csv +0 -7
  307. data/test/TestSuite/CSV-Reports/refs/resourcereport_with_tasks.csv +0 -3
  308. data/test/TestSuite/CSV-Reports/refs/sortByTree.csv +0 -7
  309. data/test/TestSuite/CSV-Reports/refs/sortBy_duration.down.csv +0 -7
  310. data/test/TestSuite/CSV-Reports/refs/sortBy_effort.up.csv +0 -7
  311. data/test/TestSuite/CSV-Reports/refs/sortBy_plan.start.down.csv +0 -7
  312. data/test/TestSuite/CSV-Reports/refs/taskreport.csv +0 -7
  313. data/test/TestSuite/CSV-Reports/refs/taskreport_with_resources.csv +0 -18
  314. data/test/TestSuite/CSV-Reports/refs/weekly.csv +0 -1
  315. data/test/TestSuite/HTML-Reports/Alerts-2.tjp +46 -0
  316. data/test/TestSuite/Scheduler/Correct/purge.tjp +30 -0
  317. data/test/TestSuite/Syntax/Correct/Export.tjp +8 -2
  318. data/test/TestSuite/Syntax/Correct/tutorial.tjp +0 -1
  319. metadata +16 -10
  320. data/test/TestSuite/CSV-Reports/refs/taskcounter.csv +0 -9
@@ -12,6 +12,7 @@
12
12
  #
13
13
 
14
14
  require 'taskjuggler/PTNProxy'
15
+ require 'taskjuggler/MessageHandler'
15
16
 
16
17
  class TaskJuggler
17
18
 
@@ -24,6 +25,8 @@ class TaskJuggler
24
25
  # PropertySet.
25
26
  class PropertyList
26
27
 
28
+ include MessageHandler
29
+
27
30
  attr_writer :query
28
31
  attr_reader :propertySet, :query, :sortingLevels, :sortingCriteria,
29
32
  :sortingUp, :scenarioIdx
@@ -72,6 +75,34 @@ class TaskJuggler
72
75
  append(adopted)
73
76
  end
74
77
 
78
+ # Make sure that the list does not contain the same PropertyTreeNode more
79
+ # than once. This could happen for adopted tasks. If you use
80
+ # includeAdopted(), you should call this method after filtering to see if
81
+ # the filter was strict enough.
82
+ def checkForDuplicates(sourceFileInfo)
83
+ ptns = {}
84
+ @items.each do |i|
85
+ if ptns.include?(i.ptn)
86
+ error('proplist_duplicate',
87
+ "An adopted property is included as #{i.logicalId} and " +
88
+ "as #{ptns[i.ptn].logicalId}. Please use stronger filtering " +
89
+ 'to avoid including the property more than once!',
90
+ sourceFileInfo)
91
+ end
92
+ ptns[i.ptn] = i
93
+ end
94
+ end
95
+
96
+
97
+ # Specialized version of Array::include? that also matches adopted tasks.
98
+ def include?(node)
99
+ !@items.find { |p| p.ptn == node.ptn }.nil?
100
+ end
101
+
102
+ def [](node)
103
+ @items.find { |n| n.ptn == node.ptn }
104
+ end
105
+
75
106
  def to_ary
76
107
  @items.dup
77
108
  end
@@ -104,6 +135,7 @@ class TaskJuggler
104
135
  end
105
136
 
106
137
  @items.concat(list)
138
+ raise "Duplicate items" if @items != @items.uniq
107
139
  sort!
108
140
  end
109
141
 
@@ -283,6 +283,10 @@ class TaskJuggler
283
283
  res
284
284
  end
285
285
 
286
+ def logicalId
287
+ fullId
288
+ end
289
+
286
290
  # Return the full id of this node. For PropertySet objects with a flat
287
291
  # namespace, this is just the ID. Otherwise, the full ID is composed of all
288
292
  # IDs from the root node to this node, separating the IDs by a dot.
@@ -184,13 +184,8 @@ class TaskJuggler
184
184
  #puts "Booking resource #{@property.fullId} at " +
185
185
  # "#{@scoreboard.idxToDate(sbIdx)}/#{sbIdx} for task #{task.fullId}\n"
186
186
  @scoreboard[sbIdx] = task
187
- # Track the total allocated slots for this resource and all parent
188
- # resources.
189
- t = @property
190
- while t
191
- t['effort', @scenarioIdx] += 1
192
- t = t.parent
193
- end
187
+ # Track the total allocated slots for this resource.
188
+ @effort += @efficiency
194
189
  @limits.inc(sbIdx) if @limits
195
190
 
196
191
  # Scoreboard iterations are fairly expensive but they are very frequent
@@ -198,19 +193,16 @@ class TaskJuggler
198
193
  # relevant intervals, we store the interval for all bookings and for
199
194
  # each individual task.
200
195
  if @firstBookedSlot.nil? || @firstBookedSlot > sbIdx
201
- @firstBookedSlot = sbIdx
196
+ @firstBookedSlot = @firstBookedSlots[task] = sbIdx
197
+ elsif @firstBookedSlots[task].nil? || @firstBookedSlots[task] > sbIdx
198
+ @firstBookedSlots[task] = sbIdx
202
199
  end
203
200
  if @lastBookedSlot.nil? || @lastBookedSlot < sbIdx
204
- @lastBookedSlot = sbIdx
205
- end
206
- if task
207
- if @firstBookedSlots[task].nil? || @firstBookedSlots[task] > sbIdx
208
- @firstBookedSlots[task] = sbIdx
209
- end
210
- if @lastBookedSlots[task].nil? || @lastBookedSlots[task] < sbIdx
211
- @lastBookedSlots[task] = sbIdx
212
- end
201
+ @lastBookedSlot = @lastBookedSlots[task] = sbIdx
202
+ elsif @lastBookedSlots[task].nil? || @lastBookedSlots[task] < sbIdx
203
+ @lastBookedSlots[task] = sbIdx
213
204
  end
205
+
214
206
  true
215
207
  end
216
208
 
@@ -253,6 +245,21 @@ class TaskJuggler
253
245
  book(sbIdx, booking.task, true)
254
246
  end
255
247
 
248
+ # @effort only trackes the already allocated effort for leaf resources. It's
249
+ # too expensive to propagate this to the group resources on every booking.
250
+ # If a value for a group effort is needed, it's computed here.
251
+ def bookedEffort
252
+ if @property.leaf?
253
+ @effort
254
+ else
255
+ effort = 0
256
+ @property.kids.each do |r|
257
+ effort += r.bookedEffort(@scenarioIdx)
258
+ end
259
+ effort
260
+ end
261
+ end
262
+
256
263
  # Compute the annual leave days within the period specified by the
257
264
  # _query_. The result is in days.
258
265
  def query_annualleave(query)
@@ -296,6 +303,31 @@ class TaskJuggler
296
303
  query.string = query.scaleLoad(effort)
297
304
  end
298
305
 
306
+ # The completed (as of 'now') effort allocated for the resource in the
307
+ # specified interval. In case a Task is given as scope property only
308
+ # the effort allocated for this Task is taken into account.
309
+ def query_effortdone(query)
310
+ # For this query, we always override the query period.
311
+ query.sortable = query.numerical = effort =
312
+ getEffectiveWork(@project.dateToIdx(@project['start'], false),
313
+ @project.dateToIdx(@project['now']),
314
+ query.scopeProperty)
315
+ query.string = query.scaleLoad(effort)
316
+ end
317
+
318
+
319
+ # The remaining (as of 'now') effort allocated for the resource in the
320
+ # specified interval. In case a Task is given as scope property only
321
+ # the effort allocated for this Task is taken into account.
322
+ def query_effortleft(query)
323
+ # For this query, we always override the query period.
324
+ query.sortable = query.numerical = effort =
325
+ getEffectiveWork(@project.dateToIdx(@project['now']),
326
+ @project.dateToIdx(@project['end'], false),
327
+ query.scopeProperty)
328
+ query.string = query.scaleLoad(effort)
329
+ end
330
+
299
331
  # The unallocated work time of the Resource during the specified interval.
300
332
  def query_freetime(query)
301
333
  query.sortable = query.numerical = time =
@@ -449,9 +481,10 @@ class TaskJuggler
449
481
  # There can't be any effective work if the start is after the end or the
450
482
  # todo list doesn't contain the specified task.
451
483
  return 0.0 if startIdx >= endIdx || (task && !@duties.include?(task))
484
+ # Temporary workaround until @duties is fixed again.
452
485
 
453
486
  # The unique key we use to address the result in the cache.
454
- @dCache.cached(self, :ResourceScenarioEffectiveWork, startIdx, endIdx,
487
+ @dCache.cached(self, :ResourceScenarioGetEffectiveWork, startIdx, endIdx,
455
488
  task) do
456
489
  work = 0.0
457
490
  if @property.container?
@@ -516,14 +549,14 @@ class TaskJuggler
516
549
  end
517
550
  else
518
551
  if task
519
- # If we have a know task, we only include the amount that is
552
+ # If we have a known task, we only include the amount that is
520
553
  # specific to this resource, this task and the chargeset of the
521
554
  # task.
522
555
  amount += task.turnover(@scenarioIdx, startIdx, endIdx, account,
523
556
  @property)
524
557
  elsif !@chargeset.empty?
525
558
  # If no tasks was provided, we include the amount of this resource,
526
- # weighted by the charset of this resource.
559
+ # weighted by the chargeset of this resource.
527
560
  totalResourceCost = cost(startIdx, endIdx)
528
561
  @chargeset.each do |set|
529
562
  set.each do |accnt, share|
@@ -63,14 +63,16 @@ class TaskJuggler
63
63
  @toc = TableOfContents.new
64
64
  @references = {}
65
65
  @anchors = []
66
+ # Collect all file names as potentencial anchors.
66
67
  @snippets.each do |snip|
67
68
  snip.tableOfContents(@toc, snip.name)
68
69
  @anchors << snip.name
69
- @toc.each do |tocEntry|
70
- @anchors << snip.name + '#' + tocEntry.tag
71
- end
72
- (refs = snip.internalReferences).empty? ||
73
- @references[snip.name] = refs
70
+ (refs = snip.internalReferences).empty? || @references[snip.name] = refs
71
+ end
72
+ # Then add all section entries as well. We use the HTML style
73
+ # <file>#<tag> notation.
74
+ @toc.each do |tocEntry|
75
+ @anchors << tocEntry.file + '#' + tocEntry.tag
74
76
  end
75
77
  end
76
78
 
@@ -33,66 +33,66 @@ class TaskJuggler
33
33
  def initialize(masterFile, log)
34
34
  tokenPatterns = [
35
35
  # :bol mode rules
36
- [ :LINEBREAK, /\s*\n/, :bol, method('linebreak') ],
37
- [ nil, /\s+/, :bol, method('inlineMode') ],
36
+ [ :LINEBREAK, '\s*\n', /\s*\n/, :bol, method('linebreak') ],
37
+ [ nil, '\s+', /\s+/, :bol, method('inlineMode') ],
38
38
 
39
39
  # :bop mode rules
40
- [ :PRE, / [^\n]+\n?/, :bop, method('pre') ],
41
- [ nil, /\s*\n/, :bop, method('linebreak') ],
40
+ [ :PRE, ' [^\n]+\n?', / [^\n]+\n?/, :bop, method('pre') ],
41
+ [ nil, '\s*\n', /\s*\n/, :bop, method('linebreak') ],
42
42
 
43
43
  # :inline mode rules
44
- [ :SPACE, /[ \t\n]+/, :inline, method('space') ],
44
+ [ :SPACE, '[ \t\n]+', /[ \t\n]+/, :inline, method('space') ],
45
45
 
46
46
  # :bop and :bol mode rules
47
- [ :INLINEFUNCSTART, /<-/, [ :bop, :bol, :inline ],
47
+ [ :INLINEFUNCSTART, '<-', /<-/, [ :bop, :bol, :inline ],
48
48
  method('functionStart') ],
49
- [ :BLOCKFUNCSTART, /<\[/, [ :bop, :bol ], method('functionStart') ],
50
- [ ':TITLE*', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
51
- [ 'TITLE*END', /={2,5}/, :inline, method('titleEnd') ],
52
- [ 'BULLET*', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
53
- [ 'NUMBER*', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
54
- [ :HLINE, /----/, [ :bop, :bol ], method('inlineMode') ],
49
+ [ :BLOCKFUNCSTART, '<\[', /<\[/, [ :bop, :bol ], method('functionStart') ],
50
+ [ ':TITLE*', '={2,5}', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
51
+ [ 'TITLE*END', '={2,5}', /={2,5}/, :inline, method('titleEnd') ],
52
+ [ 'BULLET*', '\*{1,4}[ \t]+', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
53
+ [ 'NUMBER*', '\#{1,4}[ \t]+', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
54
+ [ :HLINE, '----', /----/, [ :bop, :bol ], method('inlineMode') ],
55
55
 
56
56
  # :bop, :bol and :inline mode rules
57
57
  # The <nowiki> token puts the scanner into :nowiki mode.
58
- [ nil, /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
59
- [ :FCOLSTART, /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
58
+ [ nil, '<nowiki>', /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
59
+ [ :FCOLSTART, '<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>', /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
60
60
  :inline ],
61
61
  method('fontColorStart') ],
62
- [ :FCOLEND, /<\/fcol>/, [ :bop, :bol, :inline ],
62
+ [ :FCOLEND, '<\/fcol>', /<\/fcol>/, [ :bop, :bol, :inline ],
63
63
  method('fontColorEnd') ],
64
- [ :QUOTES, /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
65
- [ :REF, /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
66
- [ :HREF, /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
67
- [ :WORD, /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
64
+ [ :QUOTES, '\'{2,5}', /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
65
+ [ :REF, '\[\[', /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
66
+ [ :HREF, '\[', /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
67
+ [ :WORD, '.[^ \n\t\[<\']*', /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
68
68
  method('inlineMode') ],
69
69
 
70
70
  # :nowiki mode rules
71
- [ nil, /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
72
- [ :WORD, /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
73
- [ :SPACE, /[ \t]+/, :nowiki ],
74
- [ :LINEBREAK, /\s*\n/, :nowiki ],
71
+ [ nil, '<\/nowiki>', /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
72
+ [ :WORD, '(<(?!\/nowiki>)|[^ \t\n<])+', /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
73
+ [ :SPACE, '[ \t]+', /[ \t]+/, :nowiki ],
74
+ [ :LINEBREAK, '\s*\n', /\s*\n/, :nowiki ],
75
75
 
76
76
  # :ref mode rules
77
- [ :REFEND, /\]\]/, :ref, method('refEnd') ],
78
- [ :WORD, /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
79
- [ :QUERY, /<-\w+->/, :ref, method('query') ],
80
- [ :LITERAL, /./, :ref ],
77
+ [ :REFEND, '\]\]', /\]\]/, :ref, method('refEnd') ],
78
+ [ :WORD, '(<(?!-)|(\](?!\])|[^|<\]]))+', /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
79
+ [ :QUERY, '<-\w+->', /<-\w+->/, :ref, method('query') ],
80
+ [ :LITERAL, '.', /./, :ref ],
81
81
 
82
82
  # :href mode rules
83
- [ :HREFEND, /\]/, :href, method('hrefEnd') ],
84
- [ :WORD, /(<(?!-)|[^ \t\n\]<])+/, :href ],
85
- [ :QUERY, /<-\w+->/, :href, method('query') ],
86
- [ :SPACE, /[ \t\n]+/, :href ],
83
+ [ :HREFEND, '\]', /\]/, :href, method('hrefEnd') ],
84
+ [ :WORD, '(<(?!-)|[^ \t\n\]<])+', /(<(?!-)|[^ \t\n\]<])+/, :href ],
85
+ [ :QUERY, '<-\w+->', /<-\w+->/, :href, method('query') ],
86
+ [ :SPACE, '[ \t\n]+', /[ \t\n]+/, :href ],
87
87
 
88
88
  # :func mode rules
89
- [ :INLINEFUNCEND, /->/ , :func, method('functionEnd') ],
90
- [ :BLOCKFUNCEND, /\]>/, :func, method('functionEnd') ],
91
- [ :ID, /[a-zA-Z_]\w*/, :func ],
92
- [ :STRING, /"(\\"|[^"])*"/, :func, method('dqString') ],
93
- [ :STRING, /'(\\'|[^'])*'/, :func, method('sqString') ],
94
- [ nil, /[ \t\n]+/, :func ],
95
- [ :LITERAL, /./, :func ]
89
+ [ :INLINEFUNCEND, '->' , /->/ , :func, method('functionEnd') ],
90
+ [ :BLOCKFUNCEND, '\]>', /\]>/, :func, method('functionEnd') ],
91
+ [ :ID, '[a-zA-Z_]\w*', /[a-zA-Z_]\w*/, :func ],
92
+ [ :STRING, '"(\\\\"|[^"])*"', /"(\\"|[^"])*"/, :func, method('dqString') ],
93
+ [ :STRING, '\'(\\\\\'|[^\'])*\'', /'(\\'|[^'])*'/, :func, method('sqString') ],
94
+ [ nil, '[ \t\n]+', /[ \t\n]+/, :func ],
95
+ [ :LITERAL, '.', /./, :func ]
96
96
  ]
97
97
  super(masterFile, log, tokenPatterns, :bop)
98
98
  end
@@ -22,7 +22,7 @@ class TaskJuggler
22
22
  # be turned into an HTML tree.
23
23
  class TOCEntry
24
24
 
25
- attr_reader :number, :title, :tag
25
+ attr_reader :number, :title, :file, :tag
26
26
 
27
27
  # Create a TOCEntry object.
28
28
  # _number_: The section number as String, e. g. '1.2.3' or 'A.3'.
@@ -18,7 +18,7 @@ class TaskJuggler
18
18
 
19
19
  class TaskScenario < ScenarioData
20
20
 
21
- attr_reader :isRunAway
21
+ attr_reader :isRunAway, :hasDurationSpec
22
22
 
23
23
  # Create a new TaskScenario object.
24
24
  def initialize(task, scenarioIdx, attributes)
@@ -67,19 +67,27 @@ class TaskJuggler
67
67
  @startPropagated = false
68
68
  @endPropagated = false
69
69
 
70
+ @durationType =
71
+ if @effort > 0
72
+ @hasDurationSpec = true
73
+ :effortTask
74
+ elsif @length > 0
75
+ @hasDurationSpec = true
76
+ :lengthTask
77
+ elsif @duration > 0
78
+ @hasDurationSpec = true
79
+ :durationTask
80
+ else
81
+ # If the task is set as milestone is has a duration spec.
82
+ @hasDurationSpec = @milestone
83
+ :startEndTask
84
+ end
85
+
70
86
  markMilestone
71
- # Milestones may only have start or end date even when the 'scheduled'
72
- # attribute is set. For further processing, we need to add the missing
73
- # date.
74
- if @milestone
75
- @end = @start if @start && !@end
76
- @start = @end if !@start && @end
77
- end
78
87
 
79
88
  # For start-end-tasks without allocation, we don't have to do
80
89
  # anything but to set the 'scheduled' flag.
81
- if @start && @end && @effort == 0 && @length == 0 && @duration == 0 &&
82
- @allocate.empty?
90
+ if @durationType == :startEndTask && @start && @end && @allocate.empty?
83
91
  @scheduled = true
84
92
  Log.msg { "Task #{@property.fullId}: #{period_to_s}" }
85
93
  end
@@ -106,16 +114,10 @@ class TaskJuggler
106
114
 
107
115
  bookBookings
108
116
 
109
- @durationType =
110
- if @effort > 0
111
- :effortTask
112
- elsif @length > 0
113
- :lengthTask
114
- elsif @duration > 0
115
- :durationTask
116
- else
117
- :startEndTask
118
- end
117
+ if @durationType == :startEndTask
118
+ @startIdx = @project.dateToIdx(@start) if @start
119
+ @endIdx = @project.dateToIdx(@end) if @end
120
+ end
119
121
  end
120
122
 
121
123
  # The parser only stores the full task IDs for each of the dependencies.
@@ -377,9 +379,11 @@ class TaskJuggler
377
379
  end
378
380
  end
379
381
 
380
- # This list is no longer needed, so let's save some memory. Set it to
382
+ # These lists are no longer needed, so let's save some memory. Set it to
381
383
  # nil so we can detect accidental use.
382
384
  @candidates = nil
385
+ @mandatories = nil
386
+ @allLimits = nil
383
387
  end
384
388
 
385
389
  # This function is not essential but does perform a large number of
@@ -880,9 +884,9 @@ class TaskJuggler
880
884
  return false if @isRunAway
881
885
 
882
886
  if @forward
883
- return true if @start && (hasDurationSpec? || @end)
887
+ return true if @start && (@hasDurationSpec || @end)
884
888
  else
885
- return true if @end && (hasDurationSpec? || @start)
889
+ return true if @end && (@hasDurationSpec || @start)
886
890
  end
887
891
 
888
892
  false
@@ -962,6 +966,10 @@ class TaskJuggler
962
966
  # are only set in scheduleContainer().
963
967
  if @property.leaf?
964
968
  instance_variable_set(('@' + thisEnd).intern, date)
969
+ if @durationType == :startEndTask
970
+ instance_variable_set(('@' + thisEnd + 'Idx').intern,
971
+ @project.dateToIdx(date))
972
+ end
965
973
  Log.msg { "Task #{@property.fullId}: #{period_to_s}" }
966
974
  end
967
975
 
@@ -1053,21 +1061,20 @@ class TaskJuggler
1053
1061
 
1054
1062
  hasThatSpec = !instance_variable_get('@' + thatEnd).nil? ||
1055
1063
  hasStrongDeps?(!atEnd)
1056
- hasDurationSpec = hasDurationSpec?
1057
1064
 
1058
1065
  # Check for tasks that have no start and end spec, no duration spec but
1059
1066
  # allocates. They can inherit the start and end date.
1060
- return true if hasThatSpec && !hasDurationSpec && !@allocate.empty?
1067
+ return true if hasThatSpec && !@hasDurationSpec && !@allocate.empty?
1061
1068
 
1062
1069
  if @forward ^ atEnd
1063
1070
  # the scheduling direction is pointing away from this end
1064
- return true if hasDurationSpec || !@booking.empty?
1071
+ return true if @hasDurationSpec || !@booking.empty?
1065
1072
 
1066
1073
  return hasThatSpec
1067
1074
  else
1068
1075
  # the scheduling direction is pointing towards this end
1069
1076
  return !instance_variable_get('@' + thatEnd).nil? &&
1070
- !hasDurationSpec && @booking.empty? #&& @allocate.empty?
1077
+ !@hasDurationSpec && @booking.empty? #&& @allocate.empty?
1071
1078
  end
1072
1079
  end
1073
1080
 
@@ -1117,11 +1124,6 @@ class TaskJuggler
1117
1124
  propagateDate(nEnd, true) if endSet
1118
1125
  end
1119
1126
 
1120
- # Return true if the task has a effort, length or duration setting.
1121
- def hasDurationSpec?
1122
- @length > 0 || @duration > 0 || @effort > 0 || @milestone
1123
- end
1124
-
1125
1127
  # Find the earliest possible start date for the task. This date must be
1126
1128
  # after the end date of all the task that this task depends on.
1127
1129
  # Dependencies may also require a minimum gap between the tasks.
@@ -1669,7 +1671,8 @@ class TaskJuggler
1669
1671
 
1670
1672
  # Gather a list of Resource objects that have been assigned to the task
1671
1673
  # (including sub tasks) for the given Interval _interval_.
1672
- def assignedResources(interval)
1674
+ def assignedResources(interval = nil)
1675
+ interval = Interval.new(a('start'), a('end')) unless interval
1673
1676
  list = []
1674
1677
 
1675
1678
  if @property.container?
@@ -1746,9 +1749,8 @@ class TaskJuggler
1746
1749
 
1747
1750
  # Depending on the scheduling direction we can mark the task as
1748
1751
  # scheduled once we have reached the other end.
1749
- currentSlot = @project.idxToDate(@currentSlotIdx)
1750
- if (@forward && currentSlot + @project['scheduleGranularity'] >= @end) ||
1751
- (!@forward && currentSlot <= @start)
1752
+ if (@forward && @currentSlotIdx >= @endIdx) |
1753
+ (!@forward && @currentSlotIdx <= @startIdx)
1752
1754
  @scheduled = true
1753
1755
  @property.parents.each do |parent|
1754
1756
  parent.scheduleContainer(@scenarioIdx)
@@ -1920,6 +1922,7 @@ class TaskJuggler
1920
1922
  error('booking_forward_only',
1921
1923
  "Only forward scheduled tasks may have booking statements.")
1922
1924
  end
1925
+ booked = false
1923
1926
  booking.intervals.each do |interval|
1924
1927
  startIdx = @project.dateToIdx(interval.start, false)
1925
1928
  endIdx = @project.dateToIdx(interval.end, false)
@@ -1927,18 +1930,18 @@ class TaskJuggler
1927
1930
  if booking.resource.bookBooking(@scenarioIdx, idx, booking)
1928
1931
  # Booking was successful for this time slot.
1929
1932
  @doneEffort += booking.resource['efficiency', @scenarioIdx]
1933
+ booked = true
1930
1934
 
1931
1935
  # Store the indexes of the first slot and the slot after the
1932
1936
  # last slot.
1933
1937
  firstSlotIdx = idx if !firstSlotIdx || firstSlotIdx > idx
1934
1938
  lastSlotIdx = idx if !lastSlotIdx || lastSlotIdx < idx
1935
-
1936
- unless @assignedresources.include?(booking.resource)
1937
- @assignedresources << booking.resource
1938
- end
1939
1939
  end
1940
1940
  end
1941
1941
  end
1942
+ if booked && !@assignedresources.include?(booking.resource)
1943
+ @assignedresources << booking.resource
1944
+ end
1942
1945
  end
1943
1946
 
1944
1947
  # For effort based tasks, or tasks without a start date, with bookings
@@ -2094,7 +2097,7 @@ class TaskJuggler
2094
2097
  # This function determines if a task is a milestones and marks it
2095
2098
  # accordingly.
2096
2099
  def markMilestone
2097
- return if @property.container? || hasDurationSpec? ||
2100
+ return if @property.container? || @hasDurationSpec ||
2098
2101
  !@booking.empty? || !@allocate.empty?
2099
2102
 
2100
2103
  # The following cases qualify for an automatic milestone promotion.
@@ -2112,6 +2115,15 @@ class TaskJuggler
2112
2115
  @milestone = (hasStartSpec && @forward && !hasEndSpec) ||
2113
2116
  (!hasStartSpec && !@forward && hasEndSpec) ||
2114
2117
  (!hasStartSpec && !hasEndSpec)
2118
+
2119
+ # Milestones may only have start or end date even when the 'scheduled'
2120
+ # attribute is set. For further processing, we need to add the missing
2121
+ # date.
2122
+ if @milestone
2123
+ @hasDurationSpec = true
2124
+ @end = @start if @start && !@end
2125
+ @start = @end if !@start && @end
2126
+ end
2115
2127
  end
2116
2128
 
2117
2129
  def checkDependency(dependency, depType)
@@ -2180,7 +2192,7 @@ class TaskJuggler
2180
2192
 
2181
2193
  # Don't propagate if the task has a duration or is a milestone and the
2182
2194
  # task end to set is in the scheduling direction.
2183
- return if task.hasDurationSpec?(@scenarioIdx) &&
2195
+ return if task.hasDurationSpec(@scenarioIdx) &&
2184
2196
  !(atEnd ^ task['forward', @scenarioIdx])
2185
2197
 
2186
2198
  # Check if all other dependencies for that task end have been determined
@@ -2245,7 +2257,7 @@ class TaskJuggler
2245
2257
  return nil unless (comp = child.calcCompletion(@scenarioIdx))
2246
2258
  completion += comp
2247
2259
  end
2248
- completion /= @property.children.length
2260
+ completion /= @property.kids.length
2249
2261
  else
2250
2262
  # For leaf tasks we first compare the start and end dates against the
2251
2263
  # current date.
@@ -2380,42 +2392,42 @@ class TaskJuggler
2380
2392
  # current task.
2381
2393
  def inputs(foundInputs, includeChildren, checkedTasks = [])
2382
2394
  # Ignore tasks that we have already included in the checked tasks list.
2383
- return if checkedTasks.include?(@property)
2384
- checkedTasks << @property
2395
+ return if checkedTasks.include?([ @property, includeChildren ])
2396
+ checkedTasks << [ @property, includeChildren ]
2385
2397
 
2386
- # A target must be a leaf function that has no direct or indirect
2387
- # (through parent) following tasks. Only milestones are recognized as
2388
- # targets.
2398
+ # An "input" must be a leaf task that has no direct or indirect (through
2399
+ # parent) following tasks. Only milestones are recognized as inputs.
2389
2400
  if @property.leaf? && !hasPredecessors && @milestone
2390
2401
  foundInputs << @property
2391
2402
  return
2392
2403
  end
2393
2404
 
2405
+ # We also include inputs of child tasks if requested. The recursive
2406
+ # iteration of child tasks is limited to the tested task only. The
2407
+ # predecessors children are not iterated. (see further below)
2408
+ if includeChildren
2409
+ @property.kids.each do |child|
2410
+ child.inputs(@scenarioIdx, foundInputs, true, checkedTasks)
2411
+ end
2412
+ end
2413
+
2414
+ # Now check the direct predecessors.
2394
2415
  @startpreds.each do |t, onEnd|
2395
2416
  t.inputs(@scenarioIdx, foundInputs, false, checkedTasks)
2396
2417
  end
2397
2418
 
2398
- # Check for indirect predecessors.
2419
+ # Check for indirect predecessors inherited from the ancestors.
2399
2420
  if @property.parent
2400
2421
  @property.parent.inputs(@scenarioIdx, foundInputs, false, checkedTasks)
2401
2422
  end
2402
-
2403
- # Also include targets of child tasks. The recursive iteration of child
2404
- # tasks is limited to the tested task only. The predecessors are not
2405
- # iterated.
2406
- if includeChildren
2407
- @property.kids.each do |child|
2408
- child.inputs(@scenarioIdx, foundInputs, true, checkedTasks)
2409
- end
2410
- end
2411
2423
  end
2412
2424
 
2413
2425
  # Recursively compile a list of Task properties which depend on the
2414
2426
  # current task.
2415
2427
  def targets(foundTargets, includeChildren, checkedTasks = [])
2416
2428
  # Ignore tasks that we have already included in the checked tasks list.
2417
- return if checkedTasks.include?(@property)
2418
- checkedTasks << @property
2429
+ return if checkedTasks.include?([ @property, includeChildren ])
2430
+ checkedTasks << [ @property, includeChildren ]
2419
2431
 
2420
2432
  # A target must be a leaf function that has no direct or indirect
2421
2433
  # (through parent) following tasks. Only milestones are recognized as
@@ -2458,9 +2470,13 @@ class TaskJuggler
2458
2470
  end
2459
2471
  end
2460
2472
 
2473
+ # If we are evaluating the task in the context of a specific resource,
2474
+ # we use the chargeset of that resource, not the chargeset of the task.
2475
+ chargeset = resource ? resource['chargeset', @scenarioIdx] : @chargeset
2476
+
2461
2477
  # If there are no chargeset defined for this task, we don't need to
2462
2478
  # compute the resource related or other cost.
2463
- unless @chargeset.empty?
2479
+ unless chargeset.empty?
2464
2480
  resourceCost = 0.0
2465
2481
  otherCost = 0.0
2466
2482
 
@@ -2490,7 +2506,7 @@ class TaskJuggler
2490
2506
 
2491
2507
  totalCost = resourceCost + otherCost
2492
2508
  # Now weight the total cost by the share of the account
2493
- @chargeset.each do |set|
2509
+ chargeset.each do |set|
2494
2510
  set.each do |accnt, share|
2495
2511
  if share > 0.0 && (accnt == account || accnt.isChildOf?(account))
2496
2512
  amount += totalCost * share