From 90b4ece099e4e2c006dc27f09a5e3054f48d7d26 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Tue, 26 Dec 2023 16:25:26 +0800 Subject: [PATCH] * Refactor programplan create page. --- module/execution/model.php | 2 + module/execution/tao.php | 5 +- module/program/tao.php | 1 + module/programplan/config/form.php | 84 +++--- module/programplan/control.php | 8 +- module/programplan/model.php | 353 +++----------------------- module/programplan/tao.php | 91 ++++++- module/programplan/ui/create.html.php | 46 +--- module/programplan/zen.php | 82 +++++- 9 files changed, 259 insertions(+), 413 deletions(-) diff --git a/module/execution/model.php b/module/execution/model.php index 49ad4d6739..22a17e1d70 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -3214,6 +3214,8 @@ class executionModel extends model */ public function addProjectMembers(int $projectID = 0, array $members = array()) { + if(empty($members)) return; + $projectType = 'project'; $oldJoin = $this->dao->select('`account`, `join`')->from(TABLE_TEAM)->where('root')->eq($projectID)->andWhere('type')->eq($projectType)->fetchPairs(); diff --git a/module/execution/tao.php b/module/execution/tao.php index ed90d73a97..423c3ed9bd 100644 --- a/module/execution/tao.php +++ b/module/execution/tao.php @@ -545,8 +545,11 @@ class executionTao extends executionModel $roles = $this->loadModel('user')->getUserRoles(array_values($members)); $today = helper::today(); $teamMembers = array(); + $oldTeams = $this->dao->select('account')->from(TABLE_TEAM)->where('root')->eq($executionID)->andWhere('type')->eq('execution')->fetchPairs(); foreach($members as $account) { + if(isset($oldTeams[$account])) continue; + $member = new stdClass(); $member->root = $executionID; $member->type = 'execution'; @@ -592,7 +595,7 @@ class executionTao extends executionModel $lib->addedDate = helper::now(); $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec(); - if(dao::isError())return false; + if(dao::isError()) return false; return $this->dao->lastInsertID(); } } diff --git a/module/program/tao.php b/module/program/tao.php index d6bf1ad7c8..dc78fab5fa 100644 --- a/module/program/tao.php +++ b/module/program/tao.php @@ -306,6 +306,7 @@ class programTao extends programModel foreach($summary as $execution) { $executionID = $execution->execution; + if(!isset($executionPaths[$executionID])) continue; foreach($executionPaths[$executionID] as $nodeID) { if(!isset($stats[$nodeID])) $stats[$nodeID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalLeftNotDel' => 0, 'totalConsumedNotDel' => 0); diff --git a/module/programplan/config/form.php b/module/programplan/config/form.php index f52a1827b1..4a4af6c668 100644 --- a/module/programplan/config/form.php +++ b/module/programplan/config/form.php @@ -1,54 +1,46 @@ programplan->create)) $config->programplan->create = new stdclass; -$config->programplan->create->form = array(); -$config->programplan->create->formFields = array('planIDList', 'names', 'PM', 'percents', 'attributes', 'acl', 'milestone', 'begin', 'end', 'realBegan', 'realEnd', 'desc', 'orders', 'type'); -foreach($config->programplan->create->formFields as $field) $config->programplan->create->form[$field] = array('required' => false, 'type' => 'array'); - -if(!isset($config->programplan->edit)) $config->programplan->edit = new stdClass(); -$config->programplan->edit->form = array(); -$config->programplan->edit->form['parent'] = array('required' => false, 'type' => 'int', 'default' => 0); -$config->programplan->edit->form['name'] = array('required' => true, 'type' => 'string', 'default' => ''); -$config->programplan->edit->form['code'] = array('required' => false, 'type' => 'string', 'default' => ''); -$config->programplan->edit->form['PM'] = array('required' => false, 'type' => 'string', 'default' => ''); -$config->programplan->edit->form['percent'] = array('required' => false, 'type' => 'float', 'default' => 0); -$config->programplan->edit->form['attribute'] = array('required' => false, 'type' => 'string', 'default' => ''); -$config->programplan->edit->form['acl'] = array('required' => false, 'type' => 'string', 'default' => ''); -$config->programplan->edit->form['begin'] = array('required' => false, 'type' => 'string', 'default' => '0000-00-00'); -$config->programplan->edit->form['end'] = array('required' => false, 'type' => 'string', 'default' => '0000-00-00'); -$config->programplan->edit->form['realBegan'] = array('required' => false, 'type' => 'string', 'default' => '0000-00-00'); -$config->programplan->edit->form['realEnd'] = array('required' => false, 'type' => 'string', 'default' => '0000-00-00'); -$config->programplan->edit->form['milestone'] = array('required' => false, 'type' => 'int', 'default' => 0); -$config->programplan->edit->form['output'] = array('required' => false, 'type' => 'array', 'default' => array()); - -$config->programplan->ajaxCustom = new stdClass(); -$config->programplan->ajaxCustom->form = array(); -$config->programplan->ajaxCustom->form['zooming'] = array('required' => false, 'type' => 'string'); -$config->programplan->ajaxCustom->form['stageCustom'] = array('required' => false, 'type' => 'array'); -$config->programplan->ajaxCustom->form['ganttFields'] = array('required' => false, 'type' => 'array'); - /* Batch stages creation. */ global $app, $lang; $app->loadLang('execution'); $app->loadLang('stage'); -!isset($config->programplan->form) && $config->programplan->form = new stdClass(); +$config->programplan->form = new stdClass(); $config->programplan->form->create = common::formConfig('programplan', 'create'); -$config->programplan->form->create['planIDList'] = array('label' => '', 'type' => 'array', 'control' => 'text', 'required' => false, 'default' => ''); -$config->programplan->form->create['orders'] = array('label' => '', 'type' => 'array', 'control' => 'text', 'required' => false, 'default' => ''); -$config->programplan->form->create['type'] = array('label' => $lang->execution->method, 'type' => 'array', 'control' => 'picker', 'required' => true, 'default' => '', 'options' => $lang->execution->typeList); -$config->programplan->form->create['name'] = array('label' => $lang->nameAB, 'type' => 'array', 'control' => 'text', 'required' => true, 'default' => '', 'base' => true); -$config->programplan->form->create['code'] = array('label' => $lang->code, 'type' => 'array', 'control' => 'text', 'required' => false, 'default' => '', 'options' => array()); -$config->programplan->form->create['PM'] = array('label' => $lang->programplan->PMAB, 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => array()); -$config->programplan->form->create['percent'] = array('label' => $lang->programplan->percent, 'type' => 'array', 'control' => 'number', 'required' => false, 'default' => 0, 'options' => array()); -$config->programplan->form->create['attribute'] = array('label' => $lang->programplan->attribute, 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => 0, 'options' => $lang->stage->typeList); -$config->programplan->form->create['acl'] = array('label' => $lang->programplan->acl, 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => 'open', 'options' => $lang->execution->aclList); -$config->programplan->form->create['begin'] = array('label' => $lang->programplan->begin, 'type' => 'array', 'control' => 'date', 'required' => true, 'default' => ''); -$config->programplan->form->create['end'] = array('label' => $lang->programplan->end, 'type' => 'array', 'control' => 'date', 'required' => true, 'default' => ''); -$config->programplan->form->create['realBegan'] = array('label' => $lang->programplan->realBegan, 'type' => 'array', 'control' => 'date', 'required' => false, 'default' => ''); -$config->programplan->form->create['realEnd'] = array('label' => $lang->programplan->realEnd, 'type' => 'array', 'control' => 'date', 'required' => false, 'default' => ''); -$config->programplan->form->create['milestone'] = array('label' => $lang->programplan->milestone, 'type' => 'array', 'control' => 'radioList','required' => false, 'default' => 0, 'options' => $lang->programplan->milestoneList); -$config->programplan->form->create['desc'] = array('label' => $lang->programplan->desc, 'type' => 'array', 'control' => 'textarea', 'required' => false, 'default' => ''); +$config->programplan->form->create['id'] = array('label' => '', 'type' => 'int', 'control' => 'text', 'required' => false, 'default' => 0); +$config->programplan->form->create['order'] = array('label' => '', 'type' => 'int', 'control' => 'text', 'required' => false, 'default' => 0); +$config->programplan->form->create['type'] = array('label' => $lang->execution->method, 'type' => 'string', 'control' => 'picker', 'required' => true, 'default' => 'stage', 'options' => $lang->execution->typeList); +$config->programplan->form->create['name'] = array('label' => $lang->nameAB, 'type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'base' => true, 'filter' => 'trim'); +$config->programplan->form->create['code'] = array('label' => $lang->code, 'type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'filter' => 'trim'); +$config->programplan->form->create['PM'] = array('label' => $lang->programplan->PMAB, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => ''); +$config->programplan->form->create['percent'] = array('label' => $lang->programplan->percent, 'type' => 'float', 'control' => 'text', 'required' => false, 'default' => 0); +$config->programplan->form->create['attribute'] = array('label' => $lang->programplan->attribute, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 0, 'options' => $lang->stage->typeList); +$config->programplan->form->create['acl'] = array('label' => $lang->programplan->acl, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 'open', 'options' => $lang->execution->aclList); +$config->programplan->form->create['begin'] = array('label' => $lang->programplan->begin, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null); +$config->programplan->form->create['end'] = array('label' => $lang->programplan->end, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null); +$config->programplan->form->create['realBegan'] = array('label' => $lang->programplan->realBegan, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); +$config->programplan->form->create['realEnd'] = array('label' => $lang->programplan->realEnd, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); +$config->programplan->form->create['milestone'] = array('label' => $lang->programplan->milestone, 'type' => 'int', 'control' => 'radioList','required' => false, 'default' => 0, 'options' => $lang->programplan->milestoneList); +$config->programplan->form->create['desc'] = array('label' => $lang->programplan->desc, 'type' => 'string', 'control' => 'textarea', 'required' => false, 'default' => '', 'filter' => 'trim'); +$config->programplan->form->create['output'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => ''); + +$config->programplan->form->edit = common::formConfig('programplan', 'edit'); +$config->programplan->form->edit['parent'] = array('required' => false, 'type' => 'int', 'default' => 0); +$config->programplan->form->edit['name'] = array('required' => true, 'type' => 'string', 'default' => ''); +$config->programplan->form->edit['code'] = array('required' => false, 'type' => 'string', 'default' => ''); +$config->programplan->form->edit['PM'] = array('required' => false, 'type' => 'string', 'default' => ''); +$config->programplan->form->edit['percent'] = array('required' => false, 'type' => 'float', 'default' => 0); +$config->programplan->form->edit['attribute'] = array('required' => false, 'type' => 'string', 'default' => ''); +$config->programplan->form->edit['acl'] = array('required' => false, 'type' => 'string', 'default' => ''); +$config->programplan->form->edit['begin'] = array('required' => false, 'type' => 'string', 'default' => null); +$config->programplan->form->edit['end'] = array('required' => false, 'type' => 'string', 'default' => null); +$config->programplan->form->edit['realBegan'] = array('required' => false, 'type' => 'string', 'default' => null); +$config->programplan->form->edit['realEnd'] = array('required' => false, 'type' => 'string', 'default' => null); +$config->programplan->form->edit['milestone'] = array('required' => false, 'type' => 'int', 'default' => 0); +$config->programplan->form->edit['output'] = array('required' => false, 'type' => 'array', 'default' => '', 'filter' => 'join'); + +$config->programplan->form->ajaxCustom = array(); +$config->programplan->form->ajaxCustom['zooming'] = array('required' => false, 'type' => 'string'); +$config->programplan->form->ajaxCustom['stageCustom'] = array('required' => false, 'type' => 'array'); +$config->programplan->form->ajaxCustom['ganttFields'] = array('required' => false, 'type' => 'array'); $config->programplan->form->updateDateByGantt['id'] = array('required' => false, 'type' => 'int', 'default' => 0); $config->programplan->form->updateDateByGantt['startDate'] = array('required' => false, 'type' => 'string', 'default' => null); @@ -57,3 +49,5 @@ $config->programplan->form->updateDateByGantt['type'] = array('required' => $config->programplan->form->updateTaskOrderByGantt['id'] = array('required' => false, 'type' => 'string', 'default' => ''); $config->programplan->form->updateTaskOrderByGantt['tasks'] = array('required' => false, 'type' => 'array', 'default' => array()); + +if(empty($config->setCode)) unset($config->programplan->form->create['code']); diff --git a/module/programplan/control.php b/module/programplan/control.php index 45450cdbcf..2cb6ecd89a 100644 --- a/module/programplan/control.php +++ b/module/programplan/control.php @@ -82,10 +82,10 @@ class programplan extends control $this->productID = $this->commonAction($projectID, $productID); if($_POST) { - $formData = form::data($this->config->programplan->form->create); - $formData = $this->programplanZen->beforeCreate($formData); + $plans = $this->programplanZen->buildPlansForCreate($projectID, $planID); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - $this->programplan->create($formData, $projectID, $this->productID, $planID); + $this->programplan->create($plans, $projectID, $this->productID, $planID); if(dao::isError()) { $errors = dao::getError(); @@ -147,7 +147,7 @@ class programplan extends control if($_POST) { - $formData = form::data($this->config->programplan->edit->form); + $formData = form::data($this->config->programplan->form->edit); $postData = $this->programplanZen->beforeEdit($formData); $postData->id = $planID; diff --git a/module/programplan/model.php b/module/programplan/model.php index 7cb3a8f315..e92752adc9 100755 --- a/module/programplan/model.php +++ b/module/programplan/model.php @@ -451,12 +451,10 @@ class programplanModel extends model } } - $replaced = '0000-00-00'; - - $plan->begin = $plan->begin == $replaced ? '' : $plan->begin; - $plan->end = $plan->end == $replaced ? '' : $plan->end; - $plan->realBegan = $plan->realBegan == $replaced ? '' : $plan->realBegan; - $plan->realEnd = $plan->realEnd == $replaced ? '' : $plan->realEnd; + $plan->begin = helper::isZeroDate($plan->begin) ? '' : $plan->begin; + $plan->end = helper::isZeroDate($plan->end) ? '' : $plan->end; + $plan->realBegan = helper::isZeroDate($plan->realBegan) ? '' : $plan->realBegan; + $plan->realEnd = helper::isZeroDate($plan->realEnd) ? '' : $plan->realEnd; $plan->product = $this->loadModel('product')->getProductIDByProject($plan->id); $plan->productName = $this->dao->findByID($plan->product)->from(TABLE_PRODUCT)->fetch('name'); @@ -482,353 +480,74 @@ class programplanModel extends model * 创建/设置一个项目阶段。 * Create/Set a project plan/phase. * - * @param object $formData + * @param array $plans * @param int $projectID * @param int $productID * @param int $parentID * @access public * @return bool */ - public function create(object $formData, int $projectID = 0, int $productID = 0, int $parentID = 0): bool + public function create(array $plans, int $projectID = 0, int $productID = 0, int $parentID = 0): bool { - /* Get every value from formData without use extract(). */ - $planIDList = $formData->get('planIDList'); - $names = $formData->get('name'); - $projectManager = $formData->get('PM'); - $percent = $formData->get('percent'); - $attributes = $formData->get('attribute'); - $acl = $formData->get('acl'); - $milestone = $formData->get('milestone'); - $begin = $formData->get('begin'); - $end = $formData->get('end'); - $realBegan = $formData->get('realBegan'); - $realEnd = $formData->get('realEnd'); - $desc = $formData->get('desc'); - $orders = $formData->get('orders'); - $type = $formData->get('type'); - $code = $formData->get('code'); - $output = $formData->get('output'); - - /* Determine if a task has been created under the parent phase. */ - if(!$this->isCreateTask($parentID)) return dao::$errors['message'][] = $this->lang->programplan->error->createdTask; - - /* The child phase type setting is the same as the parent phase. */ - $parentAttribute = ''; - if($parentID) - { - $parentStage = $this->getByID($parentID); - $parentAttribute = $parentStage->attribute; - $parentACL = $parentStage->acl; - } - - /* Remove empty items and get same items in array. */ - $names = array_filter($names); - $sameNames = array_diff_assoc($names, array_unique($names)); - - /* Check weather need to set code and compute same code. */ - $setCode = isset($this->config->setCode) && $this->config->setCode == 1; - $sameCodes = $setCode ? $this->checkCodeUnique($code, isset($planIDList) ? $planIDList : '') : false; - - /* Prepare the plans user inputted. Process the plan which names not empty only. */ - $project = $this->loadModel('project')->getByID($projectID); - $setPercent = isset($this->config->setPercent) && $this->config->setPercent == 1; - $plans = array(); - foreach($names as $key => $name) - { - if(empty($name)) continue; - - $plan = new stdclass(); - $plan->id = isset($planIDList[$key]) ? (int)$planIDList[$key] : ''; - $plan->type = empty($type[$key]) ? 'stage' : $type[$key]; - $plan->project = $projectID; - $plan->parent = $parentID ? $parentID : $projectID; - $plan->name = $names[$key]; - $plan->attribute = (empty($parentID) or $parentAttribute == 'mix') ? $attributes[$key] : $parentAttribute; - $plan->milestone = !empty($milestone[$key]) ? 1 : 0; - $plan->output = empty($output[$key]) ? '' : implode(',', $output[$key]); - $plan->acl = empty($parentID) ? $acl[$key] : $parentACL; - $plan->PM = empty($projectManager[$key]) ? '' : $projectManager[$key]; - $plan->desc = empty($desc[$key]) ? '' : $desc[$key]; - $plan->hasProduct = $project->hasProduct; - if($setCode) $plan->code = empty($code[$key]) ? '' : $code[$key]; - if($setPercent) $plan->percent = empty($percent[$key]) ? 0 : $percent[$key]; - - $plan->begin = empty($begin[$key]) ? null : $begin[$key]; - $plan->end = empty($end[$key]) ? null : $end[$key]; - $plan->realBegan = empty($realBegan[$key]) ? null : $realBegan[$key]; - $plan->realEnd = empty($realEnd[$key]) ? null : $realEnd[$key]; - - $plans[] = $plan; - } - - /* Set dao error and return false if the programplan has no name. */ - if(empty($plans)) - { - dao::$errors['message'][] = sprintf($this->lang->error->notempty, $this->lang->programplan->name); - return false; - } - - /* Check every plan is valid. */ - $totalPercent = 0; - $milestone = 0; - foreach($plans as $index => $plan) - { - /* Check duplicated names to avoid to save same names. */ - if(!empty($sameNames) and in_array($plan->name, $sameNames)) dao::$errors[$index]['name'] = empty($type) ? $this->lang->programplan->error->sameName : str_replace($this->lang->execution->stage, '', $this->lang->programplan->error->sameName); - if($setCode and $sameCodes !== true and !empty($sameCodes) and in_array($plan->code, $sameCodes)) dao::$errors[$index]['code'] = sprintf($this->lang->error->repeat, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code, $plan->code); - - if($setPercent and $plan->percent and !preg_match("/^[0-9]+(.[0-9]{1,3})?$/", $plan->percent)) - { - dao::$errors[$index]['percent'] = $this->lang->programplan->error->percentNumber; - } - if(helper::isZeroDate($plan->begin)) - { - dao::$errors[$index]['begin'] = $this->lang->programplan->emptyBegin; - } - if(!validater::checkDate($plan->begin) and empty(dao::$errors[$index]['begin'])) - { - dao::$errors[$index]['begin'] = $this->lang->programplan->checkBegin; - } - if(helper::isZeroDate($plan->end)) - { - dao::$errors[$index]['end'] = $this->lang->programplan->emptyEnd; - } - if(!validater::checkDate($plan->end) and empty(dao::$errors[$index]['end'])) - { - dao::$errors[$index]['end'] = $this->lang->programplan->checkEnd; - } - if(!helper::isZeroDate($plan->end) and $plan->end < $plan->begin and empty(dao::$errors[$index]['begin'])) - { - dao::$errors[$index]['end'] = $this->lang->programplan->error->planFinishSmall; - } - if(isset($parentStage) and $plan->begin < $parentStage->begin) - { - dao::$errors[$index]['begin'] = sprintf($this->lang->programplan->error->letterParent, $parentStage->begin); - } - if(isset($parentStage) and $plan->end > $parentStage->end) - { - dao::$errors[$index]['end'] = sprintf($this->lang->programplan->error->greaterParent, $parentStage->end); - } - if($plan->begin < $project->begin and empty(dao::$errors[$index]['begin'])) - { - dao::$errors[$index]['begin'] = sprintf($this->lang->programplan->errorBegin, $project->begin); - } - if(!helper::isZeroDate($plan->end) and $plan->end > $project->end and empty(dao::$errors[$index]['end'])) - { - dao::$errors[$index]['end'] = sprintf($this->lang->programplan->errorEnd, $project->end); - } - - if(helper::isZeroDate($plan->begin)) $plan->begin = ''; - if(helper::isZeroDate($plan->end)) $plan->end = ''; - if($setCode and empty($plan->code)) - { - dao::$errors[$index]['code'] = sprintf($this->lang->error->notempty, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code); - } - foreach(explode(',', $this->config->programplan->create->requiredFields) as $field) - { - $field = trim($field); - if($field and empty($plan->$field)) - { - dao::$errors[$index][$field] = sprintf($this->lang->error->notempty, $this->lang->programplan->$field); - } - } - - if($setPercent) - { - $plan->percent = (float)$plan->percent; - $totalPercent += $plan->percent; - } - - if($plan->milestone) $milestone = 1; - } - - if($setPercent and $totalPercent > 100) dao::$errors['percent'] = $this->lang->programplan->error->percentOver; + if(empty($plans)) dao::$errors['message'][] = sprintf($this->lang->error->notempty, $this->lang->programplan->name); + if(!$this->isCreateTask($parentID)) dao::$errors['message'][] = $this->lang->programplan->error->createdTask; if(dao::isError()) return false; $this->loadModel('action'); - $this->loadModel('user'); $this->loadModel('execution'); - $this->app->loadLang('doc'); - $account = $this->app->user->account; - $now = helper::now(); - - /* Compute the new orders for programplan. if orders is not set then begin with the order in zt_execution table. */ - $orders = $this->programplanTao->computeOrders($orders, $plans); + $project = $this->fetchById($projectID, 'project'); /* Get linked product by projectID. */ - $linkProducts = array(); - $linkBranches = array(); - $productList = $this->loadModel('product')->getProducts($projectID); - if($project->stageBy) - { - $linkProducts = array(0 => $productID); - !empty($productList) && $linkBranches = array(0 => $productList[$productID]->branches); - } - else - { - $linkProducts = array_keys($productList); - foreach($linkProducts as $index => $productID) - { - !empty($productList) && $linkBranches[$index] = $productList[$productID]->branches; - } - } - $this->post->set('products', $linkProducts); - $this->post->set('branch', $linkBranches); + $linkProducts = $this->programplanTao->getLinkProductsForCreate($projectID, $productID); /* Set each plans. */ + $updateUserViewIdList = array(); + $milestone = 0; foreach($plans as $plan) { - /* Set planDuration and realDuration. */ - if(in_array($this->config->edition, array('max', 'ipd'))) - { - $plan->planDuration = $this->getDuration($plan->begin, $plan->end); - $plan->realDuration = $this->getDuration($plan->realBegan, $plan->realEnd); - } - - $plan->order = (int)current($orders); - $plan->days = helper::diffDate($plan->end, $plan->begin) + 1; - if($plan->id) { $stageID = $plan->id; - unset($plan->id, $plan->type); + unset($plan->id, $plan->type, $plan->order); $oldStage = $this->getByID($stageID); $planChanged = ($oldStage->name != $plan->name || $oldStage->milestone != $plan->milestone || $oldStage->begin != $plan->begin || $oldStage->end != $plan->end); - - unset($plan->order); if($planChanged) $plan->version = $oldStage->version + 1; - $this->dao->update(TABLE_PROJECT)->data($plan) - ->autoCheck() - ->batchCheck($this->config->programplan->edit->requiredFields, 'notempty') - ->checkIF(!empty($plan->percent) and $setPercent, 'percent', 'float') - ->where('id')->eq($stageID) - ->exec(); - - /* Add PM to stage teams and project teams. */ - if(!empty($plan->PM)) - { - $team = $this->user->getTeamMemberPairs($stageID, 'execution'); - if(isset($team[$plan->PM])) continue; - - $roles = $this->user->getUserRoles($plan->PM); - $member = new stdclass(); - $member->root = $stageID; - $member->account = $plan->PM; - $member->role = zget($roles, $plan->PM, ''); - $member->join = $now; - $member->type = 'execution'; - $member->days = $plan->days; - $member->hours = $this->config->execution->defaultWorkhours; - $this->dao->insert(TABLE_TEAM)->data($member)->exec(); - $this->execution->addProjectMembers($plan->project, array($plan->PM => $member)); - } - - if($plan->acl != 'open') $this->user->updateUserView(array($stageID), 'sprint'); /* Record version change information. */ + $this->dao->update(TABLE_PROJECT)->data($plan)->where('id')->eq($stageID)->exec(); if($planChanged) $this->programplanTao->insertProjectSpec($stageID, $plan); - $changes = common::createChanges($oldStage, $plan); - $actionID = $this->action->create('execution', $stageID, 'edited'); - $this->action->logHistory($actionID, $changes); + /* Add PM to stage teams and project teams. */ + if(!empty($plan->PM)) $this->execution->addExecutionMembers($stageID, array($plan->PM)); + if($plan->acl != 'open') $updateUserViewIdList[] = $stageID; + + $changes = common::createChanges($oldStage, $plan); + if($changes) + { + $actionID = $this->action->create('execution', $stageID, 'edited'); + $this->action->logHistory($actionID, $changes); + } } else { - unset($plan->id); - $plan->status = 'wait'; - $plan->stageBy = $project->stageBy; - $plan->version = 1; - $plan->parentVersion = $plan->parent == 0 ? 0 : $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('version'); - $plan->team = substr($plan->name,0, 30); - $plan->openedBy = $account; - $plan->openedDate = $now; - $plan->openedVersion = $this->config->version; - if(!isset($plan->acl)) $plan->acl = $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('acl'); - $this->dao->insert(TABLE_PROJECT)->data($plan) - ->autoCheck() - ->batchCheck($this->config->programplan->create->requiredFields, 'notempty') - ->checkIF(!empty($data->percent) and $setPercent, 'percent', 'float') - ->exec(); + $stageID = $this->programplanTao->insertStage($plan, $project, $productID, $parentID); + if(dao::isError()) return false; - if(!dao::isError()) - { - $stageID = $this->dao->lastInsertID(); - if($stageID) $stageID = (int)$stageID; - if(!$stageID) dao::$errors['name'] = $this->lang->fail; + $extra = ''; + if($project->hasProduct and !empty($linkProducts['products'])) $extra = implode(',', $linkProducts['products']); + $this->action->create('execution', $stageID, 'opened', '', $extra); - /* Ipd project create default review points. */ - if($project->model == 'ipd' && $this->config->edition == 'ipd' && !$parentID) $this->loadModel('review')->createDefaultPoint($projectID, $productID, $data->attribute); - - if($plan->type == 'kanban') - { - $execution = $this->execution->getByID($stageID); - $this->loadModel('kanban')->createRDKanban($execution); - } - - if($plan->acl != 'open') $this->user->updateUserView(array($stageID), 'sprint'); - - /* Create doc lib. */ - $lib = new stdclass(); - $lib->project = $projectID; - $lib->execution = $stageID; - $lib->name = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->doclib->main['execution']); - $lib->type = 'execution'; - $lib->main = '1'; - $lib->acl = 'default'; - $lib->addedBy = $this->app->user->account; - $lib->addedDate = helper::now(); - $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec(); - - /* Add creators and PM to stage teams and project teams. */ - $teamMembers = array(); - $members = array($this->app->user->account, $plan->PM); - $roles = $this->user->getUserRoles(array_values($members)); - $team = $this->user->getTeamMemberPairs($stageID, 'execution'); - foreach($members as $teamMember) - { - if(empty($teamMember) or isset($team[$teamMember]) or isset($teamMembers[$teamMember])) continue; - - $member = new stdclass(); - $member->root = $stageID; - $member->account = $teamMember; - $member->role = zget($roles, $teamMember, ''); - $member->join = $now; - $member->type = 'execution'; - $member->days = $plan->days; - $member->hours = $this->config->execution->defaultWorkhours; - $this->dao->insert(TABLE_TEAM)->data($member)->exec(); - $teamMembers[$teamMember] = $member; - } - $this->execution->addProjectMembers($plan->project, $teamMembers); - - $this->setTreePath($stageID); - if($plan->acl != 'open') $this->user->updateUserView(array($stageID), 'sprint'); - - /* Record version change information. */ - $this->programplanTao->insertProjectSpec($stageID, $plan); - - if($project->hasProduct and !empty($linkProducts)) - { - $this->action->create('execution', $stageID, 'opened', '', implode(',', $linkProducts)); - } - else - { - $this->action->create('execution', $stageID, 'opened'); - } - $this->computeProgress($stageID, 'create'); - } + $this->execution->updateProducts($stageID, $linkProducts); + if($plan->acl != 'open') $updateUserViewIdList[] = $stageID; } - $this->execution->updateProducts($stageID, $_POST); - - /* If child plans has milestone, update parent plan set milestone eq 0 . */ - if($parentID and $milestone) $this->dao->update(TABLE_PROJECT)->set('milestone')->eq(0)->where('id')->eq($parentID)->exec(); - - if(dao::isError()) return (bool)print(js::error(dao::getError())); - - next($orders); + if($plan->milestone) $milestone = 1; } + /* If child plans has milestone, update parent plan set milestone eq 0 . */ + if($parentID and $milestone) $this->dao->update(TABLE_PROJECT)->set('milestone')->eq(0)->where('id')->eq($parentID)->exec(); + if($updateUserViewIdList) $this->loadModel('user')->updateUserView($updateUserViewIdList, 'sprint'); + return true; } diff --git a/module/programplan/tao.php b/module/programplan/tao.php index 7739e8401e..9533b2c0f5 100644 --- a/module/programplan/tao.php +++ b/module/programplan/tao.php @@ -10,7 +10,6 @@ declare(strict_types=1); */ class programplanTao extends programplanModel { - /** * 更新项目阶段。 * update program plan. @@ -551,13 +550,15 @@ class programplanTao extends programplanModel */ protected function computeOrders(array $orders, array $plans):array { - if(!isset($orders)) $orders = array(); + if(empty($orders)) $orders = array(); asort($orders); - if(count($orders) < count($plans)) + + $planCount = count($plans); + if(count($orders) < $planCount) { $orderIndex = empty($orders) ? 0 : count($orders); - $lastID = $this->dao->select('id')->from(TABLE_EXECUTION)->orderBy('id_desc')->fetch('id'); - for($i = $orderIndex; $i < count($plans); $i ++) + $lastID = $this->dao->select('id')->from(TABLE_EXECUTION)->orderBy('id_desc')->limit(1)->fetch('id'); + for($i = $orderIndex; $i < $planCount; $i ++) { $lastID ++; $orders[$i] = $lastID * 5; @@ -705,6 +706,86 @@ class programplanTao extends programplanModel } } } + } + /** + * Get linkProducts for create. + * + * @param int $projectID + * @param int $productID + * @access protected + * @return array + */ + protected function getLinkProductsForCreate(int $projectID, int $productID): array + { + $project = $this->fetchByID($projectID, 'project'); + + $linkProducts = array(); + $linkBranches = array(); + $productList = $this->loadModel('product')->getProducts($projectID); + if($project->stageBy) + { + $linkProducts = array(0 => $productID); + if(!empty($productList)) $linkBranches = array(0 => $productList[$productID]->branches); + } + else + { + $linkProducts = array_keys($productList); + foreach($linkProducts as $index => $productID) + { + if(!empty($productList)) $linkBranches[$index] = $productList[$productID]->branches; + } + } + + return array('products' => $linkProducts, 'branch' => $linkBranches); + } + + /** + * 插入阶段数据。 + * Insert stage. + * @param object $plan + * @param object $project + * @param int $productID + * @param int $parentID + * @access protected + * @return int|false + */ + protected function insertStage(object $plan, object $project, int $productID, int $parentID): int|false + { + $account = $this->app->user->account; + + unset($plan->id); + $plan->status = 'wait'; + $plan->stageBy = $project->stageBy; + $plan->version = 1; + $plan->parentVersion = $plan->parent == 0 ? 0 : $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('version'); + $plan->team = substr($plan->name,0, 30); + $plan->openedBy = $account; + $plan->openedDate = helper::now(); + $plan->openedVersion = $this->config->version; + if(!isset($plan->acl)) $plan->acl = $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('acl'); + $this->dao->insert(TABLE_PROJECT)->data($plan)->exec(); + + if(dao::isError()) return false; + + $stageID = (int)$this->dao->lastInsertID(); + $this->insertProjectSpec($stageID, $plan); + + /* Ipd project create default review points. */ + if($project->model == 'ipd' && $this->config->edition == 'ipd' && !$parentID) $this->loadModel('review')->createDefaultPoint($project->id, $productID, $plan->attribute); + + if($plan->type == 'kanban') + { + $execution = $this->execution->getByID($stageID); + $this->loadModel('kanban')->createRDKanban($execution); + } + + $this->loadModel('execution')->createMainLib($project->id, $stageID); + $this->execution->addExecutionMembers($stageID, array($account, $plan->PM)); + + $this->setTreePath($stageID); + $this->computeProgress($stageID, 'create'); + + return $stageID; } } diff --git a/module/programplan/ui/create.html.php b/module/programplan/ui/create.html.php index 63bda19a0c..1fef0c9061 100644 --- a/module/programplan/ui/create.html.php +++ b/module/programplan/ui/create.html.php @@ -97,13 +97,6 @@ $fnGenerateFields = function() use ($config, $lang, $requiredFields, $showFields $items = array(); $items[] = array('name' => 'id', 'label' => $lang->idAB, 'control' => 'index', 'width' => '32px'); - /* If enabled set stage code, then append 'code' field to the required fields list. */ - if(isset($config->setCode) && $config->setCode == 1) - { - $requiredFields = array_merge($requiredFields, array('code'=> true)); - $fields['code']['required'] = true; - } - $fields['attribute']['required'] = $fields['acl']['required'] = true; $renderFields = implode(',', array_keys($requiredFields)); @@ -114,25 +107,14 @@ $fnGenerateFields = function() use ($config, $lang, $requiredFields, $showFields $field['name'] = $name; /* Convert 'options' to 'items'. */ - if(!empty($field['options'])) - { - $field['items'] = $field['options']; - } + if(!empty($field['options'])) $field['items'] = $field['options']; unset($field['options']); /* Assgn item data to PM field. */ - if($name == 'PM') - { - $field['items'] = $PMUsers; - } + if($name == 'PM') $field['items'] = $PMUsers; - /* Form field name is plural nouns, so remove the suffix 's' */ - $name = trim($name, 's'); /* Set hidden attribute. */ - if(!str_contains($renderFields, ",$name,")) - { - $field['hidden'] = true; - } + if(!str_contains($renderFields, ",$name,")) $field['hidden'] = true; /* Sub-stage. */ if($name == 'attribute' && !$enableOptionalAttr) @@ -172,11 +154,8 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe { $item = new stdClass(); - $item->name = $stage->name; - if(isset($config->setCode) && $config->setCode == 1) - { - $item->code = isset($stage->code) ? $stage->code : ''; - } + $item->name = $stage->name; + $item->code = isset($stage->code) ? $stage->code : ''; $item->percent = $stage->percent; $item->attribute = $stage->type; $item->acl = 'open'; @@ -193,14 +172,11 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe { $item = new stdClass(); - $item->disabled = !isset($plan->setMilestone); - $item->planIDList = $plan->id; - $item->type = $plan->type; - $item->name = $plan->name; - if(isset($config->setCode) && $config->setCode == 1) - { - $item->code = $plan->code; - } + $item->disabled = !isset($plan->setMilestone); + $item->id = $plan->id; + $item->type = $plan->type; + $item->name = $plan->name; + $item->code = $plan->code; $item->PM = $plan->PM; $item->percent = $plan->percent; $item->attribute = $plan->attribute; @@ -212,11 +188,11 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe $item->realEnd = $plan->realEnd; $item->desc = $plan->desc; $item->setMilestone = isset($plan->setMilestone) ? $plan->setMilestone : false; + $item->order = $plan->order; if(in_array($config->edition, array('max', 'ipd')) && $executionType == 'stage') { $item->output = empty($plan->output) ? 0 : explode(',', $plan->output); } - $item->order = $plan->order; $items[] = $item; } diff --git a/module/programplan/zen.php b/module/programplan/zen.php index a69ccbc39b..80a96ec183 100644 --- a/module/programplan/zen.php +++ b/module/programplan/zen.php @@ -10,18 +10,88 @@ declare(strict_types=1); */ class programplanZen extends programplan { + /** + * Check legally date. + * + * @param object $plan + * @param object $project + * @param object|null $parent + * @access private + * @return void + */ + private function checkLegallyDate(object $plan, object $project, object|null $parent): void + { + $beginIsZeroDate = helper::isZeroDate($plan->begin); + $endIsZeroDate = helper::isZeroDate($plan->end); + if($beginIsZeroDate) dao::$errors['begin'] = $this->lang->programplan->emptyBegin; + if($endIsZeroDate) dao::$errors['end'] = $this->lang->programplan->emptyEnd; + if(!$beginIsZeroDate and !$endIsZeroDate and $plan->end < $plan->begin) dao::$errors['end'] = $this->lang->programplan->error->planFinishSmall; + + if(!empty($parent)) + { + if(!$beginIsZeroDate and $plan->begin < $parent->begin) dao::$errors['begin'] = sprintf($this->lang->programplan->error->letterParent, $parent->begin); + if(!$endIsZeroDate and $plan->end > $parent->end) dao::$errors['end'] = sprintf($this->lang->programplan->error->greaterParent, $parent->end); + } + if(!$beginIsZeroDate and $plan->begin < $project->begin) dao::$errors['begin'] = sprintf($this->lang->programplan->errorBegin, $project->begin); + if(!$endIsZeroDate and $plan->end > $project->end) dao::$errors['end'] = sprintf($this->lang->programplan->errorEnd, $project->end); + } + /** * Process formData before use it to create programplan. * - * @param object $formData + * @param int $projectID + * @param int $parentID * @access protected - * @return object + * @return array */ - protected function beforeCreate(object $formData): object + protected function buildPlansForCreate(int $projectID, int $parentID): array { - $formData->setIF(empty($formData->data->code), 'code', array()); - $formData->setIF(empty($formData->data->output), 'output', array()); - return $formData; + $project = $this->loadModel('project')->getByID($projectID); + if($parentID) $parentStage = $this->programplan->getByID($parentID); + + $fields = $this->config->programplan->form->create; + foreach(explode(',', $this->config->programplan->create->requiredFields) as $field) + { + $field = trim($field); + if(isset($fields[$field])) $fields[$field]['required'] = true; + } + + $totalPercent = 0; + $names = $codes = array(); + $plans = form::batchData($fields)->get(); + $orders = $this->programplan->computeOrders(array(), $plans); + foreach($plans as $plan) + { + $plan->days = helper::diffDate($plan->end, $plan->begin) + 1; + $plan->project = $projectID; + $plan->parent = $parentID ? $parentID : $projectID; + $plan->order = (int)array_shift($orders); + $plan->hasProduct = $project->hasProduct; + if(!empty($parentID) and !empty($parentStage) and $parentStage->attribute != 'mix') $plan->attribute = $parentStage->attribute;; + if(!empty($parentID) and !empty($parentStage)) $plan->acl = $parentStage->acl; + + if(in_array($this->config->edition, array('max', 'ipd'))) + { + $plan->planDuration = $this->programplan->getDuration($plan->begin, $plan->end); + $plan->realDuration = $this->programplan->getDuration($plan->realBegan, $plan->realEnd); + } + + /* Check duplicated names to avoid to save same names. */ + if(in_array($plan->name, $names)) dao::$errors['name'] = empty($plan->type) ? $this->lang->programplan->error->sameName : str_replace($this->lang->execution->stage, '', $this->lang->programplan->error->sameName); + if(isset($plan->code)) + { + if(in_array($plan->code, $codes)) dao::$errors['code'] = sprintf($this->lang->error->repeat, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code, $plan->code); + if(empty($plan->code)) dao::$errors['code'] = sprintf($this->lang->error->notempty, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code); + } + + $totalPercent += $plan->percent; + $names[] = $plan->name; + if(!empty($plan->code)) $codes[] = $plan->code; + + $this->checkLegallyDate($plan, $project, !empty($parentStage) ? $parentStage : null); + } + if(!empty($this->config->setPercent) and $totalPercent > 100) dao::$errors['percent'] = $this->lang->programplan->error->percentOver; + return $plans; } /**