diff --git a/module/project/config/form.php b/module/project/config/form.php index 22433ff158..c717afed7d 100644 --- a/module/project/config/form.php +++ b/module/project/config/form.php @@ -1,7 +1,22 @@ project->form = new stdclass(); -$config->project->form->start = array(); -$config->project->form->suspend = array(); +$config->project->form->create = array(); +$config->project->form->start = array(); + +$config->project->form->create['parent'] = array('type' => 'int', 'required' => false, 'default' => ''); +$config->project->form->create['name'] = array('type' => 'string', 'required' => true, 'filter' => 'trim'); +$config->project->form->create['code'] = array('type' => 'string', 'required' => true, 'filter' => 'trim'); +$config->project->form->create['multiple'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['hasProduct'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['PM'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['budget'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['budgetUnit'] = array('type' => 'string', 'required' => false, 'default' => 'CNY'); +$config->project->form->create['begin'] = array('type' => 'date', 'required' => true); +$config->project->form->create['end'] = array('type' => 'date', 'required' => true); +$config->project->form->create['desc'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['acl'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->project->form->create['whitelist'] = array('type' => 'array', 'required' => false, 'default' => ''); +$config->project->form->create['auth'] = array('type' => 'array', 'required' => false, 'default' => ''); +$config->project->form->create['model'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->project->form->start['realBegan'] = array('type' => 'date', 'required' => true, 'filter' => 'trim'); -$config->project->form->suspend['realBegan'] = array('type' => 'date', 'required' => true, 'filter' => 'trim'); diff --git a/module/project/control.php b/module/project/control.php index 470ef54cd2..7b73d21a64 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -277,7 +277,7 @@ class project extends control if($objectType == 'program') { - $minChildBegin = $this->dao->select('`begin` as minBegin')->from(TABLE_PROGRAM)->where('id')->ne($objectID)->andWhere('deleted')->eq(0)->andWhere('path')->like("%,{$objectID},%")->orderBy('begin_asc')->fetch('tminBegin'); + $minChildBegin = $this->dao->select('`begin` as minBegin')->from(TABLE_PROGRAM)->where('id')->ne($objectID)->andWhere('deleted')->eq(0)->andWhere('path')->like("%,{$objectID},%")->orderBy('begin_asc')->fetch('minBegin'); $maxChildEnd = $this->dao->select('`end` as maxEnd')->from(TABLE_PROGRAM)->where('id')->ne($objectID)->andWhere('deleted')->eq(0)->andWhere('path')->like("%,{$objectID},%")->andWhere('end')->ne('0000-00-00')->orderBy('end_desc')->fetch('maxEnd'); } } @@ -487,48 +487,25 @@ class project extends control $this->loadModel('execution'); $this->loadModel('product'); + $this->session->set('projectModel', $model); + if($model == 'kanban') unset($this->lang->project->authList['reset']); if($_POST) { - $projectID = $this->project->create(); + $postData = form::data($this->config->project->form->create); + + $project = $this->projectZen->prepareCreateExtras($postData); + + $projectID = $this->project->create($project, $postData); + $projectID = (int)$projectID; + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->loadModel('action')->create('project', $projectID, 'opened'); /* Link the plan stories. */ - if(!empty($_POST['hasProduct']) && !empty($_POST['plans'])) - { - $planIdList = array(); - foreach($_POST['plans'] as $plans) - { - foreach($plans as $planID) - { - $planIdList[$planID] = $planID; - } - } - - $planStoryGroup = $this->loadModel('story')->getStoriesByPlanIdList($planIdList); - foreach($planIdList as $planID) - { - $planStories = $planProducts = array(); - $planStory = isset($planStoryGroup[$planID]) ? $planStoryGroup[$planID] : array(); - if(!empty($planStory)) - { - foreach($planStory as $id => $story) - { - if($story->status == 'draft' or $story->status == 'reviewing') - { - unset($planStory[$id]); - continue; - } - $planProducts[$story->id] = $story->product; - } - $planStories = array_keys($planStory); - $this->execution->linkStory($projectID, $planStories, $planProducts); - } - } - } + if(!empty($_POST['hasProduct']) && !empty($_POST['plans'])) $this->projectZen->linkPlanStories($postData); $message = $this->executeHooks($projectID); if($message) $this->lang->saveSuccess = $message; @@ -551,7 +528,7 @@ class project extends control $parent = isset($_POST['parent']) ? $_POST['parent'] : 0; $systemMode = $this->loadModel('setting')->getItem('owner=system&module=common§ion=global&key=mode'); if(!empty($systemMode) and $systemMode == 'light') $parent = 0; - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'browse', "programID=$parent&browseType=all", '', '', $projectID))); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'browse', "programID=$parent&browseType=all", '', false, $projectID))); } } diff --git a/module/project/lang/de.php b/module/project/lang/de.php index b0c53041bc..0ff816e5d1 100644 --- a/module/project/lang/de.php +++ b/module/project/lang/de.php @@ -64,8 +64,6 @@ $lang->project->daysGreaterProject = "Days cannot be greater than days of {$lan $lang->project->errorHours = 'Hours/Day cannot be greater than『24』'; $lang->project->workdaysExceed = 'No more than『%s』working days'; $lang->project->teamMembersCount = ', there are %s team members.'; -$lang->project->budgetNumber = '『Budget』must be numbers.'; -$lang->project->budgetGe0 = '『Budget』must be greater than or equal to 0.'; $lang->project->allProjects = "All {$lang->projectCommon}s"; $lang->project->ignore = 'Ignore'; $lang->project->disableExecution = "{$lang->projectCommon} of disable {$lang->executionCommon}"; @@ -211,8 +209,6 @@ $lang->project->noProgram = "Independent {$lang->projectCommon}s"; $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761'); -$lang->project->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; -$lang->project->existProductName = "{$lang->productCommon} name already exists."; $lang->project->changeProgram = "%s > Change {$lang->projectCommon}"; $lang->project->changeProgramTip = "Once the program is edited, the {$lang->productCommon} that is linked to this program will be changed. Do you want to edit it?"; $lang->project->linkedProjectsTip = "Linked {$lang->projectCommon}s are as follows"; @@ -242,6 +238,13 @@ $lang->project->tip->actived = 'The project has been activated. Re-activated $lang->project->tip->group = "It's a Kanban project. Editing privilege group is not available."; $lang->project->tip->whitelist = "It's a public project with open permissions. No need to edit whitelists."; +$lang->project->error = new stdclass(); +$lang->project->error->existProductName = "{$lang->productCommon} name already exists."; +$lang->project->error->budgetGe0 = '『Budget』must be greater than or equal to 0.'; +$lang->project->error->budgetNumber = '『Budget』must be numbers.'; +$lang->project->error->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; +$lang->project->error->emptyBranch = 'Branch can not be empty!'; + $lang->project->hundredMillion = 'Hundred Million'; $lang->project->unitList['CNY'] = 'RMB'; @@ -385,7 +388,6 @@ $lang->project->agileplus = 'Agile +'; $lang->project->waterfallplus = 'Waterfall +'; $lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.'; $lang->project->emptyPM = 'No manager'; -$lang->project->emptyBranch = 'Branch can not be empty!'; $lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent."; $lang->project->cannotCancelCat = "It has child {$lang->projectCommon}s, so you cannot unmark the parent."; $lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s"; diff --git a/module/project/lang/en.php b/module/project/lang/en.php index f74b059176..d411564ca9 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -64,8 +64,6 @@ $lang->project->daysGreaterProject = "Days cannot be greater than days of {$lan $lang->project->errorHours = 'Hours/Day cannot be greater than『24』'; $lang->project->workdaysExceed = 'No more than『%s』working days'; $lang->project->teamMembersCount = ', there are %s team members.'; -$lang->project->budgetNumber = '『Budget』must be numbers.'; -$lang->project->budgetGe0 = '『Budget』must be greater than or equal to 0.'; $lang->project->allProjects = "All {$lang->projectCommon}s"; $lang->project->ignore = 'Ignore'; $lang->project->disableExecution = "{$lang->projectCommon} of disable {$lang->executionCommon}"; @@ -213,8 +211,6 @@ $lang->project->noProgram = "Independent {$lang->projectCommon}s"; $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761'); -$lang->project->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; -$lang->project->existProductName = "{$lang->productCommon} name already exists."; $lang->project->changeProgram = "%s > Change {$lang->projectCommon}"; $lang->project->changeProgramTip = "Once the program is edited, the {$lang->productCommon} that is linked to this program will be changed. Do you want to edit it?"; $lang->project->linkedProjectsTip = "Linked {$lang->projectCommon}s are as follows"; @@ -243,6 +239,13 @@ $lang->project->tip->actived = 'The project has been activated. Re-activated $lang->project->tip->group = "It's a Kanban project. Editing privilege group is not available."; $lang->project->tip->whitelist = "It's a public project with open permissions. No need to edit whitelists."; +$lang->project->error = new stdclass(); +$lang->project->error->existProductName = "{$lang->productCommon} name already exists."; +$lang->project->error->budgetGe0 = '『Budget』must be greater than or equal to 0.'; +$lang->project->error->budgetNumber = '『Budget』must be numbers.'; +$lang->project->error->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; +$lang->project->error->emptyBranch = 'Branch can not be empty!'; + $lang->project->tenThousand = 'Ten Thousand'; $lang->project->hundredMillion = 'Hundred Million'; @@ -387,7 +390,6 @@ $lang->project->agileplus = 'Agile +'; $lang->project->waterfallplus = 'Waterfall +'; $lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.'; $lang->project->emptyPM = 'No manager'; -$lang->project->emptyBranch = 'Branch can not be empty!'; $lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent."; $lang->project->cannotCancelCat = "It has child {$lang->projectCommon}s, so you cannot unmark the parent."; $lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s"; diff --git a/module/project/lang/fr.php b/module/project/lang/fr.php index a569842be7..26d905b025 100644 --- a/module/project/lang/fr.php +++ b/module/project/lang/fr.php @@ -64,8 +64,6 @@ $lang->project->daysGreaterProject = "Days cannot be greater than days of {$lan $lang->project->errorHours = 'Hours/Day cannot be greater than『24』'; $lang->project->workdaysExceed = 'No more than『%s』working days'; $lang->project->teamMembersCount = ', there are %s team members.'; -$lang->project->budgetNumber = '『Budget』must be numbers.'; -$lang->project->budgetGe0 = '『Budget』must be greater than or equal to 0.'; $lang->project->allProjects = "All {$lang->projectCommon}s"; $lang->project->ignore = 'Ignore'; $lang->project->disableExecution = "{$lang->projectCommon} of disable {$lang->executionCommon}"; @@ -212,8 +210,6 @@ $lang->project->noProgram = "Independent {$lang->projectCommon}s"; $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761'); -$lang->project->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; -$lang->project->existProductName = "{$lang->productCommon} name already exists."; $lang->project->changeProgram = "%s > Change {$lang->projectCommon}"; $lang->project->changeProgramTip = "Once the program is edited, the {$lang->productCommon} that is linked to this program will be changed. Do you want to edit it?"; $lang->project->linkedProjectsTip = "Linked {$lang->projectCommon}s are as follows"; @@ -243,6 +239,13 @@ $lang->project->tip->actived = 'The project has been activated. Re-activated $lang->project->tip->group = "It's a Kanban project. Editing privilege group is not available."; $lang->project->tip->whitelist = "It's a public project with open permissions. No need to edit whitelists."; +$lang->project->error = new stdclass(); +$lang->project->error->existProductName = "{$lang->productCommon} name already exists."; +$lang->project->error->budgetGe0 = '『Budget』must be greater than or equal to 0.'; +$lang->project->error->budgetNumber = '『Budget』must be numbers.'; +$lang->project->error->productNotEmpty = "Please link {$lang->productCommon}s or create {$lang->productCommon}s."; +$lang->project->error->emptyBranch = 'Branch can not be empty!'; + $lang->project->hundredMillion = 'Hundred Million'; $lang->project->unitList['CNY'] = 'RMB'; @@ -385,7 +388,6 @@ $lang->project->agileplus = 'Agile +'; $lang->project->waterfallplus = 'Waterfall +'; $lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.'; $lang->project->emptyPM = 'No manager'; -$lang->project->emptyBranch = 'Branch can not be empty!'; $lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent."; $lang->project->cannotCancelCat = "It has child {$lang->projectCommon}s, so you cannot unmark the parent."; $lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s"; diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index 55d5612ff4..fcc469dbd2 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -64,8 +64,6 @@ $lang->project->daysGreaterProject = "可用工日不能大于{$lang->projectCo $lang->project->errorHours = '可用工时/天不能大于『24』'; $lang->project->workdaysExceed = '可用工作日不能超过『%s』天'; $lang->project->teamMembersCount = ',团队成员共%s人。'; -$lang->project->budgetNumber = '『预算』金额必须为数字。'; -$lang->project->budgetGe0 = '『预算』金额必须大于等于0。'; $lang->project->allProjects = "所有{$lang->projectCommon}"; $lang->project->ignore = '忽略'; $lang->project->disableExecution = "不启用{$lang->executionCommon}的{$lang->projectCommon}"; @@ -212,8 +210,6 @@ $lang->project->noProgram = "无项目集归属{$lang->projectCommon}"; $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761'); -$lang->project->productNotEmpty = "请关联{$lang->productCommon}或创建{$lang->productCommon}。"; -$lang->project->existProductName = "{$lang->productCommon}名称已存在。"; $lang->project->changeProgram = '%s > 修改项目集'; $lang->project->changeProgramTip = "修改项目集后,该{$lang->projectCommon}关联{$lang->productCommon}的项目集也会被修改,请确认是否修改。"; $lang->project->linkedProjectsTip = "关联的{$lang->projectCommon}如下"; @@ -234,6 +230,13 @@ $lang->project->allSummary = "本页共 %s 个{$lan $lang->project->checkedSummary = "选中 %total% 个{$lang->projectCommon}。"; $lang->project->checkedAllSummary = "选中 %total% 个{$lang->projectCommon},未开始 %wait%,进行中 %doing%,已挂起 %suspended%,已关闭 %closed% 。"; +$lang->project->error = new stdclass(); +$lang->project->error->existProductName = "{$lang->productCommon}名称已存在。"; +$lang->project->error->budgetGe0 = '『预算』金额必须大于等于0。'; +$lang->project->error->budgetNumber = '『预算』金额必须为数字。'; +$lang->project->error->productNotEmpty = "请关联{$lang->productCommon}或创建{$lang->productCommon}。"; +$lang->project->error->emptyBranch = '分支不能为空!'; + $lang->project->tip = new stdclass(); $lang->project->tip->closed = '该项目已是关闭状态,无须关闭。'; $lang->project->tip->notSuspend = '该项目已关闭,不可进行挂起操作。'; @@ -386,7 +389,6 @@ $lang->project->agileplus = '融合敏捷'; $lang->project->waterfallplus = '融合瀑布'; $lang->project->cannotCreateChild = "该{$lang->projectCommon}已经有实际的内容,无法直接添加子{$lang->projectCommon}。您可以为当前{$lang->projectCommon}创建一个父{$lang->projectCommon},然后在新的父{$lang->projectCommon}下面添加子{$lang->projectCommon}。"; $lang->project->emptyPM = '暂无'; -$lang->project->emptyBranch = '分支不能为空!'; $lang->project->cannotChangeToCat = "该{$lang->projectCommon}已经有实际的内容,无法修改为父{$lang->projectCommon}"; $lang->project->cannotCancelCat = "该{$lang->projectCommon}下已经有子{$lang->projectCommon},无法取消父{$lang->projectCommon}标记"; $lang->project->parentBeginEnd = "父{$lang->projectCommon}起止时间:%s ~ %s"; diff --git a/module/project/model.php b/module/project/model.php index 888a1454ef..e2dd67d668 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -1,4 +1,5 @@ callFunc('name', 'trim') - ->setDefault('status', 'wait') - ->setIF($this->post->delta == 999, 'end', LONG_TIME) - ->setIF($this->post->delta == 999, 'days', 0) - ->setIF($this->post->acl == 'open', 'whitelist', '') - ->setIF(!isset($_POST['whitelist']), 'whitelist', '') - ->setIF(!isset($_POST['multiple']), 'multiple', '1') - ->setDefault('openedBy', $this->app->user->account) - ->setDefault('openedDate', helper::now()) - ->setDefault('team', $this->post->name) - ->setDefault('lastEditedBy', $this->app->user->account) - ->setDefault('lastEditedDate', helper::now()) - ->setDefault('days', '0') - ->add('type', 'project') - ->join('whitelist', ',') - ->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags) - ->remove('uid,products,branch,plans,delta,newProduct,productName,future,contactListMenu,teamMembers') - ->get(); - if(!isset($this->config->setCode) or $this->config->setCode == 0) unset($project->code); - - /* Lean mode relation defaultProgram. */ - if($this->config->systemMode == 'light') $project->parent = $this->config->global->defaultProgram; - - $linkedProductsCount = 0; - if($project->hasProduct && isset($_POST['products'])) - { - foreach($_POST['products'] as $product) - { - if(!empty($product)) $linkedProductsCount++; - } - } - - if($_POST['products']) - { - $topProgramID = $this->loadModel('program')->getTopByID($project->parent); - $multipleProducts = $this->loadModel('product')->getMultiBranchPairs($topProgramID); - foreach($_POST['products'] as $index => $productID) - { - if(isset($multipleProducts[$productID]) and empty($_POST['branch'][$index])) - { - dao::$errors[] = $this->lang->project->emptyBranch; - return false; - } - } - } - - $program = new stdClass(); - if($project->parent) - { - $program = $this->dao->select('*')->from(TABLE_PROGRAM)->where('id')->eq($project->parent)->fetch(); - - /* Judge products not empty. */ - if($project->hasProduct && empty($linkedProductsCount) and !isset($_POST['newProduct'])) - { - dao::$errors['products0'] = $this->lang->project->productNotEmpty; - return false; - } - } - - /* Judge workdays is legitimate. */ - $workdays = helper::diffDate($project->end, $project->begin) + 1; - if(isset($project->days) and $project->days > $workdays) - { - dao::$errors['days'] = sprintf($this->lang->project->workdaysExceed, $workdays); - return false; - } - - if(!empty($project->budget)) - { - if(!is_numeric($project->budget)) - { - dao::$errors['budget'] = sprintf($this->lang->project->budgetNumber); - return false; - } - else if(is_numeric($project->budget) and ($project->budget < 0)) - { - dao::$errors['budget'] = sprintf($this->lang->project->budgetGe0); - return false; - } - else - { - $project->budget = round((float)$this->post->budget, 2); - } - } - - /* When select create new product, product name cannot be empty and duplicate. */ - if($project->hasProduct && isset($_POST['newProduct'])) - { - if(empty($_POST['productName'])) - { - $this->app->loadLang('product'); - dao::$errors['productName'] = sprintf($this->lang->error->notempty, $this->lang->product->name); - return false; - } - else - { - $programID = isset($project->parent) ? $project->parent : 0; - $existProductName = $this->dao->select('name')->from(TABLE_PRODUCT)->where('name')->eq($_POST['productName'])->andWhere('program')->eq($programID)->fetch('name'); - if(!empty($existProductName)) - { - dao::$errors['productName'] = $this->lang->project->existProductName; - return false; - } - } - } - $requiredFields = $this->config->project->create->requiredFields; - if($this->post->delta == 999) $requiredFields = trim(str_replace(',end,', ',', ",{$requiredFields},"), ','); + if($postData->rawdata->delta == 999) $requiredFields = trim(str_replace(',end,', ',', ",{$requiredFields},"), ','); $this->lang->error->unique = $this->lang->error->repeat; - $project = $this->loadModel('file')->processImgURL($project, $this->config->project->editor->create['id'], $this->post->uid); + $project = $this->loadModel('file')->processImgURL($project, $this->config->project->editor->create['id'], $postData->rawdata->uid); $this->dao->insert(TABLE_PROJECT)->data($project) ->autoCheck() ->batchcheck($requiredFields, 'notempty') @@ -1460,9 +1354,9 @@ class projectModel extends model { /* If parent not empty, link products or create products. */ $product = new stdclass(); - $product->name = $project->hasProduct && $this->post->productName ? $this->post->productName : $project->name; + $product->name = $project->hasProduct && $postData->rawdata->productName ? $postData->rawdata->productName : $project->name; $product->shadow = zget($project, 'vision', 'rnd') == 'rnd' ? (int)empty($project->hasProduct) : 1; - $product->bind = $this->post->parent ? 0 : 1; + $product->bind = $postData->rawdata->parent ? 0 : 1; $product->program = $project->parent ? current(array_filter(explode(',', $program->path))) : 0; $product->acl = $project->acl == 'open' ? 'open' : 'private'; $product->PO = $project->PM; @@ -1510,7 +1404,7 @@ class projectModel extends model /* Save order. */ $this->dao->update(TABLE_PROJECT)->set('`order`')->eq($projectID * 5)->where('id')->eq($projectID)->exec(); - $this->file->updateObjectID($this->post->uid, $projectID, 'project'); + $this->file->updateObjectID($postData->rawdata->uid, $projectID, 'project'); $this->loadModel('program')->setTreePath($projectID); /* Add project admin. */ diff --git a/module/project/test/model/create.php b/module/project/test/model/create.php index 35e2735dc3..92c2226f82 100755 --- a/module/project/test/model/create.php +++ b/module/project/test/model/create.php @@ -4,55 +4,77 @@ include dirname(__FILE__, 5) . "/test/lib/init.php"; include dirname(__FILE__, 2) . '/project.class.php'; su('admin'); +$program = zdTable('project'); +$program->id->range('1'); +$program->name->range('项目集一'); +$program->model->range('program'); +$program->code->range('项目集代号'); +$program->desc->range('测试项目集'); +$program->gen(1); + /** title=测试 projectModel->create(); +timeout=0 cid=1 -pid=1 -创建新项目 >> 测试新增项目一 -项目名称为空时 >> 『项目名称』不能为空。 -项目的完成时间为空 >> 『计划完成』不能为空。 -项目的计划完成时间大于计划开始时间 >> 『计划完成』应当大于『2022-02-07』。 -项目的开始时间为空 >> 『计划开始』不能为空。 +- 执行projectClass模块的cr方法,参数是$normalProject, $postData,属性name @测试新增项目一 +- 执行projectClass模块的cr方法,参数是$emptyNameProject, $postData,属性name @『项目名称』不能为空。 + +- 执行projectClass模块的cr方法,参数是$emptyEndProject, $postData,属性end @『计划完成』不能为空。 + +- 执行projectClass模块的cr方法,参数是$beginGtEndProject, $postData,属性end @『计划完成』应当大于『2022-02-07』。 + +- 执行projectClass模块的cr方法,参数是$emptyBeginProject, $postData,属性begin @『计划开始』不能为空。 + + */ global $tester; -$tester->app->loadConfig('execution'); -$project = new Project(); +$projectClass = new project(); -$data = array( - 'parent' => 1, - 'name' => '测试新增项目一', - 'budget' => '', - 'budgetUnit' => 'CNY', - 'begin' => '2022-02-07', - 'end' => '2022-03-01', - 'desc' => '测试项目描述', - 'acl' => 'private', - 'whitelist' => '', - 'PM' => '', - 'products' => array(1) -); +$project = new stdclass(); +$project->parent = 0; +$project->name = '测试新增项目一'; +$project->budget = ''; +$project->budgetUnit = 'CNY'; +$project->begin = '2022-02-07'; +$project->end = '2023-01-01'; +$project->desc = '测试项目描述'; +$project->acl = 'private'; +$project->whitelist = 'user1,user2,user3'; +$project->PM = 'admin'; +$project->type = 'project'; +$project->model = 'scrum'; +$project->multiple = 1; +$project->hasProduct = 1; -$normalProject = $data; +$postData = new stdclass(); +$postData->rowdata = clone $project; +$postData->rowdata->uid = '64dda2xc'; +$postData->rowdata->delta = 0; +$postData->rowdata->products = array(1); -$emptyNameProject = $data; -$emptyNameProject['name'] = ''; +$normalProject = clone $project; -$emptyBeginProject = $data; -$emptyBeginProject['begin'] = ''; +$emptyNameProject = clone $project; +$emptyNameProject->name = ''; -$emptyEndProject = $data; -$emptyEndProject['end'] = ''; +$emptyBeginProject = clone $project; +$emptyBeginProject->name = '测试新增项目二'; +$emptyBeginProject->begin = ''; -$beginGtEndProject = $data; -$beginGtEndProject['end'] = '2022-01-10'; +$emptyEndProject = clone $project; +$emptyEndProject->end = ''; +$emptyEndProject->name = '测试新增项目三'; -r($project->create($normalProject)) && p('name') && e('测试新增项目一'); // 创建新项目 -r($project->create($emptyNameProject)) && p('message[name]:0') && e('『项目名称』不能为空。'); // 项目名称为空时 -r($project->create($emptyEndProject)) && p('message[end]:0') && e('『计划完成』不能为空。'); // 项目的完成时间为空 -r($project->create($beginGtEndProject)) && p('message[end]:0') && e('『计划完成』应当大于『2022-02-07』。'); // 项目的计划完成时间大于计划开始时间 -r($project->create($emptyBeginProject)) && p('message[begin]:0') && e('『计划开始』不能为空。'); // 项目的开始时间为空 +$beginGtEndProject = clone $project; +$beginGtEndProject->end = '2021-01-10'; +$beginGtEndProject->name = '测试新增项目四'; +r($projectClass->create($normalProject, $postData)) && p('name') && e('测试新增项目一'); +r($projectClass->create($emptyNameProject, $postData)) && p('message[name]:0') && e('『项目名称』不能为空。'); +r($projectClass->create($emptyEndProject, $postData)) && p('message[end]:0') && e('『计划完成』不能为空。'); +r($projectClass->create($beginGtEndProject, $postData)) && p('message[end]:0') && e('『计划完成』应当大于『2022-02-07』。'); +r($projectClass->create($emptyBeginProject, $postData)) && p('message[begin]:0') && e('『计划开始』不能为空。'); \ No newline at end of file diff --git a/module/project/test/model/start.php b/module/project/test/model/start.php index fe29a3e38f..0acb912f08 100755 --- a/module/project/test/model/start.php +++ b/module/project/test/model/start.php @@ -12,10 +12,10 @@ title=测试 projectModel->start(); timeout=0 cid=1 -- 执行project模块的start方法,参数是11, $data- ,属性0 @status - @status -- 执行project模块的start方法,参数是12, $data- ,属性0 @doing - @doing +- 执行project模块的start方法,参数是11, $data,属性0 @status + +- 执行project模块的start方法,参数是12, $data,属性0 @doing + - 执行project模块的start方法,参数是13, $data - 属性name @0 - 属性status @0 diff --git a/module/project/test/project.class.php b/module/project/test/project.class.php index 8abf7d2773..11910c81d1 100644 --- a/module/project/test/project.class.php +++ b/module/project/test/project.class.php @@ -40,12 +40,9 @@ class Project * @access public * @return void */ - public function create($params) + public function create($project, $postData) { - $_POST = $params; - - $projectID = $this->project->create(); - unset($_POST); + $projectID = $this->project->create($project, $postData); if(dao::isError()) return array('message' => dao::getError()); diff --git a/module/project/zen.php b/module/project/zen.php index b8606ca076..d4fae9afbc 100644 --- a/module/project/zen.php +++ b/module/project/zen.php @@ -10,8 +10,148 @@ declare(strict_types=1); */ class projectZen extends project { + /** + * Append extras data to post data. + * + * @param object $postData + * @access protected + * @return int|object + */ + protected function prepareCreateExtras(object $postData): object + { + $rawdata = $postData->rawdata; + $project = $postData->setDefault('status', 'wait') + ->setIF($rawdata->delta == 999, 'end', LONG_TIME) + ->setIF($rawdata->delta == 999, 'days', 0) + ->setIF($rawdata->acl == 'open', 'whitelist', '') + ->setIF(!isset($rawdata->whitelist), 'whitelist', '') + ->setIF(!isset($rawdata->multiple), 'multiple', '1') + ->setDefault('openedBy', $this->app->user->account) + ->setDefault('openedDate', helper::now()) + ->setDefault('team', $rawdata->name) + ->setDefault('lastEditedBy', $this->app->user->account) + ->setDefault('lastEditedDate', helper::now()) + ->setDefault('days', '0') + ->add('type', 'project') + ->join('whitelist', ',') + ->join('auth', ',') + ->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags) + ->get(); + + if(!isset($this->config->setCode) or $this->config->setCode == 0) unset($project->code); + + /* Lean mode relation defaultProgram. */ + if($this->config->systemMode == 'light') $project->parent = $this->config->global->defaultProgram; + + if(!$this->checkProductAndBranch($rawdata, $project)) return false; + if(!$this->checkDaysAndBudget($rawdata, $project)) return false; + if(!$this->checkProductNameUnqiue($rawdata, $project)) return false; + + return $project; + } + + private function checkProductAndBranch(object $rawdata, object $project): bool + { + $linkedProductsCount = 0; + if($project->hasProduct && isset($rawdata->products)) + { + foreach($rawdata->products as $product) + { + if(!empty($product)) $linkedProductsCount++; + } + } + + if($rawdata->products) + { + $topProgramID = $this->loadModel('program')->getTopByID($project->parent); + $multipleProducts = $this->loadModel('product')->getMultiBranchPairs($topProgramID); + foreach($rawdata->products as $index => $productID) + { + if(isset($multipleProducts[$productID]) and empty($rawdata->branch[$index])) + { + dao::$errors[] = $this->lang->project->error->emptyBranch; + return false; + } + } + } + + $program = new stdClass(); + if($project->parent) + { + $program = $this->dao->select('*')->from(TABLE_PROGRAM)->where('id')->eq($project->parent)->fetch(); + + /* Judge products not empty. */ + if($project->hasProduct && empty($linkedProductsCount) and !isset($rawdata->newProduct)) + { + dao::$errors['products0'] = $this->lang->project->error->productNotEmpty; + return false; + } + } + + return true; + } + + private function checkDaysAndBudget(object $rawdata, object $project): bool + { + /* Judge workdays is legitimate. */ + $workdays = helper::diffDate($project->end, $project->begin) + 1; + if(isset($project->days) and $project->days > $workdays) + { + dao::$errors['days'] = sprintf($this->lang->project->workdaysExceed, $workdays); + return false; + } + + if(!empty($project->budget)) + { + if(!is_numeric($project->budget)) + { + dao::$errors['budget'] = sprintf($this->lang->project->error->budgetNumber); + return false; + } + else if(is_numeric($project->budget) and ($project->budget < 0)) + { + dao::$errors['budget'] = sprintf($this->lang->project->error->budgetGe0); + return false; + } + else + { + $project->budget = round((float)$rawdata->budget, 2); + } + } + + return true; + } + + private function checkProductNameUnqiue(object $rawdata, object $project): bool + { + /* When select create new product, product name cannot be empty and duplicate. */ + if($project->hasProduct && isset($rawdata->newProduct)) + { + if(empty($rawdata->productName)) + { + $this->app->loadLang('product'); + dao::$errors['productName'] = sprintf($this->lang->error->notempty, $this->lang->product->name); + return false; + } + else + { + $programID = isset($project->parent) ? $project->parent : 0; + $existProductName = $this->dao->select('name')->from(TABLE_PRODUCT)->where('name')->eq($rawdata->productName)->andWhere('program')->eq($programID)->fetch('name'); + if(!empty($existProductName)) + { + dao::$errors['productName'] = $this->lang->project->error->existProductName; + return false; + } + } + } + + return true; + } + + /** * Send variables to create page. + * * @param string $model * @param int $programID * @param int $copyProjectID @@ -19,9 +159,10 @@ class projectZen extends project * @access protected * @return void */ - protected function buildCreateForm(string $model, int $programID, int $copyProjectID, string $extra):void + protected function buildCreateForm(string $model, int $programID, int $copyProjectID, string $extra): void { $this->loadModel('product'); + $this->loadModel('program'); $extra = str_replace(array(',', ' '), array('&', ''), $extra); parse_str($extra, $output); @@ -29,20 +170,9 @@ class projectZen extends project if($this->app->tab == 'program' and $programID) $this->loadModel('program')->setMenu($programID); if($this->app->tab == 'product' and !empty($output['productID'])) $this->loadModel('product')->setMenu($output['productID']); if($this->app->tab == 'doc') unset($this->lang->doc->menu->project['subMenu']); - $this->session->set('projectModel', $model); - if($copyProjectID) - { - $copyProject = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->eq($copyProjectID)->fetch(); - $products = $this->product->getProducts($copyProjectID); - - if(!$copyProject->hasProduct) $shadow = 1; - foreach($products as $product) - { - $branches = implode(',', $product->branches); - $copyProject->productPlans[$product->id] = $this->loadModel('productplan')->getPairs($product->id, $branches, 'noclosed', true); - } - } + if($copyProjectID) $copyProject = $this->copyProject((int)$copyProjectID); + $shadow = empty($copyProject->hasProduct) ? 1 : 0; if($this->view->globalDisableProgram) $programID = $this->config->global->defaultProgram; $topProgramID = $this->program->getTopByID($programID); @@ -53,10 +183,6 @@ class projectZen extends project $this->lang->project->subAclList = $this->lang->project->kanbanSubAclList; } - $sprintConcept = empty($this->config->custom->sprintConcept) ? - $this->config->executionCommonList[$this->app->getClientLang()][0] : - $this->config->executionCommonList[$this->app->getClientLang()][1]; - $withProgram = $this->config->systemMode == 'ALM' ? true : false; $allProducts = array('0' => '') + $this->program->getProductPairs($programID, 'all', 'noclosed', '', $shadow, $withProgram); $parentProgram = $this->loadModel('program')->getByID($programID); @@ -66,13 +192,10 @@ class projectZen extends project $this->view->model = $model; $this->view->pmUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst'); $this->view->users = $this->user->getPairs('noclosed|nodeleted'); - $this->view->products = $products; $this->view->programID = $programID; $this->view->productID = isset($output['productID']) ? $output['productID'] : 0; $this->view->branchID = isset($output['branchID']) ? $output['branchID'] : 0; $this->view->allProducts = $allProducts; - $this->view->productPlans = array('0' => '') + $productPlans; - $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'noclosed'); $this->view->multiBranchProducts = $this->product->getMultiBranchPairs($topProgramID); $this->view->copyProjects = $this->project->getPairsByModel($model); $this->view->copyProjectID = $copyProjectID; @@ -85,6 +208,72 @@ class projectZen extends project $this->display(); } + /** + * Get copy project and send variables to create page. + * + * @param int $copyProjectID + * @access protected + * @return void + */ + private function getCopyProject(int $copyProjectID): object + { + $copyProject = $this->project->getByID($copyProjectID); + $products = $this->product->getProducts($copyProjectID); + + foreach($products as $product) + { + $branches = implode(',', $product->branches); + $copyProject->productPlans[$product->id] = $this->loadModel('productplan')->getPairs($product->id, $branches, 'noclosed', true); + } + + $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'noclosed'); + $this->view->products = $products; + $this->view->copyProject = $copyProject; + + return $copyProject; + } + + /** + * Link plan's stories after create a project. + * + * @param object $postData + * @access protected + * @return void + */ + protected function linkPlanStories(object $postData) + { + $planIdList = array(); + foreach($postData->rawdata->plans as $plans) + { + foreach($plans as $planID) + { + $planIdList[$planID] = $planID; + } + } + + $planStoryGroup = $this->loadModel('story')->getStoriesByPlanIdList($planIdList); + foreach($planIdList as $planID) + { + $planStories = $planProducts = array(); + $planStory = isset($planStoryGroup[$planID]) ? $planStoryGroup[$planID] : array(); + if(!empty($planStory)) + { + foreach($planStory as $id => $story) + { + if($story->status == 'draft' or $story->status == 'reviewing') + { + unset($planStory[$id]); + continue; + } + $planProducts[$story->id] = $story->product; + } + + $planStories = array_keys($planStory); + $this->loadModel('execution')->linkStory($projectID, $planStories, $planProducts); + } + } + } + /** * Append extras data to post data. *