cookie->showStage) return $rows;
$this->app->loadConfig('project');
$this->loadModel('review');
$this->loadModel('approval');
foreach($points as $point)
{
$canSubmit = true;
foreach($this->config->project->execution->dtable->actionsRule['point'] as $actionName)
{
$actions = explode('|', $actionName);
foreach($actions as $name)
{
$rawAction = str_replace('Review', '', $name);
$action = array('name' => $name);
if($rawAction == 'progress' && !common::hasPriv('approval', 'progress')) continue;
elseif($rawAction != 'progress' && !common::hasPriv('review', $rawAction)) continue;
$clickable = $this->review->isClickable($point, $rawAction);
$disabled = $clickable ? false : true;
/* 阶段未开始时,当前评审点不允许评审。*/
if($rawAction == 'create' && $point->execStatus == 'wait')
{
$disabled = true;
$action['hint'] = $this->lang->review->stageNotStartTip;
}
/* 前一评审点未评审通过时,当前评审点不允许评审。*/
if(!$point->isFirst && $rawAction == 'create' && $point->preResult != 'pass' && !$point->result)
{
$disabled = true;
$action['hint'] = $this->lang->review->prePointNotPassTip;
}
if(!empty($execution->isTpl) && $rawAction == 'create') $disabled = true;
if($point->result == 'pass' && $rawAction == 'create') $disabled = true;
if($rawAction == 'recall' && $point->status != 'reverting' && $this->approval->canCancel($point)) $disabled = false;
if($rawAction == 'assess') $disabled = !isset($pendingReviews[$point->review]);
if($rawAction == 'create' && $disabled) $canSubmit = false;
$action['disabled'] = $disabled;
if(!$disabled) break;
}
if($action) $point->actions[] = $action;
}
$point->name = (common::hasPriv('review', 'view') and $point->rawStatus) ? html::a(helper::createLink('review', 'view', "id=$point->review"), $point->text, '', "", "data-app='project'") : "$point->text";
if(!$point->rawStatus && $canSubmit) $point->name .= html::a(helper::createLink('review', 'create', "projectID=$point->project&deliverable=0&reviewID=0&type=decision&objectID={$point->category}"), '', '', 'class="btn btn-link submitBtn" title="' . $this->lang->programplan->submit . '"');
$point->rawID = $point->id;
$point->id = 'tid' . (string)$point->id;
$point->end = $point->deadline;
$point->left = '';
$point->consumed = '';
$point->estimate = '';
$point->begin = '';
$point->parent = 'pid' . (string)$point->parent;
$rows[] = $point;
}
return $rows;
}
/**
* Check ipd stage is ready to begin.
*
* @param array $stages
* @access public
* @return array
*/
public function appendActions($stages = array())
{
if(empty($stages)) return $stages;
$projects = $this->loadModel('project')->getPairsByModel('ipd');
$newStages = array();
$ipdProjectGroup = array();
foreach($stages as $stage)
{
$stageProject = $stage->project;
$newStages[$stage->id] = $stage;
if($stage->parallel) continue; // 如果是并行阶段,跳过
if(!isset($projects[$stageProject])) continue; // 不是IPD项目,跳过
if($stage->parent != $stage->project) continue; // 不是父阶段,跳过
$ipdProjectGroup[$stageProject][$stage->order] = $stage;
}
if(empty($ipdProjectGroup)) return $stages; // Do not need to append actions (no $ipdProjectGroup)
foreach($ipdProjectGroup as $project => $projectStages)
{
$preStage = '';
foreach($projectStages as $order => $stage)
{
$preStageName = $preStage ? $preStage->name : '';
$stage->action = new stdclass();
$stage->action->start = array('disabled' => false, 'hint' => '', 'preStageName' => $preStageName, 'stageName' => $stage->name);
$stage->action->close = $stage->action->start;
/* 上一阶段结束,当前阶段才能开始。*/
if($preStage && $preStage->status != 'closed')
{
$message = sprintf($this->lang->execution->disabledTip->startTip, $preStage->name , $stage->name);
$stage->action->start['disabled'] = true;
$stage->action->start['hint'] = $message;
}
/* 当前阶段评审点都通过,当前阶段才能关闭。*/
if(empty($stage->points)) continue;
foreach($stage->points as $point)
{
if($point->result == 'pass') continue;
$message = $this->lang->execution->disabledTip->closeTip;
$stage->action->close['disabled'] = true;
$stage->action->close['hint'] = $message;
}
$preStage = $stage;
$newStages[$stage->id] = $stage;
}
}
return $newStages;
}
/**
* 获取执行列表数据。
* Get statData.
*
* @param int $projectID
* @param string $browseType
* @param int $productID
* @param int $branch
* @param bool $withTasks
* @param string|int $param
* @param string $orderBy
* @param object|null $pager
* @access public
* @return array
*/
public function getStatData($projectID = 0, $browseType = 'undone', $productID = 0, $branch = 0, $withTasks = false, $param = '', $orderBy = 'order_asc', $pager = null)
{
$executions = parent::getStatData($projectID, $browseType, $productID, $branch, $withTasks, $param, $orderBy, $pager);
$project = $this->loadModel('project')->getByID($projectID);
if(!$this->app->tab == 'project' || ($project && $project->model != 'ipd')) return $executions;
$points = $this->loadModel('review')->getPointsByProjectID($projectID);
$executions = $this->buildExecutionTree($executions, $points);
$executions = $this->appendActions($executions);
return $executions;
}
/**
* 构建执行树。
* Build execution tree.
*
* @param array $executions
* @param array $points
*
* @access public
* @return array
*/
public function buildExecutionTree($executions = array(), $points = array())
{
$prePointResult = '';
$isFirst = true;
$reviewDeadline = array();
$this->loadModel('review');
$this->loadModel('programplan');
foreach($executions as $execution)
{
if($execution->projectModel == 'ipd' && $execution->grade == 1) $execution->isIpdStage = true;
if($execution->grade > 1) continue; // 如果是子阶段,跳过
$reviewDeadline[$execution->id]['stageEnd'] = $execution->end;
$reviewDeadline[$execution->id]['stageBegin'] = $execution->begin;
foreach($points as $id => $point)
{
if($point->execution != $execution->id) continue;
$data = new stdclass();
$data->id = $execution->id . '-' . $point->categoryTitle . '-' . $point->id;
$data->type = 'point';
$data->text = " " . $point->title;
$data->name = $point->title;
$data->rawName = $point->title;
$data->open = true;
$data->duration = 1;
$data->parent = $execution->id;
$data->review = $point->review;
$data->result = $point->result;
$data->approval = $point->approval;
$data->project = $execution->project;
$data->begin = $data->deadline = $data->endDate = $point->deadline;
$data->realBegan = $point->realBegan;
$data->realEnd = $point->lastReviewedDate;;
$data->rawStatus = $point->status;
$data->status = $point->status ? $point->status : $this->lang->programplan->wait;
$data->preResult = $prePointResult;
$data->execStatus = $execution->status;
$data->isFirst = $isFirst;
$data->deadline = $this->programplan->getPointEndDate($execution->id, $point, $reviewDeadline);
$data->category = $point->category;
if($data->status == 'wait') $data->status = zget($this->lang->review->statusList, $data->status);
if($isFirst) $isFirst = false;
$execution->points[] = $data;
$execution->isIpdStage = true;
$prePointResult = $point->result;
}
}
return $executions;
}
/*
* 构建任务操作按钮。
* Build task actions.
*
* @param array $executions
* @access public
* @return array
*/
/**
* @param mixed[] $executions
*/
public function buildTaskActions($executions = array())
{
foreach($executions as $execution)
{
if(empty($execution->tasks)) continue;
if(empty($execution->action->start['disabled'])) continue;
$startAction = $execution->action->start;
foreach($execution->tasks as $task)
{
foreach($task->actions as $key => $action)
{
if(!in_array($action['name'], array('startTask', 'finishTask', 'recordWorkhour'))) continue;
$action['disabled'] = true;
if(isset($startAction['preStageName']) && $action['name'] == 'startTask') $action['hint'] = sprintf($this->lang->execution->disabledTip->taskStartTip, $startAction['preStageName'], $startAction['stageName']);
if(isset($startAction['preStageName']) && $action['name'] == 'finishTask') $action['hint'] = sprintf($this->lang->execution->disabledTip->taskFinishTip, $startAction['preStageName'], $startAction['stageName']);
if(isset($startAction['preStageName']) && $action['name'] == 'recordWorkhour') $action['hint'] = sprintf($this->lang->execution->disabledTip->taskRecordTip, $startAction['preStageName'], $startAction['stageName']);
$task->actions[$key] = $action;
}
}
}
return $executions;
}
/**
* 生成dtable的行数据。
* Generate row for dtable.
*
* @param array $executions
* @param array $users
* @param array $avatarList
* @access public
* @return array
*/
public function generateRow($executions, $users, $avatarList)
{
$executions = parent::generateRow($executions, $users, $avatarList);
$pendingReviews = $this->loadModel('approval')->getPendingReviews('review');
$startActionInfo = array();
foreach($executions as $execution)
{
foreach($execution->actions as $key => $action)
{
$actionName = $action['name'];
if($actionName != 'start' && $actionName != 'close') continue;
/* 如果是子阶段的开始动作,则继承父阶段的开始动作。*/
if($execution->grade > 1 && $actionName == 'start' && isset($startActionInfo[$execution->parent]))
{
if(!isset($execution->action)) $execution->action = new stdclass();
$execution->action->start = $startActionInfo[$execution->parent];
$execution->actions[$key] = $startActionInfo[$execution->parent];
$startActionInfo[$execution->id] = $startActionInfo[$execution->parent];
continue;
}
if(empty($execution->action->$actionName['disabled']))
{
/* 阶段关闭未被禁用时,如果阶段的开始动作被禁用,阶段的关闭动作也被禁用。 */
if($actionName == 'close' && isset($execution->action->start) && !empty($execution->action->start['disabled']))
{
$execution->actions[$key]['disabled'] = true;
$execution->action->close['disabled'] = true;
$execution->actions[$key]['hint'] = '';
$execution->action->close['hint'] = '';
}
continue;
}
$action['disabled'] = $execution->action->$actionName['disabled'];
$action['hint'] = $execution->action->$actionName['hint'];
if($actionName == 'start')
{
$startActionInfo[$execution->id] = $action;
$execution->action->start = array_merge($action, $execution->action->start);
}
$execution->actions[$key] = $action;
}
if(!empty($execution->points)) $executions = $this->appendPoints($execution->points, $execution, $executions, $pendingReviews);
}
if($this->cookie->showTask) $executions = $this->buildTaskActions($executions);
return $executions;
}
/*
* 检查阶段能否开启或关闭。
* Check ipd stage is ready to begin.
*
* @param int $executionID
* @param string $option
* @access public
* @return array
*/
/**
* @param int $executionID
* @param string $option
*/
public function checkStageStatus($executionID = 0, $option = 'start')
{
$execution = $this->getById($executionID);
$project = $this->loadModel('project')->getById($execution->project);
if(isset($project->model) && $project->model == 'ipd')
{
$executions = $this->getStatData($execution->project, 'all');
$execution = isset($executions[$executionID]) ? $executions[$executionID] : $execution;
/* 子阶段的开始动作继承父阶段的开始动作。 */
if(!isset($execution->action) && $execution->grade > 1)
{
$pathExecutions = explode(',', trim($execution->path, ','));
$firstGradeExecutionID = isset($pathExecutions[1]) ? $pathExecutions[1] : 0;
if($firstGradeExecutionID && !empty($executions[$firstGradeExecutionID]) && !empty($executions[$firstGradeExecutionID]->action) && !empty($executions[$firstGradeExecutionID]->action->start))
{
if(!isset($execution->action)) $execution->action = new stdclass();
$execution->action->start = $executions[$firstGradeExecutionID]->action->start;
if(!empty($execution->action->start['disabled']))
{
/* 如果父阶段的开始动作被禁用,子阶段的关闭动作也被禁用。 */
if(!isset($execution->action->close)) $execution->action->close = new stdclass();
$execution->action->close = $execution->action->start;
}
}
}
if(!empty($execution->action->$option['hint']))
{
return array('message' => $execution->action->$option['hint'], 'disabled' => true);
}
}
return array('disabled' => false);
}
/**
* 根据产品/执行等信息获取任务列表
* Get tasks by product/execution etc.
*
* @param int $productID
* @param int|array $executionID
* @param array $executions
* @param string $browseType
* @param int $queryID
* @param int $moduleID
* @param string $sort
* @param object $pager
* @access public
* @return array
*/
public function getTasks($productID, $executionID, $executions, $browseType, $queryID, $moduleID, $sort, $pager = null)
{
$tasks = parent::getTasks($productID, $executionID, $executions, $browseType, $queryID, $moduleID, $sort, $pager);
return $this->loadModel('story')->getAffectObject($tasks, 'task');
}
/**
* 判断操作按钮是否可以点击。
* Judge an action is clickable or not.
*
* @param object $execution
* @param string $action
* @access public
* @return bool
*/
public static function isClickable($execution, $action)
{
if($action != 'delete') return parent::isClickable($execution, $action);
if($action == 'delete')
{
if((!empty($execution->isIpdStage) || (isset($execution->projectInfo) && $execution->projectInfo->model == 'ipd')) && $execution->grade == 1)
{
return false;
}
else
{
return parent::isClickable($execution, $action);
}
}
}