kplato

kptresource.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "kptresource.h"
22#include "kptappointment.h"
23#include "kptproject.h"
24#include "kpttask.h"
25#include "kptdatetime.h"
26#include "kptcalendar.h"
27#include "kpteffortcostmap.h"
28#include "kptschedule.h"
29
30#include <kdebug.h>
31#include <tdeglobal.h>
32#include <tdelocale.h>
33
34namespace KPlato
35{
36
37ResourceGroup::ResourceGroup(Project *project) {
38 m_project = project;
39 m_type = Type_Work;
40 m_resources.setAutoDelete(true);
41 generateId();
42 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
43}
44
45ResourceGroup::~ResourceGroup() {
46 if (findId() == this) {
47 removeId(); // only remove myself (I may be just a working copy)
48 }
49 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
50}
51
52bool ResourceGroup::setId(TQString id) {
53 //kdDebug()<<k_funcinfo<<id<<endl;
54 if (id.isEmpty()) {
55 kdError()<<k_funcinfo<<"id is empty"<<endl;
56 m_id = id;
57 return false;
58 }
59 ResourceGroup *g = findId();
60 if (g == this) {
61 //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl;
62 removeId();
63 } else if (g) {
64 //Hmmm, shouldn't happen
65 kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different group: "<<g->name()<<endl;
66 }
67 if (findId(id)) {
68 kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different group: "<<findId(id)->name()<<endl;
69 m_id = TQString(); // hmmm
70 return false;
71 }
72 m_id = id;
73 insertId(id);
74 //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl;
75 return true;
76}
77
78void ResourceGroup::generateId() {
79 if (!m_id.isEmpty()) {
80 removeId();
81 }
82 for (int i=0; i<32000 ; ++i) {
83 m_id = m_id.setNum(i);
84 if (!findId()) {
85 insertId(m_id);
86 return;
87 }
88 }
89 m_id = TQString();
90}
91
93 m_resources.append(resource);
94}
95
96Resource* ResourceGroup::getResource(int) {
97 return 0L;
98}
99
100Risk* ResourceGroup::getRisk(int) {
101 return 0L;
102}
103
104void ResourceGroup::removeResource(Resource *resource) {
105 m_resources.removeRef(resource);
106}
107
108Resource *ResourceGroup::takeResource(Resource *resource) {
109 return m_resources.take(m_resources.findRef(resource));
110}
111
112void ResourceGroup::removeResource(int) {
113}
114
117
121
124
125bool ResourceGroup::load(TQDomElement &element) {
126 //kdDebug()<<k_funcinfo<<endl;
127 setId(element.attribute("id"));
128 m_name = element.attribute("name");
129
130 TQDomNodeList list = element.childNodes();
131 for (unsigned int i=0; i<list.count(); ++i) {
132 if (list.item(i).isElement()) {
133 TQDomElement e = list.item(i).toElement();
134 if (e.tagName() == "resource") {
135 // Load the resource
136 Resource *child = new Resource(m_project);
137 if (child->load(e))
138 addResource(child, 0);
139 else
140 // TODO: Complain about this
141 delete child;
142 }
143 }
144 }
145 return true;
146}
147
148void ResourceGroup::save(TQDomElement &element) const {
149 //kdDebug()<<k_funcinfo<<endl;
150
151 TQDomElement me = element.ownerDocument().createElement("resource-group");
152 element.appendChild(me);
153
154 me.setAttribute("id", m_id);
155 me.setAttribute("name", m_name);
156
157 TQPtrListIterator<Resource> it(m_resources);
158 for ( ; it.current(); ++it ) {
159 it.current()->save(me);
160 }
161}
162
163void ResourceGroup::initiateCalculation(Schedule &sch) {
164 TQPtrListIterator<Resource> it(m_resources);
165 for (; it.current(); ++it) {
166 it.current()->initiateCalculation(sch);
167 }
168 clearNodes();
169}
170
171int ResourceGroup::units() {
172 int u = 0;
173 TQPtrListIterator<Resource> it = m_resources;
174 for (; it.current(); ++it) {
175 u += it.current()->units();
176 }
177 return u;
178}
179
180ResourceGroup *ResourceGroup::findId(const TQString &id) const {
181 return m_project ? m_project->findResourceGroup(id) : 0;
182}
183
184bool ResourceGroup::removeId(const TQString &id) {
185 return m_project ? m_project->removeResourceGroupId(id): false;
186}
187
188void ResourceGroup::insertId(const TQString &id) {
189 if (m_project)
190 m_project->insertResourceGroupId(id, this);
191}
192
193Appointment ResourceGroup::appointmentIntervals() const {
194 Appointment a;
195 TQPtrListIterator<Resource> it = m_resources;
196 for (; it.current(); ++it) {
197 a += it.current()->appointmentIntervals();
198 }
199 return a;
200}
201
202Resource::Resource(Project *project) : m_project(project), m_schedules(), m_workingHours() {
203 m_type = Type_Work;
204 m_units = 100; // %
205
206 m_availableFrom = DateTime(TQDate::currentDate());
207 m_availableUntil = TQDateTime(m_availableFrom.addYears(2));
208
209 cost.normalRate = 100;
210 cost.overtimeRate = 200;
211 cost.fixed = 0;
212 m_calendar = 0;
213 m_currentSchedule = 0;
214 generateId();
215 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
216}
217
218Resource::Resource(Resource *resource) {
219 //kdDebug()<<k_funcinfo<<"("<<this<<") from ("<<resource<<")"<<endl;
220 copy(resource);
221}
222
223Resource::~Resource() {
224 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
225 if (findId() == this) {
226 removeId(); // only remove myself (I may be just a working copy)
227 }
228 TQPtrListIterator<ResourceRequest> it = m_requests;
229 for (; it.current(); ++it) {
230 it.current()->setResource(0); // avoid the request to mess with my list
231 }
232 m_requests.first();
233 for (; m_requests.current(); m_requests.next()) {
234 m_requests.current()->parent()->removeResourceRequest(m_requests.current()); // deletes the request
235 }
236}
237
238bool Resource::setId(TQString id) {
239 //kdDebug()<<k_funcinfo<<id<<endl;
240 if (id.isEmpty()) {
241 kdError()<<k_funcinfo<<"id is empty"<<endl;
242 m_id = id;
243 return false;
244 }
245 Resource *r = findId();
246 if (r == this) {
247 //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl;
248 removeId();
249 } else if (r) {
250 //Hmmm, shouldn't happen
251 kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different resource: "<<r->name()<<endl;
252 }
253 if (findId(id)) {
254 kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different resource: "<<findId(id)->name()<<endl;
255 m_id = TQString(); // hmmm
256 return false;
257 }
258 m_id = id;
259 insertId(id);
260 //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl;
261 return true;
262}
263
264void Resource::generateId() {
265 if (!m_id.isEmpty()) {
266 removeId();
267 }
268 for (int i=0; i<32000 ; ++i) {
269 m_id = m_id.setNum(i);
270 if (!findId()) {
271 insertId(m_id);
272 //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<m_id<<endl;
273 return;
274 }
275 }
276 m_id = TQString();
277}
278
279void Resource::setType(const TQString &type) {
280 if (type == "Work")
281 m_type = Type_Work;
282 else if (type == "Material")
283 m_type = Type_Material;
284}
285
286TQString Resource::typeToString() const {
287 if (m_type == Type_Work)
288 return TQString("Work");
289 else if (m_type == Type_Material)
290 return TQString("Material");
291
292 return TQString();
293}
294
295void Resource::copy(Resource *resource) {
296 m_project = resource->project();
297 //m_appointments = resource->appointments(); // Note
298 m_id = resource->id();
299 m_name = resource->name();
300 m_initials = resource->initials();
301 m_email = resource->email();
302 m_availableFrom = resource->availableFrom();
303 m_availableUntil = resource->availableUntil();
304 m_workingHours.clear();
305 m_workingHours = resource->workingHours();
306
307 m_units = resource->units(); // available units in percent
308
309 m_type = resource->type();
310
311 cost.normalRate = resource->normalRate();
312 cost.overtimeRate = resource->overtimeRate();
313 cost.fixed = resource->fixedCost();
314
315 m_calendar = resource->m_calendar;
316}
317
318void Resource::addWorkingHour(TQTime from, TQTime until) {
319 //kdDebug()<<k_funcinfo<<endl;
320 m_workingHours.append(new TQTime(from));
321 m_workingHours.append(new TQTime(until));
322}
323
324Calendar *Resource::calendar(bool local) const {
325 if (!local && project() != 0 && (m_calendar == 0 || m_calendar->isDeleted())) {
326 return project()->defaultCalendar();
327 }
328 if (m_calendar && m_calendar->isDeleted()) {
329 return 0;
330 }
331 return m_calendar;
332}
333
334DateTime Resource::getFirstAvailableTime(DateTime /*after*/) {
335 return DateTime();
336}
337
338DateTime Resource::getBestAvailableTime(Duration /*duration*/) {
339 return DateTime();
340}
341
342DateTime Resource::getBestAvailableTime(const DateTime /*after*/, const Duration /*duration*/) {
343 return DateTime();
344}
345
346bool Resource::load(TQDomElement &element) {
347 //kdDebug()<<k_funcinfo<<endl;
348 TQString s;
349 setId(element.attribute("id"));
350 m_name = element.attribute("name");
351 m_initials = element.attribute("initials");
352 m_email = element.attribute("email");
353 setType(element.attribute("type"));
354 m_calendar = findCalendar(element.attribute("calendar-id"));
355 m_units = element.attribute("units", "100").toInt();
356 s = element.attribute("available-from");
357 if (s != "")
358 m_availableFrom = DateTime::fromString(s);
359 s = element.attribute("available-until");
360 if (s != "")
361 m_availableUntil = DateTime::fromString(s);
362
363 cost.normalRate = TDEGlobal::locale()->readMoney(element.attribute("normal-rate"));
364 cost.overtimeRate = TDEGlobal::locale()->readMoney(element.attribute("overtime-rate"));
365 return true;
366}
367
368void Resource::save(TQDomElement &element) const {
369 //kdDebug()<<k_funcinfo<<endl;
370 TQDomElement me = element.ownerDocument().createElement("resource");
371 element.appendChild(me);
372
373 if (calendar(true))
374 me.setAttribute("calendar-id", m_calendar->id());
375 me.setAttribute("id", m_id);
376 me.setAttribute("name", m_name);
377 me.setAttribute("initials", m_initials);
378 me.setAttribute("email", m_email);
379 me.setAttribute("type", typeToString());
380 me.setAttribute("units", m_units);
381 me.setAttribute("available-from", m_availableFrom.toString(TQt::ISODate));
382 me.setAttribute("available-until", m_availableUntil.toString(TQt::ISODate));
383 me.setAttribute("normal-rate", TDEGlobal::locale()->formatMoney(cost.normalRate));
384 me.setAttribute("overtime-rate", TDEGlobal::locale()->formatMoney(cost.overtimeRate));
385}
386
387bool Resource::isAvailable(Task */*task*/) {
388 bool busy = false;
389/* TQPtrListIterator<Appointment> it(m_appointments);
390 for (; it.current(); ++it) {
391 if (it.current()->isBusy(task->startTime(), task->endTime())) {
392 busy = true;
393 break;
394 }
395 }*/
396 return !busy;
397}
398
399TQPtrList<Appointment> Resource::appointments() {
400 TQPtrList<Appointment> lst;
401 if (m_currentSchedule)
402 lst = m_currentSchedule->appointments();
403 //kdDebug()<<k_funcinfo<<lst.count()<<endl;
404 return lst;
405}
406
407Appointment *Resource::findAppointment(Node */*node*/) {
408/* TQPtrListIterator<Appointment> it = m_appointments;
409 for (; it.current(); ++it) {
410 if (it.current()->node() == node)
411 return it.current();
412 }*/
413 return 0;
414}
415
417 if (m_currentSchedule)
418 return m_currentSchedule->add(appointment);
419 return false;
420}
421
423 Schedule *s = findSchedule(main.id());
424 if (s == 0) {
425 s = createSchedule(&main);
426 }
427 appointment->setResource(s);
428 return s->add(appointment);
429}
430
431void Resource::addAppointment(Schedule *node, DateTime &start, DateTime &end, double load) {
432 //kdDebug()<<k_funcinfo<<endl;
433 Schedule *s = findSchedule(node->id());
434 if (s == 0) {
435 s = createSchedule(node->parent());
436 }
437 s->addAppointment(node, start, end, load);
438}
439
440void Resource::initiateCalculation(Schedule &sch) {
441 m_currentSchedule = createSchedule(&sch);
442}
443
445 takeSchedule(schedule);
446 delete schedule;
447}
448
449void Resource::takeSchedule(const Schedule *schedule) {
450 if (schedule == 0)
451 return;
452 if (m_currentSchedule == schedule)
453 m_currentSchedule = 0;
454 m_schedules.take(schedule->id());
455}
456
457void Resource::addSchedule(Schedule *schedule) {
458 if (schedule == 0)
459 return;
460 m_schedules.replace(schedule->id(), schedule);
461}
462
463ResourceSchedule *Resource::createSchedule(TQString name, int type, long id) {
464 ResourceSchedule *sch = new ResourceSchedule(this, name, (Schedule::Type)type, id);
465 addSchedule(sch);
466 return sch;
467}
468
469ResourceSchedule *Resource::createSchedule(Schedule *parent) {
470 ResourceSchedule *sch = new ResourceSchedule(parent, this);
471 addSchedule(sch);
472 return sch;
473}
474
475void Resource::makeAppointment(Schedule *node, const DateTime &from, const DateTime &end) {
476 if (!from.isValid() || !end.isValid()) {
477 kdWarning()<<k_funcinfo<<"Invalid time"<<endl;
478 return;
479 }
480 Calendar *cal = calendar();
481 if (cal == 0) {
482 return;
483 }
484 DateTime time = from;
485 while (time < end) {
486 //kdDebug()<<k_funcinfo<<time.toString()<<" to "<<end.toString()<<endl;
487 if (!time.isValid() || !end.isValid()) {
488 kdWarning()<<k_funcinfo<<"Invalid time"<<endl;
489 return;
490 }
491 if (!cal->hasInterval(time, end)) {
492 //kdDebug()<<time.toString()<<" to "<<end.toString()<<": No (more) interval(s)"<<endl;
493 kdWarning()<<k_funcinfo<<m_name<<": Resource only partially available"<<endl;
494 //node->resourceNotAvailable = true;
495 return; // nothing more to do
496 }
497 TQPair<DateTime, DateTime> i = cal->firstInterval(time, end);
498 if (!i.second.isValid()) {
499 kdWarning()<<k_funcinfo<<"Invalid interval: "<<time<<", "<<end<<endl;
500 return;
501 }
502 if (time == i.second)
503 return; // hmmm, didn't get a new interval, avoid loop
504 addAppointment(node, i.first, i.second, m_units);
505 //kdDebug()<<k_funcinfo<<"Add :"<<i.first.toString()<<" to "<<i.second.toString()<<endl;
506 if (!(node->workStartTime.isValid()) || i.first < node->workStartTime)
507 node->workStartTime = i.first;
508 if (!(node->workEndTime.isValid()) || i.second > node->workEndTime)
509 node->workEndTime = i.second;
510 time = i.second;
511 }
512 return;
513}
514void Resource::makeAppointment(Schedule *node) {
515 //kdDebug()<<k_funcinfo<<m_name<< ": "<<node->node()->name()<<": "<<node->startTime.toString()<<" dur "<<node->duration.toString()<<endl;
516 if (!node->startTime.isValid()) {
517 kdWarning()<<k_funcinfo<<m_name<<": startTime invalid"<<endl;
518 return;
519 }
520 if (!node->endTime.isValid()) {
521 kdWarning()<<k_funcinfo<<m_name<<": endTime invalid"<<endl;
522 return;
523 }
524 Calendar *cal = calendar();
525 if (m_type == Type_Material) {
526 DateTime from = availableAfter(node->startTime, node->endTime);
527 DateTime end = availableBefore(node->endTime, node->startTime);
528 if (!from.isValid() || !end.isValid()) {
529 return;
530 }
531 if (cal == 0) {
532 // Allocate the whole period
533 addAppointment(node, from, end, m_units);
534 return;
535 }
536 makeAppointment(node, from, end);
537 }
538 if (!cal) {
539 kdWarning()<<k_funcinfo<<m_name<<": No calendar defined"<<endl;
540 return;
541 }
542 //TODO: units and standard non-working days
543 DateTime time = node->startTime;
544 DateTime end = node->endTime;
545 time = availableAfter(time, end);
546 if (!time.isValid()) {
547 kdWarning()<<k_funcinfo<<m_name<<": Resource not available (after="<<node->startTime<<", "<<end<<")"<<endl;
548 node->resourceNotAvailable = true;
549 return;
550 }
551 end = availableBefore(end, time);
552 if (!end.isValid()) {
553 kdWarning()<<k_funcinfo<<m_name<<": Resource not available (before="<<node->endTime<<", "<<time<<")"<<endl;
554 node->resourceNotAvailable = true;
555 return;
556 }
557 //kdDebug()<<k_funcinfo<<time.toString()<<" to "<<end.toString()<<endl;
558 makeAppointment(node, time, end);
559}
560
561// the amount of effort we can do within the duration
562Duration Resource::effort(const DateTime &start, const Duration &duration, bool backward, bool *ok) const {
563 //kdDebug()<<k_funcinfo<<m_name<<": "<<start.date().toString()<<" for duration "<<duration.toString(Duration::Format_Day)<<endl;
564 bool sts=false;
565 Duration e;
566 if (duration == 0) {
567 kdWarning()<<k_funcinfo<<"zero duration"<<endl;
568 return e;
569 }
570 Calendar *cal = calendar();
571 if (cal == 0) {
572 kdWarning()<<k_funcinfo<<m_name<<": No calendar defined"<<endl;
573 return e;
574 }
575 if (backward) {
576 DateTime limit = start - duration;
577 DateTime t = availableBefore(start, limit);
578 if (t.isValid()) {
579 sts = true;
580 e = (cal->effort(limit, t) * m_units)/100;
581 } else {
582 //kdDebug()<<k_funcinfo<<m_name<<": Not available (start="<<start<<", "<<limit<<")"<<endl;
583 }
584 } else {
585 DateTime limit = start + duration;
586 DateTime t = availableAfter(start, limit);
587 if (t.isValid()) {
588 sts = true;
589 e = (cal->effort(t, limit) * m_units)/100;
590 }
591 }
592 //kdDebug()<<k_funcinfo<<start.toString()<<" e="<<e.toString(Duration::Format_Day)<<" ("<<m_units<<")"<<endl;
593 if (ok) *ok = sts;
594 return e;
595}
596
597DateTime Resource::availableAfter(const DateTime &time, const DateTime limit, bool checkAppointments) const {
598 DateTime t;
599 if (m_units == 0) {
600 return t;
601 }
602 DateTime lmt = m_availableUntil;
603 if (limit.isValid() && limit < lmt) {
604 lmt = limit;
605 }
606 if (time >= lmt) {
607 return t;
608 }
609 if (type() == Type_Material) {
610 t = time > m_availableFrom ? time : m_availableFrom;
611 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl;
612 return t;
613 }
614 Calendar *cal = calendar();
615 if (cal == 0) {
616 return t;
617 }
618 t = m_availableFrom > time ? m_availableFrom : time;
619 t = cal->firstAvailableAfter(t, lmt);
620 if (checkAppointments) {
621 //TODO
622 }
623 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl;
624 return t;
625}
626
627DateTime Resource::availableBefore(const DateTime &time, const DateTime limit, bool checkAppointments) const {
628 DateTime t;
629 if (m_units == 0) {
630 return t;
631 }
632 DateTime lmt = m_availableFrom;
633 if (limit.isValid() && limit > lmt) {
634 lmt = limit;
635 }
636 if (time <= lmt) {
637 return t;
638 }
639 if (type() == Type_Material) {
640 t = time < m_availableUntil ? time : m_availableUntil;
641 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl;
642 return t;
643 }
644 Calendar *cal = calendar();
645 if (cal == 0) {
646 return t;
647 }
648 if (!m_availableUntil.isValid()) {
649 kdWarning()<<k_funcinfo<<m_name<<": availabelUntil is invalid"<<endl;
650 t = time;
651 } else {
652 t = m_availableUntil < time ? m_availableUntil : time;
653 }
654 //kdDebug()<<k_funcinfo<<t<<", "<<lmt<<endl;
655 t = cal->firstAvailableBefore(t, lmt);
656 if (checkAppointments) {
657 //TODO
658 }
659 //kdDebug()<<k_funcinfo<<m_name<<" returns: "<<time<<"="<<t<<" "<<endl;
660 return t;
661}
662
663Resource *Resource::findId(const TQString &id) const {
664 return m_project ? m_project->findResource(id) : 0;
665}
666
667bool Resource::removeId(const TQString &id) {
668 return m_project ? m_project->removeResourceId(id) : false;
669}
670
671void Resource::insertId(const TQString &id) {
672 if (m_project)
673 m_project->insertResourceId(id, this);
674}
675
676Calendar *Resource::findCalendar(const TQString &id) const {
677 return (m_project ? m_project->findCalendar(id) : 0);
678}
679
680bool Resource::isOverbooked() const {
681 return isOverbooked(DateTime(), DateTime());
682}
683
684bool Resource::isOverbooked(const TQDate &date) const {
685 return isOverbooked(DateTime(date), DateTime(date.addDays(1)));
686}
687
688bool Resource::isOverbooked(const DateTime &start, const DateTime &end) const {
689 //kdDebug()<<k_funcinfo<<m_name<<": "<<start.toString()<<" - "<<end.toString()<<" cs=("<<m_currentSchedule<<")"<<endl;
690 return m_currentSchedule ? m_currentSchedule->isOverbooked(start, end) : false;
691}
692
693Appointment Resource::appointmentIntervals() const {
694 Appointment a;
695 if (m_currentSchedule == 0)
696 return a;
697 TQPtrListIterator<Appointment> it = m_currentSchedule->appointments();
698 for (; it.current(); ++it) {
699 a += *(it.current());
700 }
701 return a;
702}
703
704Duration Resource::plannedEffort(const TQDate &date) const {
705 return m_currentSchedule ? m_currentSchedule->plannedEffort(date) : Duration::zeroDuration;
706}
707
709Risk::Risk(Node *n, Resource *r, RiskType rt) {
710 m_node=n;
711 m_resource=r;
712 m_riskType=rt;
713}
714
715Risk::~Risk() {
716}
717
718ResourceRequest::ResourceRequest(Resource *resource, int units)
719 : m_resource(resource),
720 m_units(units),
721 m_parent(0) {
722 //kdDebug()<<k_funcinfo<<"("<<this<<") Request to: "<<(resource ? resource->name() : TQString("None"))<<endl;
723}
724
725ResourceRequest::~ResourceRequest() {
726 //kdDebug()<<k_funcinfo<<"("<<this<<") resource: "<<(m_resource ? m_resource->name() : TQString("None"))<<endl;
727 if (m_resource)
728 m_resource->unregisterRequest(this);
729 m_resource = 0;
730}
731
732bool ResourceRequest::load(TQDomElement &element, Project &project) {
733 //kdDebug()<<k_funcinfo<<endl;
734 m_resource = project.resource(element.attribute("resource-id"));
735 if (m_resource == 0) {
736 kdWarning()<<k_funcinfo<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id")<<endl;
737 return false;
738 }
739 m_units = element.attribute("units").toInt();
740 return true;
741}
742
743void ResourceRequest::save(TQDomElement &element) const {
744 TQDomElement me = element.ownerDocument().createElement("resource-request");
745 element.appendChild(me);
746 me.setAttribute("resource-id", m_resource->id());
747 me.setAttribute("units", m_units);
748}
749
750int ResourceRequest::units() const {
751 //kdDebug()<<k_funcinfo<<m_resource->name()<<": units="<<m_units<<endl;
752 return m_units;
753}
754
755int ResourceRequest::workUnits() const {
756 if (m_resource->type() == Resource::Type_Work)
757 return units();
758
759 //kdDebug()<<k_funcinfo<<"units=0"<<endl;
760 return 0;
761}
762
763Task *ResourceRequest::task() const {
764 return m_parent ? m_parent->task() : 0;
765}
766
768ResourceGroupRequest::ResourceGroupRequest(ResourceGroup *group, int units)
769 : m_group(group), m_units(units) {
770
771 //kdDebug()<<k_funcinfo<<"Request to: "<<(group ? group->name() : TQString("None"))<<endl;
772 if (group)
773 group->registerRequest(this);
774 m_resourceRequests.setAutoDelete(true);
775}
776
777ResourceGroupRequest::~ResourceGroupRequest() {
778 //kdDebug()<<k_funcinfo<<"Group: "<<m_group->name()<<endl;
779 if (m_group)
780 m_group->unregisterRequest(this);
781 m_resourceRequests.clear();
782}
783
784void ResourceGroupRequest::addResourceRequest(ResourceRequest *request) {
785 //kdDebug()<<k_funcinfo<<"("<<request<<") to Group: "<<m_group->name()<<endl;
786 request->setParent(this);
787 m_resourceRequests.append(request);
788 request->registerRequest();
789}
790
791ResourceRequest *ResourceGroupRequest::takeResourceRequest(ResourceRequest *request) {
792 if (request)
793 request->unregisterRequest();
794 return m_resourceRequests.take(m_resourceRequests.findRef(request));
795}
796
797ResourceRequest *ResourceGroupRequest::find(Resource *resource) const {
798 TQPtrListIterator<ResourceRequest> it(m_resourceRequests);
799 for (; it.current(); ++it)
800 if (it.current()->resource() == resource)
801 return it.current();
802
803 return 0;
804}
805
806bool ResourceGroupRequest::load(TQDomElement &element, Project &project) {
807 //kdDebug()<<k_funcinfo<<endl;
808 m_group = project.findResourceGroup(element.attribute("group-id"));
809 if (m_group == 0) {
810 //kdDebug()<<k_funcinfo<<"The referenced resource group does not exist: group id="<<element.attribute("group-id")<<endl;
811 return false;
812 }
813 m_group->registerRequest(this);
814
815 m_units = element.attribute("units").toInt();
816
817 TQDomNodeList list = element.childNodes();
818 for (unsigned int i=0; i<list.count(); ++i) {
819 if (list.item(i).isElement()) {
820 TQDomElement e = list.item(i).toElement();
821 if (e.tagName() == "resource-request") {
822 ResourceRequest *r = new ResourceRequest();
823 if (r->load(e, project))
824 addResourceRequest(r);
825 else {
826 kdError()<<k_funcinfo<<"Failed to load resource request"<<endl;
827 delete r;
828 }
829 }
830 }
831 }
832 return true;
833}
834
835void ResourceGroupRequest::save(TQDomElement &element) const {
836 if (units() == 0)
837 return;
838 TQDomElement me = element.ownerDocument().createElement("resourcegroup-request");
839 element.appendChild(me);
840 me.setAttribute("group-id", m_group->id());
841 me.setAttribute("units", m_units);
842 TQPtrListIterator<ResourceRequest> it(m_resourceRequests);
843 for (; it.current(); ++it)
844 it.current()->save(me);
845}
846
847int ResourceGroupRequest::units() const {
848 int units = m_units;
849 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
850 for (; it.current(); ++it) {
851 units += it.current()->units();
852 }
853 //kdDebug()<<k_funcinfo<<"units="<<units<<endl;
854 return units;
855}
856
857int ResourceGroupRequest::workUnits() const {
858 int units = 0;
859 if (m_group->type() == ResourceGroup::Type_Work)
860 units = m_units;
861 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
862 for (; it.current(); ++it) {
863 units += it.current()->workUnits();
864 }
865 //kdDebug()<<k_funcinfo<<"units="<<units<<endl;
866 return units;
867}
868
869//TODO: handle nonspecific resources
870Duration ResourceGroupRequest::effort(const DateTime &time, const Duration &duration, bool backward, bool *ok) const {
871 Duration e;
872 bool sts=false;
873 if (ok) *ok = sts;
874 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
875 for (; it.current(); ++it) {
876 e += it.current()->resource()->effort(time, duration, backward, &sts);
877 if (sts && ok) *ok = sts;
878 //kdDebug()<<k_funcinfo<<(backward?"(B)":"(F)" )<<it.current()->resource()->name()<<": time="<<time<<" dur="<<duration.toString()<<"gave e="<<e.toString()<<endl;
879 }
880 //kdDebug()<<k_funcinfo<<time.toString()<<"d="<<duration.toString()<<": e="<<e.toString()<<endl;
881 return e;
882}
883
884int ResourceGroupRequest::numDays(const DateTime &time, bool backward) const {
885 DateTime t1, t2 = time;
886 if (backward) {
887 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
888 for (; it.current(); ++it) {
889 t1 = it.current()->resource()->availableFrom();
890 if (!t2.isValid() || t2 > t1)
891 t2 = t1;
892 }
893 //kdDebug()<<k_funcinfo<<"bw "<<time.toString()<<": "<<t2.daysTo(time)<<endl;
894 return t2.daysTo(time);
895 }
896 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
897 for (; it.current(); ++it) {
898 t1 = it.current()->resource()->availableUntil();
899 if (!t2.isValid() || t2 < t1)
900 t2 = t1;
901 }
902 //kdDebug()<<k_funcinfo<<"fw "<<time.toString()<<": "<<time.daysTo(t2)<<endl;
903 return time.daysTo(t2);
904}
905
906Duration ResourceGroupRequest::duration(const DateTime &time, const Duration &_effort, bool backward) {
907 //kdDebug()<<k_funcinfo<<"--->"<<(backward?"(B) ":"(F) ")<<m_group->name()<<" "<<time.toString()<<": effort: "<<_effort.toString(Duration::Format_Day)<<" ("<<_effort.milliseconds()<<")"<<endl;
908 Duration e;
909 if (_effort == Duration::zeroDuration) {
910 return e;
911 }
912 bool sts=true;
913 bool match = false;
914 DateTime start = time;
915 int inc = backward ? -1 : 1;
916 DateTime end = start;
917 Duration e1;
918 Duration d(1, 0, 0); // 1 day
919 int nDays = numDays(time, backward) + 1;
920 for (int i=0; !match && i <= nDays; ++i) {
921 // days
922 end = TQDateTime(end.addDays(inc));
923 e1 = effort(start, d, backward, &sts);
924 //kdDebug()<<"["<<i<<"of"<<nDays<<"] "<<(backward?"(B)":"(F):")<<" start="<<start<<" e+e1="<<(e+e1).toString()<<" match "<<_effort.toString()<<endl;
925 if (e + e1 < _effort) {
926 e += e1;
927 start = end;
928 } else if (e + e1 == _effort) {
929 e += e1;
930 match = true;
931 } else {
932 end = start;
933 break;
934 }
935 }
936 //kdDebug()<<"duration "<<(backward?"backward ":"forward: ")<<start.toString()<<" - "<<end.toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<") match="<<match<<" sts="<<sts<<endl;
937 d = Duration(0, 1, 0); // 1 hour
938 for (int i=0; !match && i < 24; ++i) {
939 // hours
940 end = TQDateTime(end.addSecs(inc*60*60));
941 e1 = effort(start, d, backward, &sts);
942 if (e + e1 < _effort) {
943 e += e1;
944 start = end;
945 } else if (e + e1 == _effort) {
946 e += e1;
947 match = true;
948 } else {
949 end = start;
950 break;
951 }
952 //kdDebug()<<"duration(h)["<<i<<"]"<<(backward?"backward ":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl;
953 }
954 //kdDebug()<<"duration "<<(backward?"backward ":"forward: ")<<start.toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<") match="<<match<<" sts="<<sts<<endl;
955 d = Duration(0, 0, 1); // 1 minute
956 for (int i=0; !match && i < 60; ++i) {
957 //minutes
958 end = TQDateTime(end.addSecs(inc*60));
959 e1 = effort(start, d, backward, &sts);
960 if (e + e1 < _effort) {
961 e += e1;
962 start = end;
963 } else if (e + e1 == _effort) {
964 e += e1;
965 match = true;
966 } else if (e + e1 > _effort) {
967 end = start;
968 break;
969 }
970 //kdDebug()<<"duration(m) "<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl;
971 }
972 //kdDebug()<<"duration "<<(backward?"backward":"forward:")<<" start="<<start.toString()<<" e="<<e.toString()<<" match="<<match<<" sts="<<sts<<endl;
973 d = Duration(0, 0, 0, 1); // 1 second
974 for (int i=0; !match && i < 60 && sts; ++i) {
975 //seconds
976 end = TQDateTime(end.addSecs(inc));
977 e1 = effort(start, d, backward, &sts);
978 if (e + e1 < _effort) {
979 e += e1;
980 start = end;
981 } else if (e + e1 == _effort) {
982 e += e1;
983 match = true;
984 } else if (e + e1 > _effort) {
985 end = start;
986 break;
987 }
988 //kdDebug()<<"duration(s)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl;
989 }
990 d = Duration(0, 0, 0, 0, 1); // 1 millisecond
991 for (int i=0; !match && i < 1000; ++i) {
992 //milliseconds
993 end.setTime(end.time().addMSecs(inc));
994 e1 = effort(start, d, backward, &sts);
995 if (e + e1 < _effort) {
996 e += e1;
997 start = end;
998 } else if (e + e1 == _effort) {
999 e += e1;
1000 match = true;
1001 } else if (e + e1 > _effort) {
1002 break;
1003 }
1004 //kdDebug()<<"duration(ms)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl;
1005 }
1006 if (!match) {
1007 kdError()<<k_funcinfo<<(task()?task()->name():"No task")<<" "<<time<<": Could not match effort."<<" Want: "<<_effort.toString(Duration::Format_Day)<<" got: "<<e.toString(Duration::Format_Day)<<" sts="<<sts<<endl;
1008 }
1009 DateTime t;
1010 if (e != Duration::zeroDuration) {
1011 t = backward ? availableAfter(end) : availableBefore(end);
1012 }
1013 end = t.isValid() ? t : time;
1014 //kdDebug()<<k_funcinfo<<"<---"<<(backward?"(B) ":"(F) ")<<m_group->name()<<": "<<end.toString()<<"-"<<time.toString()<<"="<<(end - time).toString()<<" effort: "<<_effort.toString(Duration::Format_Day)<<endl;
1015 return (end>time?end-time:time-end);
1016}
1017
1018DateTime ResourceGroupRequest::availableAfter(const DateTime &time) {
1019 DateTime start;
1020 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
1021 for (; it.current(); ++it) {
1022 DateTime t = it.current()->resource()->availableAfter(time);
1023 if (t.isValid() && (!start.isValid() || t < start))
1024 start = t;
1025 }
1026 if (start.isValid() && start < time)
1027 start = time;
1028 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<start.toString()<<" "<<m_group->name()<<endl;
1029 return start;
1030}
1031
1032DateTime ResourceGroupRequest::availableBefore(const DateTime &time) {
1033 DateTime end;
1034 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
1035 for (; it.current(); ++it) {
1036 DateTime t = it.current()->resource()->availableBefore(time);
1037 if (t.isValid() && (!end.isValid() || t > end))
1038 end = t;
1039 }
1040 if (!end.isValid() || end > time)
1041 end = time;
1042 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<end.toString()<<" "<<m_group->name()<<endl;
1043 return end;
1044}
1045
1046void ResourceGroupRequest::makeAppointments(Schedule *schedule) {
1047 //kdDebug()<<k_funcinfo<<endl;
1048 TQPtrListIterator<ResourceRequest> it = m_resourceRequests;
1049 for (; it.current(); ++it) {
1050 it.current()->makeAppointment(schedule);
1051 }
1052}
1053
1054void ResourceGroupRequest::reserve(const DateTime &start, const Duration &duration) {
1055 m_start = start;
1056 m_duration = duration;
1057}
1058
1059bool ResourceGroupRequest::isEmpty() const {
1060 return m_resourceRequests.isEmpty() && m_units == 0;
1061}
1062
1063Task *ResourceGroupRequest::task() const {
1064 return m_parent ? &(m_parent->task()) : 0;
1065}
1066
1068ResourceRequestCollection::ResourceRequestCollection(Task &task)
1069 : m_task(task) {
1070 m_requests.setAutoDelete(true);
1071}
1072
1073ResourceRequestCollection::~ResourceRequestCollection() {
1074 //kdDebug()<<k_funcinfo<<"Group: "<<m_group->name()<<endl;
1075 m_requests.clear();
1076}
1077
1078ResourceGroupRequest *ResourceRequestCollection::find(ResourceGroup *group) const {
1079 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1080 for (; it.current(); ++it) {
1081 if (it.current()->group() == group)
1082 return it.current(); // we assume only one request to the same group
1083 }
1084 return 0;
1085}
1086
1087
1088ResourceRequest *ResourceRequestCollection::find(Resource *resource) const {
1089 ResourceRequest *req = 0;
1090 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1091 for (; !req && it.current(); ++it) {
1092 req = it.current()->find(resource);
1093 }
1094 return req;
1095}
1096
1097// bool ResourceRequestCollection::load(TQDomElement &element, Project &project) {
1098// //kdDebug()<<k_funcinfo<<endl;
1099// return true;
1100// }
1101
1102void ResourceRequestCollection::save(TQDomElement &element) const {
1103 //kdDebug()<<k_funcinfo<<endl;
1104 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1105 for ( ; it.current(); ++it ) {
1106 it.current()->save(element);
1107 }
1108}
1109
1110int ResourceRequestCollection::units() const {
1111 //kdDebug()<<k_funcinfo<<endl;
1112 int units = 0;
1113 TQPtrListIterator<ResourceGroupRequest> it = m_requests;
1114 for (; it.current(); ++it) {
1115 units += it.current()->units();
1116 //kdDebug()<<k_funcinfo<<" Group: "<<it.current()->group()->name()<<" now="<<units<<endl;
1117 }
1118 return units;
1119}
1120
1121int ResourceRequestCollection::workUnits() const {
1122 //kdDebug()<<k_funcinfo<<endl;
1123 int units = 0;
1124 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1125 for (; it.current(); ++it) {
1126 units += it.current()->workUnits();
1127 }
1128 //kdDebug()<<k_funcinfo<<" units="<<units<<endl;
1129 return units;
1130}
1131
1132// Returns the longest duration needed by any of the groups.
1133// The effort is distributed on "work type" resourcegroups in proportion to
1134// the amount of resources requested for each group.
1135// "Material type" of resourcegroups does not (atm) affect the duration.
1136Duration ResourceRequestCollection::duration(const DateTime &time, const Duration &effort, bool backward) {
1137 //kdDebug()<<k_funcinfo<<"time="<<time.toString()<<" effort="<<effort.toString(Duration::Format_Day)<<" backward="<<backward<<endl;
1138 if (isEmpty()) {
1139 return effort;
1140 }
1141 Duration dur;
1142 int units = workUnits();
1143 if (units == 0)
1144 units = 100; //hmmmm
1145 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1146 for (; it.current(); ++it) {
1147 if (it.current()->isEmpty())
1148 continue;
1149 if (it.current()->group()->type() == ResourceGroup::Type_Work) {
1150 Duration d = it.current()->duration(time, (effort*it.current()->workUnits())/units, backward);
1151 if (d > dur)
1152 dur = d;
1153 } else if (it.current()->group()->type() == ResourceGroup::Type_Material) {
1154 //TODO
1155 if (dur == Duration::zeroDuration)
1156 dur = effort;
1157 }
1158 }
1159 return dur;
1160}
1161
1162DateTime ResourceRequestCollection::availableAfter(const DateTime &time) {
1163 DateTime start;
1164 TQPtrListIterator<ResourceGroupRequest> it = m_requests;
1165 for (; it.current(); ++it) {
1166 DateTime t = it.current()->availableAfter(time);
1167 if (t.isValid() && (!start.isValid() || t < start))
1168 start = t;
1169 }
1170 if (start.isValid() && start < time)
1171 start = time;
1172 //kdDebug()<<k_funcinfo<<time.toString()<<"="<<start.toString()<<endl;
1173 return start;
1174}
1175
1176DateTime ResourceRequestCollection::availableBefore(const DateTime &time) {
1177 DateTime end;
1178 TQPtrListIterator<ResourceGroupRequest> it = m_requests;
1179 for (; it.current(); ++it) {
1180 DateTime t = it.current()->availableBefore(time);
1181 if (t.isValid() && (!end.isValid() ||t > end))
1182 end = t;
1183 }
1184 if (!end.isValid() || end > time)
1185 end = time;
1186 return end;
1187}
1188
1189
1190void ResourceRequestCollection::makeAppointments(Schedule *schedule) {
1191 //kdDebug()<<k_funcinfo<<endl;
1192 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1193 for (; it.current(); ++it) {
1194 it.current()->makeAppointments(schedule);
1195 }
1196}
1197
1198void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) {
1199 //kdDebug()<<k_funcinfo<<endl;
1200 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1201 for (; it.current(); ++it) {
1202 it.current()->reserve(start, duration);
1203 }
1204}
1205
1206bool ResourceRequestCollection::isEmpty() const {
1207 TQPtrListIterator<ResourceGroupRequest> it(m_requests);
1208 for (; it.current(); ++it) {
1209 if (!it.current()->isEmpty())
1210 return false;
1211 }
1212 return true;
1213}
1214#ifndef NDEBUG
1215
1216void ResourceGroup::printDebug(TQString indent)
1217{
1218 kdDebug()<<indent<<" + Resource group: "<<m_name<<" id="<<m_id<<endl;
1219 indent += " !";
1220 TQPtrListIterator<Resource> it(m_resources);
1221 for ( ; it.current(); ++it)
1222 it.current()->printDebug(indent);
1223}
1224void Resource::printDebug(TQString indent)
1225{
1226 kdDebug()<<indent<<" + Resource: "<<m_name<<" id="<<m_id/*<<" Overbooked="<<isOverbooked()*/<<endl;
1227 TQIntDictIterator<Schedule> it(m_schedules);
1228 indent += " ";
1229 for (; it.current(); ++it) {
1230 it.current()->printDebug(indent);
1231 }
1232 indent += " !";
1233}
1234
1235void ResourceGroupRequest::printDebug(TQString indent)
1236{
1237 kdDebug()<<indent<<" + Request to group: "<<(m_group ? m_group->name() : "None")<<" units="<<m_units<<"%"<<endl;
1238 indent += " !";
1239 TQPtrListIterator<ResourceRequest> it(m_resourceRequests);
1240 for (; it.current(); ++it) {
1241 it.current()->printDebug(indent);
1242 }
1243}
1244
1245void ResourceRequest::printDebug(TQString indent)
1246{
1247 kdDebug()<<indent<<" + Request to resource: "<<(m_resource ? m_resource->name() : "None")<<" units="<<m_units<<"%"<<endl;
1248}
1249
1250void ResourceRequestCollection::printDebug(TQString indent)
1251{
1252 kdDebug()<<indent<<" + Resource requests:"<<endl;
1253 TQPtrListIterator<ResourceGroupRequest> it = m_requests;
1254 for (; it.current(); ++it) {
1255 it.current()->printDebug(indent+" ");
1256 }
1257}
1258#endif
1259
1260} //KPlato namespace
A resource (Resource) can be scheduled to be used at any time, this is represented internally with Ap...
Calendar defines the working and nonworking days and hours.
DateTime firstAvailableAfter(const DateTime &time, const DateTime &limit)
Find the first available time after time before limit.
DateTime firstAvailableBefore(const DateTime &time, const DateTime &limit)
Find the first available time backwards from time.
DateTime is a TQDateTime which knows about Duration.
Definition kptdatetime.h:33
static const Duration zeroDuration
This is useful for occasions where we need a zero duration.
This class represents any node in the project, a node can be a project or a subproject or any task.
Definition kptnode.h:57
bool removeResourceGroupId(const TQString &id)
Remove the resourcegroup with identity id from the register.
Definition kptproject.h:189
void insertResourceId(const TQString &id, const Resource *resource)
Insert the resource with identity id.
Definition kptproject.h:201
virtual Calendar * findCalendar(const TQString &id) const
Find the calendar with identity id.
Definition kptproject.h:205
void insertResourceGroupId(const TQString &id, const ResourceGroup *group)
Insert the resourcegroup with identity id.
Definition kptproject.h:192
bool removeResourceId(const TQString &id)
Remove the resource with identity id from the register.
Definition kptproject.h:198
This class represents a group of similar resources to be assigned to a task e.g.
Definition kptresource.h:66
void addRequiredResource(ResourceGroup *)
Manage the dependent resources.
ResourceGroup * getRequiredResource(int)
Manage the dependent resources.
void removeRequiredResource(int)
Manage the dependent resources.
void addResource(Resource *, Risk *)
Manage the resources in this list.
Any resource that is used by a task.
void takeSchedule(const Schedule *schedule)
Take, don't delete.
TQPtrList< Appointment > appointments()
Return the list of appointments for current schedule.
Calendar * calendar(bool local=false) const
Get the calendar for this resource.
void removeSchedule(Schedule *schedule)
Take, and delete.
DateTime availableBefore(const DateTime &time, const DateTime limit=DateTime(), bool checkAppointments=false) const
Find the first available time before time, within limit.
DateTime availableAfter(const DateTime &time, const DateTime limit=DateTime(), bool checkAppointments=false) const
Find the first available time after time, within limit.
virtual bool addAppointment(Appointment *appointment)
Adds appointment to current schedule.
Risk is associated with a resource/task pairing to indicate the planner's confidence in the estimated...
The Schedule class holds data calculated during project calculation and scheduling,...
Definition kptschedule.h:55
virtual Duration plannedEffort() const
Returns the total planned effort for this task (or subtasks)
virtual bool add(Appointment *appointment)
Adds appointment to this schedule only.
virtual void addAppointment(Schedule *, DateTime &, DateTime &, double=100)
Adds appointment to both this resource schedule and node schedule.
Definition kptschedule.h:94
TQPtrList< Appointment > & appointments()
Return the list of appointments.
Definition kptschedule.h:90