21#include "kptproject.h"
22#include "kptappointment.h"
24#include "kptprojectdialog.h"
25#include "kptdatetime.h"
28#include "kpteffortcostmap.h"
29#include "kptschedule.h"
33#include <tqdatetime.h>
38#include <kdatetimewidget.h>
45Project::Project(
Node *parent)
50 m_constraint = Node::MustStartOn;
51 m_standardWorktime =
new StandardWorktime();
52 m_schedules.setAutoDelete(
true);
59 m_constraintStartTime = TQDateTime(TQDate::currentDate(), TQTime());
60 m_constraintEndTime = TQDateTime(m_constraintStartTime.addDays(1));
62 m_calendars.setAutoDelete(
true);
67 m_resourceGroups.setAutoDelete(
true);
68 m_resourceGroups.clear();
69 delete m_standardWorktime;
76 kdError()<<k_funcinfo<<
"Schedule == 0, cannot calculate"<<endl;
79 m_currentSchedule = schedule;
84 m_currentSchedule =
findSchedule((Schedule::Type)estType);
85 if (m_currentSchedule == 0) {
86 m_currentSchedule =
createSchedule(i18n(
"Standard"), (Schedule::Type)estType);
92 if (m_currentSchedule == 0) {
93 kdError()<<k_funcinfo<<
"No current schedule to calculate"<<endl;
96 Effort::Use estType = (Effort::Use)m_currentSchedule->type();
97 if (
type() == Type_Project) {
98 initiateCalculation(*m_currentSchedule);
99 if (m_constraint == Node::MustStartOn) {
104 propagateEarliestStart(m_currentSchedule->earliestStart);
105 m_currentSchedule->latestFinish = calculateForward(estType);
106 propagateLatestFinish(m_currentSchedule->latestFinish);
107 calculateBackward(estType);
108 m_currentSchedule->endTime = scheduleForward(m_currentSchedule->startTime, estType);
109 calcCriticalPath(
false);
115 propagateLatestFinish(m_currentSchedule->latestFinish);
116 m_currentSchedule->earliestStart = calculateBackward(estType);
117 propagateEarliestStart(m_currentSchedule->earliestStart);
118 calculateForward(estType);
119 m_currentSchedule->startTime = scheduleBackward(m_currentSchedule->endTime, estType);
120 calcCriticalPath(
true);
124 m_currentSchedule->notScheduled =
false;
125 }
else if (
type() == Type_Subproject) {
126 kdWarning()<<k_funcinfo<<
"Subprojects not implemented"<<endl;
128 kdError()<<k_funcinfo<<
"Illegal project type: "<<
type()<<endl;
132bool Project::calcCriticalPath(
bool fromEnd) {
135 TQPtrListIterator<Node> startnodes = m_startNodes;
136 for (; startnodes.current(); ++startnodes) {
137 startnodes.current()->calcCriticalPath(fromEnd);
140 TQPtrListIterator<Node> endnodes = m_endNodes;
141 for (; endnodes.current(); ++endnodes) {
142 endnodes.current()->calcCriticalPath(fromEnd);
150 if (m_currentSchedule)
151 return m_currentSchedule->startTime;
158 if (m_currentSchedule)
159 return m_currentSchedule->endTime;
173DateTime Project::calculateForward(
int use) {
175 if (type() == Node::Type_Project) {
180 TQPtrListIterator<Node> endnodes = m_endNodes;
181 for (; endnodes.current(); ++endnodes) {
182 time = endnodes.current()->calculateForward(use);
183 if (!finish.isValid() || time > finish)
194DateTime Project::calculateBackward(
int use) {
196 if (type() == Node::Type_Project) {
201 TQPtrListIterator<Node> startnodes = m_startNodes;
202 for (; startnodes.current(); ++startnodes) {
203 time = startnodes.current()->calculateBackward(use);
204 if (!start.isValid() || time < start)
215DateTime Project::scheduleForward(
const DateTime &earliest,
int use) {
217 DateTime end = earliest;
219 TQPtrListIterator<Node> it(m_endNodes);
220 for (; it.current(); ++it) {
221 time = it.current()->scheduleForward(earliest, use);
230DateTime Project::scheduleBackward(
const DateTime &latest,
int use) {
232 DateTime start = latest;
234 TQPtrListIterator<Node> it(m_startNodes);
235 for (; it.current(); ++it) {
236 time = it.current()->scheduleBackward(latest, use);
245void Project::adjustSummarytask() {
246 TQPtrListIterator<Node> it(m_summarytasks);
247 for (; it.current(); ++it) {
248 it.current()->adjustSummarytask();
252void Project::initiateCalculation(Schedule &sch) {
255 m_visitedForward =
false;
256 m_visitedBackward =
false;
257 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
258 for ( ; git.current(); ++git ) {
259 git.current()->initiateCalculation(sch);
261 Node::initiateCalculation(sch);
262 m_startNodes.clear();
264 m_summarytasks.clear();
265 initiateCalculationLists(m_startNodes, m_endNodes, m_summarytasks);
268void Project::initiateCalculationLists(TQPtrList<Node> &startnodes, TQPtrList<Node> &endnodes, TQPtrList<Node> &summarytasks) {
270 if (type() == Node::Type_Project) {
271 TQPtrListIterator<Node> it = childNodeIterator();
272 for (; it.current(); ++it) {
273 it.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
280bool Project::load(TQDomElement &element) {
284 TQString
id = element.attribute(
"id");
286 kdWarning()<<k_funcinfo<<
"Id must be unique: "<<
id<<endl;
288 m_name = element.attribute(
"name");
289 m_leader = element.attribute(
"leader");
290 m_description = element.attribute(
"description");
295 s = element.attribute(
"scheduling",
"0");
296 m_constraint = (Node::ConstraintType)s.toInt(&ok);
299 if (m_constraint != Node::MustStartOn &&
300 m_constraint != Node::MustFinishOn) {
301 kdError()<<k_funcinfo<<
"Illegal constraint: "<<constraintToString()<<endl;
302 setConstraint(Node::MustStartOn);
304 s = element.attribute(
"start-time");
306 m_constraintStartTime = DateTime::fromString(s);
307 s = element.attribute(
"end-time");
309 m_constraintEndTime = DateTime::fromString(s);
313 TQDomNodeList list = element.childNodes();
314 for (
unsigned int i=0; i<list.count(); ++i) {
315 if (list.item(i).isElement()) {
316 TQDomElement e = list.item(i).toElement();
317 if (e.tagName() ==
"calendar") {
320 Calendar *child =
new Calendar();
321 child->setProject(
this);
322 if (child->load(e)) {
326 kdError()<<k_funcinfo<<
"Failed to load calendar"<<endl;
329 }
else if (e.tagName() ==
"standard-worktime") {
331 StandardWorktime *child =
new StandardWorktime();
332 if (child->load(e)) {
333 setStandardWorktime(child);
335 kdError()<<k_funcinfo<<
"Failed to load standard worktime"<<endl;
341 for (
unsigned int i=0; i<list.count(); ++i) {
342 if (list.item(i).isElement()) {
343 TQDomElement e = list.item(i).toElement();
345 if (e.tagName() ==
"resource-group") {
348 ResourceGroup *child =
new ResourceGroup(
this);
349 if (child->load(e)) {
350 addResourceGroup(child);
358 for (
unsigned int i=0; i<list.count(); ++i) {
359 if (list.item(i).isElement()) {
360 TQDomElement e = list.item(i).toElement();
362 if (e.tagName() ==
"project") {
365 Project *child =
new Project(
this);
366 if (child->load(e)) {
367 if (!addTask(child,
this)) {
374 }
else if (e.tagName() ==
"task") {
378 Task *child =
new Task(
this);
379 if (child->load(e, *
this)) {
380 if (!addTask(child,
this)) {
391 for (
unsigned int i=0; i<list.count(); ++i) {
392 if (list.item(i).isElement()) {
393 TQDomElement e = list.item(i).toElement();
394 if (e.tagName() ==
"accounts") {
398 if (!m_accounts.load(e, *
this)) {
399 kdError()<<k_funcinfo<<
"Failed to load accounts"<<endl;
401 }
else if (e.tagName() ==
"relation") {
405 Relation *child =
new Relation();
406 if (!child->load(e, *
this)) {
408 kdError()<<k_funcinfo<<
"Failed to load relation"<<endl;
412 }
else if (e.tagName() ==
"schedules") {
416 TQDomNodeList lst = e.childNodes();
417 for (
unsigned int i=0; i<lst.count(); ++i) {
418 if (lst.item(i).isElement()) {
419 TQDomElement el = lst.item(i).toElement();
420 if (el.tagName() ==
"schedule") {
421 MainSchedule *sch =
new MainSchedule();
422 if (sch->loadXML(el, *
this)) {
425 setParentSchedule(sch);
427 sch->setScheduled(
true);
429 kdError()<<k_funcinfo<<
"Failed to load schedule"<<endl;
441 TQPtrListIterator<Calendar> calit(m_calendars);
442 for (; calit.current(); ++calit) {
443 if (calit.current()->id() == calit.current()->parentId()) {
444 kdError()<<k_funcinfo<<
"Calendar want itself as parent"<<endl;
447 calit.current()->setParent(calendar(calit.current()->parentId()));
450 TQIntDictIterator<Schedule> it = m_schedules;
452 if (m_constraint == Node::MustFinishOn)
453 m_constraintEndTime = it.current()->endTime;
455 m_constraintStartTime = it.current()->startTime;
462void Project::save(TQDomElement &element)
const {
463 TQDomElement me = element.ownerDocument().createElement(
"project");
464 element.appendChild(me);
466 me.setAttribute(
"name", m_name);
467 me.setAttribute(
"leader", m_leader);
468 me.setAttribute(
"id", m_id);
469 me.setAttribute(
"description", m_description);
473 me.setAttribute(
"scheduling",constraintToString());
474 me.setAttribute(
"start-time", m_constraintStartTime.toString(TQt::ISODate));
475 me.setAttribute(
"end-time", m_constraintEndTime.toString(TQt::ISODate));
480 TQPtrListIterator<Calendar> calit(m_calendars);
481 for (; calit.current(); ++calit) {
482 calit.current()->save(me);
485 if (m_standardWorktime)
486 m_standardWorktime->save(me);
489 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
490 for ( ; git.current(); ++git ) {
491 git.current()->save(me);
495 TQPtrListIterator<Relation> it(m_dependParentNodes);
496 for ( ; it.current(); ++it ) {
497 it.current()->save(me);
500 for (
int i=0; i<numChildren(); i++)
502 getChildNode(i)->save(me);
505 TQPtrListIterator<Node> nodes(m_nodes);
506 for ( ; nodes.current(); ++nodes ) {
507 nodes.current()->saveRelations(me);
510 if (!m_schedules.isEmpty()) {
511 TQDomElement el = me.ownerDocument().createElement(
"schedules");
513 TQIntDictIterator<Schedule> it = m_schedules;
514 for (; it.current(); ++it) {
515 if (!it.current()->isDeleted() && it.current()->isScheduled()) {
516 TQDomElement schs = el.ownerDocument().createElement(
"schedule");
517 el.appendChild(schs);
518 it.current()->saveXML(schs);
520 Node::saveAppointments(schs, it.current()->id());
527 TQPtrListIterator<Node> it = m_nodes;
528 for (; it.current(); ++it) {
529 it.current()->setParentSchedule(sch);
534 m_resourceGroups.append(group);
538void Project::removeResourceGroup(ResourceGroup * group){
539 m_resourceGroups.remove(group);
543void Project::removeResourceGroup(
int ){
548void Project::insertResourceGroup(
unsigned int ,
552TQPtrList<ResourceGroup> &Project::resourceGroups() {
553 return m_resourceGroups;
556bool Project::addTask( Node* task, Node* position )
560 if ( 0 == position ) {
561 kdError()<<k_funcinfo<<
"position=0, could not add task: "<<task->name()<<endl;
567 if ( Node::Type_Project == position->type() ) {
568 return addSubTask(task, position);
572 Node* parentNode = position->getParent();
574 kdDebug()<<k_funcinfo<<
"parent node not found???"<<endl;
577 int index = parentNode->findChildNode( position );
580 kdDebug()<<k_funcinfo<<
"Task not found???"<<endl;
583 return addSubTask(task, index+1, parentNode);
586bool Project::addSubTask( Node* task, Node* position )
590 if ( 0 == position ) {
591 kdError()<<k_funcinfo<<
"No parent, can not add subtask: "<<task->name()<<endl;
594 if (!registerNodeId(task)) {
595 kdError()<<k_funcinfo<<
"Failed to register node id, can not add subtask: "<<task->name()<<endl;
598 position->addChildNode(task);
602bool Project::addSubTask( Node* task,
int index, Node* parent )
606 kdError()<<k_funcinfo<<
"No parent, can not add subtask: "<<task->name()<<endl;
609 if (!registerNodeId(task)) {
610 kdError()<<k_funcinfo<<
"Failed to register node id, can not add subtask: "<<task->name()<<endl;
613 parent->insertChildNode(index, task);
617void Project::delTask(Node *node)
619 Node *parent = node->getParent();
621 kdDebug()<<k_funcinfo<<
"Node must have a parent!"<<endl;
624 removeId(node->id());
625 parent->delChildNode(node,
false);
628bool Project::canIndentTask(Node* node)
635 if (node->type() == Node::Type_Project) {
640 Node* parentNode = node->getParent();
644 if (parentNode->findChildNode(node) == -1) {
645 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
648 Node *sib = node->siblingBefore();
653 if (node->findParentRelation(sib) || node->findChildRelation(sib)) {
660bool Project::indentTask( Node* node )
662 if (canIndentTask(node)) {
663 Node *newParent = node->siblingBefore();
664 node->getParent()->delChildNode(node,
false);
665 newParent->addChildNode(node);
671bool Project::canUnindentTask( Node* node )
678 if ( Node::Type_Project == node->type() ) {
684 Node* parentNode = node->getParent();
688 Node* grandParentNode = parentNode->getParent();
689 if ( !grandParentNode ) {
693 int index = parentNode->findChildNode( node );
695 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
701bool Project::unindentTask( Node* node )
703 if (canUnindentTask(node)) {
704 Node *parentNode = node->getParent();
705 Node *grandParentNode = parentNode->getParent();
706 parentNode->delChildNode(node,
false);
707 grandParentNode->addChildNode(node,parentNode);
713bool Project::canMoveTaskUp( Node* node )
718 Node* parentNode = node->getParent();
723 if (parentNode->findChildNode(node) == -1) {
724 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
727 if (node->siblingBefore()) {
733bool Project::moveTaskUp( Node* node )
735 if (canMoveTaskUp(node)) {
736 return node->getParent()->moveChildUp(node);
741bool Project::canMoveTaskDown( Node* node )
746 Node* parentNode = node->getParent();
750 if (parentNode->findChildNode(node) == -1) {
751 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
754 if (node->siblingAfter()) {
760bool Project::moveTaskDown( Node* node )
762 if (canMoveTaskDown(node)) {
763 return node->getParent()->moveChildDown(node);
768Task *Project::createTask(Node* parent) {
769 Task* node =
new Task(parent);
770 node->setId(uniqueNodeId());
774Task *Project::createTask(Task &def, Node* parent) {
775 Task* node =
new Task(def, parent);
776 node->setId(uniqueNodeId());
782 while (
findNode(TQString(
"%1").arg(i))) {
785 return TQString(
"%1").arg(i);
789 kdDebug()<<k_funcinfo<<
"id="<<
id<<endl;
790 return (m_parent ? m_parent->removeId(
id) : nodeIdDict.remove(
id));
794 kdDebug()<<k_funcinfo<<
"id="<<
id<<
" "<<node->name()<<endl;
795 m_parent ? m_parent->insertId(
id, node) : nodeIdDict.insert(
id, node);
799 if (node->id().isEmpty()) {
800 kdError()<<k_funcinfo<<
"Id is empty."<<endl;
809 kdError()<<k_funcinfo<<
"Id allready exists for different task: "<<node->id()<<endl;
817 return findResourceGroup(
id);
821 return findResource(
id);
825EffortCostMap Project::plannedEffortCostPrDay(
const TQDate &,
const TQDate &)
const {
836 TQPtrListIterator<Node> it(childNodeIterator());
837 for (; it.current(); ++it) {
838 eff += it.current()->plannedEffort();
847 TQPtrListIterator<Node> it(childNodeIterator());
848 for (; it.current(); ++it) {
849 eff += it.current()->plannedEffort(date);
858 TQPtrListIterator<Node> it(childNodeIterator());
859 for (; it.current(); ++it) {
860 eff += it.current()->plannedEffortTo(date);
869 TQPtrListIterator<Node> it(childNodeIterator());
870 for (; it.current(); ++it) {
871 eff += it.current()->actualEffort();
880 TQPtrListIterator<Node> it(childNodeIterator());
881 for (; it.current(); ++it) {
882 eff += it.current()->actualEffort(date);
891 TQPtrListIterator<Node> it(childNodeIterator());
892 for (; it.current(); ++it) {
893 eff += it.current()->actualEffortTo(date);
901 TQPtrListIterator<Node> it(childNodeIterator());
902 for (; it.current(); ++it) {
903 c += it.current()->plannedCost();
912 TQPtrListIterator<Node> it(childNodeIterator());
913 for (; it.current(); ++it) {
914 c += it.current()->plannedCost(date);
923 TQPtrListIterator<Node> it(childNodeIterator());
924 for (; it.current(); ++it) {
925 c += it.current()->plannedCostTo(date);
933 TQPtrListIterator<Node> it(childNodeIterator());
934 for (; it.current(); ++it) {
935 c += it.current()->actualCost();
944 TQPtrListIterator<Node> it(childNodeIterator());
945 for (; it.current(); ++it) {
946 c += it.current()->actualCost(date);
955 TQPtrListIterator<Node> it(childNodeIterator());
956 for (; it.current(); ++it) {
957 c += it.current()->actualCostTo(date);
962void Project::addCalendar(
Calendar *calendar) {
964 m_calendars.append(calendar);
971TQPtrList<Calendar> Project::calendars() {
972 TQPtrList<Calendar> list;
973 TQPtrListIterator<Calendar> it = m_calendars;
974 for (; it.current(); ++it) {
975 if (!it.current()->isDeleted()) {
976 list.append(it.current());
982void Project::setStandardWorktime(StandardWorktime * worktime) {
983 if (m_standardWorktime != worktime) {
984 delete m_standardWorktime;
985 m_standardWorktime = worktime;
992 if (!child || par->isDependChildOf(child)) {
997 if (par->isParentOf(child) || child->isParentOf(par)) {
1001 legal = legalChildren(par, child);
1003 legal = legalParents(par, child);
1008bool Project::legalParents(
Node *par,
Node *child) {
1011 for (
int i=0; i < par->numDependParentNodes() && legal; ++i) {
1012 Node *pNode = par->getDependParentNode(i)->
parent();
1013 if (child->isParentOf(pNode) || pNode->isParentOf(child)) {
1017 legal = legalChildren(pNode, child);
1020 legal = legalParents(pNode, child);
1025bool Project::legalChildren(Node *par, Node *child) {
1028 for (
int j=0; j < child->numDependChildNodes() && legal; ++j) {
1029 Node *cNode = child->getDependChildNode(j)->child();
1030 if (par->isParentOf(cNode) || cNode->isParentOf(par)) {
1034 legal = legalChildren(par, cNode);
1041 if (
type() == Type_Subproject || def.level0Enabled()) {
1044 TQPtrListIterator<Node> it = m_nodes;
1045 for (
int i=0; it.current(); ++it) {
1046 it.current()->generateWBS(++i, def, m_wbs);
1054 TQDictIterator<Resource> it = resourceIdDict;
1055 for (; it.current(); ++it) {
1056 it.current()->setCurrentSchedule(
id);
1063 while (m_schedules.find(i)) {
1072 kdDebug()<<k_funcinfo<<
"id="<<
id<<endl;
1073 return calendarIdDict.remove(
id);
1077 kdDebug()<<k_funcinfo<<
"id="<<
id<<
": "<<
calendar->name()<<endl;
1078 calendarIdDict.insert(
id,
calendar);
1082void Project::printDebug(
bool children, TQCString indent) {
1084 kdDebug()<<indent<<
"+ Project node: "<<name()<<endl;
1086 TQPtrListIterator<ResourceGroup> it(resourceGroups());
1087 for ( ; it.current(); ++it)
1088 it.current()->printDebug(indent);
1090 Node::printDebug(children, indent);
1092void Project::printCalendarDebug(TQCString indent) {
1093 kdDebug()<<indent<<
"-------- Calendars debug printout --------"<<endl;
1094 TQPtrListIterator<Calendar> it = m_calendars;
1095 for (; it.current(); ++it) {
1096 it.current()->printDebug(indent +
"--");
1099 if (m_standardWorktime)
1100 m_standardWorktime->printDebug();
Calendar defines the working and nonworking days and hours.
DateTime is a TQDateTime which knows about Duration.
The duration class can be used to store a timespan in a convenient format.
MainSchedule holds scheduling information for the main project node.
This class represents any node in the project, a node can be a project or a subproject or any task.
Schedule * findSchedule(const TQString name, const Schedule::Type type) const
Find schedule matching name and type. Does not return deleted schedule.
DateTime getLatestFinish() const
getLatestFinish() returns latest time this node can finish
DateTime m_constraintEndTime
m_constraintEndTime is used if any of the constraints FixedInterval, FinishNotLater,...
void addSchedule(Schedule *schedule)
Add schedule to list, replace if schedule with same id allready exists.
virtual void setCurrentSchedule(long id)
Set current schedule to schedule with identity id, for me and my children.
DateTime m_constraintStartTime
m_constraintTime is used if any of the constraints FixedInterval, StartNotEarlier,...
virtual void calcResourceOverbooked()
Calculates if the assigned resource is overbooked within the duration of this node.
DateTime getEarliestStart() const
getEarliestStart() returns earliest time this node can start given the constraints of the network.
virtual void generateWBS(int count, WBSDefinition &def, TQString wbs=TQString())
Generate WBS.
virtual double plannedCost()
Returns the total planned cost for this project.
virtual void setCurrentSchedule(long id)
Set current schedule to schedule with identity id, for me and my children.
ResourceGroup * group(TQString id)
Returns the resourcegroup with identity id.
virtual bool removeCalendarId(const TQString &id)
Remove the calendar with identity id from the register.
TQString uniqueNodeId(int seed=1)
Create a unique id.
virtual void setParentSchedule(Schedule *sch)
Set parent schedule for my children.
virtual Duration plannedEffort()
Returns the total planned effort for this project (or subproject).
virtual Duration actualEffortTo(const TQDate &date)
Returns the actual effort up to and including date.
Resource * resource(TQString id)
Returns the resource with identity id.
virtual bool removeId(const TQString &id)
Remove the node with identity id from the register.
virtual double plannedCostTo(const TQDate &date)
Planned cost up to and including date.
Duration * getExpectedDuration()
Returns the duration calculated as latestFinish - earliestStart.
bool registerNodeId(Node *node)
Register node. The nodes id must be unique and non-empty.
bool legalToLink(Node *par, Node *child)
Check if node par can be linked to node child.
virtual Calendar * findCalendar(const TQString &id) const
Find the calendar with identity id.
Calendar * calendar(const TQString id) const
Returns the calendar with identity id.
virtual DateTime startTime() const
Return the scheduled start time.
virtual double actualCostTo(const TQDate &date)
Actual cost up to and including date.
virtual Duration actualEffort()
Returns the actual effort.
void generateWBS(int count, WBSDefinition &def, TQString wbs=TQString())
Generate WBS.
virtual Duration plannedEffortTo(const TQDate &date)
Returns the planned effort up to and including date.
virtual DateTime endTime() const
Return the scheduled end time.
virtual Node * findNode(const TQString &id) const
Find the node with identity id.
MainSchedule * createSchedule(TQString name, Schedule::Type type)
Create new schedule with unique id.
virtual void insertId(const TQString &id, const Node *node)
Insert the node with identity id.
void calculate(Schedule *scedule)
Calculate the whole project.
Duration * getRandomDuration()
Instead of using the expected duration, generate a random value using the Distribution of each Task.
virtual int type() const
Returns the node type. Can be Type_Project or Type_Subproject.
virtual double actualCost()
Returns the actually reported cost for this project.
virtual void insertCalendarId(const TQString &id, const Calendar *calendar)
Insert the calendar with identity id.
This class represents a group of similar resources to be assigned to a task e.g.
Any resource that is used by a task.
The Schedule class holds data calculated during project calculation and scheduling,...