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);
+ }
}