From ead93360dc4162cfbecb065c6acd3cf8943f3483 Mon Sep 17 00:00:00 2001 From: sunguangming Date: Sun, 7 Apr 2024 14:15:40 +0800 Subject: [PATCH] * Finish task #115125. --- db/update20.0.sql | 18 +++++ db/zentao.sql | 14 ++++ module/product/config/dtable.php | 114 +++++++++++++++++++++++++++++-- module/product/control.php | 2 - module/product/css/all.ui.css | 6 ++ module/product/js/all.ui.js | 37 ++++++++++ module/product/model.php | 14 ++-- module/product/tao.php | 51 +++++++++----- module/product/ui/all.html.php | 12 ++++ 9 files changed, 236 insertions(+), 32 deletions(-) diff --git a/db/update20.0.sql b/db/update20.0.sql index fc68751c04..bdd37e15dc 100644 --- a/db/update20.0.sql +++ b/db/update20.0.sql @@ -41,3 +41,21 @@ INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES (' INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'enableER', '0'); UPDATE `zt_config` SET `value` = CONCAT(`value`, ',productER') WHERE `key` = 'closedFeatures' AND module = 'common'; + +ALTER TABLE `zt_product` +ADD `draftRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `reviewer`, +ADD `activeRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `draftRequirements`, +ADD `changingRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `activeRequirements`, +ADD `reviewingRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `changingRequirements`, +ADD `finishedRequirements` mediumint NOT NULL DEFAULT '0' AFTER `reviewingRequirements`, +ADD `closedRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `finishedRequirements`, +ADD `totalRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `closedRequirements`; + +ALTER TABLE `zt_product` +ADD `draftEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `reviewer`, +ADD `activeEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `draftEpics`, +ADD `changingEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `activeEpics`, +ADD `reviewingEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `changingEpics`, +ADD `finishedEpics` mediumint NOT NULL DEFAULT '0' AFTER `reviewingEpics`, +ADD `closedEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `finishedEpics`, +ADD `totalEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `closedEpics`; diff --git a/db/zentao.sql b/db/zentao.sql index dbc07f14dd..d3a31a601c 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -1362,6 +1362,20 @@ CREATE TABLE IF NOT EXISTS `zt_product` ( `whitelist` text NULL, `reviewer` text NULL, `PMT` text NULL, + `draftEpics` mediumint(8) NOT NULL DEFAULT '0', + `activeEpics` mediumint(8) NOT NULL DEFAULT '0', + `changingEpics` mediumint(8) NOT NULL DEFAULT '0', + `reviewingEpics` mediumint(8) NOT NULL DEFAULT '0', + `finishedEpics` mediumint(8) NOT NULL DEFAULT '0', + `closedEpics` mediumint(8) NOT NULL DEFAULT '0', + `totalEpics` mediumint(8) NOT NULL DEFAULT '0', + `draftRequirements` mediumint(8) NOT NULL DEFAULT '0', + `activeRequirements` mediumint(8) NOT NULL DEFAULT '0', + `changingRequirements` mediumint(8) NOT NULL DEFAULT '0', + `reviewingRequirements` mediumint(8) NOT NULL DEFAULT '0', + `finishedRequirements` mediumint(8) NOT NULL DEFAULT '0', + `closedRequirements` mediumint(8) NOT NULL DEFAULT '0', + `totalRequirements` mediumint(8) NOT NULL DEFAULT '0', `draftStories` mediumint(8) NOT NULL DEFAULT '0', `activeStories` mediumint(8) NOT NULL DEFAULT '0', `changingStories` mediumint(8) NOT NULL DEFAULT '0', diff --git a/module/product/config/dtable.php b/module/product/config/dtable.php index ee852dfb5a..034f7489a9 100644 --- a/module/product/config/dtable.php +++ b/module/product/config/dtable.php @@ -195,13 +195,115 @@ $config->product->all->dtable->fieldList['createdDate']['type'] = 'date'; $config->product->all->dtable->fieldList['createdDate']['sortType'] = true; $config->product->all->dtable->fieldList['createdDate']['group'] = '1'; +if($config->enableER) +{ + $config->product->all->dtable->fieldList['draftEpics']['name'] = 'draftEpics'; + $config->product->all->dtable->fieldList['draftEpics']['title'] = $lang->product->draftStory; + $config->product->all->dtable->fieldList['draftEpics']['width'] = 64; + $config->product->all->dtable->fieldList['draftEpics']['type'] = 'number'; + $config->product->all->dtable->fieldList['draftEpics']['show'] = true; + $config->product->all->dtable->fieldList['draftEpics']['sortType'] = true; + $config->product->all->dtable->fieldList['draftEpics']['group'] = 'epicCount'; + + $config->product->all->dtable->fieldList['activeEpics']['name'] = 'activeEpics'; + $config->product->all->dtable->fieldList['activeEpics']['title'] = $lang->product->activeStory; + $config->product->all->dtable->fieldList['activeEpics']['width'] = 64; + $config->product->all->dtable->fieldList['activeEpics']['type'] = 'number'; + $config->product->all->dtable->fieldList['activeEpics']['show'] = true; + $config->product->all->dtable->fieldList['activeEpics']['sortType'] = true; + $config->product->all->dtable->fieldList['activeEpics']['group'] = 'epicCount'; + + $config->product->all->dtable->fieldList['changingEpics']['name'] = 'changingEpics'; + $config->product->all->dtable->fieldList['changingEpics']['title'] = $lang->product->changingStory; + $config->product->all->dtable->fieldList['changingEpics']['width'] = 64; + $config->product->all->dtable->fieldList['changingEpics']['type'] = 'number'; + $config->product->all->dtable->fieldList['changingEpics']['show'] = true; + $config->product->all->dtable->fieldList['changingEpics']['sortType'] = true; + $config->product->all->dtable->fieldList['changingEpics']['group'] = 'epicCount'; + + $config->product->all->dtable->fieldList['reviewingEpics']['name'] = 'reviewingEpics'; + $config->product->all->dtable->fieldList['reviewingEpics']['title'] = $lang->product->reviewingStory; + $config->product->all->dtable->fieldList['reviewingEpics']['width'] = 64; + $config->product->all->dtable->fieldList['reviewingEpics']['type'] = 'number'; + $config->product->all->dtable->fieldList['reviewingEpics']['show'] = true; + $config->product->all->dtable->fieldList['reviewingEpics']['sortType'] = true; + $config->product->all->dtable->fieldList['reviewingEpics']['group'] = 'epicCount'; + + $config->product->all->dtable->fieldList['totalEpics']['name'] = 'totalEpics'; + $config->product->all->dtable->fieldList['totalEpics']['title'] = $lang->product->totalStories; + $config->product->all->dtable->fieldList['totalEpics']['width'] = 92; + $config->product->all->dtable->fieldList['totalEpics']['minWidth'] = 100; + $config->product->all->dtable->fieldList['totalEpics']['type'] = 'number'; + $config->product->all->dtable->fieldList['totalEpics']['sortType'] = true; + $config->product->all->dtable->fieldList['totalEpics']['group'] = 'epicCount'; + + $config->product->all->dtable->fieldList['epicCompleteRate']['name'] = 'epicCompleteRate'; + $config->product->all->dtable->fieldList['epicCompleteRate']['title'] = $lang->product->completeRate; + $config->product->all->dtable->fieldList['epicCompleteRate']['width'] = 64; + $config->product->all->dtable->fieldList['epicCompleteRate']['type'] = 'progress'; + $config->product->all->dtable->fieldList['epicCompleteRate']['show'] = true; + $config->product->all->dtable->fieldList['epicCompleteRate']['sortType'] = false; + $config->product->all->dtable->fieldList['epicCompleteRate']['group'] = 'epicCount'; +} + +if($config->URAndSR) +{ + $config->product->all->dtable->fieldList['draftRequirements']['name'] = 'draftRequirements'; + $config->product->all->dtable->fieldList['draftRequirements']['title'] = $lang->product->draftStory; + $config->product->all->dtable->fieldList['draftRequirements']['width'] = 64; + $config->product->all->dtable->fieldList['draftRequirements']['type'] = 'number'; + $config->product->all->dtable->fieldList['draftRequirements']['show'] = true; + $config->product->all->dtable->fieldList['draftRequirements']['sortType'] = true; + $config->product->all->dtable->fieldList['draftRequirements']['group'] = 'requirementCount'; + + $config->product->all->dtable->fieldList['activeRequirements']['name'] = 'activeRequirements'; + $config->product->all->dtable->fieldList['activeRequirements']['title'] = $lang->product->activeStory; + $config->product->all->dtable->fieldList['activeRequirements']['width'] = 64; + $config->product->all->dtable->fieldList['activeRequirements']['type'] = 'number'; + $config->product->all->dtable->fieldList['activeRequirements']['show'] = true; + $config->product->all->dtable->fieldList['activeRequirements']['sortType'] = true; + $config->product->all->dtable->fieldList['activeRequirements']['group'] = 'requirementCount'; + + $config->product->all->dtable->fieldList['changingRequirements']['name'] = 'changingRequirements'; + $config->product->all->dtable->fieldList['changingRequirements']['title'] = $lang->product->changingStory; + $config->product->all->dtable->fieldList['changingRequirements']['width'] = 64; + $config->product->all->dtable->fieldList['changingRequirements']['type'] = 'number'; + $config->product->all->dtable->fieldList['changingRequirements']['show'] = true; + $config->product->all->dtable->fieldList['changingRequirements']['sortType'] = true; + $config->product->all->dtable->fieldList['changingRequirements']['group'] = 'requirementCount'; + + $config->product->all->dtable->fieldList['reviewingRequirements']['name'] = 'reviewingRequirements'; + $config->product->all->dtable->fieldList['reviewingRequirements']['title'] = $lang->product->reviewingStory; + $config->product->all->dtable->fieldList['reviewingRequirements']['width'] = 64; + $config->product->all->dtable->fieldList['reviewingRequirements']['type'] = 'number'; + $config->product->all->dtable->fieldList['reviewingRequirements']['show'] = true; + $config->product->all->dtable->fieldList['reviewingRequirements']['sortType'] = true; + $config->product->all->dtable->fieldList['reviewingRequirements']['group'] = 'requirementCount'; + + $config->product->all->dtable->fieldList['totalRequirements']['name'] = 'totalRequirements'; + $config->product->all->dtable->fieldList['totalRequirements']['title'] = $lang->product->totalStories; + $config->product->all->dtable->fieldList['totalRequirements']['width'] = 92; + $config->product->all->dtable->fieldList['totalRequirements']['minWidth'] = 100; + $config->product->all->dtable->fieldList['totalRequirements']['type'] = 'number'; + $config->product->all->dtable->fieldList['totalRequirements']['sortType'] = true; + $config->product->all->dtable->fieldList['totalRequirements']['group'] = 'requirementCount'; + + $config->product->all->dtable->fieldList['requirementCompleteRate']['name'] = 'requirementCompleteRate'; + $config->product->all->dtable->fieldList['requirementCompleteRate']['title'] = $lang->product->completeRate; + $config->product->all->dtable->fieldList['requirementCompleteRate']['width'] = 64; + $config->product->all->dtable->fieldList['requirementCompleteRate']['type'] = 'progress'; + $config->product->all->dtable->fieldList['requirementCompleteRate']['show'] = true; + $config->product->all->dtable->fieldList['requirementCompleteRate']['sortType'] = false; + $config->product->all->dtable->fieldList['requirementCompleteRate']['group'] = 'requirementCount'; +} + $config->product->all->dtable->fieldList['draftStories']['name'] = 'draftStories'; $config->product->all->dtable->fieldList['draftStories']['title'] = $lang->product->draftStory; $config->product->all->dtable->fieldList['draftStories']['width'] = 64; $config->product->all->dtable->fieldList['draftStories']['type'] = 'number'; $config->product->all->dtable->fieldList['draftStories']['show'] = true; $config->product->all->dtable->fieldList['draftStories']['sortType'] = true; -$config->product->all->dtable->fieldList['draftStories']['group'] = 'g4'; +$config->product->all->dtable->fieldList['draftStories']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['activeStories']['name'] = 'activeStories'; $config->product->all->dtable->fieldList['activeStories']['title'] = $lang->product->activeStory; @@ -209,7 +311,7 @@ $config->product->all->dtable->fieldList['activeStories']['width'] = 64; $config->product->all->dtable->fieldList['activeStories']['type'] = 'number'; $config->product->all->dtable->fieldList['activeStories']['show'] = true; $config->product->all->dtable->fieldList['activeStories']['sortType'] = true; -$config->product->all->dtable->fieldList['activeStories']['group'] = 'g4'; +$config->product->all->dtable->fieldList['activeStories']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['changingStories']['name'] = 'changingStories'; $config->product->all->dtable->fieldList['changingStories']['title'] = $lang->product->changingStory; @@ -217,7 +319,7 @@ $config->product->all->dtable->fieldList['changingStories']['width'] = 64; $config->product->all->dtable->fieldList['changingStories']['type'] = 'number'; $config->product->all->dtable->fieldList['changingStories']['show'] = true; $config->product->all->dtable->fieldList['changingStories']['sortType'] = true; -$config->product->all->dtable->fieldList['changingStories']['group'] = 'g4'; +$config->product->all->dtable->fieldList['changingStories']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['reviewingStories']['name'] = 'reviewingStories'; $config->product->all->dtable->fieldList['reviewingStories']['title'] = $lang->product->reviewingStory; @@ -225,7 +327,7 @@ $config->product->all->dtable->fieldList['reviewingStories']['width'] = 64; $config->product->all->dtable->fieldList['reviewingStories']['type'] = 'number'; $config->product->all->dtable->fieldList['reviewingStories']['show'] = true; $config->product->all->dtable->fieldList['reviewingStories']['sortType'] = true; -$config->product->all->dtable->fieldList['reviewingStories']['group'] = 'g4'; +$config->product->all->dtable->fieldList['reviewingStories']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['totalStories']['name'] = 'totalStories'; $config->product->all->dtable->fieldList['totalStories']['title'] = $lang->product->totalStories; @@ -233,7 +335,7 @@ $config->product->all->dtable->fieldList['totalStories']['width'] = 92; $config->product->all->dtable->fieldList['totalStories']['minWidth'] = 100; $config->product->all->dtable->fieldList['totalStories']['type'] = 'number'; $config->product->all->dtable->fieldList['totalStories']['sortType'] = true; -$config->product->all->dtable->fieldList['totalStories']['group'] = 'g4'; +$config->product->all->dtable->fieldList['totalStories']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['storyCompleteRate']['name'] = 'storyCompleteRate'; $config->product->all->dtable->fieldList['storyCompleteRate']['title'] = $lang->product->completeRate; @@ -241,7 +343,7 @@ $config->product->all->dtable->fieldList['storyCompleteRate']['width'] = 64; $config->product->all->dtable->fieldList['storyCompleteRate']['type'] = 'progress'; $config->product->all->dtable->fieldList['storyCompleteRate']['show'] = true; $config->product->all->dtable->fieldList['storyCompleteRate']['sortType'] = false; -$config->product->all->dtable->fieldList['storyCompleteRate']['group'] = 'g4'; +$config->product->all->dtable->fieldList['storyCompleteRate']['group'] = 'storyCount'; $config->product->all->dtable->fieldList['plans']['name'] = 'plans'; $config->product->all->dtable->fieldList['plans']['title'] = $lang->product->plan; diff --git a/module/product/control.php b/module/product/control.php index 20d893f4e8..2788df2080 100755 --- a/module/product/control.php +++ b/module/product/control.php @@ -601,8 +601,6 @@ class product extends control $products = $this->product->getList($programID, $browseType); } - $this->product->refreshStats(); // Refresh stats fields of products. - $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); diff --git a/module/product/css/all.ui.css b/module/product/css/all.ui.css index e69de29bb2..ff636d3ec1 100644 --- a/module/product/css/all.ui.css +++ b/module/product/css/all.ui.css @@ -0,0 +1,6 @@ +[data-col$=Stories] .dtable-cell-content { padding-left: 0; padding-right: 0} +[data-col$=Epics] .dtable-cell-content { padding-left: 0; padding-right: 0} +[data-col$=Requirements] .dtable-cell-content { padding-left: 0; padding-right: 0} +[data-col$=CompleteRate] .dtable-cell-content { padding-left: 0; padding-right: 0} +#products .absolute {margin: 0px} +#products .absolute .my-1 {display:none} diff --git a/module/product/js/all.ui.js b/module/product/js/all.ui.js index 367fb0b8ff..c0b172ddf4 100644 --- a/module/product/js/all.ui.js +++ b/module/product/js/all.ui.js @@ -39,3 +39,40 @@ function getCheckedItems() }); return checkedItems; }; + +window.onRenderHeaderCell = function(result, {col, row}) +{ + const storyGroup = ['epic', 'requirement', 'story']; + for(const storyType of storyGroup) + { + if(storyCountGroup.hasOwnProperty(storyType) && storyCountGroup[storyType].includes(col.name)) + { + const hasLink = typeof result[0] === 'object'; + const field = hasLink ? + ` + ${result[0].props.children[0]} +
+
` : result[0]; + + /* 保持父标题居中。 */ + const index = storyCountGroup[storyType].length % 2 === 1 ? Math.floor(storyCountGroup[storyType].length / 2) : storyCountGroup[storyType].length / 2; + const commonName = col.name === storyCountGroup[storyType][index] ? storyTypeLang[storyType] : ''; + + const headerContent = + `
+
+ ${commonName} +
+
+
+ ${field} +
+
`; + + result[0] = {html: headerContent}; + $.extend(result[0], {className: 'w-full h-full'}); + } + } + + return result; +} diff --git a/module/product/model.php b/module/product/model.php index bfe7f73866..a0bd091f30 100755 --- a/module/product/model.php +++ b/module/product/model.php @@ -1467,12 +1467,14 @@ class productModel extends model */ public function formatDataForList(object $product, array $users): object { - $product->type = 'product'; - $product->productLine = $product->lineName; - $product->PO = !empty($product->PO) ? zget($users, $product->PO) : ''; - $product->testCaseCoverage = $product->coverage; - $product->storyCompleteRate = $product->totalStories == 0 ? 0 : round($product->finishedStories / $product->totalStories, 3) * 100; - $product->bugFixedRate = ($product->unresolvedBugs + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unresolvedBugs + $product->fixedBugs), 3) * 100; + $product->type = 'product'; + $product->productLine = $product->lineName; + $product->PO = !empty($product->PO) ? zget($users, $product->PO) : ''; + $product->testCaseCoverage = $product->coverage; + $product->epicCompleteRate = $product->totalEpics == 0 ? 0 : round($product->finishedEpics / $product->totalEpics, 3) * 100; + $product->requirementCompleteRate = $product->totalRequirements == 0 ? 0 : round($product->finishedRequirements / $product->totalRequirements, 3) * 100; + $product->storyCompleteRate = $product->totalStories == 0 ? 0 : round($product->finishedStories / $product->totalStories, 3) * 100; + $product->bugFixedRate = ($product->unresolvedBugs + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unresolvedBugs + $product->fixedBugs), 3) * 100; return $product; } diff --git a/module/product/tao.php b/module/product/tao.php index 283b84656f..ef302c841d 100644 --- a/module/product/tao.php +++ b/module/product/tao.php @@ -851,13 +851,29 @@ class productTao extends productModel { $product = new stdclass(); - $product->draftStories = isset($productStories[$productID]) ? $productStories[$productID]['draft'] : 0; - $product->activeStories = isset($productStories[$productID]) ? $productStories[$productID]['active'] : 0; - $product->changingStories = isset($productStories[$productID]) ? $productStories[$productID]['changing'] : 0; - $product->reviewingStories = isset($productStories[$productID]) ? $productStories[$productID]['reviewing'] : 0; - $product->closedStories = isset($productStories[$productID]) ? $productStories[$productID]['closed'] : 0; - $product->finishedStories = isset($productStories[$productID]) ? $productStories[$productID]['finished'] : 0; - $product->totalStories = isset($productStories[$productID]) ? $productStories[$productID]['total'] : 0; + $product->draftEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['draft'] : 0; + $product->activeEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['active'] : 0; + $product->changingEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['changing'] : 0; + $product->reviewingEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['reviewing'] : 0; + $product->closedEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['closed'] : 0; + $product->finishedEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['finished'] : 0; + $product->totalEpics = isset($productStories[$productID]['epic']) ? $productStories[$productID]['epic']['total'] : 0; + + $product->draftRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['draft'] : 0; + $product->activeRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['active'] : 0; + $product->changingRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['changing'] : 0; + $product->reviewingRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['reviewing'] : 0; + $product->closedRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['closed'] : 0; + $product->finishedRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['finished'] : 0; + $product->totalRequirements = isset($productStories[$productID]['requirement']) ? $productStories[$productID]['requirement']['total'] : 0; + + $product->draftStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['draft'] : 0; + $product->activeStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['active'] : 0; + $product->changingStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['changing'] : 0; + $product->reviewingStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['reviewing'] : 0; + $product->closedStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['closed'] : 0; + $product->finishedStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['finished'] : 0; + $product->totalStories = isset($productStories[$productID]['story']) ? $productStories[$productID]['story']['total'] : 0; $product->unresolvedBugs = isset($productBugs[$productID]) ? $productBugs[$productID]['unresolved'] : 0; $product->closedBugs = isset($productBugs[$productID]) ? $productBugs[$productID]['closed'] : 0; @@ -883,26 +899,25 @@ class productTao extends productModel */ protected function getStoryStats(array $productIdList): array { - $stories = $this->dao->select('product, status, `closedReason`, count(id) AS c') + $stories = $this->dao->select('product, status, `closedReason`, count(id) AS c, type') ->from(TABLE_STORY) ->where('deleted')->eq(0) ->beginIF(!empty($productIdList))->andWhere('product')->in($productIdList)->fi() - ->andWhere('type')->eq('story') - ->groupBy('product, status, `closedReason`') + ->groupBy('product, status, `closedReason`, type') ->fetchAll(); $productStories = array(); foreach($stories as $story) { - if(!isset($productStories[$story->product])) $productStories[$story->product] = array('draft' => 0, 'active' => 0, 'changing' => 0, 'reviewing' => 0, 'finished' => 0, 'closed' => 0, 'total' => 0); - if($story->status == 'draft') $productStories[$story->product]['draft'] += $story->c; - if($story->status == 'active') $productStories[$story->product]['active'] += $story->c; - if($story->status == 'changing') $productStories[$story->product]['changing'] += $story->c; - if($story->status == 'reviewing') $productStories[$story->product]['reviewing'] += $story->c; - if($story->status == 'closed') $productStories[$story->product]['closed'] += $story->c; - $productStories[$story->product]['total'] += $story->c; + if(!isset($productStories[$story->product][$story->type])) $productStories[$story->product][$story->type] = array('draft' => 0, 'active' => 0, 'changing' => 0, 'reviewing' => 0, 'finished' => 0, 'closed' => 0, 'total' => 0); + if($story->status == 'draft') $productStories[$story->product][$story->type]['draft'] += $story->c; + if($story->status == 'active') $productStories[$story->product][$story->type]['active'] += $story->c; + if($story->status == 'changing') $productStories[$story->product][$story->type]['changing'] += $story->c; + if($story->status == 'reviewing') $productStories[$story->product][$story->type]['reviewing'] += $story->c; + if($story->status == 'closed') $productStories[$story->product][$story->type]['closed'] += $story->c; + $productStories[$story->product][$story->type]['total'] += $story->c; - if($story->status == 'closed' && $story->closedReason == 'done') $productStories[$story->product]['finished'] += $story->c; + if($story->status == 'closed' && $story->closedReason == 'done') $productStories[$story->product][$story->type]['finished'] += $story->c; } return $productStories; diff --git a/module/product/ui/all.html.php b/module/product/ui/all.html.php index 878701a1ba..743eae47fd 100644 --- a/module/product/ui/all.html.php +++ b/module/product/ui/all.html.php @@ -32,6 +32,14 @@ $fnGetTableFieldList = function() use ($config) }; $cols = $fnGetTableFieldList(); +$storyCountGroup = array(); +foreach($cols as $field => $col) +{ + if(!str_contains($col['group'], 'Count')) continue; + $key = str_replace('Count', '', $col['group']); + $storyCountGroup[$key][] = $field; +} + /* Closure function for generating table data. */ $productStats = initTableData($productStats, $cols, $this->product); $fnGenerateTableData = function($productList) use($users) @@ -70,6 +78,8 @@ $fnGenerateProgramMenu = function($programList) use($lang, $programID, $browseTy }; jsVar('langSummary', $lang->product->pageSummary); +jsVar('storyCountGroup', $storyCountGroup); +jsVar('storyTypeLang', array('epic' => $lang->ERCommon, 'story' => $lang->SRCommon, 'requirement' => $lang->URCommon)); /* ====== Define the page structure with zin widgets ====== */ featureBar @@ -128,9 +138,11 @@ dtable set::data($fnGenerateTableData($productStats)), set::userMap($users), set::customCols(true), + set::headerHeight(50), set::checkable($canBatchEdit), set::sortLink(createLink('product', 'all', "browseType={$browseType}&orderBy={name}_{sortType}&recTotal={$recTotal}&recPerPage={$recPerPage}")), set::orderBy($orderBy), + set::onRenderHeaderCell(jsRaw('onRenderHeaderCell')), $canBatchEdit ? set::footToolbar ( item