* @package program * @link https://www.zentao.net */ namespace zin; $hasProduct = false; /* Closure creating program buttons. */ $fnGenerateCreateProgramBtns = function() use ($lang, $browseType) { $items = array(); hasPriv('program', 'create') && $items[] = array('text' => $lang->program->create, 'icon' => 'plus', 'url' => createLink('program', 'create')); hasPriv('product', 'create') && $items[] = array('text' => $lang->program->createProduct, 'icon' => 'plus', 'url' => createLink('product', 'create'), 'data-app' => 'product', 'class' => 'product-create-btn'); hasPriv('product', 'manageLine') && $items[] = array('text' => $lang->program->manageLine, 'icon' => 'edit', 'data-url' => createLink('product', 'manageLine', $browseType), 'data-toggle' => 'modal', 'data-id' => 'manageLineModal'); if(empty($items)) return null; return count($items) > 1 ? btnGroup ( btn ( setClass('btn primary'), set(reset($items)) ), dropdown ( btn(setClass('btn primary dropdown-toggle'), setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), set::placement('bottom-end'), set::items($items) ) ) : btn ( setClass('btn primary'), set(reset($items)) ); }; /* Closure for generating program row data. */ $fnGenerateProgramRowData = function($programID, $program) use ($config, $users) { if(!isset($program['programName']) || strpos(',ALM,PLM,', ",{$config->systemMode},") === false) return null; /* ALM mode with more data. */ $totalStories = $program['totalStories']; $pmName = ''; if(!empty($program['programPM'])) { $programPM = $program['programPM']; $userName = zget($users, $programPM); $pmName = $userName; } $item = new stdClass(); $item->type = 'program'; $item->id = 'program-' . $programID; $item->parent = 0; $item->isParent = true; $item->name = $program['programName']; $item->PM = $pmName; $item->createdDate = ''; $item->createdBy = ''; $item->totalUnclosedStories = $totalStories - $program['closedStories']; $item->totalStories = $totalStories; $item->closedStoryRate = empty($item->totalUnclosedStories + $program['finishedStories']) ? 0 : round($program['finishedStories'] / ($item->totalUnclosedStories + $program['finishedStories']), 3) * 100; $item->totalPlans = $program['plans']; $item->totalProjects = 0; $item->totalExecutions = 0; $item->testCaseCoverage = $program['coverage'] ?? 0; $item->totalActivatedBugs = 0; $item->totalBugs = 0; $item->fixedRate = empty($program['unresolvedBugs'] + $program['fixedBugs']) ? 0 : round($program['fixedBugs'] / ($program['unresolvedBugs'] + $program['fixedBugs']), 3) * 100; $item->totalReleases = $program['releases']; $item->latestReleaseDate = ''; $item->latestRelease = ''; return $item; }; /* Closure for generating product line row data. */ $fnGenerateLineRowData = function($programID, $lineID, $line) use ($config, &$linesCount) { if(!isset($line['lineName']) || !isset($line['products']) || !is_array($line['products']) || ($config->systemMode != 'ALM' && $config->systemMode != 'PLM')) return null; /* ALM mode with Product Line. */ $linesCount++; $item = new stdClass(); $item->type = 'productLine'; $item->id = 'productLine-' . $lineID; $item->parent = 'program-' . $programID; $item->isParent = true; $item->name = $line['lineName']; $item->PM = ''; $item->createdDate = ''; $item->createdBy = ''; $item->totalUnclosedStories = zget($line, 'totalStories', 0) - zget($line, 'closedStories', 0); $item->finishClosedStories = zget($line, 'finishClosedStories', 0); $item->totalStories = zget($line, 'totalStories', 0); $item->closedStoryRate = empty($item->totalUnclosedStories + $item->finishClosedStories) ? 0 : round($item->finishClosedStories / ($item->totalUnclosedStories + $item->finishClosedStories), 3) * 100; $item->totalPlans = zget($line, 'plans', 0); $item->totalProjects = 0; $item->totalExecutions = 0; $item->testCaseCoverage = zget($line, 'coverage', 0); $item->totalActivatedBugs = zget($line, 'unresolvedBugs', 0); $item->totalFixedBugs = zget($line, 'fixedBugs', 0); $item->totalBugs = 0; $item->fixedRate = empty($item->totalFixedBugs + $item->totalActivatedBugs) ? 0 : round($item->totalFixedBugs / ($item->totalActivatedBugs + $item->totalFixedBugs), 3) * 100; $item->totalReleases = zget($line, 'releases', 0); $item->latestReleaseDate = ''; $item->latestRelease = ''; return $item; }; /* Closure for generating product row data. */ $fnGenerateProductRowData = function($lineID, $product) use ($users) { $item = new stdClass(); $item->type = 'product'; $item->id = $product->id; $item->parent = $product->line ? "productLine-$lineID" : ($product->program ? "program-$product->program" : 0); $item->name = $product->name; $item->PO = !empty($product->PO) ? zget($users, $product->PO) : ''; $item->createdDate = $product->createdDate; $item->createdBy = $product->createdBy; $item->totalUnclosedStories = $product->totalStories - $product->closedStories; $item->totalStories = $product->totalStories; $item->closedStoryRate = empty($item->totalUnclosedStories + $product->finishedStories) ? 0 : round($product->finishedStories / ($item->totalUnclosedStories + $product->finishedStories), 3) * 100; $item->totalPlans = $product->plans; $item->totalProjects = $product->projects; $item->totalExecutions = $product->executions; $item->testCaseCoverage = $product->coverage; $item->totalActivatedBugs = $product->unresolvedBugs; $item->totalBugs = $product->totalBugs; $item->fixedRate = empty($item->totalActivatedBugs + $product->fixedBugs) ? 0 : round($product->fixedBugs / ($item->totalActivatedBugs + $product->fixedBugs), 3) * 100; $item->totalReleases = $product->releases; $item->latestReleaseDate = $product->latestReleaseDate; $item->latestRelease = $product->latestRelease; return $item; }; $linesCount = 0; $data = array(); foreach($productStructure as $programID => $program) { if(isset($programLines[$programID])) { foreach($programLines[$programID] as $lineID => $lineName) { if(isset($program[$lineID])) continue; $program[$lineID] = array(); $program[$lineID]['products'] = array(); $program[$lineID]['lineName'] = $lineName; } } $totalExecutions = 0; $totalBugs = 0; $totalActiveBugs = 0; $totalProjects = 0; foreach($program as $lineID => $line) { $totalProductExecutions = 0; $totalProductBugs = 0; $totalProductActiveBugs = 0; $totalProductProjects = 0; if(isset($line['products']) && is_array($line['products'])) { /* Generate product row data. */ foreach($line['products'] as $productID => $product) { $productRow = $fnGenerateProductRowData($lineID, $product); $totalProductExecutions += $productRow->totalExecutions; $totalProductBugs += $productRow->totalBugs; $totalProductActiveBugs += $productRow->totalActivatedBugs; $totalProductProjects += $productRow->totalProjects; $data[] = $productRow; /* Set flag variable. */ $hasProduct = true; } } /* Generate product line row data. */ $lineRow = $fnGenerateLineRowData($programID, $lineID, $line); if(!empty($lineRow)) { $lineRow->totalExecutions = $totalProductExecutions; $lineRow->totalBugs = $totalProductBugs; $lineRow->totalActivatedBugs = $totalProductActiveBugs; $lineRow->totalProjects = $totalProductProjects; $data[] = $lineRow; } $totalExecutions += $totalProductExecutions; $totalBugs += $totalProductBugs; $totalActiveBugs += $totalProductActiveBugs; $totalProjects += $totalProductProjects; } /* Generate program row data. */ $programRow = $fnGenerateProgramRowData($programID, $program); if(!empty($programRow)) { $programRow->totalExecutions = $totalExecutions; $programRow->totalBugs = $totalBugs; $programRow->totalActivatedBugs = $totalActiveBugs; $programRow->totalProjects = $totalProjects; $data[] = $programRow; } } $canBatchEdit = (common::hasPriv('product', 'batchEdit') && $hasProduct === true); /* Generate cols for the data table. */ $fnGenerateCols = function() use ($canBatchEdit) { $cols = $this->loadModel('datatable') ->getSetting('program'); foreach($cols as $colName => &$setting) { if($colName == 'name') { $setting['checkbox'] = $canBatchEdit; break; } } return $cols; }; $pageSummary = sprintf($lang->product->lineSummary, $linesCount, count($productStats)); jsVar('checkedSummary', $lang->program->checkedProducts); /* ZIN: layout. */ featureBar ( set::current($browseType), set::linkParams("status={key}&orderBy=$orderBy¶m={$param}"), li(searchToggle(set::open($browseType == 'bySearch'), set::module('program'))) ); toolbar($fnGenerateCreateProgramBtns()); $canSort = common::hasPriv('program', 'updateOrder') && strpos(',program_asc,order_asc,', ",$orderBy,") !== false; dtable ( setID('productviews'), set::cols($fnGenerateCols()), set::data($data), set::userMap($users), set::customCols(true), set::checkable($canBatchEdit), set::nested(true), set::className('shadow rounded'), set::footPager(usePager()), set::canRowCheckable(jsRaw("function(rowID){return this.getRowInfo(rowID).data.type == 'product';}")), set::onRenderCell(jsRaw('window.renderCellProductView')), set::orderBy($orderBy), set::plugins(array('sortable')), set::sortable($canSort), set::onSortEnd($canSort ? jsRaw('window.onSortEnd') : null), set::canSortTo($canSort ? jsRaw('window.canSortTo') : null), set::sortLink(createLink('program', 'productview', "browseType={$browseType}&orderBy={name}_{sortType}¶m={$param}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}")), set::footToolbar(array ( 'type' => 'btn-group', 'items' => array( $canBatchEdit ? array ( 'text' => $lang->edit, 'className' => 'secondary batch-btn', 'data-page' => 'batch', 'data-formaction' => $this->createLink('product', 'batchEdit') ) : null ) )), set::checkInfo(jsRaw("function(checkedIDList){ return window.footerSummary(checkedIDList, '{$pageSummary}');}")), set::emptyTip($lang->program->noProgram), set::createTip($lang->program->create), set::createLink(hasPriv('program', 'create') ? createLink('program', 'create') : null) ); render();