diff --git a/src/kptview.cpp b/src/kptview.cpp index be96d003..3ff039da 100644 --- a/src/kptview.cpp +++ b/src/kptview.cpp @@ -1,3299 +1,3260 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2011 Dag Andersen Copyright (C) 2012 Dag Andersen Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptview.h" #include #include #include "KoDocumentInfo.h" #include "KoMainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kptlocale.h" #include "kptviewbase.h" #include "kptaccountsview.h" #include "kptaccountseditor.h" #include "kptcalendareditor.h" #include "kptfactory.h" #include "kptmilestoneprogressdialog.h" #include "kpttaskdescriptiondialog.h" #include "kptdocumentsdialog.h" #include "kptnode.h" #include "kptmaindocument.h" #include "kptproject.h" #include "kptmainprojectdialog.h" #include "kpttask.h" #include "kptsummarytaskdialog.h" #include "kpttaskdialog.h" #include "kpttaskprogressdialog.h" #include "kptganttview.h" #include "kpttaskeditor.h" #include "kptdependencyeditor.h" #include "kptperteditor.h" #include "kptdatetime.h" #include "kptcommand.h" #include #include "kptrelation.h" #include "kptrelationdialog.h" #include "kptresourceappointmentsview.h" #include "ResourceGroupEditor.h" #include "kptresourceeditor.h" #include "kptscheduleeditor.h" #include "kptresourcedialog.h" #include "kptresource.h" #include "kptstandardworktimedialog.h" #include "kptwbsdefinitiondialog.h" -#include "kptresourceassignmentview.h" #include "kpttaskstatusview.h" #include "kptsplitterview.h" #include "kptpertresult.h" #include "kptinsertfiledlg.h" #include "kptloadsharedprojectsdialog.h" #include "kpthtmlview.h" #include "about/aboutpage.h" #include "kptlocaleconfigmoneydialog.h" #include "kptflatproxymodel.h" #include "kpttaskstatusmodel.h" #include "kptworkpackagemergedialog.h" #include "Help.h" #include "performance/PerformanceStatusView.h" #include "performance/ProjectStatusView.h" #include "reportsgenerator/ReportsGeneratorView.h" #ifdef PLAN_USE_KREPORT #include "reports/reportview.h" #include "reports/reportdata.h" #endif #include "kptviewlistdialog.h" #include "kptviewlistdocker.h" #include "kptviewlist.h" #include "kptschedulesdocker.h" #include "kptpart.h" #include "kptdebug.h" #include "calligraplansettings.h" #include "kptprintingcontrolprivate.h" // #include "KPtViewAdaptor.h" #include using namespace KPlato; View::View(KoPart *part, MainDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_currentEstimateType(Estimate::Use_Expected), m_scheduleActionGroup(new QActionGroup(this)), m_readWrite(false), m_defaultView(1), m_partpart (part) { //debugPlan; new Help(KPlatoSettings::contextPath(), KPlatoSettings::contextLanguage()); doc->registerView(this); setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName()); if (!doc->isReadWrite()) setXMLFile("calligraplan_readonly.rc"); else setXMLFile("calligraplan.rc"); // new ViewAdaptor(this); m_sp = new QSplitter(this); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); layout->addWidget(m_sp); ViewListDocker *docker = 0; if (mainWindow() == 0) { // Don't use docker if embedded m_viewlist = new ViewListWidget(doc, m_sp); m_viewlist->setProject(&(getProject())); connect(m_viewlist, &ViewListWidget::selectionChanged, this, &View::slotSelectionChanged); connect(this, &View::currentScheduleManagerChanged, m_viewlist, &ViewListWidget::setSelectedSchedule); connect(m_viewlist, &ViewListWidget::updateViewInfo, this, &View::slotUpdateViewInfo); } else { ViewListDockerFactory vl(this); docker = static_cast(mainWindow()->createDockWidget(&vl)); if (docker->view() != this) { docker->setView(this); } m_viewlist = docker->viewList(); #if 0 //SchedulesDocker SchedulesDockerFactory sdf; SchedulesDocker *sd = dynamic_cast(createDockWidget(&sdf)); Q_ASSERT(sd); sd->setProject(&getProject()); connect(sd, SIGNAL(selectionChanged(KPlato::ScheduleManager*)), SLOT(slotSelectionChanged(KPlato::ScheduleManager*))); connect(this, &View::currentScheduleManagerChanged, sd, SLOT(setSelectedSchedule(KPlato::ScheduleManager*))); #endif } m_tab = new QStackedWidget(m_sp); //////////////////////////////////////////////////////////////////////////////////////////////////// // Add sub views createIntroductionView(); // The menu items // ------ File actionCreateTemplate = new QAction(koIcon("document-save-as-template"), i18n("Create Project Template..."), this); actionCollection()->addAction("file_createtemplate", actionCreateTemplate); connect(actionCreateTemplate, SIGNAL(triggered(bool)), SLOT(slotCreateTemplate())); actionCreateNewProject = new QAction(i18n("Create New Project..."), this); actionCollection()->addAction("file_createnewproject", actionCreateNewProject); connect(actionCreateNewProject, &QAction::triggered, this, &View::slotCreateNewProject); // ------ Edit actionCut = actionCollection()->addAction(KStandardAction::Cut, "edit_cut", this, SLOT(slotEditCut())); actionCopy = actionCollection()->addAction(KStandardAction::Copy, "edit_copy", this, SLOT(slotEditCopy())); actionPaste = actionCollection()->addAction(KStandardAction::Paste, "edit_paste", this, SLOT(slotEditPaste())); // ------ View actionCollection()->addAction(KStandardAction::Redisplay, "view_refresh" , this, SLOT(slotRefreshView())); actionViewSelector = new KToggleAction(i18n("Show Selector"), this); actionCollection()->addAction("view_show_selector", actionViewSelector); connect(actionViewSelector, &QAction::triggered, this, &View::slotViewSelector); // ------ Insert // ------ Project actionEditMainProject = new QAction(koIcon("view-time-schedule-edit"), i18n("Edit..."), this); actionCollection()->addAction("project_edit", actionEditMainProject); connect(actionEditMainProject, &QAction::triggered, this, &View::slotProjectEdit); actionEditStandardWorktime = new QAction(koIcon("configure"), i18n("Define Estimate Conversions..."), this); actionCollection()->addAction("project_worktime", actionEditStandardWorktime); connect(actionEditStandardWorktime, &QAction::triggered, this, &View::slotProjectWorktime); actionDefineWBS = new QAction(koIcon("configure"), i18n("Define WBS Pattern..."), this); actionCollection()->addAction("tools_define_wbs", actionDefineWBS); connect(actionDefineWBS, &QAction::triggered, this, &View::slotDefineWBS); actionCurrencyConfig = new QAction(koIcon("configure"), i18n("Define Currency..."), this); actionCollection()->addAction("config_currency", actionCurrencyConfig); connect(actionCurrencyConfig, &QAction::triggered, this, &View::slotCurrencyConfig); QAction *actionProjectDescription = new QAction(koIcon("document-edit"), i18n("Edit Description..."), this); actionCollection()->addAction("edit_project_description", actionProjectDescription); connect(actionProjectDescription, &QAction::triggered, this, &View::slotOpenProjectDescription); // ------ Tools actionInsertFile = new QAction(koIcon("document-import"), i18n("Insert Project File..."), this); actionCollection()->addAction("insert_file", actionInsertFile); connect(actionInsertFile, &QAction::triggered, this, &View::slotInsertFile); actionLoadSharedProjects = new QAction(koIcon("document-import"), i18n("Load Shared Projects..."), this); actionCollection()->addAction("load_shared_projects", actionLoadSharedProjects); connect(actionLoadSharedProjects, &QAction::triggered, this, &View::slotLoadSharedProjects); #ifdef PLAN_USE_KREPORT actionOpenReportFile = new QAction(koIcon("document-open"), i18n("Open Report Definition File..."), this); actionCollection()->addAction("reportdesigner_open_file", actionOpenReportFile); connect(actionOpenReportFile, QAction::triggered, this, &View::slotOpenReportFile); #endif // ------ Help actionIntroduction = new QAction(koIcon("dialog-information"), i18n("Introduction to Plan"), this); actionCollection()->addAction("plan_introduction", actionIntroduction); connect(actionIntroduction, &QAction::triggered, this, &View::slotIntroduction); // ------ Popup actionOpenNode = new QAction(koIcon("document-edit"), i18n("Edit..."), this); actionCollection()->addAction("node_properties", actionOpenNode); connect(actionOpenNode, &QAction::triggered, this, &View::slotOpenCurrentNode); actionTaskProgress = new QAction(koIcon("document-edit"), i18n("Progress..."), this); actionCollection()->addAction("task_progress", actionTaskProgress); connect(actionTaskProgress, &QAction::triggered, this, &View::slotTaskProgress); actionDeleteTask = new QAction(koIcon("edit-delete"), i18n("Delete Task"), this); actionCollection()->addAction("delete_task", actionDeleteTask); connect(actionDeleteTask, &QAction::triggered, this, &View::slotDeleteCurrentTask); actionTaskDescription = new QAction(koIcon("document-edit"), i18n("Description..."), this); actionCollection()->addAction("task_description", actionTaskDescription); connect(actionTaskDescription, &QAction::triggered, this, &View::slotTaskDescription); actionDocuments = new QAction(koIcon("document-edit"), i18n("Documents..."), this); actionCollection()->addAction("task_documents", actionDocuments); connect(actionDocuments, &QAction::triggered, this, &View::slotDocuments); actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask); connect(actionIndentTask, &QAction::triggered, this, &View::slotIndentTask); actionUnindentTask= new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask); connect(actionUnindentTask, &QAction::triggered, this, &View::slotUnindentTask); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Task Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp); connect(actionMoveTaskUp, &QAction::triggered, this, &View::slotMoveTaskUp); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Task Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown); connect(actionMoveTaskDown, &QAction::triggered, this, &View::slotMoveTaskDown); actionEditResource = new QAction(koIcon("document-edit"), i18n("Edit Resource..."), this); actionCollection()->addAction("edit_resource", actionEditResource); connect(actionEditResource, &QAction::triggered, this, &View::slotEditCurrentResource); actionEditRelation = new QAction(koIcon("document-edit"), i18n("Edit Dependency..."), this); actionCollection()->addAction("edit_dependency", actionEditRelation); connect(actionEditRelation, &QAction::triggered, this, &View::slotModifyCurrentRelation); actionDeleteRelation = new QAction(koIcon("edit-delete"), i18n("Delete Dependency"), this); actionCollection()->addAction("delete_dependency", actionDeleteRelation); connect(actionDeleteRelation, &QAction::triggered, this, &View::slotDeleteRelation); // Viewlist popup connect(m_viewlist, &ViewListWidget::createView, this, &View::slotCreateView); m_workPackageButton = new QToolButton(this); m_workPackageButton->hide(); m_workPackageButton->setIcon(koIcon("application-x-vnd.kde.plan.work")); m_workPackageButton->setText(i18n("Work Packages...")); m_workPackageButton->setToolTip(i18nc("@info:tooltip", "Work packages available")); m_workPackageButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); connect(m_workPackageButton, &QToolButton::clicked, this, &View::openWorkPackageMergeDialog); m_estlabel = new QLabel("", 0); if (statusBar()) { addStatusBarItem(m_estlabel, 0, true); } connect(&getProject(), &Project::scheduleManagerAdded, this, &View::slotScheduleAdded); connect(&getProject(), &Project::scheduleManagerRemoved, this, &View::slotScheduleRemoved); connect(&getProject(), &Project::scheduleManagersSwapped, this, &View::slotScheduleSwapped); connect(&getProject(), &Project::sigCalculationFinished, this, &View::slotScheduleCalculated); slotPlugScheduleActions(); connect(doc, &MainDocument::changed, this, &View::slotUpdate); connect(m_scheduleActionGroup, &QActionGroup::triggered, this, &View::slotViewSchedule); connect(getPart(), &MainDocument::workPackageLoaded, this, &View::slotWorkPackageLoaded); // views take time for large projects QTimer::singleShot(0, this, &View::initiateViews); const QList pluginFactories = KoPluginLoader::instantiatePluginFactories(QStringLiteral("calligraplan/extensions")); foreach (KPluginFactory* factory, pluginFactories) { QObject *object = factory->create(this, QVariantList()); KXMLGUIClient *clientPlugin = dynamic_cast(object); if (clientPlugin) { insertChildClient(clientPlugin); } else { // not our/valid plugin, so delete the created object object->deleteLater(); } } //debugPlan<<" end"; } View::~View() { // Disconnect and delete so we do not get called by destroyed() signal const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { disconnect(it.key(), &QObject::destroyed, this, &View::slotActionDestroyed); m_scheduleActionGroup->removeAction(it.key()); delete it.key(); } ViewBase *view = currentView(); if (view) { // deactivate view to remove dockers etc slotGuiActivated(view, false); } /* removeStatusBarItem(m_estlabel); delete m_estlabel;*/ } void View::initiateViews() { QApplication::setOverrideCursor(Qt::WaitCursor); createViews(); connect(m_viewlist, &ViewListWidget::activated, this, &View::slotViewActivated); // after createViews() !! connect(m_viewlist, &ViewListWidget::viewListItemRemoved, this, &View::slotViewListItemRemoved); // after createViews() !! connect(m_viewlist, &ViewListWidget::viewListItemInserted, this, &View::slotViewListItemInserted); ViewListDocker *docker = qobject_cast(m_viewlist->parent()); if (docker) { // after createViews() !! connect(m_viewlist, &ViewListWidget::modified, docker, &ViewListDocker::slotModified); connect(m_viewlist, &ViewListWidget::modified, getPart(), &MainDocument::slotViewlistModified); connect(getPart(), &MainDocument::viewlistModified, docker, &ViewListDocker::updateWindowTitle); } connect(m_tab, &QStackedWidget::currentChanged, this, &View::slotCurrentChanged); slotSelectDefaultView(); loadContext(); QApplication::restoreOverrideCursor(); } void View::slotCreateNewProject() { debugPlan; if (KMessageBox::Continue == KMessageBox::warningContinueCancel(this, xi18nc("@info", "This action cannot be undone." "Create a new Project from the current project " "with new project- and task identities." "Resource- and calendar identities are not changed." "All scheduling information is removed." "Do you want to continue?"))) { emit currentScheduleManagerChanged(0); getPart()->createNewProject(); slotOpenNode(&getProject()); } } void View::slotCreateTemplate() { debugPlan; KoFileDialog dlg(nullptr, KoFileDialog::SaveFile, "Create Template"); dlg.setNameFilters(QStringList()<<"Plan Template (*.plant)"); QString file = dlg.filename(); if (!file.isEmpty()) { QTemporaryDir dir; dir.setAutoRemove(false); QString tmpfile = dir.path() + '/' + QUrl(file).fileName(); tmpfile.replace(".plant", ".plan"); Part *part = new Part(this); MainDocument *doc = new MainDocument(part); part->setDocument(doc); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave(0); //disable bool ok = koDocument()->exportDocument(QUrl::fromUserInput("file:/" + tmpfile)); ok &= doc->loadNativeFormat(tmpfile); if (ok) { // strip unused data Project *project = doc->project(); for (ScheduleManager *sm : project->scheduleManagers()) { DeleteScheduleManagerCmd c(*project, sm); c.redo(); } } ok &= doc->saveNativeFormat(file); part->deleteLater(); } } void View::createViews() { Context *ctx = getPart()->context(); if (ctx && ctx->isLoaded()) { debugPlan<<"isLoaded"; KoXmlNode n = ctx->context().namedItem("categories"); if (n.isNull()) { warnPlan<<"No categories"; } else { n = n.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() != "category") { continue; } debugPlan<<"category: "<addCategory(ct, cn); KoXmlNode n1 = e.firstChild(); for (; ! n1.isNull(); n1 = n1.nextSibling()) { if (! n1.isElement()) { continue; } KoXmlElement e1 = n1.toElement(); if (e1.tagName() != "view") { continue; } ViewBase *v = 0; QString type = e1.attribute("viewtype"); QString tag = e1.attribute("tag"); QString name = e1.attribute("name"); QString tip = e1.attribute("tooltip"); v = createView(cat, type, tag, name, tip); //KoXmlNode settings = e1.namedItem("settings "); ???? KoXmlNode settings = e1.firstChild(); for (; ! settings.isNull(); settings = settings.nextSibling()) { if (settings.nodeName() == "settings") { break; } } if (v && settings.isElement()) { debugPlan<<" settings"; v->loadContext(settings.toElement()); } } } } } else { debugPlan<<"Default"; ViewBase *v = 0; ViewListItem *cat; QString ct = "Editors"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createCalendarEditor(cat, "CalendarEditor", QString(), TIP_USE_DEFAULT_TEXT); createAccountsEditor(cat, "AccountsEditor", QString(), TIP_USE_DEFAULT_TEXT); v = createResourceGroupEditor(cat, "ResourceGroupEditor", QString(), TIP_USE_DEFAULT_TEXT); v = createResourceEditor(cat, "ResourceEditor", QString(), TIP_USE_DEFAULT_TEXT); v = createTaskEditor(cat, "TaskEditor", QString(), TIP_USE_DEFAULT_TEXT); m_defaultView = m_tab->count() - 1; v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodeAllocation << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeResponsible << NodeModel::NodeDescription ); v = createTaskEditor(cat, "TaskConstraintEditor", i18n("Task Constraints"), i18n("Edit task scheduling constraints")); v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodePriority << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeDescription ); v = createTaskEditor(cat, "TaskCostEditor", i18n("Task Cost"), i18n("Edit task cost")); v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription ); createDependencyEditor(cat, "DependencyEditor", QString(), TIP_USE_DEFAULT_TEXT); // Do not show by default // createPertEditor(cat, "PertEditor", QString(), TIP_USE_DEFAULT_TEXT); createScheduleHandler(cat, "ScheduleHandlerView", QString(), TIP_USE_DEFAULT_TEXT); ct = "Views"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createGanttView(cat, "GanttView", QString(), TIP_USE_DEFAULT_TEXT); createMilestoneGanttView(cat, "MilestoneGanttView", QString(), TIP_USE_DEFAULT_TEXT); createResourceAppointmentsView(cat, "ResourceAppointmentsView", QString(), TIP_USE_DEFAULT_TEXT); createResourceAppointmentsGanttView(cat, "ResourceAppointmentsGanttView", QString(), TIP_USE_DEFAULT_TEXT); createAccountsView(cat, "AccountsView", QString(), TIP_USE_DEFAULT_TEXT); ct = "Execution"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createProjectStatusView(cat, "ProjectStatusView", QString(), TIP_USE_DEFAULT_TEXT); createPerformanceStatusView(cat, "PerformanceStatusView", QString(), TIP_USE_DEFAULT_TEXT); v = createTaskStatusView(cat, "TaskStatusView", QString(), TIP_USE_DEFAULT_TEXT); v = createTaskView(cat, "TaskView", QString(), TIP_USE_DEFAULT_TEXT); v = createTaskWorkPackageView(cat, "TaskWorkPackageView", QString(), TIP_USE_DEFAULT_TEXT); ct = "Reports"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createReportsGeneratorView(cat, "ReportsGeneratorView", i18n("Generate reports"), TIP_USE_DEFAULT_TEXT); #ifdef PLAN_USE_KREPORT // Let user add reports explicitly, we prefer reportsgenerator now // A little hack to get the user started... #if 0 ReportView *rv = qobject_cast(createReportView(cat, "ReportView", i18n("Task Status Report"), TIP_USE_DEFAULT_TEXT)); if (rv) { QDomDocument doc; doc.setContent(standardTaskStatusReport()); rv->loadXML(doc); } #endif #endif } } ViewBase *View::createView(ViewListItem *cat, const QString &type, const QString &tag, const QString &name, const QString &tip, int index) { ViewBase *v = 0; //NOTE: type is the same as classname (so if it is changed...) if (type == "CalendarEditor") { v = createCalendarEditor(cat, tag, name, tip, index); } else if (type == "AccountsEditor") { v = createAccountsEditor(cat, tag, name, tip, index); } else if (type == "ResourceGroupEditor") { v = createResourceGroupEditor(cat, tag, name, tip, index); } else if (type == "ResourceEditor") { v = createResourceEditor(cat, tag, name, tip, index); } else if (type == "TaskEditor") { v = createTaskEditor(cat, tag, name, tip, index); } else if (type == "DependencyEditor") { v = createDependencyEditor(cat, tag, name, tip, index); } else if (type == "PertEditor") { v = createPertEditor(cat, tag, name, tip, index); } else if (type == "ScheduleEditor") { v = createScheduleEditor(cat, tag, name, tip, index); } else if (type == "ScheduleHandlerView") { v = createScheduleHandler(cat, tag, name, tip, index); } else if (type == "ProjectStatusView") { v = createProjectStatusView(cat, tag, name, tip, index); } else if (type == "TaskStatusView") { v = createTaskStatusView(cat, tag, name, tip, index); } else if (type == "TaskView") { v = createTaskView(cat, tag, name, tip, index); } else if (type == "TaskWorkPackageView") { v = createTaskWorkPackageView(cat, tag, name, tip, index); } else if (type == "GanttView") { v = createGanttView(cat, tag, name, tip, index); } else if (type == "MilestoneGanttView") { v = createMilestoneGanttView(cat, tag, name, tip, index); } else if (type == "ResourceAppointmentsView") { v = createResourceAppointmentsView(cat, tag, name, tip, index); } else if (type == "ResourceAppointmentsGanttView") { v = createResourceAppointmentsGanttView(cat, tag, name, tip, index); } else if (type == "AccountsView") { v = createAccountsView(cat, tag, name, tip, index); } else if (type == "PerformanceStatusView") { v = createPerformanceStatusView(cat, tag, name, tip, index); } else if (type == "ReportsGeneratorView") { v = createReportsGeneratorView(cat, tag, name, tip, index); } else if (type == "ReportView") { #ifdef PLAN_USE_KREPORT v = createReportView(cat, tag, name, tip, index); #endif } else { warnPlan<<"Unknown viewtype: "<type() == ViewListItem::ItemType_SubView) { itm->setViewInfo(defaultViewInfo(itm->viewType())); } else if (itm->type() == ViewListItem::ItemType_Category) { ViewInfo vi = defaultCategoryInfo(itm->tag()); itm->setViewInfo(vi); } } ViewInfo View::defaultViewInfo(const QString &type) const { ViewInfo vi; if (type == "CalendarEditor") { vi.name = i18n("Work & Vacation"); vi.tip = xi18nc("@info:tooltip", "Edit working- and vacation days for resources"); } else if (type == "AccountsEditor") { vi.name = i18n("Cost Breakdown Structure"); vi.tip = xi18nc("@info:tooltip", "Edit cost breakdown structure."); } else if (type == "ResourceGroupEditor") { vi.name = i18n("Resource groups"); vi.tip = xi18nc("@info:tooltip", "Edit resource groups"); } else if (type == "ResourceEditor") { vi.name = i18n("Resources"); vi.tip = xi18nc("@info:tooltip", "Edit resource breakdown structure"); } else if (type == "TaskEditor") { vi.name = i18n("Tasks"); vi.tip = xi18nc("@info:tooltip", "Edit work breakdown structure"); } else if (type == "DependencyEditor") { vi.name = i18n("Dependencies (Graphic)"); vi.tip = xi18nc("@info:tooltip", "Edit task dependencies"); } else if (type == "PertEditor") { vi.name = i18n("Dependencies (List)"); vi.tip = xi18nc("@info:tooltip", "Edit task dependencies"); } else if (type == "ScheduleEditor") { // This view is not used stand-alone atm vi.name = i18n("Schedules"); } else if (type == "ScheduleHandlerView") { vi.name = i18n("Schedules"); vi.tip = xi18nc("@info:tooltip", "Calculate and analyze project schedules"); } else if (type == "ProjectStatusView") { vi.name = i18n("Project Performance Chart"); vi.tip = xi18nc("@info:tooltip", "View project status information"); } else if (type == "TaskStatusView") { vi.name = i18n("Task Status"); vi.tip = xi18nc("@info:tooltip", "View task progress information"); } else if (type == "TaskView") { vi.name = i18n("Task Execution"); vi.tip = xi18nc("@info:tooltip", "View task execution information"); } else if (type == "TaskWorkPackageView") { vi.name = i18n("Work Package View"); vi.tip = xi18nc("@info:tooltip", "View task work package information"); } else if (type == "GanttView") { vi.name = i18n("Gantt"); vi.tip = xi18nc("@info:tooltip", "View Gantt chart"); } else if (type == "MilestoneGanttView") { vi.name = i18n("Milestone Gantt"); vi.tip = xi18nc("@info:tooltip", "View milestone Gantt chart"); } else if (type == "ResourceAppointmentsView") { vi.name = i18n("Resource Assignments"); vi.tip = xi18nc("@info:tooltip", "View resource assignments in a table"); } else if (type == "ResourceAppointmentsGanttView") { vi.name = i18n("Resource Assignments (Gantt)"); vi.tip = xi18nc("@info:tooltip", "View resource assignments in Gantt chart"); } else if (type == "AccountsView") { vi.name = i18n("Cost Breakdown"); vi.tip = xi18nc("@info:tooltip", "View planned and actual cost"); } else if (type == "PerformanceStatusView") { vi.name = i18n("Tasks Performance Chart"); vi.tip = xi18nc("@info:tooltip", "View tasks performance status information"); } else if (type == "ReportsGeneratorView") { vi.name = i18n("Reports Generator"); vi.tip = xi18nc("@info:tooltip", "Generate reports"); } else if (type == "ReportView") { vi.name = i18n("Report"); vi.tip = xi18nc("@info:tooltip", "View report"); } else { warnPlan<<"Unknown viewtype: "<count()-1) : m_visitedViews.at(m_visitedViews.count() - 2); debugPlan<<"Prev:"<setCurrentIndex(view); return; } if (url.url().startsWith(QLatin1String("about:plan"))) { getPart()->aboutPage().generatePage(v->htmlPart(), url); return; } } if (url.scheme() == QLatin1String("help")) { KHelpClient::invokeHelp("", url.fileName()); return; } // try to open the url debugPlan<htmlPart().setJScriptEnabled(false); v->htmlPart().setJavaEnabled(false); v->htmlPart().setMetaRefreshEnabled(false); v->htmlPart().setPluginsEnabled(false); slotOpenUrlRequest(v, QUrl("about:plan/main")); connect(v, &HtmlView::openUrlRequest, this, &View::slotOpenUrlRequest); m_tab->addWidget(v); return v; } ViewBase *View::createResourceAppointmentsGanttView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ResourceAppointmentsGanttView *v = new ResourceAppointmentsGanttView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("ResourceAppointmentsGanttView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, v, &ResourceAppointmentsGanttView::setScheduleManager); connect(v, &ResourceAppointmentsGanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->setProject(&(getProject())); v->setScheduleManager(currentScheduleManager()); v->updateReadWrite(m_readWrite); return v; } ViewBase *View::createResourceAppointmentsView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ResourceAppointmentsView *v = new ResourceAppointmentsView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("ResourceAppointmentsView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, v, &ResourceAppointmentsView::setScheduleManager); connect(v, &ResourceAppointmentsView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->setProject(&(getProject())); v->setScheduleManager(currentScheduleManager()); v->updateReadWrite(m_readWrite); return v; } ViewBase *View::createResourceGroupEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ResourceGroupEditor *e = new ResourceGroupEditor(getKoPart(), getPart(), m_tab); e->setViewSplitMode(false); m_tab->addWidget(e); e->setProject(&(getProject())); ViewListItem *i = m_viewlist->addView(cat, tag, name, e, getPart(), "", index); ViewInfo vi = defaultViewInfo("ResourceGroupEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(e, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(e, &ResourceGroupEditor::deleteObjectList, this, &View::slotDeleteResourceObjects); connect(e, &ResourceGroupEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); e->updateReadWrite(m_readWrite); return e; } ViewBase *View::createResourceEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ResourceEditor *resourceeditor = new ResourceEditor(getKoPart(), getPart(), m_tab); resourceeditor->setViewSplitMode(false); m_tab->addWidget(resourceeditor); resourceeditor->setProject(&(getProject())); ViewListItem *i = m_viewlist->addView(cat, tag, name, resourceeditor, getPart(), "", index); ViewInfo vi = defaultViewInfo("ResourceEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(resourceeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(resourceeditor, &ResourceEditor::deleteObjectList, this, &View::slotDeleteResourceObjects); connect(resourceeditor, &ResourceEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); resourceeditor->updateReadWrite(m_readWrite); return resourceeditor; } ViewBase *View::createTaskEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { TaskEditor *taskeditor = new TaskEditor(getKoPart(), getPart(), m_tab); taskeditor->setViewSplitMode(false); m_tab->addWidget(taskeditor); ViewListItem *i = m_viewlist->addView(cat, tag, name, taskeditor, getPart(), "", index); ViewInfo vi = defaultViewInfo("TaskEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } taskeditor->setProject(&(getProject())); taskeditor->setScheduleManager(currentScheduleManager()); connect(this, &View::currentScheduleManagerChanged, taskeditor, &TaskEditor::setScheduleManager); connect(taskeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(taskeditor, &TaskEditor::addTask, this, &View::slotAddTask); connect(taskeditor, &TaskEditor::addMilestone, this, &View::slotAddMilestone); connect(taskeditor, &TaskEditor::addSubtask, this, &View::slotAddSubTask); connect(taskeditor, &TaskEditor::addSubMilestone, this, &View::slotAddSubMilestone); connect(taskeditor, &TaskEditor::deleteTaskList, this, &View::slotDeleteTaskList); connect(taskeditor, &TaskEditor::moveTaskUp, this, &View::slotMoveTaskUp); connect(taskeditor, &TaskEditor::moveTaskDown, this, &View::slotMoveTaskDown); connect(taskeditor, &TaskEditor::indentTask, this, &View::slotIndentTask); connect(taskeditor, &TaskEditor::unindentTask, this, &View::slotUnindentTask); connect(taskeditor, &TaskEditor::saveTaskModule, this, &View::saveTaskModule); connect(taskeditor, &TaskEditor::removeTaskModule, this, &View::removeTaskModule); connect(taskeditor, &ViewBase::openDocument, static_cast(m_partpart), &Part::openTaskModule); connect(taskeditor, &TaskEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(taskeditor, &TaskEditor::openTaskDescription, this, &View::slotOpenTaskDescription); taskeditor->updateReadWrite(m_readWrite); return taskeditor; } ViewBase *View::createAccountsEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { AccountsEditor *ae = new AccountsEditor(getKoPart(), getPart(), m_tab); m_tab->addWidget(ae); ViewListItem *i = m_viewlist->addView(cat, tag, name, ae, getPart(), "", index); ViewInfo vi = defaultViewInfo("AccountsEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } ae->draw(getProject()); connect(ae, &ViewBase::guiActivated, this, &View::slotGuiActivated); ae->updateReadWrite(m_readWrite); return ae; } ViewBase *View::createCalendarEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { CalendarEditor *calendareditor = new CalendarEditor(getKoPart(), getPart(), m_tab); m_tab->addWidget(calendareditor); ViewListItem *i = m_viewlist->addView(cat, tag, name, calendareditor, getPart(), "", index); ViewInfo vi = defaultViewInfo("CalendarEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } calendareditor->draw(getProject()); connect(calendareditor, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(calendareditor, &CalendarEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); calendareditor->updateReadWrite(m_readWrite); return calendareditor; } ViewBase *View::createScheduleHandler(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ScheduleHandlerView *handler = new ScheduleHandlerView(getKoPart(), getPart(), m_tab); m_tab->addWidget(handler); ViewListItem *i = m_viewlist->addView(cat, tag, name, handler, getPart(), "", index); ViewInfo vi = defaultViewInfo("ScheduleHandlerView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(handler->scheduleEditor(), &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager); connect(handler->scheduleEditor(), &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager); connect(handler->scheduleEditor(), &ScheduleEditor::moveScheduleManager, this, &View::slotMoveScheduleManager); connect(handler->scheduleEditor(), &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule); connect(handler->scheduleEditor(), &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule); connect(handler, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, handler, &ScheduleHandlerView::currentScheduleManagerChanged); connect(handler, &ScheduleHandlerView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(handler, &ScheduleHandlerView::editNode, this, &View::slotOpenNode); connect(handler, &ScheduleHandlerView::editResource, this, &View::slotEditResource); handler->draw(getProject()); handler->updateReadWrite(m_readWrite); return handler; } ScheduleEditor *View::createScheduleEditor(QWidget *parent) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), parent); connect(scheduleeditor, &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager); connect(scheduleeditor, &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager); connect(scheduleeditor, &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule); connect(scheduleeditor, &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule); scheduleeditor->updateReadWrite(m_readWrite); return scheduleeditor; } ViewBase *View::createScheduleEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), m_tab); m_tab->addWidget(scheduleeditor); ViewListItem *i = m_viewlist->addView(cat, tag, name, scheduleeditor, getPart(), "", index); ViewInfo vi = defaultViewInfo("ScheduleEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } scheduleeditor->setProject(&(getProject())); connect(scheduleeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(scheduleeditor, &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager); connect(scheduleeditor, &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager); connect(scheduleeditor, &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule); connect(scheduleeditor, &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule); scheduleeditor->updateReadWrite(m_readWrite); return scheduleeditor; } ViewBase *View::createDependencyEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { DependencyEditor *editor = new DependencyEditor(getKoPart(), getPart(), m_tab); m_tab->addWidget(editor); ViewListItem *i = m_viewlist->addView(cat, tag, name, editor, getPart(), "", index); ViewInfo vi = defaultViewInfo("DependencyEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } editor->draw(getProject()); connect(editor, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(editor, &DependencyEditor::addRelation, this, &View::slotAddRelation); connect(editor, &DependencyEditor::modifyRelation, this, &View::slotModifyRelation); connect(editor, &DependencyEditor::editRelation, this, &View::slotEditRelation); connect(editor, &DependencyEditor::editNode, this, &View::slotOpenNode); connect(editor, &DependencyEditor::addTask, this, &View::slotAddTask); connect(editor, &DependencyEditor::addMilestone, this, &View::slotAddMilestone); connect(editor, &DependencyEditor::addSubMilestone, this, &View::slotAddSubMilestone); connect(editor, &DependencyEditor::addSubtask, this, &View::slotAddSubTask); connect(editor, &DependencyEditor::deleteTaskList, this, &View::slotDeleteTaskList); connect(this, &View::currentScheduleManagerChanged, editor, &DependencyEditor::setScheduleManager); connect(editor, &DependencyEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); editor->updateReadWrite(m_readWrite); editor->setScheduleManager(currentScheduleManager()); return editor; } ViewBase *View::createPertEditor(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { PertEditor *perteditor = new PertEditor(getKoPart(), getPart(), m_tab); m_tab->addWidget(perteditor); ViewListItem *i = m_viewlist->addView(cat, tag, name, perteditor, getPart(), "", index); ViewInfo vi = defaultViewInfo("PertEditor"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } perteditor->draw(getProject()); connect(perteditor, &ViewBase::guiActivated, this, &View::slotGuiActivated); m_updatePertEditor = true; perteditor->updateReadWrite(m_readWrite); return perteditor; } ViewBase *View::createProjectStatusView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ProjectStatusView *v = new ProjectStatusView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("ProjectStatusView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, v, &ProjectStatusView::setScheduleManager); v->updateReadWrite(m_readWrite); v->setProject(&getProject()); v->setScheduleManager(currentScheduleManager()); return v; } ViewBase *View::createPerformanceStatusView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { PerformanceStatusView *v = new PerformanceStatusView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("PerformanceStatusView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, v, &PerformanceStatusView::setScheduleManager); connect(v, &PerformanceStatusView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->updateReadWrite(m_readWrite); v->setProject(&getProject()); v->setScheduleManager(currentScheduleManager()); return v; } ViewBase *View::createTaskStatusView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { TaskStatusView *taskstatusview = new TaskStatusView(getKoPart(), getPart(), m_tab); taskstatusview->setViewSplitMode(false); m_tab->addWidget(taskstatusview); ViewListItem *i = m_viewlist->addView(cat, tag, name, taskstatusview, getPart(), "", index); ViewInfo vi = defaultViewInfo("TaskStatusView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } connect(taskstatusview, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, taskstatusview, &TaskStatusView::setScheduleManager); connect(taskstatusview, &TaskStatusView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(taskstatusview, &TaskStatusView::openTaskDescription, this, &View::slotOpenTaskDescription); taskstatusview->updateReadWrite(m_readWrite); taskstatusview->draw(getProject()); taskstatusview->setScheduleManager(currentScheduleManager()); return taskstatusview; } ViewBase *View::createTaskView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { TaskView *v = new TaskView(getKoPart(), getPart(), m_tab); v->setViewSplitMode(false); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("TaskView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } v->draw(getProject()); v->setScheduleManager(currentScheduleManager()); connect(this, &View::currentScheduleManagerChanged, v, &TaskView::setScheduleManager); connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(v, &TaskView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(v, &TaskView::openTaskDescription, this, &View::slotOpenTaskDescription); v->updateReadWrite(m_readWrite); return v; } ViewBase *View::createTaskWorkPackageView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { TaskWorkPackageView *v = new TaskWorkPackageView(getKoPart(), getPart(), m_tab); v->setViewSplitMode(false); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("TaskWorkPackageView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } v->setProject(&getProject()); v->setScheduleManager(currentScheduleManager()); connect(this, &View::currentScheduleManagerChanged, v, &TaskWorkPackageView::setScheduleManager); connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(v, &TaskWorkPackageView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(v, &TaskWorkPackageView::mailWorkpackage, this, &View::slotMailWorkpackage); connect(v, &TaskWorkPackageView::publishWorkpackages, this, &View::slotPublishWorkpackages); connect(v, &TaskWorkPackageView::openWorkpackages, this, &View::openWorkPackageMergeDialog); connect(this, &View::workPackagesAvailable, v, &TaskWorkPackageView::slotWorkpackagesAvailable); connect(v, &TaskWorkPackageView::checkForWorkPackages, getPart(), &MainDocument::checkForWorkPackages); connect(v, &TaskWorkPackageView::loadWorkPackageUrl, this, &View::loadWorkPackage); connect(v, &TaskWorkPackageView::openTaskDescription, this, &View::slotOpenTaskDescription); v->updateReadWrite(m_readWrite); return v; } ViewBase *View::createGanttView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { GanttView *ganttview = new GanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite()); m_tab->addWidget(ganttview); ViewListItem *i = m_viewlist->addView(cat, tag, name, ganttview, getPart(), "", index); ViewInfo vi = defaultViewInfo("GanttView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } ganttview->setProject(&(getProject())); ganttview->setScheduleManager(currentScheduleManager()); connect(ganttview, &ViewBase::guiActivated, this, &View::slotGuiActivated); /* TODO: Review these connect(ganttview, SIGNAL(addRelation(KPlato::Node*,KPlato::Node*,int)), SLOT(slotAddRelation(KPlato::Node*,KPlato::Node*,int))); connect(ganttview, SIGNAL(modifyRelation(KPlato::Relation*,int)), SLOT(slotModifyRelation(KPlato::Relation*,int))); connect(ganttview, SIGNAL(modifyRelation(KPlato::Relation*)), SLOT(slotModifyRelation(KPlato::Relation*))); connect(ganttview, SIGNAL(itemDoubleClicked()), SLOT(slotOpenNode())); connect(ganttview, SIGNAL(itemRenamed(KPlato::Node*,QString)), this, SLOT(slotRenameNode(KPlato::Node*,QString)));*/ connect(this, &View::currentScheduleManagerChanged, ganttview, &GanttView::setScheduleManager); connect(ganttview, &GanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(ganttview, &GanttView::openTaskDescription, this, &View::slotOpenTaskDescription); ganttview->updateReadWrite(m_readWrite); return ganttview; } ViewBase *View::createMilestoneGanttView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { MilestoneGanttView *ganttview = new MilestoneGanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite()); m_tab->addWidget(ganttview); ViewListItem *i = m_viewlist->addView(cat, tag, name, ganttview, getPart(), "", index); ViewInfo vi = defaultViewInfo("MilestoneGanttView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } ganttview->setProject(&(getProject())); ganttview->setScheduleManager(currentScheduleManager()); connect(ganttview, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(this, &View::currentScheduleManagerChanged, ganttview, &MilestoneGanttView::setScheduleManager); connect(ganttview, &MilestoneGanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(ganttview, &MilestoneGanttView::openTaskDescription, this, &View::slotOpenTaskDescription); ganttview->updateReadWrite(m_readWrite); return ganttview; } ViewBase *View::createAccountsView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { AccountsView *accountsview = new AccountsView(getKoPart(), &getProject(), getPart(), m_tab); m_tab->addWidget(accountsview); ViewListItem *i = m_viewlist->addView(cat, tag, name, accountsview, getPart(), "", index); ViewInfo vi = defaultViewInfo("AccountsView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } accountsview->setScheduleManager(currentScheduleManager()); connect(this, &View::currentScheduleManagerChanged, accountsview, &AccountsView::setScheduleManager); connect(accountsview, &ViewBase::guiActivated, this, &View::slotGuiActivated); accountsview->updateReadWrite(m_readWrite); return accountsview; } -ViewBase *View::createResourceAssignmentView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) -{ - ResourceAssignmentView *resourceAssignmentView = new ResourceAssignmentView(getKoPart(), getPart(), m_tab); - m_tab->addWidget(resourceAssignmentView); - m_updateResourceAssignmentView = true; - - ViewListItem *i = m_viewlist->addView(cat, tag, name, resourceAssignmentView, getPart(), "", index); - ViewInfo vi = defaultViewInfo("ResourceAssignmentView"); - if (name.isEmpty()) { - i->setText(0, vi.name); - } - if (tip == TIP_USE_DEFAULT_TEXT) { - i->setToolTip(0, vi.tip); - } else { - i->setToolTip(0, tip); - } - - resourceAssignmentView->draw(getProject()); - - connect(resourceAssignmentView, &ViewBase::guiActivated, this, &View::slotGuiActivated); - - connect(resourceAssignmentView, &ResourceAssignmentView::requestPopupMenu, this, &View::slotPopupMenuRequested); - resourceAssignmentView->updateReadWrite(m_readWrite); - return resourceAssignmentView; -} - ViewBase *View::createReportsGeneratorView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ReportsGeneratorView *v = new ReportsGeneratorView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("ReportsGeneratorView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } v->setProject(&getProject()); connect(this, &View::currentScheduleManagerChanged, v, &ViewBase::setScheduleManager); connect(this, &View::currentScheduleManagerChanged, v, &ViewBase::slotRefreshView); v->setScheduleManager(currentScheduleManager()); connect(v, &ViewBase::guiActivated, this, &View::slotGuiActivated); connect(v, &ReportsGeneratorView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->updateReadWrite(m_readWrite); return v; } ViewBase *View::createReportView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { #ifdef PLAN_USE_KREPORT ReportView *v = new ReportView(getKoPart(), getPart(), m_tab); m_tab->addWidget(v); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo("ReportView"); if (name.isEmpty()) { i->setText(0, vi.name); } if (tip == TIP_USE_DEFAULT_TEXT) { i->setToolTip(0, vi.tip); } else { i->setToolTip(0, tip); } v->setProject(&getProject()); connect(this, &View::currentScheduleManagerChanged, v, &ReportView::setScheduleManager); connect(this, &View::currentScheduleManagerChanged, v, SLOT(slotRefreshView())); v->setScheduleManager(currentScheduleManager()); connect(v, &ReportView::guiActivated, this, &View::slotGuiActivated); v->updateReadWrite(m_readWrite); return v; #else Q_UNUSED(cat) Q_UNUSED(tag) Q_UNUSED(name) Q_UNUSED(tip) Q_UNUSED(index) return 0; #endif } Project& View::getProject() const { return getPart() ->getProject(); } KoPrintJob * View::createPrintJob() { KoView *v = qobject_cast(canvas()); if (v == 0) { return 0; } return v->createPrintJob(); } ViewBase *View::currentView() const { return qobject_cast(m_tab->currentWidget()); } void View::slotEditCut() { ViewBase *v = currentView(); if (v) { v->slotEditCut(); } } void View::slotEditCopy() { ViewBase *v = currentView(); if (v) { v->slotEditCopy(); } } void View::slotEditPaste() { ViewBase *v = currentView(); if (v) { v->slotEditPaste(); } } void View::slotRefreshView() { ViewBase *v = currentView(); if (v) { debugPlan<slotRefreshView(); } } void View::slotViewSelector(bool show) { //debugPlan; m_viewlist->setVisible(show); } void View::slotInsertResourcesFile(const QString &file, const QUrl &projects) { getPart()->insertResourcesFile(QUrl(file), projects); } void View::slotInsertFile() { InsertFileDialog *dlg = new InsertFileDialog(getProject(), currentTask(), this); connect(dlg, &QDialog::finished, this, &View::slotInsertFileFinished); dlg->open(); } void View::slotInsertFileFinished(int result) { InsertFileDialog *dlg = qobject_cast(sender()); if (dlg == 0) { return; } if (result == QDialog::Accepted) { getPart()->insertFile(dlg->url(), dlg->parentNode(), dlg->afterNode()); } dlg->deleteLater(); } void View::slotLoadSharedProjects() { LoadSharedProjectsDialog *dlg = new LoadSharedProjectsDialog(getProject(), getPart()->url(), this); connect(dlg, &QDialog::finished, this, &View::slotLoadSharedProjectsFinished); dlg->open(); } void View::slotLoadSharedProjectsFinished(int result) { LoadSharedProjectsDialog *dlg = qobject_cast(sender()); if (dlg == 0) { return; } if (result == QDialog::Accepted) { getPart()->insertSharedProjects(dlg->urls()); } dlg->deleteLater(); } void View::slotProjectEdit() { slotOpenNode(&getProject()); } void View::slotProjectWorktime() { StandardWorktimeDialog *dia = new StandardWorktimeDialog(getProject(), this); connect(dia, &QDialog::finished, this, &View::slotProjectWorktimeFinished); dia->open(); } void View::slotProjectWorktimeFinished(int result) { StandardWorktimeDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if (cmd) { //debugPlan<<"Modifying calendar(s)"; getPart() ->addCommand(cmd); //also executes } } dia->deleteLater(); } void View::slotSelectionChanged(ScheduleManager *sm) { debugPlan<setChecked(true); // this doesn't trigger QActionGroup slotViewSchedule(a); } QList View::sortedActionList() { QMap lst; const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { lst.insert(it.key()->objectName(), it.key()); } return lst.values(); } void View::slotScheduleSwapped(ScheduleManager *from, ScheduleManager *to) { if (currentScheduleManager() == from) { QAction *a = m_scheduleActions.key(to); if (a) { a->setChecked(true); } } } void View::slotScheduleRemoved(const ScheduleManager *sch) { debugPlan<name(); QAction *a = 0; QAction *checked = m_scheduleActionGroup->checkedAction(); QMapIterator i(m_scheduleActions); while (i.hasNext()) { i.next(); if (i.value() == sch) { a = i.key(); break; } } if (a) { unplugActionList("view_schedule_list"); delete a; plugActionList("view_schedule_list", sortedActionList()); if (checked && checked != a) { checked->setChecked(true); } else if (! m_scheduleActions.isEmpty()) { m_scheduleActions.firstKey()->setChecked(true); } } slotViewSchedule(m_scheduleActionGroup->checkedAction()); } void View::slotScheduleAdded(const ScheduleManager *sch) { ScheduleManager *s = const_cast(sch); QAction *checked = m_scheduleActionGroup->checkedAction(); unplugActionList("view_schedule_list"); QAction *act = addScheduleAction(s); plugActionList("view_schedule_list", sortedActionList()); if (!currentScheduleManager()) { if (act) { act->setChecked(true); } else if (! m_scheduleActions.isEmpty()) { m_scheduleActions.firstKey()->setChecked(true); } slotViewSchedule(m_scheduleActionGroup->checkedAction()); } } void View::slotScheduleCalculated(Project *project, ScheduleManager *manager) { Q_UNUSED(project); if (manager == currentScheduleManager()) { slotViewScheduleManager(manager); } } QAction *View::addScheduleAction(ScheduleManager *sch) { QAction *act = 0; QString n = sch->name(); act = new KToggleAction(n, this); actionCollection()->addAction(n, act); m_scheduleActions.insert(act, sch); m_scheduleActionGroup->addAction(act); //debugPlan<<"Add:"<name(); m_scheduleActions.remove(static_cast(o)); } void View::slotPlugScheduleActions() { ScheduleManager *current = currentScheduleManager(); unplugActionList("view_schedule_list"); const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { m_scheduleActionGroup->removeAction(it.key()); delete it.key(); } m_scheduleActions.clear(); QAction *ca = 0; foreach(ScheduleManager *sm, getProject().allScheduleManagers()) { QAction *act = addScheduleAction(sm); if (sm == current) { ca = act; } } plugActionList("view_schedule_list", sortedActionList()); if (ca == 0 && m_scheduleActionGroup->actions().count() > 0) { ca = m_scheduleActionGroup->actions().constFirst(); } if (ca) { ca->setChecked(true); } slotViewSchedule(ca); } void View::slotCalculateSchedule(Project *project, ScheduleManager *sm) { if (project == 0 || sm == 0) { return; } if (sm->parentManager() && ! sm->parentManager()->isScheduled()) { // the parent must be scheduled return; } CalculateScheduleCmd *cmd = new CalculateScheduleCmd(*project, sm, kundo2_i18nc("@info:status 1=schedule name", "Calculate %1", sm->name())); getPart() ->addCommand(cmd); slotUpdate(); } void View::slotRemoveCommands() { while (! m_undocommands.isEmpty()) { m_undocommands.last()->undo(); delete m_undocommands.takeLast(); } } void View::slotBaselineSchedule(Project *project, ScheduleManager *sm) { if (project == 0 || sm == 0) { return; } if (! sm->isBaselined() && project->isBaselined()) { KMessageBox::sorry(this, i18n("Cannot baseline. The project is already baselined.")); return; } MacroCommand *cmd = nullptr; if (sm->isBaselined()) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel(this, i18n("This schedule is baselined. Do you want to remove the baseline?")); if (res == KMessageBox::Cancel) { return; } cmd = new MacroCommand(kundo2_i18n("Reset baseline %1", sm->name())); cmd->addCommand(new ResetBaselineScheduleCmd(*sm)); } else { cmd = new MacroCommand(kundo2_i18n("Baseline %1", sm->name())); if (sm->schedulingMode() == ScheduleManager::AutoMode) { cmd->addCommand(new ModifyScheduleManagerSchedulingModeCmd(*sm, ScheduleManager::ManualMode)); } cmd->addCommand(new BaselineScheduleCmd(*sm, kundo2_i18n("Baseline %1", sm->name()))); } getPart() ->addCommand(cmd); } void View::slotAddScheduleManager(Project *project) { if (project == 0) { return; } ScheduleManager *sm = project->createScheduleManager(); AddScheduleManagerCmd *cmd = new AddScheduleManagerCmd(*project, sm, -1, kundo2_i18n("Add schedule %1", sm->name())); getPart() ->addCommand(cmd); } void View::slotDeleteScheduleManager(Project *project, ScheduleManager *sm) { if (project == 0 || sm == 0) { return; } DeleteScheduleManagerCmd *cmd = new DeleteScheduleManagerCmd(*project, sm, kundo2_i18n("Delete schedule %1", sm->name())); getPart() ->addCommand(cmd); } void View::slotMoveScheduleManager(ScheduleManager *sm, ScheduleManager *parent, int index) { if (sm == 0) { return; } MoveScheduleManagerCmd *cmd = new MoveScheduleManagerCmd(sm, parent, index, kundo2_i18n("Move schedule %1", sm->name())); getPart() ->addCommand(cmd); } void View::slotAddSubTask() { Task * node = getProject().createTask(getPart() ->config().taskDefaults()); SubTaskAddDialog *dia = new SubTaskAddDialog(getProject(), *node, currentNode(), getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotAddSubTaskFinished); dia->open(); } void View::slotAddSubTaskFinished(int result) { SubTaskAddDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand(m); // add task to project } dia->deleteLater(); } void View::slotAddTask() { Task * node = getProject().createTask(getPart() ->config().taskDefaults()); TaskAddDialog *dia = new TaskAddDialog(getProject(), *node, currentNode(), getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotAddTaskFinished); dia->open(); } void View::slotAddTaskFinished(int result) { TaskAddDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand(m); // add task to project } dia->deleteLater(); } void View::slotAddMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); TaskAddDialog *dia = new TaskAddDialog(getProject(), *node, currentNode(), getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotAddMilestoneFinished); dia->open(); } void View::slotAddMilestoneFinished(int result) { TaskAddDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { MacroCommand *c = new MacroCommand(kundo2_i18n("Add milestone")); c->addCommand(dia->buildCommand()); getPart() ->addCommand(c); // add task to project } dia->deleteLater(); } void View::slotAddSubMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); SubTaskAddDialog *dia = new SubTaskAddDialog(getProject(), *node, currentNode(), getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotAddSubMilestoneFinished); dia->open(); } void View::slotAddSubMilestoneFinished(int result) { SubTaskAddDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { MacroCommand *c = new MacroCommand(kundo2_i18n("Add sub-milestone")); c->addCommand(dia->buildCommand()); getPart() ->addCommand(c); // add task to project } dia->deleteLater(); } void View::slotDefineWBS() { //debugPlan; Project &p = getProject(); WBSDefinitionDialog *dia = new WBSDefinitionDialog(p, p.wbsDefinition(), this); connect(dia, &QDialog::finished, this, &View::slotDefineWBSFinished); dia->open(); } void View::slotDefineWBSFinished(int result) { //debugPlan; WBSDefinitionDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command *cmd = dia->buildCommand(); if (cmd) { getPart()->addCommand(cmd); } } dia->deleteLater(); } void View::slotIntroduction() { m_tab->setCurrentIndex(0); } Calendar *View::currentCalendar() { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return 0; } return v->currentCalendar(); } Node *View::currentNode() const { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return 0; } Node * task = v->currentNode(); if (0 != task) { return task; } return &(getProject()); } Task *View::currentTask() const { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return 0; } Node * task = v->currentNode(); if (task) { return dynamic_cast(task); } return 0; } Resource *View::currentResource() { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return 0; } return v->currentResource(); } ResourceGroup *View::currentResourceGroup() { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return 0; } return v->currentResourceGroup(); } void View::slotOpenCurrentNode() { //debugPlan; Node * node = currentNode(); slotOpenNode(node); } void View::slotOpenNode(Node *node) { //debugPlan; if (!node) return ; switch (node->type()) { case Node::Type_Project: { Project * project = static_cast(node); MainProjectDialog *dia = new MainProjectDialog(*project, this); connect(dia, &MainProjectDialog::dialogFinished, this, &View::slotProjectEditFinished); connect(dia, &MainProjectDialog::sigLoadSharedResources, this, &View::slotInsertResourcesFile); connect(dia, &MainProjectDialog::loadResourceAssignments, getPart(), &MainDocument::loadResourceAssignments); connect(dia, &MainProjectDialog::clearResourceAssignments, getPart(), &MainDocument::clearResourceAssignments); dia->open(); break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = static_cast(node); TaskDialog *dia = new TaskDialog(getProject(), *task, getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotTaskEditFinished); dia->open(); break; } case Node::Type_Milestone: { // Use the normal task dialog for now. // Maybe milestone should have it's own dialog, but we need to be able to // enter a duration in case we accidentally set a tasks duration to zero // and hence, create a milestone Task *task = static_cast(node); TaskDialog *dia = new TaskDialog(getProject(), *task, getProject().accounts(), this); connect(dia, &QDialog::finished, this, &View::slotTaskEditFinished); dia->open(); break; } case Node::Type_Summarytask: { Task *task = dynamic_cast(node); Q_ASSERT(task); SummaryTaskDialog *dia = new SummaryTaskDialog(*task, this); connect(dia, &QDialog::finished, this, &View::slotSummaryTaskEditFinished); dia->open(); break; } default: break; // avoid warnings } } void View::slotProjectEditFinished(int result) { MainProjectDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if (cmd) { getPart() ->addCommand(cmd); } } dia->deleteLater(); } void View::slotTaskEditFinished(int result) { TaskDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if (cmd) { getPart() ->addCommand(cmd); } } dia->deleteLater(); } void View::slotSummaryTaskEditFinished(int result) { SummaryTaskDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if (cmd) { getPart() ->addCommand(cmd); } } dia->deleteLater(); } ScheduleManager *View::currentScheduleManager() const { return m_scheduleActions.value(m_scheduleActionGroup->checkedAction()); } long View::activeScheduleId() const { ScheduleManager *s = m_scheduleActions.value(m_scheduleActionGroup->checkedAction()); return s == nullptr || s->expected() == nullptr ? -1 : s->expected()->id(); } void View::setActiveSchedule(long id) { if (id != -1) { QMap::const_iterator it = m_scheduleActions.constBegin(); for (; it != m_scheduleActions.constEnd(); ++it) { int mid = it.value()->expected() == nullptr ? -1 : it.value()->expected()->id(); if (mid == id) { it.key()->setChecked(true); slotViewSchedule(it.key()); // signal not emitted from group, so trigger it here break; } } } } void View::slotTaskProgress() { //debugPlan; Node * node = currentNode(); if (!node) return ; switch (node->type()) { case Node::Type_Project: { break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = dynamic_cast(node); Q_ASSERT(task); TaskProgressDialog *dia = new TaskProgressDialog(*task, currentScheduleManager(), getProject().standardWorktime(), this); connect(dia, &QDialog::finished, this, &View::slotTaskProgressFinished); dia->open(); break; } case Node::Type_Milestone: { Task *task = dynamic_cast(node); Q_ASSERT(task); MilestoneProgressDialog *dia = new MilestoneProgressDialog(*task, this); connect(dia, &QDialog::finished, this, &View::slotMilestoneProgressFinished); dia->open(); break; } case Node::Type_Summarytask: { // TODO break; } default: break; // avoid warnings } } void View::slotTaskProgressFinished(int result) { TaskProgressDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if (m) { getPart() ->addCommand(m); } } dia->deleteLater(); } void View::slotMilestoneProgressFinished(int result) { MilestoneProgressDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if (m) { getPart() ->addCommand(m); } } dia->deleteLater(); } void View::slotOpenProjectDescription() { debugPlan<isReadWrite(); TaskDescriptionDialog *dia = new TaskDescriptionDialog(getProject(), this, !koDocument()->isReadWrite()); connect(dia, &QDialog::finished, this, &View::slotTaskDescriptionFinished); dia->open(); } void View::slotTaskDescription() { slotOpenTaskDescription(!koDocument()->isReadWrite()); } void View::slotOpenTaskDescription(bool ro) { //debugPlan; Node * node = currentNode(); if (!node) return ; switch (node->type()) { case Node::Type_Subproject: //TODO break; case Node::Type_Project: case Node::Type_Task: case Node::Type_Milestone: case Node::Type_Summarytask: { TaskDescriptionDialog *dia = new TaskDescriptionDialog(*node, this, ro); connect(dia, &QDialog::finished, this, &View::slotTaskDescriptionFinished); dia->open(); break; } default: break; // avoid warnings } } void View::slotTaskDescriptionFinished(int result) { TaskDescriptionDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if (m) { getPart() ->addCommand(m); } } dia->deleteLater(); } void View::slotDocuments() { //debugPlan; Node * node = currentNode(); if (!node) { return ; } switch (node->type()) { case Node::Type_Subproject: //TODO break; case Node::Type_Project: case Node::Type_Summarytask: case Node::Type_Task: case Node::Type_Milestone: { DocumentsDialog *dia = new DocumentsDialog(*node, this); connect(dia, &QDialog::finished, this, &View::slotDocumentsFinished); dia->open(); break; } default: break; // avoid warnings } } void View::slotDocumentsFinished(int result) { DocumentsDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if (m) { getPart()->addCommand(m); } } dia->deleteLater(); } void View::slotDeleteTaskList(QList lst) { //debugPlan; foreach (Node *n, lst) { if (n->isScheduled()) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel(this, i18n("A task that has been scheduled will be deleted. This will invalidate the schedule.")); if (res == KMessageBox::Cancel) { return; } break; } } if (lst.count() == 1) { getPart()->addCommand(new NodeDeleteCmd(lst.takeFirst(), kundo2_i18n("Delete task"))); return; } int num = 0; MacroCommand *cmd = new MacroCommand(kundo2_i18np("Delete task", "Delete tasks", lst.count())); while (!lst.isEmpty()) { Node *node = lst.takeFirst(); if (node == 0 || node->parentNode() == 0) { debugPlan << (node ?"Task is main project" :"No current task"); continue; } bool del = true; foreach (Node *n, lst) { if (node->isChildOf(n)) { del = false; // node is going to be deleted when we delete n break; } } if (del) { //debugPlan<name(); cmd->addCommand(new NodeDeleteCmd(node, kundo2_i18n("Delete task"))); num++; } } if (num > 0) { getPart()->addCommand(cmd); } else { delete cmd; } } void View::slotDeleteTask(Node *node) { //debugPlan; if (node == 0 || node->parentNode() == 0) { debugPlan << (node ?"Task is main project" :"No current task"); return ; } if (node->isScheduled()) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel(this, i18n("This task has been scheduled. This will invalidate the schedule.")); if (res == KMessageBox::Cancel) { return; } } NodeDeleteCmd *cmd = new NodeDeleteCmd(node, kundo2_i18n("Delete task")); getPart() ->addCommand(cmd); } void View::slotDeleteCurrentTask() { //debugPlan; return slotDeleteTask(currentNode()); } void View::slotIndentTask() { //debugPlan; Node * node = currentNode(); if (node == 0 || node->parentNode() == 0) { debugPlan << (node ?"Task is main project" :"No current task"); return ; } if (getProject().canIndentTask(node)) { NodeIndentCmd * cmd = new NodeIndentCmd(*node, kundo2_i18n("Indent task")); getPart() ->addCommand(cmd); } } void View::slotUnindentTask() { //debugPlan; Node * node = currentNode(); if (node == 0 || node->parentNode() == 0) { debugPlan << (node ?"Task is main project" :"No current task"); return ; } if (getProject().canUnindentTask(node)) { NodeUnindentCmd * cmd = new NodeUnindentCmd(*node, kundo2_i18n("Unindent task")); getPart() ->addCommand(cmd); } } void View::slotMoveTaskUp() { //debugPlan; Node * task = currentNode(); if (0 == task) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful errorPlan << "No current task" << endl; return ; } if (Node::Type_Project == task->type()) { debugPlan <<"The root node cannot be moved up"; return ; } if (getProject().canMoveTaskUp(task)) { NodeMoveUpCmd * cmd = new NodeMoveUpCmd(*task, kundo2_i18n("Move task up")); getPart() ->addCommand(cmd); } } void View::slotMoveTaskDown() { //debugPlan; Node * task = currentNode(); if (0 == task) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful return ; } if (Node::Type_Project == task->type()) { debugPlan <<"The root node cannot be moved down"; return ; } if (getProject().canMoveTaskDown(task)) { NodeMoveDownCmd * cmd = new NodeMoveDownCmd(*task, kundo2_i18n("Move task down")); getPart() ->addCommand(cmd); } } void View::openRelationDialog(Node *par, Node *child) { //debugPlan; Relation * rel = new Relation(par, child); AddRelationDialog *dia = new AddRelationDialog(getProject(), rel, this); connect(dia, &QDialog::finished, this, &View::slotAddRelationFinished); dia->open(); } void View::slotAddRelationFinished(int result) { AddRelationDialog *dia = qobject_cast(sender()); if (dia == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if (m) { getPart() ->addCommand(m); } } dia->deleteLater(); } void View::slotAddRelation(Node *par, Node *child, int linkType) { //debugPlan; if (linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish) { Relation * rel = new Relation(par, child, static_cast(linkType)); getPart() ->addCommand(new AddRelationCmd(getProject(), rel, kundo2_i18n("Add task dependency"))); } else { openRelationDialog(par, child); } } void View::slotEditRelation(Relation *rel) { //debugPlan; ModifyRelationDialog *dia = new ModifyRelationDialog(getProject(), rel, this); connect(dia, &QDialog::finished, this, &View::slotModifyRelationFinished); dia->open(); } void View::slotModifyRelationFinished(int result) { ModifyRelationDialog *dia = qobject_cast(sender()); if (dia == 0) { return ; } if (result == QDialog::Accepted) { KUndo2Command *cmd = dia->buildCommand(); if (cmd) { getPart() ->addCommand(cmd); } } dia->deleteLater(); } void View::slotModifyRelation(Relation *rel, int linkType) { //debugPlan; if (linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish) { getPart() ->addCommand(new ModifyRelationTypeCmd(rel, static_cast(linkType))); } else { slotEditRelation(rel); } } void View::slotModifyCurrentRelation() { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return; } Relation *rel = v->currentRelation(); if (rel) { slotEditRelation(rel); } } void View::slotDeleteRelation() { ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v == 0) { return; } Relation *rel = v->currentRelation(); if (rel) { getPart()->addCommand(new DeleteRelationCmd(getProject(), rel, kundo2_i18n("Delete task dependency"))); } } void View::slotEditCurrentResource() { //debugPlan; slotEditResource(currentResource()); } void View::slotEditResource(Resource *resource) { if (resource == 0) { return ; } ResourceDialog *dia = new ResourceDialog(getProject(), resource, this); connect(dia, &QDialog::finished, this, &View::slotEditResourceFinished); dia->open(); } void View::slotEditResourceFinished(int result) { //debugPlan; ResourceDialog *dia = qobject_cast(sender()); if (dia == 0) { return ; } if (result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if (cmd) getPart() ->addCommand(cmd); } dia->deleteLater(); } void View::slotDeleteResource(Resource *resource) { getPart()->addCommand(new RemoveResourceCmd(resource, kundo2_i18n("Delete resource"))); } void View::slotDeleteResourceGroup(ResourceGroup *group) { getPart()->addCommand(new RemoveResourceGroupCmd(group->project(), group, kundo2_i18n("Delete resourcegroup"))); } void View::slotDeleteResourceObjects(QObjectList lst) { //debugPlan; foreach (QObject *o, lst) { Resource *r = qobject_cast(o); if (r && r->isScheduled()) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel(this, i18n("A resource that has been scheduled will be deleted. This will invalidate the schedule.")); if (res == KMessageBox::Cancel) { return; } break; } ResourceGroup *g = qobject_cast(o); if (g && g->isScheduled()) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel(this, i18n("A resource that has been scheduled will be deleted. This will invalidate the schedule.")); if (res == KMessageBox::Cancel) { return; } break; } } if (lst.count() == 1) { Resource *r = qobject_cast(lst.first()); if (r) { slotDeleteResource(r); } else { ResourceGroup *g = qobject_cast(lst.first()); if (g) { slotDeleteResourceGroup(g); } } return; } // int num = 0; MacroCommand *cmd = 0, *rc = 0, *gc = 0; foreach (QObject *o, lst) { Resource *r = qobject_cast(o); if (r) { if (rc == 0) rc = new MacroCommand(KUndo2MagicString()); rc->addCommand(new RemoveResourceCmd(r)); continue; } ResourceGroup *g = qobject_cast(o); if (g) { if (gc == 0) gc = new MacroCommand(KUndo2MagicString()); gc->addCommand(new RemoveResourceGroupCmd(g->project(), g)); } } if (rc || gc) { KUndo2MagicString s; if (rc && gc) { s = kundo2_i18n("Delete resourcegroups and resources"); } else if (rc) { s = kundo2_i18np("Delete resource", "Delete resources", lst.count()); } else { s = kundo2_i18np("Delete resourcegroup", "Delete resourcegroups", lst.count()); } cmd = new MacroCommand(s); } if (rc) cmd->addCommand(rc); if (gc) cmd->addCommand(gc); if (cmd) getPart()->addCommand(cmd); } void View::updateReadWrite(bool readwrite) { m_readWrite = readwrite; m_viewlist->setReadWrite(readwrite); } MainDocument *View::getPart() const { return (MainDocument *) koDocument(); } KoPart *View::getKoPart() const { return m_partpart; } void View::slotConnectNode() { //debugPlan; /* NodeItem *curr = ganttview->currentItem(); if (curr) { debugPlan<<"node="<getNode().name(); }*/ } QMenu * View::popupMenu(const QString& name) { //debugPlan; if (factory()) { return ((QMenu*) factory() ->container(name, this)); } debugPlan<<"No factory"; return 0L; } void View::slotUpdate() { //debugPlan<<"calculate="<currentWidget()); } void View::slotGuiActivated(ViewBase *view, bool activate) { if (activate) { foreach (DockWidget *ds, view->dockers()) { m_dockers.append(ds); ds->activate(mainWindow()); } if (!m_dockers.isEmpty()) {debugPlan<<"Added dockers:"<deactivate(mainWindow()); } } } void View::guiActivateEvent(bool activated) { if (activated) { // plug my own actionlists, they may be gone slotPlugScheduleActions(); } // propagate to sub-view ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v) { v->setGuiActive(activated); } } void View::slotViewListItemRemoved(ViewListItem *item) { getPart()->removeViewListItem(this, item); } void View::removeViewListItem(const ViewListItem *item) { if (item == 0) { return; } ViewListItem *itm = m_viewlist->findItem(item->tag()); if (itm == 0) { return; } m_viewlist->removeViewListItem(itm); return; } void View::slotViewListItemInserted(ViewListItem *item, ViewListItem *parent, int index) { getPart()->insertViewListItem(this, item, parent, index); } void View::addViewListItem(const ViewListItem *item, const ViewListItem *parent, int index) { if (item == 0) { return; } if (parent == 0) { if (item->type() != ViewListItem::ItemType_Category) { return; } m_viewlist->blockSignals(true); ViewListItem *cat = m_viewlist->addCategory(item->tag(), item->text(0)); cat->setToolTip(0, item->toolTip(0)); m_viewlist->blockSignals(false); return; } ViewListItem *cat = m_viewlist->findCategory(parent->tag()); if (cat == 0) { return; } m_viewlist->blockSignals(true); createView(cat, item->viewType(), item->tag(), item->text(0), item->toolTip(0), index); m_viewlist->blockSignals(false); } void View::createReportView(const QDomDocument &doc) { #ifdef PLAN_USE_KREPORT QPointer vd = new ViewListReportsDialog(this, *m_viewlist, doc, this); vd->exec(); // FIXME make non-crash delete vd; #else Q_UNUSED(doc) #endif } void View::slotOpenReportFile() { #ifdef PLAN_USE_KREPORT QFileDialog *dlg = new QFileDialog(this); connect(dlg, &QDialog::finished, &View::slotOpenReportFileFinished(int))); dlg->open(); #endif } void View::slotOpenReportFileFinished(int result) { #ifdef PLAN_USE_KREPORT QFileDialog *fdlg = qobject_cast(sender()); if (fdlg == 0 || result != QDialog::Accepted) { return; } QString fn = fdlg->selectedFiles().value(0); if (fn.isEmpty()) { return; } QFile file(fn); if (! file.open(QIODevice::ReadOnly | QIODevice::Text)) { KMessageBox::sorry(this, xi18nc("@info", "Cannot open file:
%1", fn)); return; } QDomDocument doc; doc.setContent(&file); createReportView(doc); #else Q_UNUSED(result) #endif } void View::slotReportDesignFinished(int /*result */) { #ifdef PLAN_USE_KREPORT if (sender()) { sender()->deleteLater(); } #endif } void View::slotCreateView() { ViewListDialog *dlg = new ViewListDialog(this, *m_viewlist, this); connect(dlg, &QDialog::finished, this, &View::slotCreateViewFinished); dlg->open(); } void View::slotCreateViewFinished(int) { if (sender()) { sender()->deleteLater(); } } void View::slotViewActivated(ViewListItem *item, ViewListItem *prev) { QApplication::setOverrideCursor(Qt::WaitCursor); if (prev && prev->type() == ViewListItem::ItemType_Category && m_viewlist->previousViewItem()) { // A view is shown anyway... ViewBase *v = qobject_cast(m_viewlist->previousViewItem()->view()); if (v) { factory()->removeClient(v); v->setGuiActive(false); } } else if (prev && prev->type() == ViewListItem::ItemType_SubView) { ViewBase *v = qobject_cast(prev->view()); if (v) { factory()->removeClient(v); v->setGuiActive(false); } } if (item && item->type() == ViewListItem::ItemType_SubView) { //debugPlan<<"Activate:"<setCurrentWidget(item->view()); // Add sub-view specific gui ViewBase *v = dynamic_cast(m_tab->currentWidget()); if (v) { factory()->addClient(v); v->setGuiActive(true); } } QApplication::restoreOverrideCursor(); } QWidget *View::canvas() const { return m_tab->currentWidget();//KoView::canvas(); } KoPageLayout View::pageLayout() const { return currentView()->pageLayout(); } void View::setPageLayout(const KoPageLayout &pageLayout) { currentView()->setPageLayout(pageLayout); } QPrintDialog *View::createPrintDialog(KoPrintJob *printJob, QWidget *parent) { debugPlan<(printJob); if (! job) { return 0; } QPrintDialog *dia = KoView::createPrintDialog(job, parent); PrintingDialog *j = dynamic_cast(job); if (j) { new PrintingControlPrivate(j, dia); } return dia; } void View::slotCurrentChanged(int view) { m_visitedViews << view; ViewListItem *item = m_viewlist->findItem(qobject_cast(m_tab->currentWidget())); m_viewlist->setCurrentItem(item); } void View::slotSelectDefaultView() { m_tab->setCurrentIndex(qMin(m_defaultView, m_tab->count()-1)); } void View::updateView(QWidget *) { - QApplication::setOverrideCursor(Qt::WaitCursor); - //setScheduleActionsEnabled(); - - QWidget *widget2; - - widget2 = m_viewlist->findView("ResourceAssignmentView"); - if (widget2 && m_updateResourceAssignmentView) - static_cast(widget2) ->draw(getProject()); - m_updateResourceAssignmentView = false; - - QApplication::restoreOverrideCursor(); } void View::slotRenameNode(Node *node, const QString& name) { //debugPlan<type()) { case Node::Type_Task: s = kundo2_i18n("Modify task name"); break; case Node::Type_Milestone: s = kundo2_i18n("Modify milestone name"); break; case Node::Type_Summarytask: s = kundo2_i18n("Modify summarytask name"); break; case Node::Type_Project: s = kundo2_i18n("Modify project name"); break; } NodeModifyNameCmd * cmd = new NodeModifyNameCmd(*node, name, s); getPart() ->addCommand(cmd); } } void View::slotPopupMenuRequested(const QString& menuname, const QPoint & pos) { QMenu * menu = this->popupMenu(menuname); if (menu) { //debugPlan<actions().count(); ViewBase *v = qobject_cast(m_tab->currentWidget()); //debugPlan< lst; if (v) { lst = v->contextActionList(); debugPlan<addSeparator(); foreach (QAction *a, lst) { menu->addAction(a); } } } menu->exec(pos); foreach (QAction *a, lst) { menu->removeAction(a); } } } void View::slotPopupMenu(const QString& menuname, const QPoint &pos, ViewListItem *item) { //debugPlan<context(); if (ctx == 0 || ! ctx->isLoaded()) { return false; } KoXmlElement n = ctx->context(); QString cv = n.attribute("current-view"); if (! cv.isEmpty()) { m_viewlist->setSelected(m_viewlist->findItem(cv)); } else debugPlan<<"No current view"; long id = n.attribute("current-schedule", "-1").toLong(); if (id != -1) { setActiveSchedule(id); } else debugPlan<<"No current schedule"; return true; } void View::saveContext(QDomElement &me) const { //debugPlan; long id = activeScheduleId(); if (id != -1) { me.setAttribute("current-schedule", QString::number((qlonglong)id)); } ViewListItem *item = m_viewlist->findItem(qobject_cast(m_tab->currentWidget())); if (item) { me.setAttribute("current-view", item->tag()); } m_viewlist->save(me); } void View::loadWorkPackage(Project *project, const QList &urls) { bool loaded = false; for (const QUrl &url : urls) { loaded |= getPart()->loadWorkPackage(*project, url); } if (loaded) { slotWorkPackageLoaded(); } } void View::setLabel(ScheduleManager *sm) { //debugPlan; Schedule *s = sm == 0 ? 0 : sm->expected(); if (s && !s->isDeleted() && s->isScheduled()) { m_estlabel->setText(sm->name()); return; } m_estlabel->setText(xi18nc("@info:status", "Not scheduled")); } void View::slotWorkPackageLoaded() { debugPlan<workPackages(); addStatusBarItem(m_workPackageButton, 0, true); emit workPackagesAvailable(true); } void View::openWorkPackageMergeDialog() { WorkPackageMergeDialog *dlg = new WorkPackageMergeDialog(&getProject(), getPart()->workPackages(), this); connect(dlg, &QDialog::finished, this, &View::workPackageMergeDialogFinished); connect(dlg, SIGNAL(terminateWorkPackage(const KPlato::Package*)), getPart(), SLOT(terminateWorkPackage(const KPlato::Package*))); connect(dlg, &WorkPackageMergeDialog::executeCommand, koDocument(), &KoDocument::addCommand); dlg->open(); removeStatusBarItem(m_workPackageButton); emit workPackagesAvailable(false); } void View::workPackageMergeDialogFinished(int result) { debugPlanWp<<"result:"<(sender()); Q_ASSERT(dlg); if (!getPart()->workPackages().isEmpty()) { slotWorkPackageLoaded(); } if (dlg) { dlg->deleteLater(); } } void View::slotMailWorkpackage(Node *node, Resource *resource) { debugPlan; QTemporaryFile tmpfile(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String(".planwork")); tmpfile.setAutoRemove(false); if (! tmpfile.open()) { debugPlan<<"Failed to open file"; KMessageBox::error(0, i18n("Failed to open temporary file")); return; } QUrl url = QUrl::fromLocalFile(tmpfile.fileName()); if (! getPart()->saveWorkPackageUrl(url, node, activeScheduleId(), resource)) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc("@info", "Failed to save to temporary file:
%1", url.url())); return; } QStringList attachURLs; attachURLs << url.url(); QString to = resource == 0 ? node->leader() : (resource->name() + " <" + resource->email() + '>'); QString cc; QString bcc; QString subject = i18n("Work Package: %1", node->name()); QString body = i18nc("1=project name, 2=task name", "%1\n%2", getProject().name(), node->name()); QString messageFile; KToolInvocation::invokeMailer(to, cc, bcc, subject, body, messageFile, attachURLs); } void View::slotPublishWorkpackages(const QList &nodes, Resource *resource, bool mailTo) { debugPlanWp<leader() yet"; return; } bool mail = mailTo; QString body; QStringList attachURLs; QString path; if (getProject().workPackageInfo().publishUrl.isValid()) { path = getProject().workPackageInfo().publishUrl.path(); debugPlanWp<<"publish:"<saveWorkPackageUrl(url, n, activeScheduleId(), resource)) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc("@info", "Failed to save to temporary file:
%1", url.url())); return; } attachURLs << url.url(); body += n->name() + '\n'; } if (mail) { debugPlanWp<name() + " <" + resource->email() + '>'; QString subject = i18n("Work Package for project: %1", getProject().name()); QString cc; QString bcc; QString messageFile; KToolInvocation::invokeMailer(to, cc, bcc, subject, body, messageFile, attachURLs); } } void View::slotCurrencyConfig() { LocaleConfigMoneyDialog *dlg = new LocaleConfigMoneyDialog(getProject().locale(), this); connect(dlg, &QDialog::finished, this, &View::slotCurrencyConfigFinished); dlg->open(); } void View::slotCurrencyConfigFinished(int result) { LocaleConfigMoneyDialog *dlg = qobject_cast(sender()); if (dlg == 0) { return; } if (result == QDialog::Accepted) { KUndo2Command *c = dlg->buildCommand(getProject()); if (c) { getPart()->addCommand(c); } } dlg->deleteLater(); } void View::saveTaskModule(const QUrl &url, Project *project) { // NOTE: workaround: KoResourcePaths::saveLocation("calligraplan_taskmodules"); does not work const QString dir = KoResourcePaths::saveLocation("appdata", "taskmodules/"); debugPlan<<"dir="<setDocument(doc); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave(0); //disable doc->insertProject(*project, 0, 0); // FIXME: destroys project, find better way doc->getProject().setName(project->name()); doc->getProject().setLeader(project->leader()); doc->getProject().setDescription(project->description()); doc->saveNativeFormat(dir + url.fileName()); part->deleteLater(); // also deletes document debugPlan<" "" "" "%1" "" "" "predefined" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "") .arg( i18n("Report"), i18nc("Project manager", "Manager:"), i18n("Project:"), i18n("Task Status Report"), i18nc("As in: Page 1 of 2", "of"), i18n("Page"), i18nc("Task name", "Name"), i18nc("Task completion", "Completion (%)") ); #endif return s; } diff --git a/src/kptview.h b/src/kptview.h index 7580cb93..87970c9b 100644 --- a/src/kptview.h +++ b/src/kptview.h @@ -1,416 +1,413 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2010 Dag Andersen Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTVIEW_H #define KPTVIEW_H #include "plan_export.h" #include #include "kptcontext.h" #include "kptviewbase.h" #include #include #include #include class QMenu; class QPrintDialog; class QStackedWidget; class QSplitter; class QUrl; class QToolButton; class KUndo2Command; class QAction; class KToggleAction; class QLabel; class KConfigSkeleton; class KConfigSkeletonItem; class KPageWidgetItem; class KoView; namespace KPlato { class View; class ViewBase; class ViewListItem; class ViewListWidget; struct ViewInfo; class AccountsView; class ResourceCoverageView; class GanttView; class PertEditor; class AccountsEditor; class TaskEditor; class CalendarEditor; class ScheduleEditor; class ScheduleManager; class CalculateScheduleCmd; -class ResourceAssignmentView; class TaskStatusView; class Calendar; class MainDocument; class Part; class Node; class Project; class Task; class MainSchedule; class Schedule; class Resource; class ResourceGroup; class Relation; class Context; class ViewAdaptor; class HtmlView; class ReportView; class ReportDesignDialog; class DockWidget; class PLAN_EXPORT View : public KoView { Q_OBJECT public: explicit View(KoPart *part, MainDocument *doc, QWidget *parent = 0); ~View() override; MainDocument *getPart() const; KoPart *getKoPart() const; Project& getProject() const; QMenu *popupMenu(const QString& name); virtual bool loadContext(); virtual void saveContext(QDomElement &context) const; QWidget *canvas() const; KoPageLayout pageLayout() const override; void setPageLayout(const KoPageLayout &pageLayout) override; ScheduleManager *currentScheduleManager() const; long activeScheduleId() const; void setActiveSchedule(long id); /// Returns the default view information like standard name and tooltip for view type @p type ViewInfo defaultViewInfo(const QString &type) const; /// Returns the default category information like standard name and tooltip for category type @p type ViewInfo defaultCategoryInfo(const QString &type) const; ViewBase *createTaskEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createResourceGroupEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createResourceEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createAccountsEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createCalendarEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createScheduleHandler(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ScheduleEditor *createScheduleEditor(QWidget *parent); ViewBase *createScheduleEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createDependencyEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createPertEditor(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createProjectStatusView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createPerformanceStatusView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createTaskStatusView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createTaskView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createTaskWorkPackageView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createGanttView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createMilestoneGanttView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createResourceAppointmentsView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createResourceAppointmentsGanttView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createAccountsView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); - ViewBase *createResourceAssignmentView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createChartView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createReportView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); ViewBase *createReportsGeneratorView(ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1); KoPrintJob * createPrintJob() override; QPrintDialog* createPrintDialog(KoPrintJob*, QWidget*) override; Q_SIGNALS: void currentScheduleManagerChanged(KPlato::ScheduleManager *sm); void taskModulesChanged(const QStringList &modules); void workPackagesAvailable(bool); public Q_SLOTS: void slotUpdate(); void slotCreateNewProject(); void slotEditCurrentResource(); void slotEditResource(KPlato::Resource *resource); void slotEditCut(); void slotEditCopy(); void slotEditPaste(); void slotRefreshView(); void slotViewSelector(bool show); void slotAddTask(); void slotAddSubTask(); void slotAddMilestone(); void slotAddSubMilestone(); void slotProjectEdit(); void slotDefineWBS(); void slotCurrencyConfig(); void slotCreateView(); void slotIntroduction(); void openRelationDialog(KPlato::Node *par, KPlato::Node *child); void slotEditRelation(KPlato::Relation *rel); void slotAddRelation(KPlato::Node *par, KPlato::Node *child, int linkType); void slotModifyRelation(KPlato::Relation *rel, int linkType); void slotModifyCurrentRelation(); void slotDeleteRelation(); void slotRenameNode(KPlato::Node *node, const QString& name); void slotPopupMenuRequested(const QString& menuname, const QPoint &pos); void slotPopupMenu(const QString& menuname, const QPoint &pos, KPlato::ViewListItem *item); void addViewListItem(const KPlato::ViewListItem *item, const KPlato::ViewListItem *parent, int index); void removeViewListItem(const KPlato::ViewListItem *item); void slotOpenReportFile(); void slotSelectionChanged(KPlato::ScheduleManager *sm); void slotUpdateViewInfo(KPlato::ViewListItem *itm); /// Load the workpackages from @p urls into @p project. void loadWorkPackage(KPlato::Project *project, const QList &urls); void slotCreateTemplate(); protected Q_SLOTS: void slotGuiActivated(KPlato::ViewBase *view, bool); void slotViewActivated(KPlato::ViewListItem*, KPlato::ViewListItem*); void slotPlugScheduleActions(); void slotViewSchedule(QAction *act); void slotScheduleAdded(const KPlato::ScheduleManager*); void slotScheduleRemoved(const KPlato::ScheduleManager*); void slotScheduleSwapped(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); void slotScheduleCalculated(KPlato::Project *project, KPlato::ScheduleManager *manager); void slotAddScheduleManager(KPlato::Project *project); void slotDeleteScheduleManager(KPlato::Project *project, KPlato::ScheduleManager *sm); void slotMoveScheduleManager(KPlato::ScheduleManager *sm, KPlato::ScheduleManager *parent, int index); void slotCalculateSchedule(KPlato::Project*, KPlato::ScheduleManager*); void slotBaselineSchedule(KPlato::Project *project, KPlato::ScheduleManager *sm); void slotProjectWorktime(); void slotOpenCurrentNode(); void slotOpenNode(KPlato::Node *node); void slotTaskProgress(); void slotOpenProjectDescription(); void slotTaskDescription(); void slotOpenTaskDescription(bool); void slotDocuments(); void slotDeleteTaskList(QList lst); void slotDeleteTask(KPlato::Node *node); void slotDeleteCurrentTask(); void slotIndentTask(); void slotUnindentTask(); void slotMoveTaskUp(); void slotMoveTaskDown(); void slotConnectNode(); void slotDeleteResource(KPlato::Resource *resource); void slotDeleteResourceGroup(KPlato::ResourceGroup *group); void slotDeleteResourceObjects(QObjectList); void slotCurrentChanged(int); void slotSelectDefaultView(); void slotInsertResourcesFile(const QString&, const QUrl &projects); void slotInsertFile(); void slotLoadSharedProjects(); void slotWorkPackageLoaded(); void slotMailWorkpackage(KPlato::Node *node, KPlato::Resource *resource = 0); void slotPublishWorkpackages(const QList &nodes, KPlato::Resource *resource, bool mailTo); void slotOpenUrlRequest(KPlato::HtmlView *v, const QUrl &url); void createReportView(const QDomDocument &doc); void saveTaskModule(const QUrl &url, KPlato::Project *project); void removeTaskModule(const QUrl &url); protected: void guiActivateEvent(bool activated) override; void updateReadWrite(bool readwrite) override; QList sortedActionList(); QAction *addScheduleAction(ScheduleManager *sch); void setLabel(ScheduleManager *sm = 0); Task *currentTask() const; Node *currentNode() const; Resource *currentResource(); ResourceGroup *currentResourceGroup(); Calendar *currentCalendar(); void updateView(QWidget *widget); ViewBase *currentView() const; ViewBase *createIntroductionView(); private Q_SLOTS: void slotActionDestroyed(QObject *o); void slotViewListItemRemoved(KPlato::ViewListItem *item); void slotViewListItemInserted(KPlato::ViewListItem *item, KPlato::ViewListItem *parent, int index); void slotProjectEditFinished(int result); void slotTaskEditFinished(int result); void slotSummaryTaskEditFinished(int result); void slotEditResourceFinished(int result); void slotProjectWorktimeFinished(int result); void slotDefineWBSFinished(int result); void slotCurrencyConfigFinished(int result); void slotInsertFileFinished(int result); void slotAddSubTaskFinished(int result); void slotAddTaskFinished(int result); void slotAddSubMilestoneFinished(int result); void slotAddMilestoneFinished(int result); void slotTaskProgressFinished(int result); void slotMilestoneProgressFinished(int result); void slotTaskDescriptionFinished(int result); void slotDocumentsFinished(int result); void slotAddRelationFinished(int result); void slotModifyRelationFinished(int result); void slotReportDesignFinished(int result); void slotOpenReportFileFinished(int result); void slotCreateViewFinished(int result); void slotLoadSharedProjectsFinished(int result); void openWorkPackageMergeDialog(); void workPackageMergeDialogFinished(int result); void slotRemoveCommands(); void initiateViews(); void slotViewScheduleManager(KPlato::ScheduleManager *sm); private: void createViews(); ViewBase *createView(ViewListItem *cat, const QString &type, const QString &tag, const QString &name, const QString &tip, int index = -1); QString standardTaskStatusReport() const; private: QSplitter *m_sp; QStackedWidget *m_tab; ViewListWidget *m_viewlist; ViewListItem *m_viewlistItem; // requested popupmenu item //QDockWidget *m_toolbox; int m_viewGrp; int m_defaultFontSize; int m_currentEstimateType; bool m_updateAccountsview; - bool m_updateResourceAssignmentView; bool m_updatePertEditor; QLabel *m_estlabel; QToolButton *m_workPackageButton; ViewAdaptor* m_dbus; QActionGroup *m_scheduleActionGroup; QMap m_scheduleActions; QMultiMap m_calculationcommands; QList m_undocommands; bool m_readWrite; int m_defaultView; QList m_visitedViews; QList m_dockers; // ------ File QAction *actionCreateTemplate; QAction *actionCreateNewProject; // ------ Edit QAction *actionCut; QAction *actionCopy; QAction *actionPaste; // ------ View KToggleAction *actionViewSelector; // ------ Insert // ------ Project QAction *actionEditMainProject; // ------ Tools QAction *actionEditStandardWorktime; QAction *actionDefineWBS; QAction *actionInsertFile; QAction *actionCurrencyConfig; QAction *actionLoadSharedProjects; QAction *actionOpenReportFile; // ------ Settings QAction *actionConfigure; // ------ Help QAction *actionIntroduction; // ------ Popup QAction *actionOpenNode; QAction *actionTaskProgress; QAction *actionTaskDescription; QAction *actionDocuments; QAction *actionDeleteTask; QAction *actionIndentTask; QAction *actionUnindentTask; QAction *actionMoveTaskUp; QAction *actionMoveTaskDown; QAction *actionEditResource; QAction *actionEditRelation; QAction *actionDeleteRelation; //Test QAction *actNoInformation; QMap m_reportActionMap; KoPart *m_partpart; }; } //Kplato namespace #endif diff --git a/src/libs/kernel/CMakeLists.txt b/src/libs/kernel/CMakeLists.txt index 682cccb0..56ff3e57 100644 --- a/src/libs/kernel/CMakeLists.txt +++ b/src/libs/kernel/CMakeLists.txt @@ -1,73 +1,75 @@ include_directories( ${PLANKERNEL_INCLUDES} ${PLANKUNDO2_INCLUDES} ${PLANSTORE_INCLUDES} ) ########### KPlato kernel library ############### set(plankernel_LIB_SRCS kptglobal.cpp kptlocale.cpp kpteffortcostmap.cpp kptdocuments.cpp kptaccount.cpp kptappointment.cpp kptnode.cpp kptproject.cpp kptrelation.cpp ResourceGroup.cpp Resource.cpp kptresourcerequest.cpp kpttask.cpp kptduration.cpp kptdatetime.cpp kptcalendar.cpp kptschedule.cpp kptwbsdefinition.cpp kptcommand.cpp kptpackage.cpp kptdebug.cpp commands/NamedCommand.cpp commands/MacroCommand.cpp commands/SetTaskModulesCommand.cpp commands/AddResourceCmd.cpp commands/RemoveResourceCmd.cpp commands/AddParentGroupCmd.cpp commands/RemoveParentGroupCmd.cpp + commands/AddParentGroupCmd.cpp + commands/RemoveParentGroupCmd.cpp kptschedulerplugin.cpp kptconfigbase.cpp KPlatoXmlLoaderBase.cpp ) add_library(plankernel SHARED ${plankernel_LIB_SRCS}) generate_export_header(plankernel) target_link_libraries(plankernel PUBLIC plankundo2 planstore planwidgetutils # KF5::I18n # KF5::CoreAddons ) if(KF5Holidays_FOUND) target_link_libraries(plankernel PUBLIC KF5::Holidays) endif() set_target_properties(plankernel PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS plankernel ${INSTALL_TARGETS_DEFAULT_ARGS}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # plan_schedulerplugin.desktop if(BUILD_TESTING) add_subdirectory( tests ) endif() diff --git a/src/libs/kernel/KPlatoXmlLoaderBase.cpp b/src/libs/kernel/KPlatoXmlLoaderBase.cpp index 808839b6..1bffecea 100644 --- a/src/libs/kernel/KPlatoXmlLoaderBase.cpp +++ b/src/libs/kernel/KPlatoXmlLoaderBase.cpp @@ -1,1363 +1,1348 @@ /* This file is part of the KDE project Copyright (C) 2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "KPlatoXmlLoaderBase.h" #include "kptlocale.h" #include "kptxmlloaderobject.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptcalendar.h" #include "kptschedule.h" #include "kptrelation.h" #include "kptresource.h" #include "kptaccount.h" #include "kptappointment.h" #include #include #include using namespace KPlato; KPlatoXmlLoaderBase::KPlatoXmlLoaderBase() { } bool KPlatoXmlLoaderBase::load(Project *project, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"project"; // load locale first KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "locale") { Locale *l = project->locale(); l->setCurrencySymbol(e.attribute("currency-symbol", l->currencySymbol())); //NOTE: decimalplaces missing } } QList cals; QString s; bool ok = false; project->setName(element.attribute("name")); project->removeId(project->id()); project->setId(element.attribute("id")); project->registerNodeId(project); project->setLeader(element.attribute("leader")); project->setDescription(element.attribute("description")); QTimeZone tz(element.attribute("timezone").toLatin1()); if (tz.isValid()) { project->setTimeZone(tz); } else warnPlanXml<<"No timezone specified, using default (local)"; status.setProjectTimeZone(project->timeZone()); // Allow for both numeric and text s = element.attribute("scheduling", "0"); project->setConstraint((Node::ConstraintType) (s.toInt(&ok))); if (! ok) project->setConstraint(s); if (project->constraint() != Node::MustStartOn && project->constraint() != Node::MustFinishOn) { errorPlanXml << "Illegal constraint: " << project->constraintToString(); project->setConstraint(Node::MustStartOn); } s = element.attribute("start-time"); if (! s.isEmpty()) { project->setConstraintStartTime(DateTime::fromString(s, project->timeZone())); } s = element.attribute("end-time"); if (! s.isEmpty()) { project->setConstraintEndTime(DateTime::fromString(s, project->timeZone())); } // Load the project children // Do calendars first, they only reference other calendars //debugPlanXml<<"Calendars--->"; n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "calendar") { // Load the calendar. // Referenced by resources Calendar * child = new Calendar(); child->setProject(project); if (load(child, e, status)) { cals.append(child); // temporary, reorder later } else { // TODO: Complain about this errorPlanXml << "Failed to load calendar"; delete child; } } else if (e.tagName() == "standard-worktime") { // Load standard worktime StandardWorktime * child = new StandardWorktime(); if (load(child, e, status)) { project->setStandardWorktime(child); } else { errorPlanXml << "Failed to load standard worktime"; delete child; } } } // calendars references calendars in arbitrary saved order bool added = false; do { added = false; QList lst; while (!cals.isEmpty()) { Calendar *c = cals.takeFirst(); if (c->parentId().isEmpty()) { project->addCalendar(c, status.baseCalendar()); // handle pre 0.6 version added = true; //debugPlanXml<<"added to project:"<name(); } else { Calendar *par = project->calendar(c->parentId()); if (par) { project->addCalendar(c, par); added = true; //debugPlanXml<<"added:"<name()<<" to parent:"<name(); } else { lst.append(c); // treat later //debugPlanXml<<"treat later:"<name(); } } } cals = lst; } while (added); if (! cals.isEmpty()) { errorPlanXml<<"All calendars not saved!"; } //debugPlanXml<<"Calendars<---"; // Resource groups and resources, can reference calendars n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource-group") { // Load the resources // References calendars ResourceGroup * child = new ResourceGroup(); if (load(child, e, status)) { project->addResourceGroup(child); } else { // TODO: Complain about this delete child; } } } // The main stuff n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "project") { //debugPlanXml<<"Sub project--->"; /* // Load the subproject Project * child = new Project(this); if (child->load(e)) { if (!addTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == "task") { //debugPlanXml<<"Task--->"; // Load the task (and resourcerequests). // Depends on resources already loaded Task *child = new Task(project); if (load(child, e, status)) { if (! project->addTask(child, project)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } // These go last n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { debugPlanXml<"; // Load accounts // References tasks if (! load(project->accounts(), e, status)) { errorPlanXml << "Failed to load accounts"; } } else if (e.tagName() == "relation") { //debugPlanXml<<"Relation--->"; // Load the relation // References tasks Relation * child = new Relation(); if (! load(child, e, status)) { // TODO: Complain about this errorPlanXml << "Failed to load relation"; delete child; } //debugPlanXml<<"Relation<---"; } else if (e.tagName() == "schedules") { debugPlanXml<<"Project schedules & task appointments--->"; // References tasks and resources KoXmlNode sn = e.firstChild(); for (; ! sn.isNull(); sn = sn.nextSibling()) { if (! sn.isElement()) { continue; } KoXmlElement el = sn.toElement(); //debugPlanXml<findScheduleManagerByName(el.attribute("name")); if (sm == 0) { sm = new ScheduleManager(*project, el.attribute("name")); add = true; } } } else if (el.tagName() == "plan") { sm = new ScheduleManager(*project); add = true; } if (sm) { if (load(sm, el, status)) { if (add) project->addScheduleManager(sm); } else { errorPlanXml << "Failed to load schedule manager"; delete sm; } } else { debugPlanXml<<"No schedule manager ?!"; } } debugPlanXml<<"Project schedules & task appointments<---"; } else if (e.tagName() == "resource-teams") { //debugPlanXml<<"Resource teams--->"; // References other resources KoXmlNode tn = e.firstChild(); for (; ! tn.isNull(); tn = tn.nextSibling()) { if (! tn.isElement()) { continue; } KoXmlElement el = tn.toElement(); if (el.tagName() == "team") { Resource *r = project->findResource(el.attribute("team-id")); Resource *tm = project->findResource(el.attribute("member-id")); if (r == 0 || tm == 0) { errorPlanXml<<"resource-teams: cannot find resources"; continue; } if (r == tm) { errorPlanXml<<"resource-teams: a team cannot be a member of itself"; continue; } r->addTeamMemberId(tm->id()); } else { errorPlanXml<<"resource-teams: unhandled tag"<wbsDefinition(), e, status); } else if (e.tagName() == "locale") { // handled earlier } else if (e.tagName() == "resource-group") { // handled earlier } else if (e.tagName() == "calendar") { // handled earlier } else if (e.tagName() == "standard-worktime") { // handled earlier } else if (e.tagName() == "project") { // handled earlier } else if (e.tagName() == "task") { // handled earlier } else { warnPlanXml<<"Unhandled tag:"<schedules()) { project->setParentSchedule(s); } debugPlanXml<<"Project loaded:"<name()<allNodes(); return true; } bool KPlatoXmlLoaderBase::load(Task *task, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"task"; QString s; bool ok = false; task->setId(element.attribute("id")); task->setName(element.attribute("name")); task->setLeader(element.attribute("leader")); task->setDescription(element.attribute("description")); //debugPlanXml<setConstraint((Node::ConstraintType)constraint.toInt(&ok)); if (! ok) { task->setConstraint(constraint); } s = element.attribute("constraint-starttime"); if (! s.isEmpty()) { task->setConstraintStartTime(DateTime::fromString(s, status.projectTimeZone())); } s = element.attribute("constraint-endtime"); if (! s.isEmpty()) { task->setConstraintEndTime(DateTime::fromString(s, status.projectTimeZone())); } task->setStartupCost(element.attribute("startup-cost", "0.0").toDouble()); task->setShutdownCost(element.attribute("shutdown-cost", "0.0").toDouble()); // Load the task children KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "project") { // Load the subproject /* Project *child = new Project(this, status); if (child->load(e)) { if (!project.addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == "task") { // Load the task Task *child = new Task(task); if (load(child, e, status)) { if (! status.project().addSubTask(child, task)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } else if (e.tagName() == "resource") { // tasks don't have resources } else if (e.tagName() == "estimate" || (/*status.version() < "0.6" &&*/ e.tagName() == "effort")) { // Load the estimate load(task->estimate(), e, status); } else if (e.tagName() == "resourcegroup-request") { - // Load the resource request - // Handle multiple requests to same group gracefully (Not really allowed) - ResourceGroupRequest *r = task->requests().findGroupRequestById(e.attribute("group-id")); - if (r) { - warnPlanXml<<"Multiple requests to same group, loading into existing group"; - if (! load(r, e, status)) { - errorPlanXml<<"Failed to load resource request"; - } - } else { - r = new ResourceGroupRequest(); - if (load(r, e, status)) { - task->addRequest(r); - } else { - errorPlanXml<<"Failed to load resource request"; - delete r; - } - } + warnPlan<<"KPlatoXmlLoader: requests not handled"; } else if (e.tagName() == "workpackage") { load(task->workPackage(), e, status); } else if (e.tagName() == "progress") { load(task->completion(), e, status); } else if (e.tagName() == "schedules") { KoXmlNode n = e.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == "schedule") { NodeSchedule *sch = new NodeSchedule(); if (loadNodeSchedule(sch, el, status)) { sch->setNode(task); task->addSchedule(sch); } else { errorPlanXml<<"Failed to load schedule"; delete sch; } } } } else if (e.tagName() == "documents") { load(task->documents(), e, status); } else if (e.tagName() == "workpackage-log") { KoXmlNode n = e.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == "workpackage") { WorkPackage *wp = new WorkPackage(task); if (loadWpLog(wp, el, status)) { task->addWorkPackage(wp); } else { errorPlanXml<<"Failed to load logged workpackage"; delete wp; } } } } } //debugPlanXml<setId(element.attribute("id")); calendar->setParentId(element.attribute("parent")); calendar->setName(element.attribute("name","")); QTimeZone tz(element.attribute("timezone").toLatin1()); if (tz.isValid()) { calendar->setTimeZone(tz); } else warnPlanXml<<"No timezone specified, use default (local)"; bool dc = (bool)element.attribute("default","0").toInt(); if (dc) { status.project().setDefaultCalendar(calendar); } KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "weekday") { if (! load(calendar->weekdays(), e, status)) { return false; } } if (e.tagName() == "day") { CalendarDay *day = new CalendarDay(); if (load(day, e, status)) { if (! day->date().isValid()) { delete day; errorPlanXml<name()<<": Failed to load calendarDay - Invalid date"; } else { CalendarDay *d = calendar->findDay(day->date()); if (d) { // already exists, keep the new delete calendar->takeDay(d); warnPlanXml<name()<<" Load calendarDay - Date already exists"; } calendar->addDay(day); } } else { delete day; errorPlanXml<<"Failed to load calendarDay"; return true; // don't throw away the whole calendar } } } return true; } bool KPlatoXmlLoaderBase::load(CalendarDay *day, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"day"; bool ok=false; day->setState(QString(element.attribute("state", "-1")).toInt(&ok)); if (day->state() < 0) { return false; } //debugPlanXml<<" state="<setDate(QDate::fromString(s, Qt::ISODate)); if (! day->date().isValid()) { day->setDate(QDate::fromString(s)); } } day->clearIntervals(); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "interval") { //debugPlanXml<<"Interval start="<addInterval(new TimeInterval(start, length)); } } return true; } bool KPlatoXmlLoaderBase::load(CalendarWeekdays *weekdays, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"weekdays"; bool ok; int dayNo = QString(element.attribute("day","-1")).toInt(&ok); if (dayNo < 0 || dayNo > 6) { errorPlanXml<<"Illegal weekday: "<weekday(dayNo + 1); if (day == 0) { errorPlanXml<<"No weekday: "<setState(CalendarDay::None); } return true; } bool KPlatoXmlLoaderBase::load(StandardWorktime *swt, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"swt"; swt->setYear(Duration::fromString(element.attribute("year"), Duration::Format_Hour)); swt->setMonth(Duration::fromString(element.attribute("month"), Duration::Format_Hour)); swt->setWeek(Duration::fromString(element.attribute("week"), Duration::Format_Hour)); swt->setDay(Duration::fromString(element.attribute("day"), Duration::Format_Hour)); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "calendar") { // pre 0.6 version stored base calendar in standard worktime if (status.version() >= "0.6") { warnPlanXml<<"Old format, calendar in standard worktime"; warnPlanXml<<"Tries to load anyway"; } // try to load anyway Calendar *calendar = new Calendar; if (load(calendar, e, status)) { status.project().addCalendar(calendar); calendar->setDefault(true); status.project().setDefaultCalendar(calendar); // hmmm status.setBaseCalendar(calendar); } else { delete calendar; errorPlanXml<<"Failed to load calendar"; } } } return true; } bool KPlatoXmlLoaderBase::load(Relation *relation, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"relation"; relation->setParent(status.project().findNode(element.attribute("parent-id"))); if (relation->parent() == 0) { warnPlanXml<<"Parent node == 0, cannot find id:"<setChild(status.project().findNode(element.attribute("child-id"))); if (relation->child() == 0) { warnPlanXml<<"Child node == 0, cannot find id:"<child() == relation->parent()) { warnPlanXml<<"Parent node == child node"; return false; } if (! relation->parent()->legalToLink(relation->child())) { warnPlanXml<<"Realation is not legal:"<parent()->name()<<"->"<child()->name(); return false; } relation->setType(element.attribute("type")); relation->setLag(Duration::fromString(element.attribute("lag"))); if (! relation->parent()->addDependChildNode(relation)) { errorPlanXml<<"Failed to add relation: Child="<child()->name()<<" parent="<parent()->name(); return false; } if (! relation->child()->addDependParentNode(relation)) { relation->parent()->takeDependChildNode(relation); errorPlanXml<<"Failed to add relation: Child="<child()->name()<<" parent="<parent()->name(); return false; } //debugPlanXml<<"Added relation: Child="<child()->name()<<" parent="<parent()->name(); return true; } bool KPlatoXmlLoaderBase::load(ResourceGroup *rg, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"resource-group"; rg->setId(element.attribute("id")); rg->setName(element.attribute("name")); rg->setType(element.attribute("type")); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource") { // Load the resource Resource *child = new Resource(); if (load(child, e, status)) { child->addParentGroup(rg); status.project().addResource(child); } else { // TODO: Complain about this delete child; } } } return true; } bool KPlatoXmlLoaderBase::load(Resource *resource, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"resource"; const Locale *locale = status.project().locale(); QString s; resource->setId(element.attribute("id")); resource->setName(element.attribute("name")); resource->setInitials(element.attribute("initials")); resource->setEmail(element.attribute("email")); resource->setType(element.attribute("type")); resource->setCalendar(status.project().findCalendar(element.attribute("calendar-id"))); resource->setUnits(element.attribute("units", "100").toInt()); s = element.attribute("available-from"); if (! s.isEmpty()) { resource->setAvailableFrom(DateTime::fromString(s, status.projectTimeZone())); } s = element.attribute("available-until"); if (! s.isEmpty()) { resource->setAvailableUntil(DateTime::fromString(s, status.projectTimeZone())); } resource->setNormalRate(locale->readMoney(element.attribute("normal-rate"))); resource->setOvertimeRate(locale->readMoney(element.attribute("overtime-rate"))); resource->setAccount(status.project().accounts().findAccount(element.attribute("account"))); KoXmlElement e; KoXmlElement parent = element.namedItem("required-resources").toElement(); forEachElement(e, parent) { if (e.nodeName() == "resource") { QString id = e.attribute("id"); if (id.isEmpty()) { warnPlanXml<<"Missing resource id"; continue; } resource->addRequiredId(id); } } parent = element.namedItem("external-appointments").toElement(); forEachElement(e, parent) { if (e.nodeName() == "project") { QString id = e.attribute("id"); if (id.isEmpty()) { errorPlanXml<<"Missing project id"; continue; } resource->clearExternalAppointments(id); // in case... AppointmentIntervalList lst; load(lst, e, status); Appointment *a = new Appointment(); a->setIntervals(lst); a->setAuxcilliaryInfo(e.attribute("name", "Unknown")); resource->addExternalAppointment(id, a); } } return true; } bool KPlatoXmlLoaderBase::load(Accounts &accounts, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"accounts"; KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "account") { Account *child = new Account(); if (load(child, e, status)) { accounts.insert(child); } else { // TODO: Complain about this warnPlanXml<<"Loading failed"; delete child; } } } if (element.hasAttribute("default-account")) { accounts.setDefaultAccount(accounts.findAccount(element.attribute("default-account"))); if (accounts.defaultAccount() == 0) { warnPlanXml<<"Could not find default account."; } } return true; } bool KPlatoXmlLoaderBase::load(Account* account, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"account"; account->setName(element.attribute("name")); account->setDescription(element.attribute("description")); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "costplace") { Account::CostPlace *child = new Account::CostPlace(account); if (load(child, e, status)) { account->append(child); } else { delete child; } } else if (e.tagName() == "account") { Account *child = new Account(); if (load(child, e, status)) { account->insert(child); } else { // TODO: Complain about this warnPlanXml<<"Loading failed"; delete child; } } } return true; } bool KPlatoXmlLoaderBase::load(Account::CostPlace* cp, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"cost-place"; cp->setObjectId(element.attribute("object-id")); if (cp->objectId().isEmpty()) { // check old format cp->setObjectId(element.attribute("node-id")); if (cp->objectId().isEmpty()) { errorPlanXml<<"No object id"; return false; } } cp->setNode(status.project().findNode(cp->objectId())); if (cp->node() == 0) { cp->setResource(status.project().findResource(cp->objectId())); if (cp->resource() == 0) { errorPlanXml<<"Cannot find object with id: "<objectId(); return false; } } bool on = (bool)(element.attribute("running-cost").toInt()); if (on) cp->setRunning(on); on = (bool)(element.attribute("startup-cost").toInt()); if (on) cp->setStartup(on); on = (bool)(element.attribute("shutdown-cost").toInt()); if (on) cp->setShutdown(on); return true; } bool KPlatoXmlLoaderBase::load(ScheduleManager *manager, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"schedule-manager"; MainSchedule *sch = 0; if (status.version() <= "0.5") { manager->setUsePert(false); MainSchedule *sch = loadMainSchedule(manager, element, status); if (sch && sch->type() == Schedule::Expected) { sch->setManager(manager); manager->setExpected(sch); } else { delete sch; } return true; } manager->setName(element.attribute("name")); manager->setManagerId(element.attribute("id")); manager->setUsePert(element.attribute("distribution").toInt() == 1); manager->setAllowOverbooking((bool)(element.attribute("overbooking").toInt())); manager->setCheckExternalAppointments((bool)(element.attribute("check-external-appointments").toInt())); manager->setSchedulingDirection((bool)(element.attribute("scheduling-direction").toInt())); manager->setBaselined((bool)(element.attribute("baselined").toInt())); manager->setSchedulerPluginId(element.attribute("scheduler-plugin-id")); manager->setRecalculate((bool)(element.attribute("recalculate").toInt())); manager->setRecalculateFrom(DateTime::fromString(element.attribute("recalculate-from"), status.projectTimeZone())); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); //debugPlanXml<type() == Schedule::Expected) { sch->setManager(manager); manager->setExpected(sch); break; } else { delete sch; } } else if (e.tagName() == "plan") { ScheduleManager *sm = new ScheduleManager(status.project()); if (load(sm, e, status)) { status.project().addScheduleManager(sm, manager); } else { errorPlanXml<<"Failed to load schedule manager"; delete sm; } } } return true; } bool KPlatoXmlLoaderBase::load(Schedule *schedule, const KoXmlElement& element, XMLLoaderObject& /*status*/) { debugPlanXml<<"schedule"; schedule->setName(element.attribute("name")); schedule->setType(element.attribute("type")); schedule->setId(element.attribute("id").toLong()); return true; } MainSchedule* KPlatoXmlLoaderBase::loadMainSchedule(ScheduleManager* /*manager*/, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"main-schedule"; MainSchedule *sch = new MainSchedule(); if (loadMainSchedule(sch, element, status)) { status.project().addSchedule(sch); sch->setNode(&(status.project())); status.project().setParentSchedule(sch); // If it's here, it's scheduled! sch->setScheduled(true); } else { errorPlanXml << "Failed to load schedule"; delete sch; sch = 0; } return sch; } bool KPlatoXmlLoaderBase::loadMainSchedule(MainSchedule *ms, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml; QString s; load(ms, element, status); s = element.attribute("start"); if (!s.isEmpty()) { ms->startTime = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute("end"); if (!s.isEmpty()) { ms->endTime = DateTime::fromString(s, status.projectTimeZone()); } ms->duration = Duration::fromString(element.attribute("duration")); ms->constraintError = element.attribute("scheduling-conflict", "0").toInt(); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == "appointment") { // Load the appointments. // Resources and tasks must already be loaded Appointment * child = new Appointment(); if (! load(child, el, status, *ms)) { // TODO: Complain about this errorPlanXml << "Failed to load appointment" << endl; delete child; } } else if (el.tagName() == "criticalpath-list") { // Tasks must already be loaded for (KoXmlNode n1 = el.firstChild(); ! n1.isNull(); n1 = n1.nextSibling()) { if (! n1.isElement()) { continue; } KoXmlElement e1 = n1.toElement(); if (e1.tagName() != "criticalpath") { continue; } QList lst; for (KoXmlNode n2 = e1.firstChild(); ! n2.isNull(); n2 = n2.nextSibling()) { if (! n2.isElement()) { continue; } KoXmlElement e2 = n2.toElement(); if (e2.tagName() != "node") { continue; } QString s = e2.attribute("id"); Node *node = status.project().findNode(s); if (node) { lst.append(node); } else { errorPlanXml<<"Failed to find node id="<m_pathlists.append(lst); } ms->criticalPathListCached = true; } } return true; } bool KPlatoXmlLoaderBase::loadNodeSchedule(NodeSchedule* schedule, const KoXmlElement &element, XMLLoaderObject& status) { debugPlanXml<<"node-schedule"; QString s; load(schedule, element, status); s = element.attribute("earlystart"); if (s.isEmpty()) { // try version < 0.6 s = element.attribute("earlieststart"); } if (! s.isEmpty()) { schedule->earlyStart = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute("latefinish"); if (s.isEmpty()) { // try version < 0.6 s = element.attribute("latestfinish"); } if (! s.isEmpty()) { schedule->lateFinish = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute("latestart"); if (! s.isEmpty()) { schedule->lateStart = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute("earlyfinish"); if (! s.isEmpty()) { schedule->earlyFinish = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute("start"); if (! s.isEmpty()) schedule->startTime = DateTime::fromString(s, status.projectTimeZone()); s = element.attribute("end"); if (!s.isEmpty()) schedule->endTime = DateTime::fromString(s, status.projectTimeZone()); s = element.attribute("start-work"); if (!s.isEmpty()) schedule->workStartTime = DateTime::fromString(s, status.projectTimeZone()); s = element.attribute("end-work"); if (!s.isEmpty()) schedule->workEndTime = DateTime::fromString(s, status.projectTimeZone()); schedule->duration = Duration::fromString(element.attribute("duration")); schedule->inCriticalPath = element.attribute("in-critical-path", "0").toInt(); schedule->resourceError = element.attribute("resource-error", "0").toInt(); schedule->resourceOverbooked = element.attribute("resource-overbooked", "0").toInt(); schedule->resourceNotAvailable = element.attribute("resource-not-available", "0").toInt(); schedule->constraintError = element.attribute("scheduling-conflict", "0").toInt(); schedule->notScheduled = element.attribute("not-scheduled", "1").toInt(); schedule->positiveFloat = Duration::fromString(element.attribute("positive-float")); schedule->negativeFloat = Duration::fromString(element.attribute("negative-float")); schedule->freeFloat = Duration::fromString(element.attribute("free-float")); return true; } bool KPlatoXmlLoaderBase::load(WBSDefinition &def, const KoXmlElement &element, XMLLoaderObject &/*status*/) { debugPlanXml<<"wbs-def"; def.setProjectCode(element.attribute("project-code")); def.setProjectSeparator(element.attribute("project-separator")); def.setLevelsDefEnabled((bool)element.attribute("levels-enabled", "0").toInt()); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "default") { def.defaultDef().code = e.attribute("code", "Number"); def.defaultDef().separator = e.attribute("separator", "."); } else if (e.tagName() == "levels") { KoXmlNode n = e.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); WBSDefinition::CodeDef d; d.code = el.attribute("code"); d.separator = el.attribute("separator"); int lvl = el.attribute("level", "-1").toInt(); if (lvl >= 0) { def.setLevelsDef(lvl, d); } else errorPlanXml<<"Invalid levels definition"; } } } return true; } bool KPlatoXmlLoaderBase::load(Documents &documents, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"documents"; KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "document") { Document *doc = new Document(); if (! load(doc, e, status)) { warnPlanXml<<"Failed to load document"; status.addMsg(XMLLoaderObject::Errors, "Failed to load document"); delete doc; } else { documents.addDocument(doc); status.addMsg(i18n("Document loaded, URL=%1", doc->url().url())); } } } return true; } bool KPlatoXmlLoaderBase::load(Document *document, const KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<<"document"; Q_UNUSED(status); document->setUrl(QUrl(element.attribute("url"))); document->setType((Document::Type)(element.attribute("type").toInt())); document->setStatus(element.attribute("status")); document->setSendAs((Document::SendAs)(element.attribute("sendas").toInt())); return true; } bool KPlatoXmlLoaderBase::load(Estimate* estimate, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"estimate"; estimate->setType(element.attribute("type")); estimate->setRisktype(element.attribute("risk")); if (status.version() <= "0.6") { estimate->setUnit((Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h)).toInt())); QList s = status.project().standardWorktime()->scales(); estimate->setExpectedEstimate(Estimate::scale(Duration::fromString(element.attribute("expected")), estimate->unit(), s)); estimate->setOptimisticEstimate(Estimate::scale(Duration::fromString(element.attribute("optimistic")), estimate->unit(), s)); estimate->setPessimisticEstimate(Estimate::scale(Duration::fromString(element.attribute("pessimistic")), estimate->unit(), s)); } else { if (status.version() <= "0.6.2") { // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit estimate->setUnit((Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3)).toInt() + 3)); } else { estimate->setUnit(Duration::unitFromString(element.attribute("unit"))); } estimate->setExpectedEstimate(element.attribute("expected", "0.0").toDouble()); estimate->setOptimisticEstimate(element.attribute("optimistic", "0.0").toDouble()); estimate->setPessimisticEstimate(element.attribute("pessimistic", "0.0").toDouble()); estimate->setCalendar(status.project().findCalendar(element.attribute("calendar-id"))); } return true; } +#if 0 bool KPlatoXmlLoaderBase::load(ResourceGroupRequest* gr, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"resourcegroup-request"; gr->setGroup(status.project().findResourceGroup(element.attribute("group-id"))); if (gr->group() == 0) { errorPlanXml<<"The referenced resource group does not exist: group id="<group()->registerRequest(gr); Q_ASSERT(gr->parent()); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource-request") { ResourceRequest *r = new ResourceRequest(); if (load(r, e, status)) { gr->parent()->addResourceRequest(r, gr); } else { errorPlanXml<<"Failed to load resource request"; delete r; } } } // meaning of m_units changed int x = element.attribute("units").toInt() -gr->count(); gr->setUnits(x > 0 ? x : 0); return true; } - +#endif bool KPlatoXmlLoaderBase::load(ResourceRequest *rr, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"resource-request"; rr->setResource(status.project().resource(element.attribute("resource-id"))); if (rr->resource() == 0) { warnPlanXml<<"The referenced resource does not exist: resource id="<setUnits(element.attribute("units").toInt()); KoXmlElement parent = element.namedItem("required-resources").toElement(); KoXmlElement e; QList required; forEachElement(e, parent) { if (e.nodeName() == "resource") { QString id = e.attribute("id"); if (id.isEmpty()) { errorPlanXml<<"Missing project id"; continue; } Resource *r = status.project().resource(id); if (r == 0) { warnPlanXml<<"The referenced resource does not exist: resource id="<resource()) { required << r; } } } } rr->setRequiredResources(required); return true; } bool KPlatoXmlLoaderBase::load(WorkPackage &wp, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"workpackage"; Q_UNUSED(status); wp.setOwnerName(element.attribute("owner")); wp.setOwnerId(element.attribute("owner-id")); return true; } bool KPlatoXmlLoaderBase::loadWpLog(WorkPackage *wp, KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"wplog"; wp->setOwnerName(element.attribute("owner")); wp->setOwnerId(element.attribute("owner-id")); wp->setTransmitionStatus(wp->transmitionStatusFromString(element.attribute("status"))); wp->setTransmitionTime(DateTime(QDateTime::fromString(element.attribute("time"), Qt::ISODate))); return load(wp->completion(), element, status); } bool KPlatoXmlLoaderBase::load(Completion &completion, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"completion"; QString s; completion.setStarted((bool)element.attribute("started", "0").toInt()); completion.setFinished((bool)element.attribute("finished", "0").toInt()); s = element.attribute("startTime"); if (!s.isEmpty()) { completion.setStartTime(DateTime::fromString(s, status.projectTimeZone())); } s = element.attribute("finishTime"); if (!s.isEmpty()) { completion.setFinishTime(DateTime::fromString(s, status.projectTimeZone())); } completion.setEntrymode(element.attribute("entrymode")); if (status.version() < "0.6") { if (completion.isStarted()) { Completion::Entry *entry = new Completion::Entry(element.attribute("percent-finished", "0").toInt(), Duration::fromString(element.attribute("remaining-effort")), Duration::fromString(element.attribute("performed-effort"))); entry->note = element.attribute("note"); QDate date = completion.startTime().date(); if (completion.isFinished()) { date = completion.finishTime().date(); } // almost the best we can do ;) completion.addEntry(date, entry); } } else { KoXmlElement e; forEachElement(e, element) { if (e.tagName() == "completion-entry") { QDate date; s = e.attribute("date"); if (!s.isEmpty()) { date = QDate::fromString(s, Qt::ISODate); } if (!date.isValid()) { warnPlanXml<<"Invalid date: "<setEffort(date, a); } } } return true; } bool KPlatoXmlLoaderBase::load(Appointment *appointment, const KoXmlElement& element, XMLLoaderObject& status, Schedule &sch) { debugPlanXml<<"appointment"; Node *node = status.project().findNode(element.attribute("task-id")); if (node == 0) { errorPlanXml<<"The referenced task does not exists: "<addAppointment(appointment, sch)) { errorPlanXml<<"Failed to add appointment to resource: "<name(); return false; } if (! node->addAppointment(appointment, sch)) { errorPlanXml<<"Failed to add appointment to node: "<name(); appointment->resource()->takeAppointment(appointment); return false; } //debugPlanXml<<"res="<name()<name(); return false; } appointment->setIntervals(lst); return true; } bool KPlatoXmlLoaderBase::load(AppointmentIntervalList& lst, const KoXmlElement& element, XMLLoaderObject& status) { debugPlanXml<<"appointment-interval-list"; KoXmlElement e; forEachElement(e, element) { if (e.tagName() == "interval") { AppointmentInterval a; if (load(a, e, status)) { lst.add(a); } else { errorPlanXml<<"Could not load interval"; } } } return true; } bool KPlatoXmlLoaderBase::load(AppointmentInterval& interval, const KoXmlElement& element, XMLLoaderObject& status) { bool ok; QString s = element.attribute("start"); if (!s.isEmpty()) { interval.setStartTime(DateTime::fromString(s, status.projectTimeZone())); } s = element.attribute("end"); if (!s.isEmpty()) { interval.setEndTime(DateTime::fromString(s, status.projectTimeZone())); } double l = element.attribute("load", "100").toDouble(&ok); if (ok) { interval.setLoad(l); } debugPlanXml<<"interval:"< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPLATOXMLLOADERBASE_H #define KPLATOXMLLOADERBASE_H #include "kptaccount.h" #include "kpttask.h" #include #define KPLATO_MAX_FILE_SYNTAX_VERSION "0.6.5" #define KPLATOWORK_MAX_FILE_SYNTAX_VERSION "0.6.5" class KoXmlElement; namespace KPlato { class XMLLoaderObject; class Project; class Task; class Calendar; class CalendarDay; class CalendarWeekdays; class StandardWorktime; class Relation; class ResourceGroup; class Resource; class Accounts; class Account; class ScheduleManager; class Schedule; class NodeSchedule; class WBSDefinition; class WorkPackage; class Documents; class Estimate; - class ResourceGroupRequest; +// class ResourceGroupRequest; class ResourceRequest; class Completion; class Appointment; class AppointmentIntervalList; class AppointmentInterval; class PLANKERNEL_EXPORT KPlatoXmlLoaderBase : public QObject { Q_OBJECT public: KPlatoXmlLoaderBase(); ~KPlatoXmlLoaderBase() override {} bool load(Project *project, const KoXmlElement &element, XMLLoaderObject &status); bool load(Task *task, const KoXmlElement &element, XMLLoaderObject &status); bool load(Calendar *Calendar, const KoXmlElement &element, XMLLoaderObject &status); bool load(CalendarDay *day, const KoXmlElement &element, XMLLoaderObject &status); bool load(CalendarWeekdays *weekdays, const KoXmlElement& element, XMLLoaderObject& status); bool load(StandardWorktime *swt, const KoXmlElement &element, XMLLoaderObject &status); bool load(Relation *relation, const KoXmlElement &element, XMLLoaderObject &status); bool load(ResourceGroup *rg, const KoXmlElement &element, XMLLoaderObject &status); bool load(Resource *resource, const KoXmlElement &element, XMLLoaderObject &status); bool load(Accounts &accounts, const KoXmlElement &element, XMLLoaderObject &status); bool load(Account *account, const KoXmlElement &element, XMLLoaderObject &status); bool load(Account::CostPlace *cp, const KoXmlElement &element, XMLLoaderObject &status); bool load(ScheduleManager *manager, const KoXmlElement &element, XMLLoaderObject &status); bool load(Schedule *schedule, const KoXmlElement &element, XMLLoaderObject &status); MainSchedule *loadMainSchedule(ScheduleManager* manager, const KoXmlElement &element, XMLLoaderObject& status); bool loadMainSchedule(MainSchedule* ms, const KoXmlElement &element, XMLLoaderObject& status); bool loadNodeSchedule(NodeSchedule* sch, const KoXmlElement &element, XMLLoaderObject& status); bool load(WBSDefinition &def, const KoXmlElement &element, XMLLoaderObject &status); bool load(Documents &documents, const KoXmlElement &element, XMLLoaderObject &status); bool load(Document *document, const KoXmlElement &element, XMLLoaderObject &status); bool load(Estimate *estimate, const KoXmlElement &element, XMLLoaderObject &status); - bool load(ResourceGroupRequest *gr, const KoXmlElement &element, XMLLoaderObject &status); +// bool load(ResourceGroupRequest *gr, const KoXmlElement &element, XMLLoaderObject &status); bool load(ResourceRequest *rr, const KoXmlElement &element, XMLLoaderObject &status); bool load(WorkPackage& wp, const KoXmlElement& element, XMLLoaderObject& status); bool loadWpLog(WorkPackage* wp, KoXmlElement &element, XMLLoaderObject &status); bool load(Completion& completion, const KoXmlElement& element, XMLLoaderObject& status); bool load(Completion::UsedEffort *ue, const KoXmlElement& element, XMLLoaderObject& status); bool load(Appointment *appointment, const KoXmlElement& element, XMLLoaderObject& status, Schedule &sch); bool load(AppointmentIntervalList &lst, const KoXmlElement& element, XMLLoaderObject& status); bool load(AppointmentInterval &interval, const KoXmlElement& element, XMLLoaderObject& status); - }; } // namespace KPlato #endif // KPLATOXMLLOADERBASE_H diff --git a/src/libs/kernel/ResourceGroup.cpp b/src/libs/kernel/ResourceGroup.cpp index e0c2a887..8715e205 100644 --- a/src/libs/kernel/ResourceGroup.cpp +++ b/src/libs/kernel/ResourceGroup.cpp @@ -1,345 +1,342 @@ /* This file is part of the KDE project * Copyright (C) 2001 Thomas zander * Copyright (C) 2004-2007, 2012 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "ResourceGroup.h" #include "Resource.h" #include "kptresourcerequest.h" #include "kptlocale.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include using namespace KPlato; ResourceGroup::ResourceGroup() : QObject(0), m_blockChanged(false), m_shared(false) { m_project = 0; m_type = Type_Work; //debugPlan<<"("<unregister(this); - } for (Resource *r : m_resources) { r->removeParentGroup(this); } //debugPlan<<"("<m_project; //Don't copy m_id = group->m_id; m_type = group->m_type; m_name = group->m_name; } void ResourceGroup::blockChanged(bool on) { m_blockChanged = on; } void ResourceGroup::changed() { if (!m_blockChanged) { emit dataChanged(this); if (m_project) { emit m_project->resourceGroupChanged(this); } } } void ResourceGroup::setId(const QString& id) { //debugPlan<setProject(project); } } bool ResourceGroup::isScheduled() const { foreach (Resource *r, m_resources) { if (r->isScheduled()) { return true; } } return false; } bool ResourceGroup::isBaselined(long id) const { Q_UNUSED(id); foreach (const Resource *r, m_resources) { if (r->isBaselined()) { return true; } } return false; } void ResourceGroup::addResource(Resource* resource, Risk *risk) { addResource(m_resources.count(), resource, risk); } void ResourceGroup::addResource(int index, Resource* resource, Risk*) { if (!m_resources.contains(resource)) { int i = index == -1 ? m_resources.count() : index; emit resourceToBeAdded(this, i); m_resources.insert(i, resource); emit resourceAdded(resource); } resource->addParentGroup(this); } Resource *ResourceGroup::takeResource(Resource *resource) { Resource *r = 0; int i = m_resources.indexOf(resource); if (i != -1) { emit resourceToBeRemoved(this, i, resource); r = m_resources.takeAt(i); emit resourceRemoved(); r->removeParentGroup(this); } return r; } int ResourceGroup::indexOf(const Resource *resource) const { return m_resources.indexOf(const_cast(resource)); //??? } Risk* ResourceGroup::getRisk(int) { return 0L; } void ResourceGroup::addRequiredResource(ResourceGroup*) { } ResourceGroup* ResourceGroup::getRequiredResource(int) { return 0L; } void ResourceGroup::deleteRequiredResource(int) { } bool ResourceGroup::load(KoXmlElement &element, XMLLoaderObject &status) { //debugPlan; setId(element.attribute("id")); m_name = element.attribute("name"); setType(element.attribute("type")); m_shared = element.attribute("shared", "0").toInt(); if (status.version() < "0.7.0") { KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource") { // Load the resource Resource *child = new Resource(); if (child->load(e, status)) { child->addParentGroup(this); } else { // TODO: Complain about this delete child; } } } } return true; } void ResourceGroup::save(QDomElement &element) const { //debugPlan; QDomElement me = element.ownerDocument().createElement("resource-group"); element.appendChild(me); me.setAttribute("id", m_id); me.setAttribute("name", m_name); me.setAttribute("type", typeToString()); me.setAttribute("shared", m_shared); } void ResourceGroup::saveWorkPackageXML(QDomElement &element, const QList &lst) const { QDomElement me = element.ownerDocument().createElement("resource-group"); element.appendChild(me); me.setAttribute("id", m_id); me.setAttribute("name", m_name); foreach (Resource *r, m_resources) { if (lst.contains(r)) { r->save(me); } } } void ResourceGroup::initiateCalculation(Schedule &sch) { clearNodes(); } int ResourceGroup::units() const { int u = 0; foreach (const Resource *r, m_resources) { u += r->units(); } return u; } ResourceGroup *ResourceGroup::findId(const QString &id) const { return m_project ? m_project->findResourceGroup(id) : 0; } bool ResourceGroup::removeId(const QString &id) { return m_project ? m_project->removeResourceGroupId(id): false; } void ResourceGroup::insertId(const QString &id) { //debugPlan; if (m_project) m_project->insertResourceGroupId(id, this); } Appointment ResourceGroup::appointmentIntervals() const { Appointment a; foreach (Resource *r, m_resources) { a += r->appointmentIntervals(); } return a; } DateTime ResourceGroup::startTime(long id) const { DateTime dt; foreach (Resource *r, m_resources) { DateTime t = r->startTime(id); if (! dt.isValid() || t < dt) { dt = t; } } return dt; } DateTime ResourceGroup::endTime(long id) const { DateTime dt; foreach (Resource *r, m_resources) { DateTime t = r->endTime(id); if (! dt.isValid() || t > dt) { dt = t; } } return dt; } bool ResourceGroup::isShared() const { return m_shared; } void ResourceGroup::setShared(bool on) { m_shared = on; } diff --git a/src/libs/kernel/ResourceGroup.h b/src/libs/kernel/ResourceGroup.h index 81218dff..44e43287 100644 --- a/src/libs/kernel/ResourceGroup.h +++ b/src/libs/kernel/ResourceGroup.h @@ -1,224 +1,211 @@ /* This file is part of the KDE project * Copyright (C) 2001 Thomas Zander zander@kde.org * Copyright (C) 2004-2007 Dag Andersen * Copyright (C) 2011 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTRESOURCEGROUP_H #define KPTRESOURCEGROUP_H #include "plankernel_export.h" #include "kptglobal.h" #include "kptduration.h" #include "kptdatetime.h" #include "kptappointment.h" #include "kptcalendar.h" #include #include #include #include /// The main namespace. namespace KPlato { class Account; class Risk; class Effort; class Appointment; class Task; class Node; class Project; class Resource; class ResourceRequest; -class ResourceGroupRequest; class ResourceRequestCollection; class Schedule; class ResourceSchedule; class Schedule; class XMLLoaderObject; class DateTimeInterval; /** * This class represents a group of similar resources to be assigned to a task * e.g. The list of employees, computer resources, etc */ /* IDEA; lets create a resourceGroup that has the intelligence to import PIM schedules * from the kroupware project and use the schedules to use the factory pattern to build * Resources (probably a derived class) which returns values on getFirstAvailableTime * and friends based on the schedules we got from the PIM projects. * (Thomas Zander mrt-2003 by suggestion of Shaheed) */ class PLANKERNEL_EXPORT ResourceGroup : public QObject { Q_OBJECT public: /// Default constructor explicit ResourceGroup(); explicit ResourceGroup(const ResourceGroup *group); ~ResourceGroup() override; enum Type { Type_Work, Type_Material }; QString id() const { return m_id; } void setId(const QString& id); Project *project() { return m_project; } void setName(const QString& n); const QString &name() const { return m_name;} void setType(Type type); void setType(const QString &type); Type type() const { return m_type; } QString typeToString(bool trans = false) const; static QStringList typeToStringList(bool trans = false); bool isScheduled() const; /// Return true if any resource in this group is baselined bool isBaselined(long id = BASELINESCHEDULE) const; /** Manage the resources in this list *

At some point we will have to look at not mixing types of resources * (e.g. you can't add a person to a list of computers * *

Risks must always be associated with a resource, so there is no option * to manipulate risks (@ref Risk) separately */ void addResource(Resource *resource, Risk *risk = nullptr); void addResource(int index, Resource*, Risk*); Resource *takeResource(Resource *resource); QList resources() const { return m_resources; } int indexOf(const Resource *resource) const; Resource *resourceAt(int pos) const { return m_resources.value(pos); } int numResources() const { return m_resources.count(); } Risk* getRisk(int); /** Manage the dependent resources. This is a list of the resource * groups that must have available resources for this resource to * perform the work *

see also @ref getRequiredResource, @ref getRequiredResource */ void addRequiredResource(ResourceGroup*); /** Manage the dependent resources. This is a list of the resource * groups that must have available resources for this resource to * perform the work *

see also @ref addRequiredResource, @ref getRequiredResource */ ResourceGroup* getRequiredResource(int); /** Manage the dependent resources. This is a list of the resource * groups that must have available resources for this resource to * perform the work *

see also @ref getRequiredResource, @ref addRequiredResource */ void deleteRequiredResource(int); bool load(KoXmlElement &element, XMLLoaderObject &status); void save(QDomElement &element) const; /// Save workpackage document. Include only resources listed in @p lst void saveWorkPackageXML(QDomElement &element, const QList &lst) const; void initiateCalculation(Schedule &sch); void addNode(Node *node) { m_nodes.append(node); } void clearNodes() { m_nodes.clear(); } Calendar *defaultCalendar() { return m_defaultCalendar; } int units() const; - void registerRequest(ResourceGroupRequest *request) - { m_requests.append(request); } - void unregisterRequest(ResourceGroupRequest *request) - { - int i = m_requests.indexOf(request); - if (i != -1) - m_requests.removeAt(i); - } - const QList &requests() const - { return m_requests; } - ResourceGroup *findId() const { return findId(m_id); } ResourceGroup *findId(const QString &id) const; bool removeId() { return removeId(m_id); } bool removeId(const QString &id); void insertId(const QString &id); Appointment appointmentIntervals() const; // m_project is set when the resourcegroup is added to the project, // and reset when the resourcegroup is removed from the project void setProject(Project *project); void copy(const ResourceGroup *group); DateTime startTime(long id) const; DateTime endTime(long id) const; void blockChanged(bool on = true); /// A resource can be local to this project, or /// defined externally and shared with other projects bool isShared() const; /// Set resource to be shared if on = true, or local if on = false void setShared(bool on); #ifndef NDEBUG void printDebug(const QString& ident); #endif Q_SIGNALS: void dataChanged(KPlato::ResourceGroup *group); void resourceToBeAdded(KPlato::ResourceGroup *group, int row); void resourceAdded(KPlato::Resource *resource); void resourceToBeRemoved(KPlato::ResourceGroup *group, int row, KPlato::Resource *resource); void resourceRemoved(); protected: virtual void changed(); private: Project *m_project; QString m_id; // unique id QString m_name; QList m_resources; QList m_risks; QList m_requires; QList m_nodes; //The nodes that want resources from us Calendar *m_defaultCalendar; Type m_type; - QList m_requests; bool m_blockChanged; bool m_shared; }; } // namespace KPlato #endif diff --git a/src/libs/kernel/XmlSaveContext.h b/src/libs/kernel/XmlSaveContext.h index 8cc2672e..cdc652db 100644 --- a/src/libs/kernel/XmlSaveContext.h +++ b/src/libs/kernel/XmlSaveContext.h @@ -1,261 +1,238 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; * version 2 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef XMLSAVECONTEXT_H #define XMLSAVECONTEXT_H #include "plankernel_export.h" #include "kptproject.h" #include "kptnode.h" #include "kpttask.h" #include #include #include namespace KPlato { class PLANKERNEL_EXPORT XmlSaveContext { public: XmlSaveContext(Project *project = 0) : options(SaveAll) , m_project(project) {} static QDomDocument createDocument() { QDomDocument document("plan"); document.appendChild(document.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"") ); QDomElement doc = document.createElement("plan"); doc.setAttribute("editor", "Plan"); doc.setAttribute("mime", "application/x-vnd.kde.plan"); doc.setAttribute("version", PLAN_FILE_SYNTAX_VERSION); document.appendChild(doc); return document; } enum SaveOptions { SaveAll = 0x1, SaveSelectedNodes = 0x2, SaveRelations = 0x4, SaveResources = 0x8, SaveRequests = 0x10, SaveProject = 0x20 }; bool saveAll(const Project *project) const { Q_UNUSED(project); return options & SaveAll; } bool saveAll(const Task *task) const { Q_UNUSED(task); return options & SaveAll; } bool saveNode(const Node *node) const { if (options & SaveAll) { return true; } return nodes.contains(node); } bool saveChildren(const Node *node) const { Q_UNUSED(node); if (options & SaveAll) { return true; } return false; // TODO } bool saveRelation(const Relation *relation) const { if (options & SaveAll) { return true; } return (options & SaveSelectedNodes) && nodes.contains(relation->parent()) && nodes.contains(relation->child()); } void save() const { Q_ASSERT(m_project); if (!m_project) { return; } document = createDocument(); QDomElement doc = document.documentElement(); if (options & (SaveAll | SaveProject)) { debugPlanXml<<"project"; m_project->save(doc, *this); if (options & SaveAll) { return; } } else { doc.appendChild(doc.ownerDocument().createElement("project")); } QDomElement projectElement = doc.elementsByTagName("project").item(0).toElement(); Q_ASSERT(!projectElement.isNull()); if (options & SaveSelectedNodes) { debugPlanXml<<"tasks:"< it(nodes); while (it.hasNext()) { it.next()->save(projectElement, *this); } } if (options & SaveRelations) { QListIterator it(nodes); while (it.hasNext()) { it.next()->saveRelations(projectElement, *this); } } if (options & SaveResources) { debugPlanXml<<"resource-groups:"<resourceGroups().count(); if (!m_project->resourceGroups().isEmpty()) { QDomElement ge = projectElement.ownerDocument().createElement("resource-groups"); projectElement.appendChild(ge); QListIterator git(m_project->resourceGroups()); while (git.hasNext()) { git.next()->save(ge); } } debugPlanXml<<"resources:"<resourceList().count(); if (!m_project->resourceList().isEmpty()) { QDomElement re = projectElement.ownerDocument().createElement("resources"); projectElement.appendChild(re); QListIterator rit(m_project->resourceList()); while (rit.hasNext()) { rit.next()->save(re); } } debugPlanXml<<"resource-group-relations"; if (!m_project->resourceList().isEmpty() && !m_project->resourceGroups().isEmpty()) { QDomElement e = projectElement.ownerDocument().createElement("resource-group-relations"); projectElement.appendChild(e); for (ResourceGroup *g : m_project->resourceGroups()) { for (Resource *r : g->resources()) { QDomElement re = e.ownerDocument().createElement("resource-group-relation"); e.appendChild(re); re.setAttribute("group-id", g->id()); re.setAttribute("resource-id", r->id()); } } } debugPlanXml<<"required-resources"; if (m_project->resourceList().count() > 1) { QList > requiredList; for (Resource *resource : m_project->resourceList()) { for (const QString &required : resource->requiredIds()) { requiredList << std::pair(resource->id(), required); } } if (!requiredList.isEmpty()) { QDomElement e = projectElement.ownerDocument().createElement("required-resources"); projectElement.appendChild(e); for (const std::pair pair : requiredList) { QDomElement re = e.ownerDocument().createElement("required-resource"); e.appendChild(re); re.setAttribute("resource-id", pair.first); re.setAttribute("required-id", pair.second); } } } // save resource teams debugPlanXml<<"resource-teams"; QDomElement el = projectElement.ownerDocument().createElement("resource-teams"); projectElement.appendChild(el); foreach (Resource *r, m_project->resourceList()) { if (r->type() != Resource::Type_Team) { continue; } foreach (const QString &id, r->teamMemberIds()) { QDomElement e = el.ownerDocument().createElement("team"); el.appendChild(e); e.setAttribute("team-id", r->id()); e.setAttribute("member-id", id); } } } if (options & SaveRequests) { // save resource requests - QHash groups; QHash resources; for (Task *task : m_project->allTasks()) { const ResourceRequestCollection &requests = task->requests(); - for (ResourceGroupRequest *gr : requests.requests()) { - groups.insert(task, gr); - } for (ResourceRequest *rr : requests.resourceRequests(false)) { resources.insert(task, rr); } } - debugPlanXml<<"resourcegroup-requests:"<::const_iterator it; - for (it = groups.constBegin(); it != groups.constEnd(); ++it) { - if (!it.value()->group()) { - warnPlanXml<<"resourcegroup-request with no group"; - continue; - } - QDomElement ge = el.ownerDocument().createElement("resourcegroup-request"); - el.appendChild(ge); - ge.setAttribute("request-id", it.value()->id()); - ge.setAttribute("task-id", it.key()->id()); - ge.setAttribute("group-id", it.value()->group()->id()); - ge.setAttribute("units", QString::number(it.value()->units())); - } - } - QHash > required; // QHash> debugPlanXml<<"resource-requests:"< > required; // QHash> if (!resources.isEmpty()) { QDomElement el = projectElement.ownerDocument().createElement("resource-requests"); projectElement.appendChild(el); QHash::const_iterator it; for (it = resources.constBegin(); it != resources.constEnd(); ++it) { if (!it.value()->resource()) { continue; } QDomElement re = el.ownerDocument().createElement("resource-request"); el.appendChild(re); re.setAttribute("request-id", it.value()->id()); re.setAttribute("task-id", it.key()->id()); - if (it.value()->parent()) { - re.setAttribute("group-id", it.value()->parent()->group()->id()); - } re.setAttribute("resource-id", it.value()->resource()->id()); re.setAttribute("units", QString::number(it.value()->units())); // collect required resources for (Resource *r : it.value()->requiredResources()) { required.insert(it.key(), std::pair(it.value(), r)); } } + //FIXME: save required? + // TODO altarnatives } } } mutable QDomDocument document; int options; QList nodes; private: Project *m_project; }; } //namespace KPlato #endif diff --git a/src/libs/kernel/commands/AddAlternativeResourceCmd.cpp b/src/libs/kernel/commands/AddAlternativeResourceCmd.cpp new file mode 100644 index 00000000..d2f1b66e --- /dev/null +++ b/src/libs/kernel/commands/AddAlternativeResourceCmd.cpp @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +// clazy:excludeall=qstring-arg +#include "AddAlternativeResourceCmd.h" + +#include "Resource.h" +#include "ResourceGroup.h" + +using namespace KPlato; + + +AddAlternativeResourceCmd::AddAlternativeResourceCmd(ResourceRequest *request, Resource *m_resource, const KUndo2MagicString& name); + : NamedCommand(name) + , m_request(request) + , m_resource(resource) +{ +} + +AddAlternativeResourceCmd::~AddAlternativeResourceCmd() +{ +} + +void AddAlternativeResourceCmd::execute() +{ + m_request->addAlternativeResource(m_resource); +} + +void AddAlternativeResourceCmd::unexecute() +{ + m_request->removeAlternativeResource(m_resource); +} diff --git a/src/libs/kernel/commands/AddAlternativeResourceCmd.h b/src/libs/kernel/commands/AddAlternativeResourceCmd.h new file mode 100644 index 00000000..c3540d6d --- /dev/null +++ b/src/libs/kernel/commands/AddAlternativeResourceCmd.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KPTADDALTERNATIVERESOURCECMD_H +#define KPTADDALTERNATIVERESOURCECMD_H + +#include "plankernel_export.h" + +#include "NamedCommand.h" + + +/// The main namespace +namespace KPlato +{ + +class Resource; +class ResourceGroup; + +class PLANKERNEL_EXPORT AddAlternativeResourceCmd : public NamedCommand +{ +public: + AddAlternativeResourceCmd(ResourceRequest *request, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString()); + ~AddAlternativeResourceCmd() override; + void execute() override; + void unexecute() override; + +protected: + ResourceRequest *m_request; + Resource *m_resource; +}; + +} + +#endif diff --git a/src/libs/kernel/commands/RemoveAlternativeResourceCmd.cpp b/src/libs/kernel/commands/RemoveAlternativeResourceCmd.cpp new file mode 100644 index 00000000..9eca26fe --- /dev/null +++ b/src/libs/kernel/commands/RemoveAlternativeResourceCmd.cpp @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +// clazy:excludeall=qstring-arg +#include "RemoveAlternativeResourceCmd.h" + +#include "Resource.h" +#include "ResourceGroup.h" + +using namespace KPlato; + + +RemoveAlternativeResourceCmd::RemoveAlternativeResourceCmd(ResourceRequest *request, Resource *m_resource, const KUndo2MagicString& name); + : NamedCommand(name) + , m_request(request) + , m_resource(resource) +{ +} + +RemoveAlternativeResourceCmd::~RemoveAlternativeResourceCmd() +{ +} + +void RemoveAlternativeResourceCmd::execute() +{ + m_request->addAlternativeResource(m_resource); +} + +void RemoveAlternativeResourceCmd::unexecute() +{ + m_request->removeAlternativeResource(m_resource); +} diff --git a/src/libs/kernel/commands/RemoveAlternativeResourceCmd.h b/src/libs/kernel/commands/RemoveAlternativeResourceCmd.h new file mode 100644 index 00000000..f1c47690 --- /dev/null +++ b/src/libs/kernel/commands/RemoveAlternativeResourceCmd.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KPTADDALTERNATIVERESOURCECMD_H +#define KPTADDALTERNATIVERESOURCECMD_H + +#include "plankernel_export.h" + +#include "NamedCommand.h" + + +/// The main namespace +namespace KPlato +{ + +class Resource; +class ResourceGroup; + +class PLANKERNEL_EXPORT RemoveAlternativeResourceCmd : public NamedCommand +{ +public: + RemoveAlternativeResourceCmd(ResourceRequest *request, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString()); + ~RemoveAlternativeResourceCmd() override; + void execute() override; + void unexecute() override; + +protected: + ResourceRequest *m_request; + Resource *m_resource; +}; + +} + +#endif diff --git a/src/libs/kernel/commands/RemoveResourceCmd.cpp b/src/libs/kernel/commands/RemoveResourceCmd.cpp index 193b005c..8b042587 100644 --- a/src/libs/kernel/commands/RemoveResourceCmd.cpp +++ b/src/libs/kernel/commands/RemoveResourceCmd.cpp @@ -1,79 +1,79 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "RemoveResourceCmd.h" #include "Resource.h" #include "kptproject.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptresourcerequest.h" #include "kptcommand.h" #include "kptdebug.h" using namespace KPlato; RemoveResourceCmd::RemoveResourceCmd(Resource *resource, const KUndo2MagicString& name) : AddResourceCmd(resource->project(), resource, name) { //debugPlan<requests(); if (m_project) { foreach (Schedule * s, m_project->schedules()) { Schedule *rs = resource->findSchedule(s->id()); if (rs && ! rs->isDeleted()) { debugPlan<name(); addSchScheduled(s); } } } if (resource->account()) { m_postCmd.addCommand(new ResourceModifyAccountCmd(*resource, resource->account(), 0)); } - foreach (ResourceRequest * r, m_requests) { - m_preCmd.addCommand(new RemoveResourceRequestCmd(r->parent(), r)); + for (ResourceRequest *r : m_requests) { + m_preCmd.addCommand(new RemoveResourceRequestCmd(r)); //debugPlan<<"Remove request for"<resource()->name(); } } RemoveResourceCmd::~RemoveResourceCmd() { while (!m_appointments.isEmpty()) delete m_appointments.takeFirst(); } void RemoveResourceCmd::execute() { m_preCmd.redo(); AddResourceCmd::unexecute(); m_postCmd.redo(); setSchScheduled(false); } void RemoveResourceCmd::unexecute() { m_postCmd.undo(); AddResourceCmd::execute(); m_preCmd.undo(); setSchScheduled(); } diff --git a/src/libs/kernel/kptcommand.cpp b/src/libs/kernel/kptcommand.cpp index 9466de98..fc1d7970 100644 --- a/src/libs/kernel/kptcommand.cpp +++ b/src/libs/kernel/kptcommand.cpp @@ -1,3785 +1,3663 @@ /* This file is part of the KDE project * Copyright (C) 2004 - 2007 Dag Andersen * Copyright (C) 2011 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptcommand.h" #include "AddResourceCmd.h" #include "AddParentGroupCmd.h" +#include "RemoveParentGroupCmd.h" #include "RemoveResourceCmd.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptcalendar.h" #include "kptrelation.h" #include "kptresource.h" #include "kptdocuments.h" #include "kptlocale.h" #include "kptdebug.h" #include const QLoggingCategory &PLANCMDINSPROJECT_LOG() { static const QLoggingCategory category("calligra.plan.command.insertProject"); return category; } #define debugPlanInsertProject qCDebug(PLANCMDINSPROJECT_LOG) #define warnPlanInsertProject qCWarning(PLANCMDINSPROJECT_LOG) #define errorPlanInsertProject qCCritical(PLANCMDINSPROJECT_LOG) namespace KPlato { CalendarAddCmd::CalendarAddCmd(Project *project, Calendar *cal, int pos, Calendar *parent, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_cal(cal), m_pos(pos), m_parent(parent), m_mine(true) { //debugPlan<name(); Q_ASSERT(project != 0); } CalendarAddCmd::~CalendarAddCmd() { if (m_mine) delete m_cal; } void CalendarAddCmd::execute() { if (m_project) { m_project->addCalendar(m_cal, m_parent, m_pos); m_mine = false; } //debugPlan<name()<<" added to:"<name(); } void CalendarAddCmd::unexecute() { if (m_project) { m_project->takeCalendar(m_cal); m_mine = true; } //debugPlan<name(); } CalendarRemoveCmd::CalendarRemoveCmd(Project *project, Calendar *cal, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_parent(cal->parentCal()), m_cal(cal), m_index(-1), m_mine(false), m_cmd(new MacroCommand(KUndo2MagicString())) { Q_ASSERT(project != 0); m_index = m_parent ? m_parent->indexOf(cal) : project->indexOf(cal); foreach (Resource *r, project->resourceList()) { if (r->calendar(true) == cal) { m_cmd->addCommand(new ModifyResourceCalendarCmd(r, 0)); } } if (project->defaultCalendar() == cal) { m_cmd->addCommand(new ProjectModifyDefaultCalendarCmd(project, 0)); } foreach (Calendar *c, cal->calendars()) { m_cmd->addCommand(new CalendarRemoveCmd(project, c)); } } CalendarRemoveCmd::~CalendarRemoveCmd() { delete m_cmd; if (m_mine) delete m_cal; } void CalendarRemoveCmd::execute() { m_cmd->execute(); m_project->takeCalendar(m_cal); m_mine = true; } void CalendarRemoveCmd::unexecute() { m_project->addCalendar(m_cal, m_parent, m_index); m_cmd->unexecute(); m_mine = false; } CalendarMoveCmd::CalendarMoveCmd(Project *project, Calendar *cal, int position, Calendar *parent, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_cal(cal), m_newpos(position), m_newparent(parent), m_oldparent(cal->parentCal()) { //debugPlan<name(); Q_ASSERT(project != 0); m_oldpos = m_oldparent ? m_oldparent->indexOf(cal) : project->indexOf(cal); } void CalendarMoveCmd::execute() { m_project->takeCalendar(m_cal); m_project->addCalendar(m_cal, m_newparent, m_newpos); } void CalendarMoveCmd::unexecute() { m_project->takeCalendar(m_cal); m_project->addCalendar(m_cal, m_oldparent, m_oldpos); } CalendarModifyNameCmd::CalendarModifyNameCmd(Calendar *cal, const QString& newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal) { m_oldvalue = cal->name(); m_newvalue = newvalue; //debugPlan<name(); } void CalendarModifyNameCmd::execute() { m_cal->setName(m_newvalue); //debugPlan<name(); } void CalendarModifyNameCmd::unexecute() { m_cal->setName(m_oldvalue); //debugPlan<name(); } CalendarModifyParentCmd::CalendarModifyParentCmd(Project *project, Calendar *cal, Calendar *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_cal(cal), m_cmd(new MacroCommand(KUndo2MagicString())), m_oldindex(-1), m_newindex(-1) { m_oldvalue = cal->parentCal(); m_newvalue = newvalue; m_oldindex = m_oldvalue ? m_oldvalue->indexOf(cal) : m_project->indexOf(cal); if (newvalue) { m_cmd->addCommand(new CalendarModifyTimeZoneCmd(cal, newvalue->timeZone())); } //debugPlan<name(); } CalendarModifyParentCmd::~CalendarModifyParentCmd() { delete m_cmd; } void CalendarModifyParentCmd::execute() { m_project->takeCalendar(m_cal); m_project->addCalendar(m_cal, m_newvalue, m_newindex); m_cmd->execute(); } void CalendarModifyParentCmd::unexecute() { m_cmd->unexecute(); m_project->takeCalendar(m_cal); m_project->addCalendar(m_cal, m_oldvalue, m_oldindex); } CalendarModifyTimeZoneCmd::CalendarModifyTimeZoneCmd(Calendar *cal, const QTimeZone &value, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_newvalue(value), m_cmd(new MacroCommand(KUndo2MagicString())) { m_oldvalue = cal->timeZone(); foreach (Calendar *c, cal->calendars()) { m_cmd->addCommand(new CalendarModifyTimeZoneCmd(c, value)); } //debugPlan<name(); } CalendarModifyTimeZoneCmd::~CalendarModifyTimeZoneCmd() { delete m_cmd; } void CalendarModifyTimeZoneCmd::execute() { m_cmd->execute(); m_cal->setTimeZone(m_newvalue); } void CalendarModifyTimeZoneCmd::unexecute() { m_cal->setTimeZone(m_oldvalue); m_cmd->unexecute(); } #ifdef HAVE_KHOLIDAYS CalendarModifyHolidayRegionCmd::CalendarModifyHolidayRegionCmd(Calendar *cal, const QString &value, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_newvalue(value) { m_oldvalue = cal->holidayRegionCode(); } CalendarModifyHolidayRegionCmd::~CalendarModifyHolidayRegionCmd() { } void CalendarModifyHolidayRegionCmd::execute() { m_cal->setHolidayRegion(m_newvalue); } void CalendarModifyHolidayRegionCmd::unexecute() { m_cal->setHolidayRegion(m_oldvalue); } #endif CalendarAddDayCmd::CalendarAddDayCmd(Calendar *cal, CalendarDay *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_mine(true) { m_newvalue = newvalue; //debugPlan<name(); } CalendarAddDayCmd::~CalendarAddDayCmd() { //debugPlan; if (m_mine) delete m_newvalue; } void CalendarAddDayCmd::execute() { //debugPlan<name(); m_cal->addDay(m_newvalue); m_mine = false; } void CalendarAddDayCmd::unexecute() { //debugPlan<name(); m_cal->takeDay(m_newvalue); m_mine = true; } CalendarRemoveDayCmd::CalendarRemoveDayCmd(Calendar *cal,CalendarDay *day, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_value(day), m_mine(false) { //debugPlan<name(); // TODO check if any resources uses this calendar init(); } CalendarRemoveDayCmd::CalendarRemoveDayCmd(Calendar *cal, const QDate &day, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_mine(false) { m_value = cal->findDay(day); //debugPlan<name(); // TODO check if any resources uses this calendar init(); } void CalendarRemoveDayCmd::init() { } void CalendarRemoveDayCmd::execute() { //debugPlan<name(); m_cal->takeDay(m_value); m_mine = true; } void CalendarRemoveDayCmd::unexecute() { //debugPlan<name(); m_cal->addDay(m_value); m_mine = false; } CalendarModifyDayCmd::CalendarModifyDayCmd(Calendar *cal, CalendarDay *value, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_mine(true) { m_newvalue = value; m_oldvalue = cal->findDay(value->date()); //debugPlan<name()<<" old:("<takeDay(m_oldvalue); } m_cal->addDay(m_newvalue); m_mine = false; } void CalendarModifyDayCmd::unexecute() { //debugPlan; m_cal->takeDay(m_newvalue); if (m_oldvalue) { m_cal->addDay(m_oldvalue); } m_mine = true; } CalendarModifyStateCmd::CalendarModifyStateCmd(Calendar *calendar, CalendarDay *day, CalendarDay::State value, const KUndo2MagicString& name) : NamedCommand(name), m_calendar(calendar), m_day(day), m_cmd(new MacroCommand(KUndo2MagicString())) { m_newvalue = value; m_oldvalue = (CalendarDay::State)day->state(); if (value != CalendarDay::Working) { foreach (TimeInterval *ti, day->timeIntervals()) { m_cmd->addCommand(new CalendarRemoveTimeIntervalCmd(calendar, day, ti)); } } } CalendarModifyStateCmd::~CalendarModifyStateCmd() { delete m_cmd; } void CalendarModifyStateCmd::execute() { //debugPlan; m_cmd->execute(); m_calendar->setState(m_day, m_newvalue); } void CalendarModifyStateCmd::unexecute() { //debugPlan; m_calendar->setState(m_day, m_oldvalue); m_cmd->unexecute(); } CalendarModifyTimeIntervalCmd::CalendarModifyTimeIntervalCmd(Calendar *calendar, TimeInterval &newvalue, TimeInterval *value, const KUndo2MagicString& name) : NamedCommand(name), m_calendar(calendar) { m_value = value; // keep pointer m_oldvalue = *value; // save value m_newvalue = newvalue; } void CalendarModifyTimeIntervalCmd::execute() { //debugPlan; m_calendar->setWorkInterval(m_value, m_newvalue); } void CalendarModifyTimeIntervalCmd::unexecute() { //debugPlan; m_calendar->setWorkInterval(m_value, m_oldvalue); } CalendarAddTimeIntervalCmd::CalendarAddTimeIntervalCmd(Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name) : NamedCommand(name), m_calendar(calendar), m_day(day), m_value(value), m_mine(true) { } CalendarAddTimeIntervalCmd::~CalendarAddTimeIntervalCmd() { if (m_mine) delete m_value; } void CalendarAddTimeIntervalCmd::execute() { //debugPlan; m_calendar->addWorkInterval(m_day, m_value); m_mine = false; } void CalendarAddTimeIntervalCmd::unexecute() { //debugPlan; m_calendar->takeWorkInterval(m_day, m_value); m_mine = true; } CalendarRemoveTimeIntervalCmd::CalendarRemoveTimeIntervalCmd(Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name) : CalendarAddTimeIntervalCmd(calendar, day, value, name) { m_mine = false ; } void CalendarRemoveTimeIntervalCmd::execute() { CalendarAddTimeIntervalCmd::unexecute(); } void CalendarRemoveTimeIntervalCmd::unexecute() { CalendarAddTimeIntervalCmd::execute(); } CalendarModifyWeekdayCmd::CalendarModifyWeekdayCmd(Calendar *cal, int weekday, CalendarDay *value, const KUndo2MagicString& name) : NamedCommand(name), m_weekday(weekday), m_cal(cal), m_value(value), m_orig(*(cal->weekday(weekday))) { //debugPlan << cal->name() <<" (" << value <<")"; } CalendarModifyWeekdayCmd::~CalendarModifyWeekdayCmd() { //debugPlan << m_weekday <<":" << m_value; delete m_value; } void CalendarModifyWeekdayCmd::execute() { m_cal->setWeekday(m_weekday, *m_value); } void CalendarModifyWeekdayCmd::unexecute() { m_cal->setWeekday(m_weekday, m_orig); } CalendarModifyDateCmd::CalendarModifyDateCmd(Calendar *cal, CalendarDay *day, const QDate &value, const KUndo2MagicString& name) : NamedCommand(name), m_cal(cal), m_day(day), m_newvalue(value), m_oldvalue(day->date()) { //debugPlan << cal->name() <<" (" << value <<")"; } void CalendarModifyDateCmd::execute() { m_cal->setDate(m_day, m_newvalue); } void CalendarModifyDateCmd::unexecute() { m_cal->setDate(m_day, m_oldvalue); } ProjectModifyDefaultCalendarCmd::ProjectModifyDefaultCalendarCmd(Project *project, Calendar *cal, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_newvalue(cal), m_oldvalue(project->defaultCalendar()) { //debugPlan << cal->name() <<" (" << value <<")"; } void ProjectModifyDefaultCalendarCmd::execute() { m_project->setDefaultCalendar(m_newvalue); } void ProjectModifyDefaultCalendarCmd::unexecute() { m_project->setDefaultCalendar(m_oldvalue); } NodeDeleteCmd::NodeDeleteCmd(Node *node, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_index(-1), m_relCmd(0) { m_parent = node->parentNode(); m_mine = false; m_project = static_cast(node->projectNode()); if (m_project) { foreach (Schedule * s, m_project->schedules()) { if (s && s->isScheduled()) { // Only invalidate schedules this node is part of Schedule *ns = node->findSchedule(s->id()); if (ns && ! ns->isDeleted()) { addSchScheduled(s); } } } } m_cmd = new MacroCommand(KUndo2MagicString()); QList lst = node->childNodeIterator(); for (int i = lst.count(); i > 0; --i) { m_cmd->addCommand(new NodeDeleteCmd(lst[ i - 1 ])); } if (node->runningAccount()) { m_cmd->addCommand(new NodeModifyRunningAccountCmd(*node, node->runningAccount(), 0)); } if (node->startupAccount()) { m_cmd->addCommand(new NodeModifyRunningAccountCmd(*node, node->startupAccount(), 0)); } if (node->shutdownAccount()) { m_cmd->addCommand(new NodeModifyRunningAccountCmd(*node, node->shutdownAccount(), 0)); } } NodeDeleteCmd::~NodeDeleteCmd() { delete m_relCmd; // before node if (m_mine) { delete m_node; } delete m_cmd; while (!m_appointments.isEmpty()) delete m_appointments.takeFirst(); } void NodeDeleteCmd::execute() { if (m_parent && m_project) { m_index = m_parent->findChildNode(m_node); //debugPlan<name()<<""<dependChildNodes()) { m_relCmd->addCommand(new DeleteRelationCmd(*m_project, r)); } foreach (Relation * r, m_node->dependParentNodes()) { m_relCmd->addCommand(new DeleteRelationCmd(*m_project, r)); } } m_relCmd->execute(); if (m_cmd) { m_cmd->execute(); } m_project->takeTask(m_node); m_mine = true; setSchScheduled(false); } } void NodeDeleteCmd::unexecute() { if (m_parent && m_project) { //debugPlan<name()<<""<addSubTask(m_node, m_index, m_parent); if (m_cmd) { m_cmd->unexecute(); } m_relCmd->unexecute(); m_mine = false; setSchScheduled(); } } TaskAddCmd::TaskAddCmd(Project *project, Node *node, Node *after, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_node(node), m_after(after), m_added(false) { // set some reasonable defaults for normally calculated values if (after && after->parentNode() && after->parentNode() != project) { node->setStartTime(after->parentNode() ->startTime()); node->setEndTime(node->startTime() + node->duration()); } else { if (project->constraint() == Node::MustFinishOn) { node->setEndTime(project->endTime()); node->setStartTime(node->endTime() - node->duration()); } else { node->setStartTime(project->startTime()); node->setEndTime(node->startTime() + node->duration()); } } node->setEarlyStart(node->startTime()); node->setLateFinish(node->endTime()); node->setWorkStartTime(node->startTime()); node->setWorkEndTime(node->endTime()); } TaskAddCmd::~TaskAddCmd() { if (!m_added) delete m_node; } void TaskAddCmd::execute() { //debugPlan<name(); m_project->addTask(m_node, m_after); m_added = true; } void TaskAddCmd::unexecute() { m_project->takeTask(m_node); m_added = false; } SubtaskAddCmd::SubtaskAddCmd(Project *project, Node *node, Node *parent, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_node(node), m_parent(parent), m_added(false), m_cmd(0) { // set some reasonable defaults for normally calculated values node->setStartTime(parent->startTime()); node->setEndTime(node->startTime() + node->duration()); node->setEarlyStart(node->startTime()); node->setLateFinish(node->endTime()); node->setWorkStartTime(node->startTime()); node->setWorkEndTime(node->endTime()); // Summarytasks can't have resources, so remove resource requests from the new parent - foreach (ResourceGroupRequest *r, parent->requests().requests()) { + foreach (ResourceRequest *r, parent->requests().resourceRequests()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); - m_cmd->addCommand(new RemoveResourceGroupRequestCmd(r)); + m_cmd->addCommand(new RemoveResourceRequestCmd(r)); } // Also remove accounts if (parent->runningAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyRunningAccountCmd(*parent, parent->runningAccount(), 0)); } if (parent->startupAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyStartupAccountCmd(*parent, parent->startupAccount(), 0)); } if (parent->shutdownAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyShutdownAccountCmd(*parent, parent->shutdownAccount(), 0)); } } SubtaskAddCmd::~SubtaskAddCmd() { delete m_cmd; if (!m_added) delete m_node; } void SubtaskAddCmd::execute() { m_project->addSubTask(m_node, m_parent); if (m_cmd) { m_cmd->execute(); } m_added = true; } void SubtaskAddCmd::unexecute() { m_project->takeTask(m_node); if (m_cmd) { m_cmd->unexecute(); } m_added = false; } NodeModifyNameCmd::NodeModifyNameCmd(Node &node, const QString& nodename, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newName(nodename), oldName(node.name()) { } void NodeModifyNameCmd::execute() { m_node.setName(newName); } void NodeModifyNameCmd::unexecute() { m_node.setName(oldName); } NodeModifyPriorityCmd::NodeModifyPriorityCmd(Node &node, int oldValue, int newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_node(node) , m_oldValue(oldValue) , m_newValue(newValue) { } void NodeModifyPriorityCmd::execute() { m_node.setPriority(m_newValue); } void NodeModifyPriorityCmd::unexecute() { m_node.setPriority(m_oldValue); } NodeModifyLeaderCmd::NodeModifyLeaderCmd(Node &node, const QString& leader, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newLeader(leader), oldLeader(node.leader()) { } void NodeModifyLeaderCmd::execute() { m_node.setLeader(newLeader); } void NodeModifyLeaderCmd::unexecute() { m_node.setLeader(oldLeader); } NodeModifyDescriptionCmd::NodeModifyDescriptionCmd(Node &node, const QString& description, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newDescription(description), oldDescription(node.description()) { } void NodeModifyDescriptionCmd::execute() { m_node.setDescription(newDescription); } void NodeModifyDescriptionCmd::unexecute() { m_node.setDescription(oldDescription); } NodeModifyConstraintCmd::NodeModifyConstraintCmd(Node &node, Node::ConstraintType c, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newConstraint(c), oldConstraint(static_cast(node.constraint())) { } void NodeModifyConstraintCmd::execute() { m_node.setConstraint(newConstraint); } void NodeModifyConstraintCmd::unexecute() { m_node.setConstraint(oldConstraint); } NodeModifyConstraintStartTimeCmd::NodeModifyConstraintStartTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.constraintStartTime()) { if (node.projectNode()) { m_timeZone = static_cast(node.projectNode())->timeZone(); } } void NodeModifyConstraintStartTimeCmd::execute() { m_node.setConstraintStartTime(DateTime(newTime, m_timeZone)); } void NodeModifyConstraintStartTimeCmd::unexecute() { m_node.setConstraintStartTime(oldTime); } NodeModifyConstraintEndTimeCmd::NodeModifyConstraintEndTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.constraintEndTime()) { if (node.projectNode()) { m_timeZone = static_cast(node.projectNode())->timeZone(); } } void NodeModifyConstraintEndTimeCmd::execute() { m_node.setConstraintEndTime(DateTime(newTime, m_timeZone)); } void NodeModifyConstraintEndTimeCmd::unexecute() { m_node.setConstraintEndTime(oldTime); } NodeModifyStartTimeCmd::NodeModifyStartTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.startTime()) { m_timeZone = static_cast(node.projectNode())->timeZone(); } void NodeModifyStartTimeCmd::execute() { m_node.setStartTime(DateTime(newTime, m_timeZone)); } void NodeModifyStartTimeCmd::unexecute() { m_node.setStartTime(oldTime); } NodeModifyEndTimeCmd::NodeModifyEndTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.endTime()) { m_timeZone = static_cast(node.projectNode())->timeZone(); } void NodeModifyEndTimeCmd::execute() { m_node.setEndTime(DateTime(newTime, m_timeZone)); } void NodeModifyEndTimeCmd::unexecute() { m_node.setEndTime(oldTime); } NodeModifyIdCmd::NodeModifyIdCmd(Node &node, const QString& id, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newId(id), oldId(node.id()) { } void NodeModifyIdCmd::execute() { m_node.setId(newId); } void NodeModifyIdCmd::unexecute() { m_node.setId(oldId); } NodeIndentCmd::NodeIndentCmd(Node &node, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_newparent(0), m_newindex(-1), m_cmd(0) { } NodeIndentCmd::~NodeIndentCmd() { delete m_cmd; } void NodeIndentCmd::execute() { m_oldparent = m_node.parentNode(); m_oldindex = m_oldparent->findChildNode(&m_node); Project *p = dynamic_cast(m_node.projectNode()); if (p && p->indentTask(&m_node, m_newindex)) { m_newparent = m_node.parentNode(); m_newindex = m_newparent->findChildNode(&m_node); // Summarytasks can't have resources, so remove resource requests from the new parent if (m_cmd == 0) { - foreach (ResourceGroupRequest *r, m_newparent->requests().requests()) { + foreach (ResourceRequest *r, m_newparent->requests().resourceRequests()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); - m_cmd->addCommand(new RemoveResourceGroupRequestCmd(r)); + m_cmd->addCommand(new RemoveResourceRequestCmd(r)); } // Also remove accounts if (m_newparent->runningAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyRunningAccountCmd(*m_newparent, m_newparent->runningAccount(), 0)); } if (m_newparent->startupAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyStartupAccountCmd(*m_newparent, m_newparent->startupAccount(), 0)); } if (m_newparent->shutdownAccount()) { if (m_cmd == 0) m_cmd = new MacroCommand(KUndo2MagicString()); m_cmd->addCommand(new NodeModifyShutdownAccountCmd(*m_newparent, m_newparent->shutdownAccount(), 0)); } } if (m_cmd) { m_cmd->execute(); } } } void NodeIndentCmd::unexecute() { Project * p = dynamic_cast(m_node.projectNode()); if (m_newindex != -1 && p && p->unindentTask(&m_node)) { m_newindex = -1; if (m_cmd) { m_cmd->unexecute(); } } } NodeUnindentCmd::NodeUnindentCmd(Node &node, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_newparent(0), m_newindex(-1) {} void NodeUnindentCmd::execute() { m_oldparent = m_node.parentNode(); m_oldindex = m_oldparent->findChildNode(&m_node); Project *p = dynamic_cast(m_node.projectNode()); if (p && p->unindentTask(&m_node)) { m_newparent = m_node.parentNode(); m_newindex = m_newparent->findChildNode(&m_node); } } void NodeUnindentCmd::unexecute() { Project * p = dynamic_cast(m_node.projectNode()); if (m_newindex != -1 && p && p->indentTask(&m_node, m_oldindex)) { m_newindex = -1; } } NodeMoveUpCmd::NodeMoveUpCmd(Node &node, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_moved(false) { m_project = static_cast(m_node.projectNode()); } void NodeMoveUpCmd::execute() { if (m_project) { m_moved = m_project->moveTaskUp(&m_node); } } void NodeMoveUpCmd::unexecute() { if (m_project && m_moved) { m_project->moveTaskDown(&m_node); } m_moved = false; } NodeMoveDownCmd::NodeMoveDownCmd(Node &node, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_moved(false) { m_project = static_cast(m_node.projectNode()); } void NodeMoveDownCmd::execute() { if (m_project) { m_moved = m_project->moveTaskDown(&m_node); } } void NodeMoveDownCmd::unexecute() { if (m_project && m_moved) { m_project->moveTaskUp(&m_node); } m_moved = false; } NodeMoveCmd::NodeMoveCmd(Project *project, Node *node, Node *newParent, int newPos, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_node(node), m_newparent(newParent), m_newpos(newPos), m_moved(false) { m_oldparent = node->parentNode(); Q_ASSERT(m_oldparent); } void NodeMoveCmd::execute() { if (m_project) { m_oldpos = m_oldparent->indexOf(m_node); m_moved = m_project->moveTask(m_node, m_newparent, m_newpos); if (m_moved) { if (m_cmd.isEmpty()) { // Summarytasks can't have resources, so remove resource requests from the new parent - foreach (ResourceGroupRequest *r, m_newparent->requests().requests()) { - m_cmd.addCommand(new RemoveResourceGroupRequestCmd(r)); + foreach (ResourceRequest *r, m_newparent->requests().resourceRequests()) { + m_cmd.addCommand(new RemoveResourceRequestCmd(r)); } // TODO appointments ?? } m_cmd.execute(); } } } void NodeMoveCmd::unexecute() { if (m_project && m_moved) { m_moved = m_project->moveTask(m_node, m_oldparent, m_oldpos); m_cmd.unexecute(); } m_moved = false; } AddRelationCmd::AddRelationCmd(Project &project, Relation *rel, const KUndo2MagicString& name) : NamedCommand(name), m_rel(rel), m_project(project) { m_taken = true; } AddRelationCmd::~AddRelationCmd() { if (m_taken) delete m_rel; } void AddRelationCmd::execute() { //debugPlan<parent()<<" to"<child(); m_taken = false; m_project.addRelation(m_rel, false); } void AddRelationCmd::unexecute() { m_taken = true; m_project.takeRelation(m_rel); } DeleteRelationCmd::DeleteRelationCmd(Project &project, Relation *rel, const KUndo2MagicString& name) : NamedCommand(name), m_rel(rel), m_project(project) { m_taken = false; } DeleteRelationCmd::~DeleteRelationCmd() { if (m_taken) { // do not access nodes, the may already be deleted m_rel->setParent(0); m_rel->setChild(0); delete m_rel; } } void DeleteRelationCmd::execute() { //debugPlan<parent()<<" to"<child(); m_taken = true; m_project.takeRelation(m_rel); } void DeleteRelationCmd::unexecute() { m_taken = false; m_project.addRelation(m_rel, false); } ModifyRelationTypeCmd::ModifyRelationTypeCmd(Relation *rel, Relation::Type type, const KUndo2MagicString& name) : NamedCommand(name), m_rel(rel), m_newtype(type) { m_oldtype = rel->type(); m_project = dynamic_cast(rel->parent() ->projectNode()); } void ModifyRelationTypeCmd::execute() { if (m_project) { m_project->setRelationType(m_rel, m_newtype); } } void ModifyRelationTypeCmd::unexecute() { if (m_project) { m_project->setRelationType(m_rel, m_oldtype); } } ModifyRelationLagCmd::ModifyRelationLagCmd(Relation *rel, Duration lag, const KUndo2MagicString& name) : NamedCommand(name), m_rel(rel), m_newlag(lag) { m_oldlag = rel->lag(); m_project = dynamic_cast(rel->parent() ->projectNode()); } void ModifyRelationLagCmd::execute() { if (m_project) { m_project->setRelationLag(m_rel, m_newlag); } } void ModifyRelationLagCmd::unexecute() { if (m_project) { m_project->setRelationLag(m_rel, m_oldlag); } } -AddResourceRequestCmd::AddResourceRequestCmd(ResourceRequestCollection *requests, ResourceRequest *request, ResourceGroupRequest *group, const KUndo2MagicString& name) +AddResourceRequestCmd::AddResourceRequestCmd(ResourceRequestCollection *requests, ResourceRequest *request, const KUndo2MagicString& name) : NamedCommand(name) , m_collection(requests) - , m_group(group) , m_request(request) { m_mine = true; } -AddResourceRequestCmd::AddResourceRequestCmd(ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name) - : NamedCommand(name), - m_group(group), - m_request(request) -{ - Q_ASSERT(group->parent()); - m_collection = group->parent(); - m_mine = true; -} AddResourceRequestCmd::~AddResourceRequestCmd() { if (m_mine) delete m_request; } void AddResourceRequestCmd::execute() { //debugPlan<<"group="<addCommand(new RemoveCompletionEntryCmd(completion, date)); cmd->addCommand(new AddCompletionEntryCmd(completion, date, value)); } ModifyCompletionEntryCmd::~ModifyCompletionEntryCmd() { delete cmd; } void ModifyCompletionEntryCmd::execute() { cmd->execute(); } void ModifyCompletionEntryCmd::unexecute() { cmd->unexecute(); } AddCompletionUsedEffortCmd::AddCompletionUsedEffortCmd(Completion &completion, const Resource *resource, Completion::UsedEffort *value, const KUndo2MagicString& name) : NamedCommand(name), m_completion(completion), m_resource(resource), newvalue(value), m_newmine(true), m_oldmine(false) { oldvalue = m_completion.usedEffort(resource); } AddCompletionUsedEffortCmd::~AddCompletionUsedEffortCmd() { if (m_oldmine) delete oldvalue; if (m_newmine) delete newvalue; } void AddCompletionUsedEffortCmd::execute() { if (oldvalue) { m_completion.takeUsedEffort(m_resource); m_oldmine = true; } m_completion.addUsedEffort(m_resource, newvalue); m_newmine = false; } void AddCompletionUsedEffortCmd::unexecute() { m_completion.takeUsedEffort(m_resource); if (oldvalue) { m_completion.addUsedEffort(m_resource, oldvalue); } m_newmine = true; m_oldmine = false; } AddCompletionActualEffortCmd::AddCompletionActualEffortCmd(Task *task, Resource *resource, const QDate &date, const Completion::UsedEffort::ActualEffort &value, const KUndo2MagicString& name) : NamedCommand(name) , m_task(task) , m_resource(resource) , m_date(date) , newvalue(value) { oldvalue = task->completion().getActualEffort(resource, date); } AddCompletionActualEffortCmd::~AddCompletionActualEffortCmd() { } void AddCompletionActualEffortCmd::execute() { m_task->completion().setActualEffort(m_resource, m_date, newvalue); } void AddCompletionActualEffortCmd::unexecute() { m_task->completion().setActualEffort(m_resource, m_date, oldvalue); } AddAccountCmd::AddAccountCmd(Project &project, Account *account, const QString& parent, int index, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_account(account), m_parent(0), m_index(index), m_parentName(parent) { m_mine = true; } AddAccountCmd::AddAccountCmd(Project &project, Account *account, Account *parent, int index, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_account(account), m_parent(parent), m_index(index) { m_mine = true; } AddAccountCmd::~AddAccountCmd() { if (m_mine) delete m_account; } void AddAccountCmd::execute() { if (m_parent == 0 && !m_parentName.isEmpty()) { m_parent = m_project.accounts().findAccount(m_parentName); } m_project.accounts().insert(m_account, m_parent, m_index); m_mine = false; } void AddAccountCmd::unexecute() { m_project.accounts().take(m_account); m_mine = true; } RemoveAccountCmd::RemoveAccountCmd(Project &project, Account *account, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_account(account), m_parent(account->parent()) { if (m_parent) { m_index = m_parent->accountList().indexOf(account); } else { m_index = project.accounts().accountList().indexOf(account); } m_mine = false; m_isDefault = account == project.accounts().defaultAccount(); for (Account::CostPlace *cp : m_account->costPlaces()) { if (cp->node()) { if (cp->running()) { m_cmd.addCommand(new NodeModifyRunningAccountCmd(*cp->node(), cp->node()->runningAccount(), 0)); } if (cp->startup()) { m_cmd.addCommand(new NodeModifyStartupAccountCmd(*cp->node(), cp->node()->startupAccount(), 0)); } if (cp->shutdown()) { m_cmd.addCommand(new NodeModifyShutdownAccountCmd(*cp->node(), cp->node()->shutdownAccount(), 0)); } } else if (cp->resource()) { m_cmd.addCommand(new ResourceModifyAccountCmd(*cp->resource(), cp->resource()->account(), 0)); } } for (int i = account->accountList().count()-1; i >= 0; --i) { m_cmd.addCommand(new RemoveAccountCmd(project, account->accountList().at(i))); } } RemoveAccountCmd::~RemoveAccountCmd() { if (m_mine) delete m_account; } void RemoveAccountCmd::execute() { if (m_isDefault) { m_project.accounts().setDefaultAccount(0); } m_cmd.execute(); // remove costplaces and children m_project.accounts().take(m_account); m_mine = true; } void RemoveAccountCmd::unexecute() { m_project.accounts().insert(m_account, m_parent, m_index); m_cmd.unexecute(); // add costplaces && children if (m_isDefault) { m_project.accounts().setDefaultAccount(m_account); } m_mine = false; } RenameAccountCmd::RenameAccountCmd(Account *account, const QString& value, const KUndo2MagicString& name) : NamedCommand(name), m_account(account) { m_oldvalue = account->name(); m_newvalue = value; } void RenameAccountCmd::execute() { m_account->setName(m_newvalue); } void RenameAccountCmd::unexecute() { m_account->setName(m_oldvalue); } ModifyAccountDescriptionCmd::ModifyAccountDescriptionCmd(Account *account, const QString& value, const KUndo2MagicString& name) : NamedCommand(name), m_account(account) { m_oldvalue = account->description(); m_newvalue = value; } void ModifyAccountDescriptionCmd::execute() { m_account->setDescription(m_newvalue); } void ModifyAccountDescriptionCmd::unexecute() { m_account->setDescription(m_oldvalue); } NodeModifyStartupCostCmd::NodeModifyStartupCostCmd(Node &node, double value, const KUndo2MagicString& name) : NamedCommand(name), m_node(node) { m_oldvalue = node.startupCost(); m_newvalue = value; } void NodeModifyStartupCostCmd::execute() { m_node.setStartupCost(m_newvalue); } void NodeModifyStartupCostCmd::unexecute() { m_node.setStartupCost(m_oldvalue); } NodeModifyShutdownCostCmd::NodeModifyShutdownCostCmd(Node &node, double value, const KUndo2MagicString& name) : NamedCommand(name), m_node(node) { m_oldvalue = node.shutdownCost(); m_newvalue = value; } void NodeModifyShutdownCostCmd::execute() { m_node.setShutdownCost(m_newvalue); } void NodeModifyShutdownCostCmd::unexecute() { m_node.setShutdownCost(m_oldvalue); } NodeModifyRunningAccountCmd::NodeModifyRunningAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_node(node) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyRunningAccountCmd::execute() { //debugPlan; if (m_oldvalue) { m_oldvalue->removeRunning(m_node); } if (m_newvalue) { m_newvalue->addRunning(m_node); } } void NodeModifyRunningAccountCmd::unexecute() { //debugPlan; if (m_newvalue) { m_newvalue->removeRunning(m_node); } if (m_oldvalue) { m_oldvalue->addRunning(m_node); } } NodeModifyStartupAccountCmd::NodeModifyStartupAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_node(node) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyStartupAccountCmd::execute() { //debugPlan; if (m_oldvalue) { m_oldvalue->removeStartup(m_node); } if (m_newvalue) { m_newvalue->addStartup(m_node); } } void NodeModifyStartupAccountCmd::unexecute() { //debugPlan; if (m_newvalue) { m_newvalue->removeStartup(m_node); } if (m_oldvalue) { m_oldvalue->addStartup(m_node); } } NodeModifyShutdownAccountCmd::NodeModifyShutdownAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_node(node) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyShutdownAccountCmd::execute() { //debugPlan; if (m_oldvalue) { m_oldvalue->removeShutdown(m_node); } if (m_newvalue) { m_newvalue->addShutdown(m_node); } } void NodeModifyShutdownAccountCmd::unexecute() { //debugPlan; if (m_newvalue) { m_newvalue->removeShutdown(m_node); } if (m_oldvalue) { m_oldvalue->addShutdown(m_node); } } ModifyDefaultAccountCmd::ModifyDefaultAccountCmd(Accounts &acc, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_accounts(acc) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void ModifyDefaultAccountCmd::execute() { //debugPlan; m_accounts.setDefaultAccount(m_newvalue); } void ModifyDefaultAccountCmd::unexecute() { //debugPlan; m_accounts.setDefaultAccount(m_oldvalue); } ResourceModifyAccountCmd::ResourceModifyAccountCmd(Resource &resource, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name) : NamedCommand(name), m_resource(resource) { m_oldvalue = oldvalue; m_newvalue = newvalue; } void ResourceModifyAccountCmd::execute() { //debugPlan; if (m_oldvalue) { m_oldvalue->removeRunning(m_resource); } if (m_newvalue) { m_newvalue->addRunning(m_resource); } } void ResourceModifyAccountCmd::unexecute() { //debugPlan; if (m_newvalue) { m_newvalue->removeRunning(m_resource); } if (m_oldvalue) { m_oldvalue->addRunning(m_resource); } } ProjectModifyConstraintCmd::ProjectModifyConstraintCmd(Project &node, Node::ConstraintType c, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newConstraint(c), oldConstraint(static_cast(node.constraint())) { } void ProjectModifyConstraintCmd::execute() { m_node.setConstraint(newConstraint); } void ProjectModifyConstraintCmd::unexecute() { m_node.setConstraint(oldConstraint); } ProjectModifyStartTimeCmd::ProjectModifyStartTimeCmd(Project &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.startTime()) { m_timeZone = node.timeZone(); } void ProjectModifyStartTimeCmd::execute() { m_node.setConstraintStartTime(DateTime(newTime, m_timeZone)); } void ProjectModifyStartTimeCmd::unexecute() { m_node.setConstraintStartTime(oldTime); } ProjectModifyEndTimeCmd::ProjectModifyEndTimeCmd(Project &node, const QDateTime& dt, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), newTime(dt), oldTime(node.endTime()) { m_timeZone = node.timeZone(); } void ProjectModifyEndTimeCmd::execute() { m_node.setEndTime(DateTime(newTime, m_timeZone)); m_node.setConstraintEndTime(DateTime(newTime, m_timeZone)); } void ProjectModifyEndTimeCmd::unexecute() { m_node.setConstraintEndTime(oldTime); } ProjectModifyWorkPackageInfoCmd::ProjectModifyWorkPackageInfoCmd(Project &project, const Project::WorkPackageInfo &wpi, const KUndo2MagicString& name) : NamedCommand(name) , m_node(project) , m_newWpi(wpi) , m_oldWpi(project.workPackageInfo()) { } void ProjectModifyWorkPackageInfoCmd::execute() { m_node.setWorkPackageInfo(m_newWpi); } void ProjectModifyWorkPackageInfoCmd::unexecute() { m_node.setWorkPackageInfo(m_oldWpi); } //---------------------------- SwapScheduleManagerCmd::SwapScheduleManagerCmd(Project &project, ScheduleManager *from, ScheduleManager *to, const KUndo2MagicString& name) : NamedCommand(name), m_node(project), m_from(from), m_to(to) { } SwapScheduleManagerCmd::~SwapScheduleManagerCmd() { } void SwapScheduleManagerCmd::execute() { m_node.swapScheduleManagers(m_from, m_to); } void SwapScheduleManagerCmd::unexecute() { m_node.swapScheduleManagers(m_to, m_from); } AddScheduleManagerCmd::AddScheduleManagerCmd(Project &node, ScheduleManager *sm, int index, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_parent(sm->parentManager()), m_sm(sm), m_index(index), m_exp(sm->expected()), m_mine(true) { } AddScheduleManagerCmd::AddScheduleManagerCmd(ScheduleManager *parent, ScheduleManager *sm, int index, const KUndo2MagicString& name) : NamedCommand(name), m_node(parent->project()), m_parent(parent), m_sm(sm), m_index(index), m_exp(sm->expected()), m_mine(true) { if (parent->schedulingMode() == ScheduleManager::AutoMode) { m_cmd.addCommand(new ModifyScheduleManagerSchedulingModeCmd(*parent, ScheduleManager::ManualMode)); } } AddScheduleManagerCmd::~AddScheduleManagerCmd() { if (m_mine) { m_sm->setParentManager(0); delete m_sm; } } void AddScheduleManagerCmd::execute() { m_cmd.redo(); m_node.addScheduleManager(m_sm, m_parent, m_index); m_sm->setExpected(m_exp); m_mine = false; } void AddScheduleManagerCmd::unexecute() { m_node.takeScheduleManager(m_sm); m_sm->setExpected(0); m_mine = true; m_cmd.undo(); } DeleteScheduleManagerCmd::DeleteScheduleManagerCmd(Project &node, ScheduleManager *sm, const KUndo2MagicString& name) : AddScheduleManagerCmd(node, sm, -1, name) { m_mine = false; m_index = m_parent ? m_parent->indexOf(sm) : node.indexOf(sm); foreach (ScheduleManager *s, sm->children()) { cmd.addCommand(new DeleteScheduleManagerCmd(node, s)); } } void DeleteScheduleManagerCmd::execute() { cmd.execute(); AddScheduleManagerCmd::unexecute(); } void DeleteScheduleManagerCmd::unexecute() { AddScheduleManagerCmd::execute(); cmd.unexecute(); } MoveScheduleManagerCmd::MoveScheduleManagerCmd(ScheduleManager *sm, ScheduleManager *newparent, int newindex, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), m_oldparent(sm->parentManager()), m_newparent(newparent), m_newindex(newindex) { m_oldindex = sm->parentManager() ? sm->parentManager()->indexOf(sm) : sm->project().indexOf(sm); } void MoveScheduleManagerCmd::execute() { m_sm->project().moveScheduleManager(m_sm, m_newparent, m_newindex); } void MoveScheduleManagerCmd::unexecute() { m_sm->project().moveScheduleManager(m_sm, m_oldparent, m_oldindex); } ModifyScheduleManagerNameCmd::ModifyScheduleManagerNameCmd(ScheduleManager &sm, const QString& value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.name()), newvalue(value) { } void ModifyScheduleManagerNameCmd::execute() { m_sm.setName(newvalue); } void ModifyScheduleManagerNameCmd::unexecute() { m_sm.setName(oldvalue); } ModifyScheduleManagerSchedulingModeCmd::ModifyScheduleManagerSchedulingModeCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.schedulingMode()), newvalue(value) { if (value == ScheduleManager::AutoMode) { // Allow only one for (ScheduleManager *m : sm.project().allScheduleManagers()) { if (m->schedulingMode() == ScheduleManager::AutoMode) { m_cmd.addCommand(new ModifyScheduleManagerSchedulingModeCmd(*m, ScheduleManager::ManualMode)); break; } } } } void ModifyScheduleManagerSchedulingModeCmd::execute() { m_cmd.redo(); m_sm.setSchedulingMode(newvalue); } void ModifyScheduleManagerSchedulingModeCmd::unexecute() { m_sm.setSchedulingMode(oldvalue); m_cmd.undo(); } ModifyScheduleManagerAllowOverbookingCmd::ModifyScheduleManagerAllowOverbookingCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.allowOverbooking()), newvalue(value) { } void ModifyScheduleManagerAllowOverbookingCmd::execute() { m_sm.setAllowOverbooking(newvalue); } void ModifyScheduleManagerAllowOverbookingCmd::unexecute() { m_sm.setAllowOverbooking(oldvalue); } ModifyScheduleManagerDistributionCmd::ModifyScheduleManagerDistributionCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.usePert()), newvalue(value) { } void ModifyScheduleManagerDistributionCmd::execute() { m_sm.setUsePert(newvalue); } void ModifyScheduleManagerDistributionCmd::unexecute() { m_sm.setUsePert(oldvalue); } ModifyScheduleManagerSchedulingDirectionCmd::ModifyScheduleManagerSchedulingDirectionCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.schedulingDirection()), newvalue(value) { } void ModifyScheduleManagerSchedulingDirectionCmd::execute() { m_sm.setSchedulingDirection(newvalue); } void ModifyScheduleManagerSchedulingDirectionCmd::unexecute() { m_sm.setSchedulingDirection(oldvalue); } ModifyScheduleManagerSchedulerCmd::ModifyScheduleManagerSchedulerCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.schedulerPluginIndex()), newvalue(value) { } void ModifyScheduleManagerSchedulerCmd::execute() { m_sm.setSchedulerPlugin(newvalue); } void ModifyScheduleManagerSchedulerCmd::unexecute() { m_sm.setSchedulerPlugin(oldvalue); } ModifyScheduleManagerSchedulingGranularityCmd::ModifyScheduleManagerSchedulingGranularityCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm), oldvalue(sm.granularity()), newvalue(value) { } void ModifyScheduleManagerSchedulingGranularityCmd::execute() { m_sm.setGranularity(newvalue); } void ModifyScheduleManagerSchedulingGranularityCmd::unexecute() { m_sm.setGranularity(oldvalue); } CalculateScheduleCmd::CalculateScheduleCmd(Project &node, ScheduleManager *sm, const KUndo2MagicString& name) : NamedCommand(name), m_node(node), m_sm(sm), m_first(true), m_newexpected(0) { if (sm->recalculate() && sm->isScheduled()) { m_sm = new ScheduleManager(node); m_sm->setRecalculate(true); m_sm->setGranularity(sm->granularity()); m_sm->setUsePert(sm->usePert()); m_sm->setSchedulerPluginId(sm->schedulerPluginId()); m_sm->setAllowOverbooking(sm->allowOverbooking()); m_sm->setName(sm->name()); preCmd.addCommand(new AddScheduleManagerCmd(sm, m_sm)); postCmd.addCommand(new SwapScheduleManagerCmd(node, sm, m_sm)); postCmd.addCommand(new MoveScheduleManagerCmd(m_sm, sm->parentManager(), sm->parentManager() ? sm->parentManager()->indexOf(sm)+1 : node.indexOf(sm)+1)); postCmd.addCommand(new DeleteScheduleManagerCmd(node, sm)); } m_oldexpected = m_sm->expected(); } CalculateScheduleCmd::~CalculateScheduleCmd() { if (m_sm->scheduling()) { m_sm->haltCalculation(); } } void CalculateScheduleCmd::execute() { Q_ASSERT(m_sm); preCmd.redo(); if (m_first) { m_sm->calculateSchedule(); if (m_sm->calculationResult() != ScheduleManager::CalculationCanceled) { m_first = false; } m_newexpected = m_sm->expected(); } else { m_sm->setExpected(m_newexpected); } postCmd.redo(); } void CalculateScheduleCmd::unexecute() { if (m_sm->scheduling()) { // terminate scheduling QApplication::setOverrideCursor(Qt::WaitCursor); m_sm->haltCalculation(); m_first = true; QApplication::restoreOverrideCursor(); } postCmd.undo(); m_sm->setExpected(m_oldexpected); preCmd.undo(); } //------------------------ BaselineScheduleCmd::BaselineScheduleCmd(ScheduleManager &sm, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm) { } void BaselineScheduleCmd::execute() { m_sm.setBaselined(true); } void BaselineScheduleCmd::unexecute() { m_sm.setBaselined(false); } ResetBaselineScheduleCmd::ResetBaselineScheduleCmd(ScheduleManager &sm, const KUndo2MagicString& name) : NamedCommand(name), m_sm(sm) { } void ResetBaselineScheduleCmd::execute() { m_sm.setBaselined(false); } void ResetBaselineScheduleCmd::unexecute() { m_sm.setBaselined(true); } //------------------------ ModifyStandardWorktimeYearCmd::ModifyStandardWorktimeYearCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name) : NamedCommand(name), swt(wt), m_oldvalue(oldvalue), m_newvalue(newvalue) { } void ModifyStandardWorktimeYearCmd::execute() { swt->setYear(m_newvalue); } void ModifyStandardWorktimeYearCmd::unexecute() { swt->setYear(m_oldvalue); } ModifyStandardWorktimeMonthCmd::ModifyStandardWorktimeMonthCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name) : NamedCommand(name), swt(wt), m_oldvalue(oldvalue), m_newvalue(newvalue) { } void ModifyStandardWorktimeMonthCmd::execute() { swt->setMonth(m_newvalue); } void ModifyStandardWorktimeMonthCmd::unexecute() { swt->setMonth(m_oldvalue); } ModifyStandardWorktimeWeekCmd::ModifyStandardWorktimeWeekCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name) : NamedCommand(name), swt(wt), m_oldvalue(oldvalue), m_newvalue(newvalue) { } void ModifyStandardWorktimeWeekCmd::execute() { swt->setWeek(m_newvalue); } void ModifyStandardWorktimeWeekCmd::unexecute() { swt->setWeek(m_oldvalue); } ModifyStandardWorktimeDayCmd::ModifyStandardWorktimeDayCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name) : NamedCommand(name), swt(wt), m_oldvalue(oldvalue), m_newvalue(newvalue) { } void ModifyStandardWorktimeDayCmd::execute() { swt->setDay(m_newvalue); } void ModifyStandardWorktimeDayCmd::unexecute() { swt->setDay(m_oldvalue); } //---------------- DocumentAddCmd::DocumentAddCmd(Documents &docs, Document *value, const KUndo2MagicString& name) : NamedCommand(name), m_docs(docs), m_mine(true) { Q_ASSERT(value); m_value = value; } DocumentAddCmd::~DocumentAddCmd() { //debugPlan; if (m_mine) delete m_value; } void DocumentAddCmd::execute() { m_docs.addDocument(m_value); m_mine = false; } void DocumentAddCmd::unexecute() { m_docs.takeDocument(m_value); m_mine = true; } //---------------- DocumentRemoveCmd::DocumentRemoveCmd(Documents &docs, Document *value, const KUndo2MagicString& name) : NamedCommand(name), m_docs(docs), m_mine(false) { Q_ASSERT(value); m_value = value; } DocumentRemoveCmd::~DocumentRemoveCmd() { //debugPlan; if (m_mine) delete m_value; } void DocumentRemoveCmd::execute() { m_docs.takeDocument(m_value); m_mine = true; } void DocumentRemoveCmd::unexecute() { m_docs.addDocument(m_value); m_mine = false; } //---------------- DocumentModifyUrlCmd::DocumentModifyUrlCmd(Document *doc, const QUrl &value, const KUndo2MagicString& name) : NamedCommand(name), m_doc(doc) { Q_ASSERT(doc); m_value = value; m_oldvalue = doc->url(); } void DocumentModifyUrlCmd::execute() { m_doc->setUrl(m_value); } void DocumentModifyUrlCmd::unexecute() { m_doc->setUrl(m_oldvalue); } //---------------- DocumentModifyNameCmd::DocumentModifyNameCmd(Document *doc, const QString &value, const KUndo2MagicString& name) : NamedCommand(name), m_doc(doc) { Q_ASSERT(doc); m_value = value; m_oldvalue = doc->name(); } void DocumentModifyNameCmd::execute() { m_doc->setName(m_value); } void DocumentModifyNameCmd::unexecute() { m_doc->setName(m_oldvalue); } //---------------- DocumentModifyTypeCmd::DocumentModifyTypeCmd(Document *doc, Document::Type value, const KUndo2MagicString& name) : NamedCommand(name), m_doc(doc) { Q_ASSERT(doc); m_value = value; m_oldvalue = doc->type(); } void DocumentModifyTypeCmd::execute() { m_doc->setType(m_value); } void DocumentModifyTypeCmd::unexecute() { m_doc->setType(m_oldvalue); } //---------------- DocumentModifyStatusCmd::DocumentModifyStatusCmd(Document *doc, const QString &value, const KUndo2MagicString& name) : NamedCommand(name), m_doc(doc) { Q_ASSERT(doc); m_value = value; m_oldvalue = doc->type(); } void DocumentModifyStatusCmd::execute() { m_doc->setStatus(m_value); } void DocumentModifyStatusCmd::unexecute() { m_doc->setStatus(m_oldvalue); } //---------------- DocumentModifySendAsCmd::DocumentModifySendAsCmd(Document *doc, const Document::SendAs value, const KUndo2MagicString& name) : NamedCommand(name), m_doc(doc) { Q_ASSERT(doc); m_value = value; m_oldvalue = doc->sendAs(); } void DocumentModifySendAsCmd::execute() { m_doc->setSendAs(m_value); } void DocumentModifySendAsCmd::unexecute() { m_doc->setSendAs(m_oldvalue); } //---------------- WBSDefinitionModifyCmd::WBSDefinitionModifyCmd(Project &project, const WBSDefinition value, const KUndo2MagicString& name) : NamedCommand(name), m_project(project) { m_newvalue = value; m_oldvalue = m_project.wbsDefinition(); } void WBSDefinitionModifyCmd::execute() { m_project.setWbsDefinition(m_newvalue); } void WBSDefinitionModifyCmd::unexecute() { m_project.setWbsDefinition(m_oldvalue); } //---------------- InsertProjectCmd::InsertProjectCmd(Project &fromProject, Node *parent, Node *after, const KUndo2MagicString& name) : MacroCommand(name), m_project(static_cast(parent->projectNode())), m_parent(parent) { Q_ASSERT(&fromProject != m_project); if (m_project->defaultCalendar()) { fromProject.setDefaultCalendar(0); // or else m_project default calendar may be overwitten } QString defaultAccount; if (! m_project->accounts().defaultAccount() && fromProject.accounts().defaultAccount()) { defaultAccount = fromProject.accounts().defaultAccount()->name(); } QHash startupaccountmap; QHash shutdownaccountmap; QHash runningaccountmap; QHash nodecalendarmap; // remove unhandled info in tasks and get accounts and calendars foreach (Node *n, fromProject.allNodes()) { if (n->type() == Node::Type_Task) { Task *t = static_cast(n); t->workPackage().clear(); while (! t->workPackageLog().isEmpty()) { WorkPackage *wp = t->workPackageLog().at(0); t->removeWorkPackage(wp); delete wp; } } if (n->startupAccount()) { startupaccountmap.insert(n, n->startupAccount()->name()); n->setStartupAccount(0); } if (n->shutdownAccount()) { shutdownaccountmap.insert(n, n->shutdownAccount()->name()); n->setShutdownAccount(0); } if (n->runningAccount()) { runningaccountmap.insert(n, n->runningAccount()->name()); n->setRunningAccount(0); } if (n->estimate()->calendar()) { nodecalendarmap.insert(n, n->estimate()->calendar()->id()); n->estimate()->setCalendar(0); } } // get resources pointing to calendars and accounts QHash resaccountmap; QHash rescalendarmap; foreach (Resource *r, fromProject.resourceList()) { if (r->account()) { resaccountmap.insert(r, r->account()->name()); r->setAccount(0); } if (r->calendar()) { rescalendarmap.insert(r, r->calendar()->id()); r->setCalendar(0); } } // create add account commands and keep track of used and unused accounts QList unusedAccounts; QMap accountsmap; foreach (Account *a, m_project->accounts().allAccounts()) { accountsmap.insert(a->name(), a); } foreach (Account *a, fromProject.accounts().accountList()) { addAccounts(a, 0, unusedAccounts, accountsmap); } // create add calendar commands and keep track of used and unused calendars QList unusedCalendars; QMap calendarsmap; foreach (Calendar *c, m_project->allCalendars()) { calendarsmap.insert(c->id(), c); } foreach (Calendar *c, fromProject.calendars()) { addCalendars(c, 0, unusedCalendars, calendarsmap); } // get all requests from fromProject before resources are merged - QHash > greqs; - QHash > rreqs; + QHash > rreqs; foreach (Node *n, fromProject.allNodes()) { QList resReq; if (n->type() != (int)Node::Type_Task || n->requests().isEmpty()) { continue; } - while (ResourceGroupRequest *gr = n->requests().requests().value(0)) { - while (ResourceRequest *rr = gr->resourceRequests(false).value(0)) { - debugPlanInsertProject<<"Get resource request:"<(rr, rr->resource())); - // all resource requests shall be reinserted - rr->unregisterRequest(); - gr->takeResourceRequest(rr); - } - // all group requests shall be reinserted - greqs[ gr ] = QPair(n, gr->group()); - gr->group()->unregisterRequest(gr); - int i = n->requests().takeRequest(gr); - Q_ASSERT(i >= 0); -#ifdef NDEBUG - Q_UNUSED(i); -#endif + while (ResourceRequest *rr = n->requests().resourceRequests(false).value(0)) { + debugPlanInsertProject<<"Get resource request:"<(rr, rr->resource())); + // all resource requests shall be reinserted + rr->unregisterRequest(); + n->requests().removeResourceRequest(rr); } } - debugPlanInsertProject<<"fromProject group requests:"< allGroups; QList newGroups; QHash existingGroups; // QHash foreach (ResourceGroup *g, fromProject.resourceGroups()) { ResourceGroup *gr = m_project->findResourceGroup(g->id()); if (gr == 0) { gr = g; newGroups << gr; } else { existingGroups[ gr ] = g; } allGroups << gr; } debugPlanInsertProject<<"fromProject resources:"< allResources; QList newResources; // resource in fromProject that does not exist in toProject QHash existingResources; // hash[toProject resource, fromProject resource] foreach (Resource *r, fromProject.resourceList()) { while (Schedule *s = r->schedules().values().value(0)) { r->deleteSchedule(s); // schedules not handled } Resource *res = m_project->resource(r->id()); if (res == nullptr) { newResources << r; allResources << r; } else { existingResources[ res ] = r; allResources << res; } } // Add new resources for (Resource *r : newResources) { debugPlanInsertProject<<"AddResourceCmd:"<name()<parentGroups(); addCommand(new AddResourceCmd(m_project, r, kundo2_noi18n("Resource"))); } // Add new groups for (ResourceGroup *g : newGroups) { debugPlanInsertProject<<"AddResourceGroupCmd:"<name()<resources(); addCommand(new AddResourceGroupCmd(m_project, g, kundo2_noi18n("ResourceGroup"))); for (Resource *r : g->resources()) { addCommand(new AddParentGroupCmd(r, g)); } } // Update resource account {QHash::const_iterator it = resaccountmap.constBegin(); QHash::const_iterator end = resaccountmap.constEnd(); for (; it != end; ++it) { Resource *r = it.key(); if (newResources.contains(r)) { Q_ASSERT(allResources.contains(r)); addCommand(new ResourceModifyAccountCmd(*r, 0, accountsmap.value(it.value()))); } }} // Update resource calendar {QHash::const_iterator it = rescalendarmap.constBegin(); QHash::const_iterator end = rescalendarmap.constEnd(); for (; it != end; ++it) { Resource *r = it.key(); if (newResources.contains(r)) { Q_ASSERT(allResources.contains(r)); addCommand(new ModifyResourceCalendarCmd(r, calendarsmap.value(it.value()))); } }} debugPlanInsertProject<<"Requests: clean up requests to resources already in m_project"; debugPlanInsertProject<<"All groups:"< >::const_iterator gregsIt; - for (gregsIt = greqs.constBegin(); gregsIt != greqs.constEnd(); ++gregsIt) { - ResourceGroupRequest *gr = gregsIt.key(); - QPair pair = gregsIt.value(); - Node *n = pair.first; - n->requests().removeRequests(); - ResourceGroup *newGroup = pair.second; - if (ResourceGroup *ng = existingGroups.key(newGroup)) { - newGroup = ng; - debugPlanInsertProject<<"Using existing group:"<requests(); - } else { debugPlanInsertProject<<"Using group from inserted project:"<requests(); } - Q_ASSERT(allGroups.contains(newGroup)); - gr->setGroup(newGroup); - gr->setId(0); - gr->setParent(nullptr); - addCommand(new AddResourceGroupRequestCmd(static_cast(*n), gr, kundo2_noi18n("Group %1", ++gi))); - debugPlanInsertProject<<"Add resource group request: task:"<wbsCode()<name()<<":"<name()<<"requests:"<requests(); - QHash >::const_iterator i = rreqs.constFind(gr); - for (; i != rreqs.constEnd() && i.key() == gr; ++i) { - ResourceRequest *rr = i.value().first; - rr->setId(0); - rr->setCollection(nullptr); - rr->setParent(nullptr); - Resource *newRes = i.value().second; - debugPlanInsertProject<<"Resource exists:"<id()<<(void*)newRes<<':'<resource(newRes->id()); - if (Resource *nr = existingResources.key(newRes)) { - newRes = nr; - debugPlanInsertProject<<"Resource existed:"<wbsCode()<name()<<"group:"<name()<<"resource:"<requiredResources().isEmpty()) { - // the resource request may have required resources that needs mapping - QList required; - foreach (Resource *r, rr->requiredResources()) { - if (newResources.contains(r)) { - required << r; - debugPlanInsertProject<<"Request: required (new)"<name(); - continue; - } - Resource *r2 = existingResources.key(r); - Q_ASSERT(allResources.contains(r2)); - if (r2) { - debugPlanInsertProject<<"Request: required (existing)"<name(); - required << r2; - } + QHash >::const_iterator i; + for (i = rreqs.constBegin(); i != rreqs.constEnd(); ++i) { + Node *n = i.key(); + ResourceRequest *rr = i.value().first; + rr->setId(0); + rr->setCollection(nullptr); + Resource *newRes = i.value().second; + debugPlanInsertProject<<"Resource exists:"<id()<<(void*)newRes<<':'<resource(newRes->id()); + if (Resource *nr = existingResources.key(newRes)) { + newRes = nr; + debugPlanInsertProject<<"Resource existed:"<wbsCode()<name()<<"resource:"<requiredResources().isEmpty()) { + // the resource request may have required resources that needs mapping + QList required; + foreach (Resource *r, rr->requiredResources()) { + if (newResources.contains(r)) { + required << r; + debugPlanInsertProject<<"Request: required (new)"<name(); + continue; + } + Resource *r2 = existingResources.key(r); + Q_ASSERT(allResources.contains(r2)); + if (r2) { + debugPlanInsertProject<<"Request: required (existing)"<name(); + required << r2; } - rr->setRequiredResources(required); } - Q_ASSERT(allResources.contains(newRes)); - // all resource requests shall be reinserted - rr->setResource(newRes); - addCommand(new AddResourceRequestCmd(&n->requests(), rr, gr, kundo2_noi18n("Resource %1", ++ri))); + rr->setRequiredResources(required); + } + if (!rr->alternativeRequests().isEmpty()) { + // TODO alternatives } + Q_ASSERT(allResources.contains(newRes)); + // all resource requests shall be reinserted + rr->setResource(newRes); + addCommand(new AddResourceRequestCmd(&n->requests(), rr, kundo2_noi18n("Resource %1", ++ri))); } // Add nodes (ids are unique, no need to check) Node *node_after = after; for (int i = 0; i < fromProject.numChildren(); ++i) { Node *n = fromProject.childNode(i); Q_ASSERT(n); while (Schedule *s = n->schedules().values().value(0)) { n->takeSchedule(s); // schedules not handled delete s; } n->setParentNode(0); if (node_after) { addCommand(new TaskAddCmd(m_project, n, node_after, kundo2_noi18n("Task"))); node_after = n; } else { addCommand(new SubtaskAddCmd(m_project, n, parent, kundo2_noi18n("Subtask"))); } addChildNodes(n); } // Dependencies: foreach (Node *n, fromProject.allNodes()) { while (n->numDependChildNodes() > 0) { Relation *r = n->dependChildNodes().at(0); n->takeDependChildNode(r); r->child()->takeDependParentNode(r); addCommand(new AddRelationCmd(*m_project, r)); } } // node calendar {QHash::const_iterator it = nodecalendarmap.constBegin(); QHash::const_iterator end = nodecalendarmap.constEnd(); for (; it != end; ++it) { addCommand(new ModifyEstimateCalendarCmd(*(it.key()), 0, calendarsmap.value(it.value()))); }} // node startup account {QHash::const_iterator it = startupaccountmap.constBegin(); QHash::const_iterator end = startupaccountmap.constEnd(); for (; it != end; ++it) { addCommand(new NodeModifyStartupAccountCmd(*(it.key()), 0, accountsmap.value(it.value()))); }} // node shutdown account {QHash::const_iterator it = shutdownaccountmap.constBegin(); QHash::const_iterator end = shutdownaccountmap.constEnd(); for (; it != end; ++it) { addCommand(new NodeModifyShutdownAccountCmd(*(it.key()), 0, accountsmap.value(it.value()))); }} // node running account {QHash::const_iterator it = runningaccountmap.constBegin(); QHash::const_iterator end = runningaccountmap.constEnd(); for (; it != end; ++it) { addCommand(new NodeModifyRunningAccountCmd(*(it.key()), 0, accountsmap.value(it.value()))); }} if (! defaultAccount.isEmpty()) { Account *a = accountsmap.value(defaultAccount); if (a && a->list()) { addCommand(new ModifyDefaultAccountCmd(m_project->accounts(), 0, a)); } } debugPlanInsertProject<<"Cleanup unused stuff from inserted project:"<<&fromProject; // Cleanup // Remove nodes from fromProject so they are not deleted while (Node *ch = fromProject.childNode(0)) { fromProject.takeChildNode(ch); } foreach (Node *n, fromProject.allNodes()) { fromProject.removeId(n->id()); } // Remove calendars from fromProject while (fromProject.calendarCount() > 0) { fromProject.takeCalendar(fromProject.calendarAt(0)); } qDeleteAll(unusedCalendars); // Remove accounts from fromProject while (fromProject.accounts().accountCount() > 0) { fromProject.accounts().take(fromProject.accounts().accountAt(0)); } qDeleteAll(unusedAccounts); for (Resource *r = fromProject.resourceList().value(0); r; r = fromProject.resourceList().value(0)) { debugPlanInsertProject<<"remove all resources from fromProject:"< 0) { ResourceGroup *g = fromProject.resourceGroupAt(0); - debugPlanInsertProject<<"Take used group:"<requests(); + debugPlanInsertProject<<"Take used group:"< &unused, QMap &calendarsmap) { Calendar *par = 0; if (parent) { par = calendarsmap.value(parent->id()); } if (par == 0) { par = parent; } Calendar *cal = calendarsmap.value(calendar->id()); if (cal == 0) { calendarsmap.insert(calendar->id(), calendar); addCommand(new CalendarAddCmd(m_project, calendar, -1, par)); } else { unused << calendar; } foreach (Calendar *c, calendar->calendars()) { addCalendars(c, calendar, unused, calendarsmap); } } void InsertProjectCmd::addAccounts(Account *account, Account *parent, QList &unused, QMap &accountsmap) { Account *par = 0; if (parent) { par = accountsmap.value(parent->name()); } if (par == 0) { par = parent; } Account *acc = accountsmap.value(account->name()); if (acc == 0) { debugPlanInsertProject<<"Move to new project:"<name(); accountsmap.insert(account->name(), account); addCommand(new AddAccountCmd(*m_project, account, par, -1, kundo2_noi18n("Add account %1", account->name()))); } else { debugPlanInsertProject<<"Already exists:"<name(); unused << account; } while (! account->accountList().isEmpty()) { Account *a = account->accountList().first(); account->list()->take(a); addAccounts(a, account, unused, accountsmap); } } void InsertProjectCmd::addChildNodes(Node *node) { // schedules not handled while (Schedule *s = node->schedules().values().value(0)) { node->takeSchedule(s); // schedules not handled delete s; } foreach (Node *n, node->childNodeIterator()) { n->setParentNode(0); addCommand(new SubtaskAddCmd(m_project, n, node, kundo2_noi18n("Subtask"))); addChildNodes(n); } // Remove child nodes so they are not added twice while (Node *ch = node->childNode(0)) { node->takeChildNode(ch); } } void InsertProjectCmd::execute() { debugPlanInsertProject<<"before execute:"<resourceGroups()<resourceList(); QApplication::setOverrideCursor(Qt::WaitCursor); MacroCommand::execute(); QApplication::restoreOverrideCursor(); debugPlanInsertProject<<"after execute:"<resourceGroups()<resourceList(); } void InsertProjectCmd::unexecute() { QApplication::setOverrideCursor(Qt::WaitCursor); MacroCommand::unexecute(); QApplication::restoreOverrideCursor(); } WorkPackageAddCmd::WorkPackageAddCmd(Project *project, Node *node, WorkPackage *value, const KUndo2MagicString& name) : NamedCommand(name), m_project(project), m_node(node), m_wp(value), m_mine(true) { } WorkPackageAddCmd::~WorkPackageAddCmd() { if (m_mine) { delete m_wp; } } void WorkPackageAddCmd::execute() { // FIXME use project //m_project->addWorkPackage(m_node, m_wp); static_cast(m_node)->addWorkPackage(m_wp); } void WorkPackageAddCmd::unexecute() { // FIXME use project //m_project->removeWorkPackage(m_node, m_wp); static_cast(m_node)->removeWorkPackage(m_wp); } ModifyProjectLocaleCmd::ModifyProjectLocaleCmd(Project &project, const KUndo2MagicString& name) : MacroCommand(name), m_project(project) { }; void ModifyProjectLocaleCmd::execute() { MacroCommand::execute(); m_project.emitLocaleChanged(); } void ModifyProjectLocaleCmd::unexecute() { MacroCommand::unexecute(); m_project.emitLocaleChanged(); } ModifyCurrencySymolCmd::ModifyCurrencySymolCmd(Locale *locale, const QString &value, const KUndo2MagicString& name) : NamedCommand(name), m_locale(locale), m_newvalue(value), m_oldvalue(locale->currencySymbol()) { }; void ModifyCurrencySymolCmd::execute() { m_locale->setCurrencySymbol(m_newvalue); } void ModifyCurrencySymolCmd::unexecute() { m_locale->setCurrencySymbol(m_oldvalue); } ModifyCurrencyFractionalDigitsCmd::ModifyCurrencyFractionalDigitsCmd(Locale *locale, int value, const KUndo2MagicString& name) : NamedCommand(name), m_locale(locale), m_newvalue(value), m_oldvalue(locale->monetaryDecimalPlaces()) { }; void ModifyCurrencyFractionalDigitsCmd::execute() { m_locale->setMonetaryDecimalPlaces(m_newvalue); } void ModifyCurrencyFractionalDigitsCmd::unexecute() { m_locale->setMonetaryDecimalPlaces(m_oldvalue); } AddExternalAppointmentCmd::AddExternalAppointmentCmd(Resource *resource, const QString &pid, const QString &pname, const QDateTime &start, const QDateTime &end, double load, const KUndo2MagicString& name) : NamedCommand(name), m_resource(resource), m_pid(pid), m_pname(pname), m_start(start), m_end(end), m_load(load) { } void AddExternalAppointmentCmd::execute() { m_resource->addExternalAppointment(m_pid, m_pname, m_start, m_end, m_load); } void AddExternalAppointmentCmd::unexecute() { m_resource->subtractExternalAppointment(m_pid, m_start, m_end, m_load); // FIXME do this smarter if (! m_resource->externalAppointments(m_pid).isEmpty()) { m_resource->takeExternalAppointment(m_pid); } } ClearExternalAppointmentCmd::ClearExternalAppointmentCmd(Resource *resource, const QString &pid, const KUndo2MagicString &name) : NamedCommand(name), m_resource(resource), m_pid(pid), m_appointments(0) { } ClearExternalAppointmentCmd::~ClearExternalAppointmentCmd() { delete m_appointments; } void ClearExternalAppointmentCmd::execute() { // debugPlan<name()<takeExternalAppointment(m_pid); } void ClearExternalAppointmentCmd::unexecute() { // debugPlan<name()<addExternalAppointment(m_pid, m_appointments); } m_appointments = 0; } ClearAllExternalAppointmentsCmd::ClearAllExternalAppointmentsCmd(Project *project, const KUndo2MagicString &name) : NamedCommand(name), m_project(project) { foreach (Resource *r, project->resourceList()) { const QMap map = r->externalProjects(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { m_cmd.addCommand(new ClearExternalAppointmentCmd(r, it.key())); } } } void ClearAllExternalAppointmentsCmd::execute() { m_cmd.redo(); } void ClearAllExternalAppointmentsCmd::unexecute() { m_cmd.undo(); } SharedResourcesFileCmd::SharedResourcesFileCmd(Project *project, const QString &newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->sharedResourcesFile()) , m_newValue(newValue) { } void SharedResourcesFileCmd::execute() { m_project->setSharedResourcesFile(m_newValue); } void SharedResourcesFileCmd::unexecute() { m_project->setSharedResourcesFile(m_oldValue); } UseSharedResourcesCmd::UseSharedResourcesCmd(Project *project, bool newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->useSharedResources()) , m_newValue(newValue) { } void UseSharedResourcesCmd::execute() { m_project->setUseSharedResources(m_newValue); } void UseSharedResourcesCmd::unexecute() { m_project->setUseSharedResources(m_oldValue); } SharedProjectsUrlCmd::SharedProjectsUrlCmd(Project *project, const QUrl &newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->sharedProjectsUrl()) , m_newValue(newValue) { } void SharedProjectsUrlCmd::execute() { m_project->setSharedProjectsUrl(m_newValue); } void SharedProjectsUrlCmd::unexecute() { m_project->setSharedProjectsUrl(m_oldValue); } LoadProjectsAtStartupCmd::LoadProjectsAtStartupCmd(Project *project, bool newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->loadProjectsAtStartup()) , m_newValue(newValue) { } void LoadProjectsAtStartupCmd::execute() { m_project->setLoadProjectsAtStartup(m_newValue); } void LoadProjectsAtStartupCmd::unexecute() { m_project->setLoadProjectsAtStartup(m_oldValue); } } //KPlato namespace diff --git a/src/libs/kernel/kptcommand.h b/src/libs/kernel/kptcommand.h index bf0ca835..b3a6ff58 100644 --- a/src/libs/kernel/kptcommand.h +++ b/src/libs/kernel/kptcommand.h @@ -1,1976 +1,1931 @@ /* This file is part of the KDE project * Copyright (C) 2004-2007 Dag Andersen * Copyright (C) 2011 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTCOMMAND_H #define KPTCOMMAND_H #include "plankernel_export.h" #include "NamedCommand.h" #include "MacroCommand.h" #include #include #include #include "kptappointment.h" #include "kptnode.h" #include "kptproject.h" #include "kptduration.h" #include "kpttask.h" #include "kptwbsdefinition.h" class QString; /** * @file * This file includes undo/redo commands for kernel data structures */ /// The main namespace namespace KPlato { class Locale; class Account; class Accounts; class Calendar; class CalendarDay; class Relation; class ResourceGroupRequest; class ResourceRequest; class ResourceGroup; class Resource; class Schedule; class StandardWorktime; class PLANKERNEL_EXPORT CalendarAddCmd : public NamedCommand { public: CalendarAddCmd(Project *project, Calendar *cal, int pos, Calendar *parent, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarAddCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Calendar *m_cal; int m_pos; Calendar *m_parent; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveCmd : public NamedCommand { public: CalendarRemoveCmd(Project *project, Calendar *cal, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarRemoveCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Calendar *m_parent; Calendar *m_cal; int m_index; bool m_mine; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT CalendarMoveCmd : public NamedCommand { public: CalendarMoveCmd(Project *project, Calendar *cal, int position, Calendar *parent, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; Calendar *m_cal; int m_newpos; int m_oldpos; Calendar *m_newparent; Calendar *m_oldparent; }; class PLANKERNEL_EXPORT CalendarModifyNameCmd : public NamedCommand { public: CalendarModifyNameCmd(Calendar *cal, const QString& newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Calendar *m_cal; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT CalendarModifyParentCmd : public NamedCommand { public: CalendarModifyParentCmd(Project *project, Calendar *cal, Calendar *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyParentCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Calendar *m_cal; Calendar *m_newvalue; Calendar *m_oldvalue; MacroCommand *m_cmd; int m_oldindex; int m_newindex; }; class PLANKERNEL_EXPORT CalendarModifyTimeZoneCmd : public NamedCommand { public: CalendarModifyTimeZoneCmd(Calendar *cal, const QTimeZone &value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyTimeZoneCmd() override; void execute() override; void unexecute() override; private: Calendar *m_cal; QTimeZone m_newvalue; QTimeZone m_oldvalue; MacroCommand *m_cmd; }; #ifdef HAVE_KHOLIDAYS class PLANKERNEL_EXPORT CalendarModifyHolidayRegionCmd : public NamedCommand { public: CalendarModifyHolidayRegionCmd(Calendar *cal, const QString &value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyHolidayRegionCmd() override; void execute() override; void unexecute() override; private: Calendar *m_cal; QString m_newvalue; QString m_oldvalue; }; #endif class PLANKERNEL_EXPORT CalendarAddDayCmd : public NamedCommand { public: CalendarAddDayCmd(Calendar *cal, CalendarDay *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarAddDayCmd() override; void execute() override; void unexecute() override; protected: Calendar *m_cal; CalendarDay *m_newvalue; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveDayCmd : public NamedCommand { public: CalendarRemoveDayCmd(Calendar *cal, CalendarDay *day, const KUndo2MagicString& name = KUndo2MagicString()); CalendarRemoveDayCmd(Calendar *cal, const QDate &day, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; protected: Calendar *m_cal; CalendarDay *m_value; bool m_mine; private: void init(); }; class PLANKERNEL_EXPORT CalendarModifyDayCmd : public NamedCommand { public: CalendarModifyDayCmd(Calendar *cal, CalendarDay *value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyDayCmd() override; void execute() override; void unexecute() override; private: Calendar *m_cal; CalendarDay *m_newvalue; CalendarDay *m_oldvalue; bool m_mine; }; class PLANKERNEL_EXPORT CalendarModifyStateCmd : public NamedCommand { public: CalendarModifyStateCmd(Calendar *calendar, CalendarDay *day, CalendarDay::State value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyStateCmd() override; void execute() override; void unexecute() override; private: Calendar *m_calendar; CalendarDay *m_day; CalendarDay::State m_newvalue; CalendarDay::State m_oldvalue; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT CalendarModifyTimeIntervalCmd : public NamedCommand { public: CalendarModifyTimeIntervalCmd(Calendar *calendar, TimeInterval &newvalue, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Calendar *m_calendar; TimeInterval *m_value; TimeInterval m_newvalue; TimeInterval m_oldvalue; }; class PLANKERNEL_EXPORT CalendarAddTimeIntervalCmd : public NamedCommand { public: CalendarAddTimeIntervalCmd(Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarAddTimeIntervalCmd() override; void execute() override; void unexecute() override; protected: Calendar *m_calendar; CalendarDay *m_day; TimeInterval *m_value; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveTimeIntervalCmd : public CalendarAddTimeIntervalCmd { public: CalendarRemoveTimeIntervalCmd(Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; }; class PLANKERNEL_EXPORT CalendarModifyWeekdayCmd : public NamedCommand { public: CalendarModifyWeekdayCmd(Calendar *cal, int weekday, CalendarDay *value, const KUndo2MagicString& name = KUndo2MagicString()); ~CalendarModifyWeekdayCmd() override; void execute() override; void unexecute() override; private: int m_weekday; Calendar *m_cal; CalendarDay *m_value; CalendarDay m_orig; }; class PLANKERNEL_EXPORT CalendarModifyDateCmd : public NamedCommand { public: CalendarModifyDateCmd(Calendar *cal, CalendarDay *day, const QDate &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Calendar *m_cal; CalendarDay *m_day; QDate m_newvalue, m_oldvalue; }; class PLANKERNEL_EXPORT ProjectModifyDefaultCalendarCmd : public NamedCommand { public: ProjectModifyDefaultCalendarCmd(Project *project, Calendar *cal, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; Calendar *m_newvalue, *m_oldvalue; }; class PLANKERNEL_EXPORT NodeDeleteCmd : public NamedCommand { public: explicit NodeDeleteCmd(Node *node, const KUndo2MagicString& name = KUndo2MagicString()); ~NodeDeleteCmd() override; void execute() override; void unexecute() override; private: Node *m_node; Node *m_parent; Project *m_project; int m_index; bool m_mine; QList m_appointments; MacroCommand *m_cmd; MacroCommand *m_relCmd; }; class PLANKERNEL_EXPORT TaskAddCmd : public NamedCommand { public: TaskAddCmd(Project *project, Node *node, Node *after, const KUndo2MagicString& name = KUndo2MagicString()); ~TaskAddCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Node *m_node; Node *m_after; bool m_added; }; class PLANKERNEL_EXPORT SubtaskAddCmd : public NamedCommand { public: SubtaskAddCmd(Project *project, Node *node, Node *parent, const KUndo2MagicString& name = KUndo2MagicString()); ~SubtaskAddCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Node *m_node; Node *m_parent; bool m_added; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT NodeModifyNameCmd : public NamedCommand { public: NodeModifyNameCmd(Node &node, const QString& nodename, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QString newName; QString oldName; }; class PLANKERNEL_EXPORT NodeModifyPriorityCmd : public NamedCommand { public: NodeModifyPriorityCmd(Node &node, int oldValue, int newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; int m_oldValue; int m_newValue; }; class PLANKERNEL_EXPORT NodeModifyLeaderCmd : public NamedCommand { public: NodeModifyLeaderCmd(Node &node, const QString& leader, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QString newLeader; QString oldLeader; }; class PLANKERNEL_EXPORT NodeModifyDescriptionCmd : public NamedCommand { public: NodeModifyDescriptionCmd(Node &node, const QString& description, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QString newDescription; QString oldDescription; }; class PLANKERNEL_EXPORT NodeModifyConstraintCmd : public NamedCommand { public: NodeModifyConstraintCmd(Node &node, Node::ConstraintType c, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Node::ConstraintType newConstraint; Node::ConstraintType oldConstraint; }; class PLANKERNEL_EXPORT NodeModifyConstraintStartTimeCmd : public NamedCommand { public: NodeModifyConstraintStartTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyConstraintEndTimeCmd : public NamedCommand { public: NodeModifyConstraintEndTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyStartTimeCmd : public NamedCommand { public: NodeModifyStartTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyEndTimeCmd : public NamedCommand { public: NodeModifyEndTimeCmd(Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyIdCmd : public NamedCommand { public: NodeModifyIdCmd(Node &node, const QString& id, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; QString newId; QString oldId; }; class PLANKERNEL_EXPORT NodeIndentCmd : public NamedCommand { public: explicit NodeIndentCmd(Node &node, const KUndo2MagicString& name = KUndo2MagicString()); ~NodeIndentCmd() override; void execute() override; void unexecute() override; private: Node &m_node; Node *m_oldparent, *m_newparent; int m_oldindex, m_newindex; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT NodeUnindentCmd : public NamedCommand { public: explicit NodeUnindentCmd(Node &node, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Node *m_oldparent, *m_newparent; int m_oldindex, m_newindex; }; class PLANKERNEL_EXPORT NodeMoveUpCmd : public NamedCommand { public: explicit NodeMoveUpCmd(Node &node, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Project *m_project; bool m_moved; }; class PLANKERNEL_EXPORT NodeMoveDownCmd : public NamedCommand { public: explicit NodeMoveDownCmd(Node &node, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Project *m_project; bool m_moved; }; class PLANKERNEL_EXPORT NodeMoveCmd : public NamedCommand { public: NodeMoveCmd(Project *project, Node *node, Node *newParent, int newPos, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; Node *m_node; Node *m_newparent; Node *m_oldparent; int m_newpos; int m_oldpos; bool m_moved; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT AddRelationCmd : public NamedCommand { public: AddRelationCmd(Project &project, Relation *rel, const KUndo2MagicString& name = KUndo2MagicString()); ~AddRelationCmd() override; void execute() override; void unexecute() override; private: Relation *m_rel; Project &m_project; bool m_taken; }; class PLANKERNEL_EXPORT DeleteRelationCmd : public NamedCommand { public: DeleteRelationCmd(Project &project, Relation *rel, const KUndo2MagicString& name = KUndo2MagicString()); ~DeleteRelationCmd() override; void execute() override; void unexecute() override; private: Relation *m_rel; Project &m_project; bool m_taken; }; class PLANKERNEL_EXPORT ModifyRelationTypeCmd : public NamedCommand { public: ModifyRelationTypeCmd(Relation *rel, Relation::Type type, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; Relation *m_rel; Relation::Type m_newtype; Relation::Type m_oldtype; }; class PLANKERNEL_EXPORT ModifyRelationLagCmd : public NamedCommand { public: ModifyRelationLagCmd(Relation *rel, Duration lag, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; Relation *m_rel; Duration m_newlag; Duration m_oldlag; }; class PLANKERNEL_EXPORT AddResourceRequestCmd : public NamedCommand { public: - AddResourceRequestCmd(ResourceRequestCollection *requests, ResourceRequest *request, ResourceGroupRequest *group = nullptr, const KUndo2MagicString& name = KUndo2MagicString()); - AddResourceRequestCmd(ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); + AddResourceRequestCmd(ResourceRequestCollection *requests, ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); ~AddResourceRequestCmd() override; void execute() override; void unexecute() override; private: ResourceRequestCollection *m_collection; - ResourceGroupRequest *m_group; ResourceRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT RemoveResourceRequestCmd : public NamedCommand { public: - RemoveResourceRequestCmd(ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); + RemoveResourceRequestCmd(ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); ~RemoveResourceRequestCmd() override; void execute() override; void unexecute() override; private: ResourceRequestCollection *m_collection; - ResourceGroupRequest *m_group; ResourceRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT ModifyResourceRequestUnitsCmd : public NamedCommand { public: ModifyResourceRequestUnitsCmd(ResourceRequest *request, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ResourceRequest *m_request; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyResourceRequestRequiredCmd : public NamedCommand { public: ModifyResourceRequestRequiredCmd(ResourceRequest *request, const QList &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ResourceRequest *m_request; QList m_oldvalue, m_newvalue; }; -class PLANKERNEL_EXPORT ModifyResourceGroupRequestUnitsCmd : public NamedCommand -{ -public: - ModifyResourceGroupRequestUnitsCmd(ResourceGroupRequest *request, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); - void execute() override; - void unexecute() override; - -private: - ResourceGroupRequest *m_request; - int m_oldvalue, m_newvalue; - -}; - class PLANKERNEL_EXPORT ModifyEstimateCmd : public NamedCommand { public: ModifyEstimateCmd(Node &node, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString()); ~ModifyEstimateCmd() override; void execute() override; void unexecute() override; private: Estimate *m_estimate; double m_oldvalue, m_newvalue; int m_optimistic, m_pessimistic; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT EstimateModifyOptimisticRatioCmd : public NamedCommand { public: EstimateModifyOptimisticRatioCmd(Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT EstimateModifyPessimisticRatioCmd : public NamedCommand { public: EstimateModifyPessimisticRatioCmd(Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateTypeCmd : public NamedCommand { public: ModifyEstimateTypeCmd(Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateUnitCmd : public NamedCommand { public: ModifyEstimateUnitCmd(Node &node, Duration::Unit oldvalue, Duration::Unit newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; Duration::Unit m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT EstimateModifyRiskCmd : public NamedCommand { public: EstimateModifyRiskCmd(Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateCalendarCmd : public NamedCommand { public: ModifyEstimateCalendarCmd(Node &node, Calendar *oldvalue, Calendar *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Estimate *m_estimate; Calendar *m_oldvalue, *m_newvalue; }; - -class PLANKERNEL_EXPORT AddResourceGroupRequestCmd : public NamedCommand -{ -public: - AddResourceGroupRequestCmd(Task &task, ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); - void execute() override; - void unexecute() override; - -private: - Task &m_task; - ResourceGroupRequest *m_request; - bool m_mine; -}; - -class PLANKERNEL_EXPORT RemoveResourceGroupRequestCmd : public NamedCommand -{ -public: - explicit RemoveResourceGroupRequestCmd(ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); - RemoveResourceGroupRequestCmd(Task &task, ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString()); - void execute() override; - void unexecute() override; - -private: - Task &m_task; - ResourceGroupRequest *m_request; - bool m_mine; -}; - class PLANKERNEL_EXPORT MoveResourceCmd : public NamedCommand { public: MoveResourceCmd(ResourceGroup *group, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_project; Resource *m_resource; ResourceGroup *m_oldvalue, *m_newvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyResourceNameCmd : public NamedCommand { public: ModifyResourceNameCmd(Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceInitialsCmd : public NamedCommand { public: ModifyResourceInitialsCmd(Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceEmailCmd : public NamedCommand { public: ModifyResourceEmailCmd(Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAutoAllocateCmd : public NamedCommand { public: ModifyResourceAutoAllocateCmd(Resource *resource, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; bool m_newvalue; bool m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceTypeCmd : public NamedCommand { public: ModifyResourceTypeCmd(Resource *resource, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceUnitsCmd : public NamedCommand { public: ModifyResourceUnitsCmd(Resource *resource, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAvailableFromCmd : public NamedCommand { public: ModifyResourceAvailableFromCmd(Resource *resource, const QDateTime& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QDateTime m_newvalue; DateTime m_oldvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyResourceAvailableUntilCmd : public NamedCommand { public: ModifyResourceAvailableUntilCmd(Resource *resource, const QDateTime& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QDateTime m_newvalue; DateTime m_oldvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyResourceNormalRateCmd : public NamedCommand { public: ModifyResourceNormalRateCmd(Resource *resource, double value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; double m_newvalue; double m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceOvertimeRateCmd : public NamedCommand { public: ModifyResourceOvertimeRateCmd(Resource *resource, double value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; double m_newvalue; double m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceCalendarCmd : public NamedCommand { public: ModifyResourceCalendarCmd(Resource *resource, Calendar *value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; Calendar *m_newvalue; Calendar *m_oldvalue; }; class PLANKERNEL_EXPORT ModifyRequiredResourcesCmd : public NamedCommand { public: ModifyRequiredResourcesCmd(Resource *resource, const QStringList &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QStringList m_newvalue; QStringList m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAccountCmd : public NamedCommand { public: ModifyResourceAccountCmd(Resource *resource, Account *account, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; Account *m_newvalue; Account *m_oldvalue; }; class PLANKERNEL_EXPORT AddResourceTeamCmd : public NamedCommand { public: AddResourceTeamCmd(Resource *team, const QString &member, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_team; QString m_member; }; class PLANKERNEL_EXPORT RemoveResourceTeamCmd : public NamedCommand { public: RemoveResourceTeamCmd(Resource *team, const QString &member, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_team; QString m_member; }; class PLANKERNEL_EXPORT RemoveResourceGroupCmd : public NamedCommand { public: RemoveResourceGroupCmd(Project *project, ResourceGroup *group, const KUndo2MagicString& name = KUndo2MagicString()); ~RemoveResourceGroupCmd() override; void execute() override; void unexecute() override; protected: - ResourceGroup *m_group; Project *m_project; int m_index; bool m_mine; - MacroCommand *m_cmd; + MacroCommand cmd; }; class PLANKERNEL_EXPORT AddResourceGroupCmd : public RemoveResourceGroupCmd { public: AddResourceGroupCmd(Project *project, ResourceGroup *group, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; }; class PLANKERNEL_EXPORT ModifyResourceGroupNameCmd : public NamedCommand { public: ModifyResourceGroupNameCmd(ResourceGroup *group, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ResourceGroup *m_group; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceGroupTypeCmd : public NamedCommand { public: ModifyResourceGroupTypeCmd(ResourceGroup *group, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ResourceGroup *m_group; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyCompletionEntrymodeCmd : public NamedCommand { public: ModifyCompletionEntrymodeCmd(Completion &completion, Completion::Entrymode value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; Completion::Entrymode oldvalue; Completion::Entrymode newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionStartedCmd : public NamedCommand { public: ModifyCompletionStartedCmd(Completion &completion, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; bool oldvalue; bool newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionFinishedCmd : public NamedCommand { public: ModifyCompletionFinishedCmd(Completion &completion, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; bool oldvalue; bool newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionStartTimeCmd : public NamedCommand { public: ModifyCompletionStartTimeCmd(Completion &completion, const QDateTime &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; DateTime oldvalue; QDateTime newvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyCompletionFinishTimeCmd : public NamedCommand { public: ModifyCompletionFinishTimeCmd(Completion &completion, const QDateTime &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; DateTime oldvalue; QDateTime newvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT AddCompletionEntryCmd : public NamedCommand { public: AddCompletionEntryCmd(Completion &completion, const QDate &date, Completion::Entry *value, const KUndo2MagicString& name = KUndo2MagicString()); ~AddCompletionEntryCmd() override; void execute() override; void unexecute() override; private: Completion &m_completion; QDate m_date; Completion::Entry *newvalue; bool m_newmine; }; class PLANKERNEL_EXPORT RemoveCompletionEntryCmd : public NamedCommand { public: RemoveCompletionEntryCmd(Completion &completion, const QDate& date, const KUndo2MagicString& name = KUndo2MagicString()); ~RemoveCompletionEntryCmd() override; void execute() override; void unexecute() override; private: Completion &m_completion; QDate m_date; Completion::Entry *value; bool m_mine; }; class PLANKERNEL_EXPORT ModifyCompletionEntryCmd : public NamedCommand { public: ModifyCompletionEntryCmd(Completion &completion, const QDate &date, Completion::Entry *value, const KUndo2MagicString& name = KUndo2MagicString()); ~ModifyCompletionEntryCmd() override; void execute() override; void unexecute() override; private: MacroCommand *cmd; }; class PLANKERNEL_EXPORT ModifyCompletionPercentFinishedCmd : public NamedCommand { public: ModifyCompletionPercentFinishedCmd(Completion &completion, const QDate &date, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; QDate m_date; int m_newvalue, m_oldvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyCompletionRemainingEffortCmd : public NamedCommand { public: ModifyCompletionRemainingEffortCmd(Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; QDate m_date; Duration m_newvalue, m_oldvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyCompletionActualEffortCmd : public NamedCommand { public: ModifyCompletionActualEffortCmd(Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Completion &m_completion; QDate m_date; Duration m_newvalue, m_oldvalue; MacroCommand cmd; }; /** * Add used effort for @p resource. * Note that the used effort definition in @p value must contain entries for *all* dates. * If used effort is already defined it will be replaced. */ class PLANKERNEL_EXPORT AddCompletionUsedEffortCmd : public NamedCommand { public: AddCompletionUsedEffortCmd(Completion &completion, const Resource *resource, Completion::UsedEffort *value, const KUndo2MagicString& name = KUndo2MagicString()); ~AddCompletionUsedEffortCmd() override; void execute() override; void unexecute() override; private: Completion &m_completion; const Resource *m_resource; Completion::UsedEffort *oldvalue; Completion::UsedEffort *newvalue; bool m_newmine, m_oldmine; }; class PLANKERNEL_EXPORT AddCompletionActualEffortCmd : public NamedCommand { public: AddCompletionActualEffortCmd(Task *task, Resource *resource, const QDate &date, const Completion::UsedEffort::ActualEffort &value, const KUndo2MagicString& name = KUndo2MagicString()); ~AddCompletionActualEffortCmd() override; void execute() override; void unexecute() override; private: Task *m_task; Resource *m_resource; QDate m_date; Completion::UsedEffort::ActualEffort oldvalue; Completion::UsedEffort::ActualEffort newvalue; }; class PLANKERNEL_EXPORT AddAccountCmd : public NamedCommand { public: AddAccountCmd(Project &project, Account *account, Account *parent = 0, int index = -1, const KUndo2MagicString& name = KUndo2MagicString()); AddAccountCmd(Project &project, Account *account, const QString& parent, int index = -1, const KUndo2MagicString& name = KUndo2MagicString()); ~AddAccountCmd() override; void execute() override; void unexecute() override; protected: bool m_mine; private: Project &m_project; Account *m_account; Account *m_parent; int m_index; QString m_parentName; }; class PLANKERNEL_EXPORT RemoveAccountCmd : public NamedCommand { public: RemoveAccountCmd(Project &project, Account *account, const KUndo2MagicString& name = KUndo2MagicString()); ~RemoveAccountCmd() override; void execute() override; void unexecute() override; private: Project &m_project; Account *m_account; Account *m_parent; int m_index; bool m_isDefault; bool m_mine; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT RenameAccountCmd : public NamedCommand { public: RenameAccountCmd(Account *account, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Account *m_account; QString m_oldvalue; QString m_newvalue; }; class PLANKERNEL_EXPORT ModifyAccountDescriptionCmd : public NamedCommand { public: ModifyAccountDescriptionCmd(Account *account, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Account *m_account; QString m_oldvalue; QString m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyStartupCostCmd : public NamedCommand { public: NodeModifyStartupCostCmd(Node &node, double value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyShutdownCostCmd : public NamedCommand { public: NodeModifyShutdownCostCmd(Node &node, double value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyRunningAccountCmd : public NamedCommand { public: NodeModifyRunningAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyStartupAccountCmd : public NamedCommand { public: NodeModifyStartupAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyShutdownAccountCmd : public NamedCommand { public: NodeModifyShutdownAccountCmd(Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ModifyDefaultAccountCmd : public NamedCommand { public: ModifyDefaultAccountCmd(Accounts &acc, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Accounts &m_accounts; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ResourceModifyAccountCmd : public NamedCommand { public: ResourceModifyAccountCmd(Resource &resource, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource &m_resource; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ProjectModifyConstraintCmd : public NamedCommand { public: ProjectModifyConstraintCmd(Project &node, Node::ConstraintType c, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_node; Node::ConstraintType newConstraint; Node::ConstraintType oldConstraint; }; class PLANKERNEL_EXPORT ProjectModifyStartTimeCmd : public NamedCommand { public: ProjectModifyStartTimeCmd(Project &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ProjectModifyEndTimeCmd : public NamedCommand { public: ProjectModifyEndTimeCmd(Project &project, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ProjectModifyWorkPackageInfoCmd : public NamedCommand { public: ProjectModifyWorkPackageInfoCmd(Project &project, const Project::WorkPackageInfo &wpi, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_node; Project::WorkPackageInfo m_newWpi; Project::WorkPackageInfo m_oldWpi; }; class PLANKERNEL_EXPORT SwapScheduleManagerCmd : public NamedCommand { public: SwapScheduleManagerCmd(Project &project, ScheduleManager *from, ScheduleManager *to, const KUndo2MagicString& name = KUndo2MagicString()); ~SwapScheduleManagerCmd() override; void execute() override; void unexecute() override; protected: Project &m_node; ScheduleManager *m_from; ScheduleManager *m_to; }; class PLANKERNEL_EXPORT AddScheduleManagerCmd : public NamedCommand { public: AddScheduleManagerCmd(Project &project, ScheduleManager *sm, int index = -1, const KUndo2MagicString& name = KUndo2MagicString()); AddScheduleManagerCmd(ScheduleManager *parent, ScheduleManager *sm, int index = -1, const KUndo2MagicString& name = KUndo2MagicString()); ~AddScheduleManagerCmd() override; void execute() override; void unexecute() override; protected: Project &m_node; ScheduleManager *m_parent; ScheduleManager *m_sm; int m_index; MainSchedule *m_exp; bool m_mine; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT DeleteScheduleManagerCmd : public AddScheduleManagerCmd { public: DeleteScheduleManagerCmd(Project &project, ScheduleManager *sm, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: MacroCommand cmd; }; class PLANKERNEL_EXPORT MoveScheduleManagerCmd : public NamedCommand { public: MoveScheduleManagerCmd(ScheduleManager *sm, ScheduleManager *newparent, int newindex, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager *m_sm; ScheduleManager *m_oldparent; int m_oldindex; ScheduleManager *m_newparent; int m_newindex; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT ModifyScheduleManagerNameCmd : public NamedCommand { public: ModifyScheduleManagerNameCmd(ScheduleManager &sm, const QString& value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; QString oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulingModeCmd : public NamedCommand { public: ModifyScheduleManagerSchedulingModeCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; int oldvalue, newvalue; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT ModifyScheduleManagerAllowOverbookingCmd : public NamedCommand { public: ModifyScheduleManagerAllowOverbookingCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerDistributionCmd : public NamedCommand { public: ModifyScheduleManagerDistributionCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT CalculateScheduleCmd : public NamedCommand { public: CalculateScheduleCmd(Project &project, ScheduleManager *sm, const KUndo2MagicString& name = KUndo2MagicString()); ~CalculateScheduleCmd() override; void execute() override; void unexecute() override; private: Project &m_node; QPointer m_sm; bool m_first; MainSchedule *m_oldexpected; MainSchedule *m_newexpected; MacroCommand preCmd; MacroCommand postCmd; }; class PLANKERNEL_EXPORT BaselineScheduleCmd : public NamedCommand { public: explicit BaselineScheduleCmd(ScheduleManager &sm, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; }; class PLANKERNEL_EXPORT ResetBaselineScheduleCmd : public NamedCommand { public: explicit ResetBaselineScheduleCmd(ScheduleManager &sm, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulingDirectionCmd : public NamedCommand { public: ModifyScheduleManagerSchedulingDirectionCmd(ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulerCmd : public NamedCommand { public: ModifyScheduleManagerSchedulerCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; int oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulingGranularityCmd : public NamedCommand { public: ModifyScheduleManagerSchedulingGranularityCmd(ScheduleManager &sm, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: ScheduleManager &m_sm; int oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeYearCmd : public NamedCommand { public: ModifyStandardWorktimeYearCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeMonthCmd : public NamedCommand { public: ModifyStandardWorktimeMonthCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeWeekCmd : public NamedCommand { public: ModifyStandardWorktimeWeekCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeDayCmd : public NamedCommand { public: ModifyStandardWorktimeDayCmd(StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT DocumentAddCmd : public NamedCommand { public: DocumentAddCmd(Documents& docs, Document *value, const KUndo2MagicString& name = KUndo2MagicString()); ~DocumentAddCmd() override; void execute() override; void unexecute() override; private: Documents& m_docs; Document *m_value; bool m_mine; }; class PLANKERNEL_EXPORT DocumentRemoveCmd : public NamedCommand { public: DocumentRemoveCmd(Documents& docs, Document *value, const KUndo2MagicString& name = KUndo2MagicString()); ~DocumentRemoveCmd() override; void execute() override; void unexecute() override; private: Documents& m_docs; Document *m_value; bool m_mine; }; class PLANKERNEL_EXPORT DocumentModifyUrlCmd : public NamedCommand { public: DocumentModifyUrlCmd(Document *doc, const QUrl &url, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Document *m_doc; QUrl m_value; QUrl m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyNameCmd : public NamedCommand { public: DocumentModifyNameCmd(Document *doc, const QString &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Document *m_doc; QString m_value; QString m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyTypeCmd : public NamedCommand { public: DocumentModifyTypeCmd(Document *doc, Document::Type value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Document *m_doc; Document::Type m_value; Document::Type m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyStatusCmd : public NamedCommand { public: DocumentModifyStatusCmd(Document *doc, const QString &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Document *m_doc; QString m_value; QString m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifySendAsCmd : public NamedCommand { public: DocumentModifySendAsCmd(Document *doc, const Document::SendAs value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Document *m_doc; Document::SendAs m_value; Document::SendAs m_oldvalue; }; class PLANKERNEL_EXPORT WBSDefinitionModifyCmd : public NamedCommand { public: WBSDefinitionModifyCmd(Project &project, const WBSDefinition value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project &m_project; WBSDefinition m_newvalue, m_oldvalue; }; class PLANKERNEL_EXPORT InsertProjectCmd : public MacroCommand { public: InsertProjectCmd(Project &project, Node *parent, Node *after, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; protected: void addAccounts(Account *account, Account *parent, QList &unused, QMap &all); void addCalendars(Calendar *calendar, Calendar *parent, QList &unused, QMap &all); void addChildNodes(Node *node); private: Project *m_project; Node *m_parent; Node *m_after; }; class PLANKERNEL_EXPORT WorkPackageAddCmd : public NamedCommand { public: WorkPackageAddCmd(Project *project, Node *node, WorkPackage *wp, const KUndo2MagicString& name = KUndo2MagicString()); ~WorkPackageAddCmd() override; void execute() override; void unexecute() override; private: Project *m_project; Node *m_node; WorkPackage *m_wp; bool m_mine; }; class PLANKERNEL_EXPORT ModifyProjectLocaleCmd : public MacroCommand { public: ModifyProjectLocaleCmd(Project &project, const KUndo2MagicString &name); void execute() override; void unexecute() override; private: Project &m_project; }; class PLANKERNEL_EXPORT ModifyCurrencySymolCmd : public NamedCommand { public: ModifyCurrencySymolCmd(Locale *locale, const QString &value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Locale *m_locale; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyCurrencyFractionalDigitsCmd : public NamedCommand { public: ModifyCurrencyFractionalDigitsCmd(Locale *locale, int value, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Locale *m_locale; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT AddExternalAppointmentCmd : public NamedCommand { public: AddExternalAppointmentCmd(Resource *resource, const QString &pid, const QString &pname, const QDateTime &start, const QDateTime &end, double load, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Resource *m_resource; QString m_pid; QString m_pname; QDateTime m_start; QDateTime m_end; double m_load; }; class PLANKERNEL_EXPORT ClearExternalAppointmentCmd : public NamedCommand { public: ClearExternalAppointmentCmd(Resource *resource, const QString &pid, const KUndo2MagicString& name = KUndo2MagicString()); ~ClearExternalAppointmentCmd() override; void execute() override; void unexecute() override; private: Resource *m_resource; QString m_pid; Appointment *m_appointments; }; class PLANKERNEL_EXPORT ClearAllExternalAppointmentsCmd : public NamedCommand { public: explicit ClearAllExternalAppointmentsCmd(Project *project, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT SharedResourcesFileCmd : public NamedCommand { public: explicit SharedResourcesFileCmd(Project *project, const QString &newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; QString m_oldValue; QString m_newValue; }; class PLANKERNEL_EXPORT UseSharedResourcesCmd : public NamedCommand { public: explicit UseSharedResourcesCmd(Project *project, bool newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; bool m_oldValue; bool m_newValue; }; class PLANKERNEL_EXPORT SharedProjectsUrlCmd : public NamedCommand { public: explicit SharedProjectsUrlCmd(Project *project, const QUrl &newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; QUrl m_oldValue; QUrl m_newValue; }; class PLANKERNEL_EXPORT LoadProjectsAtStartupCmd : public NamedCommand { public: explicit LoadProjectsAtStartupCmd(Project *project, bool newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute() override; void unexecute() override; private: Project *m_project; bool m_oldValue; bool m_newValue; }; } //KPlato namespace #endif //COMMAND_H diff --git a/src/libs/kernel/kptproject.cpp b/src/libs/kernel/kptproject.cpp index 8a55f8c1..4d741ee7 100644 --- a/src/libs/kernel/kptproject.cpp +++ b/src/libs/kernel/kptproject.cpp @@ -1,3556 +1,3538 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2010, 2012 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptproject.h" #include "kptlocale.h" #include "kptappointment.h" #include "kpttask.h" #include "kptdatetime.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptwbsdefinition.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "kptschedulerplugin.h" #include "kptdebug.h" #include #include #include #include #include #include namespace KPlato { QString generateId() { return QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMddHHmmss")) + KRandom::randomString(10); } Project::Project(Node *parent) : Node(parent), m_accounts(*this), m_defaultCalendar(0), m_config(&emptyConfig), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false), m_useLocalTaskModules(true) { //debugPlan<<"("<setDefaultValues(*this); } Project::Project(ConfigBase &config, bool useDefaultValues, Node *parent) : Node(parent), m_accounts(*this), m_defaultCalendar(0), m_config(&config), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false), m_useLocalTaskModules(true) { debugPlan<<"("<setDefaultValues(*this); } } void Project::init() { m_refCount = 1; // always used by creator m_constraint = Node::MustStartOn; m_standardWorktime = new StandardWorktime(); m_timeZone = QTimeZone::systemTimeZone(); // local timezone as default //debugPlan<= 0); if (m_refCount <= 0) { emit aboutToBeDeleted(); deleteLater(); } } Project::~Project() { debugPlan<<"("<blockChanged(); } for (Resource *r : qAsConst(m_resources)) { r->blockChanged(); } for (ResourceGroup *g : qAsConst(resourceGroupIdDict)) { g->blockChanged(); } delete m_standardWorktime; for (Resource *r : m_resources) { delete r; } while (!m_resourceGroups.isEmpty()) delete m_resourceGroups.takeFirst(); while (!m_calendars.isEmpty()) delete m_calendars.takeFirst(); while (!m_managers.isEmpty()) delete m_managers.takeFirst(); m_config = 0; //not mine, don't delete } int Project::type() const { return Node::Type_Project; } void Project::generateUniqueNodeIds() { foreach (Node *n, nodeIdDict) { debugPlan<name()<<"old"<id(); QString uid = uniqueNodeId(); nodeIdDict.remove(n->id()); n->setId(uid); nodeIdDict[ uid ] = n; debugPlan<name()<<"new"<id(); } } void Project::generateUniqueIds() { generateUniqueNodeIds(); foreach (ResourceGroup *g, resourceGroupIdDict) { if (g->isShared()) { continue; } resourceGroupIdDict.remove(g->id()); g->setId(uniqueResourceGroupId()); resourceGroupIdDict[ g->id() ] = g; } foreach (Resource *r, resourceIdDict) { if (r->isShared()) { continue; } resourceIdDict.remove(r->id()); r->setId(uniqueResourceId()); resourceIdDict[ r->id() ] = r; } foreach (Calendar *c, calendarIdDict) { if (c->isShared()) { continue; } calendarIdDict.remove(c->id()); c->setId(uniqueCalendarId()); calendarIdDict[ c->id() ] = c; } } void Project::calculate(Schedule *schedule, const DateTime &dt) { if (schedule == 0) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(dt); } void Project::calculate(const DateTime &dt) { if (m_currentSchedule == 0) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; QLocale locale; DateTime time = dt.isValid() ? dt : DateTime(QDateTime::currentDateTime()); MainSchedule *cs = static_cast(m_currentSchedule); Estimate::Use estType = (Estimate::Use) cs->type(); if (type() == Type_Project) { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project from: %1", locale.toString(dt, QLocale::ShortFormat)), 0); initiateCalculation(*cs); initiateCalculationLists(*cs); // must be after initiateCalculation() !! propagateEarliestStart(time); // Calculate lateFinish from time. If a task has started, remainingEffort is used. cs->setPhaseName(1, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate finish"), 1); cs->lateFinish = calculateForward(estType); cs->lateFinish = checkEndConstraints(cs->lateFinish); propagateLatestFinish(cs->lateFinish); // Calculate earlyFinish. If a task has started, remainingEffort is used. cs->setPhaseName(2, i18nc("Schedule project backward","Backward")); cs->logInfo(i18n("Calculate start"), 2); calculateBackward(estType); // Schedule. If a task has started, remainingEffort is used and appointments are copied from parent cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks forward"), 3); cs->endTime = scheduleForward(cs->startTime, estType); cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3); if (cs->endTime > m_constraintEndTime) { cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else if (cs->endTime == m_constraintEndTime) { cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } calcCriticalPath(false); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged(cs); emit projectChanged(); } else if (type() == Type_Subproject) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::calculate(ScheduleManager &sm) { emit sigCalculationStarted(this, &sm); sm.setScheduling(true); m_progress = 0; int nodes = 0; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { nodes++; } } int maxprogress = nodes * 3; if (sm.recalculate()) { emit maxProgress(maxprogress); sm.setMaxProgress(maxprogress); incProgress(); if (sm.parentManager()) { sm.expected()->startTime = sm.parentManager()->expected()->startTime; sm.expected()->earlyStart = sm.parentManager()->expected()->earlyStart; } incProgress(); calculate(sm.expected(), sm.recalculateFrom()); } else { emit maxProgress(maxprogress); sm.setMaxProgress(maxprogress); calculate(sm.expected()); emit scheduleChanged(sm.expected()); setCurrentSchedule(sm.expected()->id()); } emit sigProgress(maxprogress); emit sigCalculationFinished(this, &sm); emit scheduleManagerChanged(&sm); emit projectCalculated(&sm); emit projectChanged(); sm.setScheduling(false); } void Project::calculate(Schedule *schedule) { if (schedule == 0) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(); } void Project::calculate() { if (m_currentSchedule == 0) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; MainSchedule *cs = static_cast(m_currentSchedule); bool backwards = false; if (cs->manager()) { backwards = cs->manager()->schedulingDirection(); } QLocale locale; Estimate::Use estType = (Estimate::Use) cs->type(); if (type() == Type_Project) { QTime timer; timer.start(); initiateCalculation(*cs); initiateCalculationLists(*cs); // must be after initiateCalculation() !! if (! backwards) { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project forward from: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 0); cs->startTime = m_constraintStartTime; cs->earlyStart = m_constraintStartTime; // Calculate from start time propagateEarliestStart(cs->earlyStart); cs->setPhaseName(1, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate late finish"), 1); cs->lateFinish = calculateForward(estType); // cs->lateFinish = checkEndConstraints(cs->lateFinish); cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 1); propagateLatestFinish(cs->lateFinish); cs->setPhaseName(2, i18nc("Schedule project backward", "Backward")); cs->logInfo(i18n("Calculate early start"), 2); calculateBackward(estType); cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks forward"), 3); cs->endTime = scheduleForward(cs->startTime, estType); cs->duration = cs->endTime - cs->startTime; cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3); if (cs->endTime > m_constraintEndTime) { cs->constraintError = true; cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else if (cs->endTime == m_constraintEndTime) { cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } calcCriticalPath(false); } else { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project backward from: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 0); // Calculate from end time propagateLatestFinish(m_constraintEndTime); cs->setPhaseName(1, i18nc("Schedule project backward", "Backward")); cs->logInfo(i18n("Calculate early start"), 1); cs->earlyStart = calculateBackward(estType); // cs->earlyStart = checkStartConstraints(cs->earlyStart); cs->logInfo(i18n("Early start calculated: %1", locale.toString(cs->earlyStart, QLocale::ShortFormat)), 1); propagateEarliestStart(cs->earlyStart); cs->setPhaseName(2, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate late finish"), 2); cs->lateFinish = qMax(m_constraintEndTime, calculateForward(estType)); cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 2); cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks backward"), 3); cs->startTime = scheduleBackward(cs->lateFinish, estType); cs->endTime = cs->startTime; foreach (Node *n, allNodes()) { if (n->type() == Type_Task || n->type() == Type_Milestone) { DateTime e = n->endTime(cs->id()); if (cs->endTime < e) { cs->endTime = e; } } } if (cs->endTime > m_constraintEndTime) { cs->constraintError = true; cs->logError(i18n("Failed to finish project within target time"), 3); } cs->duration = cs->endTime - cs->startTime; cs->logInfo(i18n("Scheduled start: %1, target time: %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->logError(i18n("Must start project early in order to finish in time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } else if (cs->startTime == m_constraintStartTime) { cs->logWarning(i18n("Start project exactly on time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Can start project later than time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } calcCriticalPath(true); } cs->logInfo(i18n("Calculation took: %1", KFormat().formatDuration(timer.elapsed()))); // TODO: fix this uncertainty, manager should *always* be available if (cs->manager()) { finishCalculation(*(cs->manager())); } } else if (type() == Type_Subproject) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::finishCalculation(ScheduleManager &sm) { MainSchedule *cs = sm.expected(); if (nodeIdDict.count() > 1) { // calculate project duration cs->startTime = m_constraintEndTime; cs->endTime = m_constraintStartTime; for (const Node *n : qAsConst(nodeIdDict)) { cs->startTime = qMin(cs->startTime, n->startTime(cs->id())); cs->endTime = qMax(cs->endTime, n->endTime(cs->id())); } cs->duration = cs->endTime - cs->startTime; } calcCriticalPath(false); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged(cs); emit projectChanged(); debugPlan<startTime<endTime<<"-------------------------"; } void Project::setProgress(int progress, ScheduleManager *sm) { m_progress = progress; if (sm) { sm->setProgress(progress); } emit sigProgress(progress); } void Project::setMaxProgress(int max, ScheduleManager *sm) { if (sm) { sm->setMaxProgress(max); } emitMaxProgress(max); } void Project::incProgress() { m_progress += 1; emit sigProgress(m_progress); } void Project::emitMaxProgress(int value) { emit maxProgress(value); } bool Project::calcCriticalPath(bool fromEnd) { //debugPlan; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0) { return false; } if (fromEnd) { QListIterator startnodes = cs->startNodes(); while (startnodes.hasNext()) { startnodes.next() ->calcCriticalPath(fromEnd); } } else { QListIterator endnodes = cs->endNodes(); while (endnodes.hasNext()) { endnodes.next() ->calcCriticalPath(fromEnd); } } calcCriticalPathList(cs); return false; } void Project::calcCriticalPathList(MainSchedule *cs) { //debugPlan<name(); cs->clearCriticalPathList(); foreach (Node *n, allNodes()) { if (n->numDependParentNodes() == 0 && n->inCriticalPath(cs->id())) { cs->addCriticalPath(); cs->addCriticalPathNode(n); calcCriticalPathList(cs, n); } } cs->criticalPathListCached = true; //debugPlan<<*(criticalPathList(cs->id())); } void Project::calcCriticalPathList(MainSchedule *cs, Node *node) { //debugPlan<name()<<", "<id(); bool newPath = false; QList lst = *(cs->currentCriticalPath()); foreach (Relation *r, node->dependChildNodes()) { if (r->child()->inCriticalPath(cs->id())) { if (newPath) { cs->addCriticalPath(&lst); //debugPlan<name()<<" new path"; } cs->addCriticalPathNode(r->child()); calcCriticalPathList(cs, r->child()); newPath = true; } } } const QList< QList > *Project::criticalPathList(long id) { Schedule *s = schedule(id); if (s == 0) { //debugPlan<<"No schedule with id="<(s); if (! ms->criticalPathListCached) { initiateCalculationLists(*ms); calcCriticalPathList(ms); } return ms->criticalPathList(); } QList Project::criticalPath(long id, int index) { Schedule *s = schedule(id); if (s == 0) { //debugPlan<<"No schedule with id="<(); } MainSchedule *ms = static_cast(s); if (! ms->criticalPathListCached) { initiateCalculationLists(*ms); calcCriticalPathList(ms); } return ms->criticalPath(index); } DateTime Project::startTime(long id) const { Schedule *s = schedule(id); return s ? s->startTime : m_constraintStartTime; } DateTime Project::endTime(long id) const { Schedule *s = schedule(id); return s ? s->endTime : m_constraintEndTime; } Duration Project::duration(long id) const { Schedule *s = schedule(id); return s ? s->duration : Duration::zeroDuration; } Duration *Project::getRandomDuration() { return 0L; } DateTime Project::checkStartConstraints(const DateTime &dt) const { DateTime t = dt; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { switch (n->constraint()) { case Node::FixedInterval: case Node::StartNotEarlier: case Node::MustStartOn: t = qMin(t, qMax(n->constraintStartTime(), m_constraintStartTime)); break; default: break; } } } return t; } DateTime Project::checkEndConstraints(const DateTime &dt) const { DateTime t = dt; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { switch (n->constraint()) { case Node::FixedInterval: case Node::FinishNotLater: case Node::MustFinishOn: t = qMax(t, qMin(n->constraintEndTime(), m_constraintEndTime)); break; default: break; } } } return t; } #ifndef PLAN_NLOGDEBUG bool Project::checkParent(Node *n, const QList &list, QList &checked) { if (n->isStartNode()) { debugPlan< lst = list; lst << n; foreach (Relation *r, n->dependParentNodes()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if (! checkParent(r->parent(), lst, checked)) { return false; } } Task *t = static_cast(n); foreach (Relation *r, t->parentProxyRelations()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; debugPlan<<"Proxy:"<parent()<<":"<parent(), lst, checked)) { return false; } } return true; } bool Project::checkChildren(Node *n, const QList &list, QList &checked) { if (n->isEndNode()) { debugPlan< lst = list; lst << n; foreach (Relation *r, n->dependChildNodes()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if (! checkChildren(r->child(), lst, checked)) { return false; } } Task *t = static_cast(n); foreach (Relation *r, t->childProxyRelations()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } debugPlan<<"Proxy:"<parent()<<":"<child(), lst, checked)) { return false; } } return true; } #endif void Project::tasksForward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); // Do these in reverse order to get tasks with same prio in wbs order const QList tasks = allTasks(); for (int i = tasks.count() -1; i >= 0; --i) { Task *t = tasks.at(i); switch (t->constraint()) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.prepend(t); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.prepend(t); break; default: if (t->isEndNode()) { m_terminalNodes.insert(-t->priority(), t); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"End nodes:"< lst; QList rel; Q_ASSERT(checkParent(n, lst, rel)); Q_UNUSED(n); } #endif } void Project::tasksBackward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); // Do these in reverse order to get tasks with same prio in wbs order const QList tasks = allTasks(); for (int i = tasks.count() -1; i >= 0; --i) { Task *t = tasks.at(i); switch (t->constraint()) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.prepend(t); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.prepend(t); break; default: if (t->isStartNode()) { m_terminalNodes.insert(-t->priority(), t); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"Start nodes:"< lst; QList rel; Q_ASSERT(checkChildren(n, lst, rel)); Q_UNUSED(n); } #endif } DateTime Project::calculateForward(int use) { //debugPlan<(m_currentSchedule); if (cs == 0) { return finish; } if (type() == Node::Type_Project) { QTime timer; timer.start(); cs->logInfo(i18n("Start calculating forward")); m_visitedForward = true; if (! m_visitedBackward) { // setup tasks tasksForward(); // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateEarlyFinish(use); // do not do predeccessors if (time > finish) { finish = time; } } // do the predeccessors foreach (Node *n, m_hardConstraints) { cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } // now try to schedule soft constrained *with* predeccessors foreach (Node *n, m_softConstraints) { cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } // and then the rest using the end nodes to calculate everything (remaining) foreach (Task *n, m_terminalNodes) { cs->logDebug("Calculate using end task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } } else { // tasks have been calculated backwards in this order foreach (Node *n, cs->backwardNodes()) { DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } } cs->logInfo(i18n("Finished calculating forward: %1 ms", timer.elapsed())); } else { //TODO: subproject } return finish; } DateTime Project::calculateBackward(int use) { //debugPlan<(m_currentSchedule); if (cs == 0) { return start; } if (type() == Node::Type_Project) { QTime timer; timer.start(); cs->logInfo(i18n("Start calculating backward")); m_visitedBackward = true; if (! m_visitedForward) { // setup tasks tasksBackward(); // Do all hard constrained first foreach (Task *n, m_hardConstraints) { cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateLateStart(use); // do not do predeccessors if (! start.isValid() || time < start) { start = time; } } // then do the predeccessors foreach (Task *n, m_hardConstraints) { cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } // now try to schedule soft constrained *with* predeccessors foreach (Task *n, m_softConstraints) { cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } // and then the rest using the start nodes to calculate everything (remaining) foreach (Task *n, m_terminalNodes) { cs->logDebug("Calculate using start task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } } else { // tasks have been calculated forwards in this order foreach (Node *n, cs->forwardNodes()) { DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } } cs->logInfo(i18n("Finished calculating backward: %1 ms", timer.elapsed())); } else { //TODO: subproject } return start; } DateTime Project::scheduleForward(const DateTime &earliest, int use) { DateTime end; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return DateTime(); } QTime timer; timer.start(); cs->logInfo(i18n("Start scheduling forward")); resetVisited(); // Schedule in the same order as calculated forward // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleFromStartTime(use); // do not do predeccessors if (time > end) { end = time; } } foreach (Node *n, cs->forwardNodes()) { cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleForward(earliest, use); if (time > end) { end = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo(i18n("Finished scheduling forward: %1 ms", timer.elapsed())); foreach (Node *n, allNodes()) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { Q_ASSERT(n->isScheduled()); } } return end; } DateTime Project::scheduleBackward(const DateTime &latest, int use) { DateTime start; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return start; } QTime timer; timer.start(); cs->logInfo(i18n("Start scheduling backward")); resetVisited(); // Schedule in the same order as calculated backward // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleFromEndTime(use); // do not do predeccessors if (! start.isValid() || time < start) { start = time; } } foreach (Node *n, cs->backwardNodes()) { cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleBackward(latest, use); if (! start.isValid() || time < start) { start = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo(i18n("Finished scheduling backward: %1 ms", timer.elapsed())); foreach (Node *n, allNodes()) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { Q_ASSERT(n->isScheduled()); } } return start; } void Project::adjustSummarytask() { MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return; } QListIterator it(cs->summaryTasks()); while (it.hasNext()) { it.next() ->adjustSummarytask(); } } void Project::initiateCalculation(MainSchedule &sch) { //debugPlan< git(m_resourceGroups); while (git.hasNext()) { git.next() ->initiateCalculation(sch); } for (Resource *r : m_resources) { r->initiateCalculation(sch); } Node::initiateCalculation(sch); } void Project::initiateCalculationLists(MainSchedule &sch) { //debugPlan< it = childNodeIterator(); while (it.hasNext()) { it.next() ->initiateCalculationLists(sch); } } else { //TODO: subproject } } bool Project::load(KoXmlElement &projectElement, XMLLoaderObject &status) { //debugPlan<<"--->"; QString s; bool ok = false; if (projectElement.hasAttribute("name")) { setName(projectElement.attribute("name")); } if (projectElement.hasAttribute("id")) { removeId(m_id); m_id = projectElement.attribute("id"); registerNodeId(this); } if (projectElement.hasAttribute("priority")) { m_priority = projectElement.attribute(QStringLiteral("priority"), "0").toInt(); } if (projectElement.hasAttribute("leader")) { m_leader = projectElement.attribute("leader"); } if (projectElement.hasAttribute("description")) { m_description = projectElement.attribute("description"); } if (projectElement.hasAttribute("timezone")) { QTimeZone tz(projectElement.attribute("timezone").toLatin1()); if (tz.isValid()) { m_timeZone = tz; } else warnPlan<<"No timezone specified, using default (local)"; status.setProjectTimeZone(m_timeZone); } if (projectElement.hasAttribute("scheduling")) { // Allow for both numeric and text s = projectElement.attribute("scheduling", "0"); m_constraint = (Node::ConstraintType) s.toInt(&ok); if (!ok) { setConstraint(s); } if (m_constraint != Node::MustStartOn && m_constraint != Node::MustFinishOn) { errorPlanXml << "Illegal constraint: " << constraintToString(); setConstraint(Node::MustStartOn); } } if (projectElement.hasAttribute("start-time")) { s = projectElement.attribute("start-time"); if (!s.isEmpty()) m_constraintStartTime = DateTime::fromString(s, m_timeZone); } if (projectElement.hasAttribute("end-time")) { s = projectElement.attribute("end-time"); if (!s.isEmpty()) m_constraintEndTime = DateTime::fromString(s, m_timeZone); } status.setProgress(10); // Load the project children KoXmlElement e = projectElement; if (status.version() < "0.7.0") { e = projectElement; } else { e = projectElement.namedItem("project-settings").toElement(); } if (!e.isNull()) { loadSettings(e, status); } e = projectElement.namedItem("documents").toElement(); if (!e.isNull()) { m_documents.load(e, status); } // Do calendars first, they only reference other calendars //debugPlan<<"Calendars--->"; if (status.version() < "0.7.0") { e = projectElement; } else { e = projectElement.namedItem("calendars").toElement(); } if (!e.isNull()) { debugPlanXml< cals; KoXmlElement ce; forEachElement(ce, e) { if (ce.tagName() != "calendar") { continue; } // Load the calendar. // Referenced by resources Calendar *child = new Calendar(); child->setProject(this); if (child->load(ce, status)) { cals.append(child); // temporary, reorder later } else { // TODO: Complain about this errorPlan << "Failed to load calendar"; delete child; } } // calendars references calendars in arbitrary saved order bool added = false; do { added = false; QList lst; while (!cals.isEmpty()) { Calendar *c = cals.takeFirst(); c->m_blockversion = true; if (c->parentId().isEmpty()) { addCalendar(c, status.baseCalendar()); // handle pre 0.6 version added = true; //debugPlan<<"added to project:"<name(); } else { Calendar *par = calendar(c->parentId()); if (par) { par->m_blockversion = true; addCalendar(c, par); added = true; //debugPlan<<"added:"<name()<<" to parent:"<name(); par->m_blockversion = false; } else { lst.append(c); // treat later //debugPlan<<"treat later:"<name(); } } c->m_blockversion = false; } cals = lst; } while (added); if (! cals.isEmpty()) { errorPlan<<"All calendars not saved!"; } //debugPlan<<"Calendars<---"; } status.setProgress(15); KoXmlNode n; // Resource groups and resources, can reference calendars if (status.version() < "0.7.0") { forEachElement(e, projectElement) { if (e.tagName() == "resource-group") { debugPlanXml<load(e, status)) { addResourceGroup(child); } else { // TODO: Complain about this errorPlan<resources()) { addResource(r); } } } else { e = projectElement.namedItem("resource-groups").toElement(); if (!e.isNull()) { debugPlanXml<load(ge, status)) { addResourceGroup(child); } else { // TODO: Complain about this delete child; } } } e = projectElement.namedItem("resources").toElement(); if (!e.isNull()) { debugPlanXml<load(re, status)) { addResource(r); } else { errorPlan<<"Failed to load resource xml"; delete r; } } } // resource-group relations e = projectElement.namedItem("resource-group-relations").toElement(); if (!e.isNull()) { debugPlanXml<addParentGroup(g); } else { errorPlan<<"Failed to load resource-group-relation"; } } } e = projectElement.namedItem("resource-teams").toElement(); if (!e.isNull()) { debugPlanXml<addTeamMemberId(tm->id()); } else { errorPlan<<"resource-teams: unhandled tag"<resource(re.attribute("required-id")); Resource *resource = this->resource(re.attribute("resource-id")); if (required && resource) { resource->addRequiredId(required->id()); } else { errorPlan<<"Failed to load required-resource"; } } } e = projectElement.namedItem("external-appointments").toElement(); if (!e.isNull()) { debugPlanXml<resource(e.attribute("resource-id")); if (!resource) { errorPlan<<"Cannot find resource:"<clearExternalAppointments(projectId); // in case... AppointmentIntervalList lst; lst.loadXML(e, status); Appointment *a = new Appointment(); a->setIntervals(lst); a->setAuxcilliaryInfo(e.attribute("project-name", "Unknown")); resource->addExternalAppointment(projectId, a); } } } status.setProgress(20); // The main stuff if (status.version() < "0.7.0") { e = projectElement; } else { e = projectElement.namedItem("tasks").toElement(); } if (!e.isNull()) { debugPlanXml<"; // Depends on resources already loaded Task *child = new Task(this); if (child->load(te, status)) { if (!addTask(child, this)) { delete child; // TODO: Complain about this } else { debugPlanXml<load(e, status)) { // TODO: Complain about this errorPlan << "Failed to load relation"; delete child; } //debugPlan<<"Relation<---"; } else if (status.version() < "0.7.0" && e.tagName() == "resource-requests") { // NOTE: Not supported: request to project (or sub-project) Q_ASSERT(false); #if 0 // Load the resource request // Handle multiple requests to same group gracefully (Not really allowed) KoXmlElement req; forEachElement(req, e) { ResourceGroupRequest *r = m_requests.findGroupRequestById(req.attribute(QStringLiteral("group-id"))); if (r) { warnPlan<<"Multiple requests to same group, loading into existing group"; if (! r->load(e, status)) { errorPlan<<"Failed to load resource request"; } } else { r = new ResourceGroupRequest(); if (r->load(e, status)) { addRequest(r); } else { errorPlan<<"Failed to load resource request"; delete r; } } } #endif } else if (status.version() < "0.7.0" && e.tagName() == "wbs-definition") { m_wbsDefinition.loadXML(e, status); } else if (e.tagName() == "locale") { // handled earlier } else if (e.tagName() == "resource-group") { // handled earlier } else if (e.tagName() == "calendar") { // handled earlier } else if (e.tagName() == "standard-worktime") { // handled earlier } else if (e.tagName() == "project") { // handled earlier } else if (e.tagName() == "task") { // handled earlier } else if (e.tagName() == "shared-resources") { // handled earlier } else if (e.tagName() == "documents") { // handled earlier } else if (e.tagName() == "workpackageinfo") { // handled earlier } else if (e.tagName() == "task-modules") { // handled earlier } else if (e.tagName() == "accounts") { // handled earlier } else { warnPlan<<"Unhandled tag:"<loadXML(sn, status)) { if (add) addScheduleManager(sm); } else { errorPlan << "Failed to load schedule manager"; delete sm; } } else { debugPlan<<"No schedule manager ?!"; } } //debugPlan<<"Node schedules<---"; } e = projectElement.namedItem("resource-teams").toElement(); if (!e.isNull()) { debugPlanXml<addTeamMemberId(tm->id()); } } if (status.version() >= "0.7.0") { e = projectElement.namedItem("relations").toElement(); debugPlanXml<load(de, status)) { // TODO: Complain about this errorPlan << "Failed to load relation"; delete child; } } } - e = projectElement.namedItem("resourcegroup-requests").toElement(); - if (!e.isNull()) { - debugPlanXml<setId(requestId); - task->requests().addRequest(request); - debugPlanXml<<"Added group request:"<requests().groupRequest(re.attribute("request-id").toInt()); - if (!group) { - warnPlanXml< 0); request->setId(requestId); - task->requests().addResourceRequest(request, group); + task->requests().addResourceRequest(request); } else { warnPlanXml<requests().resourceRequest(re.attribute("request-id").toInt()); Resource *required = findResource(re.attribute("required-id")); QList lst; if (required && request->resource() != required) { lst << required; } request->setRequiredResources(lst); } } + e = projectElement.namedItem("alternative-requests").toElement(); + if (!e.isNull()) { + debugPlanXml<setCurrencySymbol(e.attribute("currency-symbol", "")); if (e.hasAttribute("currency-digits")) { l->setMonetaryDecimalPlaces(e.attribute("currency-digits").toInt()); } QLocale::Language language = QLocale::AnyLanguage; QLocale::Country country = QLocale::AnyCountry; if (e.hasAttribute("language")) { language = static_cast(e.attribute("language").toInt()); } if (e.hasAttribute("country")) { country = static_cast(e.attribute("country").toInt()); } l->setCurrencyLocale(language, country); } else if (e.tagName() == "shared-resources") { m_useSharedResources = e.attribute("use", "0").toInt(); m_sharedResourcesFile = e.attribute("file"); m_sharedProjectsUrl = QUrl(e.attribute("projects-url")); m_loadProjectsAtStartup = (bool)e.attribute("projects-loadatstartup", "0").toInt(); } else if (e.tagName() == QLatin1String("workpackageinfo")) { if (e.hasAttribute("check-for-workpackages")) { m_workPackageInfo.checkForWorkPackages = e.attribute("check-for-workpackages").toInt(); } if (e.hasAttribute("retrieve-url")) { m_workPackageInfo.retrieveUrl = QUrl(e.attribute("retrieve-url")); } if (e.hasAttribute("delete-after-retrieval")) { m_workPackageInfo.deleteAfterRetrieval = e.attribute("delete-after-retrieval").toInt(); } if (e.hasAttribute("archive-after-retrieval")) { m_workPackageInfo.archiveAfterRetrieval = e.attribute("archive-after-retrieval").toInt(); } if (e.hasAttribute("archive-url")) { m_workPackageInfo.archiveUrl = QUrl(e.attribute("archive-url")); } if (e.hasAttribute("publish-url")) { m_workPackageInfo.publishUrl = QUrl(e.attribute("publish-url")); } } else if (e.tagName() == QLatin1String("task-modules")) { m_useLocalTaskModules = false; QList urls; for (KoXmlNode child = e.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement path = child.toElement(); if (path.isNull()) { continue; } QString s = path.attribute("url"); if (!s.isEmpty()) { QUrl url = QUrl::fromUserInput(s); if (!urls.contains(url)) { urls << url; } } } m_taskModules = urls; // If set adds local path to taskModules() setUseLocalTaskModules((bool)e.attribute("use-local-task-modules").toInt()); } else if (e.tagName() == "standard-worktime") { // Load standard worktime StandardWorktime *child = new StandardWorktime(); if (child->load(e, status)) { setStandardWorktime(child); } else { errorPlanXml << "Failed to load standard worktime"; delete child; } } } return true; } void Project::saveSettings(QDomElement &element, const XmlSaveContext &context) const { Q_UNUSED(context) QDomElement settingsElement = element.ownerDocument().createElement("project-settings"); element.appendChild(settingsElement); m_wbsDefinition.saveXML(settingsElement); QDomElement loc = settingsElement.ownerDocument().createElement("locale"); settingsElement.appendChild(loc); const Locale *l = locale(); loc.setAttribute("currency-symbol", l->currencySymbol()); loc.setAttribute("currency-digits", l->monetaryDecimalPlaces()); loc.setAttribute("language", l->currencyLanguage()); loc.setAttribute("country", l->currencyCountry()); QDomElement share = settingsElement.ownerDocument().createElement("shared-resources"); settingsElement.appendChild(share); share.setAttribute("use", m_useSharedResources); share.setAttribute("file", m_sharedResourcesFile); share.setAttribute("projects-url", QString(m_sharedProjectsUrl.toEncoded())); share.setAttribute("projects-loadatstartup", m_loadProjectsAtStartup); QDomElement wpi = settingsElement.ownerDocument().createElement("workpackageinfo"); settingsElement.appendChild(wpi); wpi.setAttribute("check-for-workpackages", m_workPackageInfo.checkForWorkPackages); wpi.setAttribute("retrieve-url", m_workPackageInfo.retrieveUrl.toString(QUrl::None)); wpi.setAttribute("delete-after-retrieval", m_workPackageInfo.deleteAfterRetrieval); wpi.setAttribute("archive-after-retrieval", m_workPackageInfo.archiveAfterRetrieval); wpi.setAttribute("archive-url", m_workPackageInfo.archiveUrl.toString(QUrl::None)); wpi.setAttribute("publish-url", m_workPackageInfo.publishUrl.toString(QUrl::None)); QDomElement tm = settingsElement.ownerDocument().createElement("task-modules"); settingsElement.appendChild(tm); tm.setAttribute("use-local-task-modules", m_useLocalTaskModules); for (const QUrl &url : taskModules(false/*no local*/)) { QDomElement e = tm.ownerDocument().createElement("task-module"); tm.appendChild(e); e.setAttribute("url", url.toString()); } // save standard worktime if (m_standardWorktime) { m_standardWorktime->save(settingsElement); } } void Project::save(QDomElement &element, const XmlSaveContext &context) const { debugPlanXml<save(ce); } } // save project resources debugPlanXml<<"resource-groups:"< git(m_resourceGroups); while (git.hasNext()) { git.next()->save(ge); } } debugPlanXml<<"resources:"< rit(m_resources); while (rit.hasNext()) { rit.next()->save(re); } } debugPlanXml<<"resource-group-relations"; if (!m_resources.isEmpty() && !m_resourceGroups.isEmpty()) { QDomElement e = me.ownerDocument().createElement("resource-group-relations"); me.appendChild(e); for (ResourceGroup *g : m_resourceGroups) { for (Resource *r : g->resources()) { QDomElement re = e.ownerDocument().createElement("resource-group-relation"); e.appendChild(re); re.setAttribute("group-id", g->id()); re.setAttribute("resource-id", r->id()); } } } debugPlanXml<<"required-resources"; if (m_resources.count() > 1) { QList > requiredList; for (Resource *resource : m_resources) { for (const QString &required : resource->requiredIds()) { requiredList << std::pair(resource->id(), required); } } if (!requiredList.isEmpty()) { QDomElement e = me.ownerDocument().createElement("required-resources"); me.appendChild(e); for (const std::pair pair : requiredList) { QDomElement re = e.ownerDocument().createElement("required-resource"); e.appendChild(re); re.setAttribute("resource-id", pair.first); re.setAttribute("required-id", pair.second); } } } // save resource teams debugPlanXml<<"resource-teams"; QDomElement el = me.ownerDocument().createElement("resource-teams"); me.appendChild(el); foreach (Resource *r, m_resources) { if (r->type() != Resource::Type_Team) { continue; } foreach (const QString &id, r->teamMemberIds()) { QDomElement e = el.ownerDocument().createElement("team"); el.appendChild(e); e.setAttribute("team-id", r->id()); e.setAttribute("member-id", id); } } // save resource usage in other projects QList externals; for (Resource *resource : m_resources) { if (!resource->externalAppointmentList().isEmpty()) { externals << resource; } } debugPlanXml<<"external-appointments"< projects = resource->externalProjects(); QMap::const_iterator it; for (it = projects.constBegin(); it != projects.constEnd(); ++it) { QDomElement re = e.ownerDocument().createElement("external-appointment"); e.appendChild(re); re.setAttribute("resource-id", resource->id()); re.setAttribute("project-id", it.key()); re.setAttribute("project-name", it.value()); resource->externalAppointments(it.key()).saveXML(e); } } } debugPlanXml<<"tasks:"< 0) { QDomElement e = me.ownerDocument().createElement("tasks"); me.appendChild(e); for (int i = 0; i < numChildren(); i++) { childNode(i)->save(e, context); } } // Now we can save relations assuming no tasks have relations outside the project QDomElement deps = me.ownerDocument().createElement("relations"); me.appendChild(deps); QListIterator nodes(m_nodes); while (nodes.hasNext()) { const Node *n = nodes.next(); n->saveRelations(deps, context); } debugPlanXml<<"task relations:"<saveXML(el); } } // save resource requests - QHash groups; QHash resources; for (Task *task : allTasks()) { const ResourceRequestCollection &requests = task->requests(); - for (ResourceGroupRequest *gr : requests.requests()) { - groups.insert(task, gr); - } for (ResourceRequest *rr : requests.resourceRequests(false)) { resources.insert(task, rr); } } - debugPlanXml<<"resourcegroup-requests:"<::const_iterator it; - for (it = groups.constBegin(); it != groups.constEnd(); ++it) { - if (!it.value()->group()) { - warnPlanXml<<"resourcegroup-request with no group"; - continue; - } - QDomElement ge = el.ownerDocument().createElement("resourcegroup-request"); - el.appendChild(ge); - ge.setAttribute("request-id", it.value()->id()); - ge.setAttribute("task-id", it.key()->id()); - ge.setAttribute("group-id", it.value()->group()->id()); - ge.setAttribute("units", QString::number(it.value()->units())); - } - } QHash > required; // QHash> + QHash > alternativeRequests; // QHash> debugPlanXml<<"resource-requests:"<::const_iterator it; for (it = resources.constBegin(); it != resources.constEnd(); ++it) { if (!it.value()->resource()) { continue; } QDomElement re = el.ownerDocument().createElement("resource-request"); el.appendChild(re); re.setAttribute("request-id", it.value()->id()); re.setAttribute("task-id", it.key()->id()); - if (it.value()->parent()) { - re.setAttribute("group-id", it.value()->parent()->group()->id()); - } re.setAttribute("resource-id", it.value()->resource()->id()); re.setAttribute("units", QString::number(it.value()->units())); // collect required resources for (Resource *r : it.value()->requiredResources()) { required.insert(it.key(), std::pair(it.value(), r)); } + for (ResourceRequest *r : it.value()->alternativeRequests()) { + alternativeRequests.insert(it.key(), std::pair(it.value(), r)); + } } } debugPlanXml<<"required-resource-requests:"< >::const_iterator it; for (it = required.constBegin(); it != required.constEnd(); ++it) { QDomElement req = reqs.ownerDocument().createElement("required-resource"); reqs.appendChild(req); req.setAttribute("task-id", it.key()->id()); req.setAttribute("request-id", it.value().first->id()); req.setAttribute("required-id", it.value().second->id()); } } + debugPlanXml<<"alternative-requests:"< >::const_iterator it; + for (it = alternativeRequests.constBegin(); it != alternativeRequests.constEnd(); ++it) { + QDomElement req = reqs.ownerDocument().createElement("alternative-request"); + reqs.appendChild(req); + req.setAttribute("task-id", it.key()->id()); + req.setAttribute("request-id", it.value().first->id()); + req.setAttribute("alternative-id", it.value().second->id()); + } + } } } void Project::saveWorkPackageXML(QDomElement &element, const Node *node, long id) const { QDomElement me = element.ownerDocument().createElement("project"); element.appendChild(me); me.setAttribute("name", m_name); me.setAttribute("leader", m_leader); me.setAttribute("id", m_id); me.setAttribute("description", m_description); me.setAttribute("timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString()); me.setAttribute("scheduling", constraintToString()); me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate)); me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate)); QListIterator git(m_resourceGroups); while (git.hasNext()) { git.next() ->saveWorkPackageXML(me, node->assignedResources(id)); } if (node == 0) { return; } node->saveWorkPackageXML(me, id); foreach (ScheduleManager *sm, m_managerIdMap) { if (sm->scheduleId() == id) { QDomElement el = me.ownerDocument().createElement("schedules"); me.appendChild(el); sm->saveWorkPackageXML(el, *node); break; } } } void Project::setParentSchedule(Schedule *sch) { QListIterator it = m_nodes; while (it.hasNext()) { it.next() ->setParentSchedule(sch); } } void Project::addResourceGroup(ResourceGroup *group, int index) { int i = index == -1 ? m_resourceGroups.count() : index; emit resourceGroupToBeAdded(this, i); m_resourceGroups.insert(i, group); setResourceGroupId(group); group->setProject(this); foreach (Resource *r, group->resources()) { setResourceId(r); r->setProject(this); } emit resourceGroupAdded(group); emit projectChanged(); } ResourceGroup *Project::takeResourceGroup(ResourceGroup *group) { int i = m_resourceGroups.indexOf(group); Q_ASSERT(i != -1); if (i == -1) { return 0; } emit resourceGroupToBeRemoved(this, i, group); ResourceGroup *g = m_resourceGroups.takeAt(i); Q_ASSERT(group == g); g->setProject(0); removeResourceGroupId(g->id()); foreach (Resource *r, g->resources()) { r->removeParentGroup(g); } emit resourceGroupRemoved(); emit projectChanged(); return g; } QList &Project::resourceGroups() { return m_resourceGroups; } void Project::addResource(Resource *resource, int index) { int i = index == -1 ? m_resources.count() : index; emit resourceToBeAdded(this, i); setResourceId(resource); m_resources.insert(i, resource); resource->setProject(this); emit resourceAdded(resource); emit projectChanged(); } bool Project::takeResource(Resource *resource) { int index = m_resources.indexOf(resource); emit resourceToBeRemoved(this, index, resource); bool result = removeResourceId(resource->id()); Q_ASSERT(result == true); if (!result) { warnPlan << "Could not remove resource with id" << resource->id(); } resource->removeRequests(); // not valid anymore for (ResourceGroup *g : resource->parentGroups()) { g->takeResource(resource); } bool rem = m_resources.removeOne(resource); Q_ASSERT(!m_resources.contains(resource)); emit resourceRemoved(); emit projectChanged(); return rem; } void Project::moveResource(ResourceGroup *group, Resource *resource) { if (resource->parentGroups().contains(group)) { return; } takeResource(resource); addResource(resource); group->addResource(resource); return; } QMap< QString, QString > Project::externalProjects() const { QMap< QString, QString > map; foreach (Resource *r, resourceList()) { for(QMapIterator it(r->externalProjects()); it.hasNext();) { it.next(); if (! map.contains(it.key())) { map[ it.key() ] = it.value(); } } } return map; } bool Project::addTask(Node* task, Node* position) { // we want to add a task at the given position. => the new node will // become next sibling right after position. if (0 == position) { return addSubTask(task, this); } //debugPlan<<"Add"<name()<<" after"<name(); // in case we want to add to the main project, we make it child element // of the root element. if (Node::Type_Project == position->type()) { return addSubTask(task, position); } // find the position // we have to tell the parent that we want to delete one of its children Node* parentNode = position->parentNode(); if (!parentNode) { debugPlan <<"parent node not found???"; return false; } int index = parentNode->findChildNode(position); if (-1 == index) { // ok, it does not exist debugPlan <<"Task not found???"; return false; } return addSubTask(task, index + 1, parentNode); } bool Project::addSubTask(Node* task, Node* parent) { // append task to parent return addSubTask(task, -1, parent); } bool Project::addSubTask(Node* task, int index, Node* parent, bool emitSignal) { // we want to add a subtask to the node "parent" at the given index. // If parent is 0, add to this Node *p = parent; if (0 == p) { p = this; } if (!registerNodeId(task)) { errorPlan << "Failed to register node id, can not add subtask: " << task->name(); return false; } int i = index == -1 ? p->numChildren() : index; if (emitSignal) emit nodeToBeAdded(p, i); p->insertChildNode(i, task); connect(this, &Project::standardWorktimeChanged, task, &Node::slotStandardWorktimeChanged); if (emitSignal) { emit nodeAdded(task); emit projectChanged(); if (p != this && p->numChildren() == 1) { emit nodeChanged(p, TypeProperty); } } return true; } void Project::takeTask(Node *node, bool emitSignal) { //debugPlan<name(); Node * parent = node->parentNode(); if (parent == 0) { debugPlan <<"Node must have a parent!"; return; } removeId(node->id()); if (emitSignal) emit nodeToBeRemoved(node); disconnect(this, &Project::standardWorktimeChanged, node, &Node::slotStandardWorktimeChanged); parent->takeChildNode(node); if (emitSignal) { emit nodeRemoved(node); emit projectChanged(); if (parent != this && parent->type() != Node::Type_Summarytask) { emit nodeChanged(parent, TypeProperty); } } } bool Project::canMoveTask(Node* node, Node *newParent, bool checkBaselined) { //debugPlan<name()<<" to"<name(); if (node == this) { return false; } if (checkBaselined) { if (node->isBaselined() || (newParent->type() != Node::Type_Summarytask && newParent->isBaselined())) { return false; } } Node *p = newParent; while (p && p != this) { if (! node->canMoveTo(p)) { return false; } p = p->parentNode(); } return true; } bool Project::moveTask(Node* node, Node *newParent, int newPos) { //debugPlan<name()<<" to"<name()<<","<parentNode(); int oldPos = oldParent->indexOf(node); int i = newPos < 0 ? newParent->numChildren() : newPos; if (oldParent == newParent && i == oldPos) { // no need to move to where it already is return false; } int newRow = i; if (oldParent == newParent && newPos > oldPos) { ++newRow; // itemmodels wants new row *before* node is removed from old position } debugPlan<name()<<"at"<indexOf(node)<<"to"<name()<numChildren() == 0) { emit nodeChanged(oldParent, TypeProperty); } if (newParent != this && newParent->numChildren() == 1) { emit nodeChanged(newParent, TypeProperty); } return true; } bool Project::canIndentTask(Node* node) { if (0 == node) { // should always be != 0. At least we would get the Project, // but you never know who might change that, so better be careful return false; } if (node->type() == Node::Type_Project) { //debugPlan<<"The root node cannot be indented"; return false; } // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } Node *sib = node->siblingBefore(); if (!sib) { //debugPlan<<"new parent node not found"; return false; } if (node->findParentRelation(sib) || node->findChildRelation(sib)) { //debugPlan<<"Cannot have relations to parent"; return false; } return true; } bool Project::indentTask(Node* node, int index) { if (canIndentTask(node)) { Node * newParent = node->siblingBefore(); int i = index == -1 ? newParent->numChildren() : index; moveTask(node, newParent, i); //debugPlan; return true; } return false; } bool Project::canUnindentTask(Node* node) { if (0 == node) { // is always != 0. At least we would get the Project, but you // never know who might change that, so better be careful return false; } if (Node::Type_Project == node->type()) { //debugPlan<<"The root node cannot be unindented"; return false; } // we have to find the parent of task to manipulate its list of children // and we need the parent's parent too Node* parentNode = node->parentNode(); if (!parentNode) { return false; } Node* grandParentNode = parentNode->parentNode(); if (!grandParentNode) { //debugPlan<<"This node already is at the top level"; return false; } int index = parentNode->findChildNode(node); if (-1 == index) { errorPlan << "Tasknot found???"; return false; } return true; } bool Project::unindentTask(Node* node) { if (canUnindentTask(node)) { Node * parentNode = node->parentNode(); Node *grandParentNode = parentNode->parentNode(); int i = grandParentNode->indexOf(parentNode) + 1; if (i == 0) { i = grandParentNode->numChildren(); } moveTask(node, grandParentNode, i); //debugPlan; return true; } return false; } bool Project::canMoveTaskUp(Node* node) { if (node == 0) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { //debugPlan<<"No parent found"; return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } if (node->siblingBefore()) { return true; } return false; } bool Project::moveTaskUp(Node* node) { if (canMoveTaskUp(node)) { moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) - 1); return true; } return false; } bool Project::canMoveTaskDown(Node* node) { if (node == 0) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } if (node->siblingAfter()) { return true; } return false; } bool Project::moveTaskDown(Node* node) { if (canMoveTaskDown(node)) { moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) + 1); return true; } return false; } Task *Project::createTask() { Task * node = new Task(); node->setId(uniqueNodeId()); reserveId(node->id(), node); return node; } Task *Project::createTask(const Task &def) { Task * node = new Task(def); node->setId(uniqueNodeId()); reserveId(node->id(), node); return node; } Node *Project::findNode(const QString &id) const { if (m_parent == 0) { if (nodeIdDict.contains(id)) { return nodeIdDict[ id ]; } return 0; } return m_parent->findNode(id); } bool Project::nodeIdentExists(const QString &id) const { return nodeIdDict.contains(id) || nodeIdReserved.contains(id); } QString Project::uniqueNodeId(int seed) const { Q_UNUSED(seed); QString ident = generateId(); while (nodeIdentExists(ident)) { ident = generateId(); } return ident; } QString Project::uniqueNodeId(const QList &existingIds, int seed) { QString id = uniqueNodeId(seed); while (existingIds.contains(id)) { id = uniqueNodeId(seed); } return id; } bool Project::removeId(const QString &id) { //debugPlan <<"id=" << id; if (m_parent) { return m_parent->removeId(id); } //debugPlan << "id=" << id<< nodeIdDict.contains(id); return nodeIdDict.remove(id); } void Project::reserveId(const QString &id, Node *node) { //debugPlan <<"id=" << id << node->name(); nodeIdReserved.insert(id, node); } bool Project::registerNodeId(Node *node) { nodeIdReserved.remove(node->id()); if (node->id().isEmpty()) { warnPlan << "Node id is empty, cannot register it"; return false; } Node *rn = findNode(node->id()); if (rn == 0) { //debugPlan <<"id=" << node->id() << node->name(); nodeIdDict.insert(node->id(), node); return true; } if (rn != node) { errorPlan << "Id already exists for different task: " << node->id(); return false; } //debugPlan<<"Already exists" <<"id=" << node->id() << node->name(); return true; } QList Project::allNodes(bool ordered, Node* parent) const { QList lst; if (ordered) { const Node *p = parent ? parent : this; foreach (Node *n, p->childNodeIterator()) { if (n->type() == Node::Type_Task || n->type() == Type_Milestone || n->type() == Node::Type_Summarytask) { lst << static_cast(n); lst += allNodes(ordered, n); } } } else { lst = nodeIdDict.values(); int me = lst.indexOf(const_cast(this)); if (me != -1) { lst.removeAt(me); } } return lst; } QList Project::allTasks(const Node *parent) const { QList lst; const Node *p = parent ? parent : this; foreach (Node *n, p->childNodeIterator()) { if (n->type() == Node::Type_Task || n->type() == Type_Milestone) { lst << static_cast(n); } lst += allTasks(n); } return lst; } bool Project::isStarted() const { const QList tasks = allTasks(); for (const Task *t : tasks) { if (t->isStarted()) { return true; } } return false; } bool Project::setResourceGroupId(ResourceGroup *group) { if (group == 0) { return false; } if (! group->id().isEmpty()) { ResourceGroup *g = findResourceGroup(group->id()); if (group == g) { return true; } else if (g == 0) { insertResourceGroupId(group->id(), group); return true; } } QString id = uniqueResourceGroupId(); group->setId(id); if (id.isEmpty()) { return false; } insertResourceGroupId(id, group); return true; } QString Project::uniqueResourceGroupId() const { QString id = generateId(); while (resourceGroupIdDict.contains(id)) { id = generateId(); } return id; } ResourceGroup *Project::group(const QString& id) { return findResourceGroup(id); } ResourceGroup *Project::groupByName(const QString& name) const { foreach (ResourceGroup *g, resourceGroupIdDict) { if (g->name() == name) { return g; } } return 0; } QList Project::autoAllocateResources() const { QList lst; foreach (Resource *r, m_resources) { if (r->autoAllocate()) { lst << r; } } return lst; } void Project::insertResourceId(const QString &id, Resource *resource) { resourceIdDict.insert(id, resource); } bool Project::removeResourceId(const QString &id) { return resourceIdDict.remove(id); } bool Project::setResourceId(Resource *resource) { if (resource == 0) { return false; } if (! resource->id().isEmpty()) { Resource *r = findResource(resource->id()); if (resource == r) { return true; } else if (r == 0) { insertResourceId(resource->id(), resource); return true; } } QString id = uniqueResourceId(); resource->setId(id); if (id.isEmpty()) { return false; } insertResourceId(id, resource); return true; } QString Project::uniqueResourceId() const { QString id = generateId(); while (resourceIdDict.contains(id)) { id = generateId(); } return id; } Resource *Project::resource(const QString& id) { return findResource(id); } Resource *Project::resourceByName(const QString& name) const { QHash::const_iterator it; for (it = resourceIdDict.constBegin(); it != resourceIdDict.constEnd(); ++it) { Resource *r = it.value(); if (r->name() == name) { Q_ASSERT(it.key() == r->id()); return r; } } return 0; } QStringList Project::resourceNameList() const { QStringList lst; for (Resource *r : m_resources) { lst << r->name(); } return lst; } int Project::indexOf(Resource *resource) const { return m_resources.indexOf(resource); } int Project::resourceCount() const { return m_resources.count(); } Resource *Project::resourceAt(int pos) const { return m_resources.value(pos); } EffortCostMap Project::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(start, end, id, typ); } return ec; } EffortCostMap Project::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(resource, start, end, id, typ); } return ec; } EffortCostMap Project::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(start, end, id, typ); } return ec; } EffortCostMap Project::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(resource, start, end, id, typ); } return ec; } // Returns the total planned effort for this project (or subproject) Duration Project::plannedEffort(long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffort(id, typ); } return eff; } // Returns the total planned effort for this project (or subproject) on date Duration Project::plannedEffort(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffort(date, id, typ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date Duration Project::plannedEffortTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffortTo(date, id, typ); } return eff; } // Returns the total actual effort for this project (or subproject) upto and including date Duration Project::actualEffortTo(QDate date) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->actualEffortTo(date); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date double Project::plannedCostTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; double c = 0; QListIterator it(childNodeIterator()); while (it.hasNext()) { c += it.next() ->plannedCostTo(date, id, typ); } return c; } // Returns the total actual cost for this project (or subproject) upto and including date EffortCost Project::actualCostTo(long int id, QDate date) const { //debugPlan; EffortCost c; QListIterator it(childNodeIterator()); while (it.hasNext()) { c += it.next() ->actualCostTo(id, date); } return c; } Duration Project::budgetedWorkPerformed(QDate date, long id) const { //debugPlan; Duration e; foreach (Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed(date, id); } return e; } double Project::budgetedCostPerformed(QDate date, long id) const { //debugPlan; double c = 0.0; foreach (Node *n, childNodeIterator()) { c += n->budgetedCostPerformed(date, id); } return c; } double Project::effortPerformanceIndex(QDate date, long id) const { //debugPlan; debugPlan< 0.0) { r = p / s; } debugPlan< date ? end : date), id); double budgetAtCompletion; double plannedCompleted; double budgetedCompleted; bool useEffort = false; //FIXME if (useEffort) { budgetAtCompletion = plan.totalEffort().toDouble(Duration::Unit_h); plannedCompleted = plan.effortTo(date).toDouble(Duration::Unit_h); //actualCompleted = actual.effortTo(date).toDouble(Duration::Unit_h); budgetedCompleted = budgetedWorkPerformed(date, id).toDouble(Duration::Unit_h); } else { budgetAtCompletion = plan.totalCost(); plannedCompleted = plan.costTo(date); budgetedCompleted = budgetedCostPerformed(date, id); } double c = 0.0; if (budgetAtCompletion > 0.0) { double percentageCompletion = budgetedCompleted / budgetAtCompletion; c = budgetAtCompletion * percentageCompletion; //?? debugPlan<name()<<","<<(parent?parent->name():"No parent"); int row = parent == 0 ? m_calendars.count() : parent->calendars().count(); if (index >= 0 && index < row) { row = index; } emit calendarToBeAdded(parent, row); calendar->setProject(this); if (parent == 0) { calendar->setParentCal(0); // in case m_calendars.insert(row, calendar); } else { calendar->setParentCal(parent, row); } if (calendar->isDefault()) { setDefaultCalendar(calendar); } setCalendarId(calendar); emit calendarAdded(calendar); emit projectChanged(); } void Project::takeCalendar(Calendar *calendar) { emit calendarToBeRemoved(calendar); removeCalendarId(calendar->id()); if (calendar == m_defaultCalendar) { m_defaultCalendar = 0; } if (calendar->parentCal() == 0) { int i = indexOf(calendar); if (i != -1) { m_calendars.removeAt(i); } } else { calendar->setParentCal(0); } emit calendarRemoved(calendar); calendar->setProject(0); emit projectChanged(); } int Project::indexOf(const Calendar *calendar) const { return m_calendars.indexOf(const_cast(calendar)); } Calendar *Project::calendar(const QString& id) const { return findCalendar(id); } Calendar *Project::calendarByName(const QString& name) const { foreach(Calendar *c, calendarIdDict) { if (c->name() == name) { return c; } } return 0; } const QList &Project::calendars() const { return m_calendars; } QList Project::allCalendars() const { return calendarIdDict.values(); } QStringList Project::calendarNames() const { QStringList lst; foreach(Calendar *c, calendarIdDict) { lst << c->name(); } return lst; } bool Project::setCalendarId(Calendar *calendar) { if (calendar == 0) { return false; } if (! calendar->id().isEmpty()) { Calendar *c = findCalendar(calendar->id()); if (calendar == c) { return true; } else if (c == 0) { insertCalendarId(calendar->id(), calendar); return true; } } QString id = uniqueCalendarId(); calendar->setId(id); if (id.isEmpty()) { return false; } insertCalendarId(id, calendar); return true; } QString Project::uniqueCalendarId() const { QString id = generateId(); while (calendarIdDict.contains(id)) { id = generateId(); } return id; } void Project::setDefaultCalendar(Calendar *cal) { if (m_defaultCalendar) { m_defaultCalendar->setDefault(false); } m_defaultCalendar = cal; if (cal) { cal->setDefault(true); } emit defaultCalendarChanged(cal); emit projectChanged(); } void Project::setStandardWorktime(StandardWorktime * worktime) { if (m_standardWorktime != worktime) { delete m_standardWorktime; m_standardWorktime = worktime; m_standardWorktime->setProject(this); emit standardWorktimeChanged(worktime); } } void Project::emitDocumentAdded(Node *node , Document *doc , int index) { emit documentAdded(node, doc, index); } void Project::emitDocumentRemoved(Node *node , Document *doc , int index) { emit documentRemoved(node, doc, index); } void Project::emitDocumentChanged(Node *node , Document *doc , int index) { emit documentChanged(node, doc, index); } bool Project::linkExists(const Node *par, const Node *child) const { if (par == 0 || child == 0 || par == child || par->isDependChildOf(child)) { return false; } foreach (Relation *r, par->dependChildNodes()) { if (r->child() == child) { return true; } } return false; } bool Project::legalToLink(const Node *par, const Node *child) const { //debugPlan<isDependChildOf(child)) { return false; } if (linkExists(par, child)) { return false; } bool legal = true; // see if par/child is related if (legal && (par->isParentOf(child) || child->isParentOf(par))) { legal = false; } if (legal) legal = legalChildren(par, child); if (legal) legal = legalParents(par, child); if (legal) { foreach (Node *p, par->childNodeIterator()) { if (! legalToLink(p, child)) { return false; } } } return legal; } bool Project::legalParents(const Node *par, const Node *child) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for (int i = 0; i < par->numDependParentNodes() && legal; ++i) { Node *pNode = par->getDependParentNode(i) ->parent(); if (child->isParentOf(pNode) || pNode->isParentOf(child)) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren(pNode, child); } if (legal) legal = legalParents(pNode, child); } return legal; } bool Project::legalChildren(const Node *par, const Node *child) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for (int j = 0; j < child->numDependChildNodes() && legal; ++j) { Node *cNode = child->getDependChildNode(j) ->child(); if (par->isParentOf(cNode) || cNode->isParentOf(par)) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren(par, cNode); } } return legal; } WBSDefinition &Project::wbsDefinition() { return m_wbsDefinition; } void Project::setWbsDefinition(const WBSDefinition &def) { //debugPlan; m_wbsDefinition = def; emit wbsDefinitionChanged(); emit projectChanged(); } QString Project::generateWBSCode(QList &indexes, bool sortable) const { QString code = m_wbsDefinition.projectCode(); if (sortable) { int fw = (nodeIdDict.count() / 10) + 1; QLatin1Char fc('0'); foreach (int index, indexes) { code += ".%1"; code = code.arg(QString::number(index), fw, fc); } //debugPlan<setCurrentSchedule(id); } emit currentScheduleChanged(); emit projectChanged(); } ScheduleManager *Project::scheduleManager(long id) const { foreach (ScheduleManager *sm, m_managers) { if (sm->scheduleId() == id) { return sm; } } return 0; } ScheduleManager *Project::scheduleManager(const QString &id) const { return m_managerIdMap.value(id); } ScheduleManager *Project::findScheduleManagerByName(const QString &name) const { //debugPlan; ScheduleManager *m = 0; foreach(ScheduleManager *sm, m_managers) { m = sm->findManager(name); if (m) { break; } } return m; } QList Project::allScheduleManagers() const { QList lst; foreach (ScheduleManager *sm, m_managers) { lst << sm; lst << sm->allChildren(); } return lst; } QString Project::uniqueScheduleName() const { //debugPlan; QString n = i18n("Plan"); bool unique = findScheduleManagerByName(n) == 0; if (unique) { return n; } n += " %1"; int i = 1; for (; true; ++i) { unique = findScheduleManagerByName(n.arg(i)) == 0; if (unique) { break; } } return n.arg(i); } void Project::addScheduleManager(ScheduleManager *sm, ScheduleManager *parent, int index) { int row = parent == 0 ? m_managers.count() : parent->childCount(); if (index >= 0 && index < row) { row = index; } if (parent == 0) { emit scheduleManagerToBeAdded(parent, row); m_managers.insert(row, sm); } else { emit scheduleManagerToBeAdded(parent, row); sm->setParentManager(parent, row); } if (sm->managerId().isEmpty()) { sm->setManagerId(uniqueScheduleManagerId()); } Q_ASSERT(! m_managerIdMap.contains(sm->managerId())); m_managerIdMap.insert(sm->managerId(), sm); emit scheduleManagerAdded(sm); emit projectChanged(); //debugPlan<<"Added:"<name()<<", now"<children()) { takeScheduleManager(s); } if (sm->scheduling()) { sm->stopCalculation(); } int index = -1; if (sm->parentManager()) { int index = sm->parentManager()->indexOf(sm); if (index >= 0) { emit scheduleManagerToBeRemoved(sm); sm->setParentManager(0); m_managerIdMap.remove(sm->managerId()); emit scheduleManagerRemoved(sm); emit projectChanged(); } } else { index = indexOf(sm); if (index >= 0) { emit scheduleManagerToBeRemoved(sm); m_managers.removeAt(indexOf(sm)); m_managerIdMap.remove(sm->managerId()); emit scheduleManagerRemoved(sm); emit projectChanged(); } } return index; } void Project::swapScheduleManagers(ScheduleManager *from, ScheduleManager *to) { emit scheduleManagersSwapped(from, to); } void Project::moveScheduleManager(ScheduleManager *sm, ScheduleManager *newparent, int newindex) { //debugPlan<name()<parentManager()) { m_managers.removeAt(indexOf(sm)); } sm->setParentManager(newparent, newindex); if (! newparent) { m_managers.insert(newindex, sm); } emit scheduleManagerMoved(sm, newindex); } bool Project::isScheduleManager(void *ptr) const { const ScheduleManager *sm = static_cast(ptr); if (indexOf(sm) >= 0) { return true; } foreach (ScheduleManager *p, m_managers) { if (p->isParentOf(sm)) { return true; } } return false; } ScheduleManager *Project::createScheduleManager(const QString &name) { //debugPlan<isBaselined()) { return true; } } return false; } Schedule *s = schedule(id); return s == 0 ? false : s->isBaselined(); } MainSchedule *Project::createSchedule(const QString& name, Schedule::Type type) { //debugPlan<<"No of schedules:"<setName(name); sch->setType(type); addMainSchedule(sch); return sch; } void Project::addMainSchedule(MainSchedule *sch) { if (sch == 0) { return; } //debugPlan<<"No of schedules:"<setId(i); sch->setNode(this); addSchedule(sch); } bool Project::removeCalendarId(const QString &id) { //debugPlan <<"id=" << id; return calendarIdDict.remove(id); } void Project::insertCalendarId(const QString &id, Calendar *calendar) { //debugPlan <<"id=" << id <<":" << calendar->name(); calendarIdDict.insert(id, calendar); } void Project::changed(Node *node, int property) { if (m_parent == 0) { Node::changed(node, property); // reset cache if (property != Node::TypeProperty) { // add/remove node is handled elsewhere emit nodeChanged(node, property); emit projectChanged(); } return; } Node::changed(node, property); } void Project::changed(ResourceGroup *group) { //debugPlan; // emit resourceGroupChanged(group); emit projectChanged(); } void Project::changed(ScheduleManager *sm, int property) { emit scheduleManagerChanged(sm, property); emit projectChanged(); } void Project::changed(MainSchedule *sch) { //debugPlan<id(); emit scheduleChanged(sch); emit projectChanged(); } void Project::sendScheduleToBeAdded(const ScheduleManager *sm, int row) { emit scheduleToBeAdded(sm, row); } void Project::sendScheduleAdded(const MainSchedule *sch) { //debugPlan<id(); emit scheduleAdded(sch); emit projectChanged(); } void Project::sendScheduleToBeRemoved(const MainSchedule *sch) { //debugPlan<id(); emit scheduleToBeRemoved(sch); } void Project::sendScheduleRemoved(const MainSchedule *sch) { //debugPlan<id(); emit scheduleRemoved(sch); emit projectChanged(); } void Project::changed(Resource *resource) { // emit resourceChanged(resource); emit projectChanged(); } void Project::changed(Calendar *cal) { emit calendarChanged(cal); emit projectChanged(); } void Project::changed(StandardWorktime *w) { emit standardWorktimeChanged(w); emit projectChanged(); } bool Project::addRelation(Relation *rel, bool check) { if (rel->parent() == 0 || rel->child() == 0) { return false; } if (check && !legalToLink(rel->parent(), rel->child())) { return false; } emit relationToBeAdded(rel, rel->parent()->numDependChildNodes(), rel->child()->numDependParentNodes()); rel->parent()->addDependChildNode(rel); rel->child()->addDependParentNode(rel); emit relationAdded(rel); emit projectChanged(); return true; } void Project::takeRelation(Relation *rel) { emit relationToBeRemoved(rel); rel->parent() ->takeDependChildNode(rel); rel->child() ->takeDependParentNode(rel); emit relationRemoved(rel); emit projectChanged(); } void Project::setRelationType(Relation *rel, Relation::Type type) { emit relationToBeModified(rel); rel->setType(type); emit relationModified(rel); emit projectChanged(); } void Project::setRelationLag(Relation *rel, const Duration &lag) { emit relationToBeModified(rel); rel->setLag(lag); emit relationModified(rel); emit projectChanged(); } QList Project::flatNodeList(Node *parent) { QList lst; Node *p = parent == 0 ? this : parent; //debugPlan<name()<childNodeIterator()) { lst.append(n); if (n->numChildren() > 0) { lst += flatNodeList(n); } } return lst; } void Project::setSchedulerPlugins(const QMap &plugins) { m_schedulerPlugins = plugins; debugPlan< Project::taskModules(bool includeLocal) const { if (!includeLocal && m_useLocalTaskModules) { QList lst = m_taskModules; lst.removeAll(m_localTaskModulesPath); return lst; } return m_taskModules; } void Project::setTaskModules(const QList modules, bool useLocalTaskModules) { m_taskModules = modules; m_useLocalTaskModules = useLocalTaskModules; if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) { m_taskModules.prepend(m_localTaskModulesPath); } emit taskModulesChanged(m_taskModules); } bool Project::useLocalTaskModules() const { return m_useLocalTaskModules; } void Project::setUseLocalTaskModules(bool value, bool emitChanged) { if (m_useLocalTaskModules) { m_taskModules.removeAll(m_localTaskModulesPath); } m_useLocalTaskModules = value; if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) { m_taskModules.prepend(m_localTaskModulesPath); } if (emitChanged) { emit taskModulesChanged(m_taskModules); } } void Project::setLocalTaskModulesPath(const QUrl &url) { m_taskModules.removeAll(m_localTaskModulesPath); m_localTaskModulesPath = url; if (m_useLocalTaskModules && url.isValid()) { m_taskModules.prepend(url); } emit taskModulesChanged(m_taskModules); } } //KPlato namespace diff --git a/src/libs/kernel/kptresourcerequest.cpp b/src/libs/kernel/kptresourcerequest.cpp index 6b7b817c..cdff4488 100644 --- a/src/libs/kernel/kptresourcerequest.cpp +++ b/src/libs/kernel/kptresourcerequest.cpp @@ -1,1255 +1,903 @@ /* This file is part of the KDE project * Copyright (C) 2001 Thomas Zander zander@kde.org * Copyright (C) 2004-2007 Dag Andersen * Copyright (C) 2011 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptresourcerequest.h" #include "ResourceGroup.h" #include "Resource.h" #include "kptlocale.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include #include using namespace KPlato; ResourceRequest::ResourceRequest(Resource *resource, int units) : m_id(0), m_resource(resource), m_units(units), m_collection(nullptr), - m_parent(nullptr), m_dynamic(false) { if (resource) { m_required = resource->requiredResources(); } //debugPlan<<"("<name() : QString("None")); } ResourceRequest::ResourceRequest(const ResourceRequest &r) : m_id(r.m_id), m_resource(r.m_resource), m_units(r.m_units), m_collection(nullptr), - m_parent(nullptr), m_dynamic(r.m_dynamic), m_required(r.m_required) { } ResourceRequest::~ResourceRequest() { if (m_resource) { m_resource->unregisterRequest(this); } m_resource = 0; - if (m_parent) { - m_parent->removeResourceRequest(this); - } if (m_collection && m_collection->contains(this)) { m_collection->removeResourceRequest(this); } } ResourceRequestCollection *ResourceRequest::collection() const { return m_collection; } void ResourceRequest::setCollection(ResourceRequestCollection *collection) { m_collection = collection; } int ResourceRequest::id() const { return m_id; } void ResourceRequest::setId(int id) { m_id = id; } void ResourceRequest::registerRequest() { if (m_resource) m_resource->registerRequest(this); } void ResourceRequest::unregisterRequest() { if (m_resource) m_resource->unregisterRequest(this); } bool ResourceRequest::load(KoXmlElement &element, Project &project) { debugPlanXml<id()); me.setAttribute("units", QString::number(m_units)); } int ResourceRequest::units() const { //debugPlan<name()<<": units="<task() : 0; + return m_collection ? m_collection->task() : 0; } void ResourceRequest::changed() { if (task()) { task()->changed(Node::ResourceRequestProperty); } } void ResourceRequest::setCurrentSchedulePtr(Schedule *ns) { setCurrentSchedulePtr(m_resource, ns); } void ResourceRequest::setCurrentSchedulePtr(Resource *resource, Schedule *ns) { resource->setCurrentSchedulePtr(resourceSchedule(ns, resource)); if(resource->type() == Resource::Type_Team) { foreach (Resource *member, resource->teamMembers()) { member->setCurrentSchedulePtr(resourceSchedule(ns, member)); } } foreach (Resource *r, m_required) { r->setCurrentSchedulePtr(resourceSchedule(ns, r)); } } Schedule *ResourceRequest::resourceSchedule(Schedule *ns, Resource *res) { if (ns == 0) { return 0; } Resource *r = res == 0 ? resource() : res; Schedule *s = r->findSchedule(ns->id()); if (s == 0) { s = r->createSchedule(ns->parent()); } s->setCalculationMode(ns->calculationMode()); s->setAllowOverbookingState(ns->allowOverbookingState()); static_cast(s)->setNodeSchedule(ns); //debugPlan<name()<<": id="<id()<<" mode="<calculationMode(); return s; } DateTime ResourceRequest::workTimeAfter(const DateTime &dt, Schedule *ns) { if (m_resource->type() == Resource::Type_Work) { DateTime t = availableAfter(dt, ns); foreach (Resource *r, m_required) { if (! t.isValid()) { break; } t = r->availableAfter(t, DateTime(), resourceSchedule(ns, r)); } return t; } else if (m_resource->type() == Resource::Type_Team) { return availableAfter(dt, ns); } return DateTime(); } DateTime ResourceRequest::workTimeBefore(const DateTime &dt, Schedule *ns) { if (m_resource->type() == Resource::Type_Work) { DateTime t = availableBefore(dt, ns); foreach (Resource *r, m_required) { if (! t.isValid()) { break; } t = r->availableBefore(t, DateTime(), resourceSchedule(ns, r)); } return t; } else if (m_resource->type() == Resource::Type_Team) { return availableBefore(dt, ns); } return DateTime(); } DateTime ResourceRequest::availableFrom() { DateTime dt = m_resource->availableFrom(); if (! dt.isValid()) { dt = m_resource->project()->constraintStartTime(); } return dt; } DateTime ResourceRequest::availableUntil() { DateTime dt = m_resource->availableUntil(); if (! dt.isValid()) { dt = m_resource->project()->constraintEndTime(); } return dt; } DateTime ResourceRequest::availableAfter(const DateTime &time, Schedule *ns) { if (m_resource->type() == Resource::Type_Team) { DateTime t;// = m_resource->availableFrom(); foreach (Resource *r, m_resource->teamMembers()) { setCurrentSchedulePtr(r, ns); DateTime x = r->availableAfter(time); if (x.isValid()) { t = t.isValid() ? qMin(t, x) : x; } } return t; } setCurrentSchedulePtr(ns); return m_resource->availableAfter(time); } DateTime ResourceRequest::availableBefore(const DateTime &time, Schedule *ns) { if (m_resource->type() == Resource::Type_Team) { DateTime t; foreach (Resource *r, m_resource->teamMembers()) { setCurrentSchedulePtr(r, ns); DateTime x = r->availableBefore(time); if (x.isValid()) { t = t.isValid() ? qMax(t, x) : x; } } return t; } setCurrentSchedulePtr(ns); return resource()->availableBefore(time); } Duration ResourceRequest::effort(const DateTime &time, const Duration &duration, Schedule *ns, bool backward) { - setCurrentSchedulePtr(ns); - Duration e = m_resource->effort(time, duration, m_units, backward, m_required); + Duration e; + if (m_resource->type() == Resource::Type_Team) { + for (ResourceRequest *rr : teamMembers()) { + e += rr->effort(time, duration, ns, backward); + } + } else { + setCurrentSchedulePtr(ns); + e = m_resource->effort(time, duration, m_units, backward, m_required); + } //debugPlan<name()<makeAppointment(ns, (m_resource->units() * m_units / 100), m_required); } } void ResourceRequest::makeAppointment(Schedule *ns, int amount) { if (m_resource) { setCurrentSchedulePtr(ns); m_resource->makeAppointment(ns, amount, m_required); } } long ResourceRequest::allocationSuitability(const DateTime &time, const Duration &duration, Schedule *ns, bool backward) { setCurrentSchedulePtr(ns); return resource()->allocationSuitability(time, duration, backward); } QList ResourceRequest::teamMembers() const { qDeleteAll(m_teamMembers); m_teamMembers.clear(); if (m_resource->type() == Resource::Type_Team) { foreach (Resource *r, m_resource->teamMembers()) { m_teamMembers << new ResourceRequest(r, m_units); } } return m_teamMembers; } -///////// -ResourceGroupRequest::ResourceGroupRequest(ResourceGroup *group, int units) - : m_id(0) - , m_group(group) - , m_units(units) - , m_parent(nullptr) +QList ResourceRequest::alternativeRequests() const { - //debugPlan<<"Request to:"<<(group ? group->name() : QString("None")); - if (group) - group->registerRequest(this); + return m_alternativeRequests; } -ResourceGroupRequest::ResourceGroupRequest(const ResourceGroupRequest &g) - : m_id(g.m_id) - , m_group(g.m_group) - , m_units(g.m_units) - , m_parent(0) +void ResourceRequest::setAlternativeRequests(const QList requests) { -} - -ResourceGroupRequest::~ResourceGroupRequest() { - //debugPlan; - if (m_group) { - m_group->unregisterRequest(this); - } - for (ResourceRequest *r : m_resourceRequests) { - r->setParent(nullptr); + for (ResourceRequest *r : m_alternativeRequests) { + removeAlternativeRequest(r); } - if (m_parent && m_parent->contains(this)) { - m_parent->takeRequest(this); + for (ResourceRequest *r : requests) { + addAlternativeRequest(r); } } -int ResourceGroupRequest::id() const +bool ResourceRequest::addAlternativeRequest(ResourceRequest *request) { - return m_id; -} - -void ResourceGroupRequest::setId(int id) -{ - m_id = id; -} - -void ResourceGroupRequest::addResourceRequest(ResourceRequest *request) { - //debugPlan<<"("<setParent(this); - m_resourceRequests.append(request); - changed(); -} - -ResourceRequest *ResourceGroupRequest::takeResourceRequest(ResourceRequest *request) { - if (request) - request->unregisterRequest(); - ResourceRequest *r = 0; - int i = m_resourceRequests.indexOf(request); - if (i != -1) { - r = m_resourceRequests.takeAt(i); - } - changed(); - return r; -} - -ResourceRequest *ResourceGroupRequest::find(const Resource *resource) const { - foreach (ResourceRequest *gr, m_resourceRequests) { - if (gr->resource() == resource) { - return gr; - } - } - return 0; -} - -ResourceRequest *ResourceGroupRequest::resourceRequest(const QString &name) { - foreach (ResourceRequest *r, m_resourceRequests) { - if (r->resource()->name() == name) - return r; - } - return 0; -} - -QStringList ResourceGroupRequest::requestNameList(bool includeGroup) const { - QStringList lst; - if (includeGroup && m_units > 0 && m_group) { - lst << m_group->name(); - } - foreach (ResourceRequest *r, m_resourceRequests) { - if (! r->isDynamicallyAllocated()) { - Q_ASSERT(r->resource()); - lst << r->resource()->name(); - } - } - return lst; -} - -QList ResourceGroupRequest::requestedResources() const -{ - QList lst; - foreach (ResourceRequest *r, m_resourceRequests) { - if (! r->isDynamicallyAllocated()) { - Q_ASSERT(r->resource()); - lst << r->resource(); - } - } - return lst; -} - -QList ResourceGroupRequest::resourceRequests(bool resolveTeam) const -{ - QList lst; - foreach (ResourceRequest *rr, m_resourceRequests) { - if (resolveTeam && rr->resource()->type() == Resource::Type_Team) { - lst += rr->teamMembers(); - } else { - lst << rr; - } - } - return lst; -} - -bool ResourceGroupRequest::load(KoXmlElement &element, XMLLoaderObject &status) { - debugPlanXml<registerRequest(this); - - KoXmlNode n = element.firstChild(); - for (; ! n.isNull(); n = n.nextSibling()) { - if (! n.isElement()) { - continue; - } - KoXmlElement e = n.toElement(); - if (e.tagName() == "resource-request") { - ResourceRequest *r = new ResourceRequest(); - if (r->load(e, status.project())) - addResourceRequest(r); - else { - errorPlan<<"Failed to load resource request"; - delete r; - } - } - } - // meaning of m_units changed - // Pre 0.6.6 the number *included* all requests, now it is in *addition* to resource requests - m_units = element.attribute("units").toInt(); - if (status.version() < "0.6.6") { - int x = m_units - m_resourceRequests.count(); - m_units = x > 0 ? x : 0; - } + emitAlternativeRequestToBeAdded(this, m_alternativeRequests.count()); + m_alternativeRequests.append(request); + emitAlternativeRequestAdded(request); return true; } -void ResourceGroupRequest::save(QDomElement &element) const { - QDomElement me = element.ownerDocument().createElement("resourcegroup-request"); - element.appendChild(me); - me.setAttribute("group-id", m_group->id()); - me.setAttribute("units", QString::number(m_units)); -} - -int ResourceGroupRequest::units() const { - return m_units; -} - -Duration ResourceGroupRequest::duration(const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) { - Duration dur; - if (m_parent) { - dur = m_parent->duration(m_resourceRequests, time, _effort, ns, backward); - } - return dur; -} - -DateTime ResourceGroupRequest::workTimeAfter(const DateTime &time, Schedule *ns) { - DateTime start; - if (m_resourceRequests.isEmpty()) { - return start; - } - foreach (ResourceRequest *r, m_resourceRequests) { - DateTime t = r->workTimeAfter(time, ns); - if (t.isValid() && (!start.isValid() || t < start)) - start = t; - } - if (start.isValid() && start < time) - start = time; - //debugPlan<workTimeBefore(time, ns); - if (t.isValid() && (!end.isValid() ||t > end)) - end = t; - } - if (!end.isValid() || end > time) - end = time; - return end; -} - -DateTime ResourceGroupRequest::availableAfter(const DateTime &time, Schedule *ns) { - DateTime start; - if (m_resourceRequests.isEmpty()) { - return start; - } - foreach (ResourceRequest *r, m_resourceRequests) { - DateTime t = r->availableAfter(time, ns); - if (t.isValid() && (!start.isValid() || t < start)) - start = t; - } - if (start.isValid() && start < time) - start = time; - //debugPlan<name(); - return start; -} - -DateTime ResourceGroupRequest::availableBefore(const DateTime &time, Schedule *ns) { - DateTime end; - if (m_resourceRequests.isEmpty()) { - return end; - } - foreach (ResourceRequest *r, m_resourceRequests) { - DateTime t = r->availableBefore(time, ns); - if (t.isValid() && (!end.isValid() || t > end)) - end = t; - } - if (!end.isValid() || end > time) - end = time; - //debugPlan<name(); - return end; -} - -void ResourceGroupRequest::makeAppointments(Schedule *schedule) { - //debugPlan; - foreach (ResourceRequest *r, m_resourceRequests) { - r->makeAppointment(schedule); - } -} - -void ResourceGroupRequest::reserve(const DateTime &start, const Duration &duration) { - m_start = start; - m_duration = duration; -} - -bool ResourceGroupRequest::isEmpty() const { - return m_resourceRequests.isEmpty() && m_units == 0; -} - -Task *ResourceGroupRequest::task() const { - return m_parent ? m_parent->task() : 0; -} - -void ResourceGroupRequest::changed() +bool ResourceRequest::removeAlternativeRequest(ResourceRequest *request) { - if (m_parent) - m_parent->changed(); + emitAlternativeRequestToBeRemoved(this, m_alternativeRequests.indexOf(request), request); + m_alternativeRequests.removeAll(request); + emitAlternativeRequestRemoved(); + return true; } -void ResourceGroupRequest::removeResourceRequest(ResourceRequest *request) +void ResourceRequest::emitAlternativeRequestToBeAdded(ResourceRequest *request, int row) { - m_resourceRequests.removeAll(request); - changed(); + if (m_collection) { + emit m_collection->alternativeRequestToBeAdded(request, row); + } } -void ResourceGroupRequest::deleteResourceRequest(ResourceRequest *request) +void ResourceRequest::emitAlternativeRequestAdded(ResourceRequest *alternative) { - int i = m_resourceRequests.indexOf(request); - if (i != -1) { - m_resourceRequests.removeAt(i); + if (m_collection) { + emit m_collection->alternativeRequestAdded(alternative); } - changed(); } -void ResourceGroupRequest::resetDynamicAllocations() +void ResourceRequest::emitAlternativeRequestToBeRemoved(ResourceRequest *request, int row, ResourceRequest *alternative) { - QList lst; - foreach (ResourceRequest *r, m_resourceRequests) { - if (r->isDynamicallyAllocated()) { - lst << r; - } - } - while (! lst.isEmpty()) { - deleteResourceRequest(lst.takeFirst()); + if (m_collection) { + emit m_collection->alternativeRequestToBeRemoved(request, row, alternative); } } -void ResourceGroupRequest::allocateDynamicRequests(const DateTime &time, const Duration &effort, Schedule *ns, bool backward) +void ResourceRequest::emitAlternativeRequestRemoved() { - int num = m_units; - if (num <= 0) { - return; - } - if (num == m_group->numResources()) { - // TODO: allocate all + if (m_collection) { + emit m_collection->alternativeRequestRemoved(); } - Duration e = effort / m_units; - QMap map; - foreach (Resource *r, m_group->resources()) { - if (r->type() == Resource::Type_Team) { - continue; - } - ResourceRequest *rr = find(r); - if (rr) { - if (rr->isDynamicallyAllocated()) { - --num; // already allocated - } - continue; - } - rr = new ResourceRequest(r, r->units()); - long s = rr->allocationSuitability(time, e, ns, backward); - if (s == 0) { - // not suitable at all - delete rr; - } else { - map.insertMulti(s, rr); - } - } - for (--num; num >= 0 && ! map.isEmpty(); --num) { - long key = map.lastKey(); - ResourceRequest *r = map.take(key); - r->setAllocatedDynaically(true); - addResourceRequest(r); - debugPlan<setCollection(nullptr); } - for (ResourceGroupRequest *r : m_groupRequests) { - r->setParent(nullptr); - } qDeleteAll(m_resourceRequests); // removes themselves from possible group - qDeleteAll(m_groupRequests); -} - -bool ResourceRequestCollection::contains(ResourceGroupRequest *request) const -{ - return m_groupRequests.contains(request->id()) && m_groupRequests.value(request->id()) == request; } bool ResourceRequestCollection::contains(ResourceRequest *request) const { return m_resourceRequests.contains(request->id()) && m_resourceRequests.value(request->id()) == request; } void ResourceRequestCollection::removeRequests() { const QList resourceRequests = m_resourceRequests.values(); for (ResourceRequest *r : resourceRequests) { removeResourceRequest(r); } - const QList groupRequests = m_groupRequests.values(); - for (ResourceGroupRequest *r : groupRequests) { - takeRequest(r); - } } Task *ResourceRequestCollection::task() const { return m_task; } void ResourceRequestCollection::setTask(Task *t) { m_task = t; } -void ResourceRequestCollection::addRequest(ResourceGroupRequest *request) +void ResourceRequestCollection::addResourceRequest(ResourceRequest *request) { - Q_ASSERT(request->group()); - foreach (ResourceGroupRequest *r, m_groupRequests) { - if (r->group() == request->group()) { - errorPlan<<"Request to this group already exists"; - errorPlan<<"Task:"<name()<<"Group:"<group()->name(); - Q_ASSERT(false); - } - } - int id = request->id(); - m_lastGroupId = std::max(m_lastGroupId, id); - if (id == 0) { - while (m_groupRequests.contains(++m_lastGroupId)) { - } - request->setId(m_lastGroupId); - } - Q_ASSERT(!m_groupRequests.contains(request->id())); - m_groupRequests.insert(request->id(), request); - if (!request->group()->requests().contains(request)) { - request->group()->registerRequest(request); - } - request->setParent(this); - changed(); -} - -int ResourceRequestCollection::takeRequest(ResourceGroupRequest *request) -{ - Q_ASSERT(m_groupRequests.contains(request->id())); - Q_ASSERT(m_groupRequests.value(request->id()) == request); - int i = m_groupRequests.values().indexOf(request); - m_groupRequests.remove(request->id()); - changed(); - return i; -} - -ResourceGroupRequest *ResourceRequestCollection::groupRequest(int id) const -{ - return m_groupRequests.value(id); -} - -ResourceGroupRequest *ResourceRequestCollection::find(const ResourceGroup *group) const { - foreach (ResourceGroupRequest *r, m_groupRequests) { - if (r->group() == group) - return r; // we assume only one request to the same group - } - return 0; -} - -void ResourceRequestCollection::addResourceRequest(ResourceRequest *request, ResourceGroupRequest *group) -{ - if (group) { - Q_ASSERT(m_groupRequests.contains(group->id())); - } int id = request->id(); m_lastResourceId = std::max(m_lastResourceId, id); Q_ASSERT(!m_resourceRequests.contains(id)); if (id == 0) { while (m_resourceRequests.contains(++m_lastResourceId)) { } request->setId(m_lastResourceId); } Q_ASSERT(!m_resourceRequests.contains(request->id())); request->setCollection(this); request->registerRequest(); m_resourceRequests.insert(request->id(), request); - - if (group && !group->resourceRequests().contains(request)) { - group->addResourceRequest(request); - } } void ResourceRequestCollection::removeResourceRequest(ResourceRequest *request) { Q_ASSERT(m_resourceRequests.contains(request->id())); Q_ASSERT(m_resourceRequests.values().contains(request)); m_resourceRequests.remove(request->id()); Q_ASSERT(!m_resourceRequests.values().contains(request)); - if (request->parent()) { - request->parent()->takeResourceRequest(request); - } } void ResourceRequestCollection::deleteResourceRequest(ResourceRequest *request) { delete request; } ResourceRequest *ResourceRequestCollection::find(const Resource *resource) const { for (ResourceRequest *r : m_resourceRequests) { if (r->resource() == resource) { return r; } } return nullptr; } ResourceRequest *ResourceRequestCollection::resourceRequest(int id) const { return m_resourceRequests.value(id); } ResourceRequest *ResourceRequestCollection::resourceRequest(const QString &name) const { for (ResourceRequest *r : m_resourceRequests) { if (r->resource()->name() == name) { return r; } } return nullptr; } -QStringList ResourceRequestCollection::requestNameList(bool includeGroup) const { +QStringList ResourceRequestCollection::requestNameList() const +{ QStringList lst; - foreach (ResourceGroupRequest *r, m_groupRequests) { - lst << r->requestNameList(includeGroup); + for (ResourceRequest *r : m_resourceRequests) { + lst << r->resource()->name(); } return lst; } QList ResourceRequestCollection::requestedResources() const { QList lst; - foreach (ResourceGroupRequest *g, m_groupRequests) { - lst += g->requestedResources(); + for (ResourceRequest *r : m_resourceRequests) { + lst += r->resource(); } return lst; } QList ResourceRequestCollection::resourceRequests(bool resolveTeam) const { - // FIXME: skip going through groups, probably separate method for resolveTeam case? - if (!resolveTeam) { - return m_resourceRequests.values(); - } - QList lst; - foreach (ResourceGroupRequest *g, m_groupRequests) { - foreach (ResourceRequest *r, g->resourceRequests(resolveTeam)) { - lst << r; + QList lst = m_resourceRequests.values(); + if (resolveTeam) { + for (ResourceRequest *r : m_resourceRequests) { + lst += r->teamMembers(); } } return lst; } -bool ResourceRequestCollection::contains(const QString &identity) const { +bool ResourceRequestCollection::contains(const QString &identity) const +{ QStringList lst = requestNameList(); return lst.indexOf(QRegExp(identity, Qt::CaseSensitive, QRegExp::FixedString)) != -1; } -ResourceGroupRequest *ResourceRequestCollection::findGroupRequestById(const QString &id) const -{ - foreach (ResourceGroupRequest *r, m_groupRequests) { - if (r->group()->id() == id) { - return r; - } - } - return 0; -} - // bool ResourceRequestCollection::load(KoXmlElement &element, Project &project) { // //debugPlan; // return true; // } void ResourceRequestCollection::save(QDomElement &element) const { //debugPlan; - foreach (ResourceGroupRequest *r, m_groupRequests) { + for (ResourceRequest *r : m_resourceRequests) { r->save(element); } } // Returns the duration needed by the working resources // "Material type" of resourcegroups does not (atm) affect the duration. Duration ResourceRequestCollection::duration(const DateTime &time, const Duration &effort, Schedule *ns, bool backward) { //debugPlan<<"time="<workTimeBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } DateTime ResourceRequestCollection::availableAfter(const DateTime &time, Schedule *ns) { DateTime start; - foreach (ResourceGroupRequest *r, m_groupRequests) { + // TODO: Alternatives + for (ResourceRequest *r : m_resourceRequests) { DateTime t = r->availableAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<availableBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } DateTime ResourceRequestCollection::workStartAfter(const DateTime &time, Schedule *ns) { DateTime start; - foreach (ResourceGroupRequest *r, m_groupRequests) { - if (r->group()->type() != ResourceGroup::Type_Work) { + // TODO: Alternatives + for (ResourceRequest *r : m_resourceRequests) { + if (r->resource()->type() != Resource::Type_Work) { continue; } DateTime t = r->availableAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<group()->type() != ResourceGroup::Type_Work) { + // TODO: Alternatives + for (ResourceRequest *r : m_resourceRequests) { + if (r->resource()->type() != Resource::Type_Work) { continue; } DateTime t = r->availableBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } void ResourceRequestCollection::makeAppointments(Schedule *schedule) { //debugPlan; - foreach (ResourceGroupRequest *r, m_groupRequests) { - r->makeAppointments(schedule); + // TODO: Alternatives + for (ResourceRequest *r : m_resourceRequests) { + r->makeAppointment(schedule); } } void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) { //debugPlan; - foreach (ResourceGroupRequest *r, m_groupRequests) { - r->reserve(start, duration); + // TODO: Alternatives + for (ResourceRequest *r : m_resourceRequests) { +// r->reserve(start, duration); //FIXME } } bool ResourceRequestCollection::isEmpty() const { - foreach (ResourceGroupRequest *r, m_groupRequests) { - if (!r->isEmpty()) - return false; - } - return true; + return m_resourceRequests.isEmpty(); } void ResourceRequestCollection::changed() { //debugPlan<changed(Node::ResourceRequestProperty); } } void ResourceRequestCollection::resetDynamicAllocations() { - foreach (ResourceGroupRequest *g, m_groupRequests) { - g->resetDynamicAllocations(); - } + //TODO } Duration ResourceRequestCollection::effort(const QList &lst, const DateTime &time, const Duration &duration, Schedule *ns, bool backward) const { Duration e; foreach (ResourceRequest *r, lst) { e += r->effort(time, duration, ns, backward); //debugPlan<<(backward?"(B)":"(F)")<availableUntil(); if (!t2.isValid() || t2 < t1) t2 = t1; } //debugPlan<<"fw"< &lst, const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<resource()->name(); } ns->logDebug("Match effort:" + time.toString() + "," + _effort.toString()); ns->logDebug("Resources: " + (nl.isEmpty() ? QString("None") : nl.join(", "))); } #endif QLocale locale; Duration e; if (_effort == Duration::zeroDuration) { return e; } DateTime logtime = time; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration e1; int nDays = numDays(lst, time, backward) + 1; int day = 0; for (day=0; !match && day <= nDays; ++day) { // days end = end.addDays(inc); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); //debugPlan<<"["<logDebug("Days: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else { if (false/*roundToHour*/ && (_effort - e) < (e + e1 - _effort)) { end = start; match = true; } else { end = start; } break; } //debugPlan<<"duration(h)["< _effort) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<logDebug("Minutes: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif logtime = start; for (int i=0; !match && i < 60; ++i) { //seconds end = end.addSecs(inc); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else if (e + e1 > _effort) { end = start; break; } //debugPlan<<"duration(s)["<logDebug("Seconds: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else if (e + e1 > _effort) { break; } //debugPlan<<"duration(ms)["<logError(i18n("Could not match effort. Want: %1 got: %2", _effort.toString(Duration::Format_Hour), e.toString(Duration::Format_Hour))); foreach (ResourceRequest *r, lst) { Resource *res = r->resource(); ns->logInfo(i18n("Resource %1 available from %2 to %3", res->name(), locale.toString(r->availableFrom(), QLocale::ShortFormat), locale.toString(r->availableUntil(), QLocale::ShortFormat))); } } DateTime t; if (e != Duration::zeroDuration) { foreach (ResourceRequest *r, lst) { DateTime tt; if (backward) { tt = r->availableAfter(end, ns); if (tt.isValid() && (! t.isValid() || tt < t)) { t = tt; } } else { tt = r->availableBefore(end, ns); if (tt.isValid() && (! t.isValid() || tt > t)) { t = tt; } } } } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<<":"<time?end-time:time-end); } QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest *rr) { if (rr) { dbg<<*rr; } else { dbg<<(void*)rr; } return dbg; } QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest &rr) { if (rr.resource()) { dbg<<"ResourceRequest["<name()<<']'; } else { dbg<<"ResourceRequest["< * Copyright (C) 2011 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTRESOURCEREQUEST_H #define KPTRESOURCEREQUEST_H #include "plankernel_export.h" #include "kptglobal.h" #include "kptduration.h" #include "kptdatetime.h" #include #include #include #include class QDomElement; /// The main namespace. namespace KPlato { class Account; class Risk; class Effort; class Appointment; class Task; class Node; class Project; class Resource; -class ResourceGroup; class ResourceRequest; -class ResourceGroupRequest; class ResourceRequestCollection; class Schedule; class ResourceSchedule; class Schedule; class XMLLoaderObject; class DateTimeInterval; class ResourceRequestCollection; class PLANKERNEL_EXPORT ResourceRequest { public: explicit ResourceRequest(Resource *resource = nullptr, int units = 1); explicit ResourceRequest(const ResourceRequest &r); ~ResourceRequest(); int id() const; void setId(int id); ResourceRequestCollection *collection() const; void setCollection(ResourceRequestCollection *collection); - ResourceGroupRequest *parent() const { return m_parent; } - void setParent(ResourceGroupRequest *parent) { m_parent = parent; } - Resource *resource() const { return m_resource; } void setResource(Resource* resource) { m_resource = resource; } bool load(KoXmlElement &element, Project &project); void save(QDomElement &element) const; /** * Get amount of requested resource units in percent */ int units() const; void setUnits(int value); void registerRequest(); void unregisterRequest(); void makeAppointment(Schedule *schedule, int amount); void makeAppointment(Schedule *schedule); Task *task() const; /// Return the datetime from when the resource is available. /// If it is not valid, the project constraint start time is used. /// For teams the earliest time for any team member is used. DateTime availableFrom(); /// Return the datetime until when the resource is available. /// If it is not valid, the project constraint end time is used. /// For teams the latest time for any team member is used. DateTime availableUntil(); Schedule *resourceSchedule(Schedule *ns, Resource *resource = 0); DateTime availableAfter(const DateTime &time, Schedule *ns); DateTime availableBefore(const DateTime &time, Schedule *ns); Duration effort(const DateTime &time, const Duration &duration, Schedule *ns, bool backward); DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0); DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0); /// Resource is allocated dynamically by the group request bool isDynamicallyAllocated() const { return m_dynamic; } /// Set resource is allocated dynamically void setAllocatedDynaically(bool dyn) { m_dynamic = dyn; } /// Return a measure of how suitable the resource is for allocation long allocationSuitability(const DateTime &time, const Duration &duration, Schedule *ns, bool backward); /// Returns a list of all the required resources that will be used in scheduling. /// Note: This list overrides the resources own list which is just used as default for allocation dialog. QList requiredResources() const { return m_required; } /// Set the list of required resources that will be used in scheduling. void setRequiredResources(const QList &lst) { m_required = lst; } -private: - friend class ResourceGroupRequest; + QList alternativeRequests() const; + void setAlternativeRequests(const QList requests); + bool addAlternativeRequest(ResourceRequest *request); + bool removeAlternativeRequest(ResourceRequest *request); + QList teamMembers() const; +private: + void emitAlternativeRequestToBeAdded(ResourceRequest *request, int row); + void emitAlternativeRequestAdded(ResourceRequest *alternative); + void emitAlternativeRequestToBeRemoved(ResourceRequest *request, int row, ResourceRequest *alternative); + void emitAlternativeRequestRemoved(); + protected: void changed(); void setCurrentSchedulePtr(Schedule *ns); void setCurrentSchedulePtr(Resource *resource, Schedule *ns); private: int m_id; Resource *m_resource; int m_units; ResourceRequestCollection *m_collection; - ResourceGroupRequest *m_parent; bool m_dynamic; QList m_required; mutable QList m_teamMembers; -#ifndef NDEBUG -public: - void printDebug(const QString& ident); -#endif -}; - -class PLANKERNEL_EXPORT ResourceGroupRequest -{ -public: - explicit ResourceGroupRequest(ResourceGroup *group = 0, int units = 0); - explicit ResourceGroupRequest(const ResourceGroupRequest &group); - ~ResourceGroupRequest(); - - int id() const; - void setId(int id); - - void setParent(ResourceRequestCollection *parent) { m_parent = parent;} - ResourceRequestCollection *parent() const { return m_parent; } - - ResourceGroup *group() const { return m_group; } - void setGroup(ResourceGroup *group) { m_group = group; } - void unregister(const ResourceGroup *group) { if (group == m_group) m_group = 0; } - /// Return a list of resource requests. - /// If @p resolveTeam is true, include the team members, - /// if @p resolveTeam is false, include the team resource itself. - QList resourceRequests(bool resolveTeam=true) const; - int count() const { return m_resourceRequests.count(); } - ResourceRequest *requestAt(int idx) const { return m_resourceRequests.value(idx); } - - ResourceRequest *takeResourceRequest(ResourceRequest *request); - ResourceRequest *find(const Resource *resource) const; - ResourceRequest *resourceRequest(const QString &name); - /// Return a list of allocated resources, allocation to group is not included by default. - QStringList requestNameList(bool includeGroup = false) const; - /// Return a list of allocated resources. - /// Allocations to groups are not included. - /// Team resources are included but *not* the team members. - /// Any dynamically allocated resource is not included. - QList requestedResources() const; - bool load(KoXmlElement &element, XMLLoaderObject &status); - void save(QDomElement &element) const; - - /// The number of requested resources - int units() const; - void setUnits(int value) { m_units = value; changed(); } - - /** - * Returns the duration needed to do the @p effort starting at @p start. - */ - Duration duration(const DateTime &start, const Duration &effort, Schedule *ns, bool backward = false); - - DateTime availableAfter(const DateTime &time, Schedule *ns); - DateTime availableBefore(const DateTime &time, Schedule *ns); - DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0); - DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0); - - /** - * Makes appointments for schedule @p schedule to the - * requested resources for the duration found in @ref duration(). - * @param schedule the schedule - */ - void makeAppointments(Schedule *schedule); - - /** - * Reserves the requested resources for the specified interval - */ - void reserve(const DateTime &start, const Duration &duration); - - bool isEmpty() const; - - Task *task() const; - - void changed(); - - /// Reset dynamic resource allocations - void resetDynamicAllocations(); - /// Allocate dynamic requests. Do nothing if already allocated. - void allocateDynamicRequests(const DateTime &time, const Duration &effort, Schedule *ns, bool backward); - - void removeResourceRequest(ResourceRequest *request); - -private: - friend ResourceRequestCollection; - void addResourceRequest(ResourceRequest *request); - void deleteResourceRequest(ResourceRequest *request); - -private: - int m_id; - ResourceGroup *m_group; - int m_units; - ResourceRequestCollection *m_parent; - - QList m_resourceRequests; - DateTime m_start; - Duration m_duration; + QList m_alternativeRequests; #ifndef NDEBUG public: void printDebug(const QString& ident); #endif }; -class PLANKERNEL_EXPORT ResourceRequestCollection +class PLANKERNEL_EXPORT ResourceRequestCollection : public QObject { + Q_OBJECT public: explicit ResourceRequestCollection(Task *task = 0); ~ResourceRequestCollection(); - bool contains(ResourceGroupRequest *request) const; bool contains(ResourceRequest *request) const; /// Remove all group- and resource requests /// Note: Does not delete void removeRequests(); - QList requests() const { return m_groupRequests.values(); } - void addRequest(ResourceGroupRequest *request); - void deleteRequest(ResourceGroupRequest *request) - { - Q_ASSERT(m_groupRequests.contains(request->id())); - Q_ASSERT(m_groupRequests.value(request->id()) == request); - m_groupRequests.remove(request->id()); - delete request; - changed(); - } - - int takeRequest(ResourceGroupRequest *request); - ResourceGroupRequest *groupRequest(int id) const; - ResourceGroupRequest *find(const ResourceGroup *resource) const; ResourceRequest *resourceRequest(int id) const; ResourceRequest *find(const Resource *resource) const; ResourceRequest *resourceRequest(const QString &name) const; /// The ResourceRequestCollection has no requests bool isEmpty() const; /// Empty the ResourceRequestCollection of all requets // void clear() { m_groupRequests.clear(); } /// Reset dynamic resource allocations void resetDynamicAllocations(); bool contains(const QString &identity) const; - ResourceGroupRequest *findGroupRequestById(const QString &id) const; /// Return a list of names of allocated resources. - /// Allocations to groups are not included by default. /// Team resources are included but *not* the team members. - /// Any dynamically allocated resource is not included. - QStringList requestNameList(bool includeGroup = false) const; + QStringList requestNameList() const; /// Return a list of allocated resources. - /// Allocations to groups are not included. /// Team resources are included but *not* the team members. - /// Any dynamically allocated resource is not included. QList requestedResources() const; /// Return a list of all resource requests. /// If @p resolveTeam is true, include the team members, /// if @p resolveTeam is false, include the team resource itself. QList resourceRequests(bool resolveTeam=true) const; /// Add the resource request @p request - /// If @p group is not nullptr, the request is also added to the @p group - void addResourceRequest(ResourceRequest *request, ResourceGroupRequest *group = nullptr); + void addResourceRequest(ResourceRequest *request); /// Remove resource request @p request void removeResourceRequest(ResourceRequest *request); /// Delete resource request @p request void deleteResourceRequest(ResourceRequest *request); //bool load(KoXmlElement &element, Project &project); void save(QDomElement &element) const; /** * Returns the duration needed to do the @p effort starting at @p time. */ Duration duration(const DateTime &time, const Duration &effort, Schedule *sch, bool backward = false); DateTime availableAfter(const DateTime &time, Schedule *ns); DateTime availableBefore(const DateTime &time, Schedule *ns); DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0) const; DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0) const; DateTime workStartAfter(const DateTime &time, Schedule *ns); DateTime workFinishBefore(const DateTime &time, Schedule *ns); /** * Makes appointments for the schedule @p schedule to the requested resources. * Assumes that @ref duration() has been run. * @param schedule the schedule */ void makeAppointments(Schedule *schedule); /** * Reserves the requested resources for the specified interval */ void reserve(const DateTime &start, const Duration &duration); Task *task() const; void setTask(Task *t); void changed(); Duration effort(const QList &lst, const DateTime &time, const Duration &duration, Schedule *ns, bool backward) const; int numDays(const QList &lst, const DateTime &time, bool backward) const; Duration duration(const QList &lst, const DateTime &time, const Duration &_effort, Schedule *ns, bool backward); +Q_SIGNALS: + void alternativeRequestToBeAdded(ResourceRequest *request, int row); + void alternativeRequestAdded(ResourceRequest *alternative); + void alternativeRequestToBeRemoved(ResourceRequest *request, int row, ResourceRequest *alternative); + void alternativeRequestRemoved(); + private: Task *m_task; - int m_lastGroupId; int m_lastResourceId; - QMap m_groupRequests; QMap m_resourceRequests; }; } //KPlato namespace PLANKERNEL_EXPORT QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest *r); PLANKERNEL_EXPORT QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest &r); #endif diff --git a/src/libs/kernel/kpttask.cpp b/src/libs/kernel/kpttask.cpp index f1080356..28e6314f 100644 --- a/src/libs/kernel/kpttask.cpp +++ b/src/libs/kernel/kpttask.cpp @@ -1,3917 +1,3891 @@ /* This file is part of the KDE project * Copyright (C) 2001 Thomas zander * Copyright (C) 2004 - 2007 Dag Andersen * Copyright (C) 2007 Florian Piquemal * Copyright (C) 2007 Alexis Ménard * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttask.h" #include "kptappointment.h" #include "kptproject.h" #include "kptduration.h" #include "kptrelation.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include #include #include namespace KPlato { Task::Task(Node *parent) : Node(parent), m_resource(), m_workPackage(this) { //debugPlan<<"("<setOptimisticRatio(-10); m_estimate->setPessimisticRatio(20); m_estimate->setParentNode(this); if (m_parent) m_leader = m_parent->leader(); } Task::Task(const Task &task, Node *parent) : Node(task, parent), m_resource(), m_workPackage(this) { //debugPlan<<"("<setParentNode(this); } Task::~Task() { while (!m_resource.isEmpty()) { delete m_resource.takeFirst(); } while (!m_parentProxyRelations.isEmpty()) { delete m_parentProxyRelations.takeFirst(); } while (!m_childProxyRelations.isEmpty()) { delete m_childProxyRelations.takeFirst(); } } int Task::type() const { if (numChildren() > 0) { return Node::Type_Summarytask; } else if (m_constraint == Node::FixedInterval) { if (m_constraintEndTime == m_constraintStartTime) { return Node::Type_Milestone; } } else if (m_estimate->expectedEstimate() == 0.0) { return Node::Type_Milestone; } return Node::Type_Task; } Duration *Task::getRandomDuration() { return 0L; } -ResourceGroupRequest *Task::resourceGroupRequest(const ResourceGroup *group) const { - return m_requests.find(group); -} - // void Task::clearResourceRequests() { // m_requests.clear(); // changed(this, ResourceRequestProperty); // } -void Task::addRequest(ResourceGroup *group, int numResources) { - addRequest(new ResourceGroupRequest(group, numResources)); -} - -void Task::addRequest(ResourceGroupRequest *request) { - //debugPlan<group()<group()->id()<group()->name(); - m_requests.addRequest(request); - changed(this, Node::ResourceRequestProperty); -} - -void Task::takeRequest(ResourceGroupRequest *request) { - //debugPlan< Task::requestedResources() const { return m_requests.requestedResources(); } bool Task::containsRequest(const QString &identity) const { return m_requests.contains(identity); } ResourceRequest *Task::resourceRequest(const QString &name) const { return m_requests.resourceRequest(name); } QStringList Task::assignedNameList(long id) const { Schedule *s = schedule(id); if (s == 0) { return QStringList(); } return s->resourceNameList(); } void Task::makeAppointments() { if (m_currentSchedule == 0) return; if (type() == Node::Type_Task) { //debugPlan<startTime<<","<endTime<<";"<duration.toString(); m_requests.makeAppointments(m_currentSchedule); //debugPlan<startTime<<","<endTime<<";"<duration.toString(); } else if (type() == Node::Type_Summarytask) { foreach (Node *n, m_nodes) { n->makeAppointments(); } } else if (type() == Node::Type_Milestone) { //debugPlan<<"Milestone not implemented"; // Well, shouldn't have resources anyway... } } void Task::copySchedule() { if (m_currentSchedule == 0 || type() != Node::Type_Task) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast(findSchedule(id)); if (ns == 0) { return; } if (type() == Node::Type_Task) { copyAppointments(ns->startTime, ns->endTime); } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; m_currentSchedule->endTime = ns->endTime; m_currentSchedule->lateFinish = ns->lateFinish; m_currentSchedule->duration = ns->duration; // TODO: status flags, etc //debugPlan; } void Task::copyAppointments() { copyAppointments(DateTime(), m_currentSchedule->startTime); } void Task::copyAppointments(const DateTime &start, const DateTime &end) { if (m_currentSchedule == 0 || type() != Node::Type_Task) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast(findSchedule(id)); if (ns == 0) { return; } DateTime st = start.isValid() ? start : ns->startTime; DateTime et = end.isValid() ? end : ns->endTime; //debugPlan<calculationMode(); foreach (const Appointment *a, ns->appointments()) { Resource *r = a->resource() == 0 ? 0 : a->resource()->resource(); if (r == 0) { errorPlan<<"No resource"; continue; } AppointmentIntervalList lst = a->intervals(st, et); if (lst.isEmpty()) { //debugPlan<<"No intervals to copy from"<appointments()) { if (c->resource()->resource() == r) { //debugPlan<<"Found current appointment to"<resource()->resource()->name()<add(curr); curr->setNode(m_currentSchedule); //debugPlan<<"Created new appointment"<(r->findSchedule(m_currentSchedule->id())); if (rs == 0) { rs = r->createSchedule(m_currentSchedule->parent()); rs->setId(m_currentSchedule->id()); rs->setName(m_currentSchedule->name()); rs->setType(m_currentSchedule->type()); //debugPlan<<"Resource schedule not found, id="<id(); } rs->setCalculationMode(m_currentSchedule->calculationMode()); if (! rs->appointments().contains(curr)) { //debugPlan<<"add to resource"<add(curr); curr->setResource(rs); } Appointment app; app.setIntervals(lst); //foreach (AppointmentInterval *i, curr->intervals()) { debugPlan<startTime().toString()<endTime().toString(); } curr->merge(app); //debugPlan<<"Appointments added"; } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; } void Task::calcResourceOverbooked() { if (m_currentSchedule) m_currentSchedule->calcResourceOverbooked(); } bool Task::load(KoXmlElement &element, XMLLoaderObject &status) { QString s; bool ok = false; m_id = element.attribute(QStringLiteral("id")); m_priority = element.attribute(QStringLiteral("priority"), "0").toInt(); setName(element.attribute(QStringLiteral("name"))); m_leader = element.attribute(QStringLiteral("leader")); m_description = element.attribute(QStringLiteral("description")); //debugPlan<load(e)) { if (!project.addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == QLatin1String("task")) { if (status.loadTaskChildren()) { // Load the task Task *child = new Task(this); if (child->load(e, status)) { if (!status.project().addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } else if (e.tagName() == QLatin1String("resource")) { // TODO: Load the resource (projects don't have resources yet) } else if (e.tagName() == QLatin1String("estimate") || (/*status.version() < "0.6" &&*/ e.tagName() == QLatin1String("effort"))) { // Load the estimate m_estimate->load(e, status); - } else if (e.tagName() == QLatin1String("resourcegroup-request")) { - // Load the resource request - // Handle multiple requests to same group gracefully (Not really allowed) - ResourceGroupRequest *r = m_requests.findGroupRequestById(e.attribute(QStringLiteral("group-id"))); - if (r) { - warnPlan<<"Multiple requests to same group, loading into existing group"; - if (! r->load(e, status)) { - errorPlan<<"Failed to load resource request"; - } - } else { - r = new ResourceGroupRequest(); - if (r->load(e, status)) { - addRequest(r); - for (ResourceRequest *rr : r->resourceRequests()) { - m_requests.addResourceRequest(rr); - } - } else { - errorPlan<<"Failed to load resource request"; - delete r; - } - } } else if (e.tagName() == QLatin1String("workpackage")) { m_workPackage.loadXML(e, status); } else if (e.tagName() == QLatin1String("progress")) { completion().loadXML(e, status); } else if (e.tagName() == QLatin1String("task-schedules") || (status.version() < "0.7.0" && e.tagName() == QLatin1String("schedules"))) { KoXmlNode n = e.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("schedule")) { NodeSchedule *sch = new NodeSchedule(); if (sch->loadXML(el, status)) { sch->setNode(this); addSchedule(sch); } else { errorPlan<<"Failed to load schedule"; delete sch; } } } + } else if (e.tagName() == QLatin1String("resourcegroup-request")) { + Q_ASSERT(status.version() < "0.7.0"); + KoXmlElement re; + forEachElement(re, e) { + if (re.tagName() == "resource-request") { + ResourceRequest *r = new ResourceRequest(); + if (r->load(re, status.project())) { + m_requests.addResourceRequest(r); + } else { + errorPlan<<"Failed to load resource request"; + delete r; + } + } + } + // TODO alternatives } else if (e.tagName() == QLatin1String("documents")) { m_documents.load(e, status); } else if (e.tagName() == QLatin1String("workpackage-log")) { KoXmlNode n = e.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("workpackage")) { WorkPackage *wp = new WorkPackage(this); if (wp->loadLoggedXML(el, status)) { m_packageLog << wp; } else { errorPlan<<"Failed to load logged workpackage"; delete wp; } } } } } //debugPlan<save(me); m_documents.save(me); if (context.saveAll(this)) { if (!m_schedules.isEmpty()) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("task-schedules")); me.appendChild(schs); foreach (const Schedule *s, m_schedules) { if (!s->isDeleted()) { s->saveXML(schs); } } } completion().saveXML(me); m_workPackage.saveXML(me); // The workpackage log if (!m_packageLog.isEmpty()) { QDomElement log = me.ownerDocument().createElement(QStringLiteral("workpackage-log")); me.appendChild(log); foreach (const WorkPackage *wp, m_packageLog) { wp->saveLoggedXML(log); } } } if (context.saveChildren(this)) { for (int i=0; isave(me, context); } } } void Task::saveAppointments(QDomElement &element, long id) const { //debugPlan<save(me); completion().saveXML(me); if (m_schedules.contains(id) && ! m_schedules[ id ]->isDeleted()) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("task-schedules")); me.appendChild(schs); m_schedules[ id ]->saveXML(schs); } m_documents.save(me); // TODO: copying documents } bool Task::isStarted() const { return completion().isStarted(); } EffortCostMap Task::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(start, end, id, typ); } return ec; } Schedule *s = schedule(id); if (s) { return s->plannedEffortCostPrDay(start, end, typ); } return EffortCostMap(); } EffortCostMap Task::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(resource, start, end, id, typ); } return ec; } Schedule *s = schedule(id); if (s) { return s->plannedEffortCostPrDay(resource, start, end, typ); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(start, end, id, typ); } return ec; } switch (completion().entrymode()) { case Completion::FollowPlan: return plannedEffortCostPrDay(start, end, id, typ); default: return completion().effortCostPrDay(start, end, id); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(resource, start, end, id, typ); } return ec; } switch (completion().entrymode()) { case Completion::FollowPlan: return plannedEffortCostPrDay(resource, start, end, id, typ); default: return completion().effortCostPrDay(resource, start, end); } return EffortCostMap(); } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort(const Resource *resource, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort(resource, id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffort(resource, typ); } return eff; } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort(long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort(id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffort(typ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort(const Resource *resource, QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort(resource, date, id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffort(resource, date, typ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort(date, id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffort(date, typ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo(date, id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffortTo(date, typ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo(const Resource *resource, QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo(resource, date, id, typ); } return eff; } Schedule *s = schedule(id); if (s) { eff = s->plannedEffortTo(resource, date, typ); } return eff; } // Returns the total actual effort for this task (or subtasks) Duration Task::actualEffort() const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort(); } } return completion().actualEffort(); } // Returns the total actual effort for this task (or subtasks) on date Duration Task::actualEffort(QDate date) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort(date); } return eff; } return completion().actualEffort(date); } // Returns the total actual effort for this task (or subtasks) to date Duration Task::actualEffortTo(QDate date) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffortTo(date); } return eff; } return completion().actualEffortTo(date); } EffortCost Task::plannedCost(long id, EffortCostCalculationType typ) const { //debugPlan; if (type() == Node::Type_Summarytask) { return Node::plannedCost(id, typ); } EffortCost c; Schedule *s = schedule(id); if (s) { c = s->plannedCost(typ); } return c; } double Task::plannedCostTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; double c = 0; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { c += n->plannedCostTo(date, id, typ); } return c; } Schedule *s = schedule(id); if (s == 0) { return c; } c = s->plannedCostTo(date, typ); if (date >= s->startTime.date()) { c += m_startupCost; } if (date >= s->endTime.date()) { c += m_shutdownCost; } return c; } EffortCost Task::actualCostTo(long int id, QDate date) const { //debugPlan; EffortCostMap ecm = acwp(id); return EffortCost(ecm.effortTo(date), ecm.costTo(date)); } double Task::bcws(QDate date, long id) const { //debugPlan; double c = plannedCostTo(date, id); //debugPlan<bcwsPrDayCache(typ); if (! cache.cached) { EffortCostMap ec = s->bcwsPrDay(typ); if (typ != ECCT_Work) { if (m_startupCost > 0.0) { ec.add(s->startTime.date(), Duration::zeroDuration, m_startupCost); } if (m_shutdownCost > 0.0) { ec.add(s->endTime.date(), Duration::zeroDuration, m_shutdownCost); } cache.effortcostmap = ec; cache.cached = true; } } return cache.effortcostmap; } EffortCostMap Task::bcwpPrDay(long int id, EffortCostCalculationType typ) { //debugPlan; if (type() == Node::Type_Summarytask) { return Node::bcwpPrDay(id, typ); } Schedule *s = schedule(id); if (s == 0) { return EffortCostMap(); } EffortCostCache cache = s->bcwpPrDayCache(typ); if (! cache.cached) { // do not use bcws cache, it includes startup/shutdown cost EffortCostMap e = s->plannedEffortCostPrDay(s->appointmentStartTime().date(), s->appointmentEndTime().date(), typ); if (completion().isStarted() && ! e.isEmpty()) { // calculate bcwp on bases of bcws *without* startup/shutdown cost double totEff = e.totalEffort().toDouble(Duration::Unit_h); double totCost = e.totalCost(); QDate sd = completion().entries().keys().value(0); if (! sd.isValid() || e.startDate() < sd) { sd = e.startDate(); } QDate ed = qMax(e.endDate(), completion().entryDate()); for (QDate d = sd; d <= ed; d = d.addDays(1)) { double p = (double)(completion().percentFinished(d)) / 100.0; EffortCost ec = e.days()[ d ]; ec.setBcwpEffort(totEff * p); ec.setBcwpCost(totCost * p); e.insert(d, ec); } } if (typ != ECCT_Work) { // add bcws startup/shutdown cost if (m_startupCost > 0.0) { e.add(s->startTime.date(), Duration::zeroDuration, m_startupCost); } if (m_shutdownCost > 0.0) { e.add(s->endTime.date(), Duration::zeroDuration, m_shutdownCost); } // add bcwp startup/shutdown cost if (m_shutdownCost > 0.0 && completion().finishIsValid()) { QDate finish = completion().finishTime().date(); e.addBcwpCost(finish, m_shutdownCost); debugPlan<<"addBcwpCost:"< finish) { e.addBcwpCost(date, m_shutdownCost); debugPlan<<"addBcwpCost:"< 0.0 && completion().startIsValid()) { QDate start = completion().startTime().date(); e.addBcwpCost(start, m_startupCost); // bcwp is cumulative so add to all entries after start for (EffortCostDayMap::const_iterator it = e.days().constBegin(); it != e.days().constEnd(); ++it) { const QDate date = it.key(); if (date > start) { e.addBcwpCost(date, m_startupCost); } } } } cache.effortcostmap = e; cache.cached = true; } return cache.effortcostmap; } Duration Task::budgetedWorkPerformed(QDate date, long id) const { //debugPlan; Duration e; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed(date, id); } return e; } e = plannedEffort(id) * (double)completion().percentFinished(date) / 100.0; //debugPlan<budgetedCostPerformed(date, id); } return c; } c = plannedCost(id).cost() * (double)completion().percentFinished(date) / 100.0; if (completion().isStarted() && date >= completion().startTime().date()) { c += m_startupCost; } if (completion().isFinished() && date >= completion().finishTime().date()) { c += m_shutdownCost; } //debugPlan<acwpCache(typ); if (! ec.cached) { //debugPlan<= completion().startTime().date()) { c.add(Duration::zeroDuration, m_startupCost); } if (completion().isFinished() && date >= completion().finishTime().date()) { c.add(Duration::zeroDuration, m_shutdownCost); } return c; } double Task::schedulePerformanceIndex(QDate date, long id) const { //debugPlan; double r = 1.0; double s = bcws(date, id); double p = bcwp(date, id); if (s > 0.0) { r = p / s; } return r; } double Task::effortPerformanceIndex(QDate date, long id) const { //debugPlan; double r = 1.0; Duration a, b; if (m_estimate->type() == Estimate::Type_Effort) { Duration b = budgetedWorkPerformed(date, id); if (b == Duration::zeroDuration) { return r; } Duration a = actualEffortTo(date); if (b == Duration::zeroDuration) { return 1.0; } r = b.toDouble() / a.toDouble(); } else if (m_estimate->type() == Estimate::Type_Duration) { //TODO } return r; } //FIXME Handle summarytasks double Task::costPerformanceIndex(long int id, QDate date, bool *error) const { double res = 0.0; double ac = actualCostTo(id, date).cost(); bool e = (ac == 0.0 || completion().percentFinished() == 0); if (error) { *error = e; } if (!e) { res = (plannedCostTo(date, id) * completion().percentFinished()) / (100 * ac); } return res; } void Task::initiateCalculation(MainSchedule &sch) { //debugPlan< &list_, int use) { DateTime time; // do them forward QMultiMap lst; for (Relation* r : list_) { lst.insert(-r->parent()->priority(), r); } const QList list = lst.values(); foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->parent()->calculateForward(use); // early finish switch (r->type()) { case Relation::StartStart: // I can't start earlier than my predesseccor t = r->parent()->earlyStart() + r->lag(); break; case Relation::FinishFinish: { // I can't finish earlier than my predeccessor, so // I can't start earlier than it's (earlyfinish+lag)- my duration t += r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState(Schedule::OBS_Allow); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug(QStringLiteral("FinishFinish: get duration to calculate early finish")); #endif t -= duration(t, use, true); m_currentSchedule->setAllowOverbookingState(obs); break; } default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<earlyFinish; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode(Schedule::CalculateForward); //cs->logDebug("calculateForward: earlyStart=" + cs->earlyStart.toString()); // calculate all predecessors if (!dependParentNodes().isEmpty()) { DateTime time = calculatePredeccessors(dependParentNodes(), use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug(QString("calculate forward: early start moved to: %1").arg(cs->earlyStart.toString())); } } if (!m_parentProxyRelations.isEmpty()) { DateTime time = calculatePredeccessors(m_parentProxyRelations, use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug(QString("calculate forward: early start moved to: %1").arg(cs->earlyStart.toString())); } } m_calculateForwardRun = true; //cs->logDebug("calculateForward: earlyStart=" + cs->earlyStart.toString()); return calculateEarlyFinish(use); } DateTime Task::calculateEarlyFinish(int use) { //debugPlan<usePert(); cs->setCalculationMode(Schedule::CalculateForward); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug(QStringLiteral("Start calculate forward: %1 ").arg(constraintToString(true))); #endif QLocale locale; cs->logInfo(i18n("Calculate early finish ")); //debugPlan<<"------>"<earlyStart; if (type() == Node::Type_Task) { m_durationForward = m_estimate->value(use, pert); switch (constraint()) { case Node::ASAP: case Node::ALAP: { //debugPlan<earlyStart; cs->earlyStart = workTimeAfter(cs->earlyStart); m_durationForward = duration(cs->earlyStart, use, false); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + m_earlyFinish.toString()); #endif if (!cs->allowOverbooking()) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState(Schedule::OBS_Allow); m_durationForward = duration(cs->earlyStart, use, false); cs->setAllowOverbookingState(obs); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString()); #endif } break; } case Node::MustFinishOn: { cs->earlyStart = workTimeAfter(cs->earlyStart); m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"MustFinishOn:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } cs->earlyFinish = qMax(cs->earlyFinish, m_constraintEndTime); if (!cs->allowOverbooking()) { cs->endTime = cs->earlyFinish; cs->startTime = cs->earlyFinish - duration(cs->earlyFinish, use, true); makeAppointments(); } m_earlyFinish = cs->earlyFinish; m_durationForward = m_earlyFinish - cs->earlyStart; break; } case Node::FinishNotLater: { m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"FinishNotLater:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } if (!cs->allowOverbooking()) { cs->startTime = cs->earlyStart; cs->endTime = cs->earlyFinish; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MSO/SNE:"<earlyStart; cs->logDebug(constraintToString() + ": " + m_constraintStartTime.toString() + ' ' + cs->earlyStart.toString()); cs->earlyStart = workTimeAfter(qMax(cs->earlyStart, m_constraintStartTime)); if (cs->earlyStart < m_constraintStartTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } m_durationForward = duration(cs->earlyStart, use, false); m_earlyFinish = cs->earlyStart + m_durationForward; if (!cs->allowOverbooking()) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState(Schedule::OBS_Allow); m_durationForward = duration(cs->startTime, use, false); cs->setAllowOverbookingState(obs); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("MSO/SNE earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString()); #endif } break; } case Node::FixedInterval: { if (cs->earlyStart > m_constraintStartTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } //cs->earlyStart = m_constraintStartTime; m_durationForward = m_constraintEndTime - m_constraintStartTime; if (cs->earlyStart < m_constraintStartTime) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if (!cs->allowOverbooking()) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } } } else if (type() == Node::Type_Milestone) { m_durationForward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<earlyStart; //cs->logDebug(QString("%1: %2, early start: %3").arg(constraintToString()).arg(m_constraintEndTime.toString()).arg(cs->earlyStart.toString())); if (cs->earlyStart < m_constraintEndTime) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if (cs->earlyStart > m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<earlyStart; if (cs->earlyStart > m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } m_earlyFinish = cs->earlyStart; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<earlyStart; if (cs->earlyStart < m_constraintStartTime) { m_durationForward = m_constraintStartTime - cs->earlyStart; } if (cs->earlyStart > m_constraintStartTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::StartNotEarlier: //debugPlan<<"StartNotEarlier:"<earlyStart; if (cs->earlyStart < m_constraintStartTime) { m_durationForward = m_constraintStartTime - cs->earlyStart; } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FixedInterval: m_earlyFinish = cs->earlyStart + m_durationForward; break; default: m_earlyFinish = cs->earlyStart + m_durationForward; break; } //debugPlan<insertForwardNode(this); cs->earlyFinish = cs->earlyStart + m_durationForward; foreach (const Appointment *a, cs->appointments(Schedule::CalculateForward)) { cs->logInfo(i18n("Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat))); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo(i18n("Early finish calculated: %1", locale.toString(cs->earlyFinish, QLocale::ShortFormat))); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug(QStringLiteral("Finished calculate forward: %1 ms").arg(timer.elapsed())); #endif return m_earlyFinish; } DateTime Task::calculateSuccessors(const QList &list_, int use) { DateTime time; QMultiMap lst; for (Relation* r : list_) { lst.insert(-r->child()->priority(), r); } const QList list = lst.values(); foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->child()->calculateBackward(use); switch (r->type()) { case Relation::StartStart: { // I must start before my successor, so // I can't finish later than it's (starttime-lag) + my duration t -= r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState(Schedule::OBS_Allow); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug(QStringLiteral("StartStart: get duration to calculate late start")); #endif t += duration(t, use, false); m_currentSchedule->setAllowOverbookingState(obs); break; } case Relation::FinishFinish: // My successor cannot finish before me, so // I can't finish later than it's latest finish - lag t = r->child()->lateFinish() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } //debugPlan<lateStart; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode(Schedule::CalculateBackward); //cs->lateFinish = projectNode()->constraintEndTime(); // calculate all successors if (!dependChildNodes().isEmpty()) { DateTime time = calculateSuccessors(dependChildNodes(), use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } if (!m_childProxyRelations.isEmpty()) { DateTime time = calculateSuccessors(m_childProxyRelations, use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } m_calculateBackwardRun = true; return calculateLateStart(use); } DateTime Task::calculateLateStart(int use) { //debugPlan<lateStart; } bool pert = cs->usePert(); cs->setCalculationMode(Schedule::CalculateBackward); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug(QStringLiteral("Start calculate backward: %1 ").arg(constraintToString(true))); #endif QLocale locale; cs->logInfo(i18n("Calculate late start")); cs->logDebug(QStringLiteral("%1: late finish= %2").arg(constraintToString()).arg(cs->lateFinish.toString())); //debugPlan<lateFinish; cs->lateFinish = workTimeBefore(cs->lateFinish); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + cs->lateStart.toString()); #endif if (!cs->allowOverbooking()) { cs->startTime = cs->lateStart; cs->endTime = cs->lateFinish; makeAppointments(); // calculate wo checking bookings = latest start possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState(Schedule::OBS_Allow); m_durationBackward = duration(cs->lateFinish, use, true); cs->setAllowOverbookingState(obs); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP latest start possible: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + (cs->lateFinish-m_durationBackward).toString()); #endif } break; case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MustStartOn:"<lateFinish; cs->lateFinish = workTimeBefore(cs->lateFinish); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; if (cs->lateStart < m_constraintStartTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } else { cs->lateStart = qMax(cs->earlyStart, m_constraintStartTime); } if (!cs->allowOverbooking()) { if (constraint() == MustStartOn) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintStartTime + duration(m_constraintStartTime, use, false); } else { cs->startTime = qMax(cs->lateStart, m_constraintStartTime); cs->endTime = qMax(cs->lateFinish, cs->startTime); // safety } makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } case Node::MustFinishOn: case Node::FinishNotLater: //debugPlan<<"MustFinishOn:"<lateFinish; cs->lateFinish = workTimeBefore(cs->lateFinish); cs->endTime = cs->lateFinish; if (cs->lateFinish < m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } else { cs->endTime = qMax(cs->earlyFinish, m_constraintEndTime); } m_durationBackward = duration(cs->endTime, use, true); cs->startTime = cs->endTime - m_durationBackward; if (!cs->allowOverbooking()) { makeAppointments(); } m_durationBackward = cs->lateFinish - cs->startTime; cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FixedInterval: { //cs->lateFinish = m_constraintEndTime; if (cs->lateFinish < m_constraintEndTime) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } m_durationBackward = m_constraintEndTime - m_constraintStartTime; if (cs->lateFinish > m_constraintEndTime) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } if (!cs->allowOverbooking()) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } } } else if (type() == Node::Type_Milestone) { m_durationBackward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<lateFinish; if (m_constraintEndTime < cs->lateFinish) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if (m_constraintEndTime > cs->lateFinish) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<lateFinish; if (m_constraintEndTime < cs->lateFinish) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if (m_constraintEndTime > cs->lateFinish) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<lateFinish; if (m_constraintStartTime < cs->lateFinish) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } else if (m_constraintStartTime > cs->lateFinish) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } cs->lateStart = cs->lateFinish - m_durationBackward; //cs->logDebug(QString("%1: constraint:%2, start=%3, finish=%4").arg(constraintToString()).arg(m_constraintStartTime.toString()).arg(cs->lateStart.toString()).arg(cs->lateFinish.toString())); break; case Node::StartNotEarlier: //debugPlan<<"MustStartOn:"<lateFinish; if (m_constraintStartTime > cs->lateFinish) { cs->logWarning(i18nc("1=type of constraint", "%1: Failed to meet constraint", constraintToString(true))); } cs->lateStart = cs->lateFinish; break; case Node::FixedInterval: cs->lateStart = cs->lateFinish - m_durationBackward; break; default: cs->lateStart = cs->lateFinish - m_durationBackward; break; } //debugPlan<lateFinish; } else if (type() == Node::Type_Summarytask) { warnPlan<<"Summarytasks should not be calculated here: "<insertBackwardNode(this); cs->lateStart = cs->lateFinish - m_durationBackward; foreach (const Appointment *a, cs->appointments(Schedule::CalculateBackward)) { cs->logInfo(i18n("Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat))); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo(i18n("Late start calculated: %1", locale.toString(cs->lateStart, QLocale::ShortFormat))); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug(QStringLiteral("Finished calculate backward: %1 ms").arg(timer.elapsed())); #endif return cs->lateStart; } DateTime Task::schedulePredeccessors(const QList &list_, int use) { DateTime time; QMultiMap lst; for (Relation* r : list_) { lst.insert(-r->parent()->priority(), r); } const QList list = lst.values(); foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } // schedule the predecessors DateTime earliest = r->parent()->earlyStart(); DateTime t = r->parent()->scheduleForward(earliest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my predesseccor t = r->parent()->startTime() + r->lag(); break; case Relation::FinishFinish: // I can't end before my predecessor, so // I can't start before it's endtime - my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug(QStringLiteral("FinishFinish: get duration to calculate earliest start")); #endif t -= duration(t + r->lag(), use, true); break; default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<endTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; //cs->logDebug(QString("Schedule forward (early start: %1)").arg(cs->earlyStart.toString())); cs->setCalculationMode(Schedule::Scheduling); DateTime startTime = earliest > cs->earlyStart ? earliest : cs->earlyStart; // First, calculate all my own predecessors DateTime time = schedulePredeccessors(dependParentNodes(), use); if (time > startTime) { startTime = time; //debugPlan<earlyStart.toString())); cs->startTime = cs->earlyStart; } QTime timer; timer.start(); cs->logInfo(i18n("Start schedule forward: %1 ", constraintToString(true))); QLocale locale; cs->logInfo(i18n("Schedule from start %1", locale.toString(cs->startTime, QLocale::ShortFormat))); //debugPlan<startTime<<"earliest:"<earlyStart; if (false/*useCalculateForwardAppointments*/ && m_estimate->type() == Estimate::Type_Effort && ! cs->allowOverbooking() && cs->hasAppointments(Schedule::CalculateForward) ) { #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString()); #endif cs->copyAppointments(Schedule::CalculateForward, Schedule::Scheduling); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); } cs->startTime = cs->appointmentStartTime(); cs->endTime = cs->appointmentEndTime(); Q_ASSERT(cs->startTime.isValid()); Q_ASSERT(cs->endTime.isValid()); cs->duration = cs->endTime - cs->startTime; if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->logInfo(i18n("Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat))); return cs->endTime; } cs->startTime = workTimeAfter(cs->startTime, cs); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString()); #endif cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } break; case Node::ALAP: // cs->startTime calculated above //debugPlan<startTime<endTime<<" latest="<lateFinish; cs->endTime = workTimeBefore(cs->lateFinish, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<endTime = workTimeBefore(cs->earlyFinish, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; makeAppointments(); } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->recalculate() && completion().isStarted()) { cs->earlyStart = cs->startTime = completion().startTime(); } break; case Node::StartNotEarlier: // cs->startTime calculated above //debugPlan<<"StartNotEarlier:"<startTime<lateStart; cs->startTime = workTimeAfter(qMax(cs->startTime, m_constraintStartTime), cs); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } break; case Node::FinishNotLater: // cs->startTime calculated above //debugPlan<<"FinishNotLater:"<startTime; cs->startTime = workTimeAfter(cs->startTime, cs); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->endTime > m_constraintEndTime) { //warnPlan<<"cs->endTime > m_constraintEndTime"; cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } break; case Node::MustStartOn: // Always try to put it on time cs->startTime = workTimeAfter(m_constraintStartTime, cs); //debugPlan<<"MustStartOn="<startTime; cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; #ifndef PLAN_NLOGDEBUG cs->logDebug(QStringLiteral("%1: Schedule from %2 to %3").arg(constraintToString()).arg(cs->startTime.toString()).arg(cs->endTime.toString())); #endif makeAppointments(); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (m_constraintStartTime < cs->startTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } break; case Node::MustFinishOn: // Just try to schedule on time cs->endTime = workTimeBefore(m_constraintEndTime, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<<"MustFinishOn:"<lateFinish<<":"<startTime<endTime; makeAppointments(); if (cs->recalculate() && completion().isStarted()) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->endTime != m_constraintEndTime) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } break; case Node::FixedInterval: { // cs->startTime calculated above //debugPlan<<"FixedInterval="<startTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if (m_constraintStartTime >= cs->earlyStart) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; } else { cs->startTime = cs->earlyStart; cs->endTime = cs->startTime + cs->duration; cs->constraintError = true; } if (m_constraintStartTime < cs->startTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->workStartTime = m_constraintStartTime; cs->workEndTime = m_constraintEndTime; //debugPlan<<"FixedInterval="<startTime<<","<endTime; makeAppointments(); break; } default: break; } if (m_estimate->type() == Estimate::Type_Effort) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = (m_estimate->value(use, cs->usePert()) - cs->plannedEffort()) > (5 * 60000); if (cs->effortNotMet) { cs->logError(i18n("Effort not met. Estimate: %1, planned: %2", estimate()->value(use, cs->usePert()).toHours(), cs->plannedEffort().toHours())); } } } else if (type() == Node::Type_Milestone) { if (cs->recalculate() && completion().isFinished()) { cs->startTime = completion().startTime(); cs->endTime = completion().finishTime(); m_visitedForward = true; return cs->endTime; } switch (m_constraint) { case Node::ASAP: { cs->endTime = cs->startTime; // TODO check, do we need to check successors earliestStart? cs->positiveFloat = cs->lateFinish - cs->endTime; break; } case Node::ALAP: { cs->startTime = qMax(cs->lateFinish, cs->earlyFinish); cs->endTime = cs->startTime; cs->positiveFloat = Duration::zeroDuration; break; } case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { //debugPlan<<"MustStartOn:"<startTime; DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; #ifndef PLAN_NLOGDEBUG cs->logDebug(QStringLiteral("%1: constraint time=%2, start time=%3").arg(constraintToString()).arg(contime.toString()).arg(cs->startTime.toString())); #endif if (cs->startTime < contime) { if (contime <= cs->lateFinish || contime <= cs->earlyFinish) { cs->startTime = contime; } } cs->negativeFloat = cs->startTime > contime ? cs->startTime - contime : contime - cs->startTime; if (cs->negativeFloat != 0) { cs->constraintError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->endTime = cs->startTime; if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: if (cs->startTime < m_constraintStartTime) { if (m_constraintStartTime <= cs->lateFinish || m_constraintStartTime <= cs->earlyFinish) { cs->startTime = m_constraintStartTime; } } if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->endTime = cs->startTime; if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: //debugPlan<startTime; if (cs->startTime > m_constraintEndTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintEndTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->endTime = cs->startTime; if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; //debugPlan<startTime<<","<endTime; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->endTime = cs->startTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime<<" :"<endTime<<""<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime()) { cs->logError(i18n("Failed to schedule within project target time")); } foreach (const Appointment *a, cs->appointments()) { cs->logInfo(i18n("Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat))); } if (cs->startTime < cs->earlyStart) { cs->logWarning(i18n("Starting earlier than early start")); } if (cs->endTime > cs->lateFinish) { cs->logWarning(i18n("Finishing later than late finish")); } cs->logInfo(i18n("Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat))); m_visitedForward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); cs->logInfo(i18n("Finished schedule forward: %1 ms", timer.elapsed())); return cs->endTime; } DateTime Task::scheduleSuccessors(const QList &list_, int use) { DateTime time; QMultiMap lst; for (Relation* r : list_) { lst.insert(-r->child()->priority(), r); } const QList list = lst.values(); foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<child()->name(); continue; } // get the successors starttime DateTime latest = r->child()->lateFinish(); DateTime t = r->child()->scheduleBackward(latest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my successor, so // I can't finish later than it's starttime + my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug(QStringLiteral("StartStart: get duration to calculate late finish")); #endif t += duration(t - r->lag(), use, false); break; case Relation::FinishFinish: t = r->child()->endTime() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } return time; } DateTime Task::scheduleBackward(const DateTime &latest, int use) { if (m_scheduleBackwardRun) { return m_currentSchedule->startTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode(Schedule::Scheduling); DateTime endTime = latest < cs->lateFinish ? latest : cs->lateFinish; // First, calculate all my own successors DateTime time = scheduleSuccessors(dependChildNodes(), use); if (time.isValid() && time < endTime) { endTime = time; } // Then my parents time = scheduleSuccessors(m_childProxyRelations, use); if (time.isValid() && time < endTime) { endTime = time; } if (! m_visitedBackward) { cs->endTime = endTime; } m_scheduleBackwardRun = true; return scheduleFromEndTime(use); } DateTime Task::scheduleFromEndTime(int use) { //debugPlan<setCalculationMode(Schedule::Scheduling); bool pert = cs->usePert(); if (m_visitedBackward) { return cs->startTime; } cs->notScheduled = false; if (!cs->endTime.isValid()) { cs->endTime = cs->lateFinish; } #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug(QStringLiteral("Start schedule backward: %1 ").arg(constraintToString(true))); #endif QLocale locale; cs->logInfo(i18n("Schedule from end time: %1", cs->endTime.toString())); if (type() == Node::Type_Task) { cs->duration = m_estimate->value(use, pert); switch (m_constraint) { case Node::ASAP: { // cs->endTime calculated above //debugPlan<duration = duration(cs->endTime, use, true); e = cs->endTime; cs->startTime = e - cs->duration; } if (e > cs->lateFinish) { cs->schedulingError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to schedule within late finish.", constraintToString())); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP: late finish=" + cs->lateFinish.toString() + " end time=" + e.toString()); #endif } else if (e > cs->endTime) { cs->schedulingError = true; cs->logWarning(i18nc("1=type of constraint", "%1: Failed to schedule within successors start time", constraintToString())); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP: succ. start=" + cs->endTime.toString() + " end time=" + e.toString()); #endif } if (cs->lateFinish > e) { DateTime w = workTimeBefore(cs->lateFinish); if (w > e) { cs->positiveFloat = w - e; } #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP: positiveFloat=" + cs->positiveFloat.toString()); #endif } cs->endTime = e; makeAppointments(); break; } case Node::ALAP: { // cs->endTime calculated above //debugPlan<logDebug("ALAP: earlyStart=" + cs->earlyStart.toString() + " cs->startTime=" + cs->startTime.toString()); #endif } else if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; #ifndef PLAN_NLOGDEBUG cs->logDebug("ALAP: positiveFloat=" + cs->positiveFloat.toString()); #endif } //debugPlan<endTime; cs->endTime = workTimeBefore(cs->endTime, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (cs->startTime < m_constraintStartTime) { //warnPlan<<"m_constraintStartTime > cs->lateStart"; cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } makeAppointments(); if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } break; case Node::FinishNotLater: // cs->endTime calculated above //debugPlan<<"FinishNotLater:"<endTime; if (cs->endTime > m_constraintEndTime) { cs->endTime = qMax(qMin(m_constraintEndTime, cs->lateFinish), cs->earlyFinish); } cs->endTime = workTimeBefore(cs->endTime, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (cs->endTime > m_constraintEndTime) { cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->constraintError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } makeAppointments(); if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } break; case Node::MustStartOn: // Just try to schedule on time //debugPlan<<"MustStartOn="<startTime.toString(); cs->startTime = workTimeAfter(m_constraintStartTime, cs); cs->duration = duration(cs->startTime, use, false); if (cs->endTime >= cs->startTime + cs->duration) { cs->endTime = cs->startTime + cs->duration; } else { cs->endTime = workTimeBefore(cs->endTime); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; } if (m_constraintStartTime != cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } makeAppointments(); if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } break; case Node::MustFinishOn: // Just try to schedule on time //debugPlan<endTime<earlyFinish; cs->endTime = workTimeBefore(m_constraintEndTime, cs); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); //warnPlan<<"m_constraintEndTime > cs->endTime"; } makeAppointments(); if (cs->lateFinish > cs->endTime) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } break; case Node::FixedInterval: { // cs->endTime calculated above //debugPlan<endTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if (cs->endTime > m_constraintEndTime) { cs->endTime = qMax(m_constraintEndTime, cs->earlyFinish); } cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->workStartTime = workTimeAfter(cs->startTime); cs->workEndTime = workTimeBefore(cs->endTime); makeAppointments(); if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = workTimeBefore(cs->lateFinish) - cs->endTime; } break; } default: break; } m_requests.reserve(cs->startTime, cs->duration); if (m_estimate->type() == Estimate::Type_Effort) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = (m_estimate->value(use, cs->usePert()) - cs->plannedEffort()) > (5 * 60000); if (cs->effortNotMet) { cs->logError(i18n("Effort not met. Estimate: %1, planned: %2", estimate()->value(use, cs->usePert()).toHours(), cs->plannedEffort().toHours())); } } } else if (type() == Node::Type_Milestone) { switch (m_constraint) { case Node::ASAP: if (cs->endTime < cs->earlyStart) { cs->schedulingError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to schedule after early start.", constraintToString())); cs->endTime = cs->earlyStart; } else { cs->positiveFloat = cs->lateFinish - cs->endTime; } //cs->endTime = cs->earlyStart; FIXME need to follow predeccessors. Defer scheduling? cs->startTime = cs->endTime; break; case Node::ALAP: cs->startTime = cs->endTime; cs->positiveFloat = cs->lateFinish - cs->endTime; break; case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; if (contime < cs->earlyStart) { if (cs->earlyStart < cs->endTime) { cs->endTime = cs->earlyStart; } } else if (contime < cs->endTime) { cs->endTime = contime; } cs->negativeFloat = cs->endTime > contime ? cs->endTime - contime : contime - cs->endTime; if (cs->negativeFloat != 0) { cs->constraintError = true; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->startTime = cs->endTime; if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: cs->startTime = cs->endTime; if (m_constraintStartTime > cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: if (m_constraintEndTime < cs->earlyStart) { if (cs->earlyStart < cs->endTime) { cs->endTime = cs->earlyStart; } } else if (m_constraintEndTime < cs->endTime) { cs->endTime = m_constraintEndTime; } if (m_constraintEndTime > cs->endTime) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError(i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString(true), cs->negativeFloat.toString(Duration::Format_i18nHour))); } cs->startTime = cs->endTime; if (cs->negativeFloat == Duration::zeroDuration) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->startTime = cs->endTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime()) { cs->logError(i18n("Failed to schedule within project target time")); } foreach (const Appointment *a, cs->appointments()) { cs->logInfo(i18n("Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat))); } if (cs->startTime < cs->earlyStart) { cs->logWarning(i18n("Starting earlier than early start")); } if (cs->endTime > cs->lateFinish) { cs->logWarning(i18n("Finishing later than late finish")); } cs->logInfo(i18n("Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat))); m_visitedBackward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); #ifndef PLAN_NLOGDEBUG cs->logDebug(QStringLiteral("Finished schedule backward: %1 ms").arg(timer.elapsed())); #endif return cs->startTime; } void Task::adjustSummarytask() { if (m_currentSchedule == 0) return; if (type() == Type_Summarytask) { DateTime start = m_currentSchedule->lateFinish; DateTime end = m_currentSchedule->earlyStart; foreach (Node *n, m_nodes) { n->adjustSummarytask(); if (n->startTime() < start) start = n->startTime(); if (n->endTime() > end) end = n->endTime(); } m_currentSchedule->startTime = start; m_currentSchedule->endTime = end; m_currentSchedule->duration = end - start; m_currentSchedule->notScheduled = false; //debugPlan<name<<":"<startTime.toString()<<" :"<endTime.toString(); } } Duration Task::duration(const DateTime &time, int use, bool backward) { //debugPlan; // TODO: handle risc if (m_currentSchedule == 0) { errorPlan<<"No current schedule"; return Duration::zeroDuration; } if (!time.isValid()) { #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug(QStringLiteral("Calculate duration: Start time is not valid")); #endif return Duration::zeroDuration; } //debugPlan< calcDuration"<<(backward?"(B)":"(F)")<resourceNotAvailable = true; dur = effort; //??? } return dur; } if (m_estimate->type() == Estimate::Type_Duration) { return length(time, dur, backward); } errorPlan<<"Unsupported estimate type: "<type(); return dur; } Duration Task::length(const DateTime &time, KPlato::Duration duration, bool backward) { return length(time, duration, m_currentSchedule, backward); } Duration Task::length(const DateTime &time, KPlato::Duration duration, Schedule *sch, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<logDebug(QStringLiteral("Calculate length: estimate == 0")); #else Q_UNUSED(sch) #endif return l; } Calendar *cal = m_estimate->calendar(); if (cal == 0) { #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug("Calculate length: No calendar, return estimate " + duration.toString()); #endif return duration; } #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug("Calculate length from: " + time.toString()); #endif DateTime logtime = time; bool sts=true; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration l1; int nDays = backward ? projectNode()->constraintStartTime().daysTo(time) : time.daysTo(projectNode()->constraintEndTime()); for (int i=0; !match && i <= nDays; ++i) { // days end = end.addDays(inc); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); //debugPlan<<"["<logDebug("Days: duration " + logtime.toString() + " - " + end.toString() + " = " + l.toString() + " (" + (duration - l).toString() + ')'); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { end = start; break; } //debugPlan<<"duration(h)["<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(s)["<logDebug("Seconds: duration " + logtime.toString() + " - " + end.toString() + " l " + l.toString() + " (" + (duration - l).toString() + ')'); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug("Got more than asked for, should not happen! Want: " + duration.toString(Duration::Format_Hour) + " got: " + l.toString(Duration::Format_Hour)); #endif break; } //debugPlan<<"duration(ms)["<logError(i18n("Could not match work duration. Want: %1 got: %2", l.toString(Duration::Format_i18nHour), duration.toString(Duration::Format_i18nHour))); } DateTime t = end; if (l != Duration::zeroDuration) { if (backward) { if (end < projectNode()->constraintEndTime()) { t = cal->firstAvailableAfter(end, projectNode()->constraintEndTime()); } } else { if (end > projectNode()->constraintStartTime()) { t = cal->firstAvailableBefore(end, projectNode()->constraintStartTime()); } } #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug("Moved end to work: " + end.toString() + " -> " + t.toString()); #endif } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<time ? end-time : time-end; if (match) { #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug("Calculated length: " + time.toString() + " - " + end.toString() + " = " + l.toString()); #endif } return l; } void Task::clearProxyRelations() { m_parentProxyRelations.clear(); m_childProxyRelations.clear(); } void Task::addParentProxyRelations(const QList &list) { //debugPlan<addParentProxyRelations(list); n->addParentProxyRelations(dependParentNodes()); } } else { // add 'this' as child relation to the relations parent //debugPlan<parent()->addChildProxyRelation(this, r); // add a parent relation to myself addParentProxyRelation(r->parent(), r); } } } void Task::addChildProxyRelations(const QList &list) { //debugPlan<addChildProxyRelations(list); n->addChildProxyRelations(dependChildNodes()); } } else { // add 'this' as parent relation to the relations child //debugPlan<child()->addParentProxyRelation(this, r); // add a child relation to myself addChildProxyRelation(r->child(), r); } } } void Task::addParentProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add parent proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addParentProxyRelation(node, rel); } } else { //debugPlan<<"Add parent proxy from"<name()<<" to (me)"<type(), rel->lag())); } } } void Task::addChildProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add child proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addChildProxyRelation(node, rel); } } else { //debugPlan<<"Add child proxy from (me)"<name(); m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag())); } } } bool Task::isEndNode() const { return m_dependChildNodes.isEmpty() && m_childProxyRelations.isEmpty(); } bool Task::isStartNode() const { return m_dependParentNodes.isEmpty() && m_parentProxyRelations.isEmpty(); } DateTime Task::workTimeAfter(const DateTime &dt, Schedule *sch) const { DateTime t; if (m_estimate->type() == Estimate::Type_Duration) { if (m_estimate->calendar()) { t = m_estimate->calendar()->firstAvailableAfter(dt, projectNode()->constraintEndTime()); } } else { t = m_requests.workTimeAfter(dt, sch); #ifndef PLAN_NLOGDEBUG if (sch) sch->logDebug(QStringLiteral("workTimeAfter: %1 = %2").arg(dt.toString()).arg(t.toString())); #endif } return t.isValid() ? t : dt; } DateTime Task::workTimeBefore(const DateTime &dt, Schedule *sch) const { DateTime t; if (m_estimate->type() == Estimate::Type_Duration) { if (m_estimate->calendar()) { t = m_estimate->calendar()->firstAvailableBefore(dt, projectNode()->constraintStartTime()); } } else { t = m_requests.workTimeBefore(dt, sch); } return t.isValid() ? t : dt; } Duration Task::positiveFloat(long id) const { Schedule *s = schedule(id); return s == 0 ? Duration::zeroDuration : s->positiveFloat; } void Task::setPositiveFloat(KPlato::Duration fl, long id) const { Schedule *s = schedule(id); if (s) s->positiveFloat = fl; } Duration Task::negativeFloat(long id) const { Schedule *s = schedule(id); return s == 0 ? Duration::zeroDuration : s->negativeFloat; } void Task::setNegativeFloat(KPlato::Duration fl, long id) const { Schedule *s = schedule(id); if (s) s->negativeFloat = fl; } Duration Task::freeFloat(long id) const { Schedule *s = schedule(id); return s == 0 ? Duration::zeroDuration : s->freeFloat; } void Task::setFreeFloat(KPlato::Duration fl, long id) const { Schedule *s = schedule(id); if (s) s->freeFloat = fl; } Duration Task::startFloat(long id) const { Schedule *s = schedule(id); return s == 0 || s->earlyStart > s->lateStart ? Duration::zeroDuration : (s->earlyStart - s->lateStart); } Duration Task::finishFloat(long id) const { Schedule *s = schedule(id); return s == 0 || s->lateFinish < s->earlyFinish ? Duration::zeroDuration : (s->lateFinish - s->earlyFinish); } bool Task::isCritical(long id) const { Schedule *s = schedule(id); return s == 0 ? false : s->isCritical(); } bool Task::calcCriticalPath(bool fromEnd) { if (m_currentSchedule == 0) return false; //debugPlan<child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependChildNodes) { if (r->child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } else { if (isStartNode() && startFloat() == 0 && finishFloat() == 0) { m_currentSchedule->inCriticalPath = true; //debugPlan<parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependParentNodes) { if (r->parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } //debugPlan<freeFloat.toString(); } } void Task::setCurrentSchedule(long id) { setCurrentSchedulePtr(findSchedule(id)); Node::setCurrentSchedule(id); } bool Task::effortMetError(long id) const { Schedule *s = schedule(id); if (s == 0 || s->notScheduled || m_estimate->type() != Estimate::Type_Effort) { return false; } return s->effortNotMet; } uint Task::state(long id) const { int st = Node::State_None; if (! isScheduled(id)) { st |= State_NotScheduled; } if (completion().isFinished()) { st |= Node::State_Finished; if (completion().finishTime() > endTime(id)) { st |= State_FinishedLate; } if (completion().finishTime() < endTime(id)) { st |= State_FinishedEarly; } } else if (completion().isStarted()) { st |= Node::State_Started; if (completion().startTime() > startTime(id)) { st |= State_StartedLate; } if (completion().startTime() < startTime(id)) { st |= State_StartedEarly; } if (completion().percentFinished() > 0) { st |= State_Running; } if (endTime(id) < QDateTime::currentDateTime()) { st |= State_Late; } } else if (isScheduled(id)) { if (startTime(id) < QDateTime::currentDateTime()) { st |= State_Late; } } st |= State_ReadyToStart; //TODO: check proxy relations foreach (const Relation *r, m_dependParentNodes) { if (! static_cast(r->parent())->completion().isFinished()) { st &= ~Node::State_ReadyToStart; st |= Node::State_NotReadyToStart; break; } } return st; } void Task::addWorkPackage(WorkPackage *wp) { emit workPackageToBeAdded(this, m_packageLog.count()); m_packageLog.append(wp); emit workPackageAdded(this); } void Task::removeWorkPackage(WorkPackage *wp) { int index = m_packageLog.indexOf(wp); if (index < 0) { return; } emit workPackageToBeRemoved(this, index); m_packageLog.removeAt(index); emit workPackageRemoved(this); } WorkPackage *Task::workPackageAt(int index) const { Q_ASSERT (index >= 0 && index < m_packageLog.count()); return m_packageLog.at(index); } QString Task::wpOwnerName() const { if (m_packageLog.isEmpty()) { return m_workPackage.ownerName(); } return m_packageLog.last()->ownerName(); } WorkPackage::WPTransmitionStatus Task::wpTransmitionStatus() const { if (m_packageLog.isEmpty()) { return m_workPackage.transmitionStatus(); } return m_packageLog.last()->transmitionStatus(); } DateTime Task::wpTransmitionTime() const { if (m_packageLog.isEmpty()) { return m_workPackage.transmitionTime(); } return m_packageLog.last()->transmitionTime(); } //------------------------------------------ Completion::Completion(Node *node) : m_node(node), m_started(false), m_finished(false), m_entrymode(EnterEffortPerResource) {} Completion::Completion(const Completion &c) { copy(c); } Completion::~Completion() { qDeleteAll(m_entries); qDeleteAll(m_usedEffort); } void Completion::copy(const Completion &p) { m_node = 0; //NOTE m_started = p.isStarted(); m_finished = p.isFinished(); m_startTime = p.startTime(); m_finishTime = p.finishTime(); m_entrymode = p.entrymode(); qDeleteAll(m_entries); m_entries.clear(); Completion::EntryList::ConstIterator entriesIt = p.entries().constBegin(); const Completion::EntryList::ConstIterator entriesEnd = p.entries().constEnd(); for (; entriesIt != entriesEnd; ++entriesIt) { addEntry(entriesIt.key(), new Entry(*entriesIt.value())); } qDeleteAll(m_usedEffort); m_usedEffort.clear(); Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapIt = p.usedEffortMap().constBegin(); const Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapEnd = p.usedEffortMap().constEnd(); for (; usedEffortMapIt != usedEffortMapEnd; ++usedEffortMapIt) { addUsedEffort(usedEffortMapIt.key(), new UsedEffort(*usedEffortMapIt.value())); } } bool Completion::operator==(const Completion &p) { return m_started == p.isStarted() && m_finished == p.isFinished() && m_startTime == p.startTime() && m_finishTime == p.finishTime() && m_entries == p.entries() && m_usedEffort == p.usedEffortMap(); } Completion &Completion::operator=(const Completion &p) { copy(p); return *this; } void Completion::changed(int property) { if (m_node) { m_node->changed(property); } } void Completion::setStarted(bool on) { m_started = on; changed(Node::CompletionStartedProperty); } void Completion::setFinished(bool on) { m_finished = on; changed(Node::CompletionFinishedProperty); } void Completion::setStartTime(const DateTime &dt) { m_startTime = dt; changed(Node::CompletionStartTimeProperty); } void Completion::setFinishTime(const DateTime &dt) { m_finishTime = dt; changed(Node::CompletionFinishTimeProperty); } void Completion::setPercentFinished(QDate date, int value) { Entry *e = 0; if (m_entries.contains(date)) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->percentFinished = value; changed(Node::CompletionPercentageProperty); } void Completion::setRemainingEffort(QDate date, KPlato::Duration value) { Entry *e = 0; if (m_entries.contains(date)) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->remainingEffort = value; changed(Node::CompletionRemainingEffortProperty); } void Completion::setActualEffort(QDate date, KPlato::Duration value) { Entry *e = 0; if (m_entries.contains(date)) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->totalPerformed = value; changed(Node::CompletionActualEffortProperty); } void Completion::addEntry(QDate date, Entry *entry) { m_entries.insert(date, entry); //debugPlan<percentFinished; } int Completion::percentFinished(QDate date) const { int x = 0; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->percentFinished; } return x; } Duration Completion::remainingEffort() const { return m_entries.isEmpty() ? Duration::zeroDuration : m_entries.last()->remainingEffort; } Duration Completion::remainingEffort(QDate date) const { Duration x; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->remainingEffort; } return x; } Duration Completion::actualEffort() const { Duration eff; if (m_entrymode == EnterEffortPerResource) { foreach(const UsedEffort *ue, m_usedEffort) { const QMap map = ue->actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { eff += it.value().effort(); } } } else if (! m_entries.isEmpty()) { eff = m_entries.last()->totalPerformed; } return eff; } Duration Completion::actualEffort(const Resource *resource, QDate date) const { UsedEffort *ue = usedEffort(resource); if (ue == 0) { return Duration::zeroDuration; } UsedEffort::ActualEffort ae = ue->effort(date); return ae.effort(); } Duration Completion::actualEffort(QDate date) const { Duration eff; if (m_entrymode == EnterEffortPerResource) { foreach(const UsedEffort *ue, m_usedEffort) { if (ue && ue->actualEffortMap().contains(date)) { eff += ue->actualEffortMap().value(date).effort(); } } } else { // Hmmm: How to really know a specific date? if (m_entries.contains(date)) { eff = m_entries[ date ]->totalPerformed; } } return eff; } Duration Completion::actualEffortTo(QDate date) const { //debugPlan<effortTo(date); } } else { QListIterator it(m_entries.uniqueKeys()); it.toBack(); while (it.hasPrevious()) { QDate d = it.previous(); if (d <= date) { eff = m_entries[ d ]->totalPerformed; break; } } } return eff; } double Completion::averageCostPrHour(QDate date, long id) const { Schedule *s = m_node->schedule(id); if (s == 0) { return 0.0; } double cost = 0.0; double eff = 0.0; QList cl; foreach (const Appointment *a, s->appointments()) { cl << a->resource()->resource()->normalRate(); double e = a->plannedEffort(date).toDouble(Duration::Unit_h); if (e > 0.0) { eff += e; cost += e * cl.last(); } } if (eff > 0.0) { cost /= eff; } else { foreach (double c, cl) { cost += c; } cost /= cl.count(); } return cost; } EffortCostMap Completion::effortCostPrDay(QDate start, QDate end, long id) const { //debugPlan<name()< et) { break; } Duration e = m_entries[ d ]->totalPerformed; if (e != Duration::zeroDuration && e != last) { Duration eff = e - last; ec.insert(d, eff, eff.toDouble(Duration::Unit_h) * averageCostPrHour(d, id)); last = e; } } break; } case EnterEffortPerResource: { std::pair dates = actualStartEndDates(); if (! dates.first.isValid()) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for (QDate d = st; d <= et; d = d.addDays(1)) { ec.add(d, actualEffort(d), actualCost(d)); } break; } } return ec; } EffortCostMap Completion::effortCostPrDay(const Resource *resource, QDate start, QDate end, long id) const { Q_UNUSED(id); //debugPlan<name()< dates = actualStartEndDates(); if (! dates.first.isValid()) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for (QDate d = st; d <= et; d = d.addDays(1)) { ec.add(d, actualEffort(resource, d), actualCost(resource, d)); } break; } } return ec; } void Completion::addUsedEffort(const Resource *resource, Completion::UsedEffort *value) { UsedEffort *v = value == 0 ? new UsedEffort() : value; if (m_usedEffort.contains(resource)) { m_usedEffort[ resource ]->mergeEffort(*v); delete v; } else { m_usedEffort.insert(resource, v); } changed(Node::CompletionUsedEffortProperty); } void Completion::setActualEffort(Resource *resource, const QDate &date, const Completion::UsedEffort::ActualEffort &value) { if (value.isNull()) { if (!m_usedEffort.contains(resource)) { return; } UsedEffort *ue = m_usedEffort.value(resource); if (!ue) { return; } ue->takeEffort(date); } else { UsedEffort *ue = m_usedEffort[resource]; if (!ue) { ue = new UsedEffort(); m_usedEffort.insert(resource, ue); } ue->setEffort(date, value); } changed(Node::CompletionActualEffortProperty); } Completion::UsedEffort::ActualEffort Completion::getActualEffort(Resource *resource, const QDate &date) const { UsedEffort::ActualEffort value; UsedEffort *ue = m_usedEffort.value(resource); if (ue) { value = ue->effort(date); } return value; } QString Completion::note() const { return m_entries.isEmpty() ? QString() : m_entries.last()->note; } void Completion::setNote(const QString &str) { if (! m_entries.isEmpty()) { m_entries.last()->note = str; changed(Node::CompletionNoteProperty); } } std::pair Completion::actualStartEndDates() const { std::pair p; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { if (!it.value()->actualEffortMap().isEmpty()) { QDate d = it.value()->firstDate(); if (!p.first.isValid() || d < p.first) { p.first = d; } d = it.value()->lastDate(); if (!p.second.isValid() || d > p.second) { p.second = d; } } } return p; } double Completion::actualCost(QDate date) const { //debugPlan<normalRate(); double oc = it.key()->overtimeRate(); if (it.value()->actualEffortMap().contains(date)) { UsedEffort::ActualEffort a = it.value()->effort(date); c += a.normalEffort().toDouble(Duration::Unit_h) * nc; c += a.overtimeEffort().toDouble(Duration::Unit_h) * oc; } } return c; } double Completion::actualCost(const Resource *resource) const { UsedEffort *ue = usedEffort(resource); if (ue == 0) { return 0.0; } double c = 0.0; double nc = resource->normalRate(); double oc = resource->overtimeRate(); foreach (const UsedEffort::ActualEffort &a, ue->actualEffortMap()) { c += a.normalEffort().toDouble(Duration::Unit_h) * nc; c += a.overtimeEffort().toDouble(Duration::Unit_h) * oc; } return c; } double Completion::actualCost() const { double c = 0.0; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { c += actualCost(it.key()); } return c; } double Completion::actualCost(const Resource *resource, QDate date) const { UsedEffort *ue = usedEffort(resource); if (ue == 0) { return 0.0; } UsedEffort::ActualEffort a = ue->actualEffortMap().value(date); double c = a.normalEffort().toDouble(Duration::Unit_h) * resource->normalRate(); c += a.overtimeEffort().toDouble(Duration::Unit_h) * resource->overtimeRate(); return c; } EffortCostMap Completion::actualEffortCost(long int id, KPlato::EffortCostCalculationType type) const { //debugPlan; EffortCostMap map; if (! isStarted()) { return map; } QList< QMap > lst; QList< double > rate; QDate start, end; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { const Resource *r = it.key(); //debugPlan<name()<name(); lst << usedEffort(r)->actualEffortMap(); if (lst.last().isEmpty()) { lst.takeLast(); continue; } if (r->type() == Resource::Type_Material) { if (type == ECCT_All) { rate.append(r->normalRate()); } else if (type == ECCT_EffortWork) { rate.append(0.0); } else { lst.takeLast(); continue; } } else { rate.append(r->normalRate()); } if (! start.isValid() || start > lst.last().firstKey()) { start = lst.last().firstKey(); } if (! end.isValid() || end < lst.last().lastKey()) { end = lst.last().lastKey(); } } if (! lst.isEmpty() && start.isValid() && end.isValid()) { for (QDate d = start; d <= end; d = d.addDays(1)) { EffortCost c; for (int i = 0; i < lst.count(); ++i) { UsedEffort::ActualEffort a = lst.at(i).value(d); double nc = rate.value(i); Duration eff = a.normalEffort(); double cost = eff.toDouble(Duration::Unit_h) * nc; c.add(eff, cost); } if (c.effort() != Duration::zeroDuration || c.cost() != 0.0) { map.add(d, c); } } } else if (! m_entries.isEmpty()) { QDate st = start.isValid() ? start : m_startTime.date(); QDate et = end.isValid() ? end : m_finishTime.date(); Duration last; foreach (const QDate &d, m_entries.uniqueKeys()) { if (d < st) { continue; } Duration e = m_entries[ d ]->totalPerformed; if (e != Duration::zeroDuration && e != last) { //debugPlan<name()< et) { break; } } } return map; } EffortCost Completion::actualCostTo(long int id, QDate date) const { //debugPlan<(m); } QString Completion::entryModeToString() const { return entrymodeList().value(m_entrymode); } bool Completion::loadXML(KoXmlElement &element, XMLLoaderObject &status) { //debugPlan; QString s; m_started = (bool)element.attribute(QStringLiteral("started"), QStringLiteral("0")).toInt(); m_finished = (bool)element.attribute(QStringLiteral("finished"), QStringLiteral("0")).toInt(); s = element.attribute(QStringLiteral("startTime")); if (!s.isEmpty()) { m_startTime = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute(QStringLiteral("finishTime")); if (!s.isEmpty()) { m_finishTime = DateTime::fromString(s, status.projectTimeZone()); } setEntrymode(element.attribute(QStringLiteral("entrymode"))); if (status.version() < QLatin1String("0.6")) { if (m_started) { Entry *entry = new Entry(element.attribute(QStringLiteral("percent-finished"), QStringLiteral("0")).toInt(), Duration::fromString(element.attribute(QStringLiteral("remaining-effort"))), Duration::fromString(element.attribute(QStringLiteral("performed-effort")))); entry->note = element.attribute(QStringLiteral("note")); QDate date = m_startTime.date(); if (m_finished) { date = m_finishTime.date(); } // almost the best we can do ;) addEntry(date, entry); } } else { KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("completion-entry")) { QDate date; s = e.attribute(QStringLiteral("date")); if (!s.isEmpty()) { date = QDate::fromString(s, Qt::ISODate); } if (!date.isValid()) { warnPlan<<"Invalid date: "<percentFinished); elm.setAttribute(QStringLiteral("remaining-effort"), e->remainingEffort.toString()); elm.setAttribute(QStringLiteral("performed-effort"), e->totalPerformed.toString()); elm.setAttribute(QStringLiteral("note"), e->note); } if (! m_usedEffort.isEmpty()) { QDomElement elm = el.ownerDocument().createElement(QStringLiteral("used-effort")); el.appendChild(elm); ResourceUsedEffortMap::ConstIterator i = m_usedEffort.constBegin(); for (; i != m_usedEffort.constEnd(); ++i) { if (i.value() == 0) { continue; } QDomElement e = elm.ownerDocument().createElement(QStringLiteral("resource")); elm.appendChild(e); e.setAttribute(QStringLiteral("id"), i.key()->id()); i.value()->saveXML(e); } } } //-------------- Completion::UsedEffort::UsedEffort() { } Completion::UsedEffort::UsedEffort(const UsedEffort &e) { mergeEffort(e); } Completion::UsedEffort::~UsedEffort() { } void Completion::UsedEffort::mergeEffort(const Completion::UsedEffort &value) { const QMap map = value.actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { setEffort(it.key(), it.value()); } } void Completion::UsedEffort::setEffort(QDate date, const ActualEffort &value) { m_actual.insert(date, value); } Duration Completion::UsedEffort::effortTo(QDate date) const { Duration eff; QMap::const_iterator it; for (it = m_actual.constBegin(); it != m_actual.constEnd() && it.key() <= date; ++it) { eff += it.value().effort(); } return eff; } Duration Completion::UsedEffort::effort() const { Duration eff; foreach (const ActualEffort &e, m_actual) { eff += e.effort(); } return eff; } bool Completion::UsedEffort::operator==(const Completion::UsedEffort &e) const { return m_actual == e.actualEffortMap(); } bool Completion::UsedEffort::loadXML(KoXmlElement &element, XMLLoaderObject &) { //debugPlan; KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("actual-effort")) { QDate date = QDate::fromString(e.attribute(QStringLiteral("date")), Qt::ISODate); if (date.isValid()) { ActualEffort a; a.setNormalEffort(Duration::fromString(e.attribute(QStringLiteral("normal-effort")))); a.setOvertimeEffort(Duration::fromString(e.attribute(QStringLiteral("overtime-effort")))); setEffort(date, a); } } } return true; } void Completion::UsedEffort::saveXML(QDomElement &element) const { if (m_actual.isEmpty()) { return; } DateUsedEffortMap::ConstIterator i = m_actual.constBegin(); for (; i != m_actual.constEnd(); ++i) { QDomElement el = element.ownerDocument().createElement(QStringLiteral("actual-effort")); element.appendChild(el); el.setAttribute(QStringLiteral("overtime-effort"), i.value().overtimeEffort().toString()); el.setAttribute(QStringLiteral("normal-effort"), i.value().normalEffort().toString()); el.setAttribute(QStringLiteral("date"), i.key().toString(Qt::ISODate)); } } //---------------------------------- WorkPackage::WorkPackage(Task *task) : m_task(task), m_manager(0), m_transmitionStatus(TS_None) { m_completion.setNode(task); } WorkPackage::WorkPackage(const WorkPackage &wp) : m_task(0), m_manager(0), m_completion(wp.m_completion), m_ownerName(wp.m_ownerName), m_ownerId(wp.m_ownerId), m_transmitionStatus(wp.m_transmitionStatus), m_transmitionTime(wp.m_transmitionTime) { } WorkPackage::~WorkPackage() { } bool WorkPackage::loadXML(KoXmlElement &element, XMLLoaderObject &status) { Q_UNUSED(status); m_ownerName = element.attribute(QStringLiteral("owner")); m_ownerId = element.attribute(QStringLiteral("owner-id")); return true; } void WorkPackage::saveXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute(QStringLiteral("owner"), m_ownerName); el.setAttribute(QStringLiteral("owner-id"), m_ownerId); } bool WorkPackage::loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status) { m_ownerName = element.attribute(QStringLiteral("owner")); m_ownerId = element.attribute(QStringLiteral("owner-id")); m_transmitionStatus = transmitionStatusFromString(element.attribute(QStringLiteral("status"))); m_transmitionTime = DateTime(QDateTime::fromString(element.attribute(QStringLiteral("time")), Qt::ISODate)); return m_completion.loadXML(element, status); } void WorkPackage::saveLoggedXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute(QStringLiteral("owner"), m_ownerName); el.setAttribute(QStringLiteral("owner-id"), m_ownerId); el.setAttribute(QStringLiteral("status"), transmitionStatusToString(m_transmitionStatus)); el.setAttribute(QStringLiteral("time"), m_transmitionTime.toString(Qt::ISODate)); m_completion.saveXML(el); } QList WorkPackage::fetchResources() { return fetchResources(id()); } QList WorkPackage::fetchResources(long id) { //debugPlan< lst; if (id == NOTSCHEDULED) { if (m_task) { lst << m_task->requestedResources(); } } else { if (m_task) lst = m_task->assignedResources(id); foreach (const Resource *r, m_completion.resources()) { if (! lst.contains(const_cast(r))) { lst << const_cast(r); } } } return lst; } Completion &WorkPackage::completion() { return m_completion; } const Completion &WorkPackage::completion() const { return m_completion; } void WorkPackage::setScheduleManager(ScheduleManager *sm) { m_manager = sm; } QString WorkPackage::transmitionStatusToString(WorkPackage::WPTransmitionStatus sts, bool trans) { QString s = trans ? i18n("None") : QStringLiteral("None"); switch (sts) { case TS_Send: s = trans ? i18n("Send") : QStringLiteral("Send"); break; case TS_Receive: s = trans ? i18n("Receive") : QStringLiteral("Receive"); break; case TS_Rejected: s = trans ? i18n("Rejected") : QStringLiteral("Rejected"); break; default: break; } return s; } WorkPackage::WPTransmitionStatus WorkPackage::transmitionStatusFromString(const QString &sts) { QStringList lst; lst << QStringLiteral("None") << QStringLiteral("Send") << QStringLiteral("Receive"); int s = lst.indexOf(sts); return s < 0 ? TS_None : static_cast(s); } void WorkPackage::clear() { //m_task = 0; m_manager = 0; m_ownerName.clear(); m_ownerId.clear(); m_transmitionStatus = TS_None; m_transmitionTime = DateTime(); m_log.clear(); m_completion = Completion(); m_completion.setNode(m_task); } //-------------------------------- WorkPackageSettings::WorkPackageSettings() : usedEffort(true), progress(true), documents(true), remainingEffort(true) { } void WorkPackageSettings::saveXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("settings")); element.appendChild(el); el.setAttribute(QStringLiteral("used-effort"), QString::number(usedEffort)); el.setAttribute(QStringLiteral("progress"), QString::number(progress)); el.setAttribute(QStringLiteral("documents"), QString::number(documents)); el.setAttribute(QStringLiteral("remaining-effort"), QString::number(remainingEffort)); } bool WorkPackageSettings::loadXML(const KoXmlElement &element) { usedEffort = (bool)element.attribute(QStringLiteral("used-effort")).toInt(); progress = (bool)element.attribute(QStringLiteral("progress")).toInt(); documents = (bool)element.attribute(QStringLiteral("documents")).toInt(); remainingEffort = (bool)element.attribute(QStringLiteral("remaining-effort")).toInt(); return true; } bool WorkPackageSettings::operator==(KPlato::WorkPackageSettings s) const { return usedEffort == s.usedEffort && progress == s.progress && documents == s.documents && remainingEffort == s.remainingEffort; } bool WorkPackageSettings::operator!=(KPlato::WorkPackageSettings s) const { return ! operator==(s); } } //KPlato namespace #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae) { dbg << QStringLiteral("%1").arg(ae.normalEffort().toDouble(KPlato::Duration::Unit_h), 1); return dbg; } #endif diff --git a/src/libs/kernel/kpttask.h b/src/libs/kernel/kpttask.h index 9fe924f9..5bf71ce7 100644 --- a/src/libs/kernel/kpttask.h +++ b/src/libs/kernel/kpttask.h @@ -1,773 +1,765 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASK_H #define KPTTASK_H #include "plankernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include #include #include /// The main namespace. namespace KPlato { class Completion; class XmlSaveContext; /** * The Completion class holds information about the tasks progress. */ class PLANKERNEL_EXPORT Completion { public: class PLANKERNEL_EXPORT UsedEffort { public: class PLANKERNEL_EXPORT ActualEffort : public std::pair { public: explicit ActualEffort(KPlato::Duration ne = Duration::zeroDuration, KPlato::Duration oe = Duration::zeroDuration) : std::pair(ne, oe) {} ActualEffort(const ActualEffort &e) : std::pair(e.first, e.second) {} ~ActualEffort() {} bool isNull() const { return (first + second) == Duration::zeroDuration; } Duration normalEffort() const { return first; } void setNormalEffort(KPlato::Duration e) { first = e; } Duration overtimeEffort() const { return second; } void setOvertimeEffort(KPlato::Duration e) { second = e; } /// Returns the sum of normalEffort + overtimeEffort Duration effort() const { return first + second; } void setEffort(KPlato::Duration ne, KPlato::Duration oe = Duration::zeroDuration) { first = ne; second = oe; } }; UsedEffort(); UsedEffort(const UsedEffort &e); ~UsedEffort(); bool operator==(const UsedEffort &e) const; bool operator!=(const UsedEffort &e) const { return !operator==(e); } void mergeEffort(const UsedEffort &value); void setEffort(QDate date, const ActualEffort &value); /// Returns the total effort up to @p date Duration effortTo(QDate date) const; /// Returns the total effort on @p date ActualEffort effort(QDate date) const { return m_actual.value(date); } ActualEffort takeEffort(QDate date) { return m_actual.take(date); } /// Returns the total effort for all registered dates Duration effort() const; QDate firstDate() const { return m_actual.firstKey(); } QDate lastDate() const { return m_actual.lastKey(); } QMap actualEffortMap() const { return m_actual; } /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status); /// Save to document void saveXML(QDomElement &element) const; bool contains(QDate date) const { return m_actual.contains(date); } private: QMap m_actual; }; typedef QMap DateUsedEffortMap; class PLANKERNEL_EXPORT Entry { public: Entry() : percentFinished(0), remainingEffort(Duration::zeroDuration), totalPerformed(Duration::zeroDuration) {} Entry(int percent, Duration remaining, Duration performed) : percentFinished(percent), remainingEffort(remaining), totalPerformed(performed) {} Entry(const Entry &e) { copy(e); } bool operator==(const Entry &e) const { return percentFinished == e.percentFinished && remainingEffort == e.remainingEffort && totalPerformed == e.totalPerformed && note == e.note; } bool operator!=(const Entry &e) const { return ! operator==(e); } Entry &operator=(const Entry &e) { copy(e); return *this; } int percentFinished; Duration remainingEffort; Duration totalPerformed; QString note; protected: void copy(const Entry &e) { percentFinished = e.percentFinished; remainingEffort = e.remainingEffort; totalPerformed = e.totalPerformed; note = e.note; } }; typedef QMap EntryList; typedef QHash ResourceUsedEffortMap; explicit Completion(Node *node = 0); // review * or &, or at all? Completion(const Completion ©); virtual ~Completion(); bool operator==(const Completion &p); bool operator!=(Completion &p) { return !(*this == p); } Completion &operator=(const Completion &p); /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status); /// Save to document void saveXML(QDomElement &element) const; bool startIsValid() const { return m_started && m_startTime.isValid(); } bool isStarted() const { return m_started; } void setStarted(bool on); bool finishIsValid() const { return m_finished && m_finishTime.isValid(); } bool isFinished() const { return m_finished; } void setFinished(bool on); DateTime startTime() const { return m_startTime; } void setStartTime(const DateTime &dt); DateTime finishTime() const { return m_finishTime; } void setFinishTime(const DateTime &dt); void setPercentFinished(QDate date, int value); void setRemainingEffort(QDate date, Duration value); void setActualEffort(QDate date, Duration value); /// Return a list of the resource that has done any work on this task QList resources() const { return m_usedEffort.keys(); } const EntryList &entries() const { return m_entries; } void addEntry(QDate date, Entry *entry); Entry *takeEntry(QDate date) { return m_entries.take(date); changed(); } Entry *entry(QDate date) const { return m_entries[ date ]; } /// Returns the date of the latest entry QDate entryDate() const; /// Returns the percentFinished of the latest entry int percentFinished() const; /// Returns the percentFinished on @p date int percentFinished(QDate date) const; /// Returns the estimated remaining effort Duration remainingEffort() const; /// Returns the estimated remaining effort on @p date Duration remainingEffort(QDate date) const; /// Returns the total actual effort Duration actualEffort() const; /// Returns the total actual effort on @p date Duration actualEffort(QDate date) const; /// Returns the total actual effort upto and including @p date Duration actualEffortTo(QDate date) const; /// Returns the actual effort for @p resource on @p date Duration actualEffort(const Resource *resource, QDate date) const; /// TODO QString note() const; /// TODO void setNote(const QString &str); /// Returns the total actual cost double actualCost() const; /// Returns the actual cost for @p resource double actualCost(const Resource *resource) const; /// Returns the actual cost on @p date double actualCost(QDate date) const; /// Returns the total actual cost for @p resource on @p date double actualCost(const Resource *resource, QDate date) const; /// Returns the total actual effort and cost upto and including @p date EffortCost actualCostTo(long int id, QDate date) const; /** * Returns a map of all actual effort and cost entered */ virtual EffortCostMap actualEffortCost(long id, EffortCostCalculationType type = ECCT_All) const; void addUsedEffort(const Resource *resource, UsedEffort *value = 0); UsedEffort *takeUsedEffort(const Resource *r) { return m_usedEffort.take(const_cast(r) ); changed(); } UsedEffort *usedEffort(const Resource *r) const { return m_usedEffort.value(const_cast(r) ); } const ResourceUsedEffortMap &usedEffortMap() const { return m_usedEffort; } void setActualEffort(Resource *resource, const QDate &date, const UsedEffort::ActualEffort &value); // FIXME name clash UsedEffort::ActualEffort getActualEffort(Resource *resource, const QDate &date) const; void changed(int property = -1); Node *node() const { return m_node; } void setNode(Node *node) { m_node = node; } enum Entrymode { FollowPlan, EnterCompleted, EnterEffortPerTask, EnterEffortPerResource }; void setEntrymode(Entrymode mode) { m_entrymode = mode; } Entrymode entrymode() const { return m_entrymode; } void setEntrymode(const QString &mode); QString entryModeToString() const; QStringList entrymodeList() const; EffortCostMap effortCostPrDay(QDate start, QDate end, long id = -1) const; /// Returns the actual effort and cost pr day used by @p resource EffortCostMap effortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE) const; protected: void copy(const Completion ©); double averageCostPrHour(QDate date, long id) const; std::pair actualStartEndDates() const; private: Node *m_node; bool m_started, m_finished; DateTime m_startTime, m_finishTime; EntryList m_entries; ResourceUsedEffortMap m_usedEffort; Entrymode m_entrymode; #ifndef NDEBUG public: void printDebug(const QByteArray &ident) const; #endif }; /** * The WorkPackage class controls work flow for a task */ class PLANKERNEL_EXPORT WorkPackage { public: /// @enum WPTransmitionStatus describes if this package was sent or received enum WPTransmitionStatus { TS_None, /// Not sent nor received TS_Send, /// Package was sent to resource TS_Receive, /// Package was received from resource TS_Rejected /// Received package was rejected by project manager }; explicit WorkPackage(Task *task = 0); explicit WorkPackage(const WorkPackage &wp); virtual ~WorkPackage(); Task *parentTask() const { return m_task; } void setParentTask(Task *task) { m_task = task; } /// Returns the transmission status of this package WPTransmitionStatus transmitionStatus() const { return m_transmitionStatus; } void setTransmitionStatus(WPTransmitionStatus sts) { m_transmitionStatus = sts; } static QString transmitionStatusToString(WPTransmitionStatus sts, bool trans = false); static WPTransmitionStatus transmitionStatusFromString(const QString &sts); /// Load from document virtual bool loadXML(KoXmlElement &element, XMLLoaderObject &status); /// Save the full workpackage virtual void saveXML(QDomElement &element) const; /// Load from document virtual bool loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status); /// Save the full workpackage virtual void saveLoggedXML(QDomElement &element) const; /// Set schedule manager void setScheduleManager(ScheduleManager *sm); /// Return schedule manager ScheduleManager *scheduleManager() const { return m_manager; } /// Return the schedule id, or NOTSCHEDULED if no schedule manager is set long id() const { return m_manager ? m_manager->scheduleId() : NOTSCHEDULED; } Completion &completion(); const Completion &completion() const; void addLogEntry(DateTime &dt, const QString &str); QMap log() const; QStringList log(); /// Return a list of resources fetched from the appointments or requests /// merged with resources added to completion QList fetchResources(); /// Return a list of resources fetched from the appointments or requests /// merged with resources added to completion QList fetchResources(long id); /// Returns id of the resource that owns this package. If empty, task leader owns it. QString ownerId() const { return m_ownerId; } /// Set the resource that owns this package to @p owner. If empty, task leader owns it. void setOwnerId(const QString &id) { m_ownerId = id; } /// Returns the name of the resource that owns this package. QString ownerName() const { return m_ownerName; } /// Set the name of the resource that owns this package. void setOwnerName(const QString &name) { m_ownerName = name; } DateTime transmitionTime() const { return m_transmitionTime; } void setTransmitionTime(const DateTime &dt) { m_transmitionTime = dt; } /// Clear workpackage data void clear(); private: Task *m_task; ScheduleManager *m_manager; Completion m_completion; QString m_ownerName; QString m_ownerId; WPTransmitionStatus m_transmitionStatus; DateTime m_transmitionTime; QMap m_log; }; class PLANKERNEL_EXPORT WorkPackageSettings { public: WorkPackageSettings(); bool loadXML(const KoXmlElement &element); void saveXML(QDomElement &element) const; bool operator==(WorkPackageSettings settings) const; bool operator!=(WorkPackageSettings settings) const; bool usedEffort; bool progress; bool documents; bool remainingEffort; }; /** * A task in the scheduling software is represented by this class. A task * can be anything from 'build house' to 'drill hole' It will always mean * an activity. */ class PLANKERNEL_EXPORT Task : public Node { Q_OBJECT public: explicit Task(Node *parent = 0); explicit Task(const Task &task, Node *parent = 0); ~Task() override; /// Return task type. Can be Type_Task, Type_Summarytask ot Type_Milestone. int type() const override; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration() override; - /** - * Return the resource request made to group - * (There should be only one) - */ - ResourceGroupRequest *resourceGroupRequest(const ResourceGroup *group) const override; // void clearResourceRequests(); - void addRequest(ResourceGroup *group, int numResources); - void addRequest(ResourceGroupRequest *request); - void takeRequest(ResourceGroupRequest *request); void makeAppointments() override; QStringList requestNameList() const override; virtual QList requestedResources() const; bool containsRequest(const QString &/*identity*/) const override; ResourceRequest *resourceRequest(const QString &/*name*/) const override; /// Return the list of resources assigned to this task QStringList assignedNameList(long id = CURRENTSCHEDULE) const override; /** * Calculates if the assigned resource is overbooked * within the duration of this task */ void calcResourceOverbooked() override; /// Load from document bool load(KoXmlElement &element, XMLLoaderObject &status) override; /// Save to document void save(QDomElement &element, const XmlSaveContext &context) const override; /// Save appointments for schedule with id void saveAppointments(QDomElement &element, long id) const override; /// Save a workpackage document with schedule identity @p id void saveWorkPackageXML(QDomElement &element, long id) const override; /** * Returns a list of planned effort and cost for this task * for the interval start, end inclusive */ EffortCostMap plannedEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /** * Returns a list of planned effort and cost for the @p resource * for the interval @p start, @p end inclusive, useng schedule with identity @p id */ EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total planned effort for @p resource on this task (or subtasks) Duration plannedEffort(const Resource *resource, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total planned effort for this task (or subtasks) Duration plannedEffort(long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total planned effort for this task (or subtasks) on date Duration plannedEffort(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total planned effort for @p resource on this task (or subtasks) on date Duration plannedEffort(const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the planned effort up to and including date Duration plannedEffortTo(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the planned effort for @p resource up to and including date Duration plannedEffortTo(const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total actual effort for this task (or subtasks) Duration actualEffort() const override; /// Returns the total actual effort for this task (or subtasks) on date Duration actualEffort(QDate date) const override; /// Returns the actual effort up to and including date Duration actualEffortTo(QDate date) const override; /** * Returns the total planned cost for this task (or subtasks) */ EffortCost plannedCost(long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Planned cost up to and including date double plannedCostTo(QDate /*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns actual effort and cost up to and including @p date EffortCost actualCostTo(long int id, QDate date) const override; /** * Returns a list of actual effort and cost for this task * for the interval start, end inclusive */ EffortCostMap actualEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the actual effort and cost pr day used by @p resource EffortCostMap actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the effort planned to be used to reach the actual percent finished Duration budgetedWorkPerformed(QDate date, long id = CURRENTSCHEDULE) const override; /// Returns the cost planned to be used to reach the actual percent finished double budgetedCostPerformed(QDate date, long id = CURRENTSCHEDULE) const override; using Node::bcwsPrDay; /// Return map of Budgeted Cost of Work Scheduled pr day EffortCostMap bcwsPrDay(long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All) override; /// Budgeted Cost of Work Scheduled double bcws(QDate date, long id = CURRENTSCHEDULE) const override; using Node::bcwpPrDay; /// Return map of Budgeted Cost of Work Performed pr day (also includes bcwsPrDay) EffortCostMap bcwpPrDay(long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All) override; /// Budgeted Cost of Work Performed double bcwp(long id = CURRENTSCHEDULE) const override; /// Budgeted Cost of Work Performed (up to @p date) double bcwp(QDate date, long id = CURRENTSCHEDULE) const override; using Node::acwp; /// Map of Actual Cost of Work Performed EffortCostMap acwp(long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All) override; /// Actual Cost of Work Performed up to dat EffortCost acwp(QDate date, long id = CURRENTSCHEDULE) const override; /// Effort based performance index double effortPerformanceIndex(QDate date, long id = CURRENTSCHEDULE) const override; /// Schedule performance index double schedulePerformanceIndex(QDate date, long id = CURRENTSCHEDULE) const override; /// Cost performance index double costPerformanceIndex(long int id, QDate date, bool *error=0) const override; /** * Return the duration that an activity's start can be delayed * without affecting the project completion date. * An activity with positive float is not on the critical path. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration positiveFloat(long id = CURRENTSCHEDULE) const; void setPositiveFloat(Duration fl, long id = CURRENTSCHEDULE) const; /** * Return the duration by which the duration of an activity or path * has to be reduced in order to fulfill a timing- or dependency constraint. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration negativeFloat(long id = CURRENTSCHEDULE) const; void setNegativeFloat(Duration fl, long id = CURRENTSCHEDULE) const; /** * Return the duration by which an activity can be delayed or extended * without affecting the start of any succeeding activity. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration freeFloat(long id = CURRENTSCHEDULE) const; void setFreeFloat(Duration fl, long id = CURRENTSCHEDULE) const; /** * Return the duration from Early Start to Late Start. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration startFloat(long id = CURRENTSCHEDULE) const; /** * Return the duration from Early Finish to Late Finish. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration finishFloat(long id = CURRENTSCHEDULE) const; /** * A task is critical if positive float equals 0 * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ bool isCritical(long id = CURRENTSCHEDULE) const override; /** * Set current schedule to schedule with identity id, for me and my children. * @param id Schedule identity */ void setCurrentSchedule(long id) override; /** * The assigned resources can not fulfill the estimated effort. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ bool effortMetError(long id = CURRENTSCHEDULE) const override; /// @return true if this task has been started bool isStarted() const; Completion &completion() { return m_workPackage.completion(); } const Completion &completion() const { return m_workPackage.completion(); } WorkPackage &workPackage() { return m_workPackage; } const WorkPackage &workPackage() const { return m_workPackage; } int workPackageLogCount() const { return m_packageLog.count(); } QList workPackageLog() const { return m_packageLog; } void addWorkPackage(WorkPackage *wp); void removeWorkPackage(WorkPackage *wp); WorkPackage *workPackageAt(int index) const; QString wpOwnerName() const; WorkPackage::WPTransmitionStatus wpTransmitionStatus() const; DateTime wpTransmitionTime() const; /** * Returns the state of the task * @param id The identity of the schedule used when calculating the state */ uint state(long id = CURRENTSCHEDULE) const override; /// Check if this node has any dependent child nodes bool isEndNode() const override; /// Check if this node has any dependent parent nodes bool isStartNode() const override; QList parentProxyRelations() const { return m_parentProxyRelations; } QList childProxyRelations() const { return m_childProxyRelations; } /** * Calculates and returns the duration of the node. * Uses the correct expected-, optimistic- or pessimistic effort * dependent on @p use. * @param time Where to start calculation. * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @param backward If true, time specifies when the task should end. */ Duration duration(const DateTime &time, int use, bool backward) override; /** * Return the duration calculated on bases of the estimates calendar */ Duration length(const DateTime &time, Duration duration, bool backward); Duration length(const DateTime &time, Duration uration, Schedule *sch, bool backward); /// Copy info from parent schedule void copySchedule(); /// Copy intervals from parent schedule void copyAppointments(); /// Copy intervals from parent schedule in the range @p start, @p end void copyAppointments(const DateTime &start, const DateTime &end = DateTime()); Q_SIGNALS: void workPackageToBeAdded(KPlato::Node *node, int row); void workPackageAdded(KPlato::Node *node); void workPackageToBeRemoved(KPlato::Node *node, int row); void workPackageRemoved(KPlato::Node *node); public: void initiateCalculation(MainSchedule &sch) override; /** * Sets up the lists used for calculation. * This includes adding summarytasks relations to subtasks * and lists for start- and endnodes. */ void initiateCalculationLists(MainSchedule &sch) override; /** * Calculates early start and early finish, first for all predeccessors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ DateTime calculateForward(int use) override; /** * Calculates ref m_durationForward from ref earliestStart and * returns the resulting end time (early finish), * which will be used as the successors ref earliestStart. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ DateTime calculateEarlyFinish(int use) override; /** * Calculates late start and late finish, first for all successors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ DateTime calculateBackward(int use) override; /** * Calculates ref m_durationBackward from ref latestFinish and * returns the resulting start time (late start), * which will be used as the predecessors ref latestFinish. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ DateTime calculateLateStart(int use) override; /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param earliest The task is not scheduled to start earlier than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ DateTime scheduleForward(const DateTime &earliest, int use) override; /** * Schedules the task within the limits of start time and latestFinish, * Calculates end time and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ DateTime scheduleFromStartTime(int use) override; /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param latest The task is not scheduled to end later than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ DateTime scheduleBackward(const DateTime &latest, int use) override; /** * Schedules the task within the limits of end time and latestFinish. * Calculates endTime and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ DateTime scheduleFromEndTime(int use) override; /** * Summarytasks (with milestones) need special treatment because * milestones are always 'glued' to their predecessors. */ void adjustSummarytask() override; /// Calculate the critical path bool calcCriticalPath(bool fromEnd) override; void calcFreeFloat() override; // Proxy relations are relations to/from summarytasks. // These relations are distributed to the child tasks before calculation. void clearProxyRelations() override; void addParentProxyRelations(const QList &) override; void addChildProxyRelations(const QList &) override; void addParentProxyRelation(Node *, const Relation *) override; void addChildProxyRelation(Node *, const Relation *) override; public: DateTime earlyStartDate(); void setEarlyStartDate(DateTime value); DateTime earlyFinishDate(); void setEarlyFinishDate(DateTime value); DateTime lateStartDate(); void setLateStartDate(DateTime value); DateTime lateFinishDate(); void setLateFinishDate(DateTime value); int activitySlack(); void setActivitySlack(int value); int activityFreeMargin(); void setActivityFreeMargin(int value); protected: /** * Return the duration calculated on bases of the requested resources */ Duration calcDuration(const DateTime &time, Duration effort, bool backward); private: DateTime calculateSuccessors(const QList &list, int use); DateTime calculatePredeccessors(const QList &list, int use); DateTime scheduleSuccessors(const QList &list, int use); DateTime schedulePredeccessors(const QList &list, int use); /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available after @p dt /// Has working resource(s) allocated: Returns the earliest time a resource can start work after @p dt, and checks appointments if @p sch is not null. DateTime workTimeAfter(const DateTime &dt, Schedule *sch = 0) const; /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available before @p dt /// Has working resource(s) allocated: Returns the latest time a resource can finish work, and checks appointments if @p sch is not null. DateTime workTimeBefore(const DateTime &dt, Schedule *sch = 0) const; private: QList m_resource; QList m_parentProxyRelations; QList m_childProxyRelations; // This list store pointers to linked task QList m_requiredTasks; WorkPackage m_workPackage; QList m_packageLog; bool m_calculateForwardRun; bool m_calculateBackwardRun; bool m_scheduleForwardRun; bool m_scheduleBackwardRun; }; } //KPlato namespace Q_DECLARE_METATYPE(KPlato::Completion::UsedEffort::ActualEffort) #ifndef QT_NO_DEBUG_STREAM PLANKERNEL_EXPORT QDebug operator<<(QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae); #endif #endif diff --git a/src/libs/kernel/tests/AccountsTester.cpp b/src/libs/kernel/tests/AccountsTester.cpp index 749ce1d3..c882e04d 100644 --- a/src/libs/kernel/tests/AccountsTester.cpp +++ b/src/libs/kernel/tests/AccountsTester.cpp @@ -1,624 +1,622 @@ /* This file is part of the KDE project Copyright (C) 2008 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "AccountsTester.h" #include "kptaccount.h" #include "kptduration.h" #include "kptnode.h" #include "kpttask.h" #include "debug.cpp" #include namespace KPlato { void AccountsTester::init() { project = new Project(); project->setId(project->uniqueCalendarId()); project->registerNodeId(project); today = QDate::currentDate(); tomorrow = today.addDays(1); yesterday = today.addDays(-1); nextweek = today.addDays(7); t1 = QTime(9, 0, 0); t2 = QTime(17, 0, 0); length = t1.msecsTo(t2); t = project->createTask(); t->setName("T1"); project->addTask(t, project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); sm = project->createScheduleManager("Test Plan"); project->addScheduleManager(sm); // standard worktime defines 8 hour day as default Calendar *c = new Calendar(); c->setDefault(true); for (int i=1; i <= 7; ++i) { CalendarDay *d = c->weekday(i); d->setState(CalendarDay::Working); d->addInterval(t1, length); } project->addCalendar(c); ResourceGroup *g = new ResourceGroup(); project->addResourceGroup(g); r = new Resource(); r->setAvailableFrom(QDateTime(yesterday, QTime(), Qt::LocalTime)); r->setCalendar(c); r->setNormalRate(100.0); project->addResource(r); r->addParentGroup(g); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r, 100); - t->requests().addResourceRequest(rr, gr); + t->requests().addResourceRequest(rr); t->estimate()->setType(Estimate::Type_Effort); //qDebug()<<"Calculate forward, Task: ASAP -----------------------------------"; project->setConstraintStartTime(DateTime(today, QTime())); project->setConstraintEndTime(DateTime(tomorrow, QTime())); sm->createSchedules(); project->calculate(*sm); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); //### QVERIFY(t->schedulingError() == false); sm->setBaselined(true); } void AccountsTester::cleanup() { delete project; project = 0; t = 0; r = 0; sm = 0; topaccount = 0; } void AccountsTester::defaultAccount() { Account *a = new Account("Default Account"); project->accounts().insert(a); project->accounts().setDefaultAccount(a); EffortCostMap ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().setEntrymode(Completion::FollowPlan); ec = project->accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); t->completion().setEntrymode(Completion::EnterCompleted); ec = project->accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setStarted(true); t->completion().setStartTime(DateTime(tomorrow, QTime())); t->completion().setPercentFinished(tomorrow, 50); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); t->completion().setEntrymode(Completion::EnterEffortPerTask); t->completion().setActualEffort(tomorrow, Duration(0, 4, 0)); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 400.0); t->completion().setEntrymode(Completion::EnterEffortPerResource); Completion::UsedEffort *ue = new Completion::UsedEffort(); Completion::UsedEffort::ActualEffort e(Duration(0, 6, 0)) ; ue->setEffort(tomorrow, e); t->completion().addUsedEffort(r, ue); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 6.0); QCOMPARE(ec.totalCost(), 600.0); } void AccountsTester::costPlaces() { EffortCostMap ec; Account *top = new Account("Top account"); project->accounts().insert(top); Account *a = new Account("Running account"); project->accounts().insert(a, top); a->addRunning(*t); ec = a->plannedCost(sm->scheduleId()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); a = new Account("Startup account"); project->accounts().insert(a, top); a->addStartup(*t); t->setStartupCost(200.0); ec = a->plannedCost(sm->scheduleId()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 200.0); a = new Account("Shutdown cost"); project->accounts().insert(a, top); a->addShutdown(*t); t->setShutdownCost(300.0); ec = a->plannedCost(sm->scheduleId()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 300.0); ec = top->plannedCost(sm->scheduleId()); // Debug::print(top, sm->scheduleId(), "All planned cost in child accounts--------"); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 1300.0); a = new Account("All cost in one account"); project->accounts().insert(a); a->addRunning(*t); a->addStartup(*t); a->addShutdown(*t); ec = a->plannedCost(sm->scheduleId()); // Debug::print(a, sm->scheduleId(), "All planned cost in one account-----------"); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 1300.0); } void AccountsTester::startupDefault() { Account *a = new Account("Default Account"); project->accounts().insert(a); project->accounts().setDefaultAccount(a); EffortCostMap ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<setStartupCost(25.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().entryModeToString(); ec = project->accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().isStarted()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setStarted(true); t->completion().setStartTime(DateTime(tomorrow, QTime())); t->completion().setPercentFinished(tomorrow, 50); QVERIFY(t->completion().isStarted()); qDebug()<completion().startTime()<<":"<startTime().date()<accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); t->completion().setEntrymode(Completion::EnterEffortPerTask); t->completion().setActualEffort(tomorrow, Duration(0, 4, 0)); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); } void AccountsTester::startupAccount() { Account *a = new Account("Account"); project->accounts().insert(a); a->addStartup(*t); // planned wo startup cost EffortCostMap ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<setStartupCost(25.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().isStarted()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); // start task (tomorrow), no actual effort t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setStarted(true); t->completion().setStartTime(DateTime(tomorrow, QTime())); QVERIFY(t->completion().isStarted()); ec = project->accounts().actualCost(*a, tomorrow, tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); // add actual effort, but no running cost t->completion().setEntrymode(Completion::EnterEffortPerTask); t->completion().setActualEffort(tomorrow, Duration(0, 4, 0)); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); // add running account a->addRunning(*t); // planned wo startup cost t->completion().setStarted(false); t->setStartupCost(0.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<setStartupCost(25.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().isStarted()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); // start task (tomorrow), 4h actual effort from before t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setStarted(true); t->completion().setStartTime(DateTime(tomorrow, QTime())); QVERIFY(t->completion().isStarted()); Debug::print(t, "Started tomorrow, 4h actual effort"); Debug::print(t->completion(), t->name()); ec = project->accounts().actualCost(*a, tomorrow, tomorrow); Debug::print(ec); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); } void AccountsTester::shutdownAccount() { Account *a = new Account("Account"); project->accounts().insert(a); a->addShutdown(*t); t->completion().setStarted(true); t->completion().setStartTime(t->startTime()); // planned wo shutdown cost EffortCostMap ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<setShutdownCost(25.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().isFinished()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); // finish task (tomorrow), no actual effort t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setFinished(true); t->completion().setFinishTime(DateTime(tomorrow, QTime())); QVERIFY(t->completion().isFinished()); ec = project->accounts().actualCost(*a, tomorrow, tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); // add actual effort, no running account t->completion().setEntrymode(Completion::EnterEffortPerTask); t->completion().setActualEffort(tomorrow, Duration(0, 4, 0)); ec = project->accounts().actualCost(*a, t->startTime().date(), tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 25.0); // add running account a->addRunning(*t); t->completion().setFinished(false); t->setShutdownCost(0.0); // planned wo finish cost ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); ec = project->accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); Debug::print(t, "planned wo finish cost, with running", true); Debug::print(t->completion(), t->name()); Debug::print(ec); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); // planned with startup cost t->setShutdownCost(25.0); ec = project->accounts().plannedCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<accounts().actualCost(*a, t->startTime().date(), t->endTime().date()); qDebug()<startTime()<endTime()<completion().isFinished()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); // finish task (tomorrow), 4h actual effort from before t->completion().setEntrymode(Completion::EnterCompleted); t->completion().setFinished(true); t->completion().setFinishTime(DateTime(tomorrow, QTime())); QVERIFY(t->completion().isFinished()); ec = project->accounts().actualCost(*a, tomorrow, tomorrow); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); ec = project->accounts().actualCost(*a, QDate(), QDate()); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); } void AccountsTester::subaccounts() { Account *a1 = new Account("Account"); project->accounts().insert(a1); Account *a2 = new Account("Sub-Account"); project->accounts().insert(a2, a1); project->accounts().setDefaultAccount(a2); EffortCostMap ec = project->accounts().plannedCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); ec = project->accounts().plannedCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); ec = project->accounts().actualCost(*a2); qDebug()<startTime()<endTime()<accounts().actualCost(*a1); qDebug()<startTime()<endTime()<completion().setEntrymode(Completion::FollowPlan); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 800.0); t->completion().setEntrymode(Completion::EnterCompleted); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); t->completion().setEntrymode(Completion::EnterEffortPerTask); t->completion().setStarted(true); t->completion().setStartTime(DateTime(tomorrow, QTime())); t->completion().setPercentFinished(tomorrow, 50); t->completion().setActualEffort(tomorrow, Duration(0, 4, 0)); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 400.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 400.0); t->setStartupCost(25.0); a1->addStartup(*t); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 400.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); project->accounts().setDefaultAccount(a1); ec = project->accounts().plannedCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); ec = project->accounts().plannedCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 825.0); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 0.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 425.0); t->setShutdownCost(1.0); a2->addShutdown(*t); ec = project->accounts().plannedCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 1.0); ec = project->accounts().plannedCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 8.0); QCOMPARE(ec.totalCost(), 826.0); t->completion().setFinished(true); t->completion().setFinishTime(DateTime(tomorrow, QTime())); ec = project->accounts().actualCost(*a2); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 0.0); QCOMPARE(ec.totalCost(), 1.0); ec = project->accounts().actualCost(*a1); QCOMPARE(ec.totalEffort().toDouble(Duration::Unit_h), 4.0); QCOMPARE(ec.totalCost(), 426.0); } void AccountsTester::deleteAccount() { QVERIFY(project->accounts().allAccounts().isEmpty()); qInfo()<<"Add/delete one account"; Account *a1 = new Account("Account"); project->accounts().insert(a1); QCOMPARE(project->accounts().allAccounts().count(), 1); QCOMPARE(project->accounts().allAccounts().first(), a1); delete a1; QVERIFY(project->accounts().allAccounts().isEmpty()); qInfo()<<"Add/delete one account with one sub-account"; a1 = new Account("Account 1"); project->accounts().insert(a1); Account *a2 = new Account("Account 1.1"); project->accounts().insert(a2, a1); QCOMPARE(project->accounts().allAccounts().count(), 2); qInfo()<<"Delete top account"; delete a1; QVERIFY(project->accounts().allAccounts().isEmpty()); qInfo()<<"Add/delete one account with one sub-account"; a1 = new Account("Account 1"); project->accounts().insert(a1); a2 = new Account("Account 1.1"); project->accounts().insert(a2, a1); project->accounts().setDefaultAccount(a2); QCOMPARE(project->accounts().allAccounts().count(), 2); qInfo()<<"Delete sub account"; delete a2; QVERIFY(project->accounts().defaultAccount() == 0); QCOMPARE(project->accounts().allAccounts().count(), 1); qInfo()<<"Delete top account"; delete a1; QVERIFY(project->accounts().allAccounts().isEmpty()); } } //namespace KPlato QTEST_GUILESS_MAIN(KPlato::AccountsTester) diff --git a/src/libs/kernel/tests/PerformanceTester.cpp b/src/libs/kernel/tests/PerformanceTester.cpp index 4fcaf13a..f1a0adc8 100644 --- a/src/libs/kernel/tests/PerformanceTester.cpp +++ b/src/libs/kernel/tests/PerformanceTester.cpp @@ -1,1127 +1,1121 @@ /* This file is part of the KDE project Copyright (C) 2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "PerformanceTester.h" #include "kpttask.h" #include "kptduration.h" #include "kpteffortcostmap.h" #include "kptcommand.h" #include "debug.cpp" namespace KPlato { void PerformanceTester::cleanup() { delete p1; p1 = 0; r1 = 0; r2 = 0; s1 = 0; t1 = 0; s2 = 0; m1 = 0; } void PerformanceTester::init() { p1 = new Project(); p1->setId(p1->uniqueNodeId()); p1->registerNodeId(p1); p1->setName("PerformanceTester"); p1->setConstraintStartTime(DateTime(QDateTime::fromString("2010-11-19T08:00:00", Qt::ISODate))); p1->setConstraintEndTime(DateTime(QDateTime::fromString("2010-11-29T16:00:00", Qt::ISODate))); Calendar *c = new Calendar(); c->setDefault(true); QTime ta(8, 0, 0); QTime tb (16, 0, 0); int length = ta.msecsTo(tb); for (int i=1; i <= 7; ++i) { CalendarDay *d = c->weekday(i); d->setState(CalendarDay::Working); d->addInterval(ta, length); } p1->addCalendar(c); s1 = p1->createTask(); s1->setName("S1"); p1->addTask(s1, p1); t1 = p1->createTask(); t1->setName("T1"); t1->estimate()->setUnit(Duration::Unit_d); t1->estimate()->setExpectedEstimate(5.0); t1->estimate()->setType(Estimate::Type_Effort); p1->addSubTask(t1, s1); s2 = p1->createTask(); s2->setName("S2"); p1->addTask(s2, p1); m1 = p1->createTask(); m1->estimate()->setExpectedEstimate(0); m1->setName("M1"); p1->addSubTask(m1, s2); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); p1->addResourceGroup(g); r1 = new Resource(); r1->setName("R1"); r1->setNormalRate(1.0); p1->addResource(r1); r1->addParentGroup(g); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t1->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r1, 100); - t1->requests().addResourceRequest(rr, gr); + t1->requests().addResourceRequest(rr); // material resource ResourceGroup *m = new ResourceGroup(); m->setName("M1"); m->setType(ResourceGroup::Type_Material); p1->addResourceGroup(m); r2 = new Resource(); r2->setName("Material"); r2->setType(Resource::Type_Material); r2->setCalendar(c); // limit availability to working hours r2->setNormalRate(0.0); // NOTE p1->addResource(r2); r2->addParentGroup(m); r3 = new Resource(); r3->setName("Material 2"); r3->setType(Resource::Type_Material); r3->setNormalRate(6.0); p1->addResource(r3); r3->addParentGroup(m); - - gr = new ResourceGroupRequest(m); - t1->addRequest(gr); + rr = new ResourceRequest(r2, 100); - t1->requests().addResourceRequest(rr, gr); + t1->requests().addResourceRequest(rr); ScheduleManager *sm = p1->createScheduleManager("S1"); p1->addScheduleManager(sm); sm->createSchedules(); p1->calculate(*sm); t1->completion().setStarted(true); t1->completion().setStartTime(t1->startTime()); Debug::print(p1, "Project data", true); QCOMPARE(t1->startTime(), p1->mustStartOn()); QCOMPARE(t1->endTime(), t1->startTime() + Duration(4, 8, 0)); } void PerformanceTester::bcwsPrDayTask() { QDate d = t1->startTime().date().addDays(-1); EffortCostMap ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); qDebug()<bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); // add startup cost t1->setStartupCost(0.5); QCOMPARE(t1->startupCost(), 0.5); d = t1->startTime().date(); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.5); // add shutdown cost t1->setShutdownCost(0.5); QCOMPARE(t1->shutdownCost(), 0.5); d = t1->endTime().date(); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.5); // check sub-task d = t1->startTime().date(); ecm = s1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.5); d = s1->endTime().date(); ecm = s1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.5); } void PerformanceTester::bcwpPrDayTask() { QDate d = t1->startTime().date(); EffortCostMap ecm = t1->bcwpPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); ModifyCompletionPercentFinishedCmd *cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecm.bcwpCostOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecm.bcwpCostOnDate(d), 12.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 40); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 32.0); QCOMPARE(ecm.bcwpCostOnDate(d), 16.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecm.bcwpCostOnDate(d), 20.0); // modify last day cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0); // re-check d = t1->startTime().date(); ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecm.bcwpCostOnDate(d), 8.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecm.bcwpCostOnDate(d), 12.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 32.0); QCOMPARE(ecm.bcwpCostOnDate(d), 16.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0); // add startup cost t1->completion().setStartTime(t1->startTime()); t1->setStartupCost(0.5); d = t1->startTime().date(); ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5); //10% progress // add shutdown cost t1->setShutdownCost(0.5); t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); d = t1->endTime().date(); ecm = t1->bcwpPrDay(); Debug::print(t1->completion(), t1->name(), "BCWP Performance with shutdown cost"); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.5); //100% progress // check sub-task d = s1->startTime().date(); ecm = s1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5); //10% progress // add shutdown cost d = s1->endTime().date(); ecm = s1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.5); //100% progress } void PerformanceTester::acwpPrDayTask() { NamedCommand *cmd = new ModifyCompletionEntrymodeCmd(t1->completion(), Completion::EnterEffortPerResource); cmd->execute(); delete cmd; Completion::UsedEffort *ue = new Completion::UsedEffort(); t1->completion().addUsedEffort(r1, ue); QDate d = t1->startTime().date(); EffortCostMap ecb = t1->bcwpPrDay(); EffortCostMap eca = t1->acwp(); QCOMPARE(ecb.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecb.costOnDate(d), 8.0); QCOMPARE(ecb.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecb.bcwpCostOnDate(d), 0.0); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.0); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); // Debug::print(t1->completion(), t1->name(), QString("ACWP on date: %1").arg(d.toString(Qt::ISODate))); // Debug::print(eca, "ACWP"); QCOMPARE(ecb.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecb.bcwpCostOnDate(d), 4.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 6, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecb.bcwpCostOnDate(d), 8.0); QCOMPARE(eca.hoursOnDate(d), 6.0); QCOMPARE(eca.costOnDate(d), 6.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecb.bcwpCostOnDate(d), 12.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecb.bcwpCostOnDate(d), 20.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 80); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 12, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 64.0); QCOMPARE(ecb.bcwpCostOnDate(d), 32.0); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.0); // add startup cost t1->completion().setStartTime(t1->startTime()); t1->setStartupCost(0.5); d = t1->startTime().date(); eca = t1->acwp(); Debug::print(t1->completion(), t1->name(), "ACWP Performance with startup cost"); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.5); // add shutdown cost d = t1->endTime().date(); t1->setShutdownCost(0.25); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); eca = t1->acwp(); Debug::print(t1->completion(), t1->name(), "ACWP Performance with shutdown cost"); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.25); // check sub-task d = s1->startTime().date(); eca = s1->acwp(); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.5); // add shutdown cost d = s1->endTime().date(); eca = s1->acwp(); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.25); } void PerformanceTester::bcwsMilestone() { QDate d = m1->startTime().date().addDays(-1); EffortCostMap ecm = m1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration::zeroDuration); QCOMPARE(ecm.costOnDate(d), 0.0); d = d.addDays(1); ecm = m1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration::zeroDuration); QCOMPARE(ecm.costOnDate(d), 0.0); // add startup cost m1->setStartupCost(0.5); QCOMPARE(m1->startupCost(), 0.5); d = m1->startTime().date(); ecm = m1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration::zeroDuration); QCOMPARE(ecm.costOnDate(d), 0.5); // add shutdown cost m1->setShutdownCost(0.25); QCOMPARE(m1->shutdownCost(), 0.25); d = m1->endTime().date(); ecm = m1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration::zeroDuration); QCOMPARE(ecm.costOnDate(d), 0.75); } void PerformanceTester::bcwpMilestone() { QDate d = m1->startTime().date(); EffortCostMap ecm = m1->bcwpPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration::zeroDuration); QCOMPARE(ecm.costOnDate(d), 0.0); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); ModifyCompletionPercentFinishedCmd *cmd = new ModifyCompletionPercentFinishedCmd(m1->completion(), d, 100); cmd->execute(); delete cmd; ecm = m1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); // add startup cost d = m1->startTime().date(); m1->completion().setStarted(true); m1->completion().setStartTime(m1->startTime()); m1->setStartupCost(0.5); ecm = m1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.5); // add shutdown cost d = m1->endTime().date(); m1->setShutdownCost(0.25); m1->completion().setFinished(true); m1->completion().setFinishTime(m1->endTime()); ecm = m1->bcwpPrDay(); Debug::print(m1->completion(), m1->name(), "BCWP Milestone with shutdown cost"); Debug::print(ecm, "BCWP Milestone"); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.75); // check sub-milestone d = s2->startTime().date(); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.75); } void PerformanceTester::acwpMilestone() { QDate d = m1->startTime().date(); EffortCostMap eca = m1->acwp(); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.0); // add startup cost d = m1->startTime().date(); m1->completion().setStarted(true); m1->completion().setStartTime(m1->startTime()); m1->setStartupCost(0.5); eca = m1->acwp(); Debug::print(m1->completion(), m1->name(), "ACWP Milestone with startup cost"); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.5); // add shutdown cost d = m1->endTime().date(); m1->setShutdownCost(0.25); NamedCommand *cmd = new ModifyCompletionPercentFinishedCmd(m1->completion(), d, 100); cmd->execute(); delete cmd; m1->completion().setFinished(true); m1->completion().setFinishTime(m1->endTime()); eca = m1->acwp(); Debug::print(m1->completion(), m1->name(), "ACWP Milestone with shutdown cost"); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.75); // check sub-milestone d = s2->endTime().date(); eca = s2->acwp(); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.75); } void PerformanceTester::bcwsPrDayTaskMaterial() { r2->setNormalRate(0.1); QDate d = t1->startTime().date().addDays(-1); EffortCostMap ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); //material+work resource QCOMPARE(ecm.costOnDate(d), 8.8); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.8); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.8); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.8); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.8); d = d.addDays(1); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); // add startup cost t1->setStartupCost(0.5); QCOMPARE(t1->startupCost(), 0.5); d = t1->startTime().date(); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5 + 0.8); // add shutdown cost t1->setShutdownCost(0.25); QCOMPARE(t1->shutdownCost(), 0.25); d = t1->endTime().date(); ecm = t1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.25 + 0.8); // check sub-task d = s1->startTime().date(); ecm = s1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5 + 0.8); d = s1->endTime().date(); ecm = s1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.25 + 0.8); } void PerformanceTester::bcwpPrDayTaskMaterial() { r2->setNormalRate(0.1); QDate d = t1->startTime().date(); EffortCostMap ecm = t1->bcwpPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.8); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); ModifyCompletionPercentFinishedCmd *cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.4); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecm.bcwpCostOnDate(d), 8.0 + 0.8); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecm.bcwpCostOnDate(d), 12.0 + 1.2); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 40); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 32.0); QCOMPARE(ecm.bcwpCostOnDate(d), 16.0 + 1.6); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecm.bcwpCostOnDate(d), 20.0 + 2.0); // modify last day cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 4.0); // add startup cost t1->completion().setStartTime(t1->startTime()); t1->setStartupCost(0.5); d = t1->startTime().date(); ecm = t1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5 + 0.4); // 10% progress // add shutdown cost t1->setShutdownCost(0.25); t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); d = t1->endTime().date(); ecm = t1->bcwpPrDay(); Debug::print(t1->completion(), t1->name(), "BCWP Material with shutdown cost"); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.25 + 4.0); // 100% progress // check sub-task d = s1->startTime().date(); ecm = s1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5 + 0.4); // 10% progress d = s1->endTime().date(); ecm = s1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.25 + 4.0); // 100% progress } void PerformanceTester::acwpPrDayTaskMaterial() { r2->setNormalRate(0.1); NamedCommand *cmd = new ModifyCompletionEntrymodeCmd(t1->completion(), Completion::EnterEffortPerResource); cmd->execute(); delete cmd; Completion::UsedEffort *ue = new Completion::UsedEffort(); t1->completion().addUsedEffort(r1, ue); QDate d = t1->startTime().date(); EffortCostMap ecb = t1->bcwpPrDay(); EffortCostMap eca = t1->acwp(); QCOMPARE(ecb.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecb.costOnDate(d), 8.0 + 0.8); QCOMPARE(ecb.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecb.bcwpCostOnDate(d), 0.0); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.0); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); // Debug::print(t1->completion(), t1->name(), QString("ACWP on date: %1").arg(d.toString(Qt::ISODate))); // Debug::print(eca, "ACWP"); QCOMPARE(ecb.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecb.bcwpCostOnDate(d), 4.0 + 0.4); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 6, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecb.bcwpCostOnDate(d), 8.0 + 0.8); QCOMPARE(eca.hoursOnDate(d), 6.0); QCOMPARE(eca.costOnDate(d), 6.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecb.bcwpCostOnDate(d), 12.0 + 1.2); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecb.bcwpCostOnDate(d), 20.0 + 2.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 80); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 12, 0))); cmd->execute(); delete cmd; ecb = t1->bcwpPrDay(); eca = t1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 64.0); QCOMPARE(ecb.bcwpCostOnDate(d), 32.0 + 3.2); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.0); // add startup cost t1->completion().setStartTime(t1->startTime()); t1->setStartupCost(0.5); d = t1->startTime().date(); eca = t1->acwp(); Debug::print(t1->completion(), t1->name(), "ACWP Performance with startup cost"); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0 + 0.5); //NOTE: material not included // add shutdown cost d = t1->endTime().date(); t1->setShutdownCost(0.25); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); eca = t1->acwp(); Debug::print(t1->completion(), t1->name(), "ACWP Performance with shutdown cost"); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.0 + 0.25); //NOTE: material not included // check sub-task d = s1->startTime().date(); eca = s1->acwp(); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0 + 0.5); //NOTE: material not included // add shutdown cost d = s1->endTime().date(); eca = s1->acwp(); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.0 + 0.25); //NOTE: material not included } void PerformanceTester::bcwsPrDayProject() { QDate d = p1->startTime().date().addDays(-1); EffortCostMap ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); d = d.addDays(1); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 0, 0)); QCOMPARE(ecm.costOnDate(d), 0.0); // add startup cost to task t1->setStartupCost(0.5); QCOMPARE(t1->startupCost(), 0.5); d = p1->startTime().date(); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5); // add shutdown cost to task t1->setShutdownCost(0.2); QCOMPARE(t1->shutdownCost(), 0.2); d = p1->endTime().date(); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.2); // add startup cost to milestone m1->setStartupCost(0.4); QCOMPARE(m1->startupCost(), 0.4); d = p1->startTime().date(); ecm = p1->bcwsPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5 + 0.4); // add shutdown cost to milestone m1->setShutdownCost(0.3); QCOMPARE(m1->shutdownCost(), 0.3); d = p1->startTime().date(); ecm = p1->bcwsPrDay(); //Debug::print(p1, "BCWS Project", true); //Debug::print(ecm, "BCWS Project"); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5 + 0.4 + 0.3); } void PerformanceTester::bcwpPrDayProject() { QDate d = p1->startTime().date(); EffortCostMap ecm = p1->bcwpPrDay(); QCOMPARE(ecm.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecm.costOnDate(d), 8.0); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); ModifyCompletionPercentFinishedCmd *cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecm.bcwpCostOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecm.bcwpCostOnDate(d), 12.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 40); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 32.0); QCOMPARE(ecm.bcwpCostOnDate(d), 16.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecm.bcwpCostOnDate(d), 20.0); // modify last day cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0); // re-check d = p1->startTime().date(); ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecm.bcwpCostOnDate(d), 8.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecm.bcwpCostOnDate(d), 12.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 32.0); QCOMPARE(ecm.bcwpCostOnDate(d), 16.0); d = d.addDays(1); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0); // add startup cost to task t1->completion().setStartTime(t1->startTime()); t1->setStartupCost(0.5); d = p1->startTime().date(); ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5); // 10% progress // add shutdown cost to task t1->setShutdownCost(0.25); t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); d = p1->endTime().date(); ecm = p1->bcwpPrDay(); QCOMPARE(ecm.bcwpEffortOnDate(d), 80.0); QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.25); // 100% progress // check with ECCT_EffortWork d = p1->startTime().date(); ecm = p1->bcwpPrDay(CURRENTSCHEDULE, ECCT_EffortWork); QCOMPARE(ecm.totalEffort(), Duration(40.0, Duration::Unit_h)); QCOMPARE(ecm.hoursOnDate(d), 8.0); // hours from r1 QCOMPARE(ecm.costOnDate(d), 8.0 + 0.5); // cost from r1 (1.0) + r2 (0.0) + startup cost (0.5) QCOMPARE(ecm.bcwpEffortOnDate(d), 4.0); // 10% progress QCOMPARE(ecm.bcwpCostOnDate(d), 4.0 + 0.5); // 10% progress d = p1->endTime().date(); ecm = p1->bcwpPrDay(CURRENTSCHEDULE, ECCT_EffortWork); QCOMPARE(ecm.bcwpEffortOnDate(d), 40.0); // hours from r1 QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.25); // 100% progress // add a new task with a material resource Task *tt = p1->createTask(); tt->setName("TT"); p1->addTask(tt, p1); tt->estimate()->setUnit(Duration::Unit_d); tt->estimate()->setExpectedEstimate(5.0); tt->estimate()->setType(Estimate::Type_Duration); tt->estimate()->setCalendar(p1->calendarAt(0)); r3->setNormalRate(1.0); - ResourceGroupRequest *gr = new ResourceGroupRequest(r3->parentGroups().first()); - tt->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r3, 100); - tt->requests().addResourceRequest(rr, gr); + tt->requests().addResourceRequest(rr); ScheduleManager *sm = p1->createScheduleManager(""); p1->addScheduleManager(sm); sm->createSchedules(); p1->calculate(*sm); QString s = " Material resource, no progress "; Debug::print(tt, s, true); d = tt->endTime().date(); ecm = tt->bcwpPrDay(sm->scheduleId(), ECCT_EffortWork); Debug::print(ecm, "BCWP: " + tt->name() + s); QCOMPARE(ecm.hoursOnDate(d), 0.0); QCOMPARE(ecm.costOnDate(d), 16.0); QCOMPARE(ecm.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecm.bcwpCostOnDate(d), 0.0); // 0% progress d = p1->endTime().date(); ecm = p1->bcwpPrDay(sm->scheduleId(), ECCT_EffortWork); Debug::print(p1, s, true); Debug::print(ecm, "BCWP Project: " + p1->name() + s); QCOMPARE(ecm.bcwpEffortOnDate(d), 40.0); // hours from r1 QCOMPARE(ecm.bcwpCostOnDate(d), 40.0 + 0.5 + 0.25); } void PerformanceTester::acwpPrDayProject() { NamedCommand *cmd = new ModifyCompletionEntrymodeCmd(t1->completion(), Completion::EnterEffortPerResource); cmd->execute(); delete cmd; Completion::UsedEffort *ue = new Completion::UsedEffort(); t1->completion().addUsedEffort(r1, ue); QDate d = p1->startTime().date(); EffortCostMap ecb = p1->bcwpPrDay(); EffortCostMap eca = p1->acwp(); QCOMPARE(ecb.effortOnDate(d), Duration(0, 16, 0)); QCOMPARE(ecb.costOnDate(d), 8.0); QCOMPARE(ecb.bcwpEffortOnDate(d), 0.0); QCOMPARE(ecb.bcwpCostOnDate(d), 0.0); QCOMPARE(eca.hoursOnDate(d), 0.0); QCOMPARE(eca.costOnDate(d), 0.0); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 10); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = p1->bcwpPrDay(); eca = p1->acwp(); // Debug::print(eca, "ACWP Project"); QCOMPARE(ecb.bcwpEffortOnDate(d), 8.0); QCOMPARE(ecb.bcwpCostOnDate(d), 4.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 20); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 6, 0))); cmd->execute(); delete cmd; ecb = p1->bcwpPrDay(); eca = p1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 16.0); QCOMPARE(ecb.bcwpCostOnDate(d), 8.0); QCOMPARE(eca.hoursOnDate(d), 6.0); QCOMPARE(eca.costOnDate(d), 6.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 30); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = p1->bcwpPrDay(); eca = p1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 24.0); QCOMPARE(ecb.bcwpCostOnDate(d), 12.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 50); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 8, 0))); cmd->execute(); delete cmd; ecb = p1->bcwpPrDay(); eca = p1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 40.0); QCOMPARE(ecb.bcwpCostOnDate(d), 20.0); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0); d = d.addDays(1); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 80); cmd->execute(); delete cmd; cmd = new AddCompletionActualEffortCmd(t1, r1, d, Completion::UsedEffort::ActualEffort(Duration(0, 12, 0))); cmd->execute(); delete cmd; ecb = p1->bcwpPrDay(); eca = p1->acwp(); QCOMPARE(ecb.bcwpEffortOnDate(d), 64.0); QCOMPARE(ecb.bcwpCostOnDate(d), 32.0); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.0); // add startup cost t1->completion().setStartTime(t1->startTime()); t1->completion().setStarted(true); t1->setStartupCost(0.5); d = p1->startTime().date(); eca = p1->acwp(); QCOMPARE(eca.hoursOnDate(d), 8.0); QCOMPARE(eca.costOnDate(d), 8.0 + 0.5); // add shutdown cost d = t1->endTime().date(); t1->setShutdownCost(0.25); cmd = new ModifyCompletionPercentFinishedCmd(t1->completion(), d, 100); cmd->execute(); delete cmd; t1->completion().setFinished(true); t1->completion().setFinishTime(t1->endTime()); d = p1->endTime().date(); eca = p1->acwp(); QCOMPARE(eca.hoursOnDate(d), 12.0); QCOMPARE(eca.costOnDate(d), 12.25); } } //namespace KPlato QTEST_GUILESS_MAIN(KPlato::PerformanceTester) diff --git a/src/libs/kernel/tests/ProjectTester.cpp b/src/libs/kernel/tests/ProjectTester.cpp index bd502c7e..7219e288 100644 --- a/src/libs/kernel/tests/ProjectTester.cpp +++ b/src/libs/kernel/tests/ProjectTester.cpp @@ -1,3286 +1,3215 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "ProjectTester.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptresource.h" #include "kptnode.h" #include "kpttask.h" #include "kptschedule.h" #include #include "debug.cpp" namespace KPlato { void ProjectTester::initTestCase() { tz = "TZ=Europe/Copenhagen"; putenv(tz.data()); m_project = new Project(); m_project->setId(m_project->uniqueNodeId()); m_project->registerNodeId(m_project); m_project->setConstraintStartTime(QDateTime::fromString("2012-02-01T00:00", Qt::ISODate)); m_project->setConstraintEndTime(QDateTime::fromString("2013-02-01T00:00", Qt::ISODate)); // standard worktime defines 8 hour day as default QVERIFY(m_project->standardWorktime()); QCOMPARE(m_project->standardWorktime()->day(), 8.0); m_calendar = new Calendar(); m_calendar->setName("C1"); m_calendar->setDefault(true); QTime t1(9, 0, 0); QTime t2 (17, 0, 0); int length = t1.msecsTo(t2); for (int i=1; i <= 7; ++i) { CalendarDay *d = m_calendar->weekday(i); d->setState(CalendarDay::Working); d->addInterval(t1, length); } m_project->addCalendar(m_calendar); m_task = 0; } void ProjectTester::cleanupTestCase() { unsetenv("TZ"); delete m_project; } void ProjectTester::testAddTask() { m_task = m_project->createTask(); QVERIFY(m_project->addTask(m_task, m_project)); QVERIFY(m_task->parentNode() == m_project); QCOMPARE(m_project->findNode(m_task->id()), m_task); m_project->takeTask(m_task); delete m_task; m_task = 0; } void ProjectTester::testTakeTask() { m_task = m_project->createTask(); m_project->addTask(m_task, m_project); QCOMPARE(m_project->findNode(m_task->id()), m_task); m_project->takeTask(m_task); QVERIFY(m_project->findNode(m_task->id()) == 0); delete (m_task); m_task = 0; } void ProjectTester::testTaskAddCmd() { m_task = m_project->createTask(); SubtaskAddCmd *cmd = new SubtaskAddCmd(m_project, m_task, m_project); cmd->execute(); QVERIFY(m_task->parentNode() == m_project); QCOMPARE(m_project->findNode(m_task->id()), m_task); cmd->unexecute(); QVERIFY(m_project->findNode(m_task->id()) == 0); delete cmd; m_task = 0; } void ProjectTester::testTaskDeleteCmd() { m_task = m_project->createTask(); QVERIFY(m_project->addTask(m_task, m_project)); QVERIFY(m_task->parentNode() == m_project); NodeDeleteCmd *cmd = new NodeDeleteCmd(m_task); cmd->execute(); QVERIFY(m_project->findNode(m_task->id()) == 0); cmd->unexecute(); QCOMPARE(m_project->findNode(m_task->id()), m_task); cmd->execute(); delete cmd; m_task = 0; } void ProjectTester::schedule() { QDate today = QDate::fromString("2012-02-01", Qt::ISODate); QDate tomorrow = today.addDays(1); QDate yesterday = today.addDays(-1); QDate nextweek = today.addDays(7); QTime t1(9, 0, 0); QTime t2 (17, 0, 0); int length = t1.msecsTo(t2); Task *t = m_project->createTask(); t->setName("T1"); m_project->addTask(t, m_project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Duration); QString s = "Calculate forward, Task: Duration -----------------------------------"; qDebug()<name()<id()<findNode(t->id()); qDebug()<nodeDict(); Debug::print(m_project, s, true); ScheduleManager *sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->startTime(), m_project->startTime()); QCOMPARE(t->endTime(), DateTime(t->startTime().addDays(1))); s = "Calculate forward, Task: Duration w calendar -------------------------------"; qDebug()<estimate()->setCalendar(m_calendar); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->startTime(), m_calendar->firstAvailableAfter(m_project->startTime(), m_project->endTime())); QCOMPARE(t->endTime(), DateTime(t->startTime().addMSecs(length))); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); m_project->addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setAvailableFrom(QDateTime(yesterday, QTime(), Qt::LocalTime)); r->setCalendar(m_calendar); m_project->addResource(r); r->addParentGroup(g); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r, 100); - t->requests().addResourceRequest(rr, gr); + t->requests().addResourceRequest(rr); t->estimate()->setType(Estimate::Type_Effort); s = "Calculate forward, Task: ASAP -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: ASAP, Resource 50% available -----------------"; qDebug()<setUnits(50); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(1, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: ASAP, Resource 50% available, Request 50% load ---------"; qDebug()<setUnits(50); rr->setUnits(50); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(3, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: ASAP, Resource 200% available, Request 50% load ---------"; qDebug()<setUnits(200); rr->setUnits(50); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: ASAP, Resource 200% available, Request 100% load ---------"; qDebug()<setUnits(200); rr->setUnits(100); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 4, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); s = "Calculate forward, 2 tasks: Resource 200% available, Request 50% load each ---------"; qDebug()<setUnits(200); rr->setUnits(50); Task *task2 = m_project->createTask(*t); task2->setName("T2"); m_project->addTask(task2, t); - ResourceGroupRequest *gr2 = new ResourceGroupRequest(g); - task2->addRequest(gr2); ResourceRequest *rr2 = new ResourceRequest(r, 50); - task2->requests().addResourceRequest(rr2, gr2); + task2->requests().addResourceRequest(rr2); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QCOMPARE(task2->startTime(), DateTime(today, t1)); QCOMPARE(task2->endTime(), task2->startTime() + Duration(0, 8, 0)); QCOMPARE(task2->plannedEffort().toHours(), 8.0); QVERIFY(task2->schedulingError() == false); m_project->takeTask(task2); delete task2; s = "Calculate forward, Task: ASAP, Resource available tomorrow --------"; qDebug()<setAvailableFrom(QDateTime(tomorrow, QTime(), Qt::LocalTime)); qDebug()<<"Tomorrow:"<availableFrom(); r->setUnits(100); rr->setUnits(100); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); // Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(r->availableFrom().date(), t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: ALAP -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime(0,0,0))); t->setConstraint(Node::ALAP); r->setAvailableFrom(QDateTime(yesterday, QTime(), Qt::LocalTime)); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->startTime())); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->endTime()); QVERIFY(t->lateFinish() >= t->endTime()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); s = "Calculate forward, Task: MustStartOn -----------------------------------"; qDebug()<setAvailableFrom(QDateTime(yesterday, QTime(), Qt::LocalTime)); t->setConstraint(Node::MustStartOn); t->setConstraintStartTime(DateTime(nextweek, t1)); sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->startTime(), DateTime(t->constraintStartTime().date(), t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate backwards s = "Calculate backward, Task: MustStartOn -----------------------------------"; qDebug()<setConstraintEndTime(DateTime(nextweek.addDays(1), QTime())); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), DateTime(t->constraintStartTime().date(), t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate bacwords s = "Calculate backwards, Task: MustFinishOn -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime(0,0,0))); m_project->setConstraintEndTime(DateTime(nextweek.addDays(1), QTime())); t->setConstraint(Node::MustFinishOn); t->setConstraintEndTime(DateTime(nextweek.addDays(-2), t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->endTime(), t->constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: MustFinishOn -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::MustFinishOn); t->setConstraintEndTime(DateTime(tomorrow, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->endTime(), t->constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: StartNotEarlier -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::StartNotEarlier); t->setConstraintStartTime(DateTime(today, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), DateTime(tomorrow, t1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate backward s = "Calculate backwards, Task: StartNotEarlier -----------------------------------"; qDebug()<setConstraintEndTime(DateTime(nextweek, QTime())); t->setConstraint(Node::StartNotEarlier); t->setConstraintStartTime(DateTime(today, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); Debug::print(m_project->resourceList().first(), s); // Debug::printSchedulingLog(*sm, s); QVERIFY(t->lateStart() >= t->constraintStartTime()); QCOMPARE(t->earlyFinish(), t->endTime()); QVERIFY(t->lateFinish() <= m_project->constraintEndTime()); QVERIFY(t->endTime() <= t->lateFinish()); QCOMPARE(t->startTime(), t->endTime() - Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: FinishNotLater -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::FinishNotLater); t->setConstraintEndTime(DateTime(tomorrow.addDays(1), t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QVERIFY(t->startTime() >= t->earlyStart()); QVERIFY(t->startTime() <= t->lateStart()); QVERIFY(t->startTime() >= m_project->startTime()); QVERIFY(t->endTime() >= t->earlyFinish()); QVERIFY(t->endTime() <= t->lateFinish()); QVERIFY(t->endTime() <= m_project->endTime()); QVERIFY(t->earlyFinish() <= t->constraintEndTime()); QVERIFY(t->lateFinish() <= m_project->constraintEndTime()); QVERIFY(t->schedulingError() == false); // Calculate backward s = "Calculate backwards, Task: FinishNotLater -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(nextweek, QTime())); t->setConstraint(Node::FinishNotLater); t->setConstraintEndTime(DateTime(tomorrow, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); //Debug::print(m_project, t, s); QCOMPARE(t->earlyStart(), m_project->startTime()); QCOMPARE(t->lateStart(), t->startTime()); QCOMPARE(t->earlyFinish(), t->constraintEndTime()); QVERIFY(t->lateFinish() <= m_project->constraintEndTime()); QCOMPARE(t->startTime(), m_project->startTime()); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forward, Task: FixedInterval -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::FixedInterval); t->setConstraintStartTime(DateTime(tomorrow, t1)); t->setConstraintEndTime(DateTime(tomorrow, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); //Debug::print(m_project, t, s); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->constraintStartTime()); QCOMPARE(t->earlyFinish(), t->constraintEndTime()); QCOMPARE(t->lateFinish(), t->constraintEndTime()); QCOMPARE(t->startTime(), t->constraintStartTime()); QCOMPARE(t->endTime(), t->constraintEndTime()); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: FixedInterval -----------------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::FixedInterval); t->setConstraintStartTime(DateTime(tomorrow, QTime())); // outside working hours t->setConstraintEndTime(DateTime(tomorrow, t2)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->constraintStartTime()); QCOMPARE(t->earlyFinish(), t->constraintEndTime()); QCOMPARE(t->lateFinish(), t->constraintEndTime()); QCOMPARE(t->startTime(), t->constraintStartTime()); QCOMPARE(t->endTime(), t->constraintEndTime()); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: Milestone, ASAP-------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::ASAP); t->estimate()->clear(); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); //Debug::print(m_project, t, s); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->earlyStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->earlyStart()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate backward s = "Calculate backwards, Task: Milestone, ASAP-------------------------"; qDebug()<setConstraintEndTime(DateTime(today, QTime())); t->setConstraint(Node::ASAP); t->estimate()->clear(); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->earlyStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->earlyStart()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: Milestone, ALAP-------------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::ALAP); t->estimate()->clear(); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->earlyStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->earlyStart()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate backward s = "Calculate backwards, Task: Milestone, ALAP-------------------------"; qDebug()<setConstraintEndTime(DateTime(today, QTime())); t->setConstraint(Node::ALAP); t->estimate()->clear(); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->earlyStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->earlyStart()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: Milestone, MustStartOn ------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::MustStartOn); t->setConstraintStartTime(DateTime(tomorrow, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->constraintStartTime()); QCOMPARE(t->earlyFinish(), t->lateStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->constraintStartTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate backward s = "Calculate backwards, Task: Milestone, MustStartOn ------------------"; qDebug()<setConstraintEndTime(DateTime(tomorrow, QTime())); t->setConstraint(Node::MustStartOn); t->setConstraintStartTime(DateTime(today, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), t->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->earlyStart()); QCOMPARE(t->lateFinish(), m_project->constraintEndTime()); QCOMPARE(t->startTime(), t->constraintStartTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); // Calculate forward s = "Calculate forwards, Task: Milestone, MustFinishOn ------------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::MustFinishOn); t->setConstraintEndTime(DateTime(tomorrow, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->constraintEndTime()); QCOMPARE(t->earlyFinish(), t->lateStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->constraintEndTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->endTime(), t->endTime()); // Calculate backward s = "Calculate backwards, Task: Milestone, MustFinishOn ------------------"; qDebug()<setConstraintEndTime(DateTime(tomorrow, QTime())); t->setConstraint(Node::MustFinishOn); t->setConstraintEndTime(DateTime(today, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), t->constraintEndTime()); QCOMPARE(t->lateStart(), t->constraintEndTime()); QCOMPARE(t->earlyFinish(), t->lateStart()); QCOMPARE(t->lateFinish(), m_project->constraintEndTime()); QCOMPARE(t->startTime(), t->constraintEndTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->startTime(), t->startTime()); // Calculate forward s = "Calculate forwards, Task: Milestone, StartNotEarlier ---------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::StartNotEarlier); t->setConstraintEndTime(DateTime(tomorrow, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), m_project->constraintStartTime()); QCOMPARE(t->lateStart(), t->constraintStartTime()); QCOMPARE(t->earlyFinish(), t->lateStart()); QCOMPARE(t->lateFinish(), t->earlyFinish()); QCOMPARE(t->startTime(), t->constraintStartTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->endTime(), t->endTime()); // Calculate backward s = "Calculate backwards, Task: Milestone, StartNotEarlier ---------------"; qDebug()<setConstraintEndTime(DateTime(tomorrow, QTime())); t->setConstraint(Node::StartNotEarlier); t->setConstraintStartTime(DateTime(today, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, s, true); QVERIFY(t->earlyStart() >= t->constraintStartTime()); QVERIFY(t->lateStart() >= t->earlyStart()); QVERIFY(t->earlyFinish() <= t->lateFinish()); QVERIFY(t->lateFinish() >= t->constraintStartTime()); QVERIFY(t->startTime() >= t->constraintStartTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->startTime(), t->startTime()); // Calculate forward s = "Calculate forwards, Task: Milestone, FinishNotLater ---------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::FinishNotLater); t->setConstraintEndTime(DateTime(tomorrow, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QVERIFY(t->earlyStart() <= t->constraintEndTime()); QVERIFY(t->lateStart() <= t->constraintEndTime()); QVERIFY(t->earlyFinish() >= t->earlyStart()); QVERIFY(t->lateFinish() >= t->earlyFinish()); QVERIFY(t->startTime() <= t->constraintEndTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->endTime(), t->endTime()); // Calculate backward s = "Calculate backwards, Task: Milestone, FinishNotLater ---------------"; qDebug()<setConstraintEndTime(DateTime(tomorrow, QTime())); t->setConstraint(Node::FinishNotLater); t->setConstraintEndTime(DateTime(today, t1)); sm = m_project->createScheduleManager("Test Plan"); sm->setSchedulingDirection(true); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); QCOMPARE(t->earlyStart(), t->constraintStartTime()); QCOMPARE(t->lateStart(), t->earlyStart()); QCOMPARE(t->earlyFinish(), t->lateStart()); QCOMPARE(t->lateFinish(), m_project->constraintEndTime()); QCOMPARE(t->startTime(), t->constraintEndTime()); QCOMPARE(t->endTime(), t->startTime()); QVERIFY(t->schedulingError() == false); QCOMPARE(m_project->startTime(), t->startTime()); // Calculate forward s = "Calculate forward, 2 Tasks, no overbooking ----------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); m_project->setConstraintEndTime(DateTime(today, QTime()).addDays(4)); t->setConstraint(Node::ASAP); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(2.0); Task *tsk2 = m_project->createTask(*t); tsk2->setName("T2"); m_project->addTask(tsk2, m_project); - gr = new ResourceGroupRequest(g); - tsk2->addRequest(gr); rr = new ResourceRequest(r, 100); - tsk2->requests().addResourceRequest(rr, gr); + tsk2->requests().addResourceRequest(rr); sm = m_project->createScheduleManager("Test Plan"); sm->setAllowOverbooking(false); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); Debug::print(m_project, t, s); Debug::print(m_project, tsk2, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->constraintStartTime())); QCOMPARE(t->lateStart(), tsk2->startTime()); QCOMPARE(t->earlyFinish(), DateTime(tomorrow, t2)); QCOMPARE(t->lateFinish(), t->lateFinish()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->earlyFinish()); QVERIFY(t->schedulingError() == false); QCOMPARE(tsk2->earlyStart(), t->earlyStart()); QCOMPARE(tsk2->lateStart(), t->earlyFinish() + Duration(0, 16, 0)); QCOMPARE(tsk2->earlyFinish(), DateTime(tomorrow, t2)); QCOMPARE(tsk2->lateFinish(), t->lateFinish()); QCOMPARE(tsk2->startTime(), DateTime(tomorrow.addDays(1), t1)); QCOMPARE(tsk2->endTime(), tsk2->lateFinish()); QVERIFY(tsk2->schedulingError() == false); QCOMPARE(m_project->endTime(), tsk2->endTime()); // Calculate forward s = "Calculate forward, 2 Tasks, relation ---------------"; qDebug()<setConstraintStartTime(DateTime(today, QTime())); t->setConstraint(Node::ASAP); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(2.0); Relation *rel = new Relation(t, tsk2); bool relationAdded = m_project->addRelation(rel); QVERIFY(relationAdded); sm = m_project->createScheduleManager("Test Plan"); sm->setAllowOverbooking(true); sm->setSchedulingDirection(false); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); // Debug::print(m_project, t, s); // Debug::print(m_project, tsk2, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->earlyStart(), t->requests().workTimeAfter(m_project->constraintStartTime())); QCOMPARE(t->lateStart(), DateTime(today, t1)); QCOMPARE(t->earlyFinish(), DateTime(tomorrow, t2)); QCOMPARE(t->lateFinish(), t->lateFinish()); QCOMPARE(t->startTime(), DateTime(today, t1)); QCOMPARE(t->endTime(), t->earlyFinish()); QVERIFY(t->schedulingError() == false); QCOMPARE(tsk2->earlyStart(), tsk2->requests().workTimeAfter(t->earlyFinish())); QCOMPARE(tsk2->lateStart(), DateTime(tomorrow.addDays(1), t1)); QCOMPARE(tsk2->earlyFinish(), DateTime(tomorrow.addDays(2), t2)); QCOMPARE(tsk2->lateFinish(), tsk2->earlyFinish()); QCOMPARE(tsk2->startTime(), DateTime(tomorrow.addDays(1), t1)); QCOMPARE(tsk2->endTime(), tsk2->earlyFinish()); QVERIFY(tsk2->schedulingError() == false); QCOMPARE(m_project->endTime(), tsk2->endTime()); } void ProjectTester::scheduleFullday() { QString s = "Full day, 1 resource works 24 hours a day -------------"; qDebug()<setConstraintStartTime(QDateTime::fromString("2011-09-01T00:00:00", Qt::ISODate)); m_project->setConstraintEndTime(QDateTime::fromString("2011-09-16T00:00:00", Qt::ISODate)); qDebug()<constraintStartTime()<constraintEndTime(); Calendar *c = new Calendar("Test"); QTime t1(0,0,0); int length = 24*60*60*1000; for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } m_project->addCalendar(c); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); m_project->addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(c); r->setAvailableFrom(m_project->constraintStartTime()); r->setAvailableUntil(r->availableFrom().addDays(21)); m_project->addResource(r); r->addParentGroup(g); Task *t = m_project->createTask(); t->setName("T1"); t->setId(m_project->uniqueNodeId()); m_project->addTask(t, m_project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(3 * 14.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); ScheduleManager *sm = m_project->createScheduleManager("Test Plan"); m_project->addScheduleManager(sm); sm->createSchedules(); m_project->calculate(*sm); // Debug::print(c, s); // Debug::print(m_project, t, s); // Debug::print(r, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), m_project->startTime()); QCOMPARE(t->endTime(), DateTime(t->startTime().addDays(14))); s = "Full day, 8 hour shifts, 3 resources ---------------"; qDebug()<weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(6, 0, 0), 8*hour)); } m_project->addCalendar(c1); Calendar *c2 = new Calendar("Test 2"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c2->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(14, 0, 0), 8*hour)); } m_project->addCalendar(c2); Calendar *c3 = new Calendar("Test 3"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c3->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(0, 0, 0), 6*hour)); wd1->addInterval(TimeInterval(QTime(22, 0, 0), 2*hour)); } m_project->addCalendar(c3); r->setCalendar(c1); r = new Resource(); r->setName("R2"); r->setCalendar(c2); r->setAvailableFrom(m_project->constraintStartTime()); r->setAvailableUntil(r->availableFrom().addDays(21)); m_project->addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); r = new Resource(); r->setName("R3"); r->setCalendar(c3); r->setAvailableFrom(m_project->constraintStartTime()); r->setAvailableUntil(r->availableFrom().addDays(21)); m_project->addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); sm->createSchedules(); m_project->calculate(*sm); // Debug::print(m_project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), m_project->startTime()); QCOMPARE(t->endTime(), DateTime(t->startTime().addDays(14))); } void ProjectTester::scheduleFulldayDstSpring() { QString s = "Daylight saving time - Spring, 1 resource works 24 hours a day -------------"; qDebug()<weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(c); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); Task *t = project.createTask(); t->setName("T1"); t->setId(project.uniqueNodeId()); project.addTask(t, &project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(3 * 4.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(c, s); Debug::print(&project, t, s); // Debug::print(r, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), project.constraintStartTime()); QCOMPARE(t->endTime(), t->startTime() + Duration(4, 0, 0)); s = "Daylight saving time - Spring, Backward: 1 resource works 24 hours a day -------------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); project.calculate(*sm); // Debug::print(c, s); Debug::print(&project, t, s); // Debug::print(r, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->endTime(), project.constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(4, 0, 0)); s = "Daylight saving time - Spring, 8 hour shifts, 3 resources ---------------"; qDebug()<weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(6, 0, 0), 8*hour)); } project.addCalendar(c1); Calendar *c2 = new Calendar("Test 2"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c2->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(14, 0, 0), 8*hour)); } project.addCalendar(c2); Calendar *c3 = new Calendar("Test 3"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c3->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(0, 0, 0), 6*hour)); wd1->addInterval(TimeInterval(QTime(22, 0, 0), 2*hour)); } project.addCalendar(c3); r->setCalendar(c1); r = new Resource(); r->setName("R2"); r->setCalendar(c2); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); r = new Resource(); r->setName("R3"); r->setCalendar(c3); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); project.setConstraintStartTime(DateTime(QDate::fromString("2011-03-25", Qt::ISODate))); sm = project.createScheduleManager("Test Foreward"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(&project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime().toUTC(), project.constraintStartTime().toUTC()); QCOMPARE(t->endTime(), t->startTime() + Duration(4, 0, 0)); s = "Daylight saving time - Spring, Backward: 8 hour shifts, 3 resources ---------------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); project.calculate(*sm); Debug::print(&project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->endTime(), project.constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(4, 0, 0)); } void ProjectTester::scheduleFulldayDstFall() { QString s = "Daylight saving time - Fall, 1 resource works 24 hours a day -------------"; qDebug()<weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(c); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); Task *t = project.createTask(); t->setName("T1"); t->setId(project.uniqueNodeId()); project.addTask(t, &project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(3 * 4.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(c, s); Debug::print(&project, t, s); // Debug::print(r, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), project.constraintStartTime()); QCOMPARE(t->endTime(), t->startTime() + Duration(4, 0, 0)); s = "Daylight saving time - Fall, Backward: 1 resource works 24 hours a day -------------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); project.calculate(*sm); // Debug::print(c, s); Debug::print(&project, t, s); // Debug::print(r, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->endTime(), project.constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(4, 0, 0)); s = "Daylight saving time - Fall, 8 hour shifts, 3 resources ---------------"; qDebug()<weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(6, 0, 0), 8*hour)); } project.addCalendar(c1); Calendar *c2 = new Calendar("Test 2"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c2->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(14, 0, 0), 8*hour)); } project.addCalendar(c2); Calendar *c3 = new Calendar("Test 3"); for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c3->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(QTime(0, 0, 0), 6*hour)); wd1->addInterval(TimeInterval(QTime(22, 0, 0), 2*hour)); } project.addCalendar(c3); r->setCalendar(c1); r = new Resource(); r->setName("R2"); r->setCalendar(c2); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); r = new Resource(); r->setName("R3"); r->setCalendar(c3); r->setAvailableFrom(project.constraintStartTime().addDays(-1)); r->setAvailableUntil(r->availableFrom().addDays(21)); project.addResource(r); r->addParentGroup(g); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); sm = project.createScheduleManager("Test Foreward"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(&project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), project.constraintStartTime()); QCOMPARE(t->endTime(), t->startTime() + Duration(4, 0, 0)); s = "Daylight saving time - Fall, Backward: 8 hour shifts, 3 resources ---------------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); project.calculate(*sm); QCOMPARE(t->endTime(), project.constraintEndTime()); QCOMPARE(t->startTime(), t->endTime() - Duration(4, 0, 0)); } void ProjectTester::scheduleWithExternalAppointments() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate::fromString("2012-02-01", Qt::ISODate), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(3)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar c("Test"); QTime t1(0,0,0); int length = 24*60*60*1000; for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c.weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(&c); project.addResource(r); r->addParentGroup(g); r->addExternalAppointment("Ext-1", "External project 1", targetstart, targetstart.addDays(1), 100); r->addExternalAppointment("Ext-1", "External project 1", targetend.addDays(-1), targetend, 100); Task *t = project.createTask(); t->setName("T1"); project.addTask(t, &project); t->estimate()->setUnit(Duration::Unit_h); t->estimate()->setExpectedEstimate(8.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); - t->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + t->requests().addResourceRequest(new ResourceRequest(r, 100)); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); QString s = "Schedule with external appointments ----------"; qDebug()<startTime(), targetstart + Duration(1, 0, 0)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); sm->setAllowOverbooking(true); sm->createSchedules(); project.calculate(*sm); // Debug::printSchedulingLog(*sm); QCOMPARE(t->startTime(), targetstart); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); sm->setAllowOverbooking(false); sm->setSchedulingDirection(true); // backwards sm->createSchedules(); project.calculate(*sm); Debug::print(&project, s, true); Debug::print(r, "", true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), targetend - Duration(1, 8, 0)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); sm->setAllowOverbooking(true); sm->createSchedules(); project.calculate(*sm); // Debug::printSchedulingLog(*sm); QCOMPARE(t->startTime(), targetend - Duration(0, 8, 0)); QCOMPARE(t->endTime(), targetend); sm->setAllowOverbooking(false); r->clearExternalAppointments(); sm->createSchedules(); project.calculate(*sm); // Debug::printSchedulingLog(*sm); QCOMPARE(t->endTime(), targetend); QCOMPARE(t->startTime(), t->endTime() - Duration(0, 8, 0)); } void ProjectTester::reschedule() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate::fromString("2012-02-01", Qt::ISODate), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(c); project.addResource(r); r->addParentGroup(g); QString s = "Re-schedule; schedule tasks T1, T2, T3 ---------------"; qDebug()<setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_h); task1->estimate()->setExpectedEstimate(8.0); task1->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); - task1->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + task1->requests().addResourceRequest(new ResourceRequest(r, 100)); Task *task2 = project.createTask(); task2->setName("T2"); project.addTask(task2, &project); task2->estimate()->setUnit(Duration::Unit_h); task2->estimate()->setExpectedEstimate(8.0); task2->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - task2->addRequest(gr); - task2->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + task2->requests().addResourceRequest(new ResourceRequest(r, 100)); Task *task3 = project.createTask(); task3->setName("T3"); project.addTask(task3, &project); task3->estimate()->setUnit(Duration::Unit_h); task3->estimate()->setExpectedEstimate(8.0); task3->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - task3->addRequest(gr); - task3->requests().addResourceRequest(new ResourceRequest(r, 100), gr); + task3->requests().addResourceRequest(new ResourceRequest(r, 100)); Relation *rel = new Relation(task1, task2); project.addRelation(rel); rel = new Relation(task1, task3); project.addRelation(rel); ScheduleManager *sm = project.createScheduleManager("Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(&project, task1, s, true); // Debug::print(&project, task2, s, true); // Debug::print(&project, task3, s, true); // Debug::print(r, s); // Debug::printSchedulingLog(*sm); QVERIFY(task1->startTime() >= c->firstAvailableAfter(targetstart, targetend)); QVERIFY(task1->startTime() <= c->firstAvailableBefore(targetend, targetstart)); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 8, 0)); QVERIFY(task2->startTime() >= c->firstAvailableAfter(targetstart, targetend)); QVERIFY(task2->startTime() <= c->firstAvailableBefore(targetend, targetstart)); QCOMPARE(task2->endTime(), task2->startTime() + Duration(0, 8, 0)); QVERIFY(task3->startTime() >= c->firstAvailableAfter(targetstart, targetend)); QVERIFY(task3->startTime() <= c->firstAvailableBefore(targetend, targetstart)); QCOMPARE(task3->endTime(), task3->startTime() + Duration(0, 8, 0)); DateTime restart = task1->endTime(); s = QString("Re-schedule; re-schedule from %1 - tasks T1 (finished), T2, T3 ------").arg(restart.toString()); qDebug()<completion().setStarted(true); task1->completion().setPercentFinished(task1->endTime().date(), 100); task1->completion().setFinished(true); ScheduleManager *child = project.createScheduleManager("Plan.1"); project.addScheduleManager(child, sm); child->setRecalculate(true); child->setRecalculateFrom(restart); child->createSchedules(); project.calculate(*child); // Debug::print(&project, task1, s, true); // Debug::print(&project, task2, s, true); // Debug::print(&project, task3, s, true); // Debug::printSchedulingLog(*child, s); QCOMPARE(task1->startTime(), c->firstAvailableAfter(targetstart, targetend)); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 8, 0)); // either task2 or task3 may be scheduled first if (task2->startTime() < task3->startTime()) { QCOMPARE(task2->startTime(), c->firstAvailableAfter(qMax(task1->endTime(), restart), targetend)); QCOMPARE(task2->endTime(), task2->startTime() + Duration(0, 8, 0)); QCOMPARE(task3->startTime(), c->firstAvailableAfter(task2->endTime(), targetend)); QCOMPARE(task3->endTime(), task3->startTime() + Duration(0, 8, 0)); } else { QCOMPARE(task3->startTime(), c->firstAvailableAfter(qMax(task1->endTime(), restart), targetend)); QCOMPARE(task3->endTime(), task3->startTime() + Duration(0, 8, 0)); QCOMPARE(task2->startTime(), c->firstAvailableAfter(task3->endTime(), targetend)); QCOMPARE(task2->endTime(), task2->startTime() + Duration(0, 8, 0)); } } void ProjectTester::materialResource() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate::fromString("2012-02-01", Qt::ISODate), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); Task *task1 = project.createTask(); task1->setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_h); task1->estimate()->setExpectedEstimate(8.0); task1->estimate()->setType(Estimate::Type_Effort); QString s = "Calculate forward, Task: ASAP, Working + material resource --------"; qDebug()<setName("Work"); r->setAvailableFrom(targetstart); r->setCalendar(c); project.addResource(r); r->addParentGroup(g); ResourceGroup *mg = new ResourceGroup(); mg->setType(ResourceGroup::Type_Material); project.addResourceGroup(mg); Resource *mr = new Resource(); mr->setType(Resource::Type_Material); mr->setName("Material"); mr->setAvailableFrom(targetstart); mr->setCalendar(c); project.addResource(mr); mr->addParentGroup(mg); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r, 100); - task1->requests().addResourceRequest(rr, gr); + task1->requests().addResourceRequest(rr); - ResourceGroupRequest *mgr = new ResourceGroupRequest(mg); - task1->addRequest(mgr); ResourceRequest *mrr = new ResourceRequest(mr, 100); - task1->requests().addResourceRequest(mrr, mgr); + task1->requests().addResourceRequest(mrr); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r, s); // Debug::print(mr, s); -// Debug::print(&project, task1, s); +// Debug::print(&project, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->earlyStart(), task1->requests().workTimeAfter(targetstart)); QVERIFY(task1->lateStart() >= task1->earlyStart()); QVERIFY(task1->earlyFinish() <= task1->endTime()); QVERIFY(task1->lateFinish() >= task1->endTime()); QCOMPARE(task1->startTime(), DateTime(r->availableFrom().date(), t1)); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 8, 0)); QVERIFY(task1->schedulingError() == false); } void ProjectTester::requiredResource() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate::fromString("2012-02-01", Qt::ISODate), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); Task *task1 = project.createTask(); task1->setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_h); task1->estimate()->setExpectedEstimate(8.0); task1->estimate()->setType(Estimate::Type_Effort); QString s = "Required resource: Working + required material resource --------"; qDebug()<setName("Work"); r->setAvailableFrom(targetstart); r->setCalendar(c); project.addResource(r); r->addParentGroup(g); ResourceGroup *mg = new ResourceGroup(); mg->setType(ResourceGroup::Type_Material); mg->setName("MG"); project.addResourceGroup(mg); Resource *mr = new Resource(); mr->setType(Resource::Type_Material); mr->setName("Material"); mr->setAvailableFrom(targetstart); mr->setCalendar(c); project.addResource(mr); mr->addParentGroup(mg); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); ResourceRequest *rr = new ResourceRequest(r, 100); - task1->requests().addResourceRequest(rr, gr); + task1->requests().addResourceRequest(rr); QList lst; lst << mr; rr->setRequiredResources(lst); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r, s); // Debug::print(mr, s); // Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->earlyStart(), task1->requests().workTimeAfter(targetstart)); QVERIFY(task1->lateStart() >= task1->earlyStart()); QVERIFY(task1->earlyFinish() <= task1->endTime()); QVERIFY(task1->lateFinish() >= task1->endTime()); QCOMPARE(task1->startTime(), DateTime(r->availableFrom().date(), t1)); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 8, 0)); QVERIFY(task1->schedulingError() == false); QList apps = r->appointments(sm->scheduleId()); QVERIFY(apps.count() == 1); QCOMPARE(task1->startTime(), apps.first()->startTime()); QCOMPARE(task1->endTime(), apps.last()->endTime()); apps = mr->appointments(sm->scheduleId()); QVERIFY(apps.count() == 1); QCOMPARE(task1->startTime(), apps.first()->startTime()); QCOMPARE(task1->endTime(), apps.last()->endTime()); s = "Required resource limits availability --------"; qDebug()<setAvailableFrom(tomorrow); sm->createSchedules(); project.calculate(*sm); // Debug::print(r, s); // Debug::print(mr, s); // Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->earlyStart(), task1->requests().workTimeAfter(targetstart)); QVERIFY(task1->lateStart() >= task1->earlyStart()); QVERIFY(task1->earlyFinish() <= task1->endTime()); QVERIFY(task1->lateFinish() >= task1->endTime()); QCOMPARE(task1->startTime(), DateTime(mr->availableFrom().date(), t1)); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 8, 0)); QVERIFY(task1->schedulingError() == false); apps = r->appointments(sm->scheduleId()); QVERIFY(apps.count() == 1); QCOMPARE(task1->startTime(), apps.first()->startTime()); QCOMPARE(task1->endTime(), apps.last()->endTime()); apps = mr->appointments(sm->scheduleId()); QVERIFY(apps.count() == 1); QCOMPARE(task1->startTime(), apps.first()->startTime()); QCOMPARE(task1->endTime(), apps.last()->endTime()); } void ProjectTester::resourceWithLimitedAvailability() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate(2010, 5, 1), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); DateTime expectedEndTime(QDate(2010, 5, 3), QTime(16, 0, 0)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); Task *task1 = project.createTask(); task1->setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_d); task1->estimate()->setExpectedEstimate(4.0); task1->estimate()->setType(Estimate::Type_Effort); QString s = "Two resources: One with available until < resulting task length --------"; qDebug()<setName("R1"); r1->setAvailableFrom(targetstart); r1->setCalendar(c); project.addResource(r1); r1->addParentGroup(g); Resource *r2 = new Resource(); r2->setName("R2"); r2->setAvailableFrom(targetstart); r2->setAvailableUntil(targetstart.addDays(1)); r2->setCalendar(c); project.addResource(r2); r2->addParentGroup(g); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); ResourceRequest *rr1 = new ResourceRequest(r1, 100); - task1->requests().addResourceRequest(rr1, gr); + task1->requests().addResourceRequest(rr1); ResourceRequest *rr2 = new ResourceRequest(r2, 100); - task1->requests().addResourceRequest(rr2, gr); + task1->requests().addResourceRequest(rr2); ScheduleManager *sm = project.createScheduleManager("Test Plan"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r1, s); // Debug::print(r2, s); // Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), expectedEndTime); } void ProjectTester::unavailableResource() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate(2010, 5, 1), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); Task *task1 = project.createTask(); task1->setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_d); task1->estimate()->setExpectedEstimate(2.0); task1->estimate()->setType(Estimate::Type_Effort); QString s = "One available resource --------"; qDebug()<setName("R1"); r1->setCalendar(c); project.addResource(r1); r1->addParentGroup(g); Resource *r2 = new Resource(); r2->setName("Unavailable"); r2->setCalendar(c); project.addResource(r2); r2->addParentGroup(g); - - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); - task1->requests().addResourceRequest(new ResourceRequest(r1, 100), gr); + task1->requests().addResourceRequest(new ResourceRequest(r1, 100)); ScheduleManager *sm = project.createScheduleManager("Plan R1"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r1, s); // Debug::print(r2, s); Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); DateTime expectedEndTime = targetstart + Duration(1, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); s = "One available resource + one unavailable resource --------"; qDebug()<setAvailableFrom(targetend); r2->setAvailableUntil(targetend.addDays(1)); - task1->requests().addResourceRequest(new ResourceRequest(r2, 100), gr); + task1->requests().addResourceRequest(new ResourceRequest(r2, 100)); sm = project.createScheduleManager("Team + Resource"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r1, s); // Debug::print(r2, s); Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), expectedEndTime); } void ProjectTester::team() { Project project; project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); DateTime targetstart = DateTime(QDate(2010, 5, 1), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(7)); project.setConstraintStartTime(targetstart); project.setConstraintEndTime(targetend); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } project.addCalendar(c); Task *task1 = project.createTask(); task1->setName("T1"); project.addTask(task1, &project); task1->estimate()->setUnit(Duration::Unit_d); task1->estimate()->setExpectedEstimate(2.0); task1->estimate()->setType(Estimate::Type_Effort); QString s = "One team with one resource --------"; qDebug()<setName("R1"); r1->setCalendar(c); project.addResource(r1); r1->addParentGroup(g); Resource *r2 = new Resource(); r2->setName("Team member"); r2->setCalendar(c); project.addResource(r2); r2->addParentGroup(g); Resource *team = new Resource(); team->setType(Resource::Type_Team); team->setName("Team"); project.addResource(team); team->addParentGroup(g); team->addTeamMemberId(r2->id()); - - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); + ResourceRequest *tr = new ResourceRequest(team, 100); - task1->requests().addResourceRequest(tr, gr); + task1->requests().addResourceRequest(tr); ScheduleManager *sm = project.createScheduleManager("Team"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); - + // Debug::print(r1, s); // Debug::print(r2, s); - Debug::print(team, s, false); - Debug::print(&project, task1, s); + Debug::print(team, s, false); + Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); DateTime expectedEndTime = targetstart + Duration(1, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); s = "One team with one resource + one resource --------"; qDebug()<requests().addResourceRequest(rr1, gr); + task1->requests().addResourceRequest(rr1); sm = project.createScheduleManager("Team + Resource"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r1, s); // Debug::print(r2, s); Debug::print(team, s, false); Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); expectedEndTime = targetstart + Duration(0, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); s = "One team with one resource + one resource, resource available too late --------"; qDebug()<setAvailableFrom(targetend); r1->setAvailableUntil(targetend.addDays(7)); sm = project.createScheduleManager("Team + Resource not available"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); Debug::print(r1, s); // Debug::print(r2, s); Debug::print(team, s, false); - Debug::print(&project, task1, s); + Debug::print(&project, s, true); // Debug::printSchedulingLog(*sm, s); expectedEndTime = targetstart + Duration(1, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); s = "One team with two resources --------"; qDebug()<removeRequests(); team->addTeamMemberId(r1->id()); r1->setAvailableFrom(targetstart); r1->setAvailableUntil(targetend); sm = project.createScheduleManager("Team with 2 resources"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); // Debug::print(r1, s); // Debug::print(r2, s); Debug::print(team, s, false); Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); expectedEndTime = targetstart + Duration(0, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); s = "One team with two resources, one resource unavailable --------"; qDebug()<setAvailableFrom(targetend); r1->setAvailableUntil(targetend.addDays(2)); sm = project.createScheduleManager("Team, one unavailable resource"); project.addScheduleManager(sm); sm->createSchedules(); project.calculate(*sm); Debug::print(r1, s); // Debug::print(r2, s); Debug::print(team, s, false); Debug::print(&project, task1, s); // Debug::printSchedulingLog(*sm, s); expectedEndTime = targetstart + Duration(1, 16, 0); QCOMPARE(task1->endTime(), expectedEndTime); - gr->takeResourceRequest(tr); - task1->takeRequest(gr); + task1->requests().removeResourceRequest(tr); project.takeResource(team); team->removeTeamMemberId(r2->id()); - } void ProjectTester::inWBSOrder() { Project p; p.setName("WBS Order"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2012-02-01", Qt::ISODate); st = DateTime(st.addDays(1)); st.setTime(QTime (0, 0, 0)); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *t = p.createTask(); t->setName("T1"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); ResourceRequest *tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T2"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T3"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T4"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); ScheduleManager *sm = p.createScheduleManager("WBS Order, forward"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); QString s = "Schedule 4 tasks forward in wbs order -------"; // NOTE: It's not *mandatory* to schedule in wbs order but users expect it, so we'll try // This test can be removed if for some important reason this isn't possible. // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(3, 8, 0)); } void ProjectTester::resourceConflictALAP() { Project p; p.setName("resourceConflictALAP"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2012-02-01", Qt::ISODate); st = DateTime(st.addDays(1)); st.setTime(QTime (0, 0, 0)); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *t = p.createTask(); t->setName("T1"); t->setConstraint(Node::ALAP); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); ResourceRequest *tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T2"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T3"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T4"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); ScheduleManager *sm = p.createScheduleManager("T1 ALAP"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); QString s = "Schedule T1 ALAP -------"; // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); s = "Schedule T1, T2 ALAP -------"; p.allTasks().at(1)->setConstraint(Node::ALAP); sm = p.createScheduleManager("T1, T2 ALAP"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); s = "Schedule T1, T2, T3 ALAP -------"; p.allTasks().at(2)->setConstraint(Node::ALAP); sm = p.createScheduleManager("T1, T2, T3 ALAP"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); s = "Schedule T1, T2, T3, T4 ALAP -------"; p.allTasks().at(3)->setConstraint(Node::ALAP); sm = p.createScheduleManager("T1, T2, T3, T4 ALAP"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); } void ProjectTester::resourceConflictMustStartOn() { Project p; p.setName("resourceConflictMustStartOn"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2012-02-01T00:00:00", Qt::ISODate); st = DateTime(st.addDays(1)); st.setTime(QTime (0, 0, 0)); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *t = p.createTask(); t->setName("T1"); t->setConstraint(Node::MustStartOn); t->setConstraintStartTime(st + Duration(1, 8, 0)); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); ResourceRequest *tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T2"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T3"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); t = p.createTask(); t->setName("T4"); p.addSubTask(t, &p); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - t->addRequest(gr); tr = new ResourceRequest(r1, 100); - t->requests().addResourceRequest(tr, gr); + t->requests().addResourceRequest(tr); ScheduleManager *sm = p.createScheduleManager("T1 MustStartOn"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); QString s = "Schedule T1 MustStartOn -------"; // Debug::print (c, s); // Debug::print(r1, s); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); s = "Schedule T1, T2 MustStartOn -------"; p.allTasks().at(1)->setConstraint(Node::MustStartOn); p.allTasks().at(1)->setConstraintStartTime(st + Duration(2, 8, 0)); sm = p.createScheduleManager("T1, T2 MustStartOn"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); // Debug::print (c, s); // Debug::print(r1, s); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); s = "Schedule T1, T2, T3 MustStartOn -------"; p.allTasks().at(2)->setConstraint(Node::MustStartOn); p.allTasks().at(2)->setConstraintStartTime(st + Duration(3, 8, 0)); sm = p.createScheduleManager("T1, T2, T3 MustStartOn"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); // Debug::print (c, s); // Debug::print(r1, s); // Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(p.allTasks().count(), 4); QCOMPARE(p.allTasks().at(0)->startTime(), st + Duration(1, 8, 0)); QCOMPARE(p.allTasks().at(0)->endTime(), st + Duration(1, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(1)->startTime(), st + Duration(2, 8, 0)); QCOMPARE(p.allTasks().at(1)->endTime(), st + Duration(2, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(2)->startTime(), st + Duration(3, 8, 0)); QCOMPARE(p.allTasks().at(2)->endTime(), st + Duration(3, 8, 0) + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->startTime(), st + Duration(0, 8, 0)); QCOMPARE(p.allTasks().at(3)->endTime(), st + Duration(0, 8, 0) + Duration(0, 8, 0)); s = "Schedule backwards, T1 MustStartOn, T2 ASAP -------"; p.takeTask(p.allTasks().at(3), false); p.takeTask(p.allTasks().at(2), false); Task *task1 = p.allTasks().at(0); Task *task2 = p.allTasks().at(1); DateTime et = p.constraintEndTime(); task1->setConstraint(Node::MustStartOn); task1->setConstraintStartTime(et - Duration(1, 16, 0)); task2->setConstraint(Node::ASAP); sm = p.createScheduleManager("T1 MustStartOn, T2 ASAP"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->startTime(), task1->mustStartOn()); QCOMPARE(task2->startTime(), et - Duration(0, 16, 0)); s = "Schedule backwards, T1 MustStartOn, T2 StartNotEarlier -------"; task2->setConstraint(Node::StartNotEarlier); task2->setConstraintStartTime(task1->mustStartOn().addDays(-1)); sm = p.createScheduleManager("T1 MustStartOn, T2 StartNotEarlier"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->startTime(), task1->mustStartOn()); QCOMPARE(task2->startTime(), et - Duration(0, 16, 0)); task2->setConstraintStartTime(task1->mustStartOn()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->startTime(), task1->mustStartOn()); QCOMPARE(task2->startTime(), et - Duration(0, 16, 0)); task2->setConstraintStartTime(task1->mustStartOn().addDays(1)); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->startTime(), task1->mustStartOn()); QCOMPARE(task2->startTime(), et - Duration(0, 16, 0)); } void ProjectTester::resourceConflictMustFinishOn() { Project p; p.setName("P1"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2012-02-01", Qt::ISODate); st = DateTime(st.addDays(1)); st.setTime(QTime (0, 0, 0)); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *task1 = p.createTask(); task1->setName("T1"); task1->setConstraint(Node::MustFinishOn); task1->setConstraintEndTime(st + Duration(1, 16, 0)); p.addSubTask(task1, &p); task1->estimate()->setUnit(Duration::Unit_d); task1->estimate()->setExpectedEstimate(1.0); task1->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - task1->addRequest(gr); ResourceRequest *tr = new ResourceRequest(r1, 100); - task1->requests().addResourceRequest(tr, gr); + task1->requests().addResourceRequest(tr); Task *task2 = p.createTask(); task2->setName("T2"); p.addSubTask(task2, &p); task2->estimate()->setUnit(Duration::Unit_d); task2->estimate()->setExpectedEstimate(1.0); task2->estimate()->setType(Estimate::Type_Effort); - gr = new ResourceGroupRequest(g); - task2->addRequest(gr); tr = new ResourceRequest(r1, 100); - task2->requests().addResourceRequest(tr, gr); + task2->requests().addResourceRequest(tr); QString s = "Schedule T1 MustFinishOn, T2 ASAP -------"; qDebug()<createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); s = "Schedule T1 MustFinishOn, T2 ALAP -------"; qDebug()<setConstraint(Node::ALAP); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------"; qDebug()<setConstraint(Node::StartNotEarlier); task2->setConstraintStartTime(p.constraintStartTime()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------"; qDebug()<setConstraintStartTime(task1->mustFinishOn() - Duration(0, 8, 0)); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), task2->constraintStartTime() + Duration(1, 0, 0)); s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------"; qDebug()<setConstraintStartTime(task1->mustFinishOn()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), task2->constraintStartTime() + Duration(0, 16, 0)); s = "Schedule backwards, T1 MustFinishOn, T2 ASAP -------"; qDebug()<setConstraint(Node::ASAP); sm->createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->startTime(), task1->mustFinishOn() + Duration(0, 16, 0)); s = "Schedule backwards, T1 MustFinishOn, T2 ALAP -------"; qDebug()<setConstraint(Node::ALAP); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->endTime(), et - Duration(0, 8, 0)); s = "Schedule backwards, T1 MustFinishOn, T2 StartNotEarlier -------"; qDebug()<setConstraint(Node::StartNotEarlier); task2->setConstraintStartTime(task1->mustFinishOn()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->endTime(), et - Duration(0, 8, 0)); task2->setConstraintStartTime(p.constraintStartTime()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->endTime(), et - Duration(0, 8, 0)); s = "Schedule backwards, T1 MustFinishOn, T2 FinishNotLater -------"; qDebug()<setConstraint(Node::FinishNotLater); task2->setConstraintEndTime(task1->mustFinishOn()); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->endTime(), task1->startTime() - Duration(0, 16, 0)); task2->setConstraintEndTime(task1->mustFinishOn().addDays(2)); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->endTime(), task1->mustFinishOn()); QCOMPARE(task2->endTime(), task2->finishNotLater()); } void ProjectTester::fixedInterval() { Project p; p.setName("P1"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2010-10-20T08:00", Qt::ISODate); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *task1 = p.createTask(); task1->setName("T1"); task1->setConstraint(Node::FixedInterval); task1->setConstraintStartTime(st); task1->setConstraintEndTime(st.addDays(1)); p.addTask(task1, &p); QString s = "Schedule T1 Fixed interval -------"; qDebug()<createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), task1->constraintStartTime()); QCOMPARE(task1->endTime(), task1->constraintEndTime()); s = "Schedule backward: T1 Fixed interval -------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); // Debug::printSchedulingLog(*sm, s); QCOMPARE(task1->startTime(), task1->constraintStartTime()); QCOMPARE(task1->endTime(), task1->constraintEndTime()); } void ProjectTester::estimateDuration() { Project p; p.setName("P1"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2010-10-20 08:00", Qt::TextDate); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Task *task1 = p.createTask(); task1->setName("T1"); task1->setConstraint(Node::ASAP); p.addTask(task1, &p); task1->estimate()->setType(Estimate::Type_Duration); task1->estimate()->setUnit(Duration::Unit_h); task1->estimate()->setExpectedEstimate(10); QString s = "Schedule T1 Estimate type Duration -------"; qDebug()<createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime()); QCOMPARE(task1->endTime(), task1->startTime() + Duration(0, 10, 0)); s = "Schedule backward: T1 Estimate type Duration -------"; qDebug()<setSchedulingDirection(true); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->endTime(), p.constraintEndTime()); QCOMPARE(task1->startTime(), task1->endTime() - Duration(0, 10, 0)); } void ProjectTester::startStart() { Project p; p.setName("P1"); p.setId(p.uniqueNodeId()); p.registerNodeId(&p); DateTime st = QDateTime::fromString("2010-10-20T00:00:00", Qt::ISODate); p.setConstraintStartTime(st); p.setConstraintEndTime(st.addDays(5)); Calendar *c = new Calendar("Test"); QTime t1(8,0,0); int length = 8*60*60*1000; // 8 hours for (int i = 1; i <= 7; ++i) { CalendarDay *wd1 = c->weekday(i); wd1->setState(CalendarDay::Working); wd1->addInterval(TimeInterval(t1, length)); } p.addCalendar(c); p.setDefaultCalendar(c); ResourceGroup *g = new ResourceGroup(); p.addResourceGroup(g); Resource *r1 = new Resource(); r1->setName("R1"); p.addResource(r1); r1->addParentGroup(g); Resource *r2 = new Resource(); r2->setName("R2"); p.addResource(r2); r2->addParentGroup(g); Task *task1 = p.createTask(); task1->setName("T1"); task1->setConstraint(Node::ASAP); p.addTask(task1, &p); task1->estimate()->setType(Estimate::Type_Duration); task1->estimate()->setUnit(Duration::Unit_h); task1->estimate()->setExpectedEstimate(2); Task *task2 = p.createTask(); task2->setName("T2"); task2->setConstraint(Node::ASAP); p.addTask(task2, &p); task2->estimate()->setType(Estimate::Type_Duration); task2->estimate()->setUnit(Duration::Unit_h); task2->estimate()->setExpectedEstimate(2); task1->addDependChildNode(task2, Relation::StartStart); QString s = "Schedule T1 Lag = 0 -------"; qDebug()<createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime()); QCOMPARE(task1->lateStart(), task2->lateStart()); QCOMPARE(task1->startTime(), task2->startTime()); s = "Schedule backward T1 Lag = 0 -------"; qDebug()<createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); // Debug::printSchedulingLog(*sm, s); Debug::print(&p, s, true); qDebug()<<"predeccessors:"<dependParentNodes(); QCOMPARE(task2->endTime(), p.constraintEndTime()); QCOMPARE(task1->lateStart(), task2->lateStart()); QCOMPARE(task1->startTime(), task2->startTime()); s = "Schedule T1 calendar -------"; qDebug()<estimate()->setCalendar(c); sm = p.createScheduleManager("Lag = 0"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); QCOMPARE(task1->lateStart(), task2->lateStart()); QCOMPARE(task1->startTime(), task2->startTime()); s = "Schedule backward T1 calendar -------"; qDebug()<estimate()->setCalendar(0); task2->estimate()->setCalendar(c); sm = p.createScheduleManager("Backward, calendar, Lag = 0"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task2->endTime(), p.constraintEndTime() - Duration(0, 8, 0)); QCOMPARE(task1->lateStart(), task2->lateStart()); QCOMPARE(task1->startTime(), task2->startTime()); s = "Schedule Lag = 1 hour -------"; qDebug()<estimate()->setCalendar(c); task2->estimate()->setCalendar(0); task1->dependChildNodes().at(0)->setLag(Duration(0, 1, 0)); sm = p.createScheduleManager("Lag = 1 hour"); p.addScheduleManager(sm); sm->createSchedules(); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); QCOMPARE(task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QCOMPARE(task2->startTime(), task1->startTime() + task1->dependChildNodes().at(0)->lag()); s = "Schedule backward Lag = 1 hour -------"; qDebug()<estimate()->setCalendar(0); task2->estimate()->setCalendar(c); sm = p.createScheduleManager("Backward, Lag = 1 hour"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task2->endTime(), p.constraintEndTime() - Duration(0, 8, 0)); QCOMPARE(task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QCOMPARE(task2->startTime(), task1->startTime() + task1->dependChildNodes().at(0)->lag()); s = "Schedule resources Lag = 1 hour -------"; qDebug()<estimate()->setCalendar(0); task2->estimate()->setCalendar(0); - ResourceGroupRequest *gr1 = new ResourceGroupRequest(g); - task1->addRequest(gr1); ResourceRequest *rr1 = new ResourceRequest(r1, 100); - task1->requests().addResourceRequest(rr1, gr1); + task1->requests().addResourceRequest(rr1); task1->estimate()->setType(Estimate::Type_Effort); - ResourceGroupRequest *gr2 = new ResourceGroupRequest(g); - task2->addRequest(gr2); ResourceRequest *rr2 = new ResourceRequest(r2, 100); - task2->requests().addResourceRequest(rr2, gr2); + task2->requests().addResourceRequest(rr2); task2->estimate()->setType(Estimate::Type_Effort); sm = p.createScheduleManager("Resources, Lag = 1 hour"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(false); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime() + Duration(0, 8, 0)); QCOMPARE(task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QCOMPARE(task2->startTime(), task1->startTime() + task1->dependChildNodes().at(0)->lag()); s = "Schedule backward resources Lag = 1 hour -------"; qDebug()<createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task2->endTime(), p.constraintEndTime() - Duration(0, 8, 0)); QCOMPARE(task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QCOMPARE(task2->startTime(), task1->startTime() + task1->dependChildNodes().at(0)->lag()); s = "Schedule resources w limited availability, Lag = 1 hour -------"; qDebug()<setAvailableFrom(p.constraintStartTime() + Duration(0, 9, 0)); r1->setAvailableUntil(p.constraintEndTime() - Duration(0, 12, 0)); sm = p.createScheduleManager("Resources, Lag = 1 hour"); p.addScheduleManager(sm); sm->createSchedules(); sm->setSchedulingDirection(false); p.calculate(*sm); Debug::print(&p, s, true); QCOMPARE(task1->startTime(), p.constraintStartTime() + Duration(0, 9, 0)); QCOMPARE(task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QCOMPARE(task2->startTime(), task1->startTime() + task1->dependChildNodes().at(0)->lag()); s = "Schedule backward resources w limited availability Lag = 1 hour -------"; qDebug()<createSchedules(); sm->setSchedulingDirection(true); p.calculate(*sm); Debug::print(&p, s, true); QVERIFY(task2->lateStart() >= task1->lateStart() + task1->dependChildNodes().at(0)->lag()); QVERIFY(task2->startTime() >= task1->startTime() + task1->dependChildNodes().at(0)->lag()); } void ProjectTester::scheduleTimeZone() { QByteArray tz("TZ=Europe/Copenhagen"); putenv(tz.data()); qDebug()<<"Local timezone: "<day(), 8.0); Calendar *calendar = new Calendar(); calendar->setName("LocalTime, Copenhagen"); calendar->setDefault(true); QCOMPARE(calendar->timeZone(), QTimeZone::systemTimeZone()); QTime time1(9, 0, 0); QTime time2 (17, 0, 0); int length = time1.msecsTo(time2); for (int i=1; i <= 7; ++i) { CalendarDay *d = calendar->weekday(i); d->setState(CalendarDay::Working); d->addInterval(time1, length); } project.addCalendar(calendar); Calendar *cal2 = new Calendar(); cal2->setName("Helsinki"); cal2->setTimeZone(QTimeZone("Europe/Helsinki")); QVERIFY(cal2->timeZone().isValid()); for (int i=1; i <= 7; ++i) { CalendarDay *d = cal2->weekday(i); d->setState(CalendarDay::Working); d->addInterval(time1, length); } project.addCalendar(cal2); QDate today = project.constraintStartTime().date(); Task *t = project.createTask(); t->setName("T1"); project.addTask(t, &project); t->estimate()->setUnit(Duration::Unit_d); t->estimate()->setExpectedEstimate(1.0); t->estimate()->setType(Estimate::Type_Effort); Task *t2 = project.createTask(); t2->setName("T2"); project.addTask(t2, &project); t2->estimate()->setUnit(Duration::Unit_d); t2->estimate()->setExpectedEstimate(1.0); t2->estimate()->setType(Estimate::Type_Effort); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); r->setCalendar(calendar); project.addResource(r); r->addParentGroup(g); Resource *r2 = new Resource(); r2->setName("R2"); r2->setCalendar(cal2); project.addResource(r2); r2->addParentGroup(g); - - ResourceGroupRequest *gr = new ResourceGroupRequest(g); - t->addRequest(gr); + ResourceRequest *rr = new ResourceRequest(r, 100); - t->requests().addResourceRequest(rr, gr); + t->requests().addResourceRequest(rr); - ResourceGroupRequest *gr2 = new ResourceGroupRequest(g); - t2->addRequest(gr2); ResourceRequest *rr2 = new ResourceRequest(r2, 100); - t2->requests().addResourceRequest(rr2, gr2); + t2->requests().addResourceRequest(rr2); QString s = "Calculate forward, Task: ASAP -----------------------------------"; qDebug()<createSchedules(); project.calculate(*sm); Debug::print(&project, t, s); // Debug::printSchedulingLog(*sm, s); QCOMPARE(t->startTime(), DateTime(today, time1)); QCOMPARE(t->endTime(), t->startTime() + Duration(0, 8, 0)); QCOMPARE(t->plannedEffort().toHours(), 8.0); QVERIFY(t->schedulingError() == false); QCOMPARE(t2->startTime(), DateTime(today, time1.addSecs(-3600))); QCOMPARE(t2->endTime(), t2->startTime() + Duration(0, 8, 0)); QCOMPARE(t2->plannedEffort().toHours(), 8.0); QVERIFY(t2->schedulingError() == false); unsetenv("TZ"); } } //namespace KPlato QTEST_GUILESS_MAIN(KPlato::ProjectTester) diff --git a/src/libs/kernel/tests/ProjectTester.h b/src/libs/kernel/tests/ProjectTester.h index 251f72de..79d73204 100644 --- a/src/libs/kernel/tests/ProjectTester.h +++ b/src/libs/kernel/tests/ProjectTester.h @@ -1,86 +1,83 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPlato_ProjectTester_h #define KPlato_ProjectTester_h #include #include "kptproject.h" #include "kptdatetime.h" namespace KPlato { class Task; class ProjectTester : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); - void testAddTask(); void testTakeTask(); void testTaskAddCmd(); void testTaskDeleteCmd(); void schedule(); void scheduleFullday(); void scheduleFulldayDstSpring(); void scheduleFulldayDstFall(); void scheduleWithExternalAppointments(); void reschedule(); void materialResource(); void requiredResource(); void resourceWithLimitedAvailability(); void unavailableResource(); - void team(); - // NOTE: It's not *mandatory* to schedule in wbs order but users expect it, so we'll try. // This test can be removed if for some important reason this isn't possible. void inWBSOrder(); void resourceConflictALAP(); void resourceConflictMustStartOn(); void resourceConflictMustFinishOn(); void fixedInterval(); void estimateDuration(); void startStart(); void scheduleTimeZone(); private: Project *m_project; Calendar *m_calendar; Task *m_task; QByteArray tz; }; } //namespace KPlato #endif diff --git a/src/libs/kernel/tests/debug.cpp b/src/libs/kernel/tests/debug.cpp index 4e6817a4..23eb706e 100644 --- a/src/libs/kernel/tests/debug.cpp +++ b/src/libs/kernel/tests/debug.cpp @@ -1,418 +1,416 @@ /* This file is part of the KDE project * Copyright (C) 2009, 2010 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptappointment.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptproject.h" #include "kptresource.h" #include "kptnode.h" #include "kpttask.h" #include "kptschedule.h" #include #include #include namespace QTest { template<> char *toString(const KPlato::DateTime &dt) { QString s; switch (dt.timeSpec()) { case Qt::LocalTime: s = " LocalTime"; break; case Qt::UTC: s = " UTC"; break; case Qt::OffsetFromUTC: s = " OffsetFromUTC"; break; case Qt::TimeZone: s = " TimeZone (" + dt.timeZone().id() + ')'; break; } return toString(QString("%1T%2 %3").arg(dt.date().toString(Qt::ISODate), dt.time().toString("hh:mm:ss.zzz"), s)); } template<> char *toString(const KPlato::Duration &d) { return toString(d.toString()); } } namespace KPlato { class Debug { public: Debug() {} static void print(Calendar *c, const QString &str, bool full = true) { Q_UNUSED(full); QTimeZone tz = c->timeZone(); QString s = tz.isValid() ? QString::fromLatin1(tz.id()) : QStringLiteral("LocalTime"); qDebug()<<"Debug info: Calendar"<name()<weekday(wd); qDebug()<<" "<stateToString(d->state()); foreach (TimeInterval *t, d->timeIntervals()) { qDebug()<<" interval:"<first<second<<'('<second)).toString()<<')'; } } foreach (const CalendarDay *d, c->days()) { qDebug()<<" "<date()<<':'; foreach (TimeInterval *t, d->timeIntervals()) { qDebug()<<" interval:"<first<second; } } } static QString printAvailable(Resource *r, const QString &lead = QString()) { QStringList sl; sl<availableFrom().isValid() ? r->availableFrom().toString() : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString())) <<(r->availableUntil().isValid() ? r->availableUntil().toString() : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString())) <units())<<"%" <<"cost: normal"<normalRate())<<" overtime"<overtimeRate()); return sl.join(" "); } static void print(ResourceGroup *g, const QString &str, bool full = true) { qDebug()<<"Debug info: Group"<name()<numResources()<<"id:"<id()<resources()) { qDebug()<<" "<name()<id(); } } static void print(Resource *r, const QString &str, bool full = true) { qDebug()<<"Debug info: Resource"<name()<<"id:"<id()<<(void*)r<parentGroups().count(); for (ResourceGroup *g : r->parentGroups()) { qDebug()<<" "<name() + " Type: " + g->typeToString() << "id:"<id(); } qDebug()<<" Available:" <<(r->availableFrom().isValid() ? r->availableFrom().toString() : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString())) <<(r->availableUntil().isValid() ? r->availableUntil().toString() : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString())) <units()<<'%'; qDebug()<<" Type:"<typeToString(); if (r->type() == Resource::Type_Team) { qDebug()<<" Team members:"<teamMembers().count(); foreach (Resource *tm, r->teamMembers()) { - qDebug()<<" "<name()<<"Available:" - <<(r->availableFrom().isValid() - ? r->availableFrom().toString() - : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString())) - <<(r->availableUntil().isValid() - ? r->availableUntil().toString() - : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString())) - <units()<<'%'; + print(tm, ""); +// qDebug()<<" "<name()<<"Available:" +// <<(r->availableFrom().isValid() +// ? r->availableFrom().toString() +// : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString())) +// <<(r->availableUntil().isValid() +// ? r->availableUntil().toString() +// : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString())) +// <units()<<'%'; } } else { Calendar *cal = r->calendar(true); QString s; if (cal) { s = cal->name(); } else { cal = r->calendar(false); if (cal) { s = cal->name() + " (Default)"; } else { s = " No calendar"; } } qDebug()<<" Calendar:"<requiredResources().isEmpty()) { qDebug()<<" No required resources"; } else { qDebug()<<" Required resources:"<requiredResources().count()<<'('<requiredIds().count()<<')'; for (Resource *req : r->requiredResources()) { qDebug()<<" "<name()<type()<id()<<(void*)req<requiredIds().contains(req->id()); } } if (! full) return; qDebug()<<" External appointments:"<numExternalAppointments(); foreach (Appointment *a, r->externalAppointmentList()) { qDebug()<<" appointment:"<startTime().toString()<endTime().toString(); foreach(const AppointmentInterval &i, a->intervals().map()) { qDebug()<<" "<name()<constraintStartTime())); qDebug()<<"project target end :"<constraintEndTime())); if (p->isScheduled()) { qDebug()<<" project start time:"<startTime())); qDebug()<<" project end time :"<endTime())); } else { qDebug()<<" Not scheduled"; } qDebug()<<" Default calendar:"<<(p->defaultCalendar()?p->defaultCalendar()->name():QString("None")); if (! all) { return; } if (p->resourceGroups().isEmpty()) { qDebug()<<" No resource groups"; } else { for (ResourceGroup *g : p->resourceGroups()) { qDebug(); print(g, "", true); } } if (p->resourceList().isEmpty()) { qDebug()<<" No resources"; } else { for (Resource *r : p->resourceList()) { qDebug(); print(r, "", true); } } for (int i = 0; i < p->numChildren(); ++i) { qDebug(); print(static_cast(p->childNode(i)), true); } } static void print(Project *p, Task *t, const QString &str, bool full = true) { Q_UNUSED(full); print(p, str); print(t); } static void print(Task *t, const QString &str, bool full = true) { qDebug()<<"Debug info: Task"<name()<level() > 0) { pad = QString("%1").arg("", t->level()*2, ' '); } qDebug()<wbsCode()<name()<typeToString()<constraintToString()<<(void*)t; if (t->isScheduled()) { qDebug()<earlyStart())); qDebug()<lateStart())); qDebug()<earlyFinish())); qDebug()<lateFinish())); qDebug()<startTime())); qDebug()<endTime())); } else { qDebug()<constraint()) { case Node::MustStartOn: case Node::StartNotEarlier: qDebug()<constraintStartTime())); break; case Node::FixedInterval: qDebug()<constraintStartTime())); case Node::MustFinishOn: case Node::FinishNotLater: qDebug()<constraintEndTime())); break; default: break; } qDebug()<estimate()->expectedEstimate()<estimate()->unit()) <estimate()->typeToString() <<(t->estimate()->type() == Estimate::Type_Duration ? (t->estimate()->calendar()?t->estimate()->calendar()->name():"Fixed") : QString("%1 h").arg(t->estimate()->expectedValue().toDouble(Duration::Unit_h))); - qDebug()<requests().requests().count()<<"resources:"<requests().resourceRequests().count(); - foreach (ResourceGroupRequest *gr, t->requests().requests()) { - qDebug()<group()->name()<units()<<':'<<(void*)gr; - foreach (ResourceRequest *rr, gr->resourceRequests()) { - qDebug()<resource(), " " + rr->resource()->name())<<"id:"<resource()->id()<<(void*)rr->resource()<<':'<<(void*)rr; - } + qDebug()<requests().resourceRequests().count(); + for (ResourceRequest *rr : t->requests().resourceRequests()) { + qDebug()<resource(), " " + rr->resource()->name())<<"id:"<resource()->id()<<(void*)rr->resource()<<':'<<(void*)rr; } if (t->isStartNode()) { qDebug()<dependChildNodes()) { QString type; switch(r->type()) { case Relation::StartStart: type = "SS"; break; case Relation::FinishFinish: type = "FF"; break; default: type = "FS"; break; } rel << QString("(%1) -> %2, %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction)); } if (!rel.isEmpty()) { qDebug()<isEndNode()) { qDebug()<dependParentNodes()) { QString type; switch(r->type()) { case Relation::StartStart: type = "SS"; break; case Relation::FinishFinish: type = "FF"; break; default: type = "FS"; break; } rel << QString("%1 -> (%2), %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction)); } if (!rel.isEmpty()) { qDebug()<currentSchedule(); if (s) { qDebug()<appointments().count(); foreach (Appointment *a, s->appointments()) { qDebug()<resource()->resource()->name()<<"booked:"<startTime()))<endTime()))<<"effort:"<effort(a->startTime(), a->endTime()).toDouble(Duration::Unit_h)<<'h'; if (! full) { continue; } foreach(const AppointmentInterval &i, a->intervals().map()) { qDebug()<runningAccount()) { qDebug()<runningAccount()->name(); } if (t->startupAccount()) { qDebug()<startupAccount()->name()<<" cost:"<startupCost(); } if (t->shutdownAccount()) { qDebug()<shutdownAccount()->name()<<" cost:"<shutdownCost(); } if (full) { for (int i = 0; i < t->numChildren(); ++i) { qDebug()<(t->childNode(i)), full); } } } static void print(const Completion &c, const QString &name, const QString &s = QString()) { qDebug()<<"Completion:"<actualEffortMap().keys()) { // clazy:exclude=container-anti-pattern qDebug()<<" "<name()<<':'; qDebug()<<" "<logMessages()) { qDebug()<name()<name()<<(a->isDefaultAccount() ? "Default" : ""); EffortCostMap ec = a->plannedCost(id); qDebug()<<"Planned cost:"<isElement()) { foreach (Account *c, a->accountList()) { print(c); } return; } qDebug()<<"Cost places:"; foreach (Account::CostPlace *cp, a->costPlaces()) { qDebug()<<" Node:"<<(cp->node() ? cp->node()->name() : ""); qDebug()<<" running:"<running(); qDebug()<<" startup:"<startup(); qDebug()<<" shutdown:"<shutdown(); } } static void print(const AppointmentInterval &i, const QString &indent = QString()) { QString s = indent + "Interval:"; if (! i.isValid()) { qDebug()< * Copyright (C) 2011, 2012 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "AllocatedResourceItemModel.h" #include "ResourceItemModel.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptdebug.h" #include #include #include #include #include #include #include #ifdef PLAN_KCONTACTS_FOUND #include #include #endif using namespace KPlato; AllocatedResourceItemModel::AllocatedResourceItemModel(QObject *parent) : QSortFilterProxyModel(parent), m_task(0) { setDynamicSortFilter(true); setSourceModel(new ResourceItemModel(this)); } int AllocatedResourceItemModel::columnCount(const QModelIndex &idx) const { Q_UNUSED(idx); return 2; } Project *AllocatedResourceItemModel::project() const { return static_cast(sourceModel())->project(); } void AllocatedResourceItemModel::setProject(Project *project) { debugPlan<project()<<"="<project(); if (p) { disconnect(p, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged); } static_cast(sourceModel())->setProject(project); if (project) { connect(project, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged); } debugPlan<rowCount(); } void AllocatedResourceItemModel::reset() { beginResetModel(); endResetModel(); emit expandAll(); emit resizeColumnToContents(0); } void AllocatedResourceItemModel::slotNodeChanged(Node *n) { debugPlan<<(n==m_task)<name(); if (n != m_task) { return; } reset(); } Task *AllocatedResourceItemModel::task() const { return m_task; } void AllocatedResourceItemModel::setTask(Task *task) { debugPlan<name():""); m_task = task; reset(); debugPlan<rowCount(); } QObject* AllocatedResourceItemModel::object(const QModelIndex& idx) const { return nullptr; //TODO static_cast(sourceModel())->object(mapToSource(idx)); } Resource *AllocatedResourceItemModel::resource(const QModelIndex &idx) const { QModelIndex sidx = mapToSource(idx); if (sidx.isValid() && sidx.internalPointer() == nullptr) { return project()->resourceAt(sidx.row()); } return nullptr; } QModelIndex AllocatedResourceItemModel::index(Resource *r) const { return mapFromSource(static_cast(sourceModel())->index(project()->indexOf(r), 0)); } Qt::ItemFlags AllocatedResourceItemModel::flags(const QModelIndex & index) const { Qt::ItemFlags f = QSortFilterProxyModel::flags(index); f &= ~Qt::ItemIsUserCheckable; return f; } QVariant AllocatedResourceItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section == 1) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { return xi18nc("@title:column", "Allocation"); } return QVariant(); } return QSortFilterProxyModel::headerData(section, orientation, role); } QVariant AllocatedResourceItemModel::allocation(const Resource *res, int role) const { ResourceRequest *rr = m_task->requests().find(res); - ResourceGroupRequest *gr = m_task->requests().find(res->parentGroups().value(0)); - if (rr == 0 || gr == 0) { + if (rr == nullptr) { return QVariant(); } switch (role) { case Qt::DisplayRole: { case Qt::EditRole: // xgettext: no-c-format return i18nc("%", "%1%",rr->units()); } case Qt::ToolTipRole: { if (rr->units() == 0) { return xi18nc("@info:tooltip", "Not allocated"); } - return xi18nc("@info:tooltip", "%1 allocated out of %2 available", gr->count(), res->parentGroups().value(0)->numResources()); + break; } default: break; } return QVariant(); } QVariant AllocatedResourceItemModel::allocation(const ResourceGroup *res, int role) const { - ResourceGroupRequest *gr = m_task->requests().find(res); - if (gr == 0) { - return QVariant(); - } - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: - return QString("%1 (%2)").arg(gr->units()).arg(gr->count()); - case Qt::ToolTipRole: { - QString s1 = i18ncp("@info:tooltip", - "%1 resource requested for dynamic allocation", - "%1 resources requested for dynamic allocation", - gr->units()); - QString s2 = i18ncp("@info:tooltip", - "%1 resource allocated", - "%1 resources allocated", - gr->count()); - - return xi18nc("@info:tooltip", "%1%2", s1, s2); - } - case Qt::WhatsThisRole: { - return xi18nc("@info:whatsthis", - "Group allocations" - "You can allocate a number of resources from a group and let" - " the scheduler select from the available resources at the time of scheduling." - " These dynamically allocated resources will be in addition to any resource you have allocated specifically."); - } - case Role::Minimum: { - return 0; - } - case Role::Maximum: { - return res->numResources() - gr->units(); - } - default: - break; - } + // TODO return QVariant(); } QVariant AllocatedResourceItemModel::data(const QModelIndex& idx, int role) const { if (m_task == 0 || role == Qt::CheckStateRole || role == Qt::DecorationRole) { return QVariant(); } if (idx.column() == 1) { switch (role) { case Qt::TextAlignmentRole: return Qt::AlignLeft; default: { QObject *o = object(idx); Resource *r = qobject_cast(o); if (r) { return allocation(r, role); } ResourceGroup *g = qobject_cast(o); if (g) { return allocation(g, role); } break; } return QVariant(); } } return QSortFilterProxyModel::data(idx, role); } bool AllocatedResourceItemModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const { if (m_task == 0) { return false; } QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); - if (! idx.isValid()) { + if (!idx.isValid()) { return false; } bool result = false; const ResourceRequestCollection &req = m_task->requests(); if (source_parent.isValid()) { const Resource *r = static_cast(sourceModel())->resource(idx); result = (bool) req.find(r); } else { - const ResourceGroup *g = static_cast(sourceModel())->group(idx); - ResourceGroupRequest *gr = req.find(g); - result = (bool) gr && (gr->units() > 0 || gr->count() > 0); + // TODO } debugPlan< * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "InsertProjectXmlCommand.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptcalendar.h" #include "kptrelation.h" #include "kptresource.h" #include "kptdocuments.h" #include "kptlocale.h" #include "kptdebug.h" #include const QLoggingCategory &PLANCMDINSPROJECT_LOG() { static const QLoggingCategory category("calligra.plan.command.insertProjectXml"); return category; } #define debugPlanInsertProjectXml qCDebug(PLANCMDINSPROJECT_LOG) #define warnPlanInsertProjectXml qCWarning(PLANCMDINSPROJECT_LOG) #define errorPlanInsertProjectXml qCCritical(PLANCMDINSPROJECT_LOG) using namespace KPlato; AddTaskCommand::AddTaskCommand(Project *project, Node *parent, Node *node, Node *after, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_parent(parent) , m_node(node) , m_after(after) , m_added(false) { } AddTaskCommand::~AddTaskCommand() { if (!m_added) delete m_node; } void AddTaskCommand::execute() { m_project->addSubTask(m_node, m_parent->indexOf(m_after), m_parent, true); m_added = true; } void AddTaskCommand::unexecute() { m_project->takeTask(m_node); m_added = false; } InsertProjectXmlCommand::InsertProjectXmlCommand(Project *project, const QByteArray &data, Node *parent, Node *position, const KUndo2MagicString& name) : MacroCommand(name) , m_project(project) , m_data(data) , m_parent(parent) , m_position(position) , m_first(true) { //debugPlanInsertProjectXml<name(); Q_ASSERT(project != 0); m_context.setProject(project); m_context.setProjectTimeZone(project->timeZone()); // from xml doc? m_context.setLoadTaskChildren(false); } InsertProjectXmlCommand::~InsertProjectXmlCommand() { } void InsertProjectXmlCommand::execute() { if (m_first) { // create and execute commands KoXmlDocument doc; doc.setContent(m_data); m_context.setVersion(doc.documentElement().attribute("plan-version", PLAN_FILE_SYNTAX_VERSION)); KoXmlElement projectElement = doc.documentElement().namedItem("project").toElement(); createCmdAccounts(projectElement); createCmdCalendars(projectElement); createCmdResources(projectElement); createCmdTasks(projectElement); createCmdRelations(projectElement); createCmdRequests(projectElement); m_first = false; m_data.clear(); } else { MacroCommand::execute(); } } void InsertProjectXmlCommand::unexecute() { MacroCommand::unexecute(); } void InsertProjectXmlCommand::createCmdAccounts(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertProjectXmlCommand::createCmdCalendars(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertProjectXmlCommand::createCmdResources(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertProjectXmlCommand::createCmdRequests(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } debugPlanInsertProjectXml<(m_oldIds.value(ge.attribute("task-id"))); - ResourceGroup *group = m_project->findResourceGroup(ge.attribute("group-id")); - if (task && group) { - int units = ge.attribute("units", "0").toInt(); - int requestId = ge.attribute("request-id").toInt(); - ResourceGroupRequest *request = new ResourceGroupRequest(group, units); - request->setId(requestId); - KUndo2Command *cmd = new AddResourceGroupRequestCmd(*task, request); - cmd->redo(); - addCommand(cmd); - debugPlanInsertProjectXml<<"added grouprequest:"<(m_oldIds.value(re.attribute("task-id"))); if (!task) { warnPlanInsertProjectXml<requests().groupRequest(re.attribute("request-id").toInt()); - if (!group) { - warnPlanInsertProjectXml<findResource(re.attribute("resource-id")); Q_ASSERT(resource); Q_ASSERT(task); if (resource && task) { int units = re.attribute("units", "100").toInt(); ResourceRequest *request = new ResourceRequest(resource, units); int requestId = re.attribute("request-id").toInt(); Q_ASSERT(requestId > 0); request->setId(requestId); - KUndo2Command *cmd = new AddResourceRequestCmd(&task->requests(), request, group); + KUndo2Command *cmd = new AddResourceRequestCmd(&task->requests(), request); cmd->redo(); addCommand(cmd); debugPlanInsertProjectXml<<"added resourcerequest:"<(m_oldIds.value(re.attribute("task-id"))); Q_ASSERT(task); if (!task) { warnPlanInsertProjectXml<requests().resourceRequest(re.attribute("request-id").toInt()); Resource *required = m_project->findResource(re.attribute("required-id")); QList lst; if (required && request->resource() != required) { lst << required; } KUndo2Command *cmd = new ModifyResourceRequestRequiredCmd(request, lst); cmd->redo(); addCommand(cmd); debugPlanInsertProjectXml<<"added requiredrequest:"<createTask(); QString id = task->id(); task->load(taskElement, m_context); m_oldIds.insert(task->id(), task); task->setId(id); NamedCommand *cmd = new AddTaskCommand(m_project, parent, task, position); cmd->execute(); addCommand(cmd); createCmdTask(taskElement, task); // add children } } void InsertProjectXmlCommand::createCmdRelations(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } KoXmlElement relationElement; forEachElement(relationElement, projectElement) { if (relationElement.tagName() != "relation") { continue; } Node *parent = m_oldIds.value(relationElement.attribute("parent-id")); Node *child = m_oldIds.value(relationElement.attribute("child-id")); if (parent && child) { Relation *relation = new Relation(parent, child); relation->setType(relationElement.attribute("type")); relation->setLag(Duration::fromString(relationElement.attribute("lag"))); AddRelationCmd *cmd = new AddRelationCmd(*m_project, relation); cmd->execute(); addCommand(cmd); } } } diff --git a/src/libs/models/commands/InsertTaskModuleCommand.cpp b/src/libs/models/commands/InsertTaskModuleCommand.cpp index 6a0a988f..411276b3 100644 --- a/src/libs/models/commands/InsertTaskModuleCommand.cpp +++ b/src/libs/models/commands/InsertTaskModuleCommand.cpp @@ -1,304 +1,281 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "InsertTaskModuleCommand.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptcalendar.h" #include "kptrelation.h" #include "kptresource.h" #include "kptdocuments.h" #include "kptlocale.h" #include "kptdebug.h" #include const QLoggingCategory &PLANCMDINSTASKMODULE_LOG() { static const QLoggingCategory category("calligra.plan.command.inserttaskmodule"); return category; } #define debugPlanInsertTaskModule qCDebug(PLANCMDINSTASKMODULE_LOG) #define warnPlanInsertTaskModule qCWarning(PLANCMDINSTASKMODULE_LOG) #define errorPlanInsertTaskModule qCCritical(PLANCMDINSTASKMODULE_LOG) using namespace KPlato; class AddTaskCommand : public NamedCommand { public: AddTaskCommand(Project *project, Node *parent, Node *node, Node *after, const KUndo2MagicString& name = KUndo2MagicString()); ~AddTaskCommand() override; void execute() override; void unexecute() override; private: Project *m_project; Node *m_parent; Node *m_node; Node *m_after; bool m_added; }; AddTaskCommand::AddTaskCommand(Project *project, Node *parent, Node *node, Node *after, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_parent(parent) , m_node(node) , m_after(after) , m_added(false) { } AddTaskCommand::~AddTaskCommand() { if (!m_added) delete m_node; } void AddTaskCommand::execute() { m_project->addSubTask(m_node, m_parent->indexOf(m_after), m_parent, true); m_added = true; } void AddTaskCommand::unexecute() { m_project->takeTask(m_node); m_added = false; } InsertTaskModuleCommand::InsertTaskModuleCommand(Project *project, const QByteArray &data, Node *parent, Node *position, const QMap substitute, const KUndo2MagicString& name) : MacroCommand(name) , m_project(project) , m_data(data) , m_parent(parent) , m_position(position) , m_substitute(substitute) , m_first(true) { //debugPlan<name(); Q_ASSERT(project != 0); m_context.setProject(project); m_context.setProjectTimeZone(project->timeZone()); // from xml doc? m_context.setLoadTaskChildren(false); } InsertTaskModuleCommand::~InsertTaskModuleCommand() { } void InsertTaskModuleCommand::execute() { if (m_first) { // create and execute commands KoXmlDocument doc; doc.setContent(m_data); m_context.setVersion(doc.documentElement().attribute("plan-version", PLAN_FILE_SYNTAX_VERSION)); KoXmlElement projectElement = doc.documentElement().namedItem("project").toElement(); createCmdAccounts(projectElement); createCmdCalendars(projectElement); createCmdResources(projectElement); createCmdTasks(projectElement); createCmdRelations(projectElement); createCmdRequests(projectElement); m_first = false; m_data.clear(); } else { MacroCommand::execute(); } } void InsertTaskModuleCommand::unexecute() { MacroCommand::unexecute(); } void InsertTaskModuleCommand::createCmdAccounts(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertTaskModuleCommand::createCmdCalendars(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertTaskModuleCommand::createCmdResources(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } } void InsertTaskModuleCommand::createCmdRequests(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } debugPlanXml<(m_oldIds.value(ge.attribute("task-id"))); - ResourceGroup *group = m_project->findResourceGroup(ge.attribute("group-id")); - if (task && group) { - int units = ge.attribute("units", "0").toInt(); - int requestId = ge.attribute("request-id").toInt(); - ResourceGroupRequest *request = new ResourceGroupRequest(group, units); - request->setId(requestId); - KUndo2Command *cmd = new AddResourceGroupRequestCmd(*task, request); - cmd->redo(); - addCommand(cmd); - } else { - warnPlanXml<<"Failed to find group or task"<(m_oldIds.value(ge.attribute("task-id"))); + Task *task = qobject_cast(m_oldIds.value(re.attribute("task-id"))); if (!task) { warnPlanXml<requests().groupRequest(re.attribute("request-id").toInt()); - if (!group) { - warnPlanXml<findResource(re.attribute("resource-id")); Q_ASSERT(resource); Q_ASSERT(task); if (resource && task) { int units = re.attribute("units", "100").toInt(); ResourceRequest *request = new ResourceRequest(resource, units); int requestId = re.attribute("request-id").toInt(); Q_ASSERT(requestId > 0); request->setId(requestId); KUndo2Command *cmd = new AddResourceRequestCmd(&task->requests(), request); cmd->redo(); addCommand(cmd); } else { warnPlanXml<(m_oldIds.value(ge.attribute("task-id"))); + Task *task = qobject_cast(m_oldIds.value(re.attribute("task-id"))); Q_ASSERT(task); if (!task) { warnPlanXml<requests().resourceRequest(re.attribute("request-id").toInt()); Resource *required = m_project->findResource(re.attribute("required-id")); QList lst; if (required && request->resource() != required) { lst << required; } KUndo2Command *cmd = new ModifyResourceRequestRequiredCmd(request, lst); cmd->redo(); addCommand(cmd); } + // TODO alternatives } void InsertTaskModuleCommand::createCmdTasks(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } createCmdTask(projectElement, m_parent, m_position); } void InsertTaskModuleCommand::createCmdTask(const KoXmlElement &parentElement, Node *parent, Node *position) { KoXmlElement taskElement; forEachElement(taskElement, parentElement) { if (taskElement.tagName() != "task") { continue; } Task *task = m_project->createTask(); QString id = task->id(); task->load(taskElement, m_context); m_oldIds.insert(task->id(), task); task->setId(id); if (!m_substitute.isEmpty()) { substitute(task->name()); } NamedCommand *cmd = new AddTaskCommand(m_project, parent, task, position); cmd->execute(); addCommand(cmd); createCmdTask(taskElement, task); // add children } } void InsertTaskModuleCommand::createCmdRelations(const KoXmlElement &projectElement) { if (projectElement.isNull()) { return; } KoXmlElement relationElement; forEachElement(relationElement, projectElement) { if (relationElement.tagName() != "relation") { continue; } Node *parent = m_oldIds.value(relationElement.attribute("parent-id")); Node *child = m_oldIds.value(relationElement.attribute("child-id")); if (parent && child) { Relation *relation = new Relation(parent, child); relation->setType(relationElement.attribute("type")); relation->setLag(Duration::fromString(relationElement.attribute("lag"))); AddRelationCmd *cmd = new AddRelationCmd(*m_project, relation); cmd->execute(); addCommand(cmd); } } } void InsertTaskModuleCommand::substitute(QString &text) { QMap::const_iterator it = m_substitute.constBegin(); for (; it != m_substitute.constEnd(); ++it) { text.replace("[[" + it.key() + "]]", it.value()); } } diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index 7e8c1b6b..24398dab 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5393 +1,5294 @@ /* This file is part of the KDE project * Copyright (C) 2007 - 2009, 2012 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptnodeitemmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include #include #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "InsertProjectXmlCommand.h" #include "InsertTaskModuleCommand.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- NodeModel::NodeModel() : QObject(), m_project(0), m_manager(0), m_now(QDate::currentDate()), m_prec(1) { } const QMetaEnum NodeModel::columnMap() const { return metaObject()->enumerator(metaObject()->indexOfEnumerator("Properties")); } void NodeModel::setProject(Project *project) { debugPlan<"<"<name(); case Qt::ToolTipRole: { QTextEdit w(node->description(), nullptr); QString description = w.toPlainText(); if (description.length() > 200) { description = description.left(200) + " ..."; description.replace('\n', "
"); } else { description = node->description(); } w.setHtml(i18n("

%1: %2

%3

", node->wbsCode(), node->name(), description)); return w.toHtml(); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if (node->isBaselined()) { return koIcon("view-time-schedule-baselined"); } break; case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return static_cast(node)->completion().isFinished() ? m_project->config().taskFinishedColor() : m_project->config().taskNormalColor(); case Node::Type_Milestone: return static_cast(node)->completion().isFinished() ? m_project->config().milestoneFinishedColor() : m_project->config().milestoneNormalColor(); case Node::Type_Summarytask: return m_project->config().summaryTaskLevelColor(node->level()); default: break; } break; } } return QVariant(); } QVariant NodeModel::leader(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return node->leader(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::allocation(const Node *node, int role) const { if (node->type() == Node::Type_Task) { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->requests().requestNameList().join(","); case Qt::ToolTipRole: { QMap lst; foreach (ResourceRequest *rr, node->requests().resourceRequests(false)) { QStringList sl; foreach(Resource *r, rr->requiredResources()) { sl << r->name(); } lst.insert(rr->resource()->name(), sl); } if (lst.isEmpty()) { return xi18nc("@info:tooltip", "No resources has been allocated"); } QStringList sl; for (QMap::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it) { if (it.value().isEmpty()) { sl << it.key(); } else { sl << xi18nc("@info:tooltip 1=resource name, 2=list of required resources", "%1 (%2)", it.key(), it.value().join(", ")); } } if (sl.count() == 1) { return xi18nc("@info:tooltip 1=resource name", "Allocated resource:%1", sl.first()); } KLocalizedString ks = kxi18nc("@info:tooltip 1=list of resources", "Allocated resources:%1"); // Hack to get around ks escaping '<' and '>' QString s = ks.subs(sl.join("#¤#")).toString(); return s.replace("#¤#", "
"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::description(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: { KRichTextWidget w(node->description(), 0); w.switchToPlainText(); QString s = w.textOrHtml(); s.remove('\r'); return s.replace('\n', ' '); } case Qt::ToolTipRole: { KRichTextWidget w(node->description(), 0); w.switchToPlainText(); if (w.textOrHtml().isEmpty()) { return QVariant(); } return node->description(); } case Qt::EditRole: return node->description(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::type(const Node *node, int role) const { //debugPlan<name()<<", "<typeToString(true); case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return (int)(Qt::AlignLeft|Qt::AlignVCenter); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::constraint(const Node *node, int role) const { if (node->type() == Node::Type_Project) { switch (role) { case Qt::DisplayRole: return i18n("Target times"); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Earliest start and latest finish"); case Role::EnumList: case Qt::EditRole: case Role::EnumListValue: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } else if (node->type() != Node::Type_Summarytask) { switch (role) { case Qt::DisplayRole: case Qt::ToolTipRole: return node->constraintToString(true); case Role::EnumList: return Node::constraintList(true); case Qt::EditRole: return node->constraint(); case Role::EnumListValue: return (int)node->constraint(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintStartTime(const Node *node, int role) const { if (node->type() == Node::Type_Project) { switch (role) { case Qt::DisplayRole: { return QLocale().toString(node->constraintStartTime(), QLocale::ShortFormat); } case Qt::ToolTipRole: { return QLocale().toString(node->constraintStartTime(), QLocale::LongFormat); } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if (node->type() != Node::Type_Summarytask) { switch (role) { case Qt::DisplayRole: { QString s = QLocale().toString(node->constraintStartTime(), QLocale::ShortFormat); switch (node->constraint()) { case Node::StartNotEarlier: case Node::MustStartOn: case Node::FixedInterval: return s; default: break; } return QString("(%1)").arg(s); } case Qt::ToolTipRole: { int c = node->constraint(); if (c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval) { return QLocale().toString(node->constraintStartTime(), QLocale::LongFormat); } break; } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintEndTime(const Node *node, int role) const { if (node->type() == Node::Type_Project) { switch (role) { case Qt::DisplayRole: { return QLocale().toString(node->constraintEndTime(), QLocale::ShortFormat); } case Qt::ToolTipRole: { return QLocale().toString(node->constraintEndTime(), QLocale::LongFormat); } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if (node->type() != Node::Type_Summarytask) { switch (role) { case Qt::DisplayRole: { QString s = QLocale().toString(node->constraintEndTime(), QLocale::ShortFormat); switch (node->constraint()) { case Node::FinishNotLater: case Node::MustFinishOn: case Node::FixedInterval: return s; default: break; } return QString("(%1)").arg(s); } case Qt::ToolTipRole: { int c = node->constraint(); if (c == Node::FinishNotLater || c == Node::MustFinishOn || c == Node::FixedInterval) { return QLocale().toString(node->constraintEndTime(), QLocale::LongFormat); } break; } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::estimateType(const Node *node, int role) const { if (node->estimate() == 0) { return QVariant(); } switch (role) { case Qt::DisplayRole: case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { return node->estimate()->typeToString(true); } return QString(); case Role::EnumList: return Estimate::typeToStringList(true); case Qt::EditRole: if (node->type() == Node::Type_Task) { return node->estimate()->typeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->type(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimateCalendar(const Node *node, int role) const { if (node->estimate() == 0) { return QVariant(); } switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task) { if (node->estimate()->calendar()) { return node->estimate()->calendar()->name(); } return i18n("None"); } return QString(); case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { if (node->estimate()->type() == Estimate::Type_Effort) { return xi18nc("@info:tooltip", "Not applicable, estimate type is Effort"); } if (node->estimate()->calendar()) { return node->estimate()->calendar()->name(); } return QVariant(); } return QString(); case Role::EnumList: { QStringList lst; lst << i18n("None"); const Node *n = const_cast(node)->projectNode(); if (n) { lst += static_cast(n)->calendarNames(); } return lst; } case Qt::EditRole: if (node->type() == Node::Type_Task) { if (node->estimate()->calendar() == 0) { return i18n("None"); } return node->estimate()->calendar()->name(); } return QString(); case Role::EnumListValue: { if (node->estimate()->calendar() == 0) { return 0; } QStringList lst; const Node *n = const_cast(node)->projectNode(); if (n) { lst = static_cast(n)->calendarNames(); } return lst.indexOf(node->estimate()->calendar()->name()) + 1; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimate(const Node *node, int role) const { if (node->estimate() == 0) { return QVariant(); } switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString(node->estimate()->expectedEstimate(), 'f', m_prec) + Duration::unitToString(unit, true); if (node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration) { s = '(' + s + ')'; } return s; } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString(node->estimate()->expectedEstimate(), 'f', m_prec) + Duration::unitToString(unit, true); Estimate::Type t = node->estimate()->type(); if (node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration) { s = xi18nc("@info:tooltip", "Not applicable, constraint is Fixed Interval"); } else if (t == Estimate::Type_Effort) { s = xi18nc("@info:tooltip", "Estimated effort: %1", s); } else { s = xi18nc("@info:tooltip", "Estimated duration: %1", s); } return s; } break; case Qt::EditRole: return node->estimate()->expectedEstimate(); case Role::DurationUnit: return static_cast(node->estimate()->unit()); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticRatio(const Node *node, int role) const { if (node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone) { return QVariant(); } switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration) { QString s = QString::number(node->estimate()->optimisticRatio()); s = '(' + s + ')'; return s; } if (node->estimate()) { return node->estimate()->optimisticRatio(); } break; case Qt::EditRole: if (node->estimate()) { return node->estimate()->optimisticRatio(); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString(node->estimate()->optimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true); Estimate::Type t = node->estimate()->type(); if (node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration) { s = xi18nc("@info:tooltip", "Not applicable, constraint is Fixed Interval"); } else if (t == Estimate::Type_Effort) { s = xi18nc("@info:tooltip", "Optimistic effort: %1", s); } else { s = xi18nc("@info:tooltip", "Optimistic duration: %1", s); } return s; } break; case Role::Minimum: return -99; case Role::Maximum: return 0; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticRatio(const Node *node, int role) const { if (node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone) { return QVariant(); } switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration) { QString s = QString::number(node->estimate()->pessimisticRatio()); s = '(' + s + ')'; return s; } if (node->estimate()) { return node->estimate()->pessimisticRatio(); } break; case Qt::EditRole: if (node->estimate()) { return node->estimate()->pessimisticRatio(); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString(node->estimate()->pessimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true); Estimate::Type t = node->estimate()->type(); if (node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration) { s = xi18nc("@info:tooltip", "Not applicable, constraint is Fixed Interval"); } else if (t == Estimate::Type_Effort) { s = xi18nc("@info:tooltip", "Pessimistic effort: %1", s); } else { s = xi18nc("@info:tooltip", "Pessimistic duration: %1", s); } return s; } break; case Role::Minimum: return 0; case Role::Maximum: return INT_MAX; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::riskType(const Node *node, int role) const { if (node->estimate() == 0) { return QVariant(); } switch (role) { case Qt::DisplayRole: case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { return node->estimate()->risktypeToString(true); } return QString(); case Role::EnumList: return Estimate::risktypeToStringList(true); case Qt::EditRole: if (node->type() == Node::Type_Task) { return node->estimate()->risktypeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->risktype(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::priority(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->priority(); case Qt::ToolTipRole: break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::runningAccount(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task) { Account *a = node->runningAccount(); return a == 0 ? i18n("None") : a->name(); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Account *a = node->runningAccount(); return a ? xi18nc("@info:tooltip", "Account for resource cost: %1", a->name()) : xi18nc("@info:tooltip", "Account for resource cost"); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->runningAccount(); return a == 0 ? 0 : (m_project->accounts().costElements().indexOf(a->name()) + 1); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupAccount(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name(); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name()) : xi18nc("@info:tooltip", "Account for task startup cost"); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->startupAccount(); return a == 0 ? 0 : (m_project->accounts().costElements().indexOf(a->name()) + 1); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupCost(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::ToolTipRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { return m_project->locale()->formatMoney(node->startupCost()); } break; case Qt::EditRole: return node->startupCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownAccount(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { Account *a = node->shutdownAccount(); return a == 0 ? i18n("None") : a->name(); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { Account *a = node->shutdownAccount(); return a ? xi18nc("@info:tooltip", "Account for task shutdown cost: %1", a->name()) : xi18nc("@info:tooltip", "Account for task shutdown cost"); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->shutdownAccount(); return a == 0 ? 0 : (m_project->accounts().costElements().indexOf(a->name()) + 1); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownCost(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::ToolTipRole: if (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone) { return m_project->locale()->formatMoney(node->shutdownCost()); } break; case Qt::EditRole: return node->shutdownCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startTime(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return QLocale().toString(node->startTime(id()), QLocale::ShortFormat); case Qt::ToolTipRole: //debugPlan<name()<<", "<startTime(id()), QLocale::LongFormat)); case Qt::EditRole: return node->startTime(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::endTime(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return QLocale().toString(node->endTime(id()), QLocale::ShortFormat); case Qt::ToolTipRole: //debugPlan<name()<<", "<endTime(id()), QLocale::LongFormat)); case Qt::EditRole: return node->endTime(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::duration(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration(id()).toDouble(unit); return QVariant(QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } else if (node->type() == Node::Type_Project) { Duration::Unit unit = Duration::Unit_d; double v = node->duration(id()).toDouble(unit); return QVariant(QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } break; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration(id()).toDouble(unit); return xi18nc("@info:tooltip", "Scheduled duration: %1", QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } else if (node->type() == Node::Type_Project) { Duration::Unit unit = Duration::Unit_d; double v = node->duration(id()).toDouble(unit); return xi18nc("@info:tooltip", "Scheduled duration: %1", QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } break; case Qt::EditRole: { return node->duration(id()).toDouble(Duration::Unit_h); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceDuration(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance(id(), unit); return QLocale().toString(v, 'f', 2); } break; case Qt::EditRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); return node->variance(id(), unit); } return 0.0; case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance(id(), unit); return xi18nc("@info:tooltip", "PERT duration variance: %1", QLocale().toString(v ,'f', 2)); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceEstimate(const Estimate *est, int role) const { switch (role) { case Qt::DisplayRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance(unit); //debugPlan<name()<<": "<variance(est->unit()); } case Qt::ToolTipRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance(unit); return xi18nc("@info:tooltip", "PERT estimate variance: %1", QLocale().toString(v, 'f', 2) + Duration::unitToString(unit, true)); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticDuration(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: { if (node->type() != Node::Type_Task) { return QVariant(); } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->optimisticRatio())) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble(unit); //debugPlan<name()<<": "<type() != Node::Type_Task) { return 0.0; } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->optimisticRatio())) / 100; Duration::Unit unit = node->estimate()->unit(); return d.toDouble(unit); } case Qt::ToolTipRole: { if (node->type() != Node::Type_Task) { return QVariant(); } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->optimisticRatio())) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble(unit); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString(est->optimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true)); break; } case Qt::EditRole: { if (est == 0) { return 0.0; } return est->optimisticEstimate(); } case Qt::ToolTipRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc("@info:tooltip", "Optimistic estimate: %1", QLocale().toString(est->optimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true)); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pertExpected(const Estimate *est, int role) const { switch (role) { case Qt::DisplayRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale(est->pertExpected(), unit, est->scales()); return QVariant(QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } case Qt::EditRole: { if (est == 0) { return 0.0; } return Estimate::scale(est->pertExpected(), est->unit(), est->scales()); } case Qt::ToolTipRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale(est->pertExpected(), unit, est->scales()); return xi18nc("@info:tooltip", "PERT expected estimate: %1", QLocale().toString(v, 'f', m_prec) + Duration::unitToString(unit, true)); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticDuration(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: { if (node->type() != Node::Type_Task) { return QVariant(); } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->pessimisticRatio())) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble(unit); //debugPlan<name()<<": "<type() != Node::Type_Task) { return 0.0; } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->pessimisticRatio())) / 100; return d.toDouble(node->estimate()->unit()); } case Qt::ToolTipRole: { if (node->type() != Node::Type_Task) { return QVariant(); } Duration d = node->duration(id()); d = (d * (100 + node->estimate()->pessimisticRatio())) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble(unit); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString(est->pessimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true)); break; } case Qt::EditRole: { if (est == 0) { return 0.0; } return est->pessimisticEstimate(); } case Qt::ToolTipRole: { if (est == 0) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc("@info:tooltip", "Pessimistic estimate: %1", QLocale().toString(est->pessimisticEstimate(), 'f', m_prec) + Duration::unitToString(unit, true)); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyStart(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return QLocale().toString(t->earlyStart(id()), QLocale::ShortFormat); case Qt::ToolTipRole: return QLocale().toString(t->earlyStart(id()).date(), QLocale::ShortFormat); case Qt::EditRole: return t->earlyStart(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyFinish(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return QLocale().toString(t->earlyFinish(id()), QLocale::ShortFormat); case Qt::ToolTipRole: return QLocale().toString(t->earlyFinish(id()).date(), QLocale::ShortFormat); case Qt::EditRole: return t->earlyFinish(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateStart(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return QLocale().toString(t->lateStart(id()), QLocale::ShortFormat); case Qt::ToolTipRole: return QLocale().toString(t->lateStart(id()).date(), QLocale::ShortFormat); case Qt::EditRole: return t->lateStart(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateFinish(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return QLocale().toString(t->lateFinish(id()), QLocale::ShortFormat); case Qt::ToolTipRole: return QLocale().toString(t->lateFinish(id()).date(), QLocale::ShortFormat); case Qt::EditRole: return t->lateFinish(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::positiveFloat(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->positiveFloat(id()).toString(Duration::Format_i18nHourFraction); case Qt::ToolTipRole: return t->positiveFloat(id()).toString(Duration::Format_i18nDayTime); case Qt::EditRole: return t->positiveFloat(id()).toDouble(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::freeFloat(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->freeFloat(id()).toString(Duration::Format_i18nHourFraction); case Qt::ToolTipRole: return t->freeFloat(id()).toString(Duration::Format_i18nDayTime); case Qt::EditRole: return t->freeFloat(id()).toDouble(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::negativeFloat(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->negativeFloat(id()).toString(Duration::Format_i18nHourFraction); case Qt::ToolTipRole: return t->negativeFloat(id()).toString(Duration::Format_i18nDayTime); case Qt::EditRole: return t->negativeFloat(id()).toDouble(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startFloat(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->startFloat(id()).toString(Duration::Format_i18nHourFraction); case Qt::ToolTipRole: return t->startFloat(id()).toString(Duration::Format_i18nDayTime); case Qt::EditRole: return t->startFloat(id()).toDouble(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishFloat(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->finishFloat(id()).toString(Duration::Format_i18nHourFraction); case Qt::ToolTipRole: return t->finishFloat(id()).toString(Duration::Format_i18nDayTime); case Qt::EditRole: return t->finishFloat(id()).toDouble(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::assignedResources(const Node *node, int role) const { if (node->type() != Node::Type_Task) { return QVariant(); } switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->assignedNameList(id()).join(","); case Qt::ToolTipRole: { QStringList lst = node->assignedNameList(id()); if (! lst.isEmpty()) { return xi18nc("@info:tooltip 1=list of resources", "Assigned resources:%1", node->assignedNameList(id()).join(", ")); } break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::completed(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: return t->completion().percentFinished(); case Qt::EditRole: return t->completion().percentFinished(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Task is %1% completed", t->completion().percentFinished()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::status(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: { int st = t->state(id()); if (st & Node::State_NotScheduled) { return SchedulingState::notScheduled(); } if (st & Node::State_Finished) { if (st & Node::State_FinishedLate) { return i18n("Finished late"); } if (st & Node::State_FinishedEarly) { return i18n("Finished early"); } return i18n("Finished"); } if (st & Node::State_Running) { if (st & Node::State_Late) { return i18n("Running late"); } return i18n("Running"); } if (st & Node::State_Started) { if (st & Node::State_StartedLate) { return i18n("Started late"); } if (st & Node::State_StartedEarly) { return i18n("Started early"); } if (st & Node::State_Late) { return i18n("Running late"); } return i18n("Started"); } if (st & Node::State_ReadyToStart) { if (st & Node::State_Late) { return i18n("Not started"); } return i18n("Can start"); } if (st & Node::State_NotReadyToStart) { if (st & Node::State_Late) { return i18n("Delayed"); } return i18n("Cannot start"); } return i18n("Not started"); break; } case Qt::ToolTipRole: { int st = t->state(id()); if (st & Node::State_NotScheduled) { return SchedulingState::notScheduled(); } if (st & Node::State_Finished) { if (st & Node::State_FinishedLate) { Duration d = t->completion().finishTime() - t->endTime(id()); return xi18nc("@info:tooltip", "Finished %1 late", d.toString(Duration::Format_i18nDay)); } if (st & Node::State_FinishedEarly) { Duration d = t->endTime(id()) - t->completion().finishTime(); return xi18nc("@info:tooltip", "Finished %1 early", d.toString(Duration::Format_i18nDay)); } return xi18nc("@info:tooltip", "Finished"); } if (st & Node::State_Started) { if (st & Node::State_StartedLate) { Duration d = t->completion().startTime() - t->startTime(id()); return xi18nc("@info:tooltip", "Started %1 late", d.toString(Duration::Format_i18nDay)); } if (st & Node::State_StartedEarly) { Duration d = t->startTime(id()) - t->completion().startTime(); return xi18nc("@info:tooltip", "Started %1 early", d.toString(Duration::Format_i18nDay)); } return xi18nc("@info:tooltip", "Started"); } if (st & Node::State_Running) { return xi18nc("@info:tooltip", "Running"); } if (st & Node::State_ReadyToStart) { return xi18nc("@info:tooltip", "Can start"); } if (st & Node::State_NotReadyToStart) { return xi18nc("@info:tooltip", "Cannot start"); } return xi18nc("@info:tooltip", "Not started"); break; } case Qt::EditRole: return t->state(id()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startedTime(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: if (t->completion().isStarted()) { return QLocale().toString(t->completion().startTime(), QLocale::ShortFormat); } break; case Qt::ToolTipRole: if (t->completion().isStarted()) { return xi18nc("@info:tooltip", "Actual start: %1", QLocale().toString(t->completion().startTime().date(), QLocale::LongFormat)); } break; case Qt::EditRole: if (t->completion().isStarted()) { return t->completion().startTime(); } return QDateTime::currentDateTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isStarted(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isStarted(); case Qt::ToolTipRole: if (t->completion().isStarted()) { return xi18nc("@info:tooltip", "The task started at: %1", QLocale().toString(t->completion().startTime().date(), QLocale::LongFormat)); } return xi18nc("@info:tooltip", "The task is not started"); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishedTime(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: if (t->completion().isFinished()) { return QLocale().toString(t->completion().finishTime(), QLocale::ShortFormat); } break; case Qt::ToolTipRole: if (t->completion().isFinished()) { return xi18nc("@info:tooltip", "Actual finish: %1", QLocale().toString(t->completion().finishTime(), QLocale::LongFormat)); } break; case Qt::EditRole: if (t->completion().isFinished()) { return t->completion().finishTime(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isFinished(const Node *node, int role) const { if (! (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { return QVariant(); } const Task *t = static_cast(node); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isFinished(); case Qt::ToolTipRole: if (t->completion().isFinished()) { return xi18nc("@info:tooltip", "The task finished at: %1", QLocale().toString(t->completion().finishTime().date(), QLocale::LongFormat)); } return xi18nc("@info:tooltip", "The task is not finished"); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedEffortTo(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return node->plannedEffortTo(m_now, id()).format(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Planned effort until %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), node->plannedEffortTo(m_now, id()).toString(Duration::Format_i18nHour)); case Qt::EditRole: return node->plannedEffortTo(m_now, id()).toDouble(Duration::Unit_h); case Role::DurationUnit: return static_cast(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualEffortTo(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return node->actualEffortTo(m_now).format(); case Qt::ToolTipRole: //debugPlan<actualEffortTo(m_now).toString(Duration::Format_i18nHour)); case Qt::EditRole: return node->actualEffortTo(m_now).toDouble(Duration::Unit_h); case Role::DurationUnit: return static_cast(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::remainingEffort(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: { const Task *t = dynamic_cast(node); if (t) { return t->completion().remainingEffort().format(); } break; } case Qt::ToolTipRole: { const Task *t = dynamic_cast(node); if (t) { return xi18nc("@info:tooltip", "Remaining effort: %1", t->completion().remainingEffort().toString(Duration::Format_i18nHour)); } break; } case Qt::EditRole: { const Task *t = dynamic_cast(node); if (t == 0) { return QVariant(); } return t->completion().remainingEffort().toDouble(Duration::Unit_h); } case Role::DurationUnit: return static_cast(Duration::Unit_h); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedCostTo(const Node *node, int role) const { Locale *l = m_project->locale(); switch (role) { case Qt::DisplayRole: return l->formatMoney(node->plannedCostTo(m_now, id())); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Planned cost until %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), l->formatMoney(node->plannedCostTo(m_now, id()))); case Qt::EditRole: return node->plannedCostTo(m_now); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualCostTo(const Node *node, int role) const { Locale *l = m_project->locale(); switch (role) { case Qt::DisplayRole: return l->formatMoney(node->actualCostTo(id(), m_now).cost()); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Actual cost until %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), l->formatMoney(node->actualCostTo(id(), m_now).cost())); case Qt::EditRole: return node->actualCostTo(id(), m_now).cost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::note(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: if (node->type() == Node::Type_Task) { Node *n = const_cast(node); return static_cast(n)->completion().note(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeSchedulingStatus(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return node->schedulingStatus(id(), true).value(0); case Qt::EditRole: return node->schedulingStatus(id(), false).value(0); case Qt::ToolTipRole: return node->schedulingStatus(id(), true).join("\n"); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::resourceIsMissing(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->resourceError(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->resourceError(id()); case Qt::ToolTipRole: if (node->resourceError(id())) { return xi18nc("@info:tooltip", "Resource allocation is expected when the task estimate type is Effort"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsOverbooked(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->resourceOverbooked(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->resourceOverbooked(id()); case Qt::ToolTipRole: if (node->resourceOverbooked(id())) { return xi18nc("@info:tooltip", "A resource has been overbooked"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsNotAvailable(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->resourceNotAvailable(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->resourceNotAvailable(id()); case Qt::ToolTipRole: if (node->resourceNotAvailable(id())) { return xi18nc("@info:tooltip", "No resource is available for this task"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingConstraintsError(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->constraintError(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->constraintError(id()); case Qt::ToolTipRole: if (node->constraintError(id())) { return xi18nc("@info:tooltip", "Failed to comply with a timing constraint"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeIsNotScheduled(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->notScheduled(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->notScheduled(id()); case Qt::ToolTipRole: if (node->notScheduled(id())) { return xi18nc("@info:tooltip", "This task has not been scheduled"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::effortNotMet(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->effortMetError(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->effortMetError(id()); case Qt::ToolTipRole: if (node->effortMetError(id())) { return xi18nc("@info:tooltip", "The assigned resources cannot deliver the required estimated effort"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingError(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: if (node->schedulingError(id())) { return i18n("Error"); } break; case Qt::EditRole: return node->schedulingError(id()); case Qt::ToolTipRole: if (node->schedulingError(id())) { return xi18nc("@info:tooltip", "Scheduling error"); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wbsCode(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->wbsCode(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Work breakdown structure code: %1", node->wbsCode()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case SortableRole: return node->wbsCode(true); } return QVariant(); } QVariant NodeModel::nodeLevel(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->level(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Task level: %1", node->level()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWS(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return m_project->locale()->formatMoney(node->bcws(m_now, id()), QString(), 0); case Qt::EditRole: return node->bcws(m_now, id()); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Budgeted Cost of Work Scheduled at %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), m_project->locale()->formatMoney(node->bcws(m_now, id()), QString(), 0)); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWP(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return m_project->locale()->formatMoney(node->bcwp(id()), QString(), 0); case Qt::EditRole: return node->bcwp(id()); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Budgeted Cost of Work Performed at %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), m_project->locale()->formatMoney(node->bcwp(id()), QString(), 0)); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeACWP(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return m_project->locale()->formatMoney(node->acwp(m_now, id()).cost(), QString(), 0); case Qt::EditRole: return node->acwp(m_now, id()).cost(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Actual Cost of Work Performed at %1: %2", QLocale().toString(m_now, QLocale::ShortFormat), m_project->locale()->formatMoney(node->acwp(m_now, id()).cost())); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodePerformanceIndex(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: return QLocale().toString(node->schedulePerformanceIndex(m_now, id()), 'f', 2); case Qt::EditRole: return node->schedulePerformanceIndex(m_now, id()); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Schedule Performance Index at %1: %2", m_now.toString(), QLocale().toString(node->schedulePerformanceIndex(m_now, id()), 'f', 2)); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::ForegroundRole: return QColor(node->schedulePerformanceIndex(m_now, id()) < 1.0 ? Qt::red : Qt::black); } return QVariant(); } QVariant NodeModel::nodeIsCritical(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->isCritical(id()); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeInCriticalPath(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->inCriticalPath(id()); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if (! m_project) { break; } switch (node->type()) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wpOwnerName(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast(node); if (t == 0) { return QVariant(); } if (t->wpTransmitionStatus() == WorkPackage::TS_None) { return xi18nc("Not available", "NA"); } return t->wpOwnerName(); } case Qt::ToolTipRole: { const Task *task = dynamic_cast(node); if (task == 0) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime(node, Qt::DisplayRole).toString(); if (sts == WorkPackage::TS_Send) { return xi18nc("@info:tooltip", "Latest work package sent to %1 at %2", static_cast(node)->wpOwnerName(), t); } if (sts == WorkPackage::TS_Receive) { return xi18nc("@info:tooltip", "Latest work package received from %1 at %2", static_cast(node)->wpOwnerName(), t); } return xi18nc("@info:tooltip", "Not available"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionStatus(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: { const Task *t = dynamic_cast(node); if (t == 0) { return QVariant(); } if (t->wpTransmitionStatus() == WorkPackage::TS_None) { return xi18nc("Not available", "NA"); } return WorkPackage::transmitionStatusToString(t->wpTransmitionStatus(), true); } case Qt::EditRole: { const Task *t = dynamic_cast(node); if (t == 0) { return QVariant(); } return WorkPackage::transmitionStatusToString(t->wpTransmitionStatus(), false); } case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionTime(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast(node); if (t == 0) { return QVariant(); } if (t->wpTransmitionStatus() == WorkPackage::TS_None) { return xi18nc("Not available", "NA"); } return QLocale().toString(t->wpTransmitionTime(), QLocale::ShortFormat); } case Qt::ToolTipRole: { const Task *task = dynamic_cast(node); if (task == 0) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime(node, Qt::DisplayRole).toString(); if (sts == WorkPackage::TS_Send) { return xi18nc("@info:tooltip", "Latest work package sent: %1", t); } if (sts == WorkPackage::TS_Receive) { return xi18nc("@info:tooltip", "Latest work package received: %1", t); } return xi18nc("@info:tooltip", "Not available"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::data(const Node *n, int property, int role) const { QVariant result; switch (property) { // Edited by user case NodeName: result = name(n, role); break; case NodeType: result = type(n, role); break; case NodeResponsible: result = leader(n, role); break; case NodeAllocation: result = allocation(n, role); break; case NodeEstimateType: result = estimateType(n, role); break; case NodeEstimateCalendar: result = estimateCalendar(n, role); break; case NodeEstimate: result = estimate(n, role); break; case NodeOptimisticRatio: result = optimisticRatio(n, role); break; case NodePessimisticRatio: result = pessimisticRatio(n, role); break; case NodeRisk: result = riskType(n, role); break; case NodePriority: result = priority(n, role); break; case NodeConstraint: result = constraint(n, role); break; case NodeConstraintStart: result = constraintStartTime(n, role); break; case NodeConstraintEnd: result = constraintEndTime(n, role); break; case NodeRunningAccount: result = runningAccount(n, role); break; case NodeStartupAccount: result = startupAccount(n, role); break; case NodeStartupCost: result = startupCost(n, role); break; case NodeShutdownAccount: result = shutdownAccount(n, role); break; case NodeShutdownCost: result = shutdownCost(n, role); break; case NodeDescription: result = description(n, role); break; // Based on edited values case NodeExpected: result = pertExpected(n->estimate(), role); break; case NodeVarianceEstimate: result = varianceEstimate(n->estimate(), role); break; case NodeOptimistic: result = optimisticEstimate(n->estimate(), role); break; case NodePessimistic: result = pessimisticEstimate(n->estimate(), role); break; // After scheduling case NodeStartTime: result = startTime(n, role); break; case NodeEndTime: result = endTime(n, role); break; case NodeEarlyStart: result = earlyStart(n, role); break; case NodeEarlyFinish: result = earlyFinish(n, role); break; case NodeLateStart: result = lateStart(n, role); break; case NodeLateFinish: result = lateFinish(n, role); break; case NodePositiveFloat: result = positiveFloat(n, role); break; case NodeFreeFloat: result = freeFloat(n, role); break; case NodeNegativeFloat: result = negativeFloat(n, role); break; case NodeStartFloat: result = startFloat(n, role); break; case NodeFinishFloat: result = finishFloat(n, role); break; case NodeAssignments: result = assignedResources(n, role); break; // Based on scheduled values case NodeDuration: result = duration(n, role); break; case NodeVarianceDuration: result = varianceDuration(n, role); break; case NodeOptimisticDuration: result = optimisticDuration(n, role); break; case NodePessimisticDuration: result = pessimisticDuration(n, role); break; // Completion case NodeStatus: result = status(n, role); break; case NodeCompleted: result = completed(n, role); break; case NodePlannedEffort: result = plannedEffortTo(n, role); break; case NodeActualEffort: result = actualEffortTo(n, role); break; case NodeRemainingEffort: result = remainingEffort(n, role); break; case NodePlannedCost: result = plannedCostTo(n, role); break; case NodeActualCost: result = actualCostTo(n, role); break; case NodeActualStart: result = startedTime(n, role); break; case NodeStarted: result = isStarted(n, role); break; case NodeActualFinish: result = finishedTime(n, role); break; case NodeFinished: result = isFinished(n, role); break; case NodeStatusNote: result = note(n, role); break; // Scheduling errors case NodeSchedulingStatus: result = nodeSchedulingStatus(n, role); break; case NodeNotScheduled: result = nodeIsNotScheduled(n, role); break; case NodeAssignmentMissing: result = resourceIsMissing(n, role); break; case NodeResourceOverbooked: result = resourceIsOverbooked(n, role); break; case NodeResourceUnavailable: result = resourceIsNotAvailable(n, role); break; case NodeConstraintsError: result = schedulingConstraintsError(n, role); break; case NodeEffortNotMet: result = effortNotMet(n, role); break; case NodeSchedulingError: result = schedulingError(n, role); break; case NodeWBSCode: result = wbsCode(n, role); break; case NodeLevel: result = nodeLevel(n, role); break; // Performance case NodeBCWS: result = nodeBCWS(n, role); break; case NodeBCWP: result = nodeBCWP(n, role); break; case NodeACWP: result = nodeACWP(n, role); break; case NodePerformanceIndex: result = nodePerformanceIndex(n, role); break; case NodeCritical: result = nodeIsCritical(n, role); break; case NodeCriticalPath: result = nodeInCriticalPath(n, role); break; case WPOwnerName: result = wpOwnerName(n, role); break; case WPTransmitionStatus: result = wpTransmitionStatus(n, role); break; case WPTransmitionTime: result = wpTransmitionTime(n, role); break; default: //debugPlan<<"Invalid property number: "<name()) { return 0; } KUndo2MagicString s = kundo2_i18n("Modify name"); switch (node->type()) { case Node::Type_Task: s = kundo2_i18n("Modify task name"); break; case Node::Type_Milestone: s = kundo2_i18n("Modify milestone name"); break; case Node::Type_Summarytask: s = kundo2_i18n("Modify summarytask name"); break; case Node::Type_Project: s = kundo2_i18n("Modify project name"); break; } return new NodeModifyNameCmd(*node, value.toString(), s); } } return 0; } KUndo2Command *NodeModel::setLeader(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { if (value.toString() != node->leader()) { return new NodeModifyLeaderCmd(*node, value.toString(), kundo2_i18n("Modify responsible")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setAllocation(Node */*node*/, const QVariant &/*value*/, int /*role*/) { return 0; } KUndo2Command *NodeModel::setDescription(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toString() == node->description()) { return 0; } return new NodeModifyDescriptionCmd(*node, value.toString(), kundo2_i18n("Modify task description")); } return 0; } KUndo2Command *NodeModel::setType(Node *, const QVariant &, int) { return 0; } KUndo2Command *NodeModel::setConstraint(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Node::ConstraintType v; QStringList lst = node->constraintList(false); if (lst.contains(value.toString())) { v = Node::ConstraintType(lst.indexOf(value.toString())); } else { v = Node::ConstraintType(value.toInt()); } //debugPlan<constraint()) { return new NodeModifyConstraintCmd(*node, v, kundo2_i18n("Modify constraint type")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintStartTime(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime(QTime(dt.time().hour(), dt.time().minute(), 0)); // reset possible secs/msecs if (dt != node->constraintStartTime()) { return new NodeModifyConstraintStartTimeCmd(*node, dt, kundo2_i18n("Modify constraint start time")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintEndTime(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime(QTime(dt.time().hour(), dt.time().minute(), 0)); // reset possible secs/msecs if (dt != node->constraintEndTime()) { return new NodeModifyConstraintEndTimeCmd(*node, dt, kundo2_i18n("Modify constraint end time")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateType(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: { Estimate::Type v; QStringList lst = node->estimate()->typeToStringList(false); if (lst.contains(value.toString())) { v = Estimate::Type(lst.indexOf(value.toString())); } else { v = Estimate::Type(value.toInt()); } if (v != node->estimate()->type()) { return new ModifyEstimateTypeCmd(*node, node->estimate()->type(), v, kundo2_i18n("Modify estimate type")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateCalendar(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: { Calendar *c = 0; Calendar *old = node->estimate()->calendar(); if (value.toInt() > 0) { QStringList lst = estimateCalendar(node, Role::EnumList).toStringList(); if (value.toInt() < lst.count()) { c = m_project->calendarByName(lst.at(value.toInt())); } } if (c != old) { return new ModifyEstimateCalendarCmd(*node, old, c, kundo2_i18n("Modify estimate calendar")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimate(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: { double d; Duration::Unit unit; if (value.toList().count() == 2) { d = value.toList()[0].toDouble(); unit = static_cast(value.toList()[1].toInt()); } else if (value.canConvert()) { bool ok = Duration::valueFromString(value.toString(), d, unit); if (! ok) { return 0; } } else { return 0; } //debugPlan<"<estimate()->expectedEstimate()) { if (cmd == 0) cmd = new MacroCommand(kundo2_i18n("Modify estimate")); cmd->addCommand(new ModifyEstimateCmd(*node, node->estimate()->expectedEstimate(), d)); } if (unit != node->estimate()->unit()) { if (cmd == 0) cmd = new MacroCommand(kundo2_i18n("Modify estimate")); cmd->addCommand(new ModifyEstimateUnitCmd(*node, node->estimate()->unit(), unit)); } if (cmd) { return cmd; } break; } default: break; } return 0; } KUndo2Command *NodeModel::setOptimisticRatio(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: if (value.toInt() != node->estimate()->optimisticRatio()) { return new EstimateModifyOptimisticRatioCmd(*node, node->estimate()->optimisticRatio(), value.toInt(), kundo2_i18n("Modify optimistic estimate")); } break; default: break; } return 0; } KUndo2Command *NodeModel::setPessimisticRatio(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: if (value.toInt() != node->estimate()->pessimisticRatio()) { return new EstimateModifyPessimisticRatioCmd(*node, node->estimate()->pessimisticRatio(), value.toInt(), kundo2_i18n("Modify pessimistic estimate")); } default: break; } return 0; } KUndo2Command *NodeModel::setRiskType(Node *node, const QVariant &value, int role) { if (node->estimate() == 0) { return 0; } switch (role) { case Qt::EditRole: { int val = 0; QStringList lst = node->estimate()->risktypeToStringList(false); if (lst.contains(value.toString())) { val = lst.indexOf(value.toString()); } else { val = value.toInt(); } if (val != node->estimate()->risktype()) { Estimate::Risktype v = Estimate::Risktype(val); return new EstimateModifyRiskCmd(*node, node->estimate()->risktype(), v, kundo2_i18n("Modify risk type")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setPriority(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { if (value.toInt() != node->priority()) { return new NodeModifyPriorityCmd(*node, node->priority(), value.toInt(), kundo2_i18n("Modify priority")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setRunningAccount(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = runningAccount(node, Role::EnumList).toStringList(); if (value.toInt() < lst.count()) { Account *a = m_project->accounts().findAccount(lst.at(value.toInt())); Account *old = node->runningAccount(); if (old != a) { return new NodeModifyRunningAccountCmd(*node, old, a, kundo2_i18n("Modify running account")); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setStartupAccount(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = startupAccount(node, Role::EnumList).toStringList(); if (value.toInt() < lst.count()) { Account *a = m_project->accounts().findAccount(lst.at(value.toInt())); Account *old = node->startupAccount(); //debugPlan<<(value.toInt())<<";"<<(lst.at(value.toInt()))<<":"<startupCost()) { return new NodeModifyStartupCostCmd(*node, v, kundo2_i18n("Modify startup cost")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownAccount(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = shutdownAccount(node, Role::EnumList).toStringList(); if (value.toInt() < lst.count()) { Account *a = m_project->accounts().findAccount(lst.at(value.toInt())); Account *old = node->shutdownAccount(); if (old != a) { return new NodeModifyShutdownAccountCmd(*node, old, a, kundo2_i18n("Modify shutdown account")); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownCost(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { double v = value.toDouble(); if (v != node->shutdownCost()) { return new NodeModifyShutdownCostCmd(*node, v, kundo2_i18n("Modify shutdown cost")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setCompletion(Node */*node*/, const QVariant &/*value*/, int /*role*/) { return 0; } KUndo2Command *NodeModel::setRemainingEffort(Node *node, const QVariant &value, int role) { if (role == Qt::EditRole && node->type() == Node::Type_Task) { Task *t = static_cast(node); double d(value.toList()[0].toDouble()); Duration::Unit unit = static_cast(value.toList()[1].toInt()); Duration dur(d, unit); return new ModifyCompletionRemainingEffortCmd(t->completion(), QDate::currentDate(), dur, kundo2_i18n("Modify remaining effort")); } return 0; } KUndo2Command *NodeModel::setActualEffort(Node *node, const QVariant &value, int role) { if (role == Qt::EditRole && node->type() == Node::Type_Task) { Task *t = static_cast(node); double d(value.toList()[0].toDouble()); Duration::Unit unit = static_cast(value.toList()[1].toInt()); Duration dur(d, unit); return new ModifyCompletionActualEffortCmd(t->completion(), QDate::currentDate(), dur, kundo2_i18n("Modify actual effort")); } return 0; } KUndo2Command *NodeModel::setStartedTime(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Task *t = qobject_cast(node); if (t == 0) { return 0; } MacroCommand *m = new MacroCommand(kundo2_i18n("Modify actual start time")); if (! t->completion().isStarted()) { m->addCommand(new ModifyCompletionStartedCmd(t->completion(), true)); } m->addCommand(new ModifyCompletionStartTimeCmd(t->completion(), value.toDateTime())); if (t->type() == Node::Type_Milestone) { m->addCommand(new ModifyCompletionFinishedCmd(t->completion(), true)); m->addCommand(new ModifyCompletionFinishTimeCmd(t->completion(), value.toDateTime())); if (t->completion().percentFinished() < 100) { Completion::Entry *e = new Completion::Entry(100, Duration::zeroDuration, Duration::zeroDuration); m->addCommand(new AddCompletionEntryCmd(t->completion(), value.toDate(), e)); } } return m; } default: break; } return 0; } KUndo2Command *NodeModel::setFinishedTime(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Task *t = qobject_cast(node); if (t == 0) { return 0; } MacroCommand *m = new MacroCommand(kundo2_i18n("Modify actual finish time")); if (! t->completion().isFinished()) { m->addCommand(new ModifyCompletionFinishedCmd(t->completion(), true)); if (t->completion().percentFinished() < 100) { Completion::Entry *e = new Completion::Entry(100, Duration::zeroDuration, Duration::zeroDuration); m->addCommand(new AddCompletionEntryCmd(t->completion(), value.toDate(), e)); } } m->addCommand(new ModifyCompletionFinishTimeCmd(t->completion(), value.toDateTime())); if (t->type() == Node::Type_Milestone) { m->addCommand(new ModifyCompletionStartedCmd(t->completion(), true)); m->addCommand(new ModifyCompletionStartTimeCmd(t->completion(), value.toDateTime())); } return m; } default: break; } return 0; } //---------------------------- NodeItemModel::NodeItemModel(QObject *parent) : ItemModelBase(parent), m_node(0), m_projectshown(false) { setReadOnly(NodeModel::NodeDescription, true); } NodeItemModel::~NodeItemModel() { } void NodeItemModel::setShowProject(bool on) { beginResetModel(); m_projectshown = on; endResetModel(); emit projectShownChanged(on); } void NodeItemModel::slotNodeToBeInserted(Node *parent, int row) { //debugPlan<name()<<"; "<parentNode()->name()<<"-->"<name(); Q_ASSERT(node->parentNode() == m_node); endInsertRows(); m_node = 0; emit nodeInserted(node); } void NodeItemModel::slotNodeToBeRemoved(Node *node) { //debugPlan<name(); Q_ASSERT(m_node == 0); m_node = node; int row = index(node).row(); beginRemoveRows(index(node->parentNode()), row, row); } void NodeItemModel::slotNodeRemoved(Node *node) { //debugPlan<name(); Q_ASSERT(node == m_node); #ifdef NDEBUG Q_UNUSED(node) #endif endRemoveRows(); m_node = 0; } void NodeItemModel::slotNodeToBeMoved(Node *node, int pos, Node *newParent, int newPos) { //debugPlan<parentNode()->name()<name()<parentNode()), pos, pos, index(newParent), newPos); } void NodeItemModel::slotNodeMoved(Node *node) { Q_UNUSED(node); //debugPlan<parentNode()->name()<parentNode()->indexOf(node); endMoveRows(); } void NodeItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void NodeItemModel::slotProjectCalculated(ScheduleManager *sm) { debugPlan<allNodes()) { int row = n->parentNode()->indexOf(n); QModelIndex idx = createIndex(row, NodeModel::NodeWBSCode, n); emit dataChanged(idx, idx); } } void NodeItemModel::setProject(Project *project) { beginResetModel(); if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &NodeItemModel::projectDeleted); disconnect(m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect(m_project, &Project::wbsDefinitionChanged, this, &NodeItemModel::slotWbsDefinitionChanged); disconnect(m_project, &Project::nodeChanged, this, &NodeItemModel::slotNodeChanged); disconnect(m_project, &Project::nodeToBeAdded, this, &NodeItemModel::slotNodeToBeInserted); disconnect(m_project, &Project::nodeToBeRemoved, this, &NodeItemModel::slotNodeToBeRemoved); disconnect(m_project, &Project::nodeToBeMoved, this, &NodeItemModel::slotNodeToBeMoved); disconnect(m_project, &Project::nodeMoved, this, &NodeItemModel::slotNodeMoved); disconnect(m_project, &Project::nodeAdded, this, &NodeItemModel::slotNodeInserted); disconnect(m_project, &Project::nodeRemoved, this, &NodeItemModel::slotNodeRemoved); disconnect(m_project, &Project::projectCalculated, this, &NodeItemModel::slotProjectCalculated); } m_project = project; debugPlan<"<isBaselined(); flags |= Qt::ItemIsDropEnabled; switch (index.column()) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeAllocation: // allocation if (n->type() == Node::Type_Task) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeEstimateType: // estimateType { if (! baselined && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimate: // estimate { if (! baselined && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeOptimisticRatio: // optimisticRatio case NodeModel::NodePessimisticRatio: // pessimisticRatio { if (! baselined && n->type() == Node::Type_Task) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimateCalendar: { if (! baselined && n->type() == Node::Type_Task) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeRisk: // risktype { if (! baselined && n->type() == Node::Type_Task) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodePriority: { if (!baselined) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraint: // constraint type if (! baselined && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if (! baselined && n->type() == Node::Type_Project) { flags |= Qt::ItemIsEditable; break; } if (! baselined && ! (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeConstraintEnd: { // constraint end if (! baselined && n->type() == Node::Type_Project) { flags |= Qt::ItemIsEditable; break; } if (! baselined && ! (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeRunningAccount: // running account if (! baselined && n->type() == Node::Type_Task) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if (! baselined && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description flags |= Qt::ItemIsEditable; break; default: break; } Task *t = static_cast(n); if (manager() && t->isScheduled(id())) { if (! t->completion().isStarted()) { switch (index.column()) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualFinish: if (t->type() == Node::Type_Milestone) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeCompleted: if (t->state() & Node::State_ReadyToStart) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if (! t->completion().isFinished()) { switch (index.column()) { case NodeModel::NodeActualFinish: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeCompleted: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if (t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource) { flags |= Qt::ItemIsEditable; } break; default: break; } } } } return flags; } QModelIndex NodeItemModel::parent(const QModelIndex &index) const { if (! index.isValid()) { return QModelIndex(); } Node *n = node(index); if (n == 0 || n == m_project) { return QModelIndex(); } Node *p = n->parentNode(); if (p == m_project) { return m_projectshown ? createIndex(0, 0, p) : QModelIndex(); } int row = p->parentNode()->indexOf(p); if (row == -1) { return QModelIndex(); } return createIndex(row, 0, p); } QModelIndex NodeItemModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { Q_ASSERT(parent.model() == this); } //debugPlan<= columnCount() || row < 0) { //debugPlan<= p->numChildren()) { errorPlan<name()<<" row too high"<numChildren(); return QModelIndex(); } // now get the internal pointer for the index Node *n = p->childNode(row); QModelIndex idx = createIndex(row, column, n); //debugPlan<parentNode(); if (par) { //debugPlan<"<indexOf(node), column, const_cast(node)); } if (m_projectshown && node == m_project) { return createIndex(0, column, m_project); } //debugPlan<(node); if (task == 0) { return false; } switch (role) { case Qt::EditRole: { MacroCommand *cmd = new MacroCommand(); bool removedAllocations = false; bool addedAllocations = false; KUndo2Command *cc = nullptr; QStringList res = m_project->resourceNameList(); QStringList req = node->requestNameList(); QStringList alloc; foreach (const QString &s, value.toString().split(QRegExp(" *, *"), QString::SkipEmptyParts)) { alloc << s.trimmed(); } - // first add all new resources (to "default" group) - ResourceGroup *pargr = m_project->groupByName(i18n("Resources")); - foreach (const QString &s, alloc) { - Resource *r = m_project->resourceByName(s.trimmed()); - if (r != 0) { - continue; - } - if (pargr == nullptr) { - pargr = new ResourceGroup(); - pargr->setName(i18n("Resources")); - cc = new AddResourceGroupCmd(m_project, pargr); - cc->redo(); - cmd->addCommand(cc); - //debugPlan<<"add group:"<name(); - } - r = new Resource(); - r->setName(s.trimmed()); - cc = new AddResourceCmd(m_project, r); - cc->redo(); - cmd->addCommand(cc); - cc = new AddParentGroupCmd(r, pargr); - cc->redo(); - cmd->addCommand(cc); - //debugPlan<<"add resource:"<name(); - } // Handle deleted requests - foreach (const QString &s, req) { + for (const QString &s : req) { // if a request is not in alloc, it must have been be removed by the user - if (alloc.indexOf(s) == -1) { + if (!alloc.contains(s)) { // remove removed resource request ResourceRequest *r = node->resourceRequest(s); if (r) { //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); - cc = new RemoveResourceRequestCmd(r->parent(), r); + cc = new RemoveResourceRequestCmd(r); cc->redo(); cmd->addCommand(cc); removedAllocations = true; } } } // Handle new requests - QHash groupmap; for (const QString &s : alloc) { // if an allocation is not in req, it must be added - if (req.indexOf(s) == -1) { - ResourceGroup *pargr = nullptr; + if (!req.contains(s)) { Resource *r = m_project->resourceByName(s); if (r == nullptr) { - // Handle request to non existing resource - pargr = m_project->groupByName(i18n("Resources")); - if (pargr == nullptr) { - pargr = new ResourceGroup(); - pargr->setName(i18n("Resources")); - cc = new AddResourceGroupCmd(m_project, pargr); - cc->redo(); - cmd->addCommand(cc); - //debugPlan<<"add group:"<name(); - } + // Non-existent resource, add it r = new Resource(); r->setName(s); - cc = new AddResourceCmd(pargr, r); + cc = new AddResourceCmd(m_project, r); //debugPlan<<"add resource:"<name(); cc->redo(); cmd->addCommand(cc); addedAllocations = true; - } else { - pargr = r->parentGroups().value(0); - if (pargr == nullptr) { - // For now we use/add default group - pargr = m_project->groupByName(i18n("Resources")); - if (pargr == nullptr) { - pargr = new ResourceGroup(); - pargr->setName(i18n("Resources")); - cc = new AddResourceGroupCmd(m_project, pargr); - cc->redo(); - cmd->addCommand(cc); - } - cc = new AddParentGroupCmd(r, pargr); - cc->redo(); - cmd->addCommand(cc); - } - //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest(pargr); - if (g == nullptr) { - g = groupmap.value(pargr); } - if (g == nullptr) { - // create a group request - g = new ResourceGroupRequest(pargr); - cc = new AddResourceGroupRequestCmd(*task, g); - cc->redo(); - cmd->addCommand(cc); - groupmap.insert(pargr, g); - //debugPlan<<"add group request:"<units())); + cc = new AddResourceRequestCmd(&node->requests(), new ResourceRequest(r, r->units())); cc->redo(); cmd->addCommand(cc); - //debugPlan<<"add request:"<name()<<" group:"<isEmpty()) { KUndo2MagicString s = kundo2_i18n("Add resource allocation"); if (!addedAllocations && removedAllocations) { s = kundo2_i18n("Removed resource allocation"); } MacroCommand *m = new MacroCommand(s); emit executeCommand(m); m->addCommand(cmd); return true; } delete cmd; } } return false; } bool NodeItemModel::setCompletion(Node *node, const QVariant &value, int role) { debugPlan<name()<type() == Node::Type_Task) { Completion &c = static_cast(node)->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand(kundo2_i18n("Modify completion")); if (! c.isStarted()) { m->addCommand(new ModifyCompletionStartTimeCmd(c, dt)); m->addCommand(new ModifyCompletionStartedCmd(c, true)); } m->addCommand(new ModifyCompletionPercentFinishedCmd(c, date, value.toInt())); if (value.toInt() == 100) { m->addCommand(new ModifyCompletionFinishTimeCmd(c, dt)); m->addCommand(new ModifyCompletionFinishedCmd(c, true)); } emit executeCommand(m); // also adds a new entry if necessary if (c.entrymode() != Completion::EnterEffortPerResource) { Duration planned = static_cast(node)->plannedEffort(m_nodemodel.id()); Duration actual = (planned * value.toInt()) / 100; debugPlan<execute(); m->addCommand(cmd); cmd = new ModifyCompletionRemainingEffortCmd(c, date, planned - actual); cmd->execute(); m->addCommand(cmd); } return true; } if (node->type() == Node::Type_Milestone) { Completion &c = static_cast(node)->completion(); if (value.toInt() > 0) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand(kundo2_i18n("Set finished")); m->addCommand(new ModifyCompletionStartTimeCmd(c, dt)); m->addCommand(new ModifyCompletionStartedCmd(c, true)); m->addCommand(new ModifyCompletionFinishTimeCmd(c, dt)); m->addCommand(new ModifyCompletionFinishedCmd(c, true)); m->addCommand(new ModifyCompletionPercentFinishedCmd(c, date, 100)); emit executeCommand(m); // also adds a new entry if necessary return true; } return false; } return false; } QVariant NodeItemModel::data(const QModelIndex &index, int role) const { if (role == Qt::TextAlignmentRole) { return headerData(index.column(), Qt::Horizontal, role); } Node *n = node(index); if (role == Role::Object) { return n ? QVariant::fromValue(static_cast(n)) : QVariant(); } QVariant result; if (n != 0) { result = m_nodemodel.data(n, index.column(), role); //debugPlan<name()<<": "<numChildren(); } return rows; } Qt::DropActions NodeItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList NodeItemModel::mimeTypes() const { return ItemModelBase::mimeTypes() << "application/x-vnd.kde.plan.nodeitemmodel.internal" << "application/x-vnd.kde.plan.resourceitemmodel.internal" << "application/x-vnd.kde.plan.project" << "application/x-vnd.kde.plan.taskmodule" << "text/uri-list"; } QMimeData *NodeItemModel::mimeData(const QModelIndexList & indexes) const { QMimeData *m = ItemModelBase::mimeData(indexes); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if (index.isValid() && !rows.contains(index.row())) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); QList nodes; foreach (const QModelIndex &index, indexes) { if (index.isValid()) { //debugPlan<setData("application/x-vnd.kde.plan.project", context.document.toByteArray()); } return m; } bool NodeItemModel::dropAllowed(const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data) { debugPlan<hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) { switch (dropIndicatorPosition) { case ItemModelBase::OnItem: if (index.column() == NodeModel::NodeAllocation) { debugPlan<<"resource:"<type() == Node::Type_Task); return dn->type() == Node::Type_Task; } else if (index.column() == NodeModel::NodeResponsible) { debugPlan<<"resource:"<hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal") || data->hasFormat("application/x-vnd.kde.plan.project") || data->hasFormat("application/x-vnd.kde.plan.taskmodule") || data->hasUrls()) { switch (dropIndicatorPosition) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling, if not project if (dn == m_project) { return dropAllowed(dn, data); } return dropAllowed(dn->parentNode(), data); case ItemModelBase::OnItem: // dn == new parent return dropAllowed(dn, data); default: break; } } return false; } QList NodeItemModel::resourceList(QDataStream &stream) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; debugPlan<<"id"<findResource(id); if (r) { lst << r; } } debugPlan<isBaselined() && on->type() != Node::Type_Summarytask && on->type() != Node::Type_Project) { return false; } if (data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal")) { QByteArray encodedData = data->data("application/x-vnd.kde.plan.nodeitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList(stream); foreach (Node *n, lst) { if (n->type() == Node::Type_Project || on == n || on->isChildOf(n)) { return false; } } lst = removeChildNodes(lst); foreach (Node *n, lst) { if (! m_project->canMoveTask(n, on)) { return false; } } } return true; } QList NodeItemModel::nodeList(QDataStream &stream) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode(id); if (node) { lst << node; } } return lst; } QList NodeItemModel::removeChildNodes(const QList &nodes) { QList lst; foreach (Node *node, nodes) { bool ins = true; foreach (Node *n, lst) { if (node->isChildOf(n)) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if (ins) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach (Node *node, nl) { foreach (Node *n, nlst) { if (n->isChildOf(node)) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf(n); lst.removeAt(i); } } } return lst; } bool NodeItemModel::dropResourceMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent) { QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *n = node(parent); debugPlan<name(); if (parent.column() == NodeModel::NodeResponsible) { QString s; foreach (Resource *r, resourceList(stream)) { s += r->name(); } if (! s.isEmpty()) { if (action == Qt::CopyAction && ! n->leader().isEmpty()) { s += ',' + n->leader(); } KUndo2Command *cmd = m_nodemodel.setLeader(n, s, Qt::EditRole); if (cmd) { emit executeCommand(cmd); } debugPlan<type() == Node::Type_Task) { QList lst = resourceList(stream); if (action == Qt::CopyAction) { lst += static_cast(n)->requestedResources(); } KUndo2Command *cmd = createAllocationCommand(static_cast(*n), lst); if (cmd) { emit executeCommand(cmd); } return true; } return true; } bool NodeItemModel::dropProjectMimeData(const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { Node *n = node(parent); if (n == 0) { n = m_project; } debugPlan<data("application/x-vnd.kde.plan.project"), n, n->childNode(row), kundo2_i18n("Insert tasks")); emit executeCommand(cmd); return true; } bool NodeItemModel::dropTaskModuleMimeData(const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { Node *n = node(parent); if (n == 0) { n = m_project; } debugPlan<data("application/x-vnd.kde.plan.taskmodule")); QStringList substitute; while (it.hasNext()) { QRegularExpressionMatch match = it.next(); QString param = match.captured().remove("[[").remove("]]"); if (!substitute.contains(param)) { substitute << param; } } QMap params; if (!substitute.isEmpty()) { ParameterSubstitutionDialog dlg(substitute); dlg.setCaption(xi18nc("@title:window", "Task Module Parameters")); if (!dlg.exec()) { return false; } params = dlg.parameters(); } KUndo2Command *cmd = new InsertTaskModuleCommand(project(), data->data("application/x-vnd.kde.plan.taskmodule"), n, n->childNode(row), params, kundo2_i18n("Insert task module")); emit executeCommand(cmd); return true; } bool NodeItemModel::dropUrlMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (data->hasUrls()) { QList urls = data->urls(); debugPlan<bad()) { // d->lastErrorMessage = i18n("Not a valid Calligra file: %1", file); debugPlan<<"bad store"<open("root")) { // maindoc.xml debugPlan<<"No root"<device()); KoXmlElement element = doc.documentElement().namedItem("project").toElement(); Project project; XMLLoaderObject status; status.setVersion(doc.documentElement().attribute("version", PLAN_FILE_SYNTAX_VERSION)); status.setProject(&project); if (! project.load(element, status)) { debugPlan<<"Failed to load project from:"<childNode(row - 1), kundo2_i18n("Insert %1", url.fileName())); emit executeCommand(cmd); return true; } KUndo2Command *NodeItemModel::createAllocationCommand(Task &task, const QList &lst) { MacroCommand *cmd = new MacroCommand(kundo2_i18n("Modify resource allocations")); - QHash groups; - foreach (Resource *r, lst) { - if (! groups.contains(r->parentGroups().value(0)) && task.resourceGroupRequest(r->parentGroups().value(0)) == 0) { - ResourceGroupRequest *gr = new ResourceGroupRequest(r->parentGroups().value(0)); - groups[ r->parentGroups().value(0) ] = gr; - cmd->addCommand(new AddResourceGroupRequestCmd(task, gr)); - } - } QList resources = task.requestedResources(); - foreach (Resource *r, lst) { + for (Resource *r : lst) { if (resources.contains(r)) { continue; } - ResourceGroupRequest *gr = groups.value(r->parentGroups().value(0)); - if (gr == 0) { - gr = task.resourceGroupRequest(r->parentGroups().value(0)); - } - if (gr == 0) { - errorPlan<<"No group request found, cannot add resource request:"<name(); - continue; - } - cmd->addCommand(new AddResourceRequestCmd(gr, new ResourceRequest(r, 100))); + cmd->addCommand(new AddResourceRequestCmd(&task.requests(), new ResourceRequest(r, 100))); } - foreach (Resource *r, resources) { - if (! lst.contains(r)) { - ResourceGroupRequest *gr = task.resourceGroupRequest(r->parentGroups().value(0)); + for (Resource *r : resources) { + if (!lst.contains(r)) { ResourceRequest *rr = task.requests().find(r); - if (gr && rr) { - cmd->addCommand(new RemoveResourceRequestCmd(gr, rr)); + if (rr) { + cmd->addCommand(new RemoveResourceRequestCmd(rr)); } } } if (cmd->isEmpty()) { delete cmd; return 0; } return cmd; } bool NodeItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { debugPlan<formats(); if (action == Qt::IgnoreAction) { return true; } if (data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) { return dropResourceMimeData(data, action, row, column, parent); } if (data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal")) { if (action == Qt::MoveAction) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data("application/x-vnd.kde.plan.nodeitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if (parent.isValid()) { par = node(parent); } else { par = m_project; } QList lst = nodeList(stream); QList nodes = removeChildNodes(lst); // children goes with their parent foreach (Node *n, nodes) { if (! m_project->canMoveTask(n, par)) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach (Node *n, nodes) { if (cmd == 0) cmd = new MacroCommand(kundo2_i18n("Move tasks")); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; if (pos >= 0 && n->parentNode() == par && par->indexOf(n) < pos) { --pos; } if (n->parentNode() == par) { // avoid drop into the same position, QAbstractItemModel does not like it int crow = par->indexOf(n); if (((pos == -1) && (crow == par->numChildren() - 1)) || (pos == crow)) { delete cmd; cmd = 0; continue; } } cmd->addCommand(new NodeMoveCmd(m_project, n, par, pos)); offset++; } if (cmd) { emit executeCommand(cmd); } //debugPlan<name(); return true; } } if (data->hasFormat("application/x-vnd.kde.plan.project")) { debugPlan; return dropProjectMimeData(data, action, row, column, parent); } if (data->hasFormat("application/x-vnd.kde.plan.taskmodule")) { debugPlan; return dropTaskModuleMimeData(data, action, row, column, parent); } if (data->hasUrls()) { return dropUrlMimeData(data, action, row, column, parent); } return false; } Node *NodeItemModel::node(const QModelIndex &index) const { Node *n = m_project; if (index.isValid()) { //debugPlan<(index.internalPointer()); Q_ASSERT(n); } return n; } void NodeItemModel::slotNodeChanged(Node *node, int property) { if (node == 0 || (! m_projectshown && node->type() == Node::Type_Project)) { return; } if (node->type() == Node::Type_Project) { emit dataChanged(createIndex(0, 0, node), createIndex(0, columnCount()-1, node)); return; } int row = node->parentNode()->findChildNode(node); Q_ASSERT(row >= 0); emit dataChanged(createIndex(row, 0, node), createIndex(row, columnCount()-1, node)); } QModelIndex NodeItemModel::insertTask(Node *node, Node *after) { - MacroCommand *cmd = new MacroCommand(kundo2_i18n("Add task")); - cmd->addCommand(new TaskAddCmd(m_project, node, after)); - if (m_project && node->type() == Node::Type_Task) { - QHash groups; - foreach (Resource *r, m_project->autoAllocateResources()) { - if (! groups.contains(r->parentGroups().value(0))) { - ResourceGroupRequest *gr = new ResourceGroupRequest(r->parentGroups().value(0)); - cmd->addCommand(new AddResourceGroupRequestCmd(static_cast(*node), gr)); - groups[ r->parentGroups().value(0) ] = gr; - } - ResourceRequest *rr = new ResourceRequest(r, 100); - cmd->addCommand(new AddResourceRequestCmd(groups[ r->parentGroups().value(0) ], rr)); - } - } - emit executeCommand(cmd); + emit executeCommand(new TaskAddCmd(m_project, node, after, kundo2_i18n("Add task"))); int row = -1; if (node->parentNode()) { row = node->parentNode()->indexOf(node); } if (row != -1) { //debugPlan<<"Inserted: "<name()<<"; "<name(); return QModelIndex(); } QModelIndex NodeItemModel::insertSubtask(Node *node, Node *parent) { emit executeCommand(new SubtaskAddCmd(m_project, node, parent, kundo2_i18n("Add sub-task"))); int row = -1; if (node->parentNode()) { row = node->parentNode()->indexOf(node); } if (row != -1) { //debugPlan<parentNode()<<" inserted: "<name()<<"; "<name(); return QModelIndex(); } int NodeItemModel::sortRole(int column) const { int v = Qt::DisplayRole; switch (column) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } debugPlan< lst = parentmap.values(); while (! lst.isEmpty()) delete (int*)(lst.takeFirst()); } int GanttItemModel::rowCount(const QModelIndex &parent) const { if (m_showSpecial) { if (parentmap.values().contains(parent.internalPointer())) { // clazy:exclude=container-anti-pattern return 0; } Node *n = node(parent); if (n && n->type() == Node::Type_Task) { return 5; // the task + early start + late finish ++ } } return NodeItemModel::rowCount(parent); } QModelIndex GanttItemModel::index(int row, int column, const QModelIndex &parent) const { if (m_showSpecial && parent.isValid()) { Node *p = node(parent); if (p->type() == Node::Type_Task) { void *v = 0; foreach (void *i, parentmap.values(p)) { // clazy:exclude=container-anti-pattern if (*((int*)(i)) == row) { v = i; break; } } if (v == 0) { v = new int(row); const_cast(this)->parentmap.insertMulti(p, v); } return createIndex(row, column, v); } } return NodeItemModel::index(row, column, parent); } QModelIndex GanttItemModel::parent(const QModelIndex &idx) const { if (m_showSpecial) { QList lst = parentmap.keys(idx.internalPointer()); if (! lst.isEmpty()) { Q_ASSERT(lst.count() == 1); return index(lst.first()); } } return NodeItemModel::parent(idx); } QVariant GanttItemModel::data(const QModelIndex &index, int role) const { if (! index.isValid()) { return QVariant(); } if (role == Qt::TextAlignmentRole) { return headerData(index.column(), Qt::Horizontal, role); } QModelIndex idx = index; QList lst; if (m_showSpecial) { lst = parentmap.keys(idx.internalPointer()); } if (! lst.isEmpty()) { Q_ASSERT(lst.count() == 1); int row = *((int*)(idx.internalPointer())); Node *n = lst.first(); if (role == SpecialItemTypeRole) { return row; // 0=task, 1=early start, 2=late finish... } switch (row) { case 0: // the task if (idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole) { switch (n->type()) { case Node::Type_Task: return KGantt::TypeTask; default: break; } } break; case 1: { // early start if (role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole) { return QVariant(); } switch (idx.column()) { case NodeModel::NodeName: return "Early Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyStart(id()); default: break; } return QVariant(); } case 2: { // late finish if (role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole) { return QVariant(); } switch (idx.column()) { case NodeModel::NodeName: return "Late Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateFinish(id()); default: break; } return QVariant(); } case 3: { // late start if (role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole) { return QVariant(); } switch (idx.column()) { case NodeModel::NodeName: return "Late Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateStart(id()); default: break; } return QVariant(); } case 4: { // early finish if (role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole) { return QVariant(); } switch (idx.column()) { case NodeModel::NodeName: return "Early Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyFinish(id()); default: break; } return QVariant(); } default: return QVariant(); } idx = createIndex(idx.row(), idx.column(), n); } else { if (role == SpecialItemTypeRole) { return 0; // task of some type } if (idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole) { QModelIndex notScheduled = idx.sibling(idx.row(), NodeModel::NodeNotScheduled); if (notScheduled.data(Qt::EditRole).toBool()) { return QVariant(); } QVariant result = NodeItemModel::data(idx, Qt::EditRole); switch (result.toInt()) { case Node::Type_Project: return KGantt::TypeSummary; case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return m_showSpecial ? KGantt::TypeMulti : KGantt::TypeTask; } } } return NodeItemModel::data(idx, role); } //---------------------------- MilestoneItemModel::MilestoneItemModel(QObject *parent) : ItemModelBase(parent) { } MilestoneItemModel::~MilestoneItemModel() { } QList MilestoneItemModel::mileStones() const { QList lst; foreach(Node* n, m_nodemap) { if (n->type() == Node::Type_Milestone) { lst << n; } } return lst; } void MilestoneItemModel::slotNodeToBeInserted(Node *parent, int row) { Q_UNUSED(parent); Q_UNUSED(row); } void MilestoneItemModel::slotNodeInserted(Node *node) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::slotNodeToBeRemoved(Node *node) { Q_UNUSED(node); //debugPlan<name(); /* int row = m_nodemap.values().indexOf(node); if (row != -1) { Q_ASSERT(m_nodemap.contains(node->wbsCode())); Q_ASSERT(m_nodemap.keys().indexOf(node->wbsCode()) == row); beginRemoveRows(QModelIndex(), row, row); m_nodemap.remove(node->wbsCode()); endRemoveRows(); }*/ } void MilestoneItemModel::slotNodeRemoved(Node *node) { Q_UNUSED(node); resetModel(); //endRemoveRows(); } void MilestoneItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void MilestoneItemModel::slotNodeToBeMoved(Node *node, int pos, Node *newParent, int newPos) { Q_UNUSED(node); Q_UNUSED(pos); Q_UNUSED(newParent); Q_UNUSED(newPos); } void MilestoneItemModel::slotNodeMoved(Node *node) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::setProject(Project *project) { if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &MilestoneItemModel::projectDeleted); disconnect(m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect(m_project, &Project::wbsDefinitionChanged, this, &MilestoneItemModel::slotWbsDefinitionChanged); disconnect(m_project, &Project::nodeChanged, this, &MilestoneItemModel::slotNodeChanged); disconnect(m_project, &Project::nodeToBeAdded, this, &MilestoneItemModel::slotNodeToBeInserted); disconnect(m_project, &Project::nodeToBeRemoved, this, &MilestoneItemModel::slotNodeToBeRemoved); disconnect(m_project, &Project::nodeToBeMoved, this, &MilestoneItemModel::slotNodeToBeMoved); disconnect(m_project, &Project::nodeMoved, this, &MilestoneItemModel::slotNodeMoved); disconnect(m_project, &Project::nodeAdded, this, &MilestoneItemModel::slotNodeInserted); disconnect(m_project, &Project::nodeRemoved, this, &MilestoneItemModel::slotNodeRemoved); } m_project = project; //debugPlan<"<allNodes()) { m_nodemap.insert(n->wbsCode(true), n); } } return cnt != m_nodemap.count(); } void MilestoneItemModel::resetModel() { beginResetModel(); resetData(); endResetModel(); } Qt::ItemFlags MilestoneItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (!index.isValid()) { if (m_readWrite) { flags |= Qt::ItemIsDropEnabled; } return flags; } flags |= Qt::ItemIsDragEnabled; if (m_readWrite) { flags |= Qt::ItemIsDropEnabled; switch (index.column()) { case NodeModel::NodeName: // name if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraint: // constraint type if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if (!isColumnReadOnly(index.column())) { Node *n = node(index); if (n == 0) break; int c = n->constraint(); if (c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval) { flags |= Qt::ItemIsEditable; } } break; } case NodeModel::NodeConstraintEnd: { // constraint end if (!isColumnReadOnly(index.column())) { Node *n = node(index); if (n == 0) break; int c = n->constraint(); if (c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval) { flags |= Qt::ItemIsEditable; } } break; } case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if (!isColumnReadOnly(index.column())) { Node *n = node(index); if (n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone)) { flags |= Qt::ItemIsEditable; } } break; } case NodeModel::NodeDescription: // description break; case NodeModel::NodeCompleted: if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; default: flags &= ~Qt::ItemIsEditable; } } return flags; } QModelIndex MilestoneItemModel::parent(const QModelIndex &index) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex MilestoneItemModel::index(int row, int column, const QModelIndex &parent) const { //debugPlan<= m_nodemap.count()) { //debugPlan<<"No index for"<(node)), 0, const_cast(node)); // clazy:exclude=container-anti-pattern } QVariant MilestoneItemModel::data(const QModelIndex &index, int role) const { QVariant result; if (role == Qt::TextAlignmentRole) { return headerData(index.column(), Qt::Horizontal, role); } Node *n = node(index); if (n != 0) { if (index.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole) { result = m_nodemodel.data(n, index.column(), Qt::EditRole); switch (result.toInt()) { case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return KGantt::TypeTask; } return result; } } result = m_nodemodel.data(n, index.column(), role); return result; } bool MilestoneItemModel::setData(const QModelIndex &index, const QVariant &/*value*/, int role) { if ((flags(index) &Qt::ItemIsEditable) == 0 || role != Qt::EditRole) { return false; } // Node *n = node(index); switch (index.column()) { default: qWarning("data: invalid display value column %d", index.column()); return false; } return false; } QVariant MilestoneItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole || role == Qt::EditRole) { return m_nodemodel.headerData(section, role); } } if (role == Qt::ToolTipRole) { return NodeModel::headerData(section, role); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *MilestoneItemModel::createDelegate(int column, QWidget *parent) const { switch (column) { case NodeModel::NodeEstimateType: return new EnumDelegate(parent); case NodeModel::NodeEstimateCalendar: return new EnumDelegate(parent); case NodeModel::NodeEstimate: return new DurationSpinBoxDelegate(parent); case NodeModel::NodeOptimisticRatio: return new SpinBoxDelegate(parent); case NodeModel::NodePessimisticRatio: return new SpinBoxDelegate(parent); case NodeModel::NodeRisk: return new EnumDelegate(parent); case NodeModel::NodeConstraint: return new EnumDelegate(parent); case NodeModel::NodeRunningAccount: return new EnumDelegate(parent); case NodeModel::NodeStartupAccount: return new EnumDelegate(parent); case NodeModel::NodeStartupCost: return new MoneyDelegate(parent); case NodeModel::NodeShutdownAccount: return new EnumDelegate(parent); case NodeModel::NodeShutdownCost: return new MoneyDelegate(parent); case NodeModel::NodeCompleted: return new TaskCompleteDelegate(parent); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate(parent); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate(parent); default: return 0; } return 0; } int MilestoneItemModel::columnCount(const QModelIndex &/*parent*/) const { return m_nodemodel.propertyCount(); } int MilestoneItemModel::rowCount(const QModelIndex &parent) const { //debugPlan< rows; foreach (const QModelIndex &index, indexes) { if (index.isValid() && !rows.contains(index.row())) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool MilestoneItemModel::dropAllowed(const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data) { //debugPlan; Node *dn = node(index); if (dn == 0) { errorPlan<<"no node to drop on!"; return false; // hmmm } switch (dropIndicatorPosition) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling return dropAllowed(dn->parentNode(), data); case ItemModelBase::OnItem: // dn == new parent return dropAllowed(dn, data); default: break; } return false; } bool MilestoneItemModel::dropAllowed(Node *on, const QMimeData *data) { if (!data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal")) { return false; } if (on == m_project) { return true; } QByteArray encodedData = data->data("application/x-vnd.kde.plan.nodeitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList(stream); foreach (Node *n, lst) { if (on == n || on->isChildOf(n)) { return false; } } lst = removeChildNodes(lst); foreach (Node *n, lst) { if (! m_project->canMoveTask(n, on)) { return false; } } return true; } QList MilestoneItemModel::nodeList(QDataStream &stream) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode(id); if (node) { lst << node; } } return lst; } QList MilestoneItemModel::removeChildNodes(const QList &nodes) { QList lst; foreach (Node *node, nodes) { bool ins = true; foreach (Node *n, lst) { if (node->isChildOf(n)) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if (ins) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach (Node *node, nl) { foreach (Node *n, nlst) { if (n->isChildOf(node)) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf(n); lst.removeAt(i); } } } return lst; } bool MilestoneItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { //debugPlan<hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal")) { return false; } if (action == Qt::MoveAction) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data("application/x-vnd.kde.plan.nodeitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if (parent.isValid()) { par = node(parent); } else { par = m_project; } QList lst = nodeList(stream); QList nodes = removeChildNodes(lst); // children goes with their parent foreach (Node *n, nodes) { if (! m_project->canMoveTask(n, par)) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach (Node *n, nodes) { if (cmd == 0) cmd = new MacroCommand(kundo2_i18n("Move tasks")); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; cmd->addCommand(new NodeMoveCmd(m_project, n, par, pos)); offset++; } if (cmd) { emit executeCommand(cmd); } //debugPlan<name(); return true; } return false; } Node *MilestoneItemModel::node(const QModelIndex &index) const { Node *n = 0; if (index.isValid()) { //debugPlan<(index.internalPointer()); } return n; } void MilestoneItemModel::slotNodeChanged(Node *node) { //debugPlan<name(); if (node == 0) { return; } beginResetModel(); resetData(); endResetModel(); } void MilestoneItemModel::slotWbsDefinitionChanged() { //debugPlan; if (m_project == 0) { return; } if (! m_nodemap.isEmpty()) { beginResetModel(); resetData(); endResetModel(); } } int MilestoneItemModel::sortRole(int column) const { int v = Qt::DisplayRole; switch (column) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } return v; } //-------------- NodeSortFilterProxyModel::NodeSortFilterProxyModel(ItemModelBase* model, QObject *parent, bool filterUnscheduled) : QSortFilterProxyModel(parent), m_filterUnscheduled(filterUnscheduled) { setSourceModel(model); setDynamicSortFilter(true); } ItemModelBase *NodeSortFilterProxyModel::itemModel() const { return static_cast(sourceModel()); } void NodeSortFilterProxyModel::setFilterUnscheduled(bool on) { m_filterUnscheduled = on; invalidateFilter(); } bool NodeSortFilterProxyModel::filterAcceptsRow (int row, const QModelIndex & parent) const { //debugPlan<project() == 0) { //debugPlan<project(); return false; } if (m_filterUnscheduled) { QString s = sourceModel()->data(sourceModel()->index(row, NodeModel::NodeNotScheduled, parent), Qt::EditRole).toString(); if (s == "true") { //debugPlan<<"Filtered unscheduled:"<index(row, 0, parent); return false; } } bool accepted = QSortFilterProxyModel::filterAcceptsRow(row, parent); //debugPlan<index(row, 0, parent)<<"accepted ="<sortRole(column)); QSortFilterProxyModel::sort(column, order); } //------------------ TaskModuleModel::TaskModuleModel(QObject *parent) : QAbstractItemModel(parent) , m_project(nullptr) { } void TaskModuleModel::setProject(Project *project) { if (m_project) { disconnect(m_project, &Project::taskModulesChanged, this, &TaskModuleModel::slotTaskModulesChanged); } m_project = project; if (m_project) { connect(m_project, &Project::taskModulesChanged, this, &TaskModuleModel::slotTaskModulesChanged); } slotReset(); } void TaskModuleModel::slotReset() { slotTaskModulesChanged(m_project ? m_project->taskModules() : QList()); } void TaskModuleModel::addTaskModule(Project *project, const QUrl &url) { beginInsertRows(QModelIndex(), m_modules.count(), m_modules.count()); m_modules << project; m_urls << url; endInsertRows(); } Qt::ItemFlags TaskModuleModel::flags(const QModelIndex &idx) const { Qt::ItemFlags f = QAbstractItemModel::flags(idx) | Qt::ItemIsDropEnabled; if (idx.isValid()) { f |= Qt::ItemIsDragEnabled; } return f; } int TaskModuleModel::columnCount (const QModelIndex &/*idx*/) const { return 1; } int TaskModuleModel::rowCount(const QModelIndex &idx) const { return idx.isValid() ? 0 : m_modules.count(); } QVariant TaskModuleModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid() || idx.row() >= m_modules.count()) { return QVariant(); } switch (role) { case Qt::DisplayRole: return m_modules.value(idx.row())->name(); case Qt::ToolTipRole: return m_modules.value(idx.row())->description(); case Qt::WhatsThisRole: return QVariant(); case Qt::UserRole: return m_urls.value(idx.row()); default: break; } return QVariant(); } QVariant TaskModuleModel::headerData(int /*section*/, Qt::Orientation orientation , int role) const { if (orientation == Qt::Horizontal) { switch (role) { case Qt::DisplayRole: return xi18nc("@title:column", "Name"); default: break; } } return QVariant(); } QModelIndex TaskModuleModel::parent(const QModelIndex& /*idx*/) const { return QModelIndex(); } QModelIndex TaskModuleModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { return QModelIndex(); } return createIndex(row, column, m_modules.value(row)); } QStringList TaskModuleModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan" << "text/uri-list"; } bool TaskModuleModel::dropMimeData(const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &/*parent*/) { if (data->hasUrls()) { QList urls = data->urls(); debugPlan<bad()) { // d->lastErrorMessage = i18n("Not a valid Calligra file: %1", file); warnPlan<open("root")) { // maindoc.xml warnPlan<device()); KoXmlElement element = doc.documentElement().namedItem("project").toElement(); Project *project = new Project(); XMLLoaderObject status; status.setVersion(doc.documentElement().attribute("version", PLAN_FILE_SYNTAX_VERSION)); status.setProject(project); if (project->load(element, status)) { stripProject(project); } else { warnPlan<setData("application/x-vnd.kde.plan.taskmodule", context.document.toByteArray()); delete project; } } } return mime; } void TaskModuleModel::stripProject(Project *project) const { foreach (ScheduleManager *sm, project->scheduleManagers()) { DeleteScheduleManagerCmd c(*project, sm); } } void TaskModuleModel::loadTaskModules(const QStringList &files) { debugPlan< &modules) { debugPlan< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptresourceallocationmodel.h" #include "RequieredResourceDelegate.h" #include "kptcommonstrings.h" -#include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptdebug.h" #include #include -namespace KPlato -{ +using namespace KPlato; //-------------------------------------- ResourceAllocationModel::ResourceAllocationModel(QObject *parent) : QObject(parent), m_project(0), m_task(0) { } ResourceAllocationModel::~ResourceAllocationModel() { } const QMetaEnum ResourceAllocationModel::columnMap() const { return metaObject()->enumerator(metaObject()->indexOfEnumerator("Properties")); } void ResourceAllocationModel::setProject(Project *project) { m_project = project; } void ResourceAllocationModel::setTask(Task *task) { m_task = task; } int ResourceAllocationModel::propertyCount() const { return columnMap().keyCount(); } QVariant ResourceAllocationModel::name(const Resource *res, int role) const { //debugPlan<name()<<","<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } -QVariant ResourceAllocationModel::name(const ResourceGroup *res, int role) const -{ - //debugPlan<name()<<","<name(); - break; - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - } - return QVariant(); -} - QVariant ResourceAllocationModel::type(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return res->typeToString(true); case Role::EnumList: return res->typeToStringList(true); case Role::EnumListValue: return (int)res->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } -QVariant ResourceAllocationModel::type(const ResourceGroup *res, int role) const -{ - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: - case Qt::ToolTipRole: - return res->typeToString(true); - case Role::EnumList: - return res->typeToStringList(true); - case Role::EnumListValue: - return (int)res->type(); - case Qt::TextAlignmentRole: - return Qt::AlignCenter; - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - } - return QVariant(); -} - - -QVariant ResourceAllocationModel::allocation(const ResourceGroup *group, const Resource *res, int role) const +QVariant ResourceAllocationModel::allocation(const Resource *res, int role) const { - if (m_project == 0 || m_task == 0) { + if (m_project == nullptr || m_task == nullptr) { return QVariant(); } - const ResourceGroupRequest *rg = m_task->requests().find(group); - const ResourceRequest *rr = 0; - if (rg) { - rr = rg->find(res); - } + const ResourceRequest *rr = m_task->requests().find(res); switch (role) { case Qt::DisplayRole: { int units = rr ? rr->units() : 0; // xgettext: no-c-format return i18nc("%", "%1%", units); } case Qt::EditRole: return rr ? rr->units() : 0; case Qt::ToolTipRole: { int units = rr ? rr->units() : 0; if (units == 0) { return xi18nc("@info:tooltip", "Not allocated"); } // xgettext: no-c-format return xi18nc("@info:tooltip", "Allocated units: %1%", units); } case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Minimum: return 0; case Role::Maximum: return 100; case Qt::CheckStateRole: return Qt::Unchecked; } return QVariant(); } -QVariant ResourceAllocationModel::allocation(const ResourceGroup *res, int role) const -{ - if (m_project == 0 || m_task == 0) { - return QVariant(); - } - const ResourceGroupRequest *req = m_task->requests().find(res); - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: - return req ? req->units() : 0; - case Qt::ToolTipRole: - return QVariant(); - case Qt::TextAlignmentRole: - return Qt::AlignCenter; - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - case Role::Minimum: - return 0; - case Role::Maximum: - return res->numResources(); - } - return QVariant(); -} - QVariant ResourceAllocationModel::maximum(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: // xgettext: no-c-format return i18nc("%", "%1%", res->units()); case Qt::EditRole: return res->units(); case Qt::ToolTipRole: // xgettext: no-c-format return i18n("Maximum units available: %1%", res->units()); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceAllocationModel::required(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: { QStringList lst; foreach (Resource *r, res->requiredResources()) { lst << r->name(); } return lst.join(","); } case Qt::EditRole: return QVariant();//Not used case Qt::ToolTipRole: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: return QVariant(); case Qt::WhatsThisRole: return xi18nc("@info:whatsthis", "Required Resources" "A working resource can be assigned to one or more required resources." " A required resource is a material resource that the working resource depends on" " in order to do the work." "To be able to use a material resource as a required resource, the material resource" " must be part of a group of type Material."); } return QVariant(); } -QVariant ResourceAllocationModel::maximum(const ResourceGroup *res, int role) const -{ - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: - return res->numResources(); - case Qt::ToolTipRole: - return i18np("There is %1 resource available in this group", "There are %1 resources available in this group", res->numResources()); - case Qt::TextAlignmentRole: - return Qt::AlignCenter; - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - } - return QVariant(); -} - -QVariant ResourceAllocationModel::data(const ResourceGroup *group, const Resource *resource, int property, int role) const +QVariant ResourceAllocationModel::data(const Resource *resource, int property, int role) const { QVariant result; - if (resource == 0) { + if (resource == nullptr) { return result; } switch (property) { case RequestName: result = name(resource, role); break; case RequestType: result = type(resource, role); break; - case RequestAllocation: result = allocation(group, resource, role); break; + case RequestAllocation: result = allocation(resource, role); break; case RequestMaximum: result = maximum(resource, role); break; case RequestRequired: result = required(resource, role); break; default: debugPlan<<"data: invalid display value: property="<Required Resources" "A working resource can be assigned to one or more required resources." " A required resource is a material resource that the working resource depends on" " in order to do the work." "To be able to use a material resource as a required resource, the material resource" " must be part of a group of type Material."); default: return QVariant(); } } return QVariant(); } //-------------------------------------- ResourceAllocationItemModel::ResourceAllocationItemModel(QObject *parent) : ItemModelBase(parent) { } ResourceAllocationItemModel::~ResourceAllocationItemModel() { } -void ResourceAllocationItemModel::slotResourceToBeAdded(ResourceGroup *group, int row) -{ - //debugPlan<name()<<","<groupCount() == 1 && resource->parentGroups().at(0) == group) { - connectSignals(resource, false); - } -} - -void ResourceAllocationItemModel::slotResourceRemoved() -{ - endRemoveRows(); -} - -void ResourceAllocationItemModel::slotResourceGroupToBeInserted(Project *project, int row) +void ResourceAllocationItemModel::slotResourceToBeAdded(KPlato::Project *project, int row) { Q_UNUSED(project) beginInsertRows(QModelIndex(), row, row); } -void ResourceAllocationItemModel::slotResourceGroupInserted(ResourceGroup *group) +void ResourceAllocationItemModel::slotResourceAdded(KPlato::Resource *resource) { - //debugPlan<name(); - connectSignals(group, true); + Q_UNUSED(resource) endInsertRows(); } -void ResourceAllocationItemModel::slotResourceGroupToBeRemoved(Project *project, int row, ResourceGroup *group) +void ResourceAllocationItemModel::slotResourceToBeRemoved(KPlato::Project *project, int row, KPlato::Resource *resource) { Q_UNUSED(project) + Q_UNUSED(resource) beginRemoveRows(QModelIndex(), row, row); - connectSignals(group, false); } -void ResourceAllocationItemModel::slotResourceGroupRemoved() +void ResourceAllocationItemModel::slotResourceRemoved() { endRemoveRows(); } void ResourceAllocationItemModel::setProject(Project *project) { beginResetModel(); if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceAllocationItemModel::projectDeleted); - disconnect(m_project, &Project::resourceGroupChanged, this, &ResourceAllocationItemModel::slotResourceGroupChanged); - disconnect(m_project, &Project::resourceGroupToBeAdded, this, &ResourceAllocationItemModel::slotResourceGroupToBeInserted); - disconnect(m_project, &Project::resourceGroupAdded, this, &ResourceAllocationItemModel::slotResourceGroupInserted); - disconnect(m_project, &Project::resourceGroupToBeRemoved, this, &ResourceAllocationItemModel::slotResourceGroupToBeRemoved); - disconnect(m_project, &Project::resourceGroupRemoved, this, &ResourceAllocationItemModel::slotResourceGroupRemoved); - - for (ResourceGroup *g : m_project->resourceGroups()) { - connectSignals(g, false); - } + disconnect(m_project, &Project::resourceChanged, this, &ResourceAllocationItemModel::slotResourceChanged); + disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); + disconnect(m_project, &Project::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); + disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); + disconnect(m_project, &Project::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); } m_project = project; if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceAllocationItemModel::projectDeleted); - disconnect(m_project, &Project::resourceGroupChanged, this, &ResourceAllocationItemModel::slotResourceGroupChanged); - disconnect(m_project, &Project::resourceGroupToBeAdded, this, &ResourceAllocationItemModel::slotResourceGroupToBeInserted); - disconnect(m_project, &Project::resourceGroupAdded, this, &ResourceAllocationItemModel::slotResourceGroupInserted); - disconnect(m_project, &Project::resourceGroupToBeRemoved, this, &ResourceAllocationItemModel::slotResourceGroupToBeRemoved); - disconnect(m_project, &Project::resourceGroupRemoved, this, &ResourceAllocationItemModel::slotResourceGroupRemoved); - - for (ResourceGroup *g : m_project->resourceGroups()) { - connectSignals(g, true); - } + disconnect(m_project, &Project::resourceChanged, this, &ResourceAllocationItemModel::slotResourceChanged); + disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); + disconnect(m_project, &Project::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); + disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); + disconnect(m_project, &Project::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); } m_model.setProject(m_project); endResetModel(); } -void ResourceAllocationItemModel::connectSignals(ResourceGroup *group, bool enable) -{ - if (enable) { - connect(group, &ResourceGroup::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); - connect(group, &ResourceGroup::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); - connect(group, &ResourceGroup::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); - connect(group, &ResourceGroup::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); - } else { - disconnect(group, &ResourceGroup::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); - disconnect(group, &ResourceGroup::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); - disconnect(group, &ResourceGroup::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); - disconnect(group, &ResourceGroup::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); - } - for (Resource *r : group->resources()) { - connectSignals(r, enable); - } -} - -void ResourceAllocationItemModel::connectSignals(Resource *resource, bool enable) -{ - if (enable) { - connect(resource, &Resource::dataChanged, this, &ResourceAllocationItemModel::slotResourceChanged, Qt::ConnectionType(Qt::AutoConnection | Qt::UniqueConnection)); - } else { - disconnect(resource, &Resource::dataChanged, this, &ResourceAllocationItemModel::slotResourceChanged); - } -} - void ResourceAllocationItemModel::setTask(Task *task) { if (task == m_model.task()) { return; } - if (m_model.task() == 0) { + if (m_model.task() == nullptr) { beginResetModel(); filldata(task); m_model.setTask(task); endResetModel(); return; } if (task) { emit layoutAboutToBeChanged(); filldata(task); m_model.setTask(task); emit layoutChanged(); } } void ResourceAllocationItemModel::filldata(Task *task) { qDeleteAll(m_resourceCache); m_resourceCache.clear(); - qDeleteAll(m_groupCache); - m_groupCache.clear(); m_requiredChecked.clear(); if (m_project && task) { - foreach (const ResourceGroup *g, m_project->resourceGroups()) { - const ResourceGroupRequest *gr = task->requests().find(g); - if (gr) { - m_groupCache[ g ] = new ResourceGroupRequest(*gr); - } - } - foreach (const Resource *r, m_project->resourceList()) { - const ResourceRequest *rr = task->requests().find(r); - if (rr) { - m_resourceCache[ r ] = new ResourceRequest(*rr); - if (! m_resourceCache[ r ]->requiredResources().isEmpty()) { - m_requiredChecked[ r ] = Qt::Checked; - } + for (ResourceRequest *rr : task->requests().resourceRequests()) { + const Resource *r = rr->resource(); + m_resourceCache[r] = new ResourceRequest(*rr); + if (!m_resourceCache[r]->requiredResources().isEmpty()) { + m_requiredChecked[r] = Qt::Checked; } } } } bool ResourceAllocationItemModel::hasMaterialResources() const { - if (! m_project) { + if (!m_project) { return false; } - foreach (const ResourceGroup *g, m_project->resourceGroups()) { - if (g->type() == ResourceGroup::Type_Material) { - foreach (const Resource *r, g->resources()) { - if (r->type() == Resource::Type_Material) { - return true; - } - } + foreach (const Resource *r, m_project->resourceList()) { + if (r->type() == Resource::Type_Material) { + return true; } } return false; } Qt::ItemFlags ResourceAllocationItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = ItemModelBase::flags(index); if (!m_readWrite) { //debugPlan<<"read only"<type() != Resource::Type_Work) { flags &= ~(Qt::ItemIsEditable | Qt::ItemIsUserCheckable); - } else if (m_resourceCache.contains(r) && m_resourceCache[ r ]->units() > 0) { + } else if (m_resourceCache.contains(r) && m_resourceCache[r]->units() > 0) { flags |= (Qt::ItemIsEditable | Qt::ItemIsUserCheckable); - if (! hasMaterialResources()) { + if (!hasMaterialResources()) { flags &= ~Qt::ItemIsEnabled; } } break; } default: flags &= ~Qt::ItemIsEditable; break; } return flags; } QModelIndex ResourceAllocationItemModel::parent(const QModelIndex &index) const { - if (!index.isValid() || m_project == 0) { - return QModelIndex(); - } - //debugPlan<(object(index)); - if (r && r->parentGroups().value(0)) { - // only resources have parent - int row = m_project->indexOf(r->parentGroups().value(0)); - return createIndex(row, 0, r->parentGroups().value(0)); - } - + Q_UNUSED(index) return QModelIndex(); } QModelIndex ResourceAllocationItemModel::index(int row, int column, const QModelIndex &parent) const { - if (m_project == 0 || column < 0 || column >= columnCount() || row < 0) { + if (m_project == nullptr || column < 0 || column >= columnCount() || row < 0) { return QModelIndex(); } - if (! parent.isValid()) { - if (row < m_project->numResourceGroups()) { - return createIndex(row, column, m_project->resourceGroupAt(row)); - } + if (parent.isValid()) { return QModelIndex(); } - QObject *p = object(parent); - ResourceGroup *g = qobject_cast(p); - if (g) { - if (row < g->numResources()) { - return createIndex(row, column, g->resourceAt(row)); - } - return QModelIndex(); + Resource *r = m_project->resourceAt(row); + if (r) { + return createIndex(row, column, r); } return QModelIndex(); } -QModelIndex ResourceAllocationItemModel::index(const Resource *resource) const +QModelIndex ResourceAllocationItemModel::index(Resource *resource) const { - if (m_project == 0 || resource == 0) { + if (m_project == nullptr || resource == nullptr) { return QModelIndex(); } - Resource *r = const_cast(resource); - int row = -1; - ResourceGroup *par = r->parentGroups().value(0); - if (par) { - row = par->indexOf(r); - return createIndex(row, 0, r); + int row = m_project->indexOf(resource); + if (row >= 0) { + return createIndex(row, 0, resource); } return QModelIndex(); } -QModelIndex ResourceAllocationItemModel::index(const ResourceGroup *group) const -{ - if (m_project == 0 || group == 0) { - return QModelIndex(); - } - ResourceGroup *g = const_cast(group); - int row = m_project->indexOf(g); - return createIndex(row, 0, g); - -} - int ResourceAllocationItemModel::columnCount(const QModelIndex &/*parent*/) const { return m_model.propertyCount(); } int ResourceAllocationItemModel::rowCount(const QModelIndex &parent) const { - if (m_project == 0 || m_model.task() == 0) { + if (m_project == nullptr || m_model.task() == nullptr) { return 0; } - if (! parent.isValid()) { - return m_project->numResourceGroups(); - } - QObject *p = object(parent); - ResourceGroup *g = qobject_cast(p); - if (g) { - return g->numResources(); - } - return 0; + return parent.isValid() ? 0 : m_project->resourceCount(); } -QVariant ResourceAllocationItemModel::allocation(const ResourceGroup *group, const Resource *res, int role) const +QVariant ResourceAllocationItemModel::allocation(const Resource *res, int role) const { - if (m_model.task() == 0) { + if (m_model.task() == nullptr) { return QVariant(); } - if (! m_resourceCache.contains(res)) { + if (!m_resourceCache.contains(res)) { if (role == Qt::EditRole) { ResourceRequest *req = m_model.task()->requests().find(res); - if (req == 0) { + if (req == nullptr) { req = new ResourceRequest(const_cast(res), 0); } const_cast(this)->m_resourceCache.insert(res, req); return req->units(); } - return m_model.allocation(group, res, role); + return m_model.allocation(res, role); } switch (role) { case Qt::DisplayRole: { // xgettext: no-c-format - return i18nc("%", "%1%", m_resourceCache[ res ]->units()); + return i18nc("%", "%1%", m_resourceCache[res]->units()); } case Qt::EditRole: - return m_resourceCache[ res ]->units(); + return m_resourceCache[res]->units(); case Qt::ToolTipRole: { if (res->units() == 0) { return xi18nc("@info:tooltip", "Not allocated"); } - return xi18nc("@info:tooltip", "%1 allocated out of %2 available", allocation(group, res, Qt::DisplayRole).toString(), m_model.maximum(res, Qt::DisplayRole).toString()); + return xi18nc("@info:tooltip", "%1 allocated out of %2 available", allocation(res, Qt::DisplayRole).toString(), m_model.maximum(res, Qt::DisplayRole).toString()); } case Qt::CheckStateRole: - return m_resourceCache[ res ]->units() == 0 ? Qt::Unchecked : Qt::Checked; - default: - return m_model.allocation(group, res, role); - } - return QVariant(); -} - -int ResourceAllocationItemModel::requestedResources(const ResourceGroup *res) const -{ - int c = 0; - foreach (const Resource *r, res->resources()) { - if (m_resourceCache.contains(r) && m_resourceCache[ r ]->units() > 0) { - ++c; - } - } - return c; -} - -QVariant ResourceAllocationItemModel::allocation(const ResourceGroup *res, int role) const -{ - if (m_model.task() == 0) { - return QVariant(); - } - if (! m_groupCache.contains(res)) { - return m_model.allocation(res, role); - } - switch (role) { - case Qt::DisplayRole: - return QString(" %1 (%2)") - .arg(qMax(m_groupCache[ res ]->units(), allocation(res, Role::Minimum).toInt())) - .arg(requestedResources(res)); - case Qt::EditRole: - return qMax(m_groupCache[ res ]->units(), allocation(res, Role::Minimum).toInt()); - case Qt::ToolTipRole: { - QString s1 = i18ncp("@info:tooltip", - "%1 resource requested for dynamic allocation", - "%1 resources requested for dynamic allocation", - allocation(res, Qt::EditRole).toInt()); - QString s2 = i18ncp("@info:tooltip", - "%1 resource allocated", - "%1 resources allocated", - requestedResources(res)); - - return xi18nc("@info:tooltip", "%1%2", s1, s2); - } - case Qt::WhatsThisRole: { - return xi18nc("@info:whatsthis", - "Group allocations" - "You can allocate a number of resources from a group and let" - " the scheduler select from the available resources at the time of scheduling." - " These dynamically allocated resources will be in addition to any resource you have allocated specifically."); - } - case Role::Minimum: { - return 0; - } - case Role::Maximum: { - return res->numResources() - requestedResources(res); - } + return m_resourceCache[res]->units() == 0 ? Qt::Unchecked : Qt::Checked; default: return m_model.allocation(res, role); } return QVariant(); } bool ResourceAllocationItemModel::setAllocation(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { - m_resourceCache[ res ]->setUnits(value.toInt()); - QModelIndex idx = index(res->parentGroups().value(0)); - emit dataChanged(index(idx.row(), 0, QModelIndex()), index(idx.row(), columnCount() - 1, QModelIndex())); + m_resourceCache[res]->setUnits(value.toInt()); + QModelIndex idx = index(res); + emit dataChanged(idx, idx.sibling(idx.row(), ResourceAllocationModel::RequestAllocation)); return true; } case Qt::CheckStateRole: { - if (! m_resourceCache.contains(res)) { - m_resourceCache[ res ] = new ResourceRequest(res, 0); + if (!m_resourceCache.contains(res)) { + m_resourceCache[res] = new ResourceRequest(res, 0); } - if (m_resourceCache[ res ]->units() == 0) { - m_resourceCache[ res ]->setUnits(100); - ResourceGroup *g = res->parentGroups().value(0); - if (m_groupCache.contains(g)) { - ResourceGroupRequest *gr = m_groupCache[ g ]; - if (gr->units() + requestedResources(g) > g->numResources()) { - gr->setUnits(gr->units() - 1); - } - } + if (m_resourceCache[res]->units() == 0) { + m_resourceCache[res]->setUnits(100); } else { - m_resourceCache[ res ]->setUnits(0); + m_resourceCache[res]->setUnits(0); } - QModelIndex idx = index(res->parentGroups().value(0)); - emit dataChanged(index(idx.row(), 0, QModelIndex()), index(idx.row(), columnCount() - 1, QModelIndex())); + QModelIndex idx = index(res); + emit dataChanged(idx, idx); return true; } } return false; } -bool ResourceAllocationItemModel::setAllocation(ResourceGroup *res, const QVariant &value, int role) -{ - switch (role) { - case Qt::EditRole: - if (! m_groupCache.contains(res)) { - m_groupCache[ res ] = new ResourceGroupRequest(res, 0); - } - m_groupCache[ res ]->setUnits(value.toInt()); - emit dataChanged(index(res), index(res)); - return true; - } - return false; -} - -QVariant ResourceAllocationItemModel::maximum(const ResourceGroup *res, int role) const -{ - switch (role) { - case Qt::DisplayRole: { - int c = res->numResources() - requestedResources(res); - if (m_groupCache.contains(res)) { - c -= m_groupCache[ res ]->units(); - } - return i18nc("1: free resources, 2: number of resources", "%1 of %2", c, res->numResources()); - } - case Qt::ToolTipRole: - return xi18ncp("@info:tooltip", "There is %1 resource available in this group", "There are %1 resources available in this group", res->numResources()); - default: - return m_model.maximum(res, role); - } - return QVariant(); -} - QVariant ResourceAllocationItemModel::required(const QModelIndex &idx, int role) const { - if (m_model.task() == 0) { + if (m_model.task() == nullptr) { return QVariant(); } Resource *res = resource(idx); - if (res == 0) { + if (res == nullptr) { return QVariant(); } switch (role) { case Qt::DisplayRole: { if (res->type() == Resource::Type_Work) { QStringList lst; - if (m_requiredChecked[ res ]) { + if (m_requiredChecked[res]) { foreach (const Resource *r, required(idx)) { lst << r->name(); } } return lst.isEmpty() ? i18n("None") : lst.join(","); } break; } case Qt::EditRole: break; case Qt::ToolTipRole: switch (res->type()) { case Resource::Type_Work: { - if (! hasMaterialResources()) { + if (!hasMaterialResources()) { return xi18nc("@info:tooltip", "No material resources available"); } QStringList lst; - if (m_requiredChecked[ res ]) { + if (m_requiredChecked[res]) { foreach (const Resource *r, required(idx)) { lst << r->name(); } } return lst.isEmpty() ? xi18nc("@info:tooltip", "No required resources") : lst.join("\n"); } case Resource::Type_Material: return xi18nc("@info:tooltip", "Material resources cannot have required resources"); case Resource::Type_Team: return xi18nc("@info:tooltip", "Team resources cannot have required resources"); } break; case Qt::CheckStateRole: if (res->type() == Resource::Type_Work) { - return m_requiredChecked[ res ]; + return m_requiredChecked[res]; } break; default: return m_model.required(res, role); } return QVariant(); } bool ResourceAllocationItemModel::setRequired(const QModelIndex &idx, const QVariant &value, int role) { Resource *res = resource(idx); - if (res == 0) { + if (res == nullptr) { return false; } switch (role) { case Qt::CheckStateRole: - m_requiredChecked[ res ] = value.toInt(); + m_requiredChecked[res] = value.toInt(); if (value.toInt() == Qt::Unchecked) { - m_resourceCache[ res ]->setRequiredResources(QList()); + m_resourceCache[res]->setRequiredResources(QList()); } emit dataChanged(idx, idx); return true; } return false; } -QVariant ResourceAllocationItemModel::notUsed(const ResourceGroup *, int role) const -{ - switch (role) { - case Qt::DisplayRole: - return QString(" "); - case Qt::TextAlignmentRole: - return Qt::AlignCenter; - case Qt::EditRole: - case Qt::ToolTipRole: - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - } - return QVariant(); -} - QVariant ResourceAllocationItemModel::data(const QModelIndex &index, int role) const { - QVariant result; - QObject *obj = object(index); - if (obj == 0) { - return QVariant(); - } if (role == Qt::TextAlignmentRole) { // use same alignment as in header (headers always horizontal) return headerData(index.column(), Qt::Horizontal, role); } - Resource *r = qobject_cast(obj); + QVariant result; + Resource *r = resource(index); if (r) { if (index.column() == ResourceAllocationModel::RequestAllocation) { - return allocation(r->parentGroups().value(0), r, role); + return allocation(r, role); } if (index.column() == ResourceAllocationModel::RequestRequired) { return required(index, role); } - result = m_model.data(r->parentGroups().value(0), r, index.column(), role); - } else { - ResourceGroup *g = qobject_cast(obj); - if (g) { - switch (index.column()) { - case ResourceAllocationModel::RequestAllocation: - result = allocation(g, role); - break; - case ResourceAllocationModel::RequestMaximum: - result = maximum(g, role); - break; - default: - result = m_model.data(g, index.column(), role); - break; - } - } - } - if (role == Qt::DisplayRole && ! result.isValid()) { - // HACK to show focus in empty cells - result = ' '; + result = m_model.data(r, index.column(), role); } return result; } bool ResourceAllocationItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (! index.isValid()) { + if (!index.isValid()) { return ItemModelBase::setData(index, value, role); } - if ((flags(index) & Qt::ItemIsEditable) == 0) { + if (!(flags(index) & Qt::ItemIsEditable)) { return false; } QObject *obj = object(index); Resource *r = qobject_cast(obj); if (r) { switch (index.column()) { case ResourceAllocationModel::RequestAllocation: if (setAllocation(r, value, role)) { emit dataChanged(index, index); QModelIndex idx = this->index(index.row(), ResourceAllocationModel::RequestAllocation, parent(parent(index))); emit dataChanged(idx, idx); return true; } return false; case ResourceAllocationModel::RequestRequired: return setRequired(index, value, role); default: //qWarning("data: invalid display value column %d", index.column()); return false; } } - ResourceGroup *g = qobject_cast(obj); - if (g) { - switch (index.column()) { - case ResourceAllocationModel::RequestAllocation: - if (setAllocation(g, value, role)) { - emit dataChanged(index, index); - return true; - } - return false; - default: - //qWarning("data: invalid display value column %d", index.column()); - return false; - } - } return false; } QVariant ResourceAllocationItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { return m_model.headerData(section, role); } if (role == Qt::TextAlignmentRole) { switch (section) { case 0: return QVariant(); default: return Qt::AlignCenter; } return Qt::AlignCenter; } } return m_model.headerData(section, role); } QAbstractItemDelegate *ResourceAllocationItemModel::createDelegate(int col, QWidget *parent) const { switch (col) { case ResourceAllocationModel::RequestAllocation: return new SpinBoxDelegate(parent); case ResourceAllocationModel::RequestRequired: return new RequieredResourceDelegate(parent); default: break; } return 0; } QObject *ResourceAllocationItemModel::object(const QModelIndex &index) const { QObject *o = 0; if (index.isValid()) { o = static_cast(index.internalPointer()); Q_ASSERT(o); } return o; } void ResourceAllocationItemModel::slotResourceChanged(Resource *res) { - for (ResourceGroup *g : res->parentGroups()) { - int row = g->indexOf(res); - emit dataChanged(createIndex(row, 0, res), createIndex(row, columnCount() - 1, res)); - } -} - -void ResourceAllocationItemModel::slotResourceGroupChanged(ResourceGroup *group) -{ - int row = m_project->indexOf(group); - emit dataChanged(createIndex(row, 0, group), createIndex(row, columnCount() - 1, group)); + QModelIndex idx = index(res); + emit dataChanged(idx, idx.sibling(idx.row(), columnCount()-1)); } Resource *ResourceAllocationItemModel::resource(const QModelIndex &idx) const { return qobject_cast(object(idx)); } void ResourceAllocationItemModel::setRequired(const QModelIndex &idx, const QList &lst) { Resource *r = resource(idx); Q_ASSERT(r); if (m_resourceCache.contains(r)) { - m_resourceCache[ r ]->setRequiredResources(lst); + m_resourceCache[r]->setRequiredResources(lst); emit dataChanged(idx, idx); } } QList ResourceAllocationItemModel::required(const QModelIndex &idx) const { Resource *r = resource(idx); Q_ASSERT(r); if (m_resourceCache.contains(r)) { - ResourceRequest* request = m_resourceCache[ r ]; + ResourceRequest* request = m_resourceCache[r]; return request->requiredResources(); } return r->requiredResources(); } - -} // namespace KPlato diff --git a/src/libs/models/kptresourceallocationmodel.h b/src/libs/models/kptresourceallocationmodel.h index fefc9ef7..e1b12175 100644 --- a/src/libs/models/kptresourceallocationmodel.h +++ b/src/libs/models/kptresourceallocationmodel.h @@ -1,180 +1,156 @@ /* This file is part of the KDE project Copyright (C) 2009 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPTRESOURCEALLOCATIONMODEL_H #define KPTRESOURCEALLOCATIONMODEL_H #include "planmodels_export.h" #include #include #include namespace KPlato { class Project; class Task; class Resource; class ResourceGroup; class ResourceRequest; class ResourceGroupRequest; /** The ResourceAllocationModel gives access to resource requests */ class PLANMODELS_EXPORT ResourceAllocationModel : public QObject { Q_OBJECT public: explicit ResourceAllocationModel(QObject *parent = 0); ~ResourceAllocationModel() override; enum Properties { RequestName = 0, RequestType, RequestAllocation, RequestMaximum, RequestRequired }; Q_ENUM(Properties) const QMetaEnum columnMap() const; void setProject(Project *project); Task *task() const { return m_task; } void setTask(Task *task); int propertyCount() const; - QVariant data(const ResourceGroup *group, const Resource *resource, int property, int role = Qt::DisplayRole) const; - QVariant data(const ResourceGroup *group, int property, int role = Qt::DisplayRole) const; + QVariant data(const Resource *resource, int property, int role = Qt::DisplayRole) const; static QVariant headerData(int section, int role = Qt::DisplayRole); QVariant name(const Resource *res, int role) const; QVariant type(const Resource *res, int role) const; - QVariant allocation(const ResourceGroup *group, const Resource *res, int role) const; + QVariant allocation(const Resource *res, int role) const; QVariant maximum(const Resource *res, int role) const; QVariant required(const Resource *res, int role) const; - - QVariant name(const ResourceGroup *res, int role) const; - QVariant type(const ResourceGroup *res, int role) const; - QVariant allocation(const ResourceGroup *res, int role) const; - QVariant maximum(const ResourceGroup *res, int role) const; private: Project *m_project; Task *m_task; }; /** The ResourceAllocationItemModel facilitates viewing and modifying resource allocations for a task. */ class PLANMODELS_EXPORT ResourceAllocationItemModel : public ItemModelBase { Q_OBJECT public: explicit ResourceAllocationItemModel(QObject *parent = 0); ~ResourceAllocationItemModel() override; const QMetaEnum columnMap() const override { return m_model.columnMap(); } void setProject(Project *project) override; Task *task() const { return m_model.task(); } Qt::ItemFlags flags(const QModelIndex & index) const override; QModelIndex parent(const QModelIndex & index) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; - QModelIndex index(const ResourceGroup *group) const; - QModelIndex index(const Resource *resource) const; + QModelIndex index(Resource *resource) const; int columnCount(const QModelIndex & parent = QModelIndex()) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QAbstractItemDelegate *createDelegate(int col, QWidget *parent) const override; QObject *object(const QModelIndex &index) const; const QHash &resourceCache() const { return m_resourceCache; } - const QHash &groupCache() const { return m_groupCache; } Resource *resource(const QModelIndex &idx) const; void setRequired(const QModelIndex &idx, const QList &lst); QList required(const QModelIndex &idx) const; public Q_SLOTS: void setTask(KPlato::Task *task); protected Q_SLOTS: - void slotResourceGroupChanged(KPlato::ResourceGroup*); - void slotResourceGroupToBeInserted(KPlato::Project *project, int row); - void slotResourceGroupInserted(KPlato::ResourceGroup *group); - void slotResourceGroupToBeRemoved(KPlato::Project *project, int row, KPlato::ResourceGroup *group); - void slotResourceGroupRemoved(); - void slotResourceChanged(KPlato::Resource*); - void slotResourceToBeAdded(KPlato::ResourceGroup *group, int row); + void slotResourceToBeAdded(KPlato::Project *project, int row); void slotResourceAdded(KPlato::Resource *resource); - void slotResourceToBeRemoved(KPlato::ResourceGroup *group, int row, KPlato::Resource *resource); + void slotResourceToBeRemoved(KPlato::Project *project, int row, KPlato::Resource *resource); void slotResourceRemoved(); protected: void filldata(Task *task); - QVariant notUsed(const ResourceGroup *res, int role) const; - - QVariant allocation(const ResourceGroup *group, const Resource *res, int role) const; - QVariant allocation(const ResourceGroup *res, int role) const; - bool setAllocation(ResourceGroup *res, const QVariant &value, int role); + QVariant allocation(const Resource *res, int role) const; bool setAllocation(Resource *res, const QVariant &value, int role); - QVariant maximum(const ResourceGroup *res, int role) const; - bool setRequired(const QModelIndex &idx, const QVariant &value, int role); QVariant required(const QModelIndex &idx, int role) const; - void connectSignals(ResourceGroup *group, bool enable); - void connectSignals(Resource *resource, bool enable); - private: int requestedResources(const ResourceGroup *res) const; bool hasMaterialResources() const; private: ResourceAllocationModel m_model; QHash m_resourceCache; QHash m_requiredChecked; - QHash m_groupCache; }; } //KPlato namespace #endif diff --git a/src/libs/models/tests/InsertProjectXmlCommandTester.cpp b/src/libs/models/tests/InsertProjectXmlCommandTester.cpp index 9a03e478..80d9f24b 100644 --- a/src/libs/models/tests/InsertProjectXmlCommandTester.cpp +++ b/src/libs/models/tests/InsertProjectXmlCommandTester.cpp @@ -1,233 +1,226 @@ /* This file is part of the KDE project Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "InsertProjectXmlCommandTester.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptresource.h" #include "kptnode.h" #include "kpttask.h" #include "kptschedule.h" #include "kptappointment.h" #include "XmlSaveContext.h" #include "InsertProjectXmlCommand.h" #include #include #include using namespace KPlato; Project *createProject() { Project *project = new Project(); project->setName("P1"); project->setId(project->uniqueNodeId()); project->registerNodeId(project); DateTime targetstart = DateTime(QDate::currentDate(), QTime(0,0,0)); DateTime targetend = DateTime(targetstart.addDays(3)); project->setConstraintStartTime(targetstart); project->setConstraintEndTime(targetend); // standard worktime defines 8 hour day as default Calendar *calendar = new Calendar("Test"); calendar->setDefault(true); QTime t1(9, 0, 0); QTime t2 (17, 0, 0); int length = t1.msecsTo(t2); for (int i=1; i <= 7; ++i) { CalendarDay *d = calendar->weekday(i); d->setState(CalendarDay::Working); d->addInterval(t1, length); } project->addCalendar(calendar); return project; } void addResources(Project *project) { ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project->addResourceGroup(g); Resource *resource = new Resource(); resource->setName("R1"); resource->setCalendar(project->calendars().value(0)); project->addResource(resource); g->addResource(resource); } void addTask(Project *project, const QString &name, Task *parent = 0) { Task *task = project->createTask(); task->setName(name); project->addSubTask(task, parent ? (Node*)parent : (Node*)project); task->estimate()->setUnit(Duration::Unit_h); task->estimate()->setExpectedEstimate(8.0); task->estimate()->setType(Estimate::Type_Effort); } -void addRequests(Node *task, ResourceGroup *g, Resource *r) +void addRequests(Node *task, Resource *r) { - ResourceGroupRequest *gr = new ResourceGroupRequest(g); ResourceRequest *rr = new ResourceRequest(r, 100); - task->requests().addRequest(gr); - task->requests().addResourceRequest(rr, gr); + task->requests().addResourceRequest(rr); } void addDependency(Node *t1, Node *t2) { t1->addDependChildNode(t2, Relation::FinishStart); } void InsertProjectXmlCommandTester::init() { m_project = createProject(); } void InsertProjectXmlCommandTester::cleanup() { Debug::print(m_project, "--------------------", true); delete m_project; } void InsertProjectXmlCommandTester::copyBasics() { addTask(m_project, "T1"); QList old = m_project->childNodeIterator(); XmlSaveContext context(m_project); context.options = XmlSaveContext::SaveSelectedNodes; context.nodes << m_project->childNode(0); context.save(); qInfo()<childNode(0)); cmd.redo(); QCOMPARE(m_project->allTasks().count(), 2); QVERIFY(!old.contains(m_project->childNode(0))); QCOMPARE(m_project->childNode(1), old.at(0)); } void InsertProjectXmlCommandTester::copyRequests() { addResources(m_project); addTask(m_project, "T1"); addTask(m_project, "T2"); - addRequests(m_project->childNode(0), m_project->resourceGroupAt(0), m_project->resourceGroupAt(0)->resourceAt(0)); - addRequests(m_project->childNode(1), m_project->resourceGroupAt(0), m_project->resourceGroupAt(0)->resourceAt(0)); + Node *org1 = m_project->childNode(0); + Node *org2 = m_project->childNode(1); + addRequests(org1, m_project->resourceGroupAt(0)->resourceAt(0)); + addRequests(org2, m_project->resourceGroupAt(0)->resourceAt(0)); Debug::print(m_project, "--------------------", true); XmlSaveContext context(m_project); context.options = XmlSaveContext::SaveSelectedNodes|XmlSaveContext::SaveRequests; context.nodes << m_project->childNode(0) << m_project->childNode(1); context.save(); InsertProjectXmlCommand cmd(m_project, context.document.toByteArray(), m_project, nullptr /*last*/); cmd.redo(); Debug::print(m_project, "--------------------", true); QCOMPARE(m_project->allTasks().count(), 4); Node *copy1 = m_project->childNode(2); - QVERIFY(m_project->childNode(0)->id() != copy1->id()); - QCOMPARE(m_project->childNode(0)->name(), copy1->name()); + QVERIFY(org1->id() != copy1->id()); + QCOMPARE(org1->name(), copy1->name()); - QCOMPARE(m_project->childNode(0)->requests().requests().count(), copy1->requests().requests().count()); - ResourceGroupRequest *gr = m_project->childNode(0)->requests().requests().at(0); - ResourceGroupRequest *cpgr = copy1->requests().requests().at(0); - QCOMPARE(gr->group(), cpgr->group()); - QCOMPARE(gr->units(), cpgr->units()); - QCOMPARE(gr->count(), cpgr->count()); - ResourceRequest *rr = gr->resourceRequests().at(0); - ResourceRequest *cprr = cpgr->resourceRequests().at(0); + ResourceRequest *rr = org1->requests().resourceRequests().at(0); + ResourceRequest *cprr = copy1->requests().resourceRequests().at(0); QCOMPARE(rr->resource(), cprr->resource()); QCOMPARE(rr->units(), cprr->units()); - } void InsertProjectXmlCommandTester::copyDependency() { addResources(m_project); addTask(m_project, "T1"); addTask(m_project, "T2"); addDependency(m_project->childNode(0), m_project->childNode(1)); Debug::print(m_project, "--------------------", true); XmlSaveContext context(m_project); context.options = XmlSaveContext::SaveSelectedNodes | XmlSaveContext::SaveRelations; context.nodes << m_project->childNode(0) << m_project->childNode(1); context.save(); InsertProjectXmlCommand cmd(m_project, context.document.toByteArray(), m_project, nullptr /*last*/); cmd.redo(); Debug::print(m_project, "--------------------", true); QCOMPARE(m_project->allTasks().count(), 4); Node *copy1 = m_project->childNode(2); Node *copy2 = m_project->childNode(3); QVERIFY(m_project->childNode(0)->id() != copy1->id()); QCOMPARE(m_project->childNode(0)->name(), copy1->name()); QCOMPARE(m_project->childNode(0)->numDependChildNodes(), copy1->numDependChildNodes()); QCOMPARE(m_project->childNode(1)->numDependParentNodes(), copy2->numDependParentNodes()); } void InsertProjectXmlCommandTester::copyToPosition() { addTask(m_project, "T1"); addTask(m_project, "T2"); QList old = m_project->childNodeIterator(); XmlSaveContext context(m_project); context.options = XmlSaveContext::SaveSelectedNodes; context.nodes << m_project->childNode(0); context.save(); { InsertProjectXmlCommand cmd(m_project, context.document.toByteArray(), m_project, m_project->childNode(1)); cmd.redo(); } QCOMPARE(m_project->allTasks().count(), 3); QCOMPARE(m_project->childNode(0), old.at(0)); QCOMPARE(m_project->childNode(2), old.at(1)); QVERIFY(!old.contains(m_project->childNode(1))); old = m_project->childNodeIterator(); context.options = XmlSaveContext::SaveSelectedNodes; context.save(); { InsertProjectXmlCommand cmd(m_project, context.document.toByteArray(), m_project->childNode(1), nullptr); cmd.redo(); } QCOMPARE(m_project->numChildren(), 3); QCOMPARE(m_project->allNodes().count(), 4); QVERIFY(m_project->childNode(1)->type() == Node::Type_Summarytask); QCOMPARE(m_project->childNode(1)->numChildren(), 1); } QTEST_GUILESS_MAIN(KPlato::InsertProjectXmlCommandTester) diff --git a/src/libs/models/tests/WorkPackageProxyModelTester.cpp b/src/libs/models/tests/WorkPackageProxyModelTester.cpp index 6b4e5d8e..b3acaeea 100644 --- a/src/libs/models/tests/WorkPackageProxyModelTester.cpp +++ b/src/libs/models/tests/WorkPackageProxyModelTester.cpp @@ -1,228 +1,225 @@ /* This file is part of the KDE project Copyright (C) 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "WorkPackageProxyModelTester.h" #include "kptcommand.h" #include "kpttask.h" #include "kptnode.h" #include "kptnodeitemmodel.h" #include #include #include "../../kernel/tests/debug.cpp" namespace KPlato { Task *WorkPackageProxyModelTester::addTask(Node *parent, int after) { Task *task = project.createTask(); TaskAddCmd cmd(&project, task, parent->childNode(after)); cmd.redo(); - ResourceGroupRequest *gr = new ResourceGroupRequest(project.resourceGroupAt(0)); - AddResourceGroupRequestCmd c(*task, gr); - c.redo(); ResourceRequest *rr = new ResourceRequest(project.resourceGroupAt(0)->resourceAt(0), 100); - AddResourceRequestCmd c2(&task->requests(), rr, gr); + AddResourceRequestCmd c2(&task->requests(), rr); c2.redo(); return task; } void WorkPackageProxyModelTester::removeTask(Node *task) { NodeDeleteCmd cmd(task); cmd.redo(); } void WorkPackageProxyModelTester::moveTask(Node *task, Node *newParent, int newPos) { NodeMoveCmd cmd(&project, task, newParent, newPos); cmd.redo(); } void WorkPackageProxyModelTester::initTestCase() { project.setName("P1"); project.setId(project.uniqueNodeId()); project.registerNodeId(&project); Calendar *calendar = new Calendar(); calendar->setName("C1"); calendar->setDefault(true); QTime t1(9, 0, 0); QTime t2 (17, 0, 0); int length = t1.msecsTo(t2); for (int i=1; i <= 7; ++i) { CalendarDay *d = calendar->weekday(i); d->setState(CalendarDay::Working); d->addInterval(t1, length); } project.addCalendar(calendar); QCOMPARE(m_model.rowCount(), 0); m_model.setProject(&project); QCOMPARE(m_model.rowCount(), 0); ResourceGroup *g = new ResourceGroup(); g->setName("G1"); project.addResourceGroup(g); Resource *r = new Resource(); r->setName("R1"); project.addResource(r); g->addResource(r); sm = project.createScheduleManager(); m_model.setScheduleManager(sm); } void WorkPackageProxyModelTester::testInsert() { Task *task = addTask(&project, 0); task->setName("T1"); QCOMPARE(m_model.baseModel()->rowCount(), 1); QCOMPARE(m_model.rowCount(), 0); // filtered, (not scheduled) sm->createSchedules(); project.calculate(*sm); QVERIFY(task->isScheduled()); QCOMPARE(m_model.rowCount(), 1); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); task = addTask(&project, -1); task->setName("T2"); QCOMPARE(m_model.baseModel()->rowCount(), 2); QCOMPARE(m_model.rowCount(), 1); // filtered, (not scheduled) sm->createSchedules(); project.calculate(*sm); QVERIFY(task->isScheduled()); QCOMPARE(m_model.rowCount(), 2); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T2")); task = addTask(&project, -1); task->setName("T3"); QCOMPARE(m_model.baseModel()->rowCount(), 3); QCOMPARE(m_model.rowCount(), 2); // filtered, (not scheduled) sm->createSchedules(); project.calculate(*sm); QVERIFY(task->isScheduled()); QCOMPARE(m_model.rowCount(), 3); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); } void WorkPackageProxyModelTester::testRemove() { removeTask(project.childNode(0)); QCOMPARE(m_model.rowCount(), 2); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T3")); removeTask(project.childNode(1)); QCOMPARE(m_model.rowCount(), 1); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T2")); removeTask(project.childNode(0)); QCOMPARE(m_model.rowCount(), 0); } void WorkPackageProxyModelTester::testMove() { testInsert(); //Debug::print(&project, "T1, T2, T3 ---------", true); moveTask(project.childNode(0), &project, 1); //Debug::print(&project, "T2, T1, T3 ---------", true); QCOMPARE(m_model.rowCount(), 3); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); moveTask(project.childNode(1), &project, 0); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); moveTask(project.childNode(1), &project, 0); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); moveTask(project.childNode(0), &project, -1); // last QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T3")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T2")); } void WorkPackageProxyModelTester::testMoveChild() { while (project.numChildren() > 0) { Node *t = project.childNode(0); project.takeTask(t); delete t; } testInsert(); Task *task = addTask(&project, 1); // after 1 = 2 :( task->setName("T4"); //Debug::print(&project, "T1, T2, T4, T3 ----", true); QCOMPARE(project.numChildren(), 4); QCOMPARE(m_model.rowCount(), 3); // filtered, (not scheduled) sm->createSchedules(); project.calculate(*sm); //Debug::print(&project, "T1, T2, T4, T3 ----", true); QVERIFY(task->isScheduled()); QCOMPARE(m_model.rowCount(), 4); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T4")); QCOMPARE(m_model.index(3, NodeModel::NodeName).data().toString(), QString("T3")); project.indentTask(project.childNode(2)); //Debug::print(&project, "T1, T2 -> T4, T3 ----", true); QCOMPARE(m_model.rowCount(), 3); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T4")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); moveTask(project.childNode(1)->childNode(0), project.childNode(0), 0); //Debug::print(&project, "T1 -> T4, T2, T3 ----", true); QCOMPARE(m_model.rowCount(), 3); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T4")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T3")); moveTask(project.childNode(0)->childNode(0), project.childNode(2), 0); //Debug::print(&project, "T1, T2, T3 -> T4 ----", true); QCOMPARE(m_model.rowCount(), 3); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T4")); moveTask(project.childNode(2)->childNode(0), &project, 0); //Debug::print(&project, "T4, T1, T2, T3 ----", true); QCOMPARE(m_model.rowCount(), 4); QCOMPARE(m_model.index(0, NodeModel::NodeName).data().toString(), QString("T4")); QCOMPARE(m_model.index(1, NodeModel::NodeName).data().toString(), QString("T1")); QCOMPARE(m_model.index(2, NodeModel::NodeName).data().toString(), QString("T2")); QCOMPARE(m_model.index(3, NodeModel::NodeName).data().toString(), QString("T3")); } } //namespace KPlato QTEST_GUILESS_MAIN(KPlato::WorkPackageProxyModelTester) diff --git a/src/libs/ui/CMakeLists.txt b/src/libs/ui/CMakeLists.txt index 0582285d..4e0f61ac 100644 --- a/src/libs/ui/CMakeLists.txt +++ b/src/libs/ui/CMakeLists.txt @@ -1,226 +1,224 @@ include_directories( ${PLANKERNEL_INCLUDES} ${PLANMODELS_INCLUDES} ${PLANMAIN_INCLUDES} ${PLANWIDGETS_INCLUDES} ${KDEPIMLIBS_INCLUDE_DIR} ) #add_subdirectory( tests ) ########### KPlato private library ############### if (PLAN_USE_KREPORT) message(STATUS "-- Building plan with reports capability") add_subdirectory(reports/items) set(planreports_LIB_SRC reports/reportview.cpp reports/reportdata.cpp reports/reportsourceeditor.cpp reports/reportscripts.cpp ) set(planreports_ui_LIB_SRCS reports/reportsourceeditor.ui reports/reportnavigator.ui reports/reportsectionswidget.ui reports/reportgroupsectionswidget.ui reports/reporttoolswidget.ui ) endif() set(planui_LIB_SRCS ${planreports_LIB_SRC} RelationEditorDialog.cpp TasksEditController.cpp - GroupAllocationPanel.cpp +# GroupAllocationPanel.cpp TasksEditDialog.cpp RichTextWidget.cpp reportsgenerator/ReportsGeneratorView.cpp kptganttitemdelegate.cpp kptworkpackagesendpanel.cpp kptworkpackagesenddialog.cpp kptdocumentseditor.cpp kptdocumentspanel.cpp kptdocumentsdialog.cpp kptitemviewsettup.cpp kptsplitterview.cpp kptrelationeditor.cpp kptdependencyeditor.cpp kptusedefforteditor.cpp kpttaskstatusview.cpp kptcalendareditor.cpp kptviewbase.cpp kptaccountseditor.cpp kptperteditor.cpp kptpertresult.cpp kpttaskeditor.cpp kptscheduleeditor.cpp kptsummarytaskdialog.cpp kptsummarytaskgeneralpanel.cpp kptresourceappointmentsview.cpp kptaccountsviewconfigdialog.cpp kptaccountsview.cpp kpttaskcostpanel.cpp kptmilestoneprogresspanel.cpp kptmilestoneprogressdialog.cpp kpttaskdialog.cpp kptmainprojectdialog.cpp kptmainprojectpanel.cpp kptganttview.cpp gantt/DateTimeTimeLine.cpp gantt/DateTimeGrid.cpp kptrelationdialog.cpp kptrequestresourcespanel.cpp kptresourcedialog.cpp kptstandardworktimedialog.cpp kptintervaledit.cpp kpttaskgeneralpanel.cpp kpttaskprogresspanel.cpp kpttaskprogressdialog.cpp kpttaskdescriptiondialog.cpp kptwbsdefinitiondialog.cpp kptwbsdefinitionpanel.cpp - kptresourceassignmentview.cpp kptresourceallocationeditor.cpp kptworkpackagemergedialog.cpp kptrecalculatedialog.cpp kpthtmlview.cpp locale/localemon.cpp kptlocaleconfigmoneydialog.cpp ResourceGroupEditor.cpp ResourceAllocationView.cpp kptresourceeditor.cpp performance/KPlatoChart.cpp performance/PerformanceStatusBase.cpp performance/ProjectStatusView.cpp performance/PerformanceStatusView.cpp performance/PerformanceTableView.cpp ) ki18n_wrap_ui(planui_LIB_SRCS ${planreports_ui_LIB_SRCS} RelationEditorDialog.ui kptresourceappointmentsdisplayoptions.ui kptganttchartdisplayoptions.ui kptprintingheaderfooter.ui kptganttprintingoptions.ui kptworkpackagesendpanel.ui kptdocumentspanel.ui performance/PerformanceStatus.ui performance/PerformanceStatusViewSettingsPanel.ui kptcpmwidget.ui kptitemviewsettings.ui kptpertresult.ui standardworktimedialogbase.ui kptwbsdefinitionpanelbase.ui kptaccountsviewconfigurepanelbase.ui kptintervaleditbase.ui kpttaskcostpanelbase.ui kpttaskdescriptionpanelbase.ui kptsummarytaskgeneralpanelbase.ui kptmilestoneprogresspanelbase.ui resourcedialogbase.ui kptmainprojectpanelbase.ui relationpanel.ui kpttaskgeneralpanelbase.ui kpttaskprogresspanelbase.ui kptperteditor.ui - kptresourceassignmentview.ui kpttaskstatusviewsettingspanel.ui kptworkpackagemergepanel.ui kptrecalculatedialog.ui kptscheduleeditor.ui locale/localemon.ui ) add_library(planui SHARED ${planui_LIB_SRCS}) generate_export_header(planui) target_link_libraries(planui PUBLIC planmain planmodels KF5::KHtml PRIVATE KChart KF5::ItemViews KF5::IconThemes KF5::Archive KF5::TextWidgets KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM ) if (PLAN_USE_KREPORT) target_link_libraries(planui PUBLIC KReport PRIVATE KPropertyWidgets) endif() if(KF5AkonadiContact_FOUND) target_link_libraries(planui PRIVATE KF5::AkonadiContact) endif() set_target_properties(planui PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS planui ${INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES AccountsEditorUi.rc AccountsEditorUi_readonly.rc AccountsViewUi.rc CalendarEditorUi.rc CalendarEditorUi_readonly.rc TaskEditorUi.rc TaskEditorUi_readonly.rc DependencyEditorUi.rc DependencyEditorUi_readonly.rc ResourceEditorUi.rc ResourceEditorUi_readonly.rc ResourceGroupEditorUi.rc ResourceGroupEditorUi_readonly.rc ResourceAppointmentsViewUi.rc ScheduleEditorUi.rc ScheduleEditorUi_readonly.rc GanttViewUi.rc WorkPackageViewUi.rc WorkPackageViewUi_readonly.rc reportsgenerator/ReportsGeneratorViewUi.rc reportsgenerator/ReportsGeneratorViewUi_readonly.rc performance/PerformanceStatusViewUi.rc performance/ProjectStatusViewUi.rc PertResultUi.rc TaskViewUi.rc TaskStatusViewUi.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligraplan ) # reports files install(FILES reportsgenerator/templates/EmptyTemplate.odt reportsgenerator/templates/ProjectPerformanceCost.odt reportsgenerator/templates/ProjectPerformanceEffort.odt reportsgenerator/templates/TaskStatus.odt DESTINATION ${DATA_INSTALL_DIR}/calligraplan/reports ) diff --git a/src/libs/ui/ResourceAllocationView.cpp b/src/libs/ui/ResourceAllocationView.cpp index b6eb30ba..f756f444 100644 --- a/src/libs/ui/ResourceAllocationView.cpp +++ b/src/libs/ui/ResourceAllocationView.cpp @@ -1,165 +1,158 @@ /* This file is part of the KDE project Copyright (C) 2017 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "ResourceAllocationView.h" #include "kptnode.h" #include "kptresource.h" #include "kptnodeitemmodel.h" #include "ResourceGroupItemModel.h" #include "kptcommand.h" #include #include #include #include #include #include #include namespace KPlato { ResourceAllocationView::ResourceAllocationView(KoDocument *doc, QWidget *parent) : QTreeView(parent) , m_doc(doc) { m_allocateAction = new QAction(i18n("Allocate"), this); connect(m_allocateAction, &QAction::triggered, this, &ResourceAllocationView::slotAllocate); } QList ResourceAllocationView::selectedResources() const { QList resources; ResourceGroupItemModel *m = qobject_cast(model()); if (m) { foreach(const QModelIndex &idx, selectionModel()->selectedRows()) { Resource *r = m->resource(idx); if (r) { resources << r; } } } return resources; } void ResourceAllocationView::setSelectedTasks(const QItemSelection &selected, const QItemSelection &deselected) { for (QModelIndex &idx : deselected.indexes()) { if (m_tasks.contains(idx)) { m_tasks.removeAt(m_tasks.indexOf(idx)); } } QModelIndexList tasks = selected.indexes(); if (tasks.isEmpty()) { return; } const NodeItemModel *m = qobject_cast(tasks.first().model()); if (!m) { return; } for (const QModelIndex &idx : tasks) { if (idx.column() != NodeModel::NodeAllocation) { continue; } Node *n = m->node(idx); if (n->type() != Node::Type_Task) { continue; } m_tasks << QPersistentModelIndex(idx); } } void ResourceAllocationView::contextMenuEvent(QContextMenuEvent *event) { if (m_tasks.isEmpty()) { return; } if (selectedResources().isEmpty()) { return; } QMenu menu; menu.move(event->globalPos()); menu.addAction(m_allocateAction); menu.exec(); return; } void ResourceAllocationView::slotAllocate() { if (!m_doc) { warnPlan<<"ResourceAllocationView has no document, commands cannot be executed"; return; } QList lst; for (QPersistentModelIndex &idx : m_tasks) { if (idx.isValid()) { lst << idx; } } if (lst.isEmpty()) { return; } QList resources = selectedResources(); if (resources.isEmpty()) { return; } const NodeItemModel *m = qobject_cast(lst.first().model()); MacroCommand *cmd = new MacroCommand(); for (QPersistentModelIndex &idx : lst) { Node *n = m->node(idx); if (!n || n->type() != Node::Type_Task) { continue; } Task *t = static_cast(n); // remove any requests before adding new ones - foreach(ResourceGroupRequest *r, t->requests().requests()) { - RemoveResourceGroupRequestCmd *c = new RemoveResourceGroupRequestCmd(r); + foreach(ResourceRequest *r, t->requests().resourceRequests()) { + RemoveResourceRequestCmd *c = new RemoveResourceRequestCmd(r); c->execute(); // need to remove everything before we add anything cmd->addCommand(c); } - QHash groups; for (Resource *r : resources) { - if (!groups.contains(r->parentGroups().value(0))) { - groups[r->parentGroups().value(0)] = new ResourceGroupRequest(r->parentGroups().value(0)); - AddResourceGroupRequestCmd *c = new AddResourceGroupRequestCmd(*t, groups[r->parentGroups().value(0)]); - c->execute(); - cmd->addCommand(c); - } ResourceRequest *rr = new ResourceRequest(r); rr->setUnits(100); // defaults to 100% - AddResourceRequestCmd *c = new AddResourceRequestCmd(groups[r->parentGroups().value(0)], rr); + AddResourceRequestCmd *c = new AddResourceRequestCmd(&n->requests(), rr); c->execute(); cmd->addCommand(c); } } if (cmd->isEmpty()) { delete cmd; } else { MacroCommand *m = new MacroCommand(kundo2_i18n("Modify resource allocations")); m_doc->addCommand(m); m->addCommand(cmd); } } } // namespace KPlato diff --git a/src/libs/ui/kptrequestresourcespanel.cpp b/src/libs/ui/kptrequestresourcespanel.cpp index 7efbd874..3a9c1a3f 100644 --- a/src/libs/ui/kptrequestresourcespanel.cpp +++ b/src/libs/ui/kptrequestresourcespanel.cpp @@ -1,178 +1,106 @@ /* This file is part of the KDE project - Copyright (C) 2003 - 2009 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Copyright (C) 2003 - 2009 Dag Andersen + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. -*/ + */ // clazy:excludeall=qstring-arg #include "kptrequestresourcespanel.h" -#include "kpttask.h" -#include "kptproject.h" -#include "kptresource.h" -#include "kptcalendar.h" -#include "kptresourceallocationeditor.h" - +#include +#include +#include +#include #include +#include #include #include +#include -namespace KPlato -{ +using namespace KPlato; + RequestResourcesPanel::RequestResourcesPanel(QWidget *parent, Project &project, Task &task, bool) : QWidget(parent) { + m_model.setReadWrite(true); + m_model.setProject(&project); + m_model.setTask(&task); + QVBoxLayout *l = new QVBoxLayout(this); l->setMargin(0); - m_view = new ResourceAllocationTreeView(this); - m_view->setViewSplitMode(false); - m_view->masterView()->header()->moveSection(ResourceAllocationModel::RequestType, m_view->masterView()->header()->count() - 1); - m_view->setReadWrite(true); + m_view = new QTreeView(this); + m_view->setModel(&m_model); + m_view->setRootIsDecorated(false); l->addWidget(m_view); - m_view->setProject(&project); - m_view->setTask(&task); - m_view->slotExpand(); - m_view->masterView()->header()->resizeSections(QHeaderView::ResizeToContents); + m_view->header()->resizeSections(QHeaderView::ResizeToContents); - connect(m_view, &ResourceAllocationTreeView::dataChanged, this, &RequestResourcesPanel::changed); + connect(&m_model, &ResourceAllocationItemModel::dataChanged, this, &RequestResourcesPanel::changed); } bool RequestResourcesPanel::ok() { return true; } MacroCommand *RequestResourcesPanel::buildCommand() { - Task *task = m_view->task(); - if (task == 0) { - return 0; + return buildCommand(m_model.task()); +} + +MacroCommand *RequestResourcesPanel::buildCommand(Task *task) +{ + if (task == nullptr) { + return nullptr; } MacroCommand *cmd = new MacroCommand(kundo2_i18n("Modify resource allocations")); - const QHash &rmap = m_view->resourceCache(); + const QHash &rmap = m_model.resourceCache(); // First remove all that should be removed for(QHash::const_iterator rit = rmap.constBegin(); rit != rmap.constEnd(); ++rit) { if (rit.value()->units() == 0) { ResourceRequest *rr = task->requests().find(rit.key()); if (rr) { - cmd->addCommand(new RemoveResourceRequestCmd(rr->parent(), rr)); + cmd->addCommand(new RemoveResourceRequestCmd(rr)); } } } - - QHash groups; - // Add/modify - const QHash &gmap = m_view->groupCache(); - for(QHash::const_iterator git = gmap.constBegin(); git != gmap.constEnd(); ++git) { - ResourceGroupRequest *gr = task->requests().find(git.key()); - if (gr == 0) { - if (git.value()->units() > 0) { - gr = new ResourceGroupRequest(const_cast(git.key()), git.value()->units()); - cmd->addCommand(new AddResourceGroupRequestCmd(*task, gr)); - groups[ git.key() ] = gr; - } // else nothing - } else { - cmd->addCommand(new ModifyResourceGroupRequestUnitsCmd(gr, gr->units(), git.value()->units())); - } - } for(QHash::const_iterator rit = rmap.constBegin(); rit != rmap.constEnd(); ++rit) { Resource *resource = const_cast(rit.key()); - ResourceGroup *group = resource->parentGroups().value(0); if (rit.value()->units() > 0) { ResourceRequest *rr = task->requests().find(resource); - if (rr == 0) { - ResourceGroupRequest *gr = task->requests().find(group); - if (gr == 0) { - if (groups.contains(group)) { - gr = groups[ group ]; - } else { - gr = new ResourceGroupRequest(group, 0); - groups[ group ] = gr; - cmd->addCommand(new AddResourceGroupRequestCmd(*task, gr)); - } - } + if (rr == nullptr) { ResourceRequest *rr = new ResourceRequest(resource, rit.value()->units()); rr->setRequiredResources(rit.value()->requiredResources()); - cmd->addCommand(new AddResourceRequestCmd(gr, rr)); + cmd->addCommand(new AddResourceRequestCmd(&task->requests(), rr)); } else { if (rit.value()->units() != rr->units()) { cmd->addCommand(new ModifyResourceRequestUnitsCmd(rr, rr->units(), rit.value()->units())); } if (rit.value()->requiredResources() != rr->requiredResources()) { cmd->addCommand(new ModifyResourceRequestRequiredCmd(rr, rit.value()->requiredResources())); } } } } if (cmd->isEmpty()) { delete cmd; - cmd = 0; - } - return cmd; -} - - -MacroCommand *RequestResourcesPanel::buildCommand(Task *task) -{ - if (task == 0) { - return 0; - } - MacroCommand *cmd = new MacroCommand(kundo2_i18n("Modify resource allocations")); - const QHash &rmap = m_view->resourceCache(); - - // First remove all - foreach(ResourceGroupRequest *g, task->requests().requests()) { - cmd->addCommand(new RemoveResourceGroupRequestCmd(g)); - } - - QHash groups; - // Add possible requests to groups - const QHash &gmap = m_view->groupCache(); - for(QHash::const_iterator git = gmap.constBegin(); git != gmap.constEnd(); ++git) { - if (git.value()->units() > 0) { - ResourceGroupRequest *gr = new ResourceGroupRequest(const_cast(git.key()), git.value()->units()); - cmd->addCommand(new AddResourceGroupRequestCmd(*task, gr)); - groups[git.key()] = gr; - } - } - // Add possible requests to resources - for(QHash::const_iterator rit = rmap.constBegin(); rit != rmap.constEnd(); ++rit) { - Resource *resource = const_cast(rit.key()); - ResourceGroup *group = resource->parentGroups().value(0); - if (rit.value()->units() > 0) { - ResourceGroupRequest *gr = groups.value(group); - // Check if there is already a request to the group - if (gr == 0) { - gr = new ResourceGroupRequest(group, 0); - cmd->addCommand(new AddResourceGroupRequestCmd(*task, gr)); - groups[group] = gr; - } - ResourceRequest *rr = new ResourceRequest(resource, rit.value()->units()); - rr->setRequiredResources(rit.value()->requiredResources()); - cmd->addCommand(new AddResourceRequestCmd(gr, rr)); - } - } - if (cmd->isEmpty()) { - delete cmd; - cmd = 0; + cmd = nullptr; } return cmd; } - -} //KPlato namespace diff --git a/src/libs/ui/kptrequestresourcespanel.h b/src/libs/ui/kptrequestresourcespanel.h index 5f2fb3b1..64285f58 100644 --- a/src/libs/ui/kptrequestresourcespanel.h +++ b/src/libs/ui/kptrequestresourcespanel.h @@ -1,60 +1,64 @@ /* This file is part of the KDE project - Copyright (C) 2003 - 2007 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Copyright (C) 2003 - 2007 Dag Andersen + * Copyright (C) 2020 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. -*/ + */ #ifndef KPTREQUESTRESOURCESPANEL_H #define KPTREQUESTRESOURCESPANEL_H #include "planui_export.h" +#include #include +class QTreeView; + namespace KPlato { class Task; class Project; class Resource; class MacroCommand; -class ResourceAllocationTreeView; class RequestResourcesPanel : public QWidget { Q_OBJECT public: RequestResourcesPanel(QWidget *parent, Project &project, Task &task, bool baseline=false); /// Builds an undocommand for the current task /// only for changes (removals and/or additions) MacroCommand *buildCommand(); /// Builds an undo command for @p task /// that clears all current requests and adds the new ones (if any) MacroCommand *buildCommand(Task *task); bool ok(); Q_SIGNALS: void changed(); private: - ResourceAllocationTreeView *m_view; + QTreeView *m_view; + ResourceAllocationItemModel m_model; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kptresourceallocationeditor.cpp b/src/libs/ui/kptresourceallocationeditor.cpp index 546325e8..78fc8cf9 100644 --- a/src/libs/ui/kptresourceallocationeditor.cpp +++ b/src/libs/ui/kptresourceallocationeditor.cpp @@ -1,217 +1,207 @@ /* This file is part of the KDE project Copyright (C) 2009, 2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptresourceallocationeditor.h" #include "kptresourceallocationmodel.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptitemviewsettup.h" #include "kptdebug.h" #include #include #include #include namespace KPlato { ResourceAllocationTreeView::ResourceAllocationTreeView(QWidget *parent) : DoubleTreeViewBase(parent) { // header()->setContextMenuPolicy(Qt::CustomContextMenu); ResourceAllocationItemModel *m = new ResourceAllocationItemModel(this); setModel(m); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectRows); createItemDelegates(m); connect(m, &QAbstractItemModel::dataChanged, this, &ResourceAllocationTreeView::dataChanged); } QObject *ResourceAllocationTreeView::currentObject() const { return model()->object(selectionModel()->currentIndex()); } //----------------------------------- ResourceAllocationEditor::ResourceAllocationEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { QVBoxLayout * l = new QVBoxLayout(this); l->setMargin(0); m_view = new ResourceAllocationTreeView(this); l->addWidget(m_view); setupGui(); m_view->setEditTriggers(m_view->editTriggers() | QAbstractItemView::EditKeyPressed); QList lst1; lst1 << 1 << -1; QList lst2; lst2 << 0; m_view->hideColumns(lst1, lst2); m_view->masterView()->setDefaultColumns(QList() << 0); QList show; for (int c = 1; c < model()->columnCount(); ++c) { show << c; } m_view->slaveView()->setDefaultColumns(show); connect(model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand); connect(m_view, &DoubleTreeViewBase::currentChanged, this, &ResourceAllocationEditor::slotCurrentChanged); connect(m_view, &DoubleTreeViewBase::selectionChanged, this, &ResourceAllocationEditor::slotSelectionChanged); connect(m_view, &DoubleTreeViewBase::contextMenuRequested, this, &ResourceAllocationEditor::slotContextMenuRequested); connect(m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested); } void ResourceAllocationEditor::updateReadWrite(bool readwrite) { m_view->setReadWrite(readwrite); } void ResourceAllocationEditor::setGuiActive(bool activate) { debugPlan<selectionModel()->currentIndex().isValid()) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index(0, 0), QItemSelectionModel::NoUpdate); } } void ResourceAllocationEditor::slotContextMenuRequested(const QModelIndex &index, const QPoint& pos) { //debugPlan<model()->object(index); - ResourceGroup *g = qobject_cast(obj); - if (g) { - //name = "resourceeditor_group_popup"; - } else { - Resource *r = qobject_cast(obj); - if (r) { - //name = "resourceeditor_resource_popup"; - } + Resource *r = qobject_cast(obj); + if (r) { + //name = "resourceeditor_resource_popup"; } } if (name.isEmpty()) { slotHeaderContextMenuRequested(pos); return; } emit requestPopupMenu(name, pos); } Resource *ResourceAllocationEditor::currentResource() const { return qobject_cast(m_view->currentObject()); } -ResourceGroup *ResourceAllocationEditor::currentResourceGroup() const -{ - return qobject_cast(m_view->currentObject()); -} - void ResourceAllocationEditor::slotCurrentChanged(const QModelIndex &) { //debugPlan<actionSplitView(), &QAction::triggered, this, &ResourceAllocationEditor::slotSplitView); addContextAction(m_view->actionSplitView()); createOptionActions(ViewBase::OptionAll); } void ResourceAllocationEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode(! m_view->isViewSplit()); emit optionsModified(); } void ResourceAllocationEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog(this, m_view, this); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } bool ResourceAllocationEditor::loadContext(const KoXmlElement &context) { debugPlan<loadContext(model()->columnMap(), context); } void ResourceAllocationEditor::saveContext(QDomElement &context) const { debugPlan<saveContext(model()->columnMap(), context); } KoPrintJob *ResourceAllocationEditor::createPrintJob() { return m_view->createPrintJob(this); } } // namespace KPlato diff --git a/src/libs/ui/kptresourceallocationeditor.h b/src/libs/ui/kptresourceallocationeditor.h index 60f0361e..befde4cc 100644 --- a/src/libs/ui/kptresourceallocationeditor.h +++ b/src/libs/ui/kptresourceallocationeditor.h @@ -1,117 +1,114 @@ /* This file is part of the KDE project Copyright (C) 2009, 2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTRESOURCEALLOCATIONEDITOR_H #define KPTRESOURCEALLOCATIONEDITOR_H #include "planui_export.h" #include "kptviewbase.h" #include "kptresourceallocationmodel.h" #include "kpttask.h" #include class KoDocument; class QPoint; namespace KPlato { class Project; class Resource; -class ResourceGroup; class PLANUI_EXPORT ResourceAllocationTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit ResourceAllocationTreeView(QWidget *parent); ResourceAllocationItemModel *model() const { return static_cast(DoubleTreeViewBase::model()); } Project *project() const { return model()->project(); } void setProject(Project *project) { model()->setProject(project); } Task *task() const { return model()->task(); } void setTask(Task *task) { model()->setTask(task); } QObject *currentObject() const; const QHash &resourceCache() const { return model()->resourceCache(); } - const QHash &groupCache() const { return model()->groupCache(); } Q_SIGNALS: void dataChanged(); }; class PLANUI_EXPORT ResourceAllocationEditor : public ViewBase { Q_OBJECT public: ResourceAllocationEditor(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const override { return m_view->project(); } void setProject(Project *project) override { m_view->setProject(project); } ResourceAllocationItemModel *model() const { return m_view->model(); } void updateReadWrite(bool readwrite) override; Resource *currentResource() const override; - ResourceGroup *currentResourceGroup() const override; /// Loads context info into this view. Reimplement. bool loadContext(const KoXmlElement &/*context*/) override; /// Save context info from this view. Reimplement. void saveContext(QDomElement &/*context*/) const override; KoPrintJob *createPrintJob() override; public Q_SLOTS: /// Activate/deactivate the gui void setGuiActive(bool activate) override; protected Q_SLOTS: void slotOptions() override; protected: void updateActionsEnabled(bool on = true); private Q_SLOTS: void slotContextMenuRequested(const QModelIndex &index, const QPoint& pos); void slotSplitView(); void slotSelectionChanged(const QModelIndexList&); void slotCurrentChanged(const QModelIndex&); void slotEnableActions(bool on); private: ResourceAllocationTreeView *m_view; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kpttaskdialog.cpp b/src/libs/ui/kpttaskdialog.cpp index 762b2a01..15d88929 100644 --- a/src/libs/ui/kpttaskdialog.cpp +++ b/src/libs/ui/kpttaskdialog.cpp @@ -1,238 +1,227 @@ /* This file is part of the KDE project Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk Copyright (C) 2004 -2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskdialog.h" #include "kpttaskcostpanel.h" #include "kpttaskgeneralpanel.h" #include "kptrequestresourcespanel.h" -#include "GroupAllocationPanel.h" #include "kptdocumentspanel.h" #include "kpttaskdescriptiondialog.h" #include "kptcommand.h" #include "kptnode.h" #include "kpttask.h" #include "kptproject.h" #include #include namespace KPlato { TaskDialog::TaskDialog(Project &project, Task &task, Accounts &accounts, QWidget *p) : KPageDialog(p), m_project(project), m_node(&task) { setWindowTitle(i18n("Task Settings")); setFaceType(KPageDialog::Tabbed); KoVBox *page; // Create all the tabs. page = new KoVBox(); addPage(page, i18n("&General")); m_generalTab = new TaskGeneralPanel(project, task, page); page = new KoVBox(); addPage(page, i18n("&Resources")); m_resourcesTab = new RequestResourcesPanel(page, project, task); - page = new KoVBox(); - addPage(page, i18n("&Groups")); - m_groupsTab = new GroupAllocationPanel(page, project, task); - page = new KoVBox(); addPage(page, i18n("&Documents")); m_documentsTab = new DocumentsPanel(task, page); page = new KoVBox(); addPage(page, i18n("&Cost")); m_costTab = new TaskCostPanel(task, accounts, page); page = new KoVBox(); addPage(page, i18n("D&escription")); m_descriptionTab = new TaskDescriptionPanel(task, page); m_descriptionTab->namefield->hide(); m_descriptionTab->namelabel->hide(); setButtonOkEnabled(false); connect(this, &KPageDialog::currentPageChanged, this, &TaskDialog::slotCurrentChanged); connect(m_generalTab, &TaskGeneralPanelImpl::obligatedFieldsFilled, this, &TaskDialog::setButtonOkEnabled); connect(m_resourcesTab, &RequestResourcesPanel::changed, m_generalTab, &TaskGeneralPanelImpl::checkAllFieldsFilled); - connect(m_groupsTab, &GroupAllocationPanel::changed, m_generalTab, &TaskGeneralPanelImpl::checkAllFieldsFilled); connect(m_documentsTab, &DocumentsPanel::changed, m_generalTab, &TaskGeneralPanelImpl::checkAllFieldsFilled); connect(m_costTab, &TaskCostPanelImpl::changed, m_generalTab, &TaskGeneralPanelImpl::checkAllFieldsFilled); connect(m_descriptionTab, &TaskDescriptionPanelImpl::textChanged, m_generalTab, &TaskGeneralPanelImpl::checkAllFieldsFilled); connect(&project, &Project::nodeRemoved, this, &TaskDialog::slotTaskRemoved); } void TaskDialog::setButtonOkEnabled(bool enabled) { buttonBox()->button(QDialogButtonBox::Ok)->setEnabled(enabled); } void TaskDialog::slotCurrentChanged(KPageWidgetItem *current, KPageWidgetItem */*prev*/) { //debugPlan<widget()<parent(); // HACK: KPageDialog grabs focus when a tab is clicked. // KRichTextWidget still flashes the caret so the user thinks it has the focus. // For now, just give the KRichTextWidget focus. if (current->widget() == m_descriptionTab->parent()) { m_descriptionTab->descriptionfield->setFocus(); } } void TaskDialog::slotTaskRemoved(Node *node) { if (node == m_node) { reject(); } } MacroCommand *TaskDialog::buildCommand() { MacroCommand *m = new MacroCommand(kundo2_i18n("Modify task")); bool modified = false; MacroCommand *cmd = m_generalTab->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } cmd = m_resourcesTab->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } - cmd = m_groupsTab->buildCommand(); - if (cmd) { - m->addCommand(cmd); - modified = true; - } cmd = m_documentsTab->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } cmd = m_costTab->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } cmd = m_descriptionTab->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } if (!modified) { delete m; return 0; } return m; } void TaskDialog::accept() { if (!m_generalTab->ok()) return; if (!m_resourcesTab->ok()) return; if (!m_descriptionTab->ok()) return; KPageDialog::accept(); } //--------------------------- TaskAddDialog::TaskAddDialog(Project &project, Task &task, Node *currentNode, Accounts &accounts, QWidget *p) : TaskDialog(project, task, accounts, p) { m_currentnode = currentNode; // do not know wbs code yet m_generalTab->hideWbs(); connect(&project, &Project::nodeRemoved, this, &TaskAddDialog::slotNodeRemoved); } TaskAddDialog::~TaskAddDialog() { delete m_node; // in case of cancel } void TaskAddDialog::slotNodeRemoved(Node *node) { if (m_currentnode == node) { reject(); } } MacroCommand *TaskAddDialog::buildCommand() { MacroCommand *c = new MacroCommand(kundo2_i18n("Add task")); c->addCommand(new TaskAddCmd(&m_project, m_node, m_currentnode)); MacroCommand *m = TaskDialog::buildCommand(); if (m) { c->addCommand(m); } m_node = 0; // don't delete task return c; } //--------------------------- SubTaskAddDialog::SubTaskAddDialog(Project &project, Task &task, Node *currentNode, Accounts &accounts, QWidget *p) : TaskDialog(project, task, accounts, p) { m_currentnode = currentNode; // do not know wbs code yet m_generalTab->hideWbs(); connect(&project, &Project::nodeRemoved, this, &SubTaskAddDialog::slotNodeRemoved); } SubTaskAddDialog::~SubTaskAddDialog() { delete m_node; // in case of cancel } void SubTaskAddDialog::slotNodeRemoved(Node *node) { if (m_currentnode == node) { reject(); } } MacroCommand *SubTaskAddDialog::buildCommand() { KUndo2MagicString s = kundo2_i18n("Add sub-task"); if (m_currentnode == 0) { s = kundo2_i18n("Add task"); // it will be added to project } MacroCommand *c = new MacroCommand(s); c->addCommand(new SubtaskAddCmd(&m_project, m_node, m_currentnode)); MacroCommand *m = TaskDialog::buildCommand(); if (m) { c->addCommand(m); } m_node = 0; // don't delete task return c; } } //KPlato namespace diff --git a/src/libs/ui/kpttaskdialog.h b/src/libs/ui/kpttaskdialog.h index 7496d93c..f57c49a7 100644 --- a/src/libs/ui/kpttaskdialog.h +++ b/src/libs/ui/kpttaskdialog.h @@ -1,112 +1,110 @@ /* This file is part of the KDE project Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk Copyright (C) 2004 -2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASKDIALOG_H #define KPTTASKDIALOG_H #include "planui_export.h" #include namespace KPlato { class Accounts; class TaskGeneralPanel; class RequestResourcesPanel; -class GroupAllocationPanel; class DocumentsPanel; class TaskCostPanel; class TaskDescriptionPanel; class Node; class Task; class Project; class MacroCommand; /** * The dialog that shows and allows you to alter any task. */ class PLANUI_EXPORT TaskDialog : public KPageDialog { Q_OBJECT public: /** * The constructor for the task settings dialog. * @param project the project to show * @param task the task to show * @param accounts all defined accounts * @param parent parent widget */ TaskDialog(Project &project, Task &task, Accounts &accounts, QWidget *parent=0); virtual MacroCommand *buildCommand(); protected Q_SLOTS: void accept() override; void setButtonOkEnabled(bool enabled); void slotTaskRemoved(KPlato::Node *node); void slotCurrentChanged(KPageWidgetItem*, KPageWidgetItem*); protected: Project &m_project; Node *m_node; TaskGeneralPanel *m_generalTab; RequestResourcesPanel *m_resourcesTab; - GroupAllocationPanel *m_groupsTab; DocumentsPanel *m_documentsTab; TaskCostPanel *m_costTab; TaskDescriptionPanel *m_descriptionTab; }; class PLANUI_EXPORT TaskAddDialog : public TaskDialog { Q_OBJECT public: TaskAddDialog(Project &project, Task &task, Node *currentNode, Accounts &accounts, QWidget *parent=0); ~TaskAddDialog() override; MacroCommand *buildCommand() override; protected Q_SLOTS: void slotNodeRemoved(KPlato::Node*); private: Node *m_currentnode; }; class PLANUI_EXPORT SubTaskAddDialog : public TaskDialog { Q_OBJECT public: SubTaskAddDialog(Project &project, Task &task, Node *currentNode, Accounts &accounts, QWidget *parent=0); ~SubTaskAddDialog() override; MacroCommand *buildCommand() override; protected Q_SLOTS: void slotNodeRemoved(KPlato::Node*); private: Node *m_currentnode; }; } //KPlato namespace #endif // TASKDIALOG_H diff --git a/src/libs/ui/kpttaskprogresspanel.cpp b/src/libs/ui/kpttaskprogresspanel.cpp index 42b4d5f1..29a43375 100644 --- a/src/libs/ui/kpttaskprogresspanel.cpp +++ b/src/libs/ui/kpttaskprogresspanel.cpp @@ -1,437 +1,435 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007, 2012 Dag Andersen Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskprogresspanel.h" #include "kptusedefforteditor.h" #include #include #include #include #include "kpttask.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptresource.h" #include "kptdurationspinbox.h" #include "kptschedule.h" #include "kptproject.h" #include "kptdebug.h" using namespace KPlato; int toEditMode(Completion::Entrymode m) { const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask << Completion::EnterCompleted; return qBound(0, modes.indexOf(m), 1); } Completion::Entrymode fromEditMode(int m) { const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask; return modes.value(m); } //----------------- TaskProgressPanel::TaskProgressPanel(Task &task, ScheduleManager *sm, StandardWorktime *workTime, QWidget *parent) : TaskProgressPanelImpl(task, parent) { Q_UNUSED(workTime); debugPlan; started->setChecked(m_completion.isStarted()); finished->setChecked(m_completion.isFinished()); startTime->setDateTime(m_completion.startTime()); finishTime->setDateTime(m_completion.finishTime()); finishTime->setMinimumDateTime(qMax(startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime))); scheduledEffort = task.estimate()->expectedValue(); setYear(QDate::currentDate().year()); if (m_completion.usedEffortMap().isEmpty() || m_task.requests().isEmpty()) { - foreach (ResourceGroupRequest *g, m_task.requests().requests()) { - foreach (ResourceRequest *r, g->resourceRequests()) { - m_completion.addUsedEffort(r->resource()); - } + for (ResourceRequest *r : task.requests().resourceRequests()) { + m_completion.addUsedEffort(r->resource()); } } enableWidgets(); started->setFocus(); connect(weekNumber, SIGNAL(currentIndexChanged(int)), SLOT(slotWeekNumberChanged(int))); connect(ui_resourceCombo, SIGNAL(activated(QString)), resourceTable->model(), SLOT(addResource(QString))); connect(addEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::addEntry); connect(removeEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::removeEntry); entryTable->model()->setManager(sm); entryTable->model()->setTask(&task); entryTable->setCompletion(&m_completion); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanel::slotEntryAdded); connect(entryTable, &CompletionEntryEditor::rowRemoved, this, &TaskProgressPanel::slotEntryRemoved); resourceTable->setProject(static_cast(task.projectNode())); resourceTable->setCompletion(&m_completion); slotWeekNumberChanged(weekNumber->currentIndex()); updateResourceCombo(); connect(resourceTable->model(), &UsedEffortItemModel::rowInserted, this, &TaskProgressPanelImpl::updateResourceCombo); //resourceTable->resizeColumnsToContents(); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotStartedChanged); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotFinishedChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotStartTimeChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotFinishTimeChanged); finishTime->setToolTip(xi18nc("@info:tooltip", "Select the tasks finish time")); finished->setToolTip(xi18nc("@info:tooltip", "Finish the task at the selected time")); } MacroCommand *TaskProgressPanel::buildCommand() { Project *project = dynamic_cast(m_task.projectNode()); if (project == 0) { return 0; } return buildCommand(*project, m_original, m_completion); } MacroCommand *TaskProgressPanel::buildCommand(const Project &project, Completion &org, Completion &curr) { MacroCommand *cmd = 0; KUndo2MagicString c = kundo2_i18n("Modify task completion"); if (org.entrymode() != curr.entrymode()) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new ModifyCompletionEntrymodeCmd(org, curr.entrymode())); } if (org.startTime() != curr.startTime()) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new ModifyCompletionStartTimeCmd(org, curr.startTime())); } if (org.finishTime() != curr.finishTime()) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new ModifyCompletionFinishTimeCmd(org, curr.finishTime())); } if (org.isStarted() != curr.isStarted()) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new ModifyCompletionStartedCmd(org, curr.isStarted())); } if (org.isFinished() != curr.isFinished()) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new ModifyCompletionFinishedCmd(org, curr.isFinished())); } QList orgdates = org.entries().keys(); QList currdates = curr.entries().keys(); foreach (const QDate &d, orgdates) { if (currdates.contains(d)) { if (curr.entry(d) == org.entry(d)) { continue; } if (cmd == 0) cmd = new MacroCommand(c); debugPlan<<"modify entry "<addCommand(new ModifyCompletionEntryCmd(org, d, e)); } else { if (cmd == 0) cmd = new MacroCommand(c); debugPlan<<"remove entry "<addCommand(new RemoveCompletionEntryCmd(org, d)); } } foreach (const QDate &d, currdates) { if (! orgdates.contains(d)) { if (cmd == 0) cmd = new MacroCommand(c); Completion::Entry *e = new Completion::Entry(* (curr.entry(d))); debugPlan<<"add entry "<addCommand(new AddCompletionEntryCmd(org, d, e)); } } const Completion::ResourceUsedEffortMap &map = curr.usedEffortMap(); Completion::ResourceUsedEffortMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { Resource *r = project.findResource(it.key()->id()); if (r == 0) { warnPlan<<"Can't find resource:"<id()<name(); continue; } Completion::UsedEffort *ue = map[ r ]; if (ue == 0) { continue; } if (org.usedEffort(r) == 0 || *ue != *(org.usedEffort(r))) { if (cmd == 0) cmd = new MacroCommand(c); cmd->addCommand(new AddCompletionUsedEffortCmd(org, r, new Completion::UsedEffort(*ue))); } } return cmd; } void TaskProgressPanel::slotWeekNumberChanged(int index) { debugPlan<setCurrentMonday(date); } void TaskProgressPanel::slotEntryAdded(const QDate &date) { Q_UNUSED(date) updateFinishedDateTime(); } void TaskProgressPanel::slotEntryRemoved(const QDate &date) { Q_UNUSED(date) updateFinishedDateTime(); } //------------------------------------- TaskProgressPanelImpl::TaskProgressPanelImpl(Task &task, QWidget *parent) : QWidget(parent), m_task(task), m_original(task.completion()), m_completion(m_original), m_firstIsPrevYear(false), m_lastIsNextYear(false) { setupUi(this); taskName->setText(task.name()); connect(entryTable, &CompletionEntryEditor::selectedItemsChanged, this, &TaskProgressPanelImpl::slotSelectionChanged); removeEntryBtn->setEnabled(false); editmode->setCurrentIndex(toEditMode(m_original.entrymode())); connect(editmode, SIGNAL(currentIndexChanged(int)), SLOT(slotEditmodeChanged(int))); connect(editmode, SIGNAL(activated(int)), SLOT(slotChanged())); connect(resourceTable, &UsedEffortEditor::changed, this, &TaskProgressPanelImpl::slotChanged); connect(resourceTable, &UsedEffortEditor::resourceAdded, this, &TaskProgressPanelImpl::slotChanged); connect(resourceTable->model(), &UsedEffortItemModel::effortChanged, this, &TaskProgressPanelImpl::slotEffortChanged); connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotChanged); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotChanged); connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotEntryChanged); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotEntryChanged); connect(entryTable, &CompletionEntryEditor::rowRemoved, this, &TaskProgressPanelImpl::slotEntryChanged); connect(prevWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotPrevWeekBtnClicked); connect(nextWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotNextWeekBtnClicked); connect (ui_year, SIGNAL(valueChanged(int)), SLOT(slotFillWeekNumbers(int))); int y = 0; int wn = QDate::currentDate().weekNumber(&y); setYear(y); weekNumber->setCurrentIndex(wn - m_weekOffset); } void TaskProgressPanelImpl::updateFinishedDateTime() { finishTime->setMinimumDateTime(qMax(startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime))); } void TaskProgressPanelImpl::slotEffortChanged(const QDate &date) { if (date.isValid()) { entryTable->insertEntry(date); } } void TaskProgressPanelImpl::slotChanged() { emit changed(); } void TaskProgressPanelImpl::slotEditmodeChanged(int idx) { m_completion.setEntrymode(fromEditMode(idx)); entryTable->model()->slotDataChanged(); enableWidgets(); } void TaskProgressPanelImpl::slotStartedChanged(bool state) { m_completion.setStarted(state); if (state) { QTime t = QTime::currentTime(); t.setHMS(t.hour(), t.minute(), 0); m_completion.setStartTime(QDateTime(QDate::currentDate(), t, Qt::LocalTime)); startTime->setDateTime(m_completion.startTime()); slotCalculateEffort(); } enableWidgets(); } void TaskProgressPanelImpl::setFinished() { const QDateTime dt = finishTime->dateTime(); m_completion.setPercentFinished(dt.date(), 100); m_completion.setRemainingEffort(dt.date(), Duration::zeroDuration); entryTable->setCompletion(&m_completion); // for refresh updateFinishedDateTime(); } void TaskProgressPanelImpl::slotFinishedChanged(bool state) { debugPlan<dateTime(); } enableWidgets(); } void TaskProgressPanelImpl::slotFinishTimeChanged(const QDateTime &dt) { qInfo()<blockSignals(true); ui_resourceCombo->clear(); ui_resourceCombo->addItems(resourceTable->model()->freeResources().keys()); ui_resourceCombo->blockSignals(false); } void TaskProgressPanelImpl::enableWidgets() { editmode->setEnabled(!finished->isChecked()); started->setEnabled(!finished->isChecked()); finished->setEnabled(started->isChecked()); finishTime->setEnabled(!finished->isChecked()); startTime->setEnabled(started->isChecked() && !finished->isChecked()); addEntryBtn->setEnabled(started->isChecked() && !finished->isChecked()); removeEntryBtn->setEnabled(! entryTable->selectionModel()->selectedIndexes().isEmpty() && started->isChecked() && ! finished->isChecked()); ui_resourceFrame->setVisible(m_completion.entrymode() == Completion::EnterEffortPerResource); resourceTable->model()->setReadOnly((! started->isChecked()) || finished->isChecked() || editmode->currentIndex() != 0); } void TaskProgressPanelImpl::slotPercentFinishedChanged(int) { slotCalculateEffort(); } void TaskProgressPanelImpl::slotCalculateEffort() { } void TaskProgressPanelImpl::slotPrevWeekBtnClicked() { debugPlan; int i = weekNumber->currentIndex(); if (i == 0) { debugPlan<value() - 1); if (m_lastIsNextYear) { decr = 2; } weekNumber->setCurrentIndex(weekNumber->count() - decr); } else { weekNumber->setCurrentIndex(i - 1); } } void TaskProgressPanelImpl::slotNextWeekBtnClicked() { int i = weekNumber->currentIndex(); debugPlan<count(); if (i == weekNumber->count() - 1) { debugPlan<value() + 1); if (m_firstIsPrevYear) { index = 1; } weekNumber->setCurrentIndex(index); } else { weekNumber->setCurrentIndex(i + 1); } } void TaskProgressPanelImpl::setYear(int year) { debugPlan; ui_year->setValue(year); } void TaskProgressPanelImpl::slotFillWeekNumbers(int year) { debugPlan; weekNumber->clear(); m_year = year; m_weekOffset = 1; int y = 0; QDate date(year, 1, 1); int wn = date.weekNumber(&y); m_firstIsPrevYear = false; debugPlan<addItem(i18nc("Week number (year)", "Week %1 (%2)", wn, y)); m_weekOffset = 0; m_firstIsPrevYear = true; debugPlan<<"Added last week of prev year"; } for (int i=1; i <= 52; ++i) { weekNumber->addItem(i18nc("Week number", "Week %1", i)); } date = QDate(year, 12, 31); wn = date.weekNumber(&y); debugPlan<addItem(i18nc("Week number", "Week %1", wn)); } else if (wn == 1) { weekNumber->addItem(i18nc("Week number (year)", "Week %1 (%2)", wn, y)); m_lastIsNextYear = true; } } void TaskProgressPanelImpl::slotSelectionChanged(const QItemSelection &sel) { removeEntryBtn->setEnabled(! sel.isEmpty() && started->isChecked() && ! finished->isChecked()); } diff --git a/src/plugins/filters/planner/import/plannerimport.cpp b/src/plugins/filters/planner/import/plannerimport.cpp index 78422821..af1563c2 100644 --- a/src/plugins/filters/planner/import/plannerimport.cpp +++ b/src/plugins/filters/planner/import/plannerimport.cpp @@ -1,553 +1,548 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "plannerimport.h" #include "kptproject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPlato; #define PLANNERIMPORT_LOG "calligra.plan.filter.planner.import" #define debugPlannerImport qCDebug(QLoggingCategory(PLANNERIMPORT_LOG))<();) PlannerImport::PlannerImport(QObject* parent, const QVariantList &) : KoFilter(parent) { } KoFilter::ConversionStatus PlannerImport::convert(const QByteArray& from, const QByteArray& to) { debugPlannerImport << from << to; if ((from != "application/x-planner") || (to != "application/x-vnd.kde.plan")) { return KoFilter::NotImplemented; } QFile in(m_chain->inputFile()); if (!in.open(QIODevice::ReadOnly)) { errorPlannerImport << "Unable to open input file!"; in.close(); return KoFilter::FileNotFound; } QDomDocument inDoc; if (!inDoc.setContent(&in)) { errorPlannerImport << "Invalid format in input file!"; in.close(); return KoFilter::InvalidFormat; } KoDocument *part = 0; bool batch = false; if (m_chain->manager()) { batch = m_chain->manager()->getBatchMode(); } if (batch) { //TODO debugPlannerImport << "batch"; } else { //debugPlannerImport<<"online"; part = m_chain->outputDocument(); } if (part == 0 || part->project() == 0) { errorPlannerImport << "Cannot open document"; return KoFilter::InternalError; } if (!loadPlanner(inDoc, part)) { return KoFilter::ParsingError; } return KoFilter::OK; } DateTime toDateTime(const QString &dts) { // NOTE: time ends in Z, so should be UTC, but it seems it is in local time anyway. // Atm, just ignore timezone const QString format = QString("yyyyMMddThhmmssZ"); return DateTime(QDateTime::fromString(dts, format)); } // bool loadProject(const QDomElement &el, Project &project) { ScheduleManager *sm = project.createScheduleManager("Planner"); project.addScheduleManager(sm); sm->createSchedules(); sm->setAllowOverbooking(true); sm->expected()->setScheduled(true); project.setCurrentSchedule(sm->scheduleId()); project.setName(el.attribute("name")); project.setLeader(el.attribute("manager")); DateTime dt = toDateTime(el.attribute("project-start")); if (dt.isValid()) { project.setConstraintStartTime(dt); project.setStartTime(dt); } if (el.hasAttribute("calendar")) { Calendar *c = new Calendar(); c->setId(el.attribute("calendar")); project.addCalendar(c); project.setDefaultCalendar(c); debugPlannerImport<<"Added default calendar:"< // // // // // // // // // // // // interval start="0900" end="1700"/> // /overridden-day-type> // /overridden-day-types> // days/> // /calendar> // calendar id="2" name="Standard"> // default-week mon="0" tue="0" wed="0" thu="0" fri="0" sat="1" sun="1"/> // overridden-day-types> // overridden-day-type id="0"> // interval start="0800" end="1200"/> // interval start="1300" end="1700"/> // /overridden-day-type> // /overridden-day-types> // days/> // calendar id="3" name="Min kalender"> // // overridden-day-types> // overridden-day-type id="3"> // interval start="0800" end="1600"/> // /overridden-day-type> // /overridden-day-types> // days> // day date="20190506" type="day-type" id="3"/> // /days> // /calendar> // /calendar> // CalendarDay::State toDayState(int type) { QList state = QList() << CalendarDay::Working << CalendarDay::NonWorking; if (type < state.count()) { return state.value(type); } return CalendarDay::Undefined; } bool loadWeek(const QDomElement &el, Calendar *calendar) { debugPlannerImport<name(); QList defaultWeek = QList() << 2 << 2 << 2 << 2 << 2 << 2 << 2; QDomElement wel; forEachChildElementWithTag(wel, el, "default-week") { defaultWeek[0] = wel.attribute("mon", "2").toInt(); defaultWeek[1] = wel.attribute("tue", "2").toInt(); defaultWeek[2] = wel.attribute("wed", "2").toInt(); defaultWeek[3] = wel.attribute("thu", "2").toInt(); defaultWeek[4] = wel.attribute("fri", "2").toInt(); defaultWeek[5] = wel.attribute("sat", "2").toInt(); defaultWeek[6] = wel.attribute("sun", "2").toInt(); } debugPlannerImport<weekday(i+1); day->setState(toDayState(defaultWeek.at(i))); } forEachChildElementWithTag(wel, el, "overridden-day-types") { QDomElement oel; forEachChildElementWithTag(oel, wel, "overridden-day-type") { if (oel.hasAttribute("id")) { int id = oel.attribute("id").toInt(); if (!defaultWeek.contains(id)) { continue; } for (int i = 0; i < defaultWeek.count(); ++i) { if (defaultWeek.at(i) != id) { continue; } CalendarDay *day = calendar->weekday(i+1); day->setState(CalendarDay::Working); QDomElement iel; forEachChildElementWithTag(iel, oel, "interval") { QTime start = QTime::fromString(iel.attribute("start"), "hhmm"); QTime end = QTime::fromString(iel.attribute("end"), "hhmm"); day->addInterval(TimeInterval(start, start.msecsTo(end))); debugPlannerImport<<"Overridden:"<"<addInterval(TimeInterval(start, start.msecsTo(end))); } calendar->addDay(day); } return true; } bool loadCalendars(const QDomElement &el, Project &project, Calendar *parent = 0) { QDomElement cel; forEachChildElementWithTag(cel, el, "calendar") { QString id = cel.attribute("id"); Calendar *calendar = project.findCalendar(id); if (!calendar) { calendar = new Calendar(); calendar->setId(cel.attribute("id")); project.addCalendar(calendar, parent); debugPlannerImport<<"Loading new calendar"<id(); } else debugPlannerImport<<"Loading default calendar"<id(); calendar->setName(cel.attribute("name")); loadWeek(cel, calendar); loadDays(cel, calendar); loadCalendars(cel, project, calendar); } return true; } // // // bool loadResourceGroups(const QDomElement &el, Project &project) { QDomNodeList lst = el.elementsByTagName("group"); QDomElement gel; forEachElementInList(gel, lst) { ResourceGroup *g = new ResourceGroup(); g->setId(gel.attribute("id")); g->setName(gel.attribute("name")); project.addResourceGroup(g); } return true; } // // // Resource::Type toResourceType(const QString &type) { QMap types; types["0"] = Resource::Type_Material; types["1"] = Resource::Type_Work; return types.contains(type) ? types[type] : Resource::Type_Work; } bool loadResources(const QDomElement &el, Project &project) { QDomNodeList lst = el.elementsByTagName("resource"); QDomElement rel; forEachElementInList(rel, lst) { Resource *r = new Resource(); r->setId(rel.attribute("id")); r->setName(rel.attribute("name")); r->setInitials(rel.attribute("short-name")); r->setEmail(rel.attribute("email")); r->setType(toResourceType(rel.attribute("type"))); int units = rel.attribute("units", "0").toInt(); if (units == 0) { // atm. planner saves 0 but assumes 100% units = 100; } r->setUnits(units); r->setNormalRate(rel.attribute("std-rate").toDouble()); r->setCalendar(project.findCalendar(rel.attribute("calendar"))); QString gid = rel.attribute("group"); ResourceGroup *g = project.group(gid); if (!g) { // add a default g = new ResourceGroup(); g->setId(gid); // FIXME handle: gid *should* be empty if group was not found g->setName(i18n("Resources")); project.addResourceGroup(g); } project.addResource(r); g->addResource(-1, r, nullptr); } return true; } Estimate::Type toEstimateType(const QString type) { Estimate::Type res = Estimate::Type_Effort; if (type == "fixed-work") { res = Estimate::Type_Effort; } else if (type == "fixed-duration") { res = Estimate::Type_Duration; } return res; } Node::ConstraintType toConstraintType(const QString &type) { Node::ConstraintType res = Node::ASAP; if (type == "must-start-on") { res = Node::MustStartOn; } else if (type == "start-no-earlier-than") { res = Node::StartNotEarlier; } return res; } bool loadConstraint(const QDomElement &el, Task *t) { QDomElement cel; forEachChildElementWithTag(cel, el, "constraint") { t->setConstraint(toConstraintType(cel.attribute("type"))); t->setConstraintStartTime(toDateTime(cel.attribute("time"))); } return true; } // bool loadTasks(const QDomElement &el, Project &project, Node *parent = 0) { QDomElement cel; forEachChildElementWithTag(cel, el, "task") { Task *t = project.createTask(); t->setId(cel.attribute("id", t->id())); t->setName(cel.attribute("name")); t->setDescription(cel.attribute("note")); loadConstraint(cel, t); t->estimate()->setType(toEstimateType(cel.attribute("scheduling"))); t->estimate()->setExpectedEstimate(Duration(cel.attribute("work", "0").toDouble(), Duration::Unit_s).toDouble()); project.addSubTask(t, parent); long sid = project.scheduleManagers().first()->scheduleId(); NodeSchedule *sch = new NodeSchedule(); sch->setId(sid); sch->setNode(t); t->addSchedule(sch); sch->setParent(t->parentNode()->currentSchedule()); t->setCurrentSchedule(sid); const QString format = QString("yyyyMMddThhmmssZ"); QDateTime start = QDateTime::fromString(cel.attribute("work-start"), format); QDateTime end = QDateTime::fromString(cel.attribute("end"), format); t->setStartTime(DateTime(start)); t->setEndTime(DateTime(end)); sch->setScheduled(true); debugPlannerImport<<"Loaded:"< types; types["FS"] = Relation::FinishStart; types["FF"] = Relation::FinishFinish; types["SS"] = Relation::StartStart; types["SF"] = Relation::FinishStart; // not supported, use default return types.value(type); } // // // bool loadDependencies(const QDomElement &el, Project &project) { QDomElement cel; forEachChildElementWithTag(cel, el, "task") { QString succid = cel.attribute("id"); Node *child = project.findNode(succid); if (!child) { warnPlannerImport<<"Task"< bool loadAllocations(const QDomElement &el, Project &project) { QDomNodeList lst = el.elementsByTagName("allocation"); QDomElement pel; forEachElementInList(pel, lst) { Task *t = dynamic_cast(project.findNode(pel.attribute("task-id"))); Resource *r = project.findResource(pel.attribute("resource-id")); if (!t || !r) { warnPlannerImport<<"Could not find task/resource:"<resourceGroupRequest(r->parentGroups().value(0)); - if (!gr) { - gr = new ResourceGroupRequest(r->parentGroups().value(0)); - t->addRequest(gr); - } ResourceRequest *rr = new ResourceRequest(r); rr->setUnits(pel.attribute("units").toInt()); - t->requests().addResourceRequest(rr, gr); + t->requests().addResourceRequest(rr); // do assignments Calendar *calendar = r->calendar(); if (!calendar) { warnPlannerImport<<"No resource calendar:"<currentSchedule(); Schedule *rs = r->schedule(ts->id()); if (!rs) { rs = r->createSchedule(t->name(), t->type(), ts->id()); } r->setCurrentSchedulePtr(rs); AppointmentIntervalList apps = calendar->workIntervals(t->startTime(), t->endTime(), rr->units()); foreach (const AppointmentInterval &a, apps.map()) { r->addAppointment(ts, a.startTime(), a.endTime(), a.load()); } rs->setScheduled(true); debugPlannerImport<<"Assignments:"<appointmentIntervals().intervals(); } return true; } bool PlannerImport::loadPlanner(const QDomDocument &in, KoDocument *doc) const { QDomElement pel = in.documentElement(); if (pel.tagName() != "project") { errorPlannerImport << "Missing project element"; return false; } Project &project = *doc->project(); if (!loadProject(pel, project)) { return false; } QDomElement el = pel.elementsByTagName("calendars").item(0).toElement(); if (el.isNull()) { debugPlannerImport << "No calendars element"; } loadCalendars(el, project); el = pel.elementsByTagName("resource-groups").item(0).toElement(); if (el.isNull()) { debugPlannerImport << "No resource-groups element"; } loadResourceGroups(el, project); el = pel.elementsByTagName("resources").item(0).toElement(); if (el.isNull()) { debugPlannerImport << "No resources element"; } loadResources(el, project); el = pel.elementsByTagName("tasks").item(0).toElement(); if (el.isNull()) { debugPlannerImport << "No tasks element"; } else { loadTasks(el, project); loadDependencies(el, project); } loadAllocations(pel, project); foreach(const Node *n, project.allNodes()) { if (n->endTime() > project.endTime()) { project.setEndTime(n->endTime()); } } return true; } #include "plannerimport.moc" diff --git a/src/plugins/schedulers/tj/PlanTJScheduler.cpp b/src/plugins/schedulers/tj/PlanTJScheduler.cpp index f8969c0d..3a0c3a7a 100644 --- a/src/plugins/schedulers/tj/PlanTJScheduler.cpp +++ b/src/plugins/schedulers/tj/PlanTJScheduler.cpp @@ -1,922 +1,925 @@ /* This file is part of the KDE project * Copyright (C) 2009, 2010, 2011, 2012 Dag Andersen * Copyright (C) 2016 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "PlanTJScheduler.h" #include "kptproject.h" #include "kptschedule.h" #include "kptresource.h" #include "kpttask.h" #include "kptrelation.h" #include "kptduration.h" #include "kptcalendar.h" #include "kptdebug.h" #include "taskjuggler/taskjuggler.h" #include "taskjuggler/Project.h" #include "taskjuggler/Scenario.h" #include "taskjuggler/Resource.h" #include "taskjuggler/Task.h" #include "taskjuggler/Interval.h" #include "taskjuggler/Allocation.h" #include "taskjuggler/Utility.h" #include "taskjuggler/UsageLimits.h" #include "taskjuggler/CoreAttributes.h" #include "taskjuggler/TjMessageHandler.h" #include "taskjuggler/debug.h" #include #include #include #include #include #include #include #include #define PROGRESS_MAX_VALUE 100 PlanTJScheduler::PlanTJScheduler(Project *project, ScheduleManager *sm, ulong granularity, QObject *parent) : SchedulerThread(project, sm, parent), result(-1), m_schedule(0), m_recalculate(false), m_usePert(false), m_backward(false), m_granularity(granularity) { qInfo()<getType() == CA_Task && m_taskmap.contains(static_cast(object))) { log = Schedule::Log(static_cast(m_taskmap[ static_cast(object) ]), type, msg); } else if (object && object->getType() == CA_Resource && m_resourcemap.contains(static_cast(object))) { log = Schedule::Log(0, m_resourcemap[ static_cast(object) ], type, msg); } else if (object && ! object->getName().isEmpty()) { log = Schedule::Log(static_cast(m_project), type, QString("%1: %2").arg(object->getName()).arg(msg)); } else { log = Schedule::Log(static_cast(m_project), type, msg); } slotAddLog(log); } void PlanTJScheduler::run() { if (m_haltScheduling) { deleteLater(); return; } if (m_stopScheduling) { return; } setMaxProgress(PROGRESS_MAX_VALUE); { // mutex --> m_projectMutex.lock(); m_managerMutex.lock(); m_project = new Project(); loadProject(m_project, m_pdoc); m_project->setName("Schedule: " + m_project->name()); //Debug m_project->stopcalculation = false; m_manager = m_project->scheduleManager(m_mainmanagerId); qInfo()<expected()); Q_ASSERT(m_manager != m_mainmanager); Q_ASSERT(m_manager->scheduleId() == m_mainmanager->scheduleId()); Q_ASSERT(m_manager->expected() != m_mainmanager->expected()); m_manager->setName("Schedule: " + m_manager->name()); //Debug m_schedule = m_manager->expected(); bool x = connect(m_manager, SIGNAL(sigLogAdded(KPlato::Schedule::Log)), this, SLOT(slotAddLog(KPlato::Schedule::Log))); Q_ASSERT(x); Q_UNUSED(x); m_project->initiateCalculation(*m_schedule); m_project->initiateCalculationLists(*m_schedule); m_usePert = m_manager->usePert(); m_recalculate = m_manager->recalculate(); if (m_recalculate) { m_backward = false; } else { m_backward = m_manager->schedulingDirection(); } m_project->setCurrentSchedule(m_manager->expected()->id()); m_schedule->setPhaseName(0, xi18nc("@info/plain" , "Init")); QLocale locale; KFormat format(locale); if (! m_backward) { logDebug(m_project, 0, QString("Schedule project using TJ Scheduler, starting at %1, granularity %2").arg(QDateTime::currentDateTime().toString()).arg(format.formatDuration(m_granularity)), 0); if (m_recalculate) { logInfo(m_project, 0, xi18nc("@info/plain" , "Re-calculate project from start time: %1", locale.toString(m_project->constraintStartTime(), QLocale::ShortFormat)), 0); } else { logInfo(m_project, 0, xi18nc("@info/plain" , "Schedule project from start time: %1", locale.toString(m_project->constraintStartTime(), QLocale::ShortFormat)), 0); } logInfo(m_project, 0, xi18nc("@info/plain" , "Project target finish time: %1", locale.toString(m_project->constraintEndTime(), QLocale::ShortFormat)), 0); } else { logDebug(m_project, 0, QString("Schedule project backward using TJ Scheduler, starting at %1, granularity %2").arg(locale.toString(QDateTime::currentDateTime(), QLocale::ShortFormat)).arg(format.formatDuration(m_granularity)), 0); logInfo(m_project, 0, xi18nc("@info/plain" , "Schedule project from end time: %1", locale.toString(m_project->constraintEndTime(), QLocale::ShortFormat)), 0); } m_managerMutex.unlock(); m_projectMutex.unlock(); } // <--- mutex setProgress(2); if (! kplatoToTJ()) { result = 1; setProgress(PROGRESS_MAX_VALUE); return; } setMaxProgress(PROGRESS_MAX_VALUE); connect(m_tjProject, SIGNAL(updateProgressBar(int,int)), this, SLOT(setProgress(int))); m_schedule->setPhaseName(1, xi18nc("@info/plain" , "Schedule")); logInfo(m_project, 0, "Start scheduling", 1); bool r = solve(); if (! r) { debugPlan<<"Scheduling failed"; result = 2; logError(m_project, 0, xi18nc("@info/plain" , "Failed to schedule project")); setProgress(PROGRESS_MAX_VALUE); return; } if (m_haltScheduling) { debugPlan<<"Scheduling halted"; logInfo(m_project, 0, "Scheduling halted"); deleteLater(); return; } m_schedule->setPhaseName(2, xi18nc("@info/plain" , "Update")); logInfo(m_project, 0, "Scheduling finished, update project", 2); if (! kplatoFromTJ()) { logError(m_project, 0, "Project update failed"); } setProgress(PROGRESS_MAX_VALUE); m_schedule->setPhaseName(3, xi18nc("@info/plain" , "Finish")); } bool PlanTJScheduler::check() { DebugCtrl.setDebugMode(0); DebugCtrl.setDebugLevel(1000); return m_tjProject->pass2(true); } bool PlanTJScheduler::solve() { debugPlan<<"PlanTJScheduler::solve()"; TJ::Scenario *sc = m_tjProject->getScenario(0); if (! sc) { logError(m_project, 0, xi18nc("@info/plain" , "Failed to find scenario to schedule")); return false; } DebugCtrl.setDebugLevel(0); DebugCtrl.setDebugMode(PSDEBUG+TSDEBUG+RSDEBUG+PADEBUG); return m_tjProject->scheduleScenario(sc); } bool PlanTJScheduler::kplatoToTJ() { m_tjProject = new TJ::Project(); m_tjProject->setPriority(m_project->priority()); m_tjProject->setScheduleGranularity(m_granularity / 1000); m_tjProject->getScenario(0)->setMinSlackRate(0.0); // Do not calculate critical path m_tjProject->setNow(m_project->constraintStartTime().toTime_t()); m_tjProject->setStart(m_project->constraintStartTime().toTime_t()); m_tjProject->setEnd(m_project->constraintEndTime().toTime_t()); m_tjProject->setDailyWorkingHours(m_project->standardWorktime()->day()); // Set working days for the project, it is used for tasks with a length specification // FIXME: Plan has task specific calendars for this estimate type KPlato::Calendar *cal = m_project->defaultCalendar(); if (! cal) { m_project->calendars().value(0); } if (cal) { int days[ 7 ] = { Qt::Sunday, Qt::Monday, Qt::Tuesday, Qt::Wednesday, Qt::Thursday, Qt::Friday, Qt::Saturday }; for (int i = 0; i < 7; ++i) { CalendarDay *d = 0; for (Calendar *c = cal; c; c = c->parentCal()) { QTime t; t.start(); d = c->weekday(days[ i ]); Q_ASSERT(d); if (d == 0 || d->state() != CalendarDay::Undefined) { break; } } if (d && d->state() == CalendarDay::Working) { QList lst; foreach (const TimeInterval *ti, d->timeIntervals()) { TJ::Interval *tji = new TJ::Interval(toTJInterval(ti->startTime(), ti->endTime(),tjGranularity())); lst << tji; } m_tjProject->setWorkingHours(i, lst); qDeleteAll(lst); } } } addTasks(); setConstraints(); addDependencies(); addRequests(); addStartEndJob(); if (result != -1) { return false; } return check(); } void PlanTJScheduler::addStartEndJob() { TJ::Task *start = new TJ::Task(m_tjProject, "TJ::StartJob", "TJ::StartJob", 0, QString(), 0); start->setMilestone(true); if (! m_backward) { start->setSpecifiedStart(0, m_tjProject->getStart()); start->setPriority(999); } else { // backwards: insert a new ms before start and make start an ALAP to push all other jobs ALAP TJ::Task *bs = new TJ::Task(m_tjProject, "TJ::StartJob-B", "TJ::StartJob-B", 0, QString(), 0); bs->setMilestone(true); bs->setSpecifiedStart(0, m_tjProject->getStart()); bs->setPriority(999); bs->addPrecedes(start->getId()); start->addDepends(bs->getId()); start->setScheduling(TJ::Task::ALAP); } TJ::Task *end = new TJ::Task(m_tjProject, "TJ::EndJob", "TJ::EndJob", 0, QString(), 0); end->setMilestone(true); if (m_backward) { end->setSpecifiedEnd(0, m_tjProject->getEnd() - 1); end->setScheduling(TJ::Task::ALAP); } for (QMap::ConstIterator it = m_taskmap.constBegin(); it != m_taskmap.constEnd(); ++it) { if (it.value()->isStartNode()) { it.key()->addDepends(start->getId()); // logDebug(m_project, 0, QString("'%1' depends on: '%2'").arg(it.key()->getName()).arg(start->getName())); if (start->getScheduling() == TJ::Task::ALAP) { start->addPrecedes(it.key()->getId()); // logDebug(m_project, 0, QString("'%1' precedes: '%2'").arg(start->getName()).arg(it.key()->getName())); } } if (it.value()->isEndNode()) { end->addDepends(it.key()->getId()); if (it.key()->getScheduling() == TJ::Task::ALAP) { it.key()->addPrecedes(end->getId()); } } } } // static int PlanTJScheduler::toTJDayOfWeek(int day) { return day == 7 ? 0 : day; } // static DateTime PlanTJScheduler::fromTime_t(time_t t, const QTimeZone &tz) { return DateTime (QDateTime::fromTime_t(t).toTimeZone(tz)); } time_t PlanTJScheduler::toTJTime_t(const QDateTime &dt, ulong granularity) { int secs = QTime(0, 0, 0).secsTo(dt.time()); secs -= secs % granularity; return QDateTime(dt.date(), QTime(0, 0, 0).addSecs(secs), dt.timeZone()).toTime_t(); } // static AppointmentInterval PlanTJScheduler::fromTJInterval(const TJ::Interval &tji, const QTimeZone &tz) { AppointmentInterval a(fromTime_t(tji.getStart(), tz), fromTime_t(tji.getEnd(), tz).addSecs(1)); return a; } // static TJ::Interval PlanTJScheduler::toTJInterval(const QDateTime &start, const QDateTime &end, ulong granularity) { int secs = QTime(0, 0, 0).secsTo(start.time()); secs -= secs % granularity; QDateTime s(start.date(), QTime(0, 0, 0).addSecs(secs), start.timeZone()); secs = QTime(0, 0, 0).secsTo(end.time()); secs -= secs % granularity; QDateTime e(end.date(), QTime(0, 0, 0).addSecs(secs), end.timeZone()); TJ::Interval ti(s.toTime_t(), e.addSecs(-1).toTime_t()); return ti; } // static TJ::Interval PlanTJScheduler::toTJInterval(const QTime &start, const QTime &end, ulong granularity) { int secs = QTime(0, 0, 0).secsTo(start); time_t s = secs - (secs % granularity); secs = (end == QTime(0, 0, 0)) ? 86399 : QTime(0, 0, 0).secsTo(end); time_t e = secs - (secs % granularity) - 1; TJ::Interval ti(s, e); return ti; } ulong PlanTJScheduler::tjGranularity() const { return m_tjProject->getScheduleGranularity(); } bool PlanTJScheduler::kplatoFromTJ() { MainSchedule *cs = static_cast(m_project->currentSchedule()); QDateTime start; QDateTime end; for (QMap::ConstIterator it = m_taskmap.constBegin(); it != m_taskmap.constEnd(); ++it) { if (! taskFromTJ(it.key(), it.value())) { return false; } if (! start.isValid() || it.value()->startTime() < start) { start = it.value()->startTime(); } if (! end.isValid() || it.value()->endTime() > end) { end = it.value()->endTime(); } } m_project->setStartTime(start.isValid() ? start : m_project->constraintStartTime()); m_project->setEndTime(end.isValid() ? end : m_project->constraintEndTime()); adjustSummaryTasks(m_schedule->summaryTasks()); foreach (Task *task, m_taskmap) { calcPertValues(task); } m_project->calcCriticalPathList(m_schedule); // calculate positive float foreach (Task* t, m_taskmap) { if (! t->inCriticalPath() && t->isStartNode()) { calcPositiveFloat(t); } } QLocale locale; logInfo(m_project, 0, xi18nc("@info/plain" , "Project scheduled to start at %1 and finish at %2", locale.toString(m_project->startTime(), QLocale::ShortFormat), locale.toString(m_project->endTime(), QLocale::ShortFormat))); if (m_manager) { logDebug(m_project, 0, QString("Project scheduling finished at %1").arg(locale.toString(QDateTime::currentDateTime(), QLocale::ShortFormat))); m_project->finishCalculation(*m_manager); m_manager->scheduleChanged(cs); } return true; } bool PlanTJScheduler::taskFromTJ(TJ::Task *job, Task *task) { if (m_haltScheduling || m_manager == 0) { return true; } Schedule *cs = task->currentSchedule(); Q_ASSERT(cs); QTimeZone tz = m_project->timeZone(); debugPlan<<"taskFromTJ:"<name()<id()<getStart(0); if (s < m_tjProject->getStart() || s > m_tjProject->getEnd()) { m_project->currentSchedule()->setSchedulingError(true); cs->setSchedulingError(true); s = m_tjProject->getStart(); } time_t e = job->getEnd(0); if (job->isMilestone()) { Q_ASSERT(s = (e + 1)); e = s - 1; } else if (e <= s || e > m_tjProject->getEnd()) { m_project->currentSchedule()->setSchedulingError(true); cs->setSchedulingError(true); e = s + (8*60*60); } task->setStartTime(fromTime_t(s, tz)); task->setEndTime(fromTime_t(e + 1, tz)); task->setDuration(task->endTime() - task->startTime()); debugPlan<startTime()<<"-- "<endTime(); if (! task->startTime().isValid()) { logError(task, 0, xi18nc("@info/plain", "Invalid start time")); return false; } if (! task->endTime().isValid()) { logError(task, 0, xi18nc("@info/plain", "Invalid end time")); return false; } if (m_project->startTime() > task->startTime()) { m_project->setStartTime(task->startTime()); } if (task->endTime() > m_project->endTime()) { m_project->setEndTime(task->endTime()); } foreach (TJ::CoreAttributes *a, job->getBookedResources(0)) { TJ::Resource *r = static_cast(a); Resource *res = m_resourcemap[ r ]; const QVector lst = r->getBookedIntervals(0, job); foreach (const TJ::Interval &tji, lst) { AppointmentInterval ai = fromTJInterval(tji, tz); double load = res->type() == Resource::Type_Material ? res->units() : ai.load() * r->getEfficiency(); res->addAppointment(cs, ai.startTime(), ai.endTime(), load); logDebug(task, 0, '\'' + res->name() + "' added appointment: " + ai.startTime().toString(Qt::ISODate) + " - " + ai.endTime().toString(Qt::ISODate)); } } cs->setScheduled(true); QLocale locale; if (task->type() == Node::Type_Milestone) { logInfo(task, 0, xi18nc("@info/plain" , "Scheduled milestone: %1", locale.toString(task->startTime(), QLocale::ShortFormat))); } else { logInfo(task, 0, xi18nc("@info/plain" , "Scheduled task: %1 - %2", locale.toString(task->startTime(), QLocale::ShortFormat), locale.toString(task->endTime(), QLocale::ShortFormat))); } return true; } void PlanTJScheduler::adjustSummaryTasks(const QList &nodes) { foreach (Node *n, nodes) { adjustSummaryTasks(n->childNodeIterator()); if (n->parentNode()->type() == Node::Type_Summarytask) { DateTime pt = n->parentNode()->startTime(); DateTime nt = n->startTime(); if (! pt.isValid() || pt > nt) { n->parentNode()->setStartTime(nt); } pt = n->parentNode()->endTime(); nt = n->endTime(); if (! pt.isValid() || pt < nt) { n->parentNode()->setEndTime(nt); } } } } Duration PlanTJScheduler::calcPositiveFloat(Task *task) { if (task->positiveFloat() != 0) { return task->positiveFloat(); } Duration x; if (task->dependChildNodes().isEmpty() && task->childProxyRelations().isEmpty()) { x = m_project->endTime() - task->endTime(); } else { foreach (const Relation *r, task->dependChildNodes() + task->childProxyRelations()) { if (! r->child()->inCriticalPath()) { Duration f = calcPositiveFloat(static_cast(r->child())); if (x == 0 || f < x) { x = f; } } } } Duration totfloat = task->freeFloat() + x; task->setPositiveFloat(totfloat); return totfloat; } void PlanTJScheduler::calcPertValues(Task *t) { switch (t->constraint()) { case Node::MustStartOn: if (t->constraintStartTime() != t->startTime()) { t->setNegativeFloat(t->startTime() - t->constraintStartTime()); } break; case Node::StartNotEarlier: if (t->startTime() < t->constraintStartTime()) { t->setNegativeFloat(t->constraintStartTime() - t->startTime()); } break; case Node::MustFinishOn: if (t->constraintEndTime() != t->endTime()) { t->setNegativeFloat(t->endTime() - t->constraintEndTime()); } break; case Node::FinishNotLater: if (t->endTime() > t->constraintEndTime()) { t->setNegativeFloat(t->endTime() - t->constraintEndTime()); } break; case Node::FixedInterval: if (t->constraintStartTime() != t->startTime()) { t->setNegativeFloat(t->startTime() - t->constraintStartTime()); } else if (t->endTime() != t->constraintEndTime()) { t->setNegativeFloat(t->endTime() - t->constraintEndTime()); } break; default: break; } if (t->negativeFloat() != 0) { t->currentSchedule()->setConstraintError(true); m_project->currentSchedule()->setSchedulingError(true); logError(t, 0, i18nc("1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", t->constraintToString(true), t->negativeFloat().toString(Duration::Format_i18nHour))); } debugPlan<name()<startTime()<endTime(); Duration negativefloat; foreach (const Relation *r, t->dependParentNodes() + t->parentProxyRelations()) { if (r->parent()->endTime() + r->lag() > t->startTime()) { Duration f = r->parent()->endTime() + r->lag() - t->startTime(); if (f > negativefloat) { negativefloat = f; } } } if (negativefloat > 0) { t->currentSchedule()->setSchedulingError(true); m_project->currentSchedule()->setSchedulingError(true); logError(t, 0, xi18nc("@info/plain", "Failed to meet dependency. Negative float=%1", negativefloat.toString(Duration::Format_i18nHour))); if (t->negativeFloat() < negativefloat) { t->setNegativeFloat(negativefloat); } } Duration freefloat; foreach (const Relation *r, t->dependChildNodes() + t->childProxyRelations()) { if (t->endTime() + r->lag() < r->child()->startTime()) { Duration f = r->child()->startTime() - r->lag() - t->endTime(); if (f > 0 && (freefloat == 0 || freefloat > f)) { freefloat = f; } } } t->setFreeFloat(freefloat); } bool PlanTJScheduler::exists(QList &lst, CalendarDay *day) { foreach (CalendarDay *d, lst) { if (d->date() == day->date() && day->state() != CalendarDay::Undefined && d->state() != CalendarDay::Undefined) { return true; } } return false; } TJ::Resource *PlanTJScheduler::addResource(KPlato::Resource *r) { if (m_resourcemap.values().contains(r)) { debugPlan<name()<<"already exist"; return m_resourcemap.key(r); } TJ::Resource *res = new TJ::Resource(m_tjProject, r->id(), r->name(), 0); if (r->type() == Resource::Type_Material) { res->setEfficiency(0.0); } else { res->setEfficiency((double)(r->units()) / 100.); } Calendar *cal = r->calendar(); Q_ASSERT(cal); DateTime start = qMax(r->availableFrom(), m_project->constraintStartTime()); DateTime end = m_project->constraintEndTime(); if (r->availableUntil().isValid() && end > r->availableUntil()) { end = r->availableUntil(); } AppointmentIntervalList lst = cal->workIntervals(start, end, 1.0); // qDebug()<name()< &map = lst.map(); QMultiMap::const_iterator mapend = map.constEnd(); QMultiMap::const_iterator it = map.constBegin(); TJ::Shift *shift = new TJ::Shift(m_tjProject, r->id(), r->name(), 0, QString(), 0); for (; it != mapend; ++it) { shift->addWorkingInterval(toTJInterval(it.value().startTime(), it.value().endTime(), m_granularity/1000)); } res->addShift(toTJInterval(start, end, m_granularity/1000), shift); m_resourcemap[res] = r; logDebug(m_project, 0, "Added resource: " + r->name()); /* QListIterator it = res->getVacationListIterator(); while (it.hasNext()) { TJ::Interval *i = it.next(); logDebug(m_project, 0, "Vacation: " + TJ::time2ISO(i->getStart()) + " - " + TJ::time2ISO(i->getEnd())); }*/ return res; } TJ::Task *PlanTJScheduler::addTask(KPlato::Task *task, TJ::Task *parent) { /* if (m_backward && task->isStartNode()) { Relation *r = new Relation(m_backwardTask, task); m_project->addRelation(r); }*/ TJ::Task *t = new TJ::Task(m_tjProject, task->id(), task->name(), parent, QString(), 0); m_taskmap[ t ] = task; t->setPriority(task->priority()); // logDebug(m_project, 0, "Added task: " + task->name()); addWorkingTime(task, t); return t; } void PlanTJScheduler::addWorkingTime(KPlato::Task *task, TJ::Task *job) { if (task->type() != Node::Type_Task || task->estimate()->type() != Estimate::Type_Duration || ! task->estimate()->calendar()) { return; } int id = 0; Calendar *cal = task->estimate()->calendar(); DateTime start = m_project->constraintStartTime(); DateTime end = m_project->constraintEndTime(); AppointmentIntervalList lst = cal->workIntervals(start, end, 1.0); const QMultiMap &map = lst.map(); QMultiMap::const_iterator mapend = map.constEnd(); QMultiMap::const_iterator it = map.constBegin(); TJ::Shift *shift = new TJ::Shift(m_tjProject, task->id() + QString("-%1").arg(++id), task->name(), 0, QString(), 0); for (; it != mapend; ++it) { shift->addWorkingInterval(toTJInterval(it.value().startTime(), it.value().endTime(), m_granularity/1000)); } job->addShift(toTJInterval(start, end, m_granularity/1000), shift); } void PlanTJScheduler::addTasks() { debugPlan; QList list = m_project->allNodes(); for (int i = 0; i < list.count(); ++i) { Node *n = list.at(i); TJ::Task *parent = 0; switch (n->type()) { case Node::Type_Summarytask: m_schedule->insertSummaryTask(n); break; case Node::Type_Task: case Node::Type_Milestone: switch (n->constraint()) { case Node::StartNotEarlier: parent = addStartNotEarlier(n); break; case Node::FinishNotLater: parent = addFinishNotLater(n); break; } addTask(static_cast(n), parent); break; default: break; } } } void PlanTJScheduler::addDepends(const Relation *rel) { TJ::Task *child = m_tjProject->getTask(rel->child()->id()); TJ::TaskDependency *d = child->addDepends(rel->parent()->id()); d->setGapDuration(0, rel->lag().seconds()); } void PlanTJScheduler::addPrecedes(const Relation *rel) { TJ::Task *parent = m_tjProject->getTask(rel->parent()->id()); TJ::TaskDependency *d = parent->addPrecedes(rel->child()->id()); d->setGapDuration(0, rel->lag().seconds()); } void PlanTJScheduler::addDependencies(KPlato::Task *task) { foreach (Relation *r, task->dependParentNodes() + task->parentProxyRelations()) { Node *n = r->parent(); if (n == 0 || n->type() == Node::Type_Summarytask) { continue; } switch (r->type()) { case Relation::FinishStart: break; case Relation::FinishFinish: case Relation::StartStart: warnPlan<<"Dependency type not handled. Using FinishStart."; logWarning(task, 0, xi18nc("@info/plain" , "Dependency type '%1' not handled. Using FinishStart.", r->typeToString(true))); break; } switch (task->constraint()) { case Node::ASAP: case Node::ALAP: addPrecedes(r); addDepends(r); break; case Node::MustStartOn: case Node::StartNotEarlier: addPrecedes(r); if (task->constraintStartTime() < m_project->constraintStartTime()) { addDepends(r); } break; case Node::MustFinishOn: case Node::FinishNotLater: addDepends(r); if (task->constraintEndTime() < m_project->constraintEndTime()) { addPrecedes(r); } break; case Node::FixedInterval: break; } } } void PlanTJScheduler::addDependencies() { foreach (Task *t, m_taskmap) { addDependencies(t); } } void PlanTJScheduler::setConstraints() { QMap ::const_iterator it = m_taskmap.constBegin(); for (; it != m_taskmap.constEnd(); ++it) { setConstraint(it.key(), it.value()); } } void PlanTJScheduler::setConstraint(TJ::Task *job, KPlato::Task *task) { switch (task->constraint()) { case Node::ASAP: if (! job->isMilestone()) { job->setScheduling(m_backward ? TJ::Task::ALAP : TJ::Task::ASAP); } break; case Node::ALAP: job->setScheduling(TJ::Task::ALAP); break; case Node::MustStartOn: if (task->constraintStartTime() >= m_project->constraintStartTime()) { job->setPriority(600); job->setSpecifiedStart(0, task->constraintStartTime().toTime_t()); logDebug(task, 0, QString("MSO: set specified start: %1").arg(TJ::time2ISO(task->constraintStartTime().toTime_t()))); } else { logWarning(task, 0, xi18nc("@info/plain", "%1: Invalid start constraint", task->constraintToString(true))); } break; case Node::StartNotEarlier: { break; } case Node::MustFinishOn: if (task->constraintEndTime() <= m_project->constraintEndTime()) { job->setPriority(600); job->setScheduling(TJ::Task::ALAP); job->setSpecifiedEnd(0, task->constraintEndTime().toTime_t() - 1); logDebug(task, 0, QString("MFO: set specified end: %1").arg(TJ::time2ISO(task->constraintEndTime().toTime_t()))); } else { logWarning(task, 0, xi18nc("@info/plain", "%1: Invalid end constraint", task->constraintToString(true))); } break; case Node::FinishNotLater: { break; } case Node::FixedInterval: { job->setPriority(700); TJ::Interval i(toTJInterval(task->constraintStartTime(), task->constraintEndTime(), tjGranularity())); job->setSpecifiedPeriod(0, i); // estimate not allowed job->setDuration(0, 0.0); job->setLength(0, 0.0); job->setEffort(0, 0.0); logDebug(task, 0, QString("FI: set specified: %1 - %2 -> %3 - %4 (%5)") .arg(TJ::time2ISO(task->constraintStartTime().toTime_t())) .arg(TJ::time2ISO(task->constraintEndTime().toTime_t())) .arg(TJ::time2ISO(i.getStart())) .arg(TJ::time2ISO(i.getEnd())) .arg(tjGranularity())); break; } default: logWarning(task, 0, xi18nc("@info/plain", "Unhandled time constraint type")); break; } } TJ::Task *PlanTJScheduler::addStartNotEarlier(Node *task) { DateTime time = task->constraintStartTime(); if (task->estimate()->type() == Estimate::Type_Duration && task->estimate()->calendar() != 0) { Calendar *cal = task->estimate()->calendar(); if (cal != m_project->defaultCalendar() && cal != m_project->calendars().value(0)) { logWarning(task, 0, xi18nc("@info/plain", "Could not use the correct calendar for calculation of task duration")); } else { time = cal->firstAvailableAfter(time, m_project->constraintEndTime()); } } TJ::Task *p = new TJ::Task(m_tjProject, QString("%1-sne").arg(m_tjProject->taskCount() + 1), task->name() + "-sne", 0, QString(), 0); p->setSpecifiedStart(0, toTJTime_t(time, tjGranularity())); p->setSpecifiedEnd(0, m_tjProject->getEnd() - 1); qDebug()<<"PlanTJScheduler::addStartNotEarlier:"<constraintEndTime(); if (task->estimate()->type() == Estimate::Type_Duration && task->estimate()->calendar() != 0) { Calendar *cal = task->estimate()->calendar(); if (cal != m_project->defaultCalendar() && cal != m_project->calendars().value(0)) { logWarning(task, 0, xi18nc("@info/plain", "Could not use the correct calendar for calculation of task duration")); } else { time = cal->firstAvailableBefore(time, m_project->constraintStartTime()); } } TJ::Task *p = new TJ::Task(m_tjProject, QString("%1-fnl").arg(m_tjProject->taskCount() + 1), task->name() + "-fnl", 0, QString(), 0); p->setSpecifiedEnd(0, toTJTime_t(time, tjGranularity()) - 1); p->setSpecifiedStart(0, m_tjProject->getStart()); return p; } void PlanTJScheduler::addRequests() { debugPlan; QMap ::const_iterator it = m_taskmap.constBegin(); for (; it != m_taskmap.constEnd(); ++it) { addRequest(it.key(), it.value()); } } void PlanTJScheduler::addRequest(TJ::Task *job, Task *task) { debugPlan; if (task->type() == Node::Type_Milestone || task->estimate() == 0 || (m_recalculate && task->completion().isFinished())) { job->setMilestone(true); job->setDuration(0, 0.0); return; } // Note: FI tasks can never have an estimate set (duration, length or effort) if (task->constraint() != Node::FixedInterval) { if (task->estimate()->type() == Estimate::Type_Duration && task->estimate()->calendar() == 0) { job->setDuration(0, task->estimate()->value(Estimate::Use_Expected, m_usePert).toDouble(Duration::Unit_d)); return; } if (task->estimate()->type() == Estimate::Type_Duration && task->estimate()->calendar() != 0) { job->setLength(0, task->estimate()->value(Estimate::Use_Expected, m_usePert).toDouble(Duration::Unit_d) * 24.0 / m_tjProject->getDailyWorkingHours()); return; } if (m_recalculate && task->completion().isStarted()) { job->setEffort(0, task->completion().remainingEffort().toDouble(Duration::Unit_d)); } else { Estimate *estimate = task->estimate(); double e = estimate->scale(estimate->value(Estimate::Use_Expected, m_usePert), Duration::Unit_d, estimate->scales()); job->setEffort(0, e); } } if (task->requests().isEmpty()) { + if (task->type() == Node::Type_Task) { + warnPlan<<"Task:"<requests().resourceRequests(true /*resolveTeam*/)) { if (!rr->resource()->calendar()) { result = 1; // stops scheduling logError(task, 0, i18n("No working hours defined for resource: %1",rr->resource()->name())); continue; // may happen if no calendar is set, and no default calendar } TJ::Resource *tjr = addResource(rr->resource()); TJ::Allocation *a = new TJ::Allocation(); a->setSelectionMode(TJ::Allocation::order); if (rr->units() != 100) { TJ::UsageLimits *l = new TJ::UsageLimits(); l->setDailyUnits(rr->units()); a->setLimits(l); } a->addCandidate(tjr); job->addAllocation(a); logDebug(task, 0, "Added resource candidate: " + rr->resource()->name()); foreach (Resource *r, rr->requiredResources()) { TJ::Resource *tr = addResource(r); a->addRequiredResource(tjr, tr); logDebug(task, 0, "Added required resource: " + r->name()); } } } diff --git a/src/tests/InsertProjectTester.cpp b/src/tests/InsertProjectTester.cpp index 59b95caa..0f96b492 100644 --- a/src/tests/InsertProjectTester.cpp +++ b/src/tests/InsertProjectTester.cpp @@ -1,698 +1,657 @@ /* This file is part of the KDE project Copyright (C) 2009 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "InsertProjectTester.h" #include "kptcommand.h" #include #include "kptmaindocument.h" #include "kptpart.h" #include "kptcalendar.h" #include "kptresource.h" #include "kpttask.h" #include "kptaccount.h" #include #include #include namespace KPlato { Account *InsertProjectTester::addAccount(MainDocument &part, Account *parent) { Project &p = part.getProject(); Account *a = new Account(); QString s = parent == 0 ? "Account" : parent->name(); a->setName(p.accounts().uniqueId(s)); KUndo2Command *c = new AddAccountCmd(p, a, parent); part.addCommand(c); return a; } void InsertProjectTester::testAccount() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addAccount(part); Project &p = part.getProject(); QCOMPARE(p.accounts().accountCount(), 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QCOMPARE(part2.getProject().accounts().accountCount(), 1); part2.insertProject(part.getProject(), 0, 0); QCOMPARE(part2.getProject().accounts().accountCount(), 1); Part ppB(0); MainDocument partB(&ppB); ppB.setDocument(&partB); Account *parent = addAccount(partB); QCOMPARE(partB.getProject().accounts().accountCount(), 1); addAccount(partB, parent); QCOMPARE(partB.getProject().accounts().accountCount(), 1); QCOMPARE(parent->childCount(), 1); part2.insertProject(partB.getProject(), 0, 0); QCOMPARE(part2.getProject().accounts().accountCount(), 1); QCOMPARE(part2.getProject().accounts().accountAt(0)->childCount(), 1); } Calendar *InsertProjectTester::addCalendar(MainDocument &part) { Project &p = part.getProject(); Calendar *c = new Calendar(); p.setCalendarId(c); part.addCommand(new CalendarAddCmd(&p, c, -1, 0)); return c; } void InsertProjectTester::testCalendar() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); Project &p = part.getProject(); QVERIFY(p.calendarCount() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(part.getProject(), 0, 0); QVERIFY(part2.getProject().calendarCount() == 1); QVERIFY(part2.getProject().defaultCalendar() == 0); } void InsertProjectTester::testDefaultCalendar() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); Calendar *c = addCalendar(part); Project &p = part.getProject(); p.setDefaultCalendar(c); QVERIFY(p.calendarCount() == 1); QCOMPARE(p.defaultCalendar(), c); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().calendarCount() == 1); QCOMPARE(part2.getProject().defaultCalendar(), c); Part ppB(0); MainDocument partB(&ppB); ppB.setDocument(&partB); Calendar *c2 = addCalendar(partB); partB.getProject().setDefaultCalendar(c2); part2.insertProject(partB.getProject(), 0, 0); QVERIFY(part2.getProject().calendarCount() == 2); QCOMPARE(part2.getProject().defaultCalendar(), c); // NB: still c, not c2 } ResourceGroup *InsertProjectTester::addResourceGroup(MainDocument &part) { Project &p = part.getProject(); ResourceGroup *g = new ResourceGroup(); KUndo2Command *c = new AddResourceGroupCmd(&p, g); part.addCommand(c); QString s = QString("G%1").arg(part.getProject().indexOf(g)); g->setName(s); return g; } void InsertProjectTester::testResourceGroup() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addResourceGroup(part); Project &p = part.getProject(); QVERIFY(p.resourceGroupCount() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().resourceGroupCount() == 1); } Resource *InsertProjectTester::addResource(MainDocument &part, ResourceGroup *g) { Project &p = part.getProject(); if (p.resourceGroupCount() == 0) { qInfo()<<"No resource groups in project"; return nullptr; } if (g == 0) { g = p.resourceGroupAt(0); } Q_ASSERT(g); if (!p.resourceGroups().contains(g)) { qInfo()<<"Project does not contain resource group"; return nullptr; } Resource *r = new Resource(); KUndo2Command *c = new AddResourceCmd(g, r); part.addCommand(c); QString s = QString("%1.R%2").arg(g->name()).arg(g->indexOf(r)); r->setName(s); return r; } void InsertProjectTester::testResource() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addResourceGroup(part); addResource(part); Project &p = part.getProject(); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); //Debug::print(&part.getProject(), "Project to insert from: ------------", true); //Debug::print(&part2.getProject(), "Project to insert into: -----------", true); part2.insertProject(p, 0, 0); //Debug::print(&part2.getProject(), "Result: ---------------------------", true); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); } void InsertProjectTester::testTeamResource() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addResourceGroup(part); Resource *r = addResource(part); r->setType(Resource::Type_Team); ResourceGroup *tg = addResourceGroup(part); Resource *t1 = addResource(part, tg); Resource *t2 = addResource(part, tg); r->setRequiredIds(QStringList() << t1->id() << t2->id()); Project &p = part.getProject(); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); QVERIFY(p.resourceGroupAt(1)->numResources() == 2); QList required = p.resourceGroupAt(0)->resources().at(0)->requiredResources(); QCOMPARE(required.count(), 2); QCOMPARE(required.at(0), t1); QCOMPARE(required.at(1), t2); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); Project &p2 = part2.getProject(); QVERIFY(p2.resourceGroupAt(0)->numResources() == 1); QVERIFY(p2.resourceGroupAt(1)->numResources() == 2); required = p2.resourceGroupAt(0)->resources().at(0)->requiredResources(); QCOMPARE(required.count(), 2); QCOMPARE(required.at(0), t1); QCOMPARE(required.at(1), t2); } void InsertProjectTester::testResourceAccount() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addResourceGroup(part); Resource *r = addResource(part); Account *a = addAccount(part); part.addCommand(new ResourceModifyAccountCmd(*r, r->account(), a)); Project &p = part.getProject(); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QVERIFY(part2.getProject().accounts().allAccounts().contains(a)); QCOMPARE(part2.getProject().resourceGroupAt(0)->resourceAt(0)->account(), a); } void InsertProjectTester::testResourceCalendar() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); Calendar *c = addCalendar(part); Project &p = part.getProject(); QVERIFY(p.calendarCount() == 1); addResourceGroup(part); Resource *r = addResource(part); part.addCommand(new ModifyResourceCalendarCmd(r, c)); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QCOMPARE(part2.getProject().allCalendars().count(), 1); QVERIFY(part2.getProject().allCalendars().contains(c)); QCOMPARE(part2.getProject().resourceGroupAt(0)->resourceAt(0)->calendar(true), c); } Task *InsertProjectTester::addTask(MainDocument &part) { Project &p = part.getProject(); Task *t = new Task(); t->setId(p.uniqueNodeId()); KUndo2Command *c = new TaskAddCmd(&p, t, 0); part.addCommand(c); return t; } void InsertProjectTester::testTask() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); Project &p = part.getProject(); p.setName("Insert from"); addTask(part); QVERIFY(p.numChildren() == 1); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.getProject().setName("Insert into"); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().numChildren() == 1); } - -void InsertProjectTester::addGroupRequest(MainDocument &part) -{ - Project &p = part.getProject(); - Task *t = static_cast(p.childNode(0)); - int requests = p.resourceGroupAt(0)->requests().count(); - KUndo2Command *c = new AddResourceGroupRequestCmd(*t, new ResourceGroupRequest(p.resourceGroupAt(0), 1)); - part.addCommand(c); - QVERIFY(p.resourceGroupAt(0)->requests().count() == requests+1); -} - -void InsertProjectTester::testGroupRequest() -{ - Part pp(0); - MainDocument part(&pp); - pp.setDocument(&part); - Project &p = part.getProject(); - p.setName("Insert from"); - addCalendar(part); - addResourceGroup(part); - addResource(part); - addTask(part); - addGroupRequest(part); - - QVERIFY(p.numChildren() == 1); - - Part pp2(0); - MainDocument part2(&pp2); - pp2.setDocument(&part2); - Project &p2 = part2.getProject(); - p2.setName("Insert into"); - part2.insertProject(p, 0, 0); - QVERIFY(p2.childNode(0)->resourceGroupRequest(p2.resourceGroupAt(0)) != 0); -} - void InsertProjectTester::addResourceRequest(MainDocument &part) { Project &p = part.getProject(); - ResourceGroupRequest *g = p.childNode(0)->requests().requests().at(0); - KUndo2Command *c = new AddResourceRequestCmd(g, new ResourceRequest(p.resourceGroupAt(0)->resourceAt(0), 1)); + KUndo2Command *c = new AddResourceRequestCmd(&p.childNode(0)->requests(), new ResourceRequest(p.resourceGroupAt(0)->resourceAt(0), 1)); part.addCommand(c); } void InsertProjectTester::testResourceRequest() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); addResourceGroup(part); addResource(part); addTask(part); - addGroupRequest(part); addResourceRequest(part); Project &p = part.getProject(); Debug::print(&p, "To be inserted: --------", true); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); Project &p2 = part2.getProject(); QVERIFY(p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)) != 0); Debug::print(&p2, "After insert: --------", true); } void InsertProjectTester::testTeamResourceRequest() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); addResourceGroup(part); Resource *r = addResource(part); r->setType(Resource::Type_Team); ResourceGroup *tg = addResourceGroup(part); Resource *t1 = addResource(part, tg); r->addTeamMemberId(t1->id()); Resource *t2 = addResource(part, tg); r->addTeamMemberId(t2->id()); addTask(part); - addGroupRequest(part); addResourceRequest(part); qDebug()<<"Start test:"; Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(part.getProject(), 0, 0); Project &p2 = part2.getProject(); ResourceRequest *rr = p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QCOMPARE(rr->resource(), r); QCOMPARE(rr->resource()->teamMembers().count(), 2); QCOMPARE(rr->resource()->teamMembers().at(0), t1); QCOMPARE(rr->resource()->teamMembers().at(1), t2); } Relation *InsertProjectTester::addDependency(MainDocument &part, Task *t1, Task *t2) { Project &p = part.getProject(); Relation *r = new Relation(t1, t2); part.addCommand(new AddRelationCmd(p, r)); return r; } void InsertProjectTester::testDependencies() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); Task *t1 = addTask(part); Task *t2 = addTask(part); QCOMPARE(t1->numDependChildNodes(), 0); QCOMPARE(t2->numDependParentNodes(), 0); Relation *r = addDependency(part, t1, t2); QCOMPARE(t1->numDependChildNodes(), 1); QCOMPARE(t2->numDependParentNodes(), 1); QCOMPARE(t1->getDependChildNode(0), r); QCOMPARE(t2->getDependParentNode(0), r); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); QVERIFY(part2.insertProject(part.getProject(), 0, 0)); Project &p2 = part2.getProject(); QVERIFY(p2.numChildren() == 2); QCOMPARE(p2.childNode(0), t1); QCOMPARE(p2.childNode(1), t2); QCOMPARE(t1->numDependChildNodes(), 1); QCOMPARE(t2->numDependParentNodes(), 1); QCOMPARE(t1->getDependChildNode(0), r); QCOMPARE(t2->getDependParentNode(0), r); } void InsertProjectTester::testExistingResourceAccount() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addResourceGroup(part); Resource *r = addResource(part); Account *a = addAccount(part); part.addCommand(new ResourceModifyAccountCmd(*r, r->account(), a)); Project &p = part.getProject(); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); QDomDocument doc = part.saveXML(); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QVERIFY(part2.getProject().accounts().allAccounts().contains(a)); QCOMPARE(part2.getProject().resourceGroupAt(0)->resourceAt(0)->account(), a); part2.addCommand(new ResourceModifyAccountCmd(*(part2.getProject().resourceGroupAt(0)->resourceAt(0)), part2.getProject().resourceGroupAt(0)->resourceAt(0)->account(), 0)); KoXmlDocument xdoc; xdoc.setContent(doc.toString()); part.loadXML(xdoc, 0); part2.insertProject(part.getProject(), 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QVERIFY(part2.getProject().accounts().allAccounts().contains(a)); QVERIFY(part2.getProject().resourceGroupAt(0)->resourceAt(0)->account() == 0); } void InsertProjectTester::testExistingResourceCalendar() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); Calendar *c = addCalendar(part); Project &p = part.getProject(); QVERIFY(p.calendarCount() == 1); addResourceGroup(part); Resource *r = addResource(part); part.addCommand(new ModifyResourceCalendarCmd(r, c)); QVERIFY(p.resourceGroupAt(0)->numResources() == 1); QDomDocument doc = part.saveXML(); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QCOMPARE(part2.getProject().allCalendars().count(), 1); QVERIFY(part2.getProject().allCalendars().contains(c)); QCOMPARE(part2.getProject().resourceGroupAt(0)->resourceAt(0)->calendar(true), c); part2.getProject().resourceGroupAt(0)->resourceAt(0)->setCalendar(0); KoXmlDocument xdoc; xdoc.setContent(doc.toString()); part.loadXML(xdoc, 0); part2.insertProject(part.getProject(), 0, 0); QVERIFY(part2.getProject().resourceGroupAt(0)->numResources() == 1); QCOMPARE(part2.getProject().allCalendars().count(), 1); QVERIFY(part2.getProject().allCalendars().contains(c)); QVERIFY(part2.getProject().resourceGroupAt(0)->resourceAt(0)->calendar(true) == 0); } void InsertProjectTester::testExistingResourceRequest() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); addResourceGroup(part); addResource(part); addTask(part); - addGroupRequest(part); addResourceRequest(part); QDomDocument doc = part.saveXML(); Project &p = part.getProject(); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); //Debug::print(&part.getProject(), "Project to be inserted from: --------------------------", true); //Debug::print(&part2.getProject(), "Project to be inserted into: -------------------------", true); part2.insertProject(p, 0, 0); //Debug::print(&part2.getProject(), "Result1:", true); Project &p2 = part2.getProject(); QVERIFY(p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)) != 0); KoXmlDocument xdoc; xdoc.setContent(doc.toString()); part.loadXML(xdoc, 0); //Debug::print(&part.getProject(), "Project to be inserted from:", true); //Debug::print(&part2.getProject(), "Project to be inserted into:", true); part2.insertProject(part.getProject(), 0, 0); //Debug::print(&part2.getProject(), "Result2:", true); QVERIFY(p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)) != 0); QVERIFY(p2.childNode(1)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)) != 0); } void InsertProjectTester::testExistingRequiredResourceRequest() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); addResourceGroup(part); Resource *r = addResource(part); ResourceGroup *g = addResourceGroup(part); g->setType(ResourceGroup::Type_Material); QList m; m << addResource(part, g); m.first()->setType(Resource::Type_Material); r->setRequiredIds(QStringList() << m.first()->id()); addTask(part); - addGroupRequest(part); addResourceRequest(part); QDomDocument doc = part.saveXML(); Project &p = part.getProject(); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); part2.insertProject(p, 0, 0); Project &p2 = part2.getProject(); ResourceRequest *rr = p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QVERIFY(! rr->requiredResources().isEmpty()); QCOMPARE(rr->requiredResources().at(0), m.first()); KoXmlDocument xdoc; xdoc.setContent(doc.toString()); part.loadXML(xdoc, 0); part2.insertProject(part.getProject(), 0, 0); rr = p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QVERIFY(! rr->requiredResources().isEmpty()); QCOMPARE(rr->requiredResources().at(0), m.first()); rr = p2.childNode(1)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QVERIFY(! rr->requiredResources().isEmpty()); QCOMPARE(rr->requiredResources().at(0), m.first()); } void InsertProjectTester::testExistingTeamResourceRequest() { Part pp(0); MainDocument part(&pp); pp.setDocument(&part); addCalendar(part); addResourceGroup(part); Resource *r = addResource(part); r->setName("R1"); r->setType(Resource::Type_Team); ResourceGroup *tg = addResourceGroup(part); tg->setName("TG"); Resource *t1 = addResource(part, tg); t1->setName("T1"); r->addTeamMemberId(t1->id()); Resource *t2 = addResource(part, tg); t2->setName("T2"); r->addTeamMemberId(t2->id()); addTask(part); - addGroupRequest(part); addResourceRequest(part); QDomDocument doc = part.saveXML(); Part pp2(0); MainDocument part2(&pp2); pp2.setDocument(&part2); Project &p2 = part2.getProject(); part2.insertProject(part.getProject(), 0, 0); ResourceRequest *rr = p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QCOMPARE(rr->resource()->teamMembers().count(), 2); QCOMPARE(rr->resource()->teamMembers().at(0), t1); QCOMPARE(rr->resource()->teamMembers().at(1), t2); KoXmlDocument xdoc; xdoc.setContent(doc.toString()); part.loadXML(xdoc, 0); part2.insertProject(part.getProject(), 0, 0); QCOMPARE(p2.numChildren(), 2); QVERIFY(! p2.childNode(0)->requests().isEmpty()); rr = p2.childNode(0)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QCOMPARE(rr->resource()->teamMembers().count(), 2); QCOMPARE(rr->resource()->teamMembers().at(0), t1); QCOMPARE(rr->resource()->teamMembers().at(1), t2); QVERIFY(! p2.childNode(1)->requests().isEmpty()); rr = p2.childNode(1)->requests().find(p2.resourceGroupAt(0)->resourceAt(0)); QVERIFY(rr); QCOMPARE(rr->resource()->teamMembers().count(), 2); QCOMPARE(rr->resource()->teamMembers().at(0), t1); QCOMPARE(rr->resource()->teamMembers().at(1), t2); } } //namespace KPlato QTEST_MAIN(KPlato::InsertProjectTester) diff --git a/src/tests/InsertProjectTester.h b/src/tests/InsertProjectTester.h index 4c20957d..48d13ec8 100644 --- a/src/tests/InsertProjectTester.h +++ b/src/tests/InsertProjectTester.h @@ -1,72 +1,70 @@ /* This file is part of the KDE project Copyright (C) 2009 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPlato_InsertProjectTester_h #define KPlato_InsertProjectTester_h #include namespace KPlato { class MainDocument; class Account; class ResourceGroup; class Resource; class Task; class Relation; class Calendar; class InsertProjectTester : public QObject { Q_OBJECT private Q_SLOTS: void testAccount(); void testCalendar(); void testDefaultCalendar(); void testResourceGroup(); void testResource(); void testTeamResource(); void testResourceAccount(); void testResourceCalendar(); void testTask(); - void testGroupRequest(); void testResourceRequest(); void testTeamResourceRequest(); void testDependencies(); void testExistingResourceAccount(); void testExistingResourceCalendar(); void testExistingResourceRequest(); void testExistingRequiredResourceRequest(); void testExistingTeamResourceRequest(); private: Account *addAccount(MainDocument &part, Account *parent = 0); Calendar *addCalendar(MainDocument &part); ResourceGroup *addResourceGroup(MainDocument &part); Resource *addResource(MainDocument &part, ResourceGroup *g = 0); Task *addTask(MainDocument &part); - void addGroupRequest(MainDocument &part); void addResourceRequest(MainDocument &part); Relation *addDependency(MainDocument &part, Task *t1, Task *t2); }; } #endif