From bf6e53ecc7c1745d5d0820d592e1b31b523200b0 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 27 Dec 2023 14:15:50 +0800 Subject: [PATCH] * Refactor get about gantt data. --- module/programplan/control.php | 12 +- module/programplan/model.php | 294 +++----------------- module/programplan/tao.php | 495 ++++++++++++++++++++++----------- 3 files changed, 382 insertions(+), 419 deletions(-) diff --git a/module/programplan/control.php b/module/programplan/control.php index 2cb6ecd89a..3561b5308d 100644 --- a/module/programplan/control.php +++ b/module/programplan/control.php @@ -247,16 +247,11 @@ class programplan extends control public function ajaxGetAttribute(int $stageID, string $attribute, string $projectModel = ''): int { $this->app->loadLang('stage'); + if($projectModel == 'ipd') $this->lang->stage->typeList = $this->lang->stage->ipdTypeList; $stageAttribute = $this->programplan->getStageAttribute($stageID); - if($projectModel == 'ipd') $this->lang->stage->typeList = $this->lang->stage->ipdTypeList; - - if(empty($stageAttribute) || $stageAttribute == 'mix') - { - return print(html::select('attribute', $this->lang->stage->typeList, $attribute, "class='form-control chosen'")); - } - + if(empty($stageAttribute) || $stageAttribute == 'mix') return print(html::select('attribute', $this->lang->stage->typeList, $attribute, "class='form-control chosen'")); return print(zget($this->lang->stage->typeList, $stageAttribute)); } @@ -271,9 +266,6 @@ class programplan extends control public function ajaxGetStageAttr(int $stageID): int { $stageAttribute = $this->programplan->getStageAttribute($stageID); - - if(!$stageAttribute) return print(js::error(dao::getError())); - return print($stageAttribute); } } diff --git a/module/programplan/model.php b/module/programplan/model.php index 5a36d6d3d0..eef34d21f1 100755 --- a/module/programplan/model.php +++ b/module/programplan/model.php @@ -23,8 +23,7 @@ class programplanModel extends model public function getByID(int $planID): object|false { $plan = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($planID)->fetch(); - - if(dao::isError()) return false; + if(empty($plan)) return false; return $this->processPlan($plan); } @@ -56,11 +55,7 @@ class programplanModel extends model */ public function getByList(array $idList = array()): array { - $plans = $this->dao->select('*')->from(TABLE_PROJECT) - ->where('id')->in($idList) - ->andWhere('type')->eq('project') - ->fetchAll('id'); - + $plans = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->in($idList)->andWhere('type')->eq('project')->fetchAll('id'); return $this->processPlans($plans); } @@ -83,11 +78,11 @@ class programplanModel extends model $children = array(); foreach($plans as $planID => $plan) { - $plan->grade == 1 ? $parents[$planID] = $plan : $children[$plan->parent][] = $plan; + if($plan->grade == 1) $parents[$planID] = $plan; + if($plan->grade > 1) $children[$plan->parent][] = $plan; } foreach($parents as $planID => $plan) $parents[$planID]->children = isset($children[$planID]) ? $children[$planID] : array(); - return $parents; } @@ -103,23 +98,17 @@ class programplanModel extends model */ public function getPairs(int $executionID, int $productID = 0, string $type = 'all'): array { - $plans = $this->getStage($executionID, $productID, $type); - - $pairs = array(0 => ''); - - if(strpos($type, 'leaf') !== false) - { - $parents = array(); - foreach($plans as $planID => $plan) $parents[$plan->parent] = true; - } + $plans = $this->getStage($executionID, $productID, $type); + $pairs = array(0 => ''); + $parents = array(); + if(strpos($type, 'leaf') !== false) array_map(function($plan) use(&$parents){$parents[$plan->parent] = true;}, $plans); foreach($plans as $planID => $plan) { if(strpos($type, 'leaf') !== false and isset($parents[$plan->id])) continue; $paths = array_slice(explode(',', trim($plan->path, ',')), 1); $planName = ''; - foreach($paths as $path) { if(isset($plans[$path])) $planName .= '/' . $plans[$path]->name; @@ -153,27 +142,19 @@ class programplanModel extends model { $baseline = $this->loadModel('cm')->getByID($baselineID); $oldData = json_decode($baseline->data); - $oldPlans = $oldData->stage; - $plans = $this->programplanTao->setPlanBaseline($oldPlans, $plans); + $plans = $this->programplanTao->setPlanBaseline($oldData->stage, $plans); } - $datas = $planIdList = $stageIndex = array(); - /* Set plan for gantt view. */ + $datas = $planIdList = $stageIndex = array(); $this->programplanTao->setPlan($plans, $datas, $stageIndex, $planIdList); /* Judge whether to display tasks under the stage. */ - $owner = $this->app->user->account; - if(empty($selectCustom)) $selectCustom = $this->loadModel('setting')->getItem("owner={$owner}&module=programplan§ion=browse&key=stageCustom"); - - $tasks = $this->dao->select('*')->from(TABLE_TASK)->where('deleted')->eq(0)->andWhere('execution')->in($planIdList)->orderBy('execution_asc, order_asc, id_desc')->fetchAll('id'); + if(empty($selectCustom)) $selectCustom = $this->loadModel('setting')->getItem("owner={$this->app->user->account}&module=programplan§ion=browse&key=stageCustom"); /* Set task baseline data. */ - if($baselineID) - { - $oldTasks = isset($oldData->task) ? $oldData->task : array(); - $this->programplanTao->setTaskBaseline($oldTasks, $tasks); // Set task baseline. - } + $tasks = $this->dao->select('*')->from(TABLE_TASK)->where('deleted')->eq(0)->andWhere('execution')->in($planIdList)->orderBy('execution_asc, order_asc, id_desc')->fetchAll('id'); + if($baselineID) $this->programplanTao->setTaskBaseline(isset($oldData->task) ? $oldData->task : array(), $tasks); // Set task baseline. /* Set task for gantt view. */ $this->programplanTao->setTask($tasks, $plans, $selectCustom, $datas, $stageIndex); @@ -185,16 +166,14 @@ class programplanModel extends model foreach($stageIndex as $index => $stage) { $progress = empty($stage['progress']['totalConsumed']) ? 0 : round($stage['progress']['totalConsumed'] / $stage['progress']['totalReal'], 3); - $datas['data'][$index]->progress = $progress; - - $progress = ($progress * 100) . '%'; - $datas['data'][$index]->taskProgress = $progress; - $datas['data'][$index]->estimate = $stage['progress']['totalEstimate']; - $datas['data'][$index]->consumed = $stage['progress']['totalConsumed']; + $datas['data'][$index]->progress = $progress; + $datas['data'][$index]->taskProgress = ($progress * 100) . '%'; + $datas['data'][$index]->estimate = $stage['progress']['totalEstimate']; + $datas['data'][$index]->consumed = $stage['progress']['totalConsumed']; } /* Set relation task data. */ - $datas = $this->programplanTao->setRelationTask($planIdList, $datas); + $datas['links'] = $this->programplanTao->buildGanttLinks($planIdList); $datas['data'] = isset($datas['data']) ? array_values($datas['data']) : array(); return $returnJson ? json_encode($datas) : $datas; @@ -210,169 +189,46 @@ class programplanModel extends model * @param string $selectCustom * @param bool $returnJson * @access public - * @return string + * @return string|array */ - public function getDataForGanttGroupByAssignedTo($executionID, $productID, $baselineID = 0, $selectCustom = '', $returnJson = true) + public function getDataForGanttGroupByAssignedTo(int $executionID, int $productID, int $baselineID = 0, string $selectCustom = '', bool $returnJson = true): string|array { - $plans = $this->getStage($executionID, $productID); - $datas = array(); - $planIdList = array(); - $isMilestone = " "; $stageIndex = array(); - foreach($plans as $plan) $planIdList[$plan->id] = $plan->id; - - $taskPri = "%s "; + $plans = $this->getStage($executionID, $productID); + $planIdList = array_column($plans, 'id'); + $users = $this->loadModel('user')->getPairs('noletter'); + $tasks = $this->dao->select('*')->from(TABLE_TASK)->where('deleted')->eq(0)->andWhere('execution')->in($planIdList)->fetchAll('id'); + $tasksGroup = $this->programplanTao->buildTaskGroup($tasks); /* Judge whether to display tasks under the stage. */ - $owner = $this->app->user->account; - $module = 'programplan'; - $section = 'browse'; - $object = 'stageCustom'; - if(empty($selectCustom)) $selectCustom = $this->loadModel('setting')->getItem("owner={$owner}&module={$module}§ion={$section}&key={$object}"); - - $tasksGroup = $this->dao->select('*')->from(TABLE_TASK)->where('deleted')->eq(0)->andWhere('execution')->in($planIdList)->fetchGroup('assignedTo','id'); - $users = $this->loadModel('user')->getPairs('noletter'); - - $tasksMap = array(); - $multiTasks = array(); - foreach($tasksGroup as $group => $tasks) - { - foreach($tasks as $id => $task) - { - if($task->mode == 'multi') $multiTasks[$id] = $group; - $tasksMap[$task->id] = $task; - } - } - - if($multiTasks) - { - $taskTeams = $this->dao->select('t1.*,t2.realname')->from(TABLE_TASKTEAM)->alias('t1') - ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account') - ->where('t1.task')->in(array_keys($multiTasks)) - ->orderBy('t1.order') - ->fetchGroup('task', 'id'); - foreach($taskTeams as $taskID => $team) - { - $group = $multiTasks[$taskID]; - foreach($team as $member) - { - $account = $member->account; - if($account == $group) continue; - if(!isset($taskGroups[$account])) $taskGroups[$account] = array(); - - $taskGroups[$account][$taskID] = clone $tasksMap[$taskID]; - $taskGroups[$account][$taskID]->id = $taskID . '_' . $account; - $taskGroups[$account][$taskID]->realID = $taskID; - $taskGroups[$account][$taskID]->assignedTo = $account; - $taskGroups[$account][$taskID]->realname = $member->realname; - } - } - } + if(empty($selectCustom)) $selectCustom = $this->loadModel('setting')->getItem("owner={$this->app->user->account}&module=programplan§ion=browse&key=stageCustom"); $groupID = 0; foreach($tasksGroup as $group => $tasks) { $groupID --; - $groupName = $group; - $groupName = zget($users, $group); - $dataGroup = new stdclass(); - $dataGroup->id = $groupID; - $dataGroup->type = 'group'; - $dataGroup->text = $groupName; - $dataGroup->percent = ''; - $dataGroup->attribute = ''; - $dataGroup->milestone = ''; - $dataGroup->owner_id = $group; - $dataGroup->status = ''; - $dataGroup->begin = ''; - $dataGroup->deadline = ''; - $dataGroup->realBegan = ''; - $dataGroup->realEnd = ''; - $dataGroup->parent = 0; - $dataGroup->open = true; - $dataGroup->progress = ''; - $dataGroup->taskProgress = ''; - $dataGroup->color = $this->lang->execution->gantt->stage->color; - $dataGroup->progressColor = $this->lang->execution->gantt->stage->progressColor; - $dataGroup->textColor = $this->lang->execution->gantt->stage->textColor; - $dataGroup->bar_height = $this->lang->execution->gantt->bar_height; - - $groupKey = $groupID . $group; - $datas['data'][$groupKey] = $dataGroup; + $groupKey = $groupID . $group; + $datas['data'][$groupKey] = $this->programplanTao->buildGroupDataForGantt($groupID, $group, $users); $realStartDate = array(); $realEndDate = array(); - $totalTask = count($tasks); + $totalTask = count($tasks); foreach($tasks as $taskID => $task) { - $execution = zget($plans, $task->execution, array()); + $dateLimit = $this->programplanTao->getTaskDateLimit($task, zget($plans, $task->execution, null)); + if(strpos($selectCustom, 'task') !== false) $datas['data'][$task->id] = $this->programplanTao->buildTaskDataForGantt($task, $dateLimit, $groupID, $tasks); - $priIcon = sprintf($taskPri, $task->pri, $task->pri, $task->pri); + if(!empty($dateLimit['start'])) $realStartDate[] = strtotime($dateLimit['start']); + if(!empty($dateLimit['end'])) $realEndDate[] = strtotime($dateLimit['end']); - $estStart = helper::isZeroDate($task->estStarted) ? '' : $task->estStarted; - $estEnd = helper::isZeroDate($task->deadline) ? '' : $task->deadline; - $realBegan = helper::isZeroDate($task->realStarted) ? '' : $task->realStarted; - $realEnd = (in_array($task->status, array('done', 'closed')) and !helper::isZeroDate($task->finishedDate)) ? $task->finishedDate : ''; - - $start = $realBegan ? $realBegan : $estStart; - $end = $realEnd ? $realEnd : $estEnd; - if(empty($start) and $execution) $start = $execution->begin; - if(empty($end) and $execution) $end = $execution->end; - if($start > $end) $end = $start; - - $data = new stdclass(); - $data->id = $task->id; - $data->type = 'task'; - $data->text = $priIcon . $task->name; - $data->percent = ''; - $data->status = $this->processStatus('task', $task); - $data->owner_id = $task->assignedTo; - $data->attribute = ''; - $data->milestone = ''; - $data->begin = $start; - $data->deadline = $end; - $data->realBegan = $realBegan ? substr($realBegan, 0, 10) : ''; - $data->realEnd = $realEnd ? substr($realEnd, 0, 10) : ''; - $data->pri = $task->pri; - $data->parent = ($task->parent > 0 and $task->assignedTo != '' and !empty($tasksMap[$task->parent]->assignedTo)) ? $task->parent : $groupID; - $data->open = true; - $progress = $task->consumed ? round($task->consumed / ($task->left + $task->consumed), 3) : 0; - $data->progress = $progress; - $data->taskProgress = ($progress * 100) . '%'; - $data->start_date = $start; - $data->endDate = $end; - $data->duration = 1; - $data->estimate = $task->estimate; - $data->consumed = $task->consumed; - $data->color = zget($this->lang->execution->gantt->color, $task->pri, $this->lang->execution->gantt->defaultColor); - $data->progressColor = zget($this->lang->execution->gantt->progressColor, $task->pri, $this->lang->execution->gantt->defaultProgressColor); - $data->textColor = zget($this->lang->execution->gantt->textColor, $task->pri, $this->lang->execution->gantt->defaultTextColor); - $data->bar_height = $this->lang->execution->gantt->bar_height; - - if($data->endDate > $data->start_date) $data->duration = helper::diffDate($data->endDate, $data->start_date) + 1; - - if($data->start_date) $data->start_date = date('d-m-Y', strtotime($data->start_date)); - if($data->start_date == '' or $data->endDate == '') $data->duration = 1; - - if(strpos($selectCustom, 'task') !== false) $datas['data'][$data->id] = $data; - - if(!empty($start)) $realStartDate[] = strtotime($start); - if(!empty($end)) $realEndDate[] = strtotime($end); - - if(isset($stageIndex[$groupKey]['totalConsumed'])) - { - $stageIndex[$groupKey]['totalConsumed'] += $task->consumed; - $stageIndex[$groupKey]['totalReal'] += $task->left + $task->consumed; - $stageIndex[$groupKey]['totalEstimate'] += $task->estimate; - } - else - { - $stageIndex[$groupKey]['totalConsumed'] = $task->consumed; - $stageIndex[$groupKey]['totalReal'] = $task->left + $task->consumed; - $stageIndex[$groupKey]['totalEstimate'] = $task->estimate; - } + if(!isset($stageIndex[$groupKey]['totalConsumed'])) $stageIndex[$groupKey]['totalConsumed'] = 0; + if(!isset($stageIndex[$groupKey]['totalReal'])) $stageIndex[$groupKey]['totalReal'] = 0; + if(!isset($stageIndex[$groupKey]['totalEstimate'])) $stageIndex[$groupKey]['totalEstimate'] = 0; + $stageIndex[$groupKey]['totalConsumed'] += $task->consumed; + $stageIndex[$groupKey]['totalReal'] += $task->left + $task->consumed; + $stageIndex[$groupKey]['totalEstimate'] += $task->estimate; } /* Calculate group realBegan and realEnd. */ @@ -380,33 +236,9 @@ class programplanModel extends model if(!empty($realEndDate) and (count($realEndDate) == $totalTask)) $datas['data'][$groupKey]->realEnd = date('Y-m-d', max($realEndDate)); } - /* Calculate the progress of the phase. */ - foreach($stageIndex as $index => $stage) - { - $progress = empty($stage['totalReal']) ? 0 : round($stage['totalConsumed'] / $stage['totalReal'], 3); - $datas['data'][$index]->progress = $progress; - - $progress = ($progress * 100) . '%'; - $datas['data'][$index]->taskProgress = $progress; - $datas['data'][$index]->estimate = $stage['totalEstimate']; - $datas['data'][$index]->consumed = $stage['totalConsumed']; - } - - $datas['links'] = array(); - if($this->config->edition != 'open') - { - $relations = $this->dao->select('*')->from(TABLE_RELATIONOFTASKS)->where('execution')->in($planIdList)->orderBy('task,pretask')->fetchAll(); - foreach($relations as $relation) - { - $link['source'] = $relation->execution . '-' . $relation->pretask; - $link['target'] = $relation->execution . '-' . $relation->task; - $link['type'] = $this->config->execution->gantt->linkType[$relation->condition][$relation->action]; - $datas['links'][] = $link; - } - } - - $datas['data'] = isset($datas['data']) ? array_values($datas['data']) : array(); - + $datas = $this->programplanTao->setStageSummary($datas, $stageIndex); + $datas['links'] = $this->programplanTao->buildGanttLinks($planIdList); + $datas['data'] = isset($datas['data']) ? array_values($datas['data']) : array(); return $returnJson ? json_encode($datas) : $datas; } @@ -435,7 +267,6 @@ class programplanModel extends model public function processPlan(object $plan): object { $plan->setMilestone = true; - if($plan->parent) { $attribute = $this->dao->select('attribute')->from(TABLE_PROJECT)->where('id')->eq($plan->parent)->fetch('attribute'); @@ -926,9 +757,7 @@ class programplanModel extends model */ public function computeProgress(int $stageID, string $action = '', bool $isParent = false): bool { - $this->loadModel('execution'); - - $stage = $this->execution->getByID($stageID); + $stage = $this->loadModel('execution')->getByID($stageID); if(empty($stage) || empty($stage->path)) return false; $project = $this->loadModel('project')->getByID($stage->project); @@ -1105,41 +934,6 @@ class programplanModel extends model */ public function getStageAttribute(int $stageID): false|string { - $stageAttribute = $this->dao->select('attribute')->from(TABLE_EXECUTION)->where('id')->eq($stageID)->fetch('attribute'); - - if(dao::isError()) return false; - - return $stageAttribute; - } - - /** - * Get five days ago. - * - * @param string $date - * @param int $date - * @access public - * @return string - */ - public function getReviewDeadline(string $date, int $counter = 5) - { - if(helper::isZeroDate($date)) return ''; - - $weekend_days = [6, 7]; - - $timestamp = strtotime($date); - $i = 0; - $this->loadModel('holiday'); - while($i < $counter) - { - $timestamp = strtotime('-1 day', $timestamp); - $weekday = date('N', $timestamp); - $currentDate = date('Y-m-d', $timestamp); - if(!in_array($weekday, $weekend_days) and !$this->holiday->isHoliday($currentDate)) - { - $i ++; - } - } - - return date('Y-m-d', $timestamp); + return $this->dao->select('attribute')->from(TABLE_EXECUTION)->where('id')->eq($stageID)->fetch('attribute'); } } diff --git a/module/programplan/tao.php b/module/programplan/tao.php index 86602eeebd..d20603ee13 100644 --- a/module/programplan/tao.php +++ b/module/programplan/tao.php @@ -374,125 +374,49 @@ class programplanTao extends programplanModel protected function setTask(array $tasks, array $plans, string $selectCustom, array &$datas, array &$stageIndex): void { $this->app->loadLang('task'); - $taskPri = "%s "; $today = helper::today(); $taskTeams = $this->dao->select('task,account')->from(TABLE_TASKTEAM)->where('task')->in(array_keys($tasks))->fetchGroup('task', 'account'); $users = $this->loadModel('user')->getPairs('noletter'); foreach($tasks as $task) { - $execution = zget($plans, $task->execution, array()); - $pri = zget($this->lang->task->priList, $task->pri); - $priIcon = sprintf($taskPri, $task->pri, $pri, $pri); - - $estStart = helper::isZeroDate($task->estStarted) ? '' : $task->estStarted; - $estEnd = helper::isZeroDate($task->deadline) ? '' : $task->deadline; - $realBegan = helper::isZeroDate($task->realStarted) ? '' : $task->realStarted; - $realEnd = (in_array($task->status, array('done', 'closed')) and !helper::isZeroDate($task->finishedDate)) ? $task->finishedDate : ''; - - $start = $estStart; - $end = $estEnd; - if(empty($start) and $execution) $start = $execution->begin; - if(empty($end) and $execution) $end = $execution->end; - if($start > $end) $end = $start; - - $data = new stdclass(); - $data->id = $task->execution . '-' . $task->id; - $data->type = 'task'; - $data->text = $priIcon . $task->name; - $data->percent = ''; - $data->status = $this->processStatus('task', $task); - $data->owner_id = $task->assignedTo; - $data->attribute = ''; - $data->milestone = ''; - $data->begin = $start; - $data->deadline = $end; - $data->realBegan = $realBegan ? substr($realBegan, 0, 10) : ''; - $data->realEnd = $realEnd ? substr($realEnd, 0, 10) : ''; - $data->pri = $task->pri; - $data->parent = $task->parent > 0 ? $task->execution . '-' . $task->parent : $task->execution; - $data->open = true; - $progress = $task->consumed ? round($task->consumed / ($task->left + $task->consumed), 3) : 0; - $data->progress = $progress; - $data->taskProgress = ($progress * 100) . '%'; - $data->start_date = $start; - $data->endDate = $end; - $data->duration = 1; - $data->estimate = $task->estimate; - $data->consumed = $task->consumed; - $data->color = zget($this->lang->execution->gantt->color, $task->pri, $this->lang->execution->gantt->defaultColor); - $data->progressColor = zget($this->lang->execution->gantt->progressColor, $task->pri, $this->lang->execution->gantt->defaultProgressColor); - $data->textColor = zget($this->lang->execution->gantt->textColor, $task->pri, $this->lang->execution->gantt->defaultTextColor); - $data->bar_height = $this->lang->execution->gantt->bar_height; + $dateLimit = $this->getTaskDateLimit($task, zget($plans, $task->execution, null)); + $data = $this->buildTaskDataForGantt($task, $dateLimit); + $data->id = $task->execution . '-' . $task->id; + $data->parent = $task->parent > 0 ? $task->execution . '-' . $task->parent : $task->execution; /* Determines if the object is delay. */ $data->delay = $this->lang->programplan->delayList[0]; $data->delayDays = 0; - if($today > $end and $execution->status != 'closed') + if($today > $dateLimit['end'] and $execution->status != 'closed') { $data->delay = $this->lang->programplan->delayList[1]; - $data->delayDays = helper::diffDate(($task->status == 'done' || $task->status == 'closed') ? substr($task->finishedDate, 0, 10) : $today, substr($end, 0, 10)); + $data->delayDays = helper::diffDate(($task->status == 'done' || $task->status == 'closed') ? $task->finishedDate : $today, $dateLimit['end']); } /* If multi task then show the teams. */ - if($task->mode == 'multi' and !empty($taskTeams[$task->id])) - { - $teams = array_keys($taskTeams[$task->id]); - $assigneds = array(); - foreach($teams as $assignedTo) $assigneds[] = zget($users, $assignedTo); - $data->owner_id = implode(',', $assigneds); - } - - if($data->endDate > $data->start_date) $data->duration = helper::diffDate(substr($data->endDate, 0, 10), substr($data->start_date, 0, 10)) + 1; - if($data->start_date) $data->start_date = date('d-m-Y', strtotime($data->start_date)); - if($data->start_date == '' or $data->endDate == '') $data->duration = 1; + if($task->mode == 'multi' and !empty($taskTeams[$task->id])) $data->owner_id = implode(',', array_map(function($assignedTo) use($users){return zget($users, $assignedTo);}, array_keys($taskTeams[$task->id]))); if(strpos($selectCustom, 'task') !== false) $datas['data'][$data->id] = $data; foreach($stageIndex as $index => $stage) { - if($stage['planID'] == $task->execution) - { - $stageIndex[$index]['progress']['totalEstimate'] += $task->estimate; - $stageIndex[$index]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed; - $stageIndex[$index]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed); + if($stage['planID'] != $task->execution) continue; + if(!isset($stageIndex[$index])) continue; - $parent = $stage['parent']; - if(isset($stageIndex[$parent])) - { - $stageIndex[$parent]['progress']['totalEstimate'] += $task->estimate; - $stageIndex[$parent]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed; - $stageIndex[$parent]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed); - } - } + $stageIndex[$index]['progress']['totalEstimate'] += $task->estimate; + $stageIndex[$index]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed; + $stageIndex[$index]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed); + + if(!isset($stageIndex[$parent])) continue; + + $parent = $stage['parent']; + $stageIndex[$parent]['progress']['totalEstimate'] += $task->estimate; + $stageIndex[$parent]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed; + $stageIndex[$parent]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed); } } } - /** - * 设置关联任务数据。 - * Set relation task data. - * - * @param array $planIdList - * @param array $datas - * @access protected - * @return array - */ - protected function setRelationTask(array $planIdList, array $datas): array - { - $datas['links'] = array(); - if($this->config->edition == 'open') return $datas; - $relations = $this->dao->select('*')->from(TABLE_RELATIONOFTASKS)->where('execution')->in($planIdList)->orderBy('task,pretask')->fetchAll(); - foreach($relations as $relation) - { - $link['source'] = $relation->execution . '-' . $relation->pretask; - $link['target'] = $relation->execution . '-' . $relation->task; - $link['type'] = $this->config->execution->gantt->linkType[$relation->condition][$relation->action]; - $datas['links'][] = $link; - } - - return $datas; - } - /** * 获取阶段信息。 * Get stage list. @@ -504,7 +428,7 @@ class programplanTao extends programplanModel * @access protected * @return array */ - protected function getStageList(int $executionID, int $productID, string $browseType, $orderBy = 'id_asc') + protected function getStageList(int $executionID, int $productID, string $browseType, string $orderBy = 'id_asc'): array { if(empty($executionID)) return array(); $projectModel = $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($executionID)->fetch('model'); @@ -549,7 +473,7 @@ class programplanTao extends programplanModel * @access protected * @return array */ - protected function computeOrders(array $orders, array $plans):array + protected function computeOrders(array $orders, array $plans): array { if(empty($orders)) $orders = array(); asort($orders); @@ -617,6 +541,29 @@ class programplanTao extends programplanModel return $totalPercent; } + /** + * Get point end date. + * + * @param int $planID + * @param object $point + * @param array $reviewDeadline + * @access private + * @return string + */ + private function getPointEndDate(int $planID, object $point, array $reviewDeadline): string + { + if($point->end and !helper::isZeroDate($point->end)) return $point->end; + + $end = $reviewDeadline[$planID]['stageEnd']; + if(strpos($point->category, "DCP") !== false) return $this->getReviewDeadline($end, 2); + if(strpos($point->category, "TR") !== false) + { + if(isset($reviewDeadline[$planID]['taskEnd']) and !helper::isZeroDate($reviewDeadline[$planID]['taskEnd'])) return $reviewDeadline[$planID]['taskEnd']; + return $this->getReviewDeadline($end); + } + return $end; + } + /** * 构建IPD版本的甘特图数据。 * Build gantt's data for ipd edition. @@ -627,17 +574,17 @@ class programplanTao extends programplanModel * @param string $selectCustom * @param array $reviewDeadline * @access protected - * @return void + * @return array */ - protected function buildGanttData4IPD(array $datas, int $projectID, int $productID, string $selectCustom, array $reviewDeadline) + protected function buildGanttData4IPD(array $datas, int $projectID, int $productID, string $selectCustom, array $reviewDeadline): array { $this->loadModel('review'); $reviewPoints = $this->dao->select('t1.*, t2.status, t2.lastReviewedDate,t2.id as reviewID')->from(TABLE_OBJECT)->alias('t1') - ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.id = t2.object') - ->where('t1.deleted')->eq('0') - ->andWhere('t1.project')->eq($projectID) - ->andWhere('t1.product')->eq($productID) - ->fetchAll('id'); + ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.id = t2.object') + ->where('t1.deleted')->eq('0') + ->andWhere('t1.project')->eq($projectID) + ->andWhere('t1.product')->eq($productID) + ->fetchAll('id'); foreach($datas['data'] as $plan) { @@ -649,67 +596,18 @@ class programplanTao extends programplanModel if(!isset($point->status)) $point->status = ''; $categories = $this->config->stage->ipdReviewPoint->{$plan->attribute}; - if(in_array($point->category, $categories)) - { - if($point->end and !helper::isZeroDate($point->end)) - { - $end = $point->end; - } - else - { - $end = $reviewDeadline[$plan->id]['stageEnd']; - if(strpos($point->category, "TR") !== false) - { - if(isset($reviewDeadline[$plan->id]['taskEnd']) and !helper::isZeroDate($reviewDeadline[$plan->id]['taskEnd'])) - { - $end = $reviewDeadline[$plan->id]['taskEnd']; - } - else - { - $end = $this->getReviewDeadline($end); - } - } - elseif(strpos($point->category, "DCP") !== false) - { - $end = $this->getReviewDeadline($end, 2); - } - } + if(!in_array($point->category, $categories)) continue; - $data = new stdclass(); - $data->id = $plan->id . '-' . $point->category . '-' . $point->id; - $data->reviewID = $point->reviewID; - $data->type = 'point'; - $data->text = " " . $point->title; - $data->name = $point->title; - $data->attribute = ''; - $data->milestone = ''; - $data->owner_id = ''; - $data->rawStatus = $point->status; - $data->status = $point->status ? zget($this->lang->review->statusList, $point->status) : $this->lang->programplan->wait; - $data->status = "" . $data->status . ''; - $data->begin = $end; - $data->deadline = $end; - $data->realBegan = $point->createdDate; - $data->realEnd = $point->lastReviewedDate;; - $data->parent = $plan->id; - $data->open = true; - $data->start_date = $end; - $data->endDate = $end; - $data->duration = 1; - $data->color = isset($this->lang->programplan->reviewColorList[$point->status]) ? $this->lang->programplan->reviewColorList[$point->status] : '#FC913F'; - $data->progressColor = $this->lang->execution->gantt->stage->progressColor; - $data->textColor = $this->lang->execution->gantt->stage->textColor; - $data->bar_height = $this->lang->execution->gantt->bar_height; - - if($data->start_date) $data->start_date = date('d-m-Y', strtotime($data->start_date)); - - if($selectCustom && strpos($selectCustom, "point") !== false && !$plan->parent) $datas['data'][$data->id] = $data; - } + $dataID = "{$plan->id}-{$point->category}-{$point->id}"; + if($selectCustom && strpos($selectCustom, "point") !== false && !$plan->parent) $datas['data'][$dataID] = $this->buildPointDataForGantt($plan->id, $point, $reviewDeadline); } } + + return $datas; } /** + * 获取创建时的关联产品。 * Get linkProducts for create. * * @param int $projectID @@ -789,4 +687,283 @@ class programplanTao extends programplanModel return $stageID; } + + /** + * 根据任务列表,构建任务分组。 + * Build task group by assignedTo. + * + * @param array $task + * @access protected + * @return array + */ + protected function buildTaskGroup(array $tasks): array + { + $taskGroup = array(); + $multiTasks = array(); + foreach($tasks as $taskID => $task) + { + $taskGroup[$task->assignedTo][$taskID] = $task; + if($task->mode == 'multi') $multiTasks[$taskID] = $task->assignedTo; + } + if(empty($multiTasks)) return $taskGroup; + + $taskTeams = $this->dao->select('t1.*,t2.realname')->from(TABLE_TASKTEAM)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account') + ->where('t1.task')->in(array_keys($multiTasks)) + ->orderBy('t1.order') + ->fetchGroup('task', 'id'); + foreach($taskTeams as $taskID => $team) + { + $assignedTo = $multiTasks[$taskID]; + foreach($team as $member) + { + $account = $member->account; + if($account == $assignedTo) continue; + if(!isset($taskGroup[$account])) $taskGroup[$account] = array(); + + $taskGroup[$account][$taskID] = clone $tasks[$taskID]; + $taskGroup[$account][$taskID]->id = $taskID . '_' . $account; + $taskGroup[$account][$taskID]->realID = $taskID; + $taskGroup[$account][$taskID]->assignedTo = $account; + $taskGroup[$account][$taskID]->realname = $member->realname; + } + } + + return $taskGroup; + } + + /** + * Guild point data for gantt. + * + * @param int $planID + * @param object $point + * @param array $reviewDeadline + * @access protected + * @return object + */ + protected function buildPointDataForGantt(int $planID, object $point, array $reviewDeadline): object + { + $end = $this->getPointEndDate($planID, $point, $reviewDeadline); + $data = new stdclass(); + $data->id = $planID . '-' . $point->category . '-' . $point->id; + $data->reviewID = $point->reviewID; + $data->type = 'point'; + $data->text = " " . $point->title; + $data->name = $point->title; + $data->attribute = ''; + $data->milestone = ''; + $data->owner_id = ''; + $data->rawStatus = $point->status; + $data->status = $point->status ? zget($this->lang->review->statusList, $point->status) : $this->lang->programplan->wait; + $data->status = "" . $data->status . ''; + $data->begin = $end; + $data->deadline = $end; + $data->realBegan = $point->createdDate; + $data->realEnd = $point->lastReviewedDate;; + $data->parent = $planID; + $data->open = true; + $data->start_date = $end; + $data->endDate = $end; + $data->duration = 1; + $data->color = isset($this->lang->programplan->reviewColorList[$point->status]) ? $this->lang->programplan->reviewColorList[$point->status] : '#FC913F'; + $data->progressColor = $this->lang->execution->gantt->stage->progressColor; + $data->textColor = $this->lang->execution->gantt->stage->textColor; + $data->bar_height = $this->lang->execution->gantt->bar_height; + + if($data->start_date) $data->start_date = date('d-m-Y', strtotime($data->start_date)); + + return $data; + } + + /** + * 构建甘特图的分组数据。 + * Build group data for gantt. + * + * @param int $groupID + * @param string $group + * @param array $users + * @access protected + * @return object + */ + protected function buildGroupDataForGantt(int $groupID, string $group, array $users): object + { + $groupName = $group; + $groupName = zget($users, $group); + + $dataGroup = new stdclass(); + $dataGroup->id = $groupID; + $dataGroup->type = 'group'; + $dataGroup->text = $groupName; + $dataGroup->percent = ''; + $dataGroup->attribute = ''; + $dataGroup->milestone = ''; + $dataGroup->owner_id = $group; + $dataGroup->status = ''; + $dataGroup->begin = ''; + $dataGroup->deadline = ''; + $dataGroup->realBegan = ''; + $dataGroup->realEnd = ''; + $dataGroup->parent = 0; + $dataGroup->open = true; + $dataGroup->progress = ''; + $dataGroup->taskProgress = ''; + $dataGroup->color = $this->lang->execution->gantt->stage->color; + $dataGroup->progressColor = $this->lang->execution->gantt->stage->progressColor; + $dataGroup->textColor = $this->lang->execution->gantt->stage->textColor; + $dataGroup->bar_height = $this->lang->execution->gantt->bar_height; + + return $dataGroup; + } + + /** + * 构建甘特图的任务数据。 + * Build task data for gantt. + * + * @param int $groupID + * @param object $task + * @param array $dateLimit array('start' => $start, 'end' => $end, 'realBegan' => $realBegan, 'realEnd' => $realEnd); + * @param array $tasksMap + * @access protected + * @return object + */ + protected function buildTaskDataForGantt(object $task, array $dateLimit, int $groupID = 0, array $tasksMap = array()): object + { + $taskPri = "%s "; + $pri = zget($this->lang->task->priList, $task->pri); + $priIcon = sprintf($taskPri, $task->pri, $pri, $pri); + $progress = $task->consumed ? round($task->consumed / ($task->left + $task->consumed), 3) : 0; + + $data = new stdclass(); + $data->id = $task->id; + $data->type = 'task'; + $data->text = $priIcon . $task->name; + $data->percent = ''; + $data->status = $this->processStatus('task', $task); + $data->owner_id = $task->assignedTo; + $data->attribute = ''; + $data->milestone = ''; + $data->begin = $dateLimit['start']; + $data->deadline = $dateLimit['end']; + $data->realBegan = $dateLimit['realBegan'] ? substr($dateLimit['realBegan'], 0, 10) : ''; + $data->realEnd = $dateLimit['realEnd'] ? substr($dateLimit['realEnd'], 0, 10) : ''; + $data->pri = $task->pri; + $data->parent = ($task->parent > 0 and $task->assignedTo != '' and !empty($tasksMap[$task->parent]->assignedTo)) ? $task->parent : $groupID; + $data->open = true; + $data->progress = $progress; + $data->taskProgress = ($progress * 100) . '%'; + $data->start_date = $dateLimit['start']; + $data->endDate = $dateLimit['end']; + $data->duration = 1; + $data->estimate = $task->estimate; + $data->consumed = $task->consumed; + $data->color = zget($this->lang->execution->gantt->color, $task->pri, $this->lang->execution->gantt->defaultColor); + $data->progressColor = zget($this->lang->execution->gantt->progressColor, $task->pri, $this->lang->execution->gantt->defaultProgressColor); + $data->textColor = zget($this->lang->execution->gantt->textColor, $task->pri, $this->lang->execution->gantt->defaultTextColor); + $data->bar_height = $this->lang->execution->gantt->bar_height; + + if($data->endDate > $data->start_date) $data->duration = helper::diffDate($data->endDate, $data->start_date) + 1; + if(empty($data->start_date) or empty($data->endDate)) $data->duration = 1; + if($data->start_date) $data->start_date = date('d-m-Y', strtotime($data->start_date)); + + return $data; + } + + /** + * 构建甘特图的关系链接 + * Build gantt links. + * + * @param array $planIdList + * @access protected + * @return array + */ + protected function buildGanttLinks(array $planIdList): array + { + if($this->config->edition == 'open') array(); + + $links = array(); + $relations = $this->dao->select('*')->from(TABLE_RELATIONOFTASKS)->where('execution')->in($planIdList)->orderBy('task,pretask')->fetchAll(); + foreach($relations as $relation) + { + $link = array(); + $link['source'] = $relation->execution . '-' . $relation->pretask; + $link['target'] = $relation->execution . '-' . $relation->task; + $link['type'] = $this->config->execution->gantt->linkType[$relation->condition][$relation->action]; + $links[] = $link; + } + return $links; + } + + /** + * 设置阶段统计数据。 + * Set stage summary. + * + * @param array $ganttData + * @param array $stages + * @access protected + * @return array + */ + protected function setStageSummary(array $ganttData, array $stages): array + { + foreach($stages as $groupKey => $stage) + { + $progress = empty($stage['totalReal']) ? 0 : round($stage['totalConsumed'] / $stage['totalReal'], 3); + $ganttData['data'][$groupKey]->progress = $progress; + $ganttData['data'][$groupKey]->taskProgress = ($progress * 100) . '%'; + $ganttData['data'][$groupKey]->estimate = $stage['totalEstimate']; + $ganttData['data'][$groupKey]->consumed = $stage['totalConsumed']; + } + return $ganttData; + } + + /** + * 获取任务任务限制。获取该任务在甘特图中的开始,结束时间。 + * Get task date limit. + * + * @param object $task + * @param object|null $execution + * @access protected + * @return array + */ + protected function getTaskDateLimit(object $task, object|null $execution): array + { + $estStart = helper::isZeroDate($task->estStarted) ? '' : $task->estStarted; + $estEnd = helper::isZeroDate($task->deadline) ? '' : $task->deadline; + $realBegan = helper::isZeroDate($task->realStarted) ? '' : $task->realStarted; + $realEnd = (in_array($task->status, array('done', 'closed')) and !helper::isZeroDate($task->finishedDate)) ? $task->finishedDate : ''; + + $start = $realBegan ? $realBegan : $estStart; + $end = $realEnd ? $realEnd : $estEnd; + if(empty($start) and $execution) $start = $execution->begin; + if(empty($end) and $execution) $end = $execution->end; + if($start > $end) $end = $start; + + return array('start' => $start, 'end' => $end, 'realBegan' => $realBegan, 'realEnd' => $realEnd); + } + + /** + * Get five days ago. + * + * @param string $date + * @param int $date + * @access public + * @return string + */ + public function getReviewDeadline(string $date, int $counter = 5): string + { + if(helper::isZeroDate($date)) return ''; + + $this->loadModel('holiday'); + $weekendDays = array(6, 7); + $timestamp = strtotime($date); + $index = 0; + while($index < $counter) + { + $timestamp -= 24 * 3600; + $weekday = date('N', $timestamp); + $currentDate = date('Y-m-d', $timestamp); + if(!in_array($weekday, $weekendDays) and !$this->holiday->isHoliday($currentDate)) $index ++; + } + + return date('Y-m-d', $timestamp); + } }