diff --git a/module/block/control.php b/module/block/control.php index 1ca79413a4..63208c93b1 100755 --- a/module/block/control.php +++ b/module/block/control.php @@ -1816,7 +1816,7 @@ class block extends control /* load pager. */ $this->app->loadClass('pager', $static = true); $pager = new pager(0, 3, 1); - $this->view->projects = $this->loadModel('project')->getList('all', 'id_desc', 1, $pager); + $this->view->projects = $this->loadModel('project')->getList('all', 'id_desc', true, $pager); } /** diff --git a/module/project/control.php b/module/project/control.php index 2eb965fc9c..754eff9d79 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -73,7 +73,7 @@ class project extends control if(isset($fields['hasProduct'])) $fields['hasProduct'] = $projectLang->type; - $projects = $this->project->getList($status, $orderBy, null); + $projects = $this->project->getList($status, $orderBy); $users = $this->loadModel('user')->getPairs('noletter'); $this->loadModel('product'); @@ -1930,8 +1930,8 @@ class project extends control } /** - * 管理项目下的关联产品 - * Manage products. + * 管理项目的关联产品 + * Manage products under project. * * @param string $projectID * @param string $from project|program|programproject @@ -1941,6 +1941,7 @@ class project extends control */ public function manageProducts(string $projectID, $from = 'project'): mixed { + /* Access the nonProduct project alter tips. */ $projectID = (int)$projectID; $project = $this->project->getById($projectID); if(!$project->hasProduct) return print(js::error($this->lang->project->cannotManageProducts) . js::locate('back')); @@ -1957,6 +1958,7 @@ class project extends control return $this->send(array('result' => 'fail', 'message' => dao::getError())); } + /* Update linked products. */ $this->projectZen->mergeProducts($projectID, $project, $executionIDs, $postData); $locateLink = inLink('manageProducts', "projectID=$projectID"); @@ -1964,9 +1966,10 @@ class project extends control return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink)); } + /* Set menu. */ if($this->app->tab == 'program') { - $this->program->setMenu($project->parent); + $this->loadModel('program')->setMenu($project->parent); } else if($this->app->tab == 'project') { @@ -1974,7 +1977,12 @@ class project extends control } $this->projectZen->dealLinkProduct($projectID, $project); - $this->projectZen->buildMangedProductForm($projectID, $project, $executions); + + $this->view->title = $this->lang->project->manageProducts . $this->lang->colon . $project->name; + $this->view->project = $project; + $this->view->executions = $executions; + $this->view->branches = $this->project->getBranchesByProject($projectID); + $this->display(); } /** diff --git a/module/project/model.php b/module/project/model.php index f241bb17a3..82aec9ecf3 100755 --- a/module/project/model.php +++ b/module/project/model.php @@ -236,12 +236,12 @@ class projectModel extends model * * @param string $status * @param string $orderBy + * @param bool $involved * @param int $pager - * @param int $involved * @access public * @return array */ - public function getList($status = 'undone', $orderBy = 'order_desc', $pager = null, $involved = 0) + public function getList(string $status = 'undone', string $orderBy = 'order_desc', bool $involved = false, object|null $pager = null): array { /* Init vars. */ $projects = $this->projectTao->fetchProjectList($status, $orderBy, $involved, $pager); diff --git a/module/project/tao.php b/module/project/tao.php index 99df24062b..07ee19fb5f 100755 --- a/module/project/tao.php +++ b/module/project/tao.php @@ -517,18 +517,18 @@ class projectTao extends projectModel } /** - * 将旧的产品替换成新的 - * replace oldProduct + * 查找项目执行下关联的产品 + * Get linked products with execution under the project. * * @param array $executionIDs * * @access protected - * @return bool + * @return array */ - protected function replaceOldProduct(array $executionIDs): bool + protected function getExecutionProductGroup(array $executionIDs): array { $oldExecutionProducts = $this->dao->select('project,product')->from(TABLE_PROJECTPRODUCT)->where('project')->in($executionIDs)->fetchGroup('project', 'product'); - return !dao::isError(); + return $oldExecutionProducts; } /** @@ -537,12 +537,12 @@ class projectTao extends projectModel * * @param string $status * @param string $orderBy - * @param int $involved + * @param bool $involved * @param object $pager * @access protected * @return array */ - protected function fetchProjectList(string $status, string $orderBy, int $involved, object|null $pager): array + protected function fetchProjectList(string $status, string $orderBy, bool $involved, object|null $pager): array { return $this->dao->select('DISTINCT t1.*')->from(TABLE_PROJECT)->alias('t1') ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id=t2.root') diff --git a/module/project/test/model/getlist.php b/module/project/test/model/getlist.php index 9460b619c1..ba074c19aa 100755 --- a/module/project/test/model/getlist.php +++ b/module/project/test/model/getlist.php @@ -1,28 +1,59 @@ #!/usr/bin/env php gen(90); + +function initData() +{ + $project = zdTable('project'); + $project->id->range('11-19'); + $project->project->range('11-19'); + $project->name->prefix("项目")->range('11-19'); + $project->code->prefix("project")->range('11-19'); + $project->model->range("scrum"); + $project->auth->range("[]"); + $project->path->range("[]"); + $project->type->range("project"); + $project->grade->range("1"); + $project->days->range("1"); + $project->status->range("wait,doing,suspended,closed"); + $project->desc->range("[]"); + $project->budget->range("100000,200000"); + $project->budgetUnit->range("CNY"); + $project->percent->range("0-0"); + $project->openedDate->range("`2023-05-01 10:00:10`"); + $project->gen(9); + + zdTable('team')->gen(10); + + $stakeholder = zdTable('stakeholder'); + $stakeholder->id->range('1-9'); + $stakeholder->objectID->range('11-19'); + $stakeholder->objectType->range('program,project'); + $stakeholder->user->range("admin"); + $stakeholder->type->range("inside"); + $stakeholder->from->range("[]"); + $stakeholder->createdBy->range("admin"); + $stakeholder->createdDate->range("`2023-05-01 10:00:10`"); + $stakeholder->gen(9); +} /** -title=测试 projectModel::getInfoList; +title=测试 projectModel::fetchProjectList(); timeout=0 cid=1 + */ -global $tester; -$tester->loadModel('project'); +initData(); +$statusList = array('', 'all', 'undone', 'unclosed', 'error'); -$doingProjects = $tester->project->getList('doing'); -$waitProjects = $tester->project->getList('wait'); -$suspendedProjects = $tester->project->getList('suspended'); -$closedProjects = $tester->project->getList('closed'); -$allProjects = $tester->project->getList('all'); - -r(count($doingProjects)) && p() && e('44'); //查询正在进行的项目数量 -r($waitProjects) && p('11:name') && e('项目1'); //查询wait状态的Id为11的项目名称 -r(count($suspendedProjects)) && p() && e('11'); //查询暂停状态的项目数量 -r(count($closedProjects)) && p() && e('11'); //查询关闭状态的项目数量 -r(count($allProjects)) && p() && e('90'); //查询所有状态的项目数量 +$projectTester = new Project(); +r($projectTester->testGetList($statusList[0])) && p() && e('0'); // 查询状态为空的项目 +r(count($projectTester->testGetList($statusList[1]))) && p() && e('9'); // 获取所有项目数量 +r($projectTester->testGetList($statusList[2])) && p('11:code') && e('project11'); // 查询未完成的第一个项目的code +r($projectTester->testGetList($statusList[3], true)) && p('12:name') && e('项目12'); // 获取我参与的一个项目的项目名 +r($projectTester->testGetList($statusList[4])) && p() && e('0'); // 获取错误类型的项目 diff --git a/module/project/test/project.class.php b/module/project/test/project.class.php index 934f541b87..fe65375fb2 100644 --- a/module/project/test/project.class.php +++ b/module/project/test/project.class.php @@ -215,12 +215,25 @@ class Project * Test fetchProjectList function. * * @param int $status - * @param string $orderBy - * @param int $involved + * @param bool $involved * @access public * @return array */ - public function testFetchProjectList($status, $involved = 0) + public function testFetchProjectList($status, $involved = false) + { + $projects = $this->project->fetchProjectList($status, 'id_desc', $involved, null); + return $projects; + } + + /** + * Test getList function. + * + * @param int $status + * @param bool $involved + * @access public + * @return array + */ + public function testGetList($status, $involved = false) { $projects = $this->project->fetchProjectList($status, 'id_desc', $involved, null); return $projects; diff --git a/module/project/test/tao/fetchprojectlist.php b/module/project/test/tao/fetchprojectlist.php index 43ec659913..1c2dc6bed4 100755 --- a/module/project/test/tao/fetchprojectlist.php +++ b/module/project/test/tao/fetchprojectlist.php @@ -55,5 +55,5 @@ $projectTester = new Project(); r($projectTester->testFetchProjectList($statusList[0])) && p() && e('0'); // 查询状态为空的项目 r(count($projectTester->testFetchProjectList($statusList[1]))) && p() && e('9'); // 获取所有项目数量 r($projectTester->testFetchProjectList($statusList[2])) && p('11:code') && e('project11'); // 查询未完成的第一个项目的code -r($projectTester->testFetchProjectList($statusList[3], 1)) && p('12:name') && e('项目12'); // 获取我参与的一个项目的项目名 +r($projectTester->testFetchProjectList($statusList[3], true)) && p('12:name') && e('项目12'); // 获取我参与的一个项目的项目名 r($projectTester->testFetchProjectList($statusList[4])) && p() && e('0'); // 获取错误类型的项目 diff --git a/module/project/zen.php b/module/project/zen.php index 9bb1d02a00..b841f57fe2 100644 --- a/module/project/zen.php +++ b/module/project/zen.php @@ -652,8 +652,8 @@ class projectZen extends project } /** - * 处理并合并项目下的新旧产品 - * mergeProducts + * 关联产品时,合并项目下的新旧产品 + * Merge old and new products under the project When link products. * * @param int $projectID * @param object $project @@ -672,21 +672,20 @@ class projectZen extends project $this->project->updateProducts($projectID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - //判断是否为项目型项目并更新关联产品 - if(empty($project->multiple)) - { - $executionID = $this->execution->getNoMultipleID($projectID); - if($executionID) $this->execution->updateProducts($executionID); - } - - //将项目下关联的新旧产品合并 + /* Merge old and new linked products under the project. */ $newProducts = $this->product->getProducts($projectID); $oldProductIDs = array_keys($oldProducts); $newProductIDs = array_keys($newProducts); $diffProducts = array_merge(array_diff($oldProductIDs, $newProductIDs), array_diff($newProductIDs, $oldProductIDs)); if($diffProducts) $this->loadModel('action')->create('project', $projectID, 'Managed', '', !empty($postData->rawdata->products) ? implode(',', $postData->rawdata->products) : ''); - //判断是否为产品型项目并更新关联产品 + /* Multiple and Division project update linked products. */ + if(empty($project->multiple)) + { + $executionID = $this->execution->getNoMultipleID($projectID); + if($executionID) $this->execution->updateProducts($executionID); + } + if(empty($project->division)) { foreach($executionIDs as $executionID) @@ -696,48 +695,47 @@ class projectZen extends project } } - //处理非瀑布及产品型项目 - $this->dealExecutionProduct($project, $oldProducts, $newProductIDs, $executionIDs); + /* Record multiple and waterfall project unlinked products. */ + if($project->multiple and $project->model != 'waterfall' and $project->model != 'waterfallplus') + { + $this->dealUnlinkedProduct($project, $oldProducts, $newProductIDs, $executionIDs); + } } /** - * 处理项目执行下关联产品 - * deal execution product + * 记录多迭代及瀑布类项目移除的产品 + * Record multiple and waterfall project unlinked products * * @param object $project - * @param object $oldProducts + * @param array $oldProducts * @param array $newProductIDs * @param int|array $executionIDs * * @access protected * @return void */ - protected function dealExecutionProduct(object $project, object $oldProducts, array $newProductIDs, array $executionIDs): void + protected function dealUnlinkedProduct(object $project, array $oldProducts, array $newProductIDs, array $executionIDs): void { - //处理非瀑布及产品型项目 - if($project->multiple and $project->model != 'waterfall' and $project->model != 'waterfallplus') + $oldExecutionProducts = $this->projectTao->getExecutionProductGroup($executionIDs); + $unlinkedProducts = array_diff(array_keys($oldProducts), $newProductIDs); + if(!empty($unlinkedProducts)) { - $oldExecutionProducts = $this->projectTao->replaceOldProduct($executionIDs); - $unlinkedProducts = array_diff(array_keys($oldProducts), $newProductIDs); - if(!empty($unlinkedProducts)) + $unlinkedProductPairs = array(); + foreach($unlinkedProducts as $unlinkedProduct) $unlinkedProductPairs[$unlinkedProduct] = $oldProducts[$unlinkedProduct]->name; + + $unlinkExecutions = array(); + foreach($oldExecutionProducts as $executionID => $executionProducts) { - $unlinkedProductPairs = array(); - foreach($unlinkedProducts as $unlinkedProduct) $unlinkedProductPairs[$unlinkedProduct] = $oldProducts[$unlinkedProduct]->name; - - $unlinkExecutions = array(); - foreach($oldExecutionProducts as $executionID => $executionProducts) - { - $unlinkExecutionProducts = array_intersect_key($unlinkedProductPairs, $executionProducts); - if($unlinkExecutionProducts) $unlinkExecutions[$executionID] = $unlinkExecutionProducts; - } - - foreach($unlinkExecutions as $executionID => $unlinkExecutionProducts) $this->loadModel('action')->create('execution', $executionID, 'unlinkproduct', '', implode(',', $unlinkExecutionProducts)); + $unlinkExecutionProducts = array_intersect_key($unlinkedProductPairs, $executionProducts); + if($unlinkExecutionProducts) $unlinkExecutions[$executionID] = $unlinkExecutionProducts; } + + foreach($unlinkExecutions as $executionID => $unlinkExecutionProducts) $this->loadModel('action')->create('execution', $executionID, 'unlinkproduct', '', implode(',', $unlinkExecutionProducts)); } } /** - * 处理项目关联需求的产品 + * 项目关联用户故事则无法移除产品 * dealLinkProduct * * @param int $projectID @@ -759,7 +757,6 @@ class projectZen extends project $linkedProducts = $this->product->getProducts($projectID, 'all', '', true, $linkedProductIdList); $projectStories = $this->project->getStoriesByProject($projectID); $projectBranches = $this->project->getBranchGroupByProject($projectID, array_keys($linkedProducts)); - $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal|noclosed', $linkedBranchIdList); /* If the story of the product which linked the project,don't allow to remove the product. */ $unmodifiableProducts = array(); @@ -782,86 +779,87 @@ class projectZen extends project } } - $this->dealOtherLinkProduct($project, $branchGroups, $linkedBranches); + /* Initializes the product from other linked products. */ + if($this->config->systemMode == 'ALM') $this->InitOtherLinkProduct($project, $allProducts, $linkedBranchIdList, $linkedBranches, $linkedProducts); - $this->view->allBranches = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal'); - $this->view->allProducts = $allProducts; $this->view->unmodifiableProducts = $unmodifiableProducts; $this->view->unmodifiableBranches = $unmodifiableBranches; $this->view->unmodifiableMainBranches = $unmodifiableMainBranches; - $this->view->linkedBranches = $linkedBranches; - $this->view->branchGroups = $branchGroups; } /** - * dealOtherLinkProduct + * 初始化项目集下其他关联产品中当前产品 + * Initializes the current product under the projectprogram. * * @param object $project - * @param object $branchGroups + * @param array $allProducts + * @param array $linkedBranchIdList * @param array $linkedBranches + * @param array $linkedProducts * * @access protected * @return void */ - protected function dealOtherLinkProduct(object $project, object $branchGroups, array $linkedBranches): void + protected function InitOtherLinkProduct(object $project, array $allProducts, array $linkedBranchIdList, array $linkedBranches, array $linkedProducts): void { - $linkedProducts = $this->product->getProducts($projectID, 'all', '', true, $linkedProductIdList); + $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal|noclosed', $linkedBranchIdList); + $topProgramID = $project->parent ? $this->program->getTopByPath($project->path) : 0; + $productsGroupByProgram = $this->loadModel('product')->getProductsGroupByProgram(); - if($this->config->systemMode == 'ALM') + $currentProducts = array(); + foreach($productsGroupByProgram as $programID => $programProducts) { - $topProgramID = $project->parent ? $this->program->getTopByPath($project->path) : 0; - $productsGroupByProgram = $this->product->getProductsGroupByProgram(); - - $currentProducts = array(); - $otherProducts = array(); - foreach($productsGroupByProgram as $programID => $programProducts) + if($programID != $topProgramID) { - if($programID != $topProgramID) - { - foreach($programProducts as $productID => $productName) - { - if(!empty($branchGroups[$productID])) - { - foreach($branchGroups[$productID] as $branchID => $branchName) - { - if(isset($linkedProducts[$productID]) and isset($linkedBranches[$productID][$branchID])) continue; + $otherProducts = $this->InitBranchProduct($programProducts, $branchGroups, $linkedBranches, $linkedProducts); + } + else + { + $currentProducts += $programProducts; + } + } + $this->view->currentProducts = $currentProducts; + $this->view->otherProducts = $otherProducts; - $otherProducts["{$productID}_{$branchID}"] = $productName . '_' . $branchName; - } - } - else - { - if(isset($linkedProducts[$productID])) continue; - $otherProducts[$productID] = $productName; - } - } - } - else + $this->view->branchGroups = $branchGroups; + $this->view->linkedBranches = $linkedBranches; + $this->view->linkedProducts = $linkedProducts; + $this->view->allProducts = $allProducts; + $this->view->allBranches = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal'); + } + + /** + * 初始化项目集下其他关联产品中多分支产品 + * Initializes the multi-branch product under the projectprogram. + * + * @param object $programProducts + * @param array $branchGroups + * @param array $linkedBranches + * @param array $linkedProducts + * + * @access protected + * @return array + */ + protected function InitBranchProduct(object $programProducts, array $branchGroups, array $linkedBranches, array $linkedProducts): array + { + $otherProducts = array(); + foreach($programProducts as $productID => $productName) + { + if(!empty($branchGroups[$productID])) + { + foreach($branchGroups[$productID] as $branchID => $branchName) { - $currentProducts += $programProducts; + if(isset($linkedProducts[$productID]) and isset($linkedBranches[$productID][$branchID])) continue; + $otherProducts["{$productID}_{$branchID}"] = $productName . '_' . $branchName; } } - $this->view->currentProducts = $currentProducts; - $this->view->otherProducts = $otherProducts; + else + { + if(isset($linkedProducts[$productID])) continue; + $otherProducts[$productID] = $productName; + } } - $this->view->linkedProducts = $linkedProducts; - } - /** - * buildMangedProductForm - * - * @param int $projectID - * @param object $project - * - * @access protected - * @return void - */ - protected function buildMangedProductForm(int $projectID, $project, $executions): void - { - $this->view->title = $this->lang->project->manageProducts . $this->lang->colon . $project->name; - $this->view->project = $project; - $this->view->executions = $executions; - $this->view->branches = $this->project->getBranchesByProject($projectID); - $this->display(); + return $otherProducts; } }