taskjuggler 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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