1 /* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004-2007, 2012 Dag Andersen <danders@get2net.dk>
4 Copyright (C) 2016 Dag Andersen <danders@get2net.dk>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 // clazy:excludeall=qstring-arg
23 #include "kptresource.h"
24
25 #include "kptlocale.h"
26 #include "kptaccount.h"
27 #include "kptappointment.h"
28 #include "kptproject.h"
29 #include "kpttask.h"
30 #include "kptdatetime.h"
31 #include "kptcalendar.h"
32 #include "kpteffortcostmap.h"
33 #include "kptschedule.h"
34 #include "kptxmlloaderobject.h"
35 #include "kptdebug.h"
36
37 #include <KoXmlReader.h>
38
39 #include <KLocalizedString>
40
41 #include <QLocale>
42
43
44 namespace KPlato
45 {
46
ResourceGroup()47 ResourceGroup::ResourceGroup()
48 : QObject(0),
49 m_blockChanged(false),
50 m_shared(false)
51 {
52 m_project = 0;
53 m_type = Type_Work;
54 //debugPlan<<"("<<this<<")";
55 }
56
ResourceGroup(const ResourceGroup * group)57 ResourceGroup::ResourceGroup(const ResourceGroup *group)
58 : QObject(0)
59 {
60 m_project = 0;
61 copy(group);
62 }
63
~ResourceGroup()64 ResourceGroup::~ResourceGroup() {
65 //debugPlan<<"("<<this<<")";
66 if (findId() == this) {
67 removeId(); // only remove myself (I may be just a working copy)
68 }
69 foreach (ResourceGroupRequest* r, m_requests) {
70 r->unregister(this);
71 }
72 while (!m_resources.isEmpty()) {
73 delete m_resources.takeFirst();
74 }
75 //debugPlan<<"("<<this<<")";
76 }
77
copy(const ResourceGroup * group)78 void ResourceGroup::copy(const ResourceGroup *group)
79 {
80 //m_project = group->m_project; //Don't copy
81 m_id = group->m_id;
82 m_type = group->m_type;
83 m_name = group->m_name;
84 }
85
blockChanged(bool on)86 void ResourceGroup::blockChanged(bool on)
87 {
88 m_blockChanged = on;
89 }
90
changed()91 void ResourceGroup::changed() {
92 if (m_project && !m_blockChanged) {
93 m_project->changed(this);
94 }
95 }
96
setId(const QString & id)97 void ResourceGroup::setId(const QString& id) {
98 //debugPlan<<id;
99 m_id = id;
100 }
101
setName(const QString & n)102 void ResourceGroup::setName(const QString& n)
103 {
104 m_name = n.trimmed();
105 changed();
106 }
107
setType(Type type)108 void ResourceGroup::setType(Type type)
109 {
110 m_type = type;
111 changed();
112 }
113
setType(const QString & type)114 void ResourceGroup::setType(const QString &type)
115 {
116 if (type == "Work")
117 setType(Type_Work);
118 else if (type == "Material")
119 setType(Type_Material);
120 else
121 setType(Type_Work);
122 }
123
typeToString(bool trans) const124 QString ResourceGroup::typeToString(bool trans) const {
125 return typeToStringList(trans).at(m_type);
126 }
127
typeToStringList(bool trans)128 QStringList ResourceGroup::typeToStringList(bool trans) {
129 // keep these in the same order as the enum!
130 return QStringList()
131 << (trans ? i18n("Work") : QString("Work"))
132 << (trans ? i18n("Material") : QString("Material"));
133 }
134
setProject(Project * project)135 void ResourceGroup::setProject(Project *project)
136 {
137 if (project != m_project) {
138 if (m_project) {
139 removeId();
140 }
141 }
142 m_project = project;
143 foreach (Resource *r, m_resources) {
144 r->setProject(project);
145 }
146 }
147
isScheduled() const148 bool ResourceGroup::isScheduled() const
149 {
150 foreach (Resource *r, m_resources) {
151 if (r->isScheduled()) {
152 return true;
153 }
154 }
155 return false;
156 }
157
isBaselined(long id) const158 bool ResourceGroup::isBaselined(long id) const
159 {
160 Q_UNUSED(id);
161 foreach (const Resource *r, m_resources) {
162 if (r->isBaselined()) {
163 return true;
164 }
165 }
166 return false;
167 }
168
169
addResource(int index,Resource * resource,Risk *)170 void ResourceGroup::addResource(int index, Resource* resource, Risk*) {
171 int i = index == -1 ? m_resources.count() : index;
172 resource->setParentGroup(this);
173 resource->setProject(m_project);
174 m_resources.insert(i, resource);
175 }
176
takeResource(Resource * resource)177 Resource *ResourceGroup::takeResource(Resource *resource) {
178 Resource *r = 0;
179 int i = m_resources.indexOf(resource);
180 if (i != -1) {
181 r = m_resources.takeAt(i);
182 r->setParentGroup(0);
183 r->setProject(0);
184 }
185 return r;
186 }
187
indexOf(const Resource * resource) const188 int ResourceGroup::indexOf(const Resource *resource) const
189 {
190 return m_resources.indexOf(const_cast<Resource*>(resource)); //???
191 }
192
getRisk(int)193 Risk* ResourceGroup::getRisk(int) {
194 return 0L;
195 }
196
addRequiredResource(ResourceGroup *)197 void ResourceGroup::addRequiredResource(ResourceGroup*) {
198 }
199
getRequiredResource(int)200 ResourceGroup* ResourceGroup::getRequiredResource(int) {
201 return 0L;
202 }
203
deleteRequiredResource(int)204 void ResourceGroup::deleteRequiredResource(int) {
205 }
206
load(KoXmlElement & element,XMLLoaderObject & status)207 bool ResourceGroup::load(KoXmlElement &element, XMLLoaderObject &status) {
208 //debugPlan;
209 setId(element.attribute("id"));
210 m_name = element.attribute("name");
211 setType(element.attribute("type"));
212 m_shared = element.attribute("shared", "0").toInt();
213
214 KoXmlNode n = element.firstChild();
215 for (; ! n.isNull(); n = n.nextSibling()) {
216 if (! n.isElement()) {
217 continue;
218 }
219 KoXmlElement e = n.toElement();
220 if (e.tagName() == "resource") {
221 // Load the resource
222 Resource *child = new Resource();
223 if (child->load(e, status)) {
224 addResource(-1, child, 0);
225 } else {
226 // TODO: Complain about this
227 delete child;
228 }
229 }
230 }
231 return true;
232 }
233
save(QDomElement & element) const234 void ResourceGroup::save(QDomElement &element) const {
235 //debugPlan;
236
237 QDomElement me = element.ownerDocument().createElement("resource-group");
238 element.appendChild(me);
239
240 me.setAttribute("id", m_id);
241 me.setAttribute("name", m_name);
242 me.setAttribute("type", typeToString());
243 me.setAttribute("shared", m_shared);
244
245 foreach (Resource *r, m_resources) {
246 r->save(me);
247 }
248 }
249
saveWorkPackageXML(QDomElement & element,const QList<Resource * > & lst) const250 void ResourceGroup::saveWorkPackageXML(QDomElement &element, const QList<Resource*> &lst) const
251 {
252 QDomElement me = element.ownerDocument().createElement("resource-group");
253 element.appendChild(me);
254
255 me.setAttribute("id", m_id);
256 me.setAttribute("name", m_name);
257
258 foreach (Resource *r, m_resources) {
259 if (lst.contains(r)) {
260 r->save(me);
261 }
262 }
263 }
264
initiateCalculation(Schedule & sch)265 void ResourceGroup::initiateCalculation(Schedule &sch) {
266 foreach (Resource *r, m_resources) {
267 r->initiateCalculation(sch);
268 }
269 clearNodes();
270 }
271
units() const272 int ResourceGroup::units() const {
273 int u = 0;
274 foreach (const Resource *r, m_resources) {
275 u += r->units();
276 }
277 return u;
278 }
279
findId(const QString & id) const280 ResourceGroup *ResourceGroup::findId(const QString &id) const {
281 return m_project ? m_project->findResourceGroup(id) : 0;
282 }
283
removeId(const QString & id)284 bool ResourceGroup::removeId(const QString &id) {
285 return m_project ? m_project->removeResourceGroupId(id): false;
286 }
287
insertId(const QString & id)288 void ResourceGroup::insertId(const QString &id) {
289 //debugPlan;
290 if (m_project)
291 m_project->insertResourceGroupId(id, this);
292 }
293
appointmentIntervals() const294 Appointment ResourceGroup::appointmentIntervals() const {
295 Appointment a;
296 foreach (Resource *r, m_resources) {
297 a += r->appointmentIntervals();
298 }
299 return a;
300 }
301
startTime(long id) const302 DateTime ResourceGroup::startTime(long id) const
303 {
304 DateTime dt;
305 foreach (Resource *r, m_resources) {
306 DateTime t = r->startTime(id);
307 if (! dt.isValid() || t < dt) {
308 dt = t;
309 }
310 }
311 return dt;
312 }
313
endTime(long id) const314 DateTime ResourceGroup::endTime(long id) const
315 {
316 DateTime dt;
317 foreach (Resource *r, m_resources) {
318 DateTime t = r->endTime(id);
319 if (! dt.isValid() || t > dt) {
320 dt = t;
321 }
322 }
323 return dt;
324 }
325
isShared() const326 bool ResourceGroup::isShared() const
327 {
328 return m_shared;
329 }
330
setShared(bool on)331 void ResourceGroup::setShared(bool on)
332 {
333 m_shared = on;
334 }
335
Resource()336 Resource::Resource()
337 : QObject(0), // atm QObject is only for casting
338 m_project(0),
339 m_parent(0),
340 m_autoAllocate(false),
341 m_currentSchedule(0),
342 m_blockChanged(false),
343 m_shared(false)
344 {
345 m_type = Type_Work;
346 m_units = 100; // %
347
348 // m_availableFrom = DateTime(QDate::currentDate(), QTime(0, 0, 0));
349 // m_availableUntil = m_availableFrom.addYears(2);
350
351 cost.normalRate = 100;
352 cost.overtimeRate = 0;
353 cost.fixed = 0;
354 cost.account = 0;
355 m_calendar = 0;
356 m_currentSchedule = 0;
357 //debugPlan<<"("<<this<<")";
358
359 // material: by default material is always available
360 for (int i = 1; i <= 7; ++i) {
361 CalendarDay *wd = m_materialCalendar.weekday(i);
362 wd->setState(CalendarDay::Working);
363 wd->addInterval(TimeInterval(QTime(0, 0, 0), 24*60*60*1000));
364 }
365 }
366
Resource(Resource * resource)367 Resource::Resource(Resource *resource)
368 : QObject(0), // atm QObject is only for casting
369 m_project(0),
370 m_parent(0),
371 m_currentSchedule(0),
372 m_shared(false)
373 {
374 //debugPlan<<"("<<this<<") from ("<<resource<<")";
375 copy(resource);
376 }
377
~Resource()378 Resource::~Resource() {
379 //debugPlan<<"("<<this<<")";
380 if (findId() == this) {
381 removeId(); // only remove myself (I may be just a working copy)
382 }
383 removeRequests();
384 foreach (Schedule *s, m_schedules) {
385 delete s;
386 }
387 clearExternalAppointments();
388 if (cost.account) {
389 cost.account->removeRunning(*this);
390 }
391 }
392
removeRequests()393 void Resource::removeRequests() {
394 foreach (ResourceRequest *r, m_requests) {
395 r->setResource(0); // avoid the request to mess with my list
396 r->parent()->deleteResourceRequest(r);
397 }
398 m_requests.clear();
399 }
400
setId(const QString & id)401 void Resource::setId(const QString& id) {
402 //debugPlan<<id;
403 m_id = id;
404 }
405
copy(Resource * resource)406 void Resource::copy(Resource *resource) {
407 m_project = 0; // NOTE: Don't copy, will be set when added to a project
408 //m_appointments = resource->appointments(); // Note
409 m_id = resource->id();
410 m_name = resource->name();
411 m_initials = resource->initials();
412 m_email = resource->email();
413 m_autoAllocate = resource->m_autoAllocate;
414 m_availableFrom = resource->availableFrom();
415 m_availableUntil = resource->availableUntil();
416
417 m_units = resource->units(); // available units in percent
418
419 m_type = resource->type();
420
421 cost.normalRate = resource->normalRate();
422 cost.overtimeRate = resource->overtimeRate();
423 cost.account = resource->account();
424 m_calendar = resource->m_calendar;
425
426 m_requiredIds = resource->requiredIds();
427 m_teamMembers = resource->m_teamMembers;
428
429 // hmmmm
430 //m_externalAppointments = resource->m_externalAppointments;
431 //m_externalNames = resource->m_externalNames;
432 }
433
blockChanged(bool on)434 void Resource::blockChanged(bool on)
435 {
436 m_blockChanged = on;
437 }
438
changed()439 void Resource::changed()
440 {
441 if (m_project && !m_blockChanged) {
442 m_project->changed(this);
443 }
444 }
445
setType(Type type)446 void Resource::setType(Type type)
447 {
448 m_type = type;
449 changed();
450 }
451
setType(const QString & type)452 void Resource::setType(const QString &type)
453 {
454 if (type == "Work")
455 setType(Type_Work);
456 else if (type == "Material")
457 setType(Type_Material);
458 else if (type == "Team")
459 setType(Type_Team);
460 else
461 setType(Type_Work);
462 }
463
typeToString(bool trans) const464 QString Resource::typeToString(bool trans) const {
465 return typeToStringList(trans).at(m_type);
466 }
467
typeToStringList(bool trans)468 QStringList Resource::typeToStringList(bool trans) {
469 // keep these in the same order as the enum!
470 return QStringList()
471 << (trans ? xi18nc("@item:inlistbox resource type", "Work") : QString("Work"))
472 << (trans ? xi18nc("@item:inlistbox resource type", "Material") : QString("Material"))
473 << (trans ? xi18nc("@item:inlistbox resource type", "Team") : QString("Team"));
474 }
475
setName(const QString & n)476 void Resource::setName(const QString &n)
477 {
478 m_name = n.trimmed();
479 changed();
480 }
481
setInitials(const QString & initials)482 void Resource::setInitials(const QString &initials)
483 {
484 m_initials = initials.trimmed();
485 changed();
486 }
487
setEmail(const QString & email)488 void Resource::setEmail(const QString &email)
489 {
490 m_email = email;
491 changed();
492 }
493
autoAllocate() const494 bool Resource::autoAllocate() const
495 {
496 return m_autoAllocate;
497 }
498
setAutoAllocate(bool on)499 void Resource::setAutoAllocate(bool on)
500 {
501 if (m_autoAllocate != on) {
502 m_autoAllocate = on;
503 changed();
504 }
505 }
506
setUnits(int units)507 void Resource::setUnits(int units)
508 {
509 m_units = units;
510 m_workinfocache.clear();
511 changed();
512 }
513
calendar(bool local) const514 Calendar *Resource::calendar(bool local) const {
515 if (local || m_calendar) {
516 return m_calendar;
517 }
518 // No calendar is set, try default calendar
519 Calendar *c = 0;
520 if (m_type == Type_Work && project()) {
521 c = project()->defaultCalendar();
522 } else if (m_type == Type_Material) {
523 c = const_cast<Calendar*>(&m_materialCalendar);
524 }
525 return c;
526 }
527
setCalendar(Calendar * calendar)528 void Resource::setCalendar(Calendar *calendar)
529 {
530 m_calendar = calendar;
531 m_workinfocache.clear();
532 changed();
533 }
534
firstAvailableAfter(const DateTime &,const DateTime &) const535 DateTime Resource::firstAvailableAfter(const DateTime &, const DateTime &) const {
536 return DateTime();
537 }
538
getBestAvailableTime(const Duration &)539 DateTime Resource::getBestAvailableTime(const Duration &/*duration*/) {
540 return DateTime();
541 }
542
getBestAvailableTime(const DateTime &,const Duration &)543 DateTime Resource::getBestAvailableTime(const DateTime &/*after*/, const Duration &/*duration*/) {
544 return DateTime();
545 }
546
load(KoXmlElement & element,XMLLoaderObject & status)547 bool Resource::load(KoXmlElement &element, XMLLoaderObject &status) {
548 //debugPlan;
549 const Locale *locale = status.project().locale();
550 QString s;
551 setId(element.attribute("id"));
552 m_name = element.attribute("name");
553 m_initials = element.attribute("initials");
554 m_email = element.attribute("email");
555 m_autoAllocate = (bool)(element.attribute("auto-allocate", "0").toInt());
556 setType(element.attribute("type"));
557 m_shared = element.attribute("shared", "0").toInt();
558 m_calendar = status.project().findCalendar(element.attribute("calendar-id"));
559 m_units = element.attribute("units", "100").toInt();
560 s = element.attribute("available-from");
561 if (!s.isEmpty())
562 m_availableFrom = DateTime::fromString(s, status.projectTimeZone());
563 s = element.attribute("available-until");
564 if (!s.isEmpty())
565 m_availableUntil = DateTime::fromString(s, status.projectTimeZone());
566
567 // NOTE: money was earlier (2.x) saved with symbol so we need to handle that
568 QString money = element.attribute("normal-rate");
569 bool ok = false;
570 cost.normalRate = money.toDouble(&ok);
571 if (!ok) {
572 cost.normalRate = locale->readMoney(money);
573 debugPlan<<"normal-rate failed, tried readMoney()"<<money<<"->"<<cost.normalRate;;
574 }
575 money = element.attribute("overtime-rate");
576 cost.overtimeRate = money.toDouble(&ok);
577 if (!ok) {
578 cost.overtimeRate = locale->readMoney(money);
579 debugPlan<<"overtime-rate failed, tried readMoney()"<<money<<"->"<<cost.overtimeRate;;
580 }
581 cost.account = status.project().accounts().findAccount(element.attribute("account"));
582
583 KoXmlElement e;
584 KoXmlElement parent = element.namedItem("required-resources").toElement();
585 forEachElement(e, parent) {
586 if (e.nodeName() == "resource") {
587 QString id = e.attribute("id");
588 if (id.isEmpty()) {
589 warnPlan<<"Missing resource id";
590 continue;
591 }
592 addRequiredId(id);
593 }
594 }
595 parent = element.namedItem("external-appointments").toElement();
596 forEachElement(e, parent) {
597 if (e.nodeName() == "project") {
598 QString id = e.attribute("id");
599 if (id.isEmpty()) {
600 errorPlan<<"Missing project id";
601 continue;
602 }
603 clearExternalAppointments(id); // in case...
604 AppointmentIntervalList lst;
605 lst.loadXML(e, status);
606 Appointment *a = new Appointment();
607 a->setIntervals(lst);
608 a->setAuxcilliaryInfo(e.attribute("name", "Unknown"));
609 m_externalAppointments[ id ] = a;
610 }
611 }
612 // Do not load cache from old format, there was a bug (now fixed).
613 if (status.version() >= "0.6.7") {
614 loadCalendarIntervalsCache(element, status);
615 }
616 return true;
617 }
618
requiredResources() const619 QList<Resource*> Resource::requiredResources() const
620 {
621 QList<Resource*> lst;
622 foreach (const QString &s, m_requiredIds) {
623 Resource *r = findId(s);
624 if (r) {
625 lst << r;
626 }
627 }
628 return lst;
629 }
630
setRequiredIds(const QStringList & ids)631 void Resource::setRequiredIds(const QStringList &ids)
632 {
633 debugPlan<<ids;
634 m_requiredIds = ids;
635 }
636
addRequiredId(const QString & id)637 void Resource::addRequiredId(const QString &id)
638 {
639 if (! id.isEmpty() && ! m_requiredIds.contains(id)) {
640 m_requiredIds << id;
641 }
642 }
643
644
setAccount(Account * account)645 void Resource::setAccount(Account *account)
646 {
647 if (cost.account) {
648 cost.account->removeRunning(*this);
649 }
650 cost.account = account;
651 changed();
652 }
653
save(QDomElement & element) const654 void Resource::save(QDomElement &element) const {
655 //debugPlan;
656 QDomElement me = element.ownerDocument().createElement("resource");
657 element.appendChild(me);
658
659 if (calendar(true))
660 me.setAttribute("calendar-id", m_calendar->id());
661 me.setAttribute("id", m_id);
662 me.setAttribute("name", m_name);
663 me.setAttribute("initials", m_initials);
664 me.setAttribute("email", m_email);
665 me.setAttribute("auto-allocate", m_autoAllocate);
666 me.setAttribute("type", typeToString());
667 me.setAttribute("shared", m_shared);
668 me.setAttribute("units", QString::number(m_units));
669 if (m_availableFrom.isValid()) {
670 me.setAttribute("available-from", m_availableFrom.toString(Qt::ISODate));
671 }
672 if (m_availableUntil.isValid()) {
673 me.setAttribute("available-until", m_availableUntil.toString(Qt::ISODate));
674 }
675 QString money;
676 me.setAttribute("normal-rate", money.setNum(cost.normalRate));
677 me.setAttribute("overtime-rate", money.setNum(cost.overtimeRate));
678 if (cost.account) {
679 me.setAttribute("account", cost.account->name());
680 }
681
682 if (! m_requiredIds.isEmpty()) {
683 QDomElement e = me.ownerDocument().createElement("required-resources");
684 me.appendChild(e);
685 foreach (const QString &id, m_requiredIds) {
686 QDomElement el = e.ownerDocument().createElement("resource");
687 e.appendChild(el);
688 el.setAttribute("id", id);
689 }
690 }
691
692 if (! m_externalAppointments.isEmpty()) {
693 QDomElement e = me.ownerDocument().createElement("external-appointments");
694 me.appendChild(e);
695 foreach (const QString &id, m_externalAppointments.uniqueKeys()) {
696 QDomElement el = e.ownerDocument().createElement("project");
697 e.appendChild(el);
698 el.setAttribute("id", id);
699 el.setAttribute("name", m_externalAppointments[ id ]->auxcilliaryInfo());
700 m_externalAppointments[ id ]->intervals().saveXML(el);
701 }
702 }
703 saveCalendarIntervalsCache(me);
704 }
705
isAvailable(Task *)706 bool Resource::isAvailable(Task * /*task*/) {
707 bool busy = false;
708 /*
709 foreach (Appointment *a, m_appointments) {
710 if (a->isBusy(task->startTime(), task->endTime())) {
711 busy = true;
712 break;
713 }
714 }*/
715 return !busy;
716 }
717
appointments(long id) const718 QList<Appointment*> Resource::appointments(long id) const {
719 Schedule *s = schedule(id);
720 if (s == 0) {
721 return QList<Appointment*>();
722 }
723 return s->appointments();
724 }
725
addAppointment(Appointment * appointment)726 bool Resource::addAppointment(Appointment *appointment) {
727 if (m_currentSchedule)
728 return m_currentSchedule->add(appointment);
729 return false;
730 }
731
addAppointment(Appointment * appointment,Schedule & main)732 bool Resource::addAppointment(Appointment *appointment, Schedule &main) {
733 Schedule *s = findSchedule(main.id());
734 if (s == 0) {
735 s = createSchedule(&main);
736 }
737 appointment->setResource(s);
738 return s->add(appointment);
739 }
740
741 // called from makeAppointment
addAppointment(Schedule * node,const DateTime & start,const DateTime & end,double load)742 void Resource::addAppointment(Schedule *node, const DateTime &start, const DateTime &end, double load)
743 {
744 Q_ASSERT(start < end);
745 Schedule *s = findSchedule(node->id());
746 if (s == 0) {
747 s = createSchedule(node->parent());
748 }
749 s->setCalculationMode(node->calculationMode());
750 //debugPlan<<"id="<<node->id()<<" Mode="<<node->calculationMode()<<""<<start<<end;
751 s->addAppointment(node, start, end, load);
752 }
753
initiateCalculation(Schedule & sch)754 void Resource::initiateCalculation(Schedule &sch) {
755 m_currentSchedule = createSchedule(&sch);
756 }
757
schedule(long id) const758 Schedule *Resource::schedule(long id) const
759 {
760 return id == -1 ? m_currentSchedule : findSchedule(id);
761 }
762
isBaselined(long id) const763 bool Resource::isBaselined(long id) const
764 {
765 if (m_type == Resource::Type_Team) {
766 foreach (const Resource *r, teamMembers()) {
767 if (r->isBaselined(id)) {
768 return true;
769 }
770 }
771 return false;
772 }
773 Schedule *s = schedule(id);
774 return s ? s->isBaselined() : false;
775 }
776
findSchedule(long id) const777 Schedule *Resource::findSchedule(long id) const
778 {
779 if (m_schedules.contains(id)) {
780 return m_schedules[ id ];
781 }
782 if (id == CURRENTSCHEDULE) {
783 return m_currentSchedule;
784 }
785 if (id == BASELINESCHEDULE || id == ANYSCHEDULED) {
786 foreach (Schedule *s, m_schedules) {
787 if (s->isBaselined()) {
788 return s;
789 }
790 }
791 }
792 if (id == ANYSCHEDULED) {
793 foreach (Schedule *s, m_schedules) {
794 if (s->isScheduled()) {
795 return s;
796 }
797 }
798 }
799 return 0;
800 }
801
isScheduled() const802 bool Resource::isScheduled() const
803 {
804 foreach (Schedule *s, m_schedules) {
805 if (s->isScheduled()) {
806 return true;
807 }
808 }
809 return false;
810 }
811
deleteSchedule(Schedule * schedule)812 void Resource::deleteSchedule(Schedule *schedule) {
813 takeSchedule(schedule);
814 delete schedule;
815 }
816
takeSchedule(const Schedule * schedule)817 void Resource::takeSchedule(const Schedule *schedule) {
818 if (schedule == 0)
819 return;
820 if (m_currentSchedule == schedule)
821 m_currentSchedule = 0;
822 m_schedules.take(schedule->id());
823 }
824
addSchedule(Schedule * schedule)825 void Resource::addSchedule(Schedule *schedule) {
826 if (schedule == 0)
827 return;
828 m_schedules.remove(schedule->id());
829 m_schedules.insert(schedule->id(), schedule);
830 }
831
createSchedule(const QString & name,int type,long id)832 ResourceSchedule *Resource::createSchedule(const QString& name, int type, long id) {
833 ResourceSchedule *sch = new ResourceSchedule(this, name, (Schedule::Type)type, id);
834 addSchedule(sch);
835 return sch;
836 }
837
createSchedule(Schedule * parent)838 ResourceSchedule *Resource::createSchedule(Schedule *parent) {
839 ResourceSchedule *sch = new ResourceSchedule(parent, this);
840 //debugPlan<<"id="<<sch->id();
841 addSchedule(sch);
842 return sch;
843 }
844
timeZone() const845 QTimeZone Resource::timeZone() const
846 {
847 Calendar *cal = calendar();
848
849 return
850 cal ? cal->timeZone() :
851 m_project ? m_project->timeZone() :
852 /* else */ QTimeZone();
853 }
854
requiredAvailable(Schedule * node,const DateTime & start,const DateTime & end) const855 DateTimeInterval Resource::requiredAvailable(Schedule *node, const DateTime &start, const DateTime &end) const
856 {
857 Q_ASSERT(m_currentSchedule);
858 DateTimeInterval interval(start, end);
859 #ifndef PLAN_NLOGDEBUG
860 if (m_currentSchedule) m_currentSchedule->logDebug(QString("Required available in interval: %1").arg(interval.toString()));
861 #endif
862 DateTime availableFrom = m_availableFrom.isValid() ? m_availableFrom : (m_project ? m_project->constraintStartTime() : DateTime());
863 DateTime availableUntil = m_availableUntil.isValid() ? m_availableUntil : (m_project ? m_project->constraintEndTime() : DateTime());
864 DateTimeInterval x = interval.limitedTo(availableFrom, availableUntil);
865 if (calendar() == 0) {
866 #ifndef PLAN_NLOGDEBUG
867 if (m_currentSchedule) m_currentSchedule->logDebug(QString("Required available: no calendar, %1").arg(x.toString()));
868 #endif
869 return x;
870 }
871 DateTimeInterval i = m_currentSchedule->firstBookedInterval(x, node);
872 if (i.isValid()) {
873 #ifndef PLAN_NLOGDEBUG
874 if (m_currentSchedule) m_currentSchedule->logDebug(QString("Required available: booked, %1").arg(i.toString()));
875 #endif
876 return i;
877 }
878 i = calendar()->firstInterval(x.first, x.second, m_currentSchedule);
879 #ifndef PLAN_NLOGDEBUG
880 if (m_currentSchedule) m_currentSchedule->logDebug(QString("Required first available in %1: %2").arg(x.toString()).arg(i.toString()));
881 #endif
882 return i;
883 }
884
makeAppointment(Schedule * node,const DateTime & from,const DateTime & end,int load,const QList<Resource * > & required)885 void Resource::makeAppointment(Schedule *node, const DateTime &from, const DateTime &end, int load, const QList<Resource*> &required) {
886 //debugPlan<<"node id="<<node->id()<<" mode="<<node->calculationMode()<<""<<from<<" -"<<end;
887 if (!from.isValid() || !end.isValid()) {
888 m_currentSchedule->logWarning(i18n("Make appointments: Invalid time"));
889 return;
890 }
891 Calendar *cal = calendar();
892 if (cal == 0) {
893 m_currentSchedule->logWarning(i18n("Resource %1 has no calendar defined", m_name));
894 return;
895 }
896 #ifndef PLAN_NLOGDEBUG
897 if (m_currentSchedule) {
898 QStringList lst; foreach (Resource *r, required) { lst << r->name(); }
899 m_currentSchedule->logDebug(QString("Make appointments from %1 to %2 load=%4, required: %3").arg(from.toString()).arg(end.toString()).arg(lst.join(",")).arg(load));
900 }
901 #endif
902 AppointmentIntervalList lst = workIntervals(from, end, m_currentSchedule);
903 foreach (const AppointmentInterval &i, lst.map()) {
904 m_currentSchedule->addAppointment(node, i.startTime(), i.endTime(), load);
905 foreach (Resource *r, required) {
906 r->addAppointment(node, i.startTime(), i.endTime(), r->units()); //FIXME: units may not be correct
907 }
908 }
909 }
910
makeAppointment(Schedule * node,int load,const QList<Resource * > & required)911 void Resource::makeAppointment(Schedule *node, int load, const QList<Resource*> &required) {
912 //debugPlan<<m_name<<": id="<<m_currentSchedule->id()<<" mode="<<m_currentSchedule->calculationMode()<<node->node()->name()<<": id="<<node->id()<<" mode="<<node->calculationMode()<<""<<node->startTime;
913 QLocale locale;
914 if (!node->startTime.isValid()) {
915 m_currentSchedule->logWarning(i18n("Make appointments: Node start time is not valid"));
916 return;
917 }
918 if (!node->endTime.isValid()) {
919 m_currentSchedule->logWarning(i18n("Make appointments: Node end time is not valid"));
920 return;
921 }
922 if (m_type == Type_Team) {
923 #ifndef PLAN_NLOGDEBUG
924 m_currentSchedule->logDebug("Make appointments to team " + m_name);
925 #endif
926 Duration e;
927 foreach (Resource *r, teamMembers()) {
928 r->makeAppointment(node, load, required);
929 }
930 return;
931 }
932 node->resourceNotAvailable = false;
933 node->workStartTime = DateTime();
934 node->workEndTime = DateTime();
935 Calendar *cal = calendar();
936 if (m_type == Type_Material) {
937 DateTime from = availableAfter(node->startTime, node->endTime);
938 DateTime end = availableBefore(node->endTime, node->startTime);
939 if (!from.isValid() || !end.isValid()) {
940 return;
941 }
942 if (cal == 0) {
943 // Allocate the whole period
944 addAppointment(node, from, end, m_units);
945 return;
946 }
947 makeAppointment(node, from, end, load);
948 return;
949 }
950 if (!cal) {
951 m_currentSchedule->logWarning(i18n("Resource %1 has no calendar defined", m_name));
952 return;
953 }
954 DateTime time = node->startTime;
955 DateTime end = node->endTime;
956 if (time == end) {
957 #ifndef PLAN_NLOGDEBUG
958 m_currentSchedule->logDebug(QString("Task '%1' start time == end time: %2").arg(node->node()->name(), time.toString(Qt::ISODate)));
959 #endif
960 node->resourceNotAvailable = true;
961 return;
962 }
963 time = availableAfter(time, end);
964 if (!time.isValid()) {
965 m_currentSchedule->logWarning(i18n("Resource %1 not available in interval: %2 to %3", m_name, locale.toString(node->startTime, QLocale::ShortFormat), locale.toString(end, QLocale::ShortFormat)));
966 node->resourceNotAvailable = true;
967 return;
968 }
969 end = availableBefore(end, time);
970 foreach (Resource *r, required) {
971 time = r->availableAfter(time, end);
972 end = r->availableBefore(end, time);
973 if (! (time.isValid() && end.isValid())) {
974 #ifndef PLAN_NLOGDEBUG
975 if (m_currentSchedule) m_currentSchedule->logDebug("The required resource '" + r->name() + "'is not available in interval:" + node->startTime.toString() + ',' + node->endTime.toString());
976 #endif
977 break;
978 }
979 }
980 if (!end.isValid()) {
981 m_currentSchedule->logWarning(i18n("Resource %1 not available in interval: %2 to %3", m_name, locale.toString(time, QLocale::ShortFormat), locale.toString(node->endTime, QLocale::ShortFormat)));
982 node->resourceNotAvailable = true;
983 return;
984 }
985 //debugPlan<<time.toString()<<" to"<<end.toString();
986 makeAppointment(node, time, end, load, required);
987 }
988
workIntervals(const DateTime & from,const DateTime & until) const989 AppointmentIntervalList Resource::workIntervals(const DateTime &from, const DateTime &until) const
990 {
991 return workIntervals(from, until, 0);
992 }
993
workIntervals(const DateTime & from,const DateTime & until,Schedule * sch) const994 AppointmentIntervalList Resource::workIntervals(const DateTime &from, const DateTime &until, Schedule *sch) const
995 {
996 Calendar *cal = calendar();
997 if (cal == 0) {
998 return AppointmentIntervalList();
999 }
1000 // update cache
1001 calendarIntervals(from, until);
1002 AppointmentIntervalList work = m_workinfocache.intervals.extractIntervals(from, until);
1003 if (sch && ! sch->allowOverbooking()) {
1004 foreach (const Appointment *a, sch->appointments(sch->calculationMode())) {
1005 work -= a->intervals();
1006 }
1007 foreach (const Appointment *a, m_externalAppointments) {
1008 work -= a->intervals();
1009 }
1010 }
1011 return work;
1012 }
1013
calendarIntervals(const DateTime & dtFrom,const DateTime & dtUntil) const1014 void Resource::calendarIntervals(const DateTime &dtFrom, const DateTime &dtUntil) const
1015 {
1016 Calendar *cal = calendar();
1017 if (cal == 0) {
1018 m_workinfocache.clear();
1019 return;
1020 }
1021 if (cal->cacheVersion() != m_workinfocache.version) {
1022 m_workinfocache.clear();
1023 m_workinfocache.version = cal->cacheVersion();
1024 }
1025 const DateTime from = dtFrom.toTimeZone(timeZone());
1026 const DateTime until = dtUntil.toTimeZone(timeZone());
1027 if (! m_workinfocache.isValid()) {
1028 // First time
1029 // debugPlan<<"First time:"<<from<<until;
1030 m_workinfocache.start = from;
1031 m_workinfocache.end = until;
1032 m_workinfocache.intervals = cal->workIntervals(from, until, m_units);
1033 // debugPlan<<"calendarIntervals (first):"<<m_workinfocache.intervals;
1034 } else {
1035 if (from < m_workinfocache.start) {
1036 // debugPlan<<"Add to start:"<<from<<m_workinfocache.start;
1037 m_workinfocache.intervals += cal->workIntervals(from, m_workinfocache.start, m_units);
1038 m_workinfocache.start = from;
1039 // debugPlan<<"calendarIntervals (start):"<<m_workinfocache.intervals;
1040 }
1041 if (until > m_workinfocache.end) {
1042 // debugPlan<<"Add to end:"<<m_workinfocache.end<<until;
1043 m_workinfocache.intervals += cal->workIntervals(m_workinfocache.end, until, m_units);
1044 m_workinfocache.end = until;
1045 // debugPlan<<"calendarIntervals: (end)"<<m_workinfocache.intervals;
1046 }
1047 }
1048 }
1049
loadCalendarIntervalsCache(const KoXmlElement & element,XMLLoaderObject & status)1050 bool Resource::loadCalendarIntervalsCache(const KoXmlElement &element, XMLLoaderObject &status)
1051 {
1052 KoXmlElement e = element.namedItem("work-intervals-cache").toElement();
1053 if (e.isNull()) {
1054 errorPlan<<"No 'work-intervals-cache' element";
1055 return true;
1056 }
1057 m_workinfocache.load(e, status);
1058 return true;
1059 }
1060
saveCalendarIntervalsCache(QDomElement & element) const1061 void Resource::saveCalendarIntervalsCache(QDomElement &element) const
1062 {
1063 QDomElement me = element.ownerDocument().createElement("work-intervals-cache");
1064 element.appendChild(me);
1065 m_workinfocache.save(me);
1066 }
1067
firstAvailableAfter(const DateTime & time,const DateTime & limit,Calendar * cal,Schedule * sch) const1068 DateTime Resource::WorkInfoCache::firstAvailableAfter(const DateTime &time, const DateTime &limit, Calendar *cal, Schedule *sch) const
1069 {
1070 QMultiMap<QDate, AppointmentInterval>::const_iterator it = intervals.map().constEnd();
1071 if (start.isValid() && start <= time) {
1072 // possibly useful cache
1073 it = intervals.map().lowerBound(time.date());
1074 }
1075 if (it == intervals.map().constEnd()) {
1076 // nothing cached, check the old way
1077 DateTime t = cal ? cal->firstAvailableAfter(time, limit, sch) : DateTime();
1078 return t;
1079 }
1080 AppointmentInterval inp(time, limit);
1081 for (; it != intervals.map().constEnd() && it.key() <= limit.date(); ++it) {
1082 if (! it.value().intersects(inp) && it.value() < inp) {
1083 continue;
1084 }
1085 if (sch) {
1086 DateTimeInterval ti = sch->available(DateTimeInterval(it.value().startTime(), it.value().endTime()));
1087 if (ti.isValid() && ti.second > time && ti.first < limit) {
1088 ti.first = qMax(ti.first, time);
1089 return ti.first;
1090 }
1091 } else {
1092 DateTime t = qMax(it.value().startTime(), time);
1093 return t;
1094 }
1095 }
1096 if (it == intervals.map().constEnd()) {
1097 // ran out of cache, check the old way
1098 DateTime t = cal ? cal->firstAvailableAfter(time, limit, sch) : DateTime();
1099 return t;
1100 }
1101 return DateTime();
1102 }
1103
firstAvailableBefore(const DateTime & time,const DateTime & limit,Calendar * cal,Schedule * sch) const1104 DateTime Resource::WorkInfoCache::firstAvailableBefore(const DateTime &time, const DateTime &limit, Calendar *cal, Schedule *sch) const
1105 {
1106 if (time <= limit) {
1107 return DateTime();
1108 }
1109 QMultiMap<QDate, AppointmentInterval>::const_iterator it = intervals.map().constBegin();
1110 if (time.isValid() && limit.isValid() && end.isValid() && end >= time && ! intervals.isEmpty()) {
1111 // possibly useful cache
1112 it = intervals.map().upperBound(time.date());
1113 }
1114 if (it == intervals.map().constBegin()) {
1115 // nothing cached, check the old way
1116 DateTime t = cal ? cal->firstAvailableBefore(time, limit, sch) : DateTime();
1117 return t;
1118 }
1119 AppointmentInterval inp(limit, time);
1120 for (--it; it != intervals.map().constBegin() && it.key() >= limit.date(); --it) {
1121 if (! it.value().intersects(inp) && inp < it.value()) {
1122 continue;
1123 }
1124 if (sch) {
1125 DateTimeInterval ti = sch->available(DateTimeInterval(it.value().startTime(), it.value().endTime()));
1126 if (ti.isValid() && ti.second > limit) {
1127 ti.second = qMin(ti.second, time);
1128 return ti.second;
1129 }
1130 } else {
1131 DateTime t = qMin(it.value().endTime(), time);
1132 return t;
1133 }
1134 }
1135 if (it == intervals.map().constBegin()) {
1136 // ran out of cache, check the old way
1137 DateTime t = cal ? cal->firstAvailableBefore(time, limit, sch) : DateTime();
1138 return t;
1139 }
1140 return DateTime();
1141 }
1142
load(const KoXmlElement & element,XMLLoaderObject & status)1143 bool Resource::WorkInfoCache::load(const KoXmlElement &element, XMLLoaderObject &status)
1144 {
1145 clear();
1146 version = element.attribute("version").toInt();
1147 effort = Duration::fromString(element.attribute("effort"));
1148 // DateTime should always be saved in the projects timezone,
1149 // but due to a bug (fixed) this did not always happen.
1150 // This code takes care of this situation.
1151 start = QDateTime::fromString(element.attribute("start"), Qt::ISODate).toTimeZone(status.projectTimeZone());
1152 end = QDateTime::fromString(element.attribute("end"), Qt::ISODate).toTimeZone(status.projectTimeZone());
1153 KoXmlElement e = element.namedItem("intervals").toElement();
1154 if (! e.isNull()) {
1155 intervals.loadXML(e, status);
1156 }
1157 //debugPlan<<*this;
1158 return true;
1159 }
1160
save(QDomElement & element) const1161 void Resource::WorkInfoCache::save(QDomElement &element) const
1162 {
1163 element.setAttribute("version", QString::number(version));
1164 element.setAttribute("effort", effort.toString());
1165 element.setAttribute("start", start.toString(Qt::ISODate));
1166 element.setAttribute("end", end.toString(Qt::ISODate));
1167 QDomElement me = element.ownerDocument().createElement("intervals");
1168 element.appendChild(me);
1169
1170 intervals.saveXML(me);
1171 }
1172
effort(const DateTime & start,const Duration & duration,int units,bool backward,const QList<Resource * > & required) const1173 Duration Resource::effort(const DateTime& start, const Duration& duration, int units, bool backward, const QList< Resource* >& required) const
1174 {
1175 return effort(m_currentSchedule, start, duration, units, backward, required);
1176 }
1177
1178 // the amount of effort we can do within the duration
effort(Schedule * sch,const DateTime & start,const Duration & duration,int units,bool backward,const QList<Resource * > & required) const1179 Duration Resource::effort(Schedule *sch, const DateTime &start, const Duration &duration, int units, bool backward, const QList<Resource*> &required) const
1180 {
1181 //debugPlan<<m_name<<": ("<<(backward?"B)":"F)")<<start<<" for duration"<<duration.toString(Duration::Format_Day);
1182 #if 0
1183 if (sch) sch->logDebug(QString("Check effort in interval %1: %2, %3").arg(backward?"backward":"forward").arg(start.toString()).arg((backward?start-duration:start+duration).toString()));
1184 #endif
1185 Duration e;
1186 if (duration == 0 || m_units == 0 || units == 0) {
1187 warnPlan<<"zero duration or zero units";
1188 return e;
1189 }
1190 if (m_type == Type_Team) {
1191 errorPlan<<"A team resource cannot deliver any effort";
1192 return e;
1193 }
1194 Calendar *cal = calendar();
1195 if (cal == 0) {
1196 if (sch) sch->logWarning(i18n("Resource %1 has no calendar defined", m_name));
1197 return e;
1198 }
1199 DateTime from;
1200 DateTime until;
1201 if (backward) {
1202 from = availableAfter(start - duration, start, sch);
1203 until = availableBefore(start, start - duration, sch);
1204 } else {
1205 from = availableAfter(start, start + duration, sch);
1206 until = availableBefore(start + duration, start, sch);
1207 }
1208 if (! (from.isValid() && until.isValid())) {
1209 #ifndef PLAN_NLOGDEBUG
1210 if (sch) sch->logDebug("Resource not available in interval:" + start.toString() + ',' + (start+duration).toString());
1211 #endif
1212 } else {
1213 foreach (Resource *r, required) {
1214 from = r->availableAfter(from, until);
1215 until = r->availableBefore(until, from);
1216 if (! (from.isValid() && until.isValid())) {
1217 #ifndef PLAN_NLOGDEBUG
1218 if (sch) sch->logDebug("The required resource '" + r->name() + "'is not available in interval:" + start.toString() + ',' + (start+duration).toString());
1219 #endif
1220 break;
1221 }
1222 }
1223 }
1224 if (from.isValid() && until.isValid()) {
1225 #ifndef PLAN_NLOGDEBUG
1226 if (sch && until < from) sch->logDebug(" until < from: until=" + until.toString() + " from=" + from.toString());
1227 #endif
1228 e = workIntervals(from, until).effort(from, until) * units / 100;
1229 if (sch && (! sch->allowOverbooking() || sch->allowOverbookingState() == Schedule::OBS_Deny)) {
1230 Duration avail = workIntervals(from, until, sch).effort(from, until);
1231 if (avail < e) {
1232 e = avail;
1233 }
1234 }
1235 // e = (cal->effort(from, until, sch)) * m_units / 100;
1236 }
1237 //debugPlan<<m_name<<start<<" e="<<e.toString(Duration::Format_Day)<<" ("<<m_units<<")";
1238 #ifndef PLAN_NLOGDEBUG
1239 if (sch) sch->logDebug(QString("effort: %1 for %2 hours = %3").arg(start.toString()).arg(duration.toString(Duration::Format_HourFraction)).arg(e.toString(Duration::Format_HourFraction)));
1240 #endif
1241 return e;
1242 }
1243
availableAfter(const DateTime & time,const DateTime & limit) const1244 DateTime Resource::availableAfter(const DateTime &time, const DateTime &limit) const {
1245 return availableAfter(time, limit, m_currentSchedule);
1246 }
1247
availableBefore(const DateTime & time,const DateTime & limit) const1248 DateTime Resource::availableBefore(const DateTime &time, const DateTime &limit) const {
1249 return availableBefore(time, limit, m_currentSchedule);
1250 }
1251
availableAfter(const DateTime & time,const DateTime & limit,Schedule * sch) const1252 DateTime Resource::availableAfter(const DateTime &time, const DateTime &limit, Schedule *sch) const {
1253 // debugPlan<<time<<limit;
1254 DateTime t;
1255 if (m_units == 0) {
1256 debugPlan<<this<<"zero units";
1257 return t;
1258 }
1259 DateTime lmt = m_availableUntil.isValid() ? m_availableUntil : (m_project ? m_project->constraintEndTime() : DateTime());
1260 if (limit.isValid() && limit < lmt) {
1261 lmt = limit;
1262 }
1263 if (time >= lmt) {
1264 debugPlan<<this<<"time >= limit"<<time<<lmt<<m_project;
1265 return t;
1266 }
1267 Calendar *cal = calendar();
1268 if (cal == 0) {
1269 if (sch) sch->logWarning(i18n("Resource %1 has no calendar defined", m_name));
1270 debugPlan<<this<<"No calendar";
1271 return t;
1272 }
1273 DateTime availableFrom = m_availableFrom.isValid() ? m_availableFrom : (m_project ? m_project->constraintStartTime() : DateTime());
1274 t = availableFrom > time ? availableFrom : time;
1275 if (t >= lmt) {
1276 debugPlan<<this<<t<<lmt;
1277 return DateTime();
1278 }
1279 QTimeZone tz = cal->timeZone();
1280 t = t.toTimeZone(tz);
1281 lmt = lmt.toTimeZone(tz);
1282 t = m_workinfocache.firstAvailableAfter(t, lmt, cal, sch);
1283 // t = cal->firstAvailableAfter(t, lmt, sch);
1284 //if (sch) debugPlan<<sch<<""<<m_name<<" id="<<sch->id()<<" mode="<<sch->calculationMode()<<" returns:"<<time.toString()<<"="<<t.toString()<<""<<lmt.toString();
1285 return t;
1286 }
1287
availableBefore(const DateTime & time,const DateTime & limit,Schedule * sch) const1288 DateTime Resource::availableBefore(const DateTime &time, const DateTime &limit, Schedule *sch) const {
1289 DateTime t;
1290 if (m_units == 0) {
1291 return t;
1292 }
1293 DateTime lmt = m_availableFrom.isValid() ? m_availableFrom : (m_project ? m_project->constraintStartTime() : DateTime());
1294 if (limit.isValid() && limit > lmt) {
1295 lmt = limit;
1296 }
1297 if (time <= lmt) {
1298 return t;
1299 }
1300 Calendar *cal = calendar();
1301 if (cal == 0) {
1302 return t;
1303 }
1304 DateTime availableUntil = m_availableUntil.isValid() ? m_availableUntil : (m_project ? m_project->constraintEndTime() : DateTime());
1305 if (! availableUntil.isValid()) {
1306 #ifndef PLAN_NLOGDEBUG
1307 if (sch) sch->logDebug("availableUntil is invalid");
1308 #endif
1309 t = time;
1310 } else {
1311 t = availableUntil < time ? availableUntil : time;
1312 }
1313 #ifndef PLAN_NLOGDEBUG
1314 if (sch && t < lmt) sch->logDebug("t < lmt: " + t.toString() + " < " + lmt.toString());
1315 #endif
1316 QTimeZone tz = cal->timeZone();
1317 t = t.toTimeZone(tz);
1318 lmt = lmt.toTimeZone(tz);
1319 t = m_workinfocache.firstAvailableBefore(t, lmt, cal, sch);
1320 // t = cal->firstAvailableBefore(t, lmt, sch);
1321 #ifndef PLAN_NLOGDEBUG
1322 if (sch && t.isValid() && t < lmt) sch->logDebug(" t < lmt: t=" + t.toString() + " lmt=" + lmt.toString());
1323 #endif
1324 return t;
1325 }
1326
findId(const QString & id) const1327 Resource *Resource::findId(const QString &id) const {
1328 return m_project ? m_project->findResource(id) : 0;
1329 }
1330
removeId(const QString & id)1331 bool Resource::removeId(const QString &id) {
1332 return m_project ? m_project->removeResourceId(id) : false;
1333 }
1334
insertId(const QString & id)1335 void Resource::insertId(const QString &id) {
1336 //debugPlan;
1337 if (m_project)
1338 m_project->insertResourceId(id, this);
1339 }
1340
findCalendar(const QString & id) const1341 Calendar *Resource::findCalendar(const QString &id) const {
1342 return (m_project ? m_project->findCalendar(id) : 0);
1343 }
1344
isOverbooked() const1345 bool Resource::isOverbooked() const {
1346 return isOverbooked(DateTime(), DateTime());
1347 }
1348
isOverbooked(const QDate & date) const1349 bool Resource::isOverbooked(const QDate &date) const {
1350 return isOverbooked(DateTime(date), DateTime(date.addDays(1)));
1351 }
1352
isOverbooked(const DateTime & start,const DateTime & end) const1353 bool Resource::isOverbooked(const DateTime &start, const DateTime &end) const {
1354 //debugPlan<<m_name<<":"<<start.toString()<<" -"<<end.toString()<<" cs=("<<m_currentSchedule<<")";
1355 return m_currentSchedule ? m_currentSchedule->isOverbooked(start, end) : false;
1356 }
1357
appointmentIntervals(long id) const1358 Appointment Resource::appointmentIntervals(long id) const {
1359 Appointment a;
1360 Schedule *s = findSchedule(id);
1361 if (s == 0) {
1362 return a;
1363 }
1364 foreach (Appointment *app, static_cast<ResourceSchedule*>(s)->appointments()) {
1365 a += *app;
1366 }
1367 return a;
1368 }
1369
appointmentIntervals() const1370 Appointment Resource::appointmentIntervals() const {
1371 Appointment a;
1372 if (m_currentSchedule == 0)
1373 return a;
1374 foreach (Appointment *app, m_currentSchedule->appointments()) {
1375 a += *app;
1376 }
1377 return a;
1378 }
1379
plannedEffortCostPrDay(const QDate & start,const QDate & end,long id,EffortCostCalculationType typ)1380 EffortCostMap Resource::plannedEffortCostPrDay(const QDate &start, const QDate &end, long id, EffortCostCalculationType typ)
1381 {
1382 EffortCostMap ec;
1383 Schedule *s = findSchedule(id);
1384 if (s == 0) {
1385 return ec;
1386 }
1387 ec = s->plannedEffortCostPrDay(start, end, typ);
1388 return ec;
1389 }
1390
plannedEffort(const QDate & date,EffortCostCalculationType typ) const1391 Duration Resource::plannedEffort(const QDate &date, EffortCostCalculationType typ) const
1392 {
1393 return m_currentSchedule ? m_currentSchedule->plannedEffort(date, typ) : Duration::zeroDuration;
1394 }
1395
setProject(Project * project)1396 void Resource::setProject(Project *project)
1397 {
1398 if (project != m_project) {
1399 if (m_project) {
1400 removeId();
1401 }
1402 }
1403 m_project = project;
1404 }
1405
addExternalAppointment(const QString & id,Appointment * a)1406 void Resource::addExternalAppointment(const QString& id, Appointment* a)
1407 {
1408 int row = -1;
1409 if (m_externalAppointments.contains(id)) {
1410 int row = m_externalAppointments.keys().indexOf(id); // clazy:exclude=container-anti-pattern
1411 emit externalAppointmentToBeRemoved(this, row);
1412 delete m_externalAppointments.take(id);
1413 emit externalAppointmentRemoved();
1414 }
1415 if (row == -1) {
1416 m_externalAppointments[ id ] = a;
1417 row = m_externalAppointments.keys().indexOf(id); // clazy:exclude=container-anti-pattern
1418 m_externalAppointments.remove(id);
1419 }
1420 emit externalAppointmentToBeAdded(this, row);
1421 m_externalAppointments[ id ] = a;
1422 emit externalAppointmentAdded(this, a);
1423 }
1424
addExternalAppointment(const QString & id,const QString & name,const DateTime & from,const DateTime & end,double load)1425 void Resource::addExternalAppointment(const QString &id, const QString &name, const DateTime &from, const DateTime &end, double load)
1426 {
1427 Appointment *a = m_externalAppointments.value(id);
1428 if (a == 0) {
1429 a = new Appointment();
1430 a->setAuxcilliaryInfo(name);
1431 a->addInterval(from, end, load);
1432 //debugPlan<<m_name<<name<<"new appointment:"<<a<<from<<end<<load;
1433 m_externalAppointments[ id ] = a;
1434 int row = m_externalAppointments.keys().indexOf(id); // clazy:exclude=container-anti-pattern
1435 m_externalAppointments.remove(id);
1436 emit externalAppointmentToBeAdded(this, row);
1437 m_externalAppointments[ id ] = a;
1438 emit externalAppointmentAdded(this, a);
1439 } else {
1440 //debugPlan<<m_name<<name<<"new interval:"<<a<<from<<end<<load;
1441 a->addInterval(from, end, load);
1442 emit externalAppointmentChanged(this, a);
1443 }
1444 }
1445
subtractExternalAppointment(const QString & id,const DateTime & start,const DateTime & end,double load)1446 void Resource::subtractExternalAppointment(const QString &id, const DateTime &start, const DateTime &end, double load)
1447 {
1448 Appointment *a = m_externalAppointments.value(id);
1449 if (a) {
1450 //debugPlan<<m_name<<name<<"new interval:"<<a<<from<<end<<load;
1451 Appointment app;
1452 app.addInterval(start, end, load);
1453 *a -= app;
1454 emit externalAppointmentChanged(this, a);
1455 }
1456 }
1457
clearExternalAppointments()1458 void Resource::clearExternalAppointments()
1459 {
1460 const QStringList keys = m_externalAppointments.keys();
1461 foreach (const QString &id, keys) {
1462 clearExternalAppointments(id);
1463 }
1464 }
1465
clearExternalAppointments(const QString & projectId)1466 void Resource::clearExternalAppointments(const QString &projectId)
1467 {
1468 while (m_externalAppointments.contains(projectId)) {
1469 int row = m_externalAppointments.keys().indexOf(projectId); // clazy:exclude=container-anti-pattern
1470 emit externalAppointmentToBeRemoved(this, row);
1471 Appointment *a = m_externalAppointments.take(projectId);
1472 emit externalAppointmentRemoved();
1473 delete a;
1474 }
1475 }
1476
takeExternalAppointment(const QString & id)1477 Appointment *Resource::takeExternalAppointment(const QString &id)
1478 {
1479 Appointment *a = 0;
1480 if (m_externalAppointments.contains(id)) {
1481 int row = m_externalAppointments.keys().indexOf(id); // clazy:exclude=container-anti-pattern
1482 emit externalAppointmentToBeRemoved(this, row);
1483 a = m_externalAppointments.take(id);
1484 emit externalAppointmentRemoved();
1485 }
1486 return a;
1487 }
1488
externalAppointments(const QString & id)1489 AppointmentIntervalList Resource::externalAppointments(const QString &id)
1490 {
1491 if (! m_externalAppointments.contains(id)) {
1492 return AppointmentIntervalList();
1493 }
1494 return m_externalAppointments[ id ]->intervals();
1495 }
1496
externalAppointments(const DateTimeInterval & interval) const1497 AppointmentIntervalList Resource::externalAppointments(const DateTimeInterval &interval) const
1498 {
1499 //debugPlan<<m_externalAppointments;
1500 Appointment app;
1501 foreach (Appointment *a, m_externalAppointments) {
1502 app += interval.isValid() ? a->extractIntervals(interval) : *a;
1503 }
1504 return app.intervals();
1505 }
1506
externalProjects() const1507 QMap<QString, QString> Resource::externalProjects() const
1508 {
1509 QMap<QString, QString> map;
1510 for (QMapIterator<QString, Appointment*> it(m_externalAppointments); it.hasNext();) {
1511 it.next();
1512 if (! map.contains(it.key())) {
1513 map[ it.key() ] = it.value()->auxcilliaryInfo();
1514 }
1515 }
1516 // debugPlan<<map;
1517 return map;
1518 }
1519
allocationSuitability(const DateTime & time,const Duration & duration,bool backward)1520 long Resource::allocationSuitability(const DateTime &time, const Duration &duration, bool backward)
1521 {
1522 // FIXME: This is not *very* intelligent...
1523 Duration e;
1524 if (m_type == Type_Team) {
1525 foreach (Resource *r, teamMembers()) {
1526 e += r->effort(time, duration, 100, backward);
1527 }
1528 } else {
1529 e = effort(time, duration, 100, backward);
1530 }
1531 return e.minutes();
1532 }
1533
startTime(long id) const1534 DateTime Resource::startTime(long id) const
1535 {
1536 DateTime dt;
1537 Schedule *s = schedule(id);
1538 if (s == 0) {
1539 return dt;
1540 }
1541 foreach (Appointment *a, s->appointments()) {
1542 DateTime t = a->startTime();
1543 if (! dt.isValid() || t < dt) {
1544 dt = t;
1545 }
1546 }
1547 return dt;
1548 }
1549
endTime(long id) const1550 DateTime Resource::endTime(long id) const
1551 {
1552 DateTime dt;
1553 Schedule *s = schedule(id);
1554 if (s == 0) {
1555 return dt;
1556 }
1557 foreach (Appointment *a, s->appointments()) {
1558 DateTime t = a->endTime();
1559 if (! dt.isValid() || t > dt) {
1560 dt = t;
1561 }
1562 }
1563 return dt;
1564 }
1565
teamMembers() const1566 QList<Resource*> Resource::teamMembers() const
1567 {
1568 QList<Resource*> lst;
1569 foreach (const QString &s, m_teamMembers) {
1570 Resource *r = findId(s);
1571 if (r) {
1572 lst << r;
1573 }
1574 }
1575 return lst;
1576
1577 }
1578
teamMemberIds() const1579 QStringList Resource::teamMemberIds() const
1580 {
1581 return m_teamMembers;
1582 }
1583
addTeamMemberId(const QString & id)1584 void Resource::addTeamMemberId(const QString &id)
1585 {
1586 if (! id.isEmpty() && ! m_teamMembers.contains(id)) {
1587 m_teamMembers.append(id);
1588 }
1589 }
1590
removeTeamMemberId(const QString & id)1591 void Resource::removeTeamMemberId(const QString &id)
1592 {
1593 if (m_teamMembers.contains(id)) {
1594 m_teamMembers.removeAt(m_teamMembers.indexOf(id));
1595 }
1596 }
1597
setTeamMemberIds(const QStringList & ids)1598 void Resource::setTeamMemberIds(const QStringList &ids)
1599 {
1600 m_teamMembers = ids;
1601 }
1602
isShared() const1603 bool Resource::isShared() const
1604 {
1605 return m_shared;
1606 }
1607
setShared(bool on)1608 void Resource::setShared(bool on)
1609 {
1610 m_shared = on;
1611 }
1612
operator <<(QDebug dbg,const KPlato::Resource::WorkInfoCache & c)1613 QDebug operator<<(QDebug dbg, const KPlato::Resource::WorkInfoCache &c)
1614 {
1615 dbg.nospace()<<"WorkInfoCache: ["<<" version="<<c.version<<" start="<<c.start.toString(Qt::ISODate)<<" end="<<c.end.toString(Qt::ISODate)<<" intervals="<<c.intervals.map().count();
1616 if (! c.intervals.isEmpty()) {
1617 foreach (const AppointmentInterval &i, c.intervals.map()) {
1618 dbg<<endl<<" "<<i;
1619 }
1620 }
1621 dbg<<"]";
1622 return dbg;
1623 }
1624
1625 ///////// Risk /////////
Risk(Node * n,Resource * r,RiskType rt)1626 Risk::Risk(Node *n, Resource *r, RiskType rt) {
1627 m_node=n;
1628 m_resource=r;
1629 m_riskType=rt;
1630 }
1631
~Risk()1632 Risk::~Risk() {
1633 }
1634
ResourceRequest(Resource * resource,int units)1635 ResourceRequest::ResourceRequest(Resource *resource, int units)
1636 : m_resource(resource),
1637 m_units(units),
1638 m_parent(0),
1639 m_dynamic(false)
1640 {
1641 if (resource) {
1642 m_required = resource->requiredResources();
1643 }
1644 //debugPlan<<"("<<this<<") Request to:"<<(resource ? resource->name() : QString("None"));
1645 }
1646
ResourceRequest(const ResourceRequest & r)1647 ResourceRequest::ResourceRequest(const ResourceRequest &r)
1648 : m_resource(r.m_resource),
1649 m_units(r.m_units),
1650 m_parent(0),
1651 m_dynamic(r.m_dynamic),
1652 m_required(r.m_required)
1653 {
1654 }
1655
~ResourceRequest()1656 ResourceRequest::~ResourceRequest() {
1657 //debugPlan<<"("<<this<<") resource:"<<(m_resource ? m_resource->name() : QString("None"));
1658 if (m_resource)
1659 m_resource->unregisterRequest(this);
1660 m_resource = 0;
1661 qDeleteAll(m_teamMembers);
1662 }
1663
load(KoXmlElement & element,Project & project)1664 bool ResourceRequest::load(KoXmlElement &element, Project &project) {
1665 //debugPlan;
1666 m_resource = project.resource(element.attribute("resource-id"));
1667 if (m_resource == 0) {
1668 warnPlan<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id");
1669 return false;
1670 }
1671 m_units = element.attribute("units").toInt();
1672
1673 KoXmlElement parent = element.namedItem("required-resources").toElement();
1674 KoXmlElement e;
1675 forEachElement(e, parent) {
1676 if (e.nodeName() == "resource") {
1677 QString id = e.attribute("id");
1678 if (id.isEmpty()) {
1679 errorPlan<<"Missing project id";
1680 continue;
1681 }
1682 Resource *r = project.resource(id);
1683 if (r == 0) {
1684 warnPlan<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id");
1685 } else {
1686 if (r != m_resource) {
1687 m_required << r;
1688 }
1689 }
1690 }
1691 }
1692 return true;
1693 }
1694
save(QDomElement & element) const1695 void ResourceRequest::save(QDomElement &element) const {
1696 QDomElement me = element.ownerDocument().createElement("resource-request");
1697 element.appendChild(me);
1698 me.setAttribute("resource-id", m_resource->id());
1699 me.setAttribute("units", QString::number(m_units));
1700 if (! m_required.isEmpty()) {
1701 QDomElement e = me.ownerDocument().createElement("required-resources");
1702 me.appendChild(e);
1703 foreach (Resource *r, m_required) {
1704 QDomElement el = e.ownerDocument().createElement("resource");
1705 e.appendChild(el);
1706 el.setAttribute("id", r->id());
1707 }
1708 }
1709 }
1710
units() const1711 int ResourceRequest::units() const {
1712 //debugPlan<<m_resource->name()<<": units="<<m_units;
1713 return m_units;
1714 }
1715
setUnits(int value)1716 void ResourceRequest::setUnits(int value)
1717 {
1718 m_units = value; changed();
1719 }
1720
task() const1721 Task *ResourceRequest::task() const {
1722 return m_parent ? m_parent->task() : 0;
1723 }
1724
changed()1725 void ResourceRequest::changed()
1726 {
1727 if (task()) {
1728 task()->changed(Node::ResourceRequestProperty);
1729 }
1730 }
1731
setCurrentSchedulePtr(Schedule * ns)1732 void ResourceRequest::setCurrentSchedulePtr(Schedule *ns)
1733 {
1734 setCurrentSchedulePtr(m_resource, ns);
1735 }
1736
setCurrentSchedulePtr(Resource * resource,Schedule * ns)1737 void ResourceRequest::setCurrentSchedulePtr(Resource *resource, Schedule *ns)
1738 {
1739 resource->setCurrentSchedulePtr(resourceSchedule(ns, resource));
1740 if(resource->type() == Resource::Type_Team) {
1741 foreach (Resource *member, resource->teamMembers()) {
1742 member->setCurrentSchedulePtr(resourceSchedule(ns, member));
1743 }
1744 }
1745 foreach (Resource *r, m_required) {
1746 r->setCurrentSchedulePtr(resourceSchedule(ns, r));
1747 }
1748 }
1749
resourceSchedule(Schedule * ns,Resource * res)1750 Schedule *ResourceRequest::resourceSchedule(Schedule *ns, Resource *res)
1751 {
1752 if (ns == 0) {
1753 return 0;
1754 }
1755 Resource *r = res == 0 ? resource() : res;
1756 Schedule *s = r->findSchedule(ns->id());
1757 if (s == 0) {
1758 s = r->createSchedule(ns->parent());
1759 }
1760 s->setCalculationMode(ns->calculationMode());
1761 s->setAllowOverbookingState(ns->allowOverbookingState());
1762 static_cast<ResourceSchedule*>(s)->setNodeSchedule(ns);
1763 //debugPlan<<s->name()<<": id="<<s->id()<<" mode="<<s->calculationMode();
1764 return s;
1765 }
1766
workTimeAfter(const DateTime & dt,Schedule * ns)1767 DateTime ResourceRequest::workTimeAfter(const DateTime &dt, Schedule *ns) {
1768 if (m_resource->type() == Resource::Type_Work) {
1769 DateTime t = availableAfter(dt, ns);
1770 foreach (Resource *r, m_required) {
1771 if (! t.isValid()) {
1772 break;
1773 }
1774 t = r->availableAfter(t, DateTime(), resourceSchedule(ns, r));
1775 }
1776 return t;
1777 } else if (m_resource->type() == Resource::Type_Team) {
1778 return availableAfter(dt, ns);
1779 }
1780 return DateTime();
1781 }
1782
workTimeBefore(const DateTime & dt,Schedule * ns)1783 DateTime ResourceRequest::workTimeBefore(const DateTime &dt, Schedule *ns) {
1784 if (m_resource->type() == Resource::Type_Work) {
1785 DateTime t = availableBefore(dt, ns);
1786 foreach (Resource *r, m_required) {
1787 if (! t.isValid()) {
1788 break;
1789 }
1790 t = r->availableBefore(t, DateTime(), resourceSchedule(ns, r));
1791 }
1792 return t;
1793 } else if (m_resource->type() == Resource::Type_Team) {
1794 return availableBefore(dt, ns);
1795 }
1796 return DateTime();
1797 }
1798
availableFrom()1799 DateTime ResourceRequest::availableFrom()
1800 {
1801 DateTime dt = m_resource->availableFrom();
1802 if (! dt.isValid()) {
1803 dt = m_resource->project()->constraintStartTime();
1804 }
1805 return dt;
1806 }
1807
availableUntil()1808 DateTime ResourceRequest::availableUntil()
1809 {
1810 DateTime dt = m_resource->availableUntil();
1811 if (! dt.isValid()) {
1812 dt = m_resource->project()->constraintEndTime();
1813 }
1814 return dt;
1815 }
1816
availableAfter(const DateTime & time,Schedule * ns)1817 DateTime ResourceRequest::availableAfter(const DateTime &time, Schedule *ns) {
1818 if (m_resource->type() == Resource::Type_Team) {
1819 DateTime t;// = m_resource->availableFrom();
1820 foreach (Resource *r, m_resource->teamMembers()) {
1821 setCurrentSchedulePtr(r, ns);
1822 DateTime x = r->availableAfter(time);
1823 if (x.isValid()) {
1824 t = t.isValid() ? qMin(t, x) : x;
1825 }
1826 }
1827 return t;
1828 }
1829 setCurrentSchedulePtr(ns);
1830 return m_resource->availableAfter(time);
1831 }
1832
availableBefore(const DateTime & time,Schedule * ns)1833 DateTime ResourceRequest::availableBefore(const DateTime &time, Schedule *ns) {
1834 if (m_resource->type() == Resource::Type_Team) {
1835 DateTime t;
1836 foreach (Resource *r, m_resource->teamMembers()) {
1837 setCurrentSchedulePtr(r, ns);
1838 DateTime x = r->availableBefore(time);
1839 if (x.isValid()) {
1840 t = t.isValid() ? qMax(t, x) : x;
1841 }
1842 }
1843 return t;
1844 }
1845 setCurrentSchedulePtr(ns);
1846 return resource()->availableBefore(time);
1847 }
1848
effort(const DateTime & time,const Duration & duration,Schedule * ns,bool backward)1849 Duration ResourceRequest::effort(const DateTime &time, const Duration &duration, Schedule *ns, bool backward)
1850 {
1851 setCurrentSchedulePtr(ns);
1852 Duration e = m_resource->effort(time, duration, m_units, backward, m_required);
1853 //debugPlan<<m_resource->name()<<time<<duration.toString()<<"delivers:"<<e.toString()<<"request:"<<(m_units/100)<<"parts";
1854 return e;
1855 }
1856
makeAppointment(Schedule * ns)1857 void ResourceRequest::makeAppointment(Schedule *ns)
1858 {
1859 if (m_resource) {
1860 setCurrentSchedulePtr(ns);
1861 m_resource->makeAppointment(ns, (m_resource->units() * m_units / 100), m_required);
1862 }
1863 }
1864
makeAppointment(Schedule * ns,int amount)1865 void ResourceRequest::makeAppointment(Schedule *ns, int amount)
1866 {
1867 if (m_resource) {
1868 setCurrentSchedulePtr(ns);
1869 m_resource->makeAppointment(ns, amount, m_required);
1870 }
1871 }
1872
allocationSuitability(const DateTime & time,const Duration & duration,Schedule * ns,bool backward)1873 long ResourceRequest::allocationSuitability(const DateTime &time, const Duration &duration, Schedule *ns, bool backward)
1874 {
1875 setCurrentSchedulePtr(ns);
1876 return resource()->allocationSuitability(time, duration, backward);
1877 }
1878
teamMembers() const1879 QList<ResourceRequest*> ResourceRequest::teamMembers() const
1880 {
1881 qDeleteAll(m_teamMembers);
1882 m_teamMembers.clear();
1883 if (m_resource->type() == Resource::Type_Team) {
1884 foreach (Resource *r, m_resource->teamMembers()) {
1885 m_teamMembers << new ResourceRequest(r, m_units);
1886 }
1887 }
1888 return m_teamMembers;
1889 }
1890
operator <<(QDebug & dbg,const KPlato::ResourceRequest * rr)1891 QDebug &operator<<(QDebug &dbg, const KPlato::ResourceRequest *rr)
1892 {
1893 if (rr) {
1894 dbg<<*rr;
1895 } else {
1896 dbg<<(void*)rr;
1897 }
1898 return dbg;
1899 }
operator <<(QDebug & dbg,const KPlato::ResourceRequest & rr)1900 QDebug &operator<<(QDebug &dbg, const KPlato::ResourceRequest &rr)
1901 {
1902 if (rr.resource()) {
1903 dbg<<"ResourceRequest["<<rr.resource()->name()<<']';
1904 } else {
1905 dbg<<"ResourceRequest[No resource]";
1906 }
1907 return dbg;
1908 }
1909
1910 /////////
ResourceGroupRequest(ResourceGroup * group,int units)1911 ResourceGroupRequest::ResourceGroupRequest(ResourceGroup *group, int units)
1912 : m_group(group), m_units(units), m_parent(0) {
1913
1914 //debugPlan<<"Request to:"<<(group ? group->name() : QString("None"));
1915 if (group)
1916 group->registerRequest(this);
1917 }
1918
ResourceGroupRequest(const ResourceGroupRequest & g)1919 ResourceGroupRequest::ResourceGroupRequest(const ResourceGroupRequest &g)
1920 : m_group(g.m_group), m_units(g.m_units), m_parent(0)
1921 {
1922 }
1923
~ResourceGroupRequest()1924 ResourceGroupRequest::~ResourceGroupRequest() {
1925 //debugPlan;
1926 if (m_group) {
1927 m_group->unregisterRequest(this);
1928 }
1929 while (!m_resourceRequests.isEmpty()) {
1930 delete m_resourceRequests.takeFirst();
1931 }
1932 }
1933
addResourceRequest(ResourceRequest * request)1934 void ResourceGroupRequest::addResourceRequest(ResourceRequest *request) {
1935 //debugPlan<<"("<<request<<") to Group:"<<(void*)m_group;
1936 request->setParent(this);
1937 m_resourceRequests.append(request);
1938 request->registerRequest();
1939 changed();
1940 }
1941
takeResourceRequest(ResourceRequest * request)1942 ResourceRequest *ResourceGroupRequest::takeResourceRequest(ResourceRequest *request) {
1943 if (request)
1944 request->unregisterRequest();
1945 ResourceRequest *r = 0;
1946 int i = m_resourceRequests.indexOf(request);
1947 if (i != -1) {
1948 r = m_resourceRequests.takeAt(i);
1949 }
1950 changed();
1951 return r;
1952 }
1953
find(const Resource * resource) const1954 ResourceRequest *ResourceGroupRequest::find(const Resource *resource) const {
1955 foreach (ResourceRequest *gr, m_resourceRequests) {
1956 if (gr->resource() == resource) {
1957 return gr;
1958 }
1959 }
1960 return 0;
1961 }
1962
resourceRequest(const QString & name)1963 ResourceRequest *ResourceGroupRequest::resourceRequest(const QString &name) {
1964 foreach (ResourceRequest *r, m_resourceRequests) {
1965 if (r->resource()->name() == name)
1966 return r;
1967 }
1968 return 0;
1969 }
1970
requestNameList(bool includeGroup) const1971 QStringList ResourceGroupRequest::requestNameList(bool includeGroup) const {
1972 QStringList lst;
1973 if (includeGroup && m_units > 0 && m_group) {
1974 lst << m_group->name();
1975 }
1976 foreach (ResourceRequest *r, m_resourceRequests) {
1977 if (! r->isDynamicallyAllocated()) {
1978 Q_ASSERT(r->resource());
1979 lst << r->resource()->name();
1980 }
1981 }
1982 return lst;
1983 }
1984
requestedResources() const1985 QList<Resource*> ResourceGroupRequest::requestedResources() const
1986 {
1987 QList<Resource*> lst;
1988 foreach (ResourceRequest *r, m_resourceRequests) {
1989 if (! r->isDynamicallyAllocated()) {
1990 Q_ASSERT(r->resource());
1991 lst << r->resource();
1992 }
1993 }
1994 return lst;
1995 }
1996
resourceRequests(bool resolveTeam) const1997 QList<ResourceRequest*> ResourceGroupRequest::resourceRequests(bool resolveTeam) const
1998 {
1999 QList<ResourceRequest*> lst;
2000 foreach (ResourceRequest *rr, m_resourceRequests) {
2001 if (resolveTeam && rr->resource()->type() == Resource::Type_Team) {
2002 lst += rr->teamMembers();
2003 } else {
2004 lst << rr;
2005 }
2006 }
2007 return lst;
2008 }
2009
load(KoXmlElement & element,XMLLoaderObject & status)2010 bool ResourceGroupRequest::load(KoXmlElement &element, XMLLoaderObject &status) {
2011 //debugPlan;
2012 m_group = status.project().findResourceGroup(element.attribute("group-id"));
2013 if (m_group == 0) {
2014 errorPlan<<"The referenced resource group does not exist: group id="<<element.attribute("group-id");
2015 return false;
2016 }
2017 m_group->registerRequest(this);
2018
2019 KoXmlNode n = element.firstChild();
2020 for (; ! n.isNull(); n = n.nextSibling()) {
2021 if (! n.isElement()) {
2022 continue;
2023 }
2024 KoXmlElement e = n.toElement();
2025 if (e.tagName() == "resource-request") {
2026 ResourceRequest *r = new ResourceRequest();
2027 if (r->load(e, status.project()))
2028 addResourceRequest(r);
2029 else {
2030 errorPlan<<"Failed to load resource request";
2031 delete r;
2032 }
2033 }
2034 }
2035 // meaning of m_units changed
2036 // Pre 0.6.6 the number *included* all requests, now it is in *addition* to resource requests
2037 m_units = element.attribute("units").toInt();
2038 if (status.version() < "0.6.6") {
2039 int x = m_units - m_resourceRequests.count();
2040 m_units = x > 0 ? x : 0;
2041 }
2042 return true;
2043 }
2044
save(QDomElement & element) const2045 void ResourceGroupRequest::save(QDomElement &element) const {
2046 QDomElement me = element.ownerDocument().createElement("resourcegroup-request");
2047 element.appendChild(me);
2048 me.setAttribute("group-id", m_group->id());
2049 me.setAttribute("units", QString::number(m_units));
2050 foreach (ResourceRequest *r, m_resourceRequests)
2051 r->save(me);
2052 }
2053
units() const2054 int ResourceGroupRequest::units() const {
2055 return m_units;
2056 }
2057
duration(const DateTime & time,const Duration & _effort,Schedule * ns,bool backward)2058 Duration ResourceGroupRequest::duration(const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) {
2059 Duration dur;
2060 if (m_parent) {
2061 dur = m_parent->duration(m_resourceRequests, time, _effort, ns, backward);
2062 }
2063 return dur;
2064 }
2065
workTimeAfter(const DateTime & time,Schedule * ns)2066 DateTime ResourceGroupRequest::workTimeAfter(const DateTime &time, Schedule *ns) {
2067 DateTime start;
2068 if (m_resourceRequests.isEmpty()) {
2069 return start;
2070 }
2071 foreach (ResourceRequest *r, m_resourceRequests) {
2072 DateTime t = r->workTimeAfter(time, ns);
2073 if (t.isValid() && (!start.isValid() || t < start))
2074 start = t;
2075 }
2076 if (start.isValid() && start < time)
2077 start = time;
2078 //debugPlan<<time.toString()<<"="<<start.toString();
2079 return start;
2080 }
2081
workTimeBefore(const DateTime & time,Schedule * ns)2082 DateTime ResourceGroupRequest::workTimeBefore(const DateTime &time, Schedule *ns) {
2083 DateTime end;
2084 if (m_resourceRequests.isEmpty()) {
2085 return end;
2086 }
2087 foreach (ResourceRequest *r, m_resourceRequests) {
2088 DateTime t = r->workTimeBefore(time, ns);
2089 if (t.isValid() && (!end.isValid() ||t > end))
2090 end = t;
2091 }
2092 if (!end.isValid() || end > time)
2093 end = time;
2094 return end;
2095 }
2096
availableAfter(const DateTime & time,Schedule * ns)2097 DateTime ResourceGroupRequest::availableAfter(const DateTime &time, Schedule *ns) {
2098 DateTime start;
2099 if (m_resourceRequests.isEmpty()) {
2100 return start;
2101 }
2102 foreach (ResourceRequest *r, m_resourceRequests) {
2103 DateTime t = r->availableAfter(time, ns);
2104 if (t.isValid() && (!start.isValid() || t < start))
2105 start = t;
2106 }
2107 if (start.isValid() && start < time)
2108 start = time;
2109 //debugPlan<<time.toString()<<"="<<start.toString()<<""<<m_group->name();
2110 return start;
2111 }
2112
availableBefore(const DateTime & time,Schedule * ns)2113 DateTime ResourceGroupRequest::availableBefore(const DateTime &time, Schedule *ns) {
2114 DateTime end;
2115 if (m_resourceRequests.isEmpty()) {
2116 return end;
2117 }
2118 foreach (ResourceRequest *r, m_resourceRequests) {
2119 DateTime t = r->availableBefore(time, ns);
2120 if (t.isValid() && (!end.isValid() || t > end))
2121 end = t;
2122 }
2123 if (!end.isValid() || end > time)
2124 end = time;
2125 //debugPlan<<time.toString()<<"="<<end.toString()<<""<<m_group->name();
2126 return end;
2127 }
2128
makeAppointments(Schedule * schedule)2129 void ResourceGroupRequest::makeAppointments(Schedule *schedule) {
2130 //debugPlan;
2131 foreach (ResourceRequest *r, m_resourceRequests) {
2132 r->makeAppointment(schedule);
2133 }
2134 }
2135
reserve(const DateTime & start,const Duration & duration)2136 void ResourceGroupRequest::reserve(const DateTime &start, const Duration &duration) {
2137 m_start = start;
2138 m_duration = duration;
2139 }
2140
isEmpty() const2141 bool ResourceGroupRequest::isEmpty() const {
2142 return m_resourceRequests.isEmpty() && m_units == 0;
2143 }
2144
task() const2145 Task *ResourceGroupRequest::task() const {
2146 return m_parent ? m_parent->task() : 0;
2147 }
2148
changed()2149 void ResourceGroupRequest::changed()
2150 {
2151 if (m_parent)
2152 m_parent->changed();
2153 }
2154
deleteResourceRequest(ResourceRequest * request)2155 void ResourceGroupRequest::deleteResourceRequest(ResourceRequest *request)
2156 {
2157 int i = m_resourceRequests.indexOf(request);
2158 if (i != -1) {
2159 m_resourceRequests.removeAt(i);
2160 }
2161 delete request;
2162 changed();
2163 }
2164
resetDynamicAllocations()2165 void ResourceGroupRequest::resetDynamicAllocations()
2166 {
2167 QList<ResourceRequest*> lst;
2168 foreach (ResourceRequest *r, m_resourceRequests) {
2169 if (r->isDynamicallyAllocated()) {
2170 lst << r;
2171 }
2172 }
2173 while (! lst.isEmpty()) {
2174 deleteResourceRequest(lst.takeFirst());
2175 }
2176 }
2177
allocateDynamicRequests(const DateTime & time,const Duration & effort,Schedule * ns,bool backward)2178 void ResourceGroupRequest::allocateDynamicRequests(const DateTime &time, const Duration &effort, Schedule *ns, bool backward)
2179 {
2180 int num = m_units;
2181 if (num <= 0) {
2182 return;
2183 }
2184 if (num == m_group->numResources()) {
2185 // TODO: allocate all
2186 }
2187 Duration e = effort / m_units;
2188 QMap<long, ResourceRequest*> map;
2189 foreach (Resource *r, m_group->resources()) {
2190 if (r->type() == Resource::Type_Team) {
2191 continue;
2192 }
2193 ResourceRequest *rr = find(r);
2194 if (rr) {
2195 if (rr->isDynamicallyAllocated()) {
2196 --num; // already allocated
2197 }
2198 continue;
2199 }
2200 rr = new ResourceRequest(r, r->units());
2201 long s = rr->allocationSuitability(time, e, ns, backward);
2202 if (s == 0) {
2203 // not suitable at all
2204 delete rr;
2205 } else {
2206 map.insertMulti(s, rr);
2207 }
2208 }
2209 for (--num; num >= 0 && ! map.isEmpty(); --num) {
2210 long key = map.lastKey();
2211 ResourceRequest *r = map.take(key);
2212 r->setAllocatedDynaically(true);
2213 addResourceRequest(r);
2214 debugPlan<<key<<r;
2215 }
2216 qDeleteAll(map); // delete the unused
2217 }
2218
2219 /////////
ResourceRequestCollection(Task * task)2220 ResourceRequestCollection::ResourceRequestCollection(Task *task)
2221 : m_task(task) {
2222 //debugPlan<<this<<(void*)(&task);
2223 }
2224
~ResourceRequestCollection()2225 ResourceRequestCollection::~ResourceRequestCollection() {
2226 //debugPlan<<this;
2227 while (!m_requests.empty()) {
2228 delete m_requests.takeFirst();
2229 }
2230 }
2231
addRequest(ResourceGroupRequest * request)2232 void ResourceRequestCollection::addRequest(ResourceGroupRequest *request)
2233 {
2234 Q_ASSERT(request->group());
2235 foreach (ResourceGroupRequest *r, m_requests) {
2236 if (r->group() == request->group()) {
2237 errorPlan<<"Request to this group already exists";
2238 errorPlan<<"Task:"<<m_task->name()<<"Group:"<<request->group()->name();
2239 Q_ASSERT(false);
2240 }
2241 }
2242 m_requests.append(request);
2243 if (!request->group()->requests().contains(request)) {
2244 request->group()->registerRequest(request);
2245 }
2246 request->setParent(this);
2247 changed();
2248 }
2249
find(const ResourceGroup * group) const2250 ResourceGroupRequest *ResourceRequestCollection::find(const ResourceGroup *group) const {
2251 foreach (ResourceGroupRequest *r, m_requests) {
2252 if (r->group() == group)
2253 return r; // we assume only one request to the same group
2254 }
2255 return 0;
2256 }
2257
find(const Resource * resource) const2258 ResourceRequest *ResourceRequestCollection::find(const Resource *resource) const {
2259 ResourceRequest *req = 0;
2260 QListIterator<ResourceGroupRequest*> it(m_requests);
2261 while (req == 0 && it.hasNext()) {
2262 req = it.next()->find(resource);
2263 }
2264 return req;
2265 }
2266
resourceRequest(const QString & name) const2267 ResourceRequest *ResourceRequestCollection::resourceRequest(const QString &name) const {
2268 ResourceRequest *req = 0;
2269 QListIterator<ResourceGroupRequest*> it(m_requests);
2270 while (req == 0 && it.hasNext()) {
2271 req = it.next()->resourceRequest(name);
2272 }
2273 return req;
2274 }
2275
requestNameList(bool includeGroup) const2276 QStringList ResourceRequestCollection::requestNameList(bool includeGroup) const {
2277 QStringList lst;
2278 foreach (ResourceGroupRequest *r, m_requests) {
2279 lst << r->requestNameList(includeGroup);
2280 }
2281 return lst;
2282 }
2283
requestedResources() const2284 QList<Resource*> ResourceRequestCollection::requestedResources() const {
2285 QList<Resource*> lst;
2286 foreach (ResourceGroupRequest *g, m_requests) {
2287 lst += g->requestedResources();
2288 }
2289 return lst;
2290 }
2291
resourceRequests(bool resolveTeam) const2292 QList<ResourceRequest*> ResourceRequestCollection::resourceRequests(bool resolveTeam) const {
2293 QList<ResourceRequest*> lst;
2294 foreach (ResourceGroupRequest *g, m_requests) {
2295 foreach (ResourceRequest *r, g->resourceRequests(resolveTeam)) {
2296 lst << r;
2297 }
2298 }
2299 return lst;
2300 }
2301
2302
contains(const QString & identity) const2303 bool ResourceRequestCollection::contains(const QString &identity) const {
2304 QStringList lst = requestNameList();
2305 return lst.indexOf(QRegExp(identity, Qt::CaseSensitive, QRegExp::FixedString)) != -1;
2306 }
2307
findGroupRequestById(const QString & id) const2308 ResourceGroupRequest *ResourceRequestCollection::findGroupRequestById(const QString &id) const
2309 {
2310 foreach (ResourceGroupRequest *r, m_requests) {
2311 if (r->group()->id() == id) {
2312 return r;
2313 }
2314 }
2315 return 0;
2316 }
2317
2318 // bool ResourceRequestCollection::load(KoXmlElement &element, Project &project) {
2319 // //debugPlan;
2320 // return true;
2321 // }
2322
save(QDomElement & element) const2323 void ResourceRequestCollection::save(QDomElement &element) const {
2324 //debugPlan;
2325 foreach (ResourceGroupRequest *r, m_requests) {
2326 r->save(element);
2327 }
2328 }
2329
2330 // Returns the duration needed by the working resources
2331 // "Material type" of resourcegroups does not (atm) affect the duration.
duration(const DateTime & time,const Duration & effort,Schedule * ns,bool backward)2332 Duration ResourceRequestCollection::duration(const DateTime &time, const Duration &effort, Schedule *ns, bool backward) {
2333 //debugPlan<<"time="<<time.toString()<<" effort="<<effort.toString(Duration::Format_Day)<<" backward="<<backward;
2334 if (isEmpty()) {
2335 return effort;
2336 }
2337 Duration dur = effort;
2338 QList<ResourceRequest*> lst;
2339 foreach (ResourceGroupRequest *r, m_requests) {
2340 r->allocateDynamicRequests(time, effort, ns, backward);
2341 if (r->group()->type() == ResourceGroup::Type_Work) {
2342 lst << r->resourceRequests();
2343 } else if (r->group()->type() == ResourceGroup::Type_Material) {
2344 //TODO
2345 }
2346 }
2347 if (! lst.isEmpty()) {
2348 dur = duration(lst, time, effort, ns, backward);
2349 }
2350 return dur;
2351 }
2352
workTimeAfter(const DateTime & time,Schedule * ns) const2353 DateTime ResourceRequestCollection::workTimeAfter(const DateTime &time, Schedule *ns) const {
2354 DateTime start;
2355 foreach (ResourceGroupRequest *r, m_requests) {
2356 DateTime t = r->workTimeAfter(time, ns);
2357 if (t.isValid() && (!start.isValid() || t < start))
2358 start = t;
2359 }
2360 if (start.isValid() && start < time)
2361 start = time;
2362 //debugPlan<<time.toString()<<"="<<start.toString();
2363 return start;
2364 }
2365
workTimeBefore(const DateTime & time,Schedule * ns) const2366 DateTime ResourceRequestCollection::workTimeBefore(const DateTime &time, Schedule *ns) const {
2367 DateTime end;
2368 foreach (ResourceGroupRequest *r, m_requests) {
2369 DateTime t = r->workTimeBefore(time, ns);
2370 if (t.isValid() && (!end.isValid() ||t > end))
2371 end = t;
2372 }
2373 if (!end.isValid() || end > time)
2374 end = time;
2375 return end;
2376 }
2377
availableAfter(const DateTime & time,Schedule * ns)2378 DateTime ResourceRequestCollection::availableAfter(const DateTime &time, Schedule *ns) {
2379 DateTime start;
2380 foreach (ResourceGroupRequest *r, m_requests) {
2381 DateTime t = r->availableAfter(time, ns);
2382 if (t.isValid() && (!start.isValid() || t < start))
2383 start = t;
2384 }
2385 if (start.isValid() && start < time)
2386 start = time;
2387 //debugPlan<<time.toString()<<"="<<start.toString();
2388 return start;
2389 }
2390
availableBefore(const DateTime & time,Schedule * ns)2391 DateTime ResourceRequestCollection::availableBefore(const DateTime &time, Schedule *ns) {
2392 DateTime end;
2393 foreach (ResourceGroupRequest *r, m_requests) {
2394 DateTime t = r->availableBefore(time, ns);
2395 if (t.isValid() && (!end.isValid() ||t > end))
2396 end = t;
2397 }
2398 if (!end.isValid() || end > time)
2399 end = time;
2400 return end;
2401 }
2402
workStartAfter(const DateTime & time,Schedule * ns)2403 DateTime ResourceRequestCollection::workStartAfter(const DateTime &time, Schedule *ns) {
2404 DateTime start;
2405 foreach (ResourceGroupRequest *r, m_requests) {
2406 if (r->group()->type() != ResourceGroup::Type_Work) {
2407 continue;
2408 }
2409 DateTime t = r->availableAfter(time, ns);
2410 if (t.isValid() && (!start.isValid() || t < start))
2411 start = t;
2412 }
2413 if (start.isValid() && start < time)
2414 start = time;
2415 //debugPlan<<time.toString()<<"="<<start.toString();
2416 return start;
2417 }
2418
workFinishBefore(const DateTime & time,Schedule * ns)2419 DateTime ResourceRequestCollection::workFinishBefore(const DateTime &time, Schedule *ns) {
2420 DateTime end;
2421 foreach (ResourceGroupRequest *r, m_requests) {
2422 if (r->group()->type() != ResourceGroup::Type_Work) {
2423 continue;
2424 }
2425 DateTime t = r->availableBefore(time, ns);
2426 if (t.isValid() && (!end.isValid() ||t > end))
2427 end = t;
2428 }
2429 if (!end.isValid() || end > time)
2430 end = time;
2431 return end;
2432 }
2433
2434
makeAppointments(Schedule * schedule)2435 void ResourceRequestCollection::makeAppointments(Schedule *schedule) {
2436 //debugPlan;
2437 foreach (ResourceGroupRequest *r, m_requests) {
2438 r->makeAppointments(schedule);
2439 }
2440 }
2441
reserve(const DateTime & start,const Duration & duration)2442 void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) {
2443 //debugPlan;
2444 foreach (ResourceGroupRequest *r, m_requests) {
2445 r->reserve(start, duration);
2446 }
2447 }
2448
isEmpty() const2449 bool ResourceRequestCollection::isEmpty() const {
2450 foreach (ResourceGroupRequest *r, m_requests) {
2451 if (!r->isEmpty())
2452 return false;
2453 }
2454 return true;
2455 }
2456
changed()2457 void ResourceRequestCollection::changed()
2458 {
2459 //debugPlan<<m_task;
2460 if (m_task) {
2461 m_task->changed(Node::ResourceRequestProperty);
2462 }
2463 }
2464
resetDynamicAllocations()2465 void ResourceRequestCollection::resetDynamicAllocations()
2466 {
2467 foreach (ResourceGroupRequest *g, m_requests) {
2468 g->resetDynamicAllocations();
2469 }
2470 }
2471
effort(const QList<ResourceRequest * > & lst,const DateTime & time,const Duration & duration,Schedule * ns,bool backward) const2472 Duration ResourceRequestCollection::effort(const QList<ResourceRequest*> &lst, const DateTime &time, const Duration &duration, Schedule *ns, bool backward) const {
2473 Duration e;
2474 foreach (ResourceRequest *r, lst) {
2475 e += r->effort(time, duration, ns, backward);
2476 //debugPlan<<(backward?"(B)":"(F)")<<r<<": time="<<time<<" dur="<<duration.toString()<<"gave e="<<e.toString();
2477 }
2478 //debugPlan<<time.toString()<<"d="<<duration.toString()<<": e="<<e.toString();
2479 return e;
2480 }
2481
numDays(const QList<ResourceRequest * > & lst,const DateTime & time,bool backward) const2482 int ResourceRequestCollection::numDays(const QList<ResourceRequest*> &lst, const DateTime &time, bool backward) const {
2483 DateTime t1, t2 = time;
2484 if (backward) {
2485 foreach (ResourceRequest *r, lst) {
2486 t1 = r->availableFrom();
2487 if (!t2.isValid() || t2 > t1)
2488 t2 = t1;
2489 }
2490 //debugPlan<<"bw"<<time.toString()<<":"<<t2.daysTo(time);
2491 return t2.daysTo(time);
2492 }
2493 foreach (ResourceRequest *r, lst) {
2494 t1 = r->availableUntil();
2495 if (!t2.isValid() || t2 < t1)
2496 t2 = t1;
2497 }
2498 //debugPlan<<"fw"<<time.toString()<<":"<<time.daysTo(t2);
2499 return time.daysTo(t2);
2500 }
2501
duration(const QList<ResourceRequest * > & lst,const DateTime & time,const Duration & _effort,Schedule * ns,bool backward)2502 Duration ResourceRequestCollection::duration(const QList<ResourceRequest*> &lst, const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) {
2503 //debugPlan<<"--->"<<(backward?"(B)":"(F)")<<time.toString()<<": effort:"<<_effort.toString(Duration::Format_Day)<<" ("<<_effort.milliseconds()<<")";
2504 #if 0
2505 if (ns) {
2506 QStringList nl;
2507 foreach (ResourceRequest *r, lst) { nl << r->resource()->name(); }
2508 ns->logDebug("Match effort:" + time.toString() + "," + _effort.toString());
2509 ns->logDebug("Resources: " + (nl.isEmpty() ? QString("None") : nl.join(", ")));
2510 }
2511 #endif
2512 QLocale locale;
2513 Duration e;
2514 if (_effort == Duration::zeroDuration) {
2515 return e;
2516 }
2517 DateTime logtime = time;
2518 bool match = false;
2519 DateTime start = time;
2520 int inc = backward ? -1 : 1;
2521 DateTime end = start;
2522 Duration e1;
2523 int nDays = numDays(lst, time, backward) + 1;
2524 int day = 0;
2525 for (day=0; !match && day <= nDays; ++day) {
2526 // days
2527 end = end.addDays(inc);
2528 e1 = effort(lst, start, backward ? start - end : end - start, ns, backward);
2529 //debugPlan<<"["<<i<<"of"<<nDays<<"]"<<(backward?"(B)":"(F):")<<" start="<<start<<" e+e1="<<(e+e1).toString()<<" match"<<_effort.toString();
2530 if (e + e1 < _effort) {
2531 e += e1;
2532 start = end;
2533 } else if (e + e1 == _effort) {
2534 e += e1;
2535 match = true;
2536 } else {
2537 end = start;
2538 break;
2539 }
2540 }
2541 if (! match && day <= nDays) {
2542 #ifndef PLAN_NLOGDEBUG
2543 if (ns) ns->logDebug("Days: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')');
2544 #endif
2545 logtime = start;
2546 for (int i=0; !match && i < 24; ++i) {
2547 // hours
2548 end = end.addSecs(inc*60*60);
2549 e1 = effort(lst, start, backward ? start - end : end - start, ns, backward);
2550 if (e + e1 < _effort) {
2551 e += e1;
2552 start = end;
2553 } else if (e + e1 == _effort) {
2554 e += e1;
2555 match = true;
2556 } else {
2557 if (false/*roundToHour*/ && (_effort - e) < (e + e1 - _effort)) {
2558 end = start;
2559 match = true;
2560 } else {
2561 end = start;
2562 }
2563 break;
2564 }
2565 //debugPlan<<"duration(h)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")";
2566 }
2567 //debugPlan<<"duration"<<(backward?"backward":"forward:")<<start.toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<") match="<<match<<" sts="<<sts;
2568 }
2569 if (! match && day <= nDays) {
2570 #ifndef PLAN_NLOGDEBUG
2571 if (ns) ns->logDebug("Hours: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')');
2572 #endif
2573 logtime = start;
2574 for (int i=0; !match && i < 60; ++i) {
2575 //minutes
2576 end = end.addSecs(inc*60);
2577 e1 = effort(lst, start, backward ? start - end : end - start, ns, backward);
2578 if (e + e1 < _effort) {
2579 e += e1;
2580 start = end;
2581 } else if (e + e1 == _effort) {
2582 e += e1;
2583 match = true;
2584 } else if (e + e1 > _effort) {
2585 end = start;
2586 break;
2587 }
2588 //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")";
2589 }
2590 //debugPlan<<"duration"<<(backward?"backward":"forward:")<<" start="<<start.toString()<<" e="<<e.toString()<<" match="<<match<<" sts="<<sts;
2591 }
2592 // FIXME: better solution
2593 // If effort to match is reasonably large, accept a match if deviation <= 1 min
2594 if (! match && _effort > 5 * 60000) {
2595 if ((_effort - e) <= 60000){
2596 match = true;
2597 #ifndef PLAN_NLOGDEBUG
2598 if (ns) ns->logDebug("Deviation match:" + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')');
2599 #endif
2600 }
2601 }
2602 if (! match && day <= nDays) {
2603 #ifndef PLAN_NLOGDEBUG
2604 if (ns) ns->logDebug("Minutes: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')');
2605 #endif
2606 logtime = start;
2607 for (int i=0; !match && i < 60; ++i) {
2608 //seconds
2609 end = end.addSecs(inc);
2610 e1 = effort(lst, start, backward ? start - end : end - start, ns, backward);
2611 if (e + e1 < _effort) {
2612 e += e1;
2613 start = end;
2614 } else if (e + e1 == _effort) {
2615 e += e1;
2616 match = true;
2617 } else if (e + e1 > _effort) {
2618 end = start;
2619 break;
2620 }
2621 //debugPlan<<"duration(s)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")";
2622 }
2623 }
2624 if (! match && day <= nDays) {
2625 #ifndef PLAN_NLOGDEBUG
2626 if (ns) ns->logDebug("Seconds: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')');
2627 #endif
2628 for (int i=0; !match && i < 1000; ++i) {
2629 //milliseconds
2630 end.setTime(end.time().addMSecs(inc));
2631 e1 = effort(lst, start, backward ? start - end : end - start, ns, backward);
2632 if (e + e1 < _effort) {
2633 e += e1;
2634 start = end;
2635 } else if (e + e1 == _effort) {
2636 e += e1;
2637 match = true;
2638 } else if (e + e1 > _effort) {
2639 break;
2640 }
2641 //debugPlan<<"duration(ms)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")";
2642 }
2643 }
2644 if (!match && ns) {
2645 ns->logError(i18n("Could not match effort. Want: %1 got: %2", _effort.toString(Duration::Format_Hour), e.toString(Duration::Format_Hour)));
2646 foreach (ResourceRequest *r, lst) {
2647 Resource *res = r->resource();
2648 ns->logInfo(i18n("Resource %1 available from %2 to %3", res->name(), locale.toString(r->availableFrom(), QLocale::ShortFormat), locale.toString(r->availableUntil(), QLocale::ShortFormat)));
2649 }
2650
2651 }
2652 DateTime t;
2653 if (e != Duration::zeroDuration) {
2654 foreach (ResourceRequest *r, lst) {
2655 DateTime tt;
2656 if (backward) {
2657 tt = r->availableAfter(end, ns);
2658 if (tt.isValid() && (! t.isValid() || tt < t)) {
2659 t = tt;
2660 }
2661 } else {
2662 tt = r->availableBefore(end, ns);
2663 if (tt.isValid() && (! t.isValid() || tt > t)) {
2664 t = tt;
2665 }
2666 }
2667 }
2668 }
2669 end = t.isValid() ? t : time;
2670 //debugPlan<<"<---"<<(backward?"(B)":"(F)")<<":"<<end.toString()<<"-"<<time.toString()<<"="<<(end - time).toString()<<" effort:"<<_effort.toString(Duration::Format_Day);
2671 return (end>time?end-time:time-end);
2672 }
2673
2674
2675 } //KPlato namespace
2676
operator <<(QDebug dbg,KPlato::Resource * r)2677 QDebug operator<<(QDebug dbg, KPlato::Resource *r)
2678 {
2679 if (!r) { return dbg << "Resource[0x0]"; }
2680 dbg << "Resource[" << r->type();
2681 dbg << (r->name().isEmpty() ? r->id() : r->name());
2682 dbg << ']';
2683 return dbg;
2684 }
2685