Merge branch '18.8.1' into 18.x
This commit is contained in:
@@ -1 +1,3 @@
|
||||
ALTER TABLE `zt_mr` ADD executionID mediumint(8) unsigned NOT NULL DEFAULT 0 AFTER `jobID`;
|
||||
ALTER TABLE `zt_testtask` ADD COLUMN `members` text NULL;
|
||||
UPDATE `zt_solutions` SET `deleted` = '0' WHERE `deleted` = '';
|
||||
|
||||
@@ -1578,6 +1578,24 @@ class baseSQL
|
||||
*/
|
||||
public $conditionIsTrue = false;
|
||||
|
||||
/**
|
||||
* 条件层级。
|
||||
* The condition level.
|
||||
*
|
||||
* @var bool
|
||||
* @access public;
|
||||
*/
|
||||
public $conditionLevel = 0;
|
||||
|
||||
/**
|
||||
* 条件结果,beginIF 中表达式的结果会存储到这个数组中。
|
||||
* Store the result of the expression.
|
||||
*
|
||||
* @var bool
|
||||
* @access public;
|
||||
*/
|
||||
public $conditionResults = array();
|
||||
|
||||
/**
|
||||
* WHERE条件嵌套小括号标记。
|
||||
* If in mark or not.
|
||||
@@ -1889,7 +1907,9 @@ class baseSQL
|
||||
public function beginIF($condition)
|
||||
{
|
||||
$this->inCondition = true;
|
||||
$this->conditionIsTrue = $condition;
|
||||
$this->conditionLevel += 1;
|
||||
$this->conditionResults[$this->conditionLevel] = $condition;
|
||||
$this->conditionIsTrue = !in_array(false, $this->conditionResults);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -1902,6 +1922,14 @@ class baseSQL
|
||||
*/
|
||||
public function fi()
|
||||
{
|
||||
unset($this->conditionResults[$this->conditionLevel]);
|
||||
$this->conditionLevel -= 1;
|
||||
if($this->conditionLevel > 0)
|
||||
{
|
||||
$this->conditionIsTrue = !in_array(false, $this->conditionResults);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->inCondition = false;
|
||||
$this->conditionIsTrue = false;
|
||||
return $this;
|
||||
|
||||
@@ -980,12 +980,14 @@ class block extends control
|
||||
$this->app->loadLang('story');
|
||||
$this->app->loadLang('bug');
|
||||
|
||||
$this->loadModel('program')->refreshStats();
|
||||
|
||||
/* Set project status and count. */
|
||||
$status = isset($this->params->type) ? $this->params->type : 'all';
|
||||
$count = isset($this->params->count) ? (int)$this->params->count : 15;
|
||||
|
||||
/* Get projects. */
|
||||
$projects = $this->project->getOverviewList('byStatus', $status, 'order_asc', $count, '');
|
||||
$projects = $this->project->getOverviewList('byStatus', $status, 'order_asc', $count, '');
|
||||
if(empty($projects))
|
||||
{
|
||||
$this->view->projects = $projects;
|
||||
@@ -1031,7 +1033,6 @@ class block extends control
|
||||
$project->cv = $this->weekly->getCV($project->ev, $project->ac);
|
||||
|
||||
$left = (float)$this->weekly->getLeft($project->id, $today);
|
||||
$project->progress = ($project->ac + $left) ? floor($project->ac / ($project->ac + $left) * 1000) / 1000 * 100 : 0;
|
||||
$project->progress = $project->progress > 100 ? 100 : $project->progress;
|
||||
$project->current = $current;
|
||||
}
|
||||
@@ -1282,6 +1283,9 @@ class block extends control
|
||||
public function printWaterfallReportBlock()
|
||||
{
|
||||
$this->app->loadLang('programplan');
|
||||
|
||||
$this->loadModel('program')->refreshStats();
|
||||
|
||||
$project = $this->loadModel('project')->getByID($this->session->project);
|
||||
$today = helper::today();
|
||||
$date = date('Ymd', strtotime('this week Monday'));
|
||||
@@ -1298,9 +1302,8 @@ class block extends control
|
||||
$this->view->sv = $this->weekly->getSV($this->view->ev, $this->view->pv);
|
||||
$this->view->cv = $this->weekly->getCV($this->view->ev, $this->view->ac);
|
||||
|
||||
$left = (float)$this->weekly->getLeft($this->session->project, $today);
|
||||
$progress = ($this->view->ac + $left) ? floor($this->view->ac / ($this->view->ac + $left) * 1000) / 1000 * 100 : 0;
|
||||
$this->view->progress = $progress > 100 ? 100 : $progress;
|
||||
$left = (float)$this->weekly->getLeft($this->session->project, $today);
|
||||
$this->view->progress = $project->progress > 100 ? 100 : $project->progress;
|
||||
$this->view->current = $current;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<div class='col-xs-5 execution-name' title="<?php echo $project->name;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID={$project->id}"), $project->name);?></div>
|
||||
<div class='col-xs-7'>
|
||||
<div class="progress progress-text-left">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->hours->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->hours->progress;?>%">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->progress;?>%">
|
||||
<span class="progress-text"><?php echo $project->progress;?>%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -529,16 +529,16 @@ class caselib extends control
|
||||
$columnKey[] = $fields[$title];
|
||||
}
|
||||
|
||||
if(count($columnKey) != count($header) or $this->post->encode != 'utf-8')
|
||||
if(count($columnKey) <= 3 or $this->post->encode != 'utf-8')
|
||||
{
|
||||
$fc = file_get_contents($fileName);
|
||||
$encode = $this->post->encode != "utf-8" ? $this->post->encode : 'gbk';
|
||||
$fc = file_get_contents($fileName);
|
||||
$fc = helper::convertEncoding($fc, $encode, 'utf-8');
|
||||
file_put_contents($fileName, $fc);
|
||||
|
||||
$rows = $this->file->parseCSV($fileName);
|
||||
$columnKey = array();
|
||||
$header = array();
|
||||
$header = array();
|
||||
foreach($rows[0] as $i => $rowValue)
|
||||
{
|
||||
if(empty($rowValue)) break;
|
||||
@@ -550,7 +550,7 @@ class caselib extends control
|
||||
if(!isset($fields[$title])) continue;
|
||||
$columnKey[] = $fields[$title];
|
||||
}
|
||||
if(count($columnKey) != count($header)) return print(js::alert($this->lang->testcase->errorEncode));
|
||||
if(count($columnKey) == 0) return print(js::alert($this->lang->testcase->errorEncode));
|
||||
}
|
||||
|
||||
$this->session->set('fileImport', $fileName);
|
||||
|
||||
@@ -29,13 +29,14 @@ $(function()
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='c-number'><?php echo $lang->testcase->id?></th>
|
||||
<th class='c-title' id='title'> <?php echo $lang->testcase->title?></th>
|
||||
<th class='c-module' id='module'> <?php echo $lang->testcase->module?></th>
|
||||
<th class='c-pri' id='pri'> <?php echo $lang->testcase->pri?></th>
|
||||
<th class='c-type' id='type'> <?php echo $lang->testcase->type?></th>
|
||||
<th class='c-stage' id='stage'> <?php echo $lang->testcase->stage?></th>
|
||||
<th class='c-text' id='keywords'> <?php echo $lang->testcase->keywords?></th>
|
||||
<th class='c-precondition' id='precondition'><?php echo $lang->testcase->precondition?></th>
|
||||
<th class='c-title <?php if(strpos(",$requiredFields,", ',title,') !== false) echo 'required';?>' id='title'> <?php echo $lang->testcase->title?></th>
|
||||
<th class='c-module <?php if(strpos(",$requiredFields,", ',module,') !== false) echo 'required';?>' id='module'> <?php echo $lang->testcase->module?></th>
|
||||
<th class='c-pri <?php if(strpos(",$requiredFields,", ',pri,') !== false) echo 'required';?>' id='pri'> <?php echo $lang->testcase->pri?></th>
|
||||
<th class='c-type <?php if(strpos(",$requiredFields,", ',type,') !== false) echo 'required';?>' id='type'> <?php echo $lang->testcase->type?></th>
|
||||
<th class='c-stage <?php if(strpos(",$requiredFields,", ',stage,') !== false) echo 'required';?>' id='stage'> <?php echo $lang->testcase->stage?></th>
|
||||
<th class='c-text <?php if(strpos(",$requiredFields,", ',text,') !== false) echo 'required';?>' id='keywords'> <?php echo $lang->testcase->keywords?></th>
|
||||
<th class='c-precondition <?php if(strpos(",$requiredFields,", ',precondition,') !== false) echo 'required';?>' id='precondition'><?php echo $lang->testcase->precondition?></th>
|
||||
<th class='c-case-step <?php if(strpos(",$requiredFields,", ',step,') !== false) echo 'required';?> col-content'>
|
||||
<th class='c-case-step col-content'>
|
||||
<table class='w-p100 table-borderless'>
|
||||
<tr>
|
||||
|
||||
@@ -585,7 +585,7 @@ class commonModel extends model
|
||||
echo "<li class='text-gray switchTo'>{$lang->switchTo}</li>";
|
||||
foreach($userVisions as $vision)
|
||||
{
|
||||
echo ($currentVision == $vision ? '<li class="active">' : '<li>') . html::a(helper::createLink('my', 'ajaxSwitchVision', "vision=$vision"), $lang->visionList[$vision], '', "data-type='ajax'") . '</li>';
|
||||
if(isset($lang->visionList[$vision])) echo ($currentVision == $vision ? '<li class="active">' : '<li>') . html::a(helper::createLink('my', 'ajaxSwitchVision', "vision=$vision"), $lang->visionList[$vision], '', "data-type='ajax'") . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ function renderProjectItem(item, $item)
|
||||
{
|
||||
$progress = $('<div class="ring"><span></span></div>').appendTo($item);
|
||||
}
|
||||
var progress = Math.max(0, Math.min(100, Math.round(item.hours && !Array.isArray(item.hours) ? Math.round(item.hours.progress || 0) : 0)));
|
||||
var progress = Math.max(0, Math.min(100, Math.round(item.progress ? Math.round(item.progress || 0) : 0)));
|
||||
$progress.find('span').text(progress);
|
||||
$progress.css('background-position-x', -Math.ceil(progress / 2) * 24);
|
||||
$item.addClass('has-progress');
|
||||
|
||||
@@ -38,5 +38,5 @@ body {overflow: hidden;}
|
||||
#whiteListBox .input-group:last-child {margin-top: 10px;}
|
||||
#mainContent .main-header h2 {width: 90%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
|
||||
.fullscreen-save, .markdown-fullscreen-save {position: fixed; top: 10px; right: 30px; z-index: 999999;}
|
||||
|
||||
.modal-header .fullscreen-save, .markdown-fullscreen-save {top: 5px; right: 60px;}
|
||||
#dataform .table tr:last-child {position: fixed; top: 47px;}
|
||||
|
||||
@@ -591,6 +591,9 @@ class execution extends control
|
||||
$executions = $this->execution->getPairs(0, 'all', 'nocode');
|
||||
$this->execution->setMenu($executionID);
|
||||
|
||||
$execution = $this->execution->getByID($executionID);
|
||||
$project = $this->loadModel('project')->getByID($execution->project);
|
||||
|
||||
/* Load pager. */
|
||||
$this->app->loadClass('pager', $static = true);
|
||||
$pager = new pager($recTotal, $recPerPage, $pageID);
|
||||
@@ -613,13 +616,18 @@ class execution extends control
|
||||
->leftJoin(TABLE_EXECUTION)->alias('t2')
|
||||
->on('t1.project = t2.id')
|
||||
->where('t1.product')->in(array_keys($products))
|
||||
->andWhere('t2.type')->in('sprint,stage,kanban')
|
||||
->fetchPairs('project');
|
||||
|
||||
$projects = $this->loadModel('product')->getProjectPairsByProductIDList(array_keys($products));
|
||||
}
|
||||
else
|
||||
{
|
||||
$executionName = $executions[$executionID];
|
||||
unset($executions);
|
||||
$executions[$executionID] = $executionName;
|
||||
|
||||
$projects[$project->id] = $project->name;
|
||||
}
|
||||
|
||||
/* Get bugs.*/
|
||||
@@ -651,9 +659,6 @@ class execution extends control
|
||||
$bugs = $this->execution->getSearchBugs($products, $executionID, $bugQuery, $pager, 'id_desc');
|
||||
}
|
||||
|
||||
$execution = $this->execution->getByID($executionID);
|
||||
$project = $this->loadModel('project')->getByID($execution->project);
|
||||
|
||||
/* Build the search form. */
|
||||
$this->config->bug->search['actionURL'] = $this->createLink('execution', 'importBug', "executionID=$executionID&browseType=bySearch¶m=myQueryID");
|
||||
$this->config->bug->search['queryID'] = $queryID;
|
||||
@@ -687,6 +692,8 @@ class execution extends control
|
||||
}
|
||||
$this->config->bug->search['params']['module']['values'] = $bugModules;
|
||||
|
||||
$this->config->bug->search['params']['project']['values'] = array('' => '') + $projects;
|
||||
|
||||
unset($this->config->bug->search['fields']['resolvedBy']);
|
||||
unset($this->config->bug->search['fields']['closedBy']);
|
||||
unset($this->config->bug->search['fields']['status']);
|
||||
|
||||
@@ -93,6 +93,8 @@ function setStatistics()
|
||||
|
||||
const task = element.getRowInfo(id).data;
|
||||
|
||||
if(task.isParent == true) return true;
|
||||
|
||||
totalEstimate += Number(task.estimateNum);
|
||||
totalConsumed += Number(task.consumedNum);
|
||||
if(task.statusCode != 'cancel' && task.statusCode != 'closed') totalLeft += Number(task.leftNum);
|
||||
|
||||
@@ -2665,6 +2665,19 @@ class executionModel extends model
|
||||
return $execution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution by build id.
|
||||
*
|
||||
* @param int $buildID
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function getByBuild($buildID)
|
||||
{
|
||||
$build = $this->loadModel('build')->getById($buildID);
|
||||
return $this->getById($build->execution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default managers for a execution from it's related products.
|
||||
*
|
||||
|
||||
@@ -149,7 +149,7 @@ class file extends control
|
||||
|
||||
/* Judge the mode, down or open. */
|
||||
$mode = 'down';
|
||||
$fileTypes = 'txt|jpg|jpeg|gif|png|bmp|xml|html';
|
||||
$fileTypes = 'txt|jpg|jpeg|gif|png|bmp|xml|html|mp4';
|
||||
if(stripos($fileTypes, $file->extension) !== false && $mouse == 'left') $mode = 'open';
|
||||
if($file->extension == 'txt')
|
||||
{
|
||||
@@ -164,11 +164,11 @@ class file extends control
|
||||
/* If the mode is open, locate directly. */
|
||||
if($mode == 'open')
|
||||
{
|
||||
if(stripos('txt|jpg|jpeg|gif|png|bmp', $file->extension) !== false)
|
||||
if(stripos('txt|jpg|jpeg|gif|png|bmp|mp4', $file->extension) !== false)
|
||||
{
|
||||
$this->view->file = $file;
|
||||
$this->view->charset = $this->get->charset ? $this->get->charset : $this->config->charset;
|
||||
$this->view->fileType = ($file->extension == 'txt') ? 'txt' : 'image';
|
||||
$this->view->fileType = $file->extension == 'txt' ? 'txt' : ($file->extension == 'mp4' ? 'video' : 'image');
|
||||
$this->display();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -38,6 +38,7 @@ $lang->file->beginUpload = 'Upload';
|
||||
$lang->file->uploadSuccess = 'Hochgeladen!';
|
||||
$lang->file->batchExport = 'Export in batches';
|
||||
$lang->file->downloadFile = 'Download';
|
||||
$lang->file->playFailed = 'Video preview failed, please contact admin';
|
||||
|
||||
$lang->file->pathname = 'Pfadname';
|
||||
$lang->file->title = 'Titel';
|
||||
|
||||
@@ -38,6 +38,7 @@ $lang->file->beginUpload = 'Click to Upload';
|
||||
$lang->file->uploadSuccess = 'Uploaded!';
|
||||
$lang->file->batchExport = 'Export in batches';
|
||||
$lang->file->downloadFile = 'Download';
|
||||
$lang->file->playFailed = 'Video preview failed, please contact admin';
|
||||
|
||||
$lang->file->pathname = 'Path Name';
|
||||
$lang->file->title = 'Title';
|
||||
|
||||
@@ -38,6 +38,7 @@ $lang->file->beginUpload = 'Clic pour Charger';
|
||||
$lang->file->uploadSuccess = 'Chargé !';
|
||||
$lang->file->batchExport = 'Export in batches';
|
||||
$lang->file->downloadFile = 'Download';
|
||||
$lang->file->playFailed = 'Video preview failed, please contact admin';
|
||||
|
||||
$lang->file->pathname = 'Nom Chemin';
|
||||
$lang->file->title = 'Titre';
|
||||
|
||||
@@ -33,6 +33,7 @@ $lang->file->addFile = 'Thêm';
|
||||
$lang->file->beginUpload = 'Click to Upload';
|
||||
$lang->file->uploadSuccess = 'Uploaded!';
|
||||
$lang->file->batchExport = 'Export in batches';
|
||||
$lang->file->playFailed = 'Video preview failed, please contact admin';
|
||||
|
||||
$lang->file->pathname = 'Path tên';
|
||||
$lang->file->title = 'Tiêu đề';
|
||||
|
||||
@@ -38,6 +38,7 @@ $lang->file->beginUpload = '开始上传';
|
||||
$lang->file->uploadSuccess = '上传成功';
|
||||
$lang->file->batchExport = '分批导出';
|
||||
$lang->file->downloadFile = '下载';
|
||||
$lang->file->playFailed = '视频预览失败,请联系管理员';
|
||||
|
||||
$lang->file->pathname = '路径';
|
||||
$lang->file->title = '标题';
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php include '../../common/view/header.lite.html.php';?>
|
||||
<?php js::set('fileID', $file->id);?>
|
||||
<style>
|
||||
#imageFile {text-align: center; padding: 0; margin: 0 -10px;}
|
||||
#imageFile,#videoFile {text-align: center; padding: 0; margin: 0 -10px;}
|
||||
#imageFile img {max-width: 100%;}
|
||||
#videoFile video {max-width: 100%;}
|
||||
#txtFile {padding: 5px 0; margin: 0 -10px;}
|
||||
#txtFile pre {margin: 0;}
|
||||
#txtFile div {overflow-x: auto;}
|
||||
@@ -20,6 +21,11 @@
|
||||
</div>
|
||||
<?php if($fileType == 'image'):?>
|
||||
<div id='imageFile'><?php echo html::image($this->createLink('file', 'read', "fileID=$file->id"));?></div>
|
||||
<?php elseif($fileType == 'video'):?>
|
||||
<div id='videoFile'>
|
||||
<video src=<?php echo $file->webPath;?> autoplay controls controlsList='nodownload' onerror='showError()'></video>
|
||||
<div class='playfailed hide'><?php echo $lang->file->playFailed;?></div>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<div id='txtFile'>
|
||||
<?php
|
||||
@@ -51,6 +57,11 @@
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
function showError()
|
||||
{
|
||||
$('.playfailed').show();
|
||||
}
|
||||
|
||||
function setCharset(charset)
|
||||
{
|
||||
var link = createLink('file', 'download', 'fileID=' + fileID + '&mouse=left');
|
||||
|
||||
@@ -69,7 +69,7 @@ function deleteFile(fileID, obj)
|
||||
function downloadFile(fileID, extension, imageWidth, fileTitle, type = 'download')
|
||||
{
|
||||
if(!fileID) return;
|
||||
var fileTypes = 'txt,jpg,jpeg,gif,png,bmp';
|
||||
var fileTypes = 'txt,jpg,jpeg,gif,png,bmp,mp4';
|
||||
var windowWidth = $(window).width();
|
||||
var width = (windowWidth > imageWidth) ? ((imageWidth < windowWidth * 0.5) ? windowWidth * 0.5 : imageWidth) : windowWidth;
|
||||
var checkExtension = fileTitle.lastIndexOf('.' + extension) == (fileTitle.length - extension.length - 1);
|
||||
@@ -203,7 +203,7 @@ function setFileName(fileID)
|
||||
}
|
||||
|
||||
/* For the open source version of the file judgment. */
|
||||
if(stripos('txt|jpg|jpeg|gif|png|bmp', $file->extension) !== false and common::hasPriv('file', 'preview'))
|
||||
if(stripos('txt|jpg|jpeg|gif|png|bmp|mp4', $file->extension) !== false and common::hasPriv('file', 'preview'))
|
||||
{
|
||||
echo html::a($previewLink, "<i class='icon icon-eye'></i>", '_blank', "class='fileAction btn btn-link text-primary' title='{$lang->file->preview}' onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth, '$file->title', 'preview')\"");
|
||||
}
|
||||
|
||||
@@ -2974,6 +2974,7 @@ $config->group->package->reviewcl->privs['reviewcl-view'] =
|
||||
$config->group->package->reviewcl->privs['reviewcl-waterfallplusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array());
|
||||
$config->group->package->reviewcl->privs['reviewsetting-version'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array());
|
||||
$config->group->package->reviewcl->privs['reviewsetting-waterfallplusVersion'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array());
|
||||
$config->group->package->reviewcl->privs['reviewcl-setCategory'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array());
|
||||
|
||||
$config->group->package->process = new stdclass();
|
||||
$config->group->package->process->order = 25;
|
||||
|
||||
@@ -219,6 +219,15 @@ class messageModel extends model
|
||||
$toList = $toList . $ccList;
|
||||
}
|
||||
|
||||
if($objectType == 'testtask')
|
||||
{
|
||||
$members = explode(',', $object->members);
|
||||
$toList = explode(',', $toList);
|
||||
$toList = array_merge($toList, $members);
|
||||
$toList = array_filter(array_unique($toList));
|
||||
$toList = implode(',', $toList);
|
||||
}
|
||||
|
||||
return $toList;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,8 +125,6 @@ function refreshPivot(pivotInfo, isOrigin = false)
|
||||
|
||||
$('#pivot-query').addClass('hidden');
|
||||
$('#origin-query').removeClass('hidden');
|
||||
|
||||
$('.query-outside').addClass('visibility-hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+33
-31
@@ -1326,13 +1326,30 @@ class programModel extends model
|
||||
}
|
||||
|
||||
/* 1. Get summary and members of executions to be refreshed. */
|
||||
$summary = $this->dao->select('execution, SUM(t1.estimate) AS totalEstimate, SUM(t1.consumed) AS totalConsumed, SUM(t1.`left`) AS totalLeft')->from(TABLE_TASK)->alias('t1')
|
||||
$tasks = $this->dao->select('t1.id, execution, t1.estimate, t1.consumed, t1.`left`, t1.status')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->le(0) // Ignore child task.
|
||||
->beginIF(!empty($projects))->andWhere('t1.project')->in(array_keys($projects))->fi()
|
||||
->groupBy('execution')
|
||||
->fetchAll();
|
||||
->fetchAll('id');
|
||||
|
||||
$summary = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(empty($task->execution)) continue;
|
||||
if(!isset($summary[$task->execution]))
|
||||
{
|
||||
$summary[$task->execution] = new stdclass();
|
||||
$summary[$task->execution]->totalEstimate = 0;
|
||||
$summary[$task->execution]->totalConsumed = 0;
|
||||
$summary[$task->execution]->totalLeft = 0;
|
||||
}
|
||||
|
||||
$summary[$task->execution]->execution = $task->execution;
|
||||
$summary[$task->execution]->totalEstimate += $task->estimate;
|
||||
$summary[$task->execution]->totalConsumed += $task->consumed;
|
||||
$summary[$task->execution]->totalLeft += ($task->status == 'closed' or $task->status == 'cancel') ? 0 : $task->left;
|
||||
}
|
||||
|
||||
$teamMembers = $this->dao->select('t1.root, COUNT(1) AS members')->from(TABLE_TEAM)->alias('t1')
|
||||
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
|
||||
@@ -1362,6 +1379,8 @@ class programModel extends model
|
||||
$stats[$nodeID]['totalEstimate'] += $execution->totalEstimate;
|
||||
$stats[$nodeID]['totalConsumed'] += $execution->totalConsumed;
|
||||
$stats[$nodeID]['totalLeft'] += $execution->totalLeft;
|
||||
|
||||
// Check $execution->execution and $nodeID(path) is not deleted.
|
||||
if(empty($projectsPairs[$execution->execution]) && empty($projectsPairs[$nodeID]))
|
||||
{
|
||||
$stats[$nodeID]['totalConsumedNotDel'] += $execution->totalConsumed;
|
||||
@@ -1370,22 +1389,6 @@ class programModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadModel('project');
|
||||
foreach($projects as $projectID => $project)
|
||||
{
|
||||
if($project->model != 'waterfall') continue;
|
||||
|
||||
$projectStats = $this->project->getWaterfallPVEVAC($projectID);
|
||||
$stats[$projectID]['totalEstimate'] = $projectStats['PV'];
|
||||
$stats[$projectID]['totalConsumed'] = $projectStats['AC'];
|
||||
$stats[$projectID]['totalLeft'] = $projectStats['left'];
|
||||
if(empty($project->deleted))
|
||||
{
|
||||
$stats[$projectID]['totalConsumedNotDel'] = $projectStats['AC'];
|
||||
$stats[$projectID]['totalLeftNotDel'] = $projectStats['left'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($teamMembers as $projectID => $teamCount)
|
||||
{
|
||||
if(!isset($stats[$projectID])) $stats[$projectID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalConsumedNotDel' => 0, 'totalLeftNotDel' => 0);
|
||||
@@ -1401,14 +1404,14 @@ class programModel extends model
|
||||
->set('progress')->eq($progress)
|
||||
->set('teamCount')->eq($project['teamCount'])
|
||||
->set('estimate')->eq($project['totalEstimate'])
|
||||
->set('consumed')->eq($project['totalConsumed'])
|
||||
->set('left')->eq($project['totalLeft'])
|
||||
->set('consumed')->eq($project['totalConsumedNotDel'])
|
||||
->set('left')->eq($project['totalLeftNotDel'])
|
||||
->where('id')->eq($projectID)
|
||||
->exec();
|
||||
}
|
||||
|
||||
/* 5. Update programStatsTime. */
|
||||
$projectList = $this->dao->select('id,progress,path')->from(TABLE_PROJECT)
|
||||
$projectList = $this->dao->select('id,progress,path,consumed,`left`')->from(TABLE_PROJECT)
|
||||
->where('type')->eq('project')
|
||||
->andWhere('parent')->ne(0)
|
||||
->andWhere('deleted')->eq(0)
|
||||
@@ -1421,19 +1424,18 @@ class programModel extends model
|
||||
foreach($path as $programID)
|
||||
{
|
||||
if($programID == $projectID) continue;
|
||||
$programProgress[$programID][] = $project->progress;
|
||||
|
||||
if(!isset($programProgress[$programID])) $programProgress[$programID] = array('consumed' => 0, 'left' => 0);
|
||||
$programProgress[$programID]['consumed'] += $project->consumed;
|
||||
$programProgress[$programID]['left'] += $project->left;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($programProgress as $programID => $progress)
|
||||
foreach($programProgress as $programID => $hours)
|
||||
{
|
||||
$count = count($progress);
|
||||
$sum = array_sum($progress);
|
||||
$average = round($sum / $count, 2);
|
||||
$this->dao->update(TABLE_PROJECT)
|
||||
->set('progress')->eq($average)
|
||||
->where('id')->eq($programID)
|
||||
->exec();
|
||||
$progress = ($hours['consumed'] + $hours['left']) ? floor($hours['consumed'] / ($hours['consumed'] + $hours['left']) * 1000) / 1000 * 100 : 0;
|
||||
|
||||
$this->dao->update(TABLE_PROJECT)->set('progress')->eq($progress)->where('id')->eq($programID)->exec();
|
||||
}
|
||||
|
||||
/* 6. Update projectStatsTime in config. */
|
||||
|
||||
@@ -338,7 +338,7 @@ class programplanModel extends model
|
||||
if(($today > $end) and $plan->status != 'closed')
|
||||
{
|
||||
$data->delay = $this->lang->programplan->delayList[1];
|
||||
$data->delayDays = helper::diffDate($today, substr($end, 0, 10));
|
||||
$data->delayDays = helper::diffDate(($task->status == 'done' || $task->status == 'closed') ? substr($task->finishedDate, 0, 10) : $today, substr($end, 0, 10));
|
||||
}
|
||||
|
||||
/* If multi task then show the teams. */
|
||||
@@ -361,14 +361,14 @@ class programplanModel extends model
|
||||
{
|
||||
$stageIndex[$index]['progress']['totalEstimate'] += $task->estimate;
|
||||
$stageIndex[$index]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed;
|
||||
$stageIndex[$index]['progress']['totalReal'] += ($task->left + $task->consumed);
|
||||
$stageIndex[$index]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed);
|
||||
|
||||
$parent = $stage['parent'];
|
||||
if(isset($stageIndex[$parent]))
|
||||
{
|
||||
$stageIndex[$parent]['progress']['totalEstimate'] += $task->estimate;
|
||||
$stageIndex[$parent]['progress']['totalConsumed'] += $task->parent == '-1' ? 0 : $task->consumed;
|
||||
$stageIndex[$parent]['progress']['totalReal'] += ($task->left + $task->consumed);
|
||||
$stageIndex[$parent]['progress']['totalReal'] += ((($task->status == 'closed' || $task->status == 'cancel') ? 0 : $task->left) + $task->consumed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class project extends control
|
||||
if($this->config->edition != 'open') list($fields, $projects) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $projects);
|
||||
$this->post->set('fields', $fields);
|
||||
$this->post->set('rows', $projects);
|
||||
$this->post->set('kind', $this->lang->project->common);
|
||||
$this->post->set('kind', 'project');
|
||||
$this->fetch('file', 'export2' . $this->post->fileType, $_POST);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
$(function()
|
||||
{
|
||||
$('#testtaskForm').table(
|
||||
{
|
||||
replaceId: 'taskIdList',
|
||||
statisticCreator: function(table)
|
||||
{
|
||||
var $table = table.getTable();
|
||||
var $checkedRows = $table.find('tbody>tr.checked');
|
||||
var checkedTotal = $checkedRows.length;
|
||||
var checkedWait = $checkedRows.filter("[data-status=wait]").length;
|
||||
var checkedTesting = $checkedRows.filter("[data-status=doing]").length;
|
||||
var checkedBlocked = $checkedRows.filter("[data-status=blocked]").length;
|
||||
var checkedDone = $checkedRows.filter("[data-status=done]").length;
|
||||
var checkedStatistics = checkedAllSummary.replace('%total%', checkedTotal)
|
||||
.replace('%wait%', checkedWait)
|
||||
.replace('%testing%', checkedTesting)
|
||||
.replace('%blocked%', checkedBlocked)
|
||||
.replace('%done%', checkedDone);
|
||||
|
||||
return checkedTotal ? checkedStatistics : pageSummary;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<?php js::set('confirmDelete', $lang->testtask->confirmDelete)?>
|
||||
<?php if($project->hasProduct):?>
|
||||
<style>.tfoot {margin-left: 205px}</style>
|
||||
<style>.table-footer {margin-left: 205px}</style>
|
||||
<?php endif;?>
|
||||
<div id="mainMenu" class="clearfix">
|
||||
<div class="btn-toolbar pull-left">
|
||||
@@ -36,6 +36,12 @@
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$waitCount = 0;
|
||||
$testingCount = 0;
|
||||
$blockedCount = 0;
|
||||
$doneCount = 0;
|
||||
?>
|
||||
<div id="mainContent">
|
||||
<?php if(empty($tasks)):?>
|
||||
<div class="table-empty-tip">
|
||||
@@ -47,14 +53,19 @@
|
||||
</p>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<form class="main-table table-testtask" data-ride="table" data-group="true" method="post" target='hiddenwin' id='testtaskForm'>
|
||||
<form class="main-table table-testtask" data-group="true" method="post" target='hiddenwin' id='testtaskForm'>
|
||||
<table class="table table-grouped has-sort-head" id='taskList'>
|
||||
<thead>
|
||||
<?php $vars = "projectID=$projectID&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
|
||||
<?php $canTestReport = ($canBeChanged and common::hasPriv('testreport', 'browse'));?>
|
||||
<?php $canTestReport = ($canBeChanged and common::hasPriv('testreport', 'browse') and !$project->multiple);?>
|
||||
<tr class='<?php if($total and $project->hasProduct) echo 'divider'; ?>'>
|
||||
<th class='c-side text-center <?php if(!$project->hasProduct) echo 'hide';?>'><?php common::printOrderLink('product', $orderBy, $vars, $lang->testtask->product);?></th>
|
||||
<th class="c-id">
|
||||
<?php if($canTestReport):?>
|
||||
<div class="checkbox-primary check-all" title="<?php echo $lang->selectAll?>">
|
||||
<label></label>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?>
|
||||
</th>
|
||||
<th><?php common::printOrderLink('name', $orderBy, $vars, $lang->testtask->name);?></th>
|
||||
@@ -67,12 +78,6 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$waitCount = 0;
|
||||
$testingCount = 0;
|
||||
$blockedCount = 0;
|
||||
$doneCount = 0;
|
||||
?>
|
||||
<?php foreach($tasks as $product => $productTasks):?>
|
||||
<?php $productName = zget($products, $product, '');?>
|
||||
<?php foreach($productTasks as $task):?>
|
||||
@@ -80,7 +85,7 @@
|
||||
<?php if($task->status == 'doing') $testingCount ++;?>
|
||||
<?php if($task->status == 'blocked') $blockedCount ++;?>
|
||||
<?php if($task->status == 'done') $doneCount ++;?>
|
||||
<tr data-id='<?php echo $product;?>' <?php if($task == reset($productTasks)) echo "class='divider-top'";?>>
|
||||
<tr data-id='<?php echo $product;?>' <?php if($task == reset($productTasks)) echo "class='divider-top'";?> data-status='<?php echo $task->status;?>'>
|
||||
<?php if($task == reset($productTasks)):?>
|
||||
<td rowspan='<?php echo count($productTasks);?>' class='c-side text-left group-toggle <?php if(!$project->hasProduct) echo 'hide';?>'>
|
||||
<a class='text-primary' title='<?php echo $productName;?>'><i class='icon icon-caret-down'></i> <?php echo $productName;?></a>
|
||||
@@ -88,7 +93,11 @@
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td class="c-id">
|
||||
<?php if($canTestReport):?>
|
||||
<?php echo html::checkbox('taskIdList', array($task->id => sprintf('%03d', $task->id)));?>
|
||||
<?php else:?>
|
||||
<?php printf('%03d', $task->id);?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
<td class='text-left' title="<?php echo $task->name?>"><?php echo html::a($this->createLink('testtask', 'cases', "taskID=$task->id"), $task->name, '', "data-app='project'");?></td>
|
||||
<td title="<?php echo $task->buildName?>">
|
||||
@@ -139,10 +148,22 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-footer">
|
||||
<?php if($canTestReport):?>
|
||||
<div class="checkbox-primary check-all"><label><?php echo $lang->selectAll?></label></div>
|
||||
<div class="table-actions btn-toolbar">
|
||||
<?php
|
||||
$actionLink = $this->createLink('project', 'testreport', "objectID=$projectID&objctType=project");
|
||||
$misc = common::hasPriv('testreport', 'browse') ? "onclick=\"setFormAction('$actionLink', '', '#testtaskForm')\"" : "disabled='disabled'";
|
||||
echo html::commonButton($lang->testreport->common, $misc);
|
||||
?>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div class="table-statistic"><?php echo sprintf($lang->testtask->allSummary, $total, $waitCount, $testingCount, $blockedCount, $doneCount);?></div>
|
||||
<?php $pager->show('right', 'pagerjs');?>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php js::set('pageSummary', sprintf($lang->testtask->allSummary, $total, $waitCount, $testingCount, $blockedCount, $doneCount));?>
|
||||
<?php js::set('checkedAllSummary', $lang->testtask->checkedAllSummary);?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -204,10 +204,9 @@
|
||||
<tr class='statsTr'><td></td><td></td><td></td><td></td></tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<?php $progress = $project->model == 'waterfall' ? $this->project->getWaterfallProgress($project->id) : (((float)$workhour->totalConsumed + (float)$workhour->totalLeft) ? floor($workhour->totalConsumed / ((float)$workhour->totalConsumed + (float)$workhour->totalLeft) * 1000) / 1000 * 100 : 0);?>
|
||||
<?php echo $lang->project->progress;?> <?php echo $progress . $lang->percent;?>
|
||||
<?php echo $lang->project->progress;?> <?php echo $project->progress . $lang->percent;?>
|
||||
<div class="progress inline-block">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $progress . $lang->percent;?>"></div>
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->progress . $lang->percent;?>"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -263,7 +263,7 @@ $config->task->datatable->fieldList['mailto']['required'] = 'no';
|
||||
|
||||
$config->task->datatable->fieldList['module']['title'] = 'module';
|
||||
$config->task->datatable->fieldList['module']['control'] = 'select';
|
||||
$config->task->datatable->fieldList['module']['dataSource'] = array('module' => 'tree', 'method' => 'getTaskOptionMenu', 'params' => '$executionID');
|
||||
$config->task->datatable->fieldList['module']['dataSource'] = array('module' => 'tree', 'method' => 'getAllModulePairs');
|
||||
$config->task->datatable->fieldList['module']['display'] = false;
|
||||
|
||||
$config->task->datatable->fieldList['execution']['title'] = 'execution';
|
||||
|
||||
@@ -226,7 +226,7 @@ class testcase extends control
|
||||
$this->config->testcase->search['onMenuBar'] = 'yes';
|
||||
|
||||
$searchProducts = $this->product->getPairs('', 0, '', 'all');
|
||||
$this->testcase->buildSearchForm($productID, $searchProducts, $queryID, $actionURL, $projectID);
|
||||
$this->testcase->buildSearchForm($productID, $searchProducts, $queryID, $actionURL, $projectID, 0, $branch);
|
||||
|
||||
$showModule = !empty($this->config->datatable->testcaseBrowse->showModule) ? $this->config->datatable->testcaseBrowse->showModule : '';
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ $lang->testcase->mustChooseResult = 'Prüfungsergebnis wird benötigt.';
|
||||
$lang->testcase->noModule = '<div>Es existieren keine Module</div><div>Jetzt verwalten</div>';
|
||||
$lang->testcase->noCase = 'Keine Fälle. ';
|
||||
$lang->testcase->importedCases = 'The case with ID%s has been imported in the same module and has been ignored.';
|
||||
$lang->testcase->importedFromLib = '%s items imported successfully: %s.';
|
||||
|
||||
$lang->testcase->searchStories = 'Story suchen';
|
||||
$lang->testcase->selectLib = 'Bibliothek wählen';
|
||||
|
||||
@@ -246,6 +246,7 @@ $lang->testcase->mustChooseResult = 'Review result is required.';
|
||||
$lang->testcase->noModule = '<div>You have no modules.</div><div>Manage it now.</div>';
|
||||
$lang->testcase->noCase = 'No cases yet. ';
|
||||
$lang->testcase->importedCases = 'The case with ID%s has been imported in the same module and has been ignored.';
|
||||
$lang->testcase->importedFromLib = '%s items imported successfully: %s.';
|
||||
|
||||
$lang->testcase->searchStories = 'Enter to search for stories';
|
||||
$lang->testcase->selectLib = 'Select Library';
|
||||
|
||||
@@ -246,6 +246,7 @@ $lang->testcase->mustChooseResult = 'La Validation du résultat est nécessaire.
|
||||
$lang->testcase->noModule = "<div>Vous n'avez aucun modules.</div><div>Gérer les modules maintenant.</div>";
|
||||
$lang->testcase->noCase = "Aucun CasTest pour l'instant. ";
|
||||
$lang->testcase->importedCases = 'Le cas d\'utilisation avec l\'ID %s est déjà importé dans le même module, il est donc ignoré.';
|
||||
$lang->testcase->importedFromLib = '%s items imported successfully: %s.';
|
||||
|
||||
$lang->testcase->searchStories = 'Rechercher des stories';
|
||||
$lang->testcase->selectLib = 'Sélectionner Library';
|
||||
|
||||
@@ -246,6 +246,7 @@ $lang->testcase->mustChooseResult = '必须选择评审结果';
|
||||
$lang->testcase->noModule = '<div>您现在还没有模块信息</div><div>请维护测试模块</div>';
|
||||
$lang->testcase->noCase = '暂时没有用例。';
|
||||
$lang->testcase->importedCases = 'ID为 %s 的用例在相同模块已经导入,已忽略。';
|
||||
$lang->testcase->importedFromLib = '导入成功%s项:%s。';
|
||||
|
||||
$lang->testcase->searchStories = "键入来搜索{$lang->SRCommon}";
|
||||
$lang->testcase->selectLib = '请选择库';
|
||||
|
||||
@@ -1930,10 +1930,11 @@ class testcaseModel extends model
|
||||
$caseModules[$branch] = $this->loadModel('testsuite')->getCanImportModules($productID, $libID, $branch);
|
||||
}
|
||||
|
||||
$libCases = $this->dao->select('*')->from(TABLE_CASE)->where('deleted')->eq(0)->andWhere('id')->in($data->caseIdList)->fetchAll('id');
|
||||
$libSteps = $this->dao->select('*')->from(TABLE_CASESTEP)->where('`case`')->in($data->caseIdList)->orderBy('id')->fetchGroup('case');
|
||||
$libFiles = $this->dao->select('*')->from(TABLE_FILE)->where('objectID')->in($data->caseIdList)->andWhere('objectType')->eq('testcase')->fetchGroup('objectID', 'id');
|
||||
$imported = '';
|
||||
$libCases = $this->dao->select('*')->from(TABLE_CASE)->where('deleted')->eq(0)->andWhere('id')->in($data->caseIdList)->fetchAll('id');
|
||||
$libSteps = $this->dao->select('*')->from(TABLE_CASESTEP)->where('`case`')->in($data->caseIdList)->orderBy('id')->fetchGroup('case');
|
||||
$libFiles = $this->dao->select('*')->from(TABLE_FILE)->where('objectID')->in($data->caseIdList)->andWhere('objectType')->eq('testcase')->fetchGroup('objectID', 'id');
|
||||
$hasImported = '';
|
||||
$importedCases = array();
|
||||
foreach($libCases as $libCaseID => $case)
|
||||
{
|
||||
$case->fromCaseID = $case->id;
|
||||
@@ -1946,7 +1947,7 @@ class testcaseModel extends model
|
||||
$branch = isset($case->branch) ? $case->branch : 0;
|
||||
if(empty($caseModules[$branch][$case->fromCaseID][$case->module]))
|
||||
{
|
||||
$imported .= "$case->fromCaseID,";
|
||||
$hasImported .= "$case->fromCaseID,";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1991,12 +1992,18 @@ class testcaseModel extends model
|
||||
unset($file->id);
|
||||
$this->dao->insert(TABLE_FILE)->data($file)->exec();
|
||||
}
|
||||
|
||||
$importedCases[] = $libCaseID;
|
||||
|
||||
$this->loadModel('action')->create('case', $caseID, 'fromlib', '', $case->lib);
|
||||
}
|
||||
if(!empty($imported))
|
||||
|
||||
if(!empty($importedCases)) print(js::alert(sprintf($this->lang->testcase->importedFromLib, count($importedCases), implode(',', $importedCases))));
|
||||
|
||||
if(!empty($hasImported))
|
||||
{
|
||||
$imported = trim($imported, ',');
|
||||
return print(js::error(sprintf($this->lang->testcase->importedCases, $imported)));
|
||||
$hasImported = trim($hasImported, ',');
|
||||
return print(js::error(sprintf($this->lang->testcase->importedCases, $hasImported)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -302,9 +302,13 @@ class testreport extends control
|
||||
|
||||
list($bugInfo, $bugSummary) = $this->testreport->getBug4Report($tasks, $productIdList, $begin, $end, $builds);
|
||||
|
||||
$members = $this->dao->select('DISTINCT lastRunner')->from(TABLE_TESTRUN)->where('task')->in(array_keys($tasks))->fetchPairs('lastRunner', 'lastRunner');
|
||||
$taskMembers = explode(',', $task->members);
|
||||
$members = array_merge($members, $taskMembers);
|
||||
|
||||
$this->view->begin = $begin;
|
||||
$this->view->end = $end;
|
||||
$this->view->members = $this->dao->select('DISTINCT lastRunner')->from(TABLE_TESTRUN)->where('task')->in(array_keys($tasks))->fetchPairs('lastRunner', 'lastRunner');
|
||||
$this->view->members = $members;
|
||||
$this->view->owner = $owner;
|
||||
|
||||
$this->view->stories = $stories;
|
||||
|
||||
@@ -1660,4 +1660,17 @@ class testtask extends control
|
||||
$result = $this->dao->select('*')->from(TABLE_TESTRESULT)->where('id')->eq((int)$resultID)->fetch();
|
||||
$this->send(array('result' => 'success', 'message' => '', 'data' => $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Get executionID by buildID.
|
||||
*
|
||||
* @param int $buildID
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function ajaxGetExecutionByBuild($buildID)
|
||||
{
|
||||
$execution = $this->loadModel('execution')->getByBuild($buildID);
|
||||
return print($execution ? $execution->id : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,17 @@ function loadExecutions(productID)
|
||||
});
|
||||
}
|
||||
|
||||
$('#mainContent').on('change', '#build', function()
|
||||
{
|
||||
var buildID = $(this).val();
|
||||
link = createLink('testtask', 'ajaxGetExecutionByBuild', 'buildID=' + buildID);
|
||||
$.get(link, function(data)
|
||||
{
|
||||
$('#execution').val(data);
|
||||
$("#execution").trigger("chosen:updated");
|
||||
});
|
||||
})
|
||||
|
||||
/* If the mouse hover over the manage contacts button, give tip. */
|
||||
$(function()
|
||||
{
|
||||
|
||||
@@ -3,3 +3,14 @@ $(function()
|
||||
adjustPriBoxWidth();
|
||||
if(config.onlybody) $('#ownerAndPriBox .picker-selection').css('width', '123px');
|
||||
})
|
||||
|
||||
$('#mainContent').on('change', '#build', function()
|
||||
{
|
||||
var buildID = $(this).val();
|
||||
link = createLink('testtask', 'ajaxGetExecutionByBuild', 'buildID=' + buildID);
|
||||
$.get(link, function(data)
|
||||
{
|
||||
$('#execution').val(data);
|
||||
$("#execution").trigger("chosen:updated");
|
||||
});
|
||||
})
|
||||
|
||||
@@ -58,6 +58,7 @@ $lang->testtask->run = 'Test Run ID';
|
||||
$lang->testtask->job = 'Job';
|
||||
$lang->testtask->compile = 'Compile';
|
||||
$lang->testtask->duration = 'Duration';
|
||||
$lang->testtask->myInvolved = 'Involved';
|
||||
|
||||
$lang->testtask->viewAction = "View Request";
|
||||
$lang->testtask->casesAction = 'Browse Cases List';
|
||||
@@ -76,6 +77,7 @@ $lang->testtask->execution = $lang->execution->common;
|
||||
$lang->testtask->type = 'Typ';
|
||||
$lang->testtask->build = 'Build';
|
||||
$lang->testtask->owner = 'Besitzer';
|
||||
$lang->testtask->members = 'Participant';
|
||||
$lang->testtask->executor = 'Executor';
|
||||
$lang->testtask->execTime = 'Exec Time';
|
||||
$lang->testtask->pri = 'Priorität';
|
||||
@@ -202,6 +204,7 @@ $lang->testtask->report->options->width = 500;
|
||||
$lang->testtask->report->options->height = 140;
|
||||
|
||||
$lang->testtask->featureBar['browse']['totalStatus'] = $lang->testtask->totalStatus;
|
||||
$lang->testtask->featureBar['browse']['myinvolved'] = $lang->testtask->myInvolved;
|
||||
$lang->testtask->featureBar['browse']['wait'] = $lang->testtask->wait;
|
||||
$lang->testtask->featureBar['browse']['doing'] = $lang->testtask->testing;
|
||||
$lang->testtask->featureBar['browse']['blocked'] = $lang->testtask->blocked;
|
||||
|
||||
@@ -58,6 +58,7 @@ $lang->testtask->run = 'Test Run ID';
|
||||
$lang->testtask->job = 'Job';
|
||||
$lang->testtask->compile = 'Compile';
|
||||
$lang->testtask->duration = 'Duration';
|
||||
$lang->testtask->myInvolved = 'Involved';
|
||||
|
||||
$lang->testtask->viewAction = "View Request";
|
||||
$lang->testtask->casesAction = 'Browse Cases List';
|
||||
@@ -76,6 +77,7 @@ $lang->testtask->execution = $lang->execution->common;
|
||||
$lang->testtask->type = 'Type';
|
||||
$lang->testtask->build = 'Build';
|
||||
$lang->testtask->owner = 'Owner';
|
||||
$lang->testtask->members = 'Participant';
|
||||
$lang->testtask->executor = 'Executor';
|
||||
$lang->testtask->execTime = 'Exec Time';
|
||||
$lang->testtask->pri = 'Priority';
|
||||
@@ -202,6 +204,7 @@ $lang->testtask->report->options->width = 500;
|
||||
$lang->testtask->report->options->height = 140;
|
||||
|
||||
$lang->testtask->featureBar['browse']['totalStatus'] = $lang->testtask->totalStatus;
|
||||
$lang->testtask->featureBar['browse']['myinvolved'] = $lang->testtask->myInvolved;
|
||||
$lang->testtask->featureBar['browse']['wait'] = $lang->testtask->wait;
|
||||
$lang->testtask->featureBar['browse']['doing'] = $lang->testtask->testing;
|
||||
$lang->testtask->featureBar['browse']['blocked'] = $lang->testtask->blocked;
|
||||
|
||||
@@ -58,6 +58,7 @@ $lang->testtask->run = 'Test Run ID';
|
||||
$lang->testtask->job = 'Job';
|
||||
$lang->testtask->compile = 'Compile';
|
||||
$lang->testtask->duration = 'Duration';
|
||||
$lang->testtask->myInvolved = 'Involved';
|
||||
|
||||
$lang->testtask->viewAction = "View Request";
|
||||
$lang->testtask->casesAction = 'Browse Cases List';
|
||||
@@ -76,6 +77,7 @@ $lang->testtask->execution = $lang->execution->common;
|
||||
$lang->testtask->type = 'Type';
|
||||
$lang->testtask->build = 'Build';
|
||||
$lang->testtask->owner = 'Owner';
|
||||
$lang->testtask->members = 'Participant';
|
||||
$lang->testtask->executor = 'Executeur';
|
||||
$lang->testtask->execTime = 'Durée Exec';
|
||||
$lang->testtask->pri = 'Priorité';
|
||||
@@ -202,6 +204,7 @@ $lang->testtask->report->options->width = 500;
|
||||
$lang->testtask->report->options->height = 140;
|
||||
|
||||
$lang->testtask->featureBar['browse']['totalStatus'] = $lang->testtask->totalStatus;
|
||||
$lang->testtask->featureBar['browse']['myinvolved'] = $lang->testtask->myInvolved;
|
||||
$lang->testtask->featureBar['browse']['wait'] = $lang->testtask->wait;
|
||||
$lang->testtask->featureBar['browse']['doing'] = $lang->testtask->testing;
|
||||
$lang->testtask->featureBar['browse']['blocked'] = $lang->testtask->blocked;
|
||||
|
||||
@@ -58,6 +58,7 @@ $lang->testtask->run = '执行编号';
|
||||
$lang->testtask->job = '构建任务';
|
||||
$lang->testtask->compile = '构建';
|
||||
$lang->testtask->duration = '持续时间';
|
||||
$lang->testtask->myInvolved = '由我参与';
|
||||
|
||||
$lang->testtask->viewAction = "测试单概况";
|
||||
$lang->testtask->casesAction = '浏览用例列表';
|
||||
@@ -76,6 +77,7 @@ $lang->testtask->execution = '所属' . $lang->execution->common;
|
||||
$lang->testtask->type = '测试类型';
|
||||
$lang->testtask->build = '版本';
|
||||
$lang->testtask->owner = '负责人';
|
||||
$lang->testtask->members = '参与人';
|
||||
$lang->testtask->executor = '执行人';
|
||||
$lang->testtask->execTime = '执行时间';
|
||||
$lang->testtask->pri = '优先级';
|
||||
@@ -202,6 +204,7 @@ $lang->testtask->report->options->width = 500;
|
||||
$lang->testtask->report->options->height = 140;
|
||||
|
||||
$lang->testtask->featureBar['browse']['totalStatus'] = $lang->testtask->totalStatus;
|
||||
$lang->testtask->featureBar['browse']['myinvolved'] = $lang->testtask->myInvolved;
|
||||
$lang->testtask->featureBar['browse']['wait'] = $lang->testtask->wait;
|
||||
$lang->testtask->featureBar['browse']['doing'] = $lang->testtask->testing;
|
||||
$lang->testtask->featureBar['browse']['blocked'] = $lang->testtask->blocked;
|
||||
|
||||
@@ -20,7 +20,7 @@ class testtaskModel extends model
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function create($projectID = 0)
|
||||
public function create($projectID = 0)
|
||||
{
|
||||
if($this->post->execution)
|
||||
{
|
||||
@@ -28,16 +28,25 @@ class testtaskModel extends model
|
||||
$projectID = $execution->project;
|
||||
}
|
||||
|
||||
if($this->post->build && empty($projectID))
|
||||
{
|
||||
$build = $this->loadModel('build')->getById($this->post->build);
|
||||
$projectID = $build->project;
|
||||
}
|
||||
|
||||
$task = fixer::input('post')
|
||||
->setDefault('build', '')
|
||||
->setDefault('project', $projectID)
|
||||
->setDefault('createdBy', $this->app->user->account)
|
||||
->setDefault('createdDate', helper::now())
|
||||
->setDefault('members', '')
|
||||
->stripTags($this->config->testtask->editor->create['id'], $this->config->allowedTags)
|
||||
->join('mailto', ',')
|
||||
->join('type', ',')
|
||||
->join('members', ',')
|
||||
->remove('files,labels,uid,contactListMenu')
|
||||
->get();
|
||||
$task->members = trim($task->members, ',');
|
||||
|
||||
$task = $this->loadModel('file')->processImgURL($task, $this->config->testtask->editor->create['id'], $this->post->uid);
|
||||
$this->dao->insert(TABLE_TESTTASK)->data($task)
|
||||
@@ -87,8 +96,9 @@ class testtaskModel extends model
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in("0,{$this->app->user->view->sprints}")->fi()
|
||||
->beginIF($scopeAndStatus[0] == 'local')->andWhere('t1.product')->eq((int)$productID)->fi()
|
||||
->beginIF($scopeAndStatus[0] == 'all')->andWhere('t1.product')->in($products)->fi()
|
||||
->beginIF(strtolower($scopeAndStatus[1]) == 'myinvolved')->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.members)")->fi()
|
||||
->beginIF(strtolower($scopeAndStatus[1]) == 'totalstatus')->andWhere('t1.status')->in('blocked,doing,wait,done')->fi()
|
||||
->beginIF(!in_array(strtolower($scopeAndStatus[1]), array('totalstatus', 'review'), true))->andWhere('t1.status')->eq($scopeAndStatus[1])->fi()
|
||||
->beginIF(!in_array(strtolower($scopeAndStatus[1]), array('totalstatus', 'review', 'myinvolved'), true))->andWhere('t1.status')->eq($scopeAndStatus[1])->fi()
|
||||
->beginIF($branch !== 'all')->andWhere("CONCAT(',', t4.branch, ',')")->like("%,$branch,%")->fi()
|
||||
->beginIF($beginTime)->andWhere('t1.begin')->ge($beginTime)->fi()
|
||||
->beginIF($endTime)->andWhere('t1.end')->le($endTime)->fi()
|
||||
@@ -105,6 +115,7 @@ class testtaskModel extends model
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
|
||||
foreach($tasks as $taskID => $task)
|
||||
{
|
||||
if($task->multiple)
|
||||
@@ -773,15 +784,26 @@ class testtaskModel extends model
|
||||
->setDefault('type', '')
|
||||
->setDefault('mailto', '')
|
||||
->setDefault('deleteFiles', array())
|
||||
->setDefault('members', '')
|
||||
->stripTags($this->config->testtask->editor->edit['id'], $this->config->allowedTags)
|
||||
->join('mailto', ',')
|
||||
->join('type', ',')
|
||||
->join('members', ',')
|
||||
->remove('files,labels,uid,comment,contactListMenu')
|
||||
->get();
|
||||
$task->members = trim($task->members, ',');
|
||||
|
||||
/* Fix bug #35419. */
|
||||
$execution = $this->loadModel('execution')->getByID($task->execution);
|
||||
$task->project = $execution->project;
|
||||
$execution = $this->loadModel('execution')->getByID($task->execution);
|
||||
if(!$execution)
|
||||
{
|
||||
$build = $this->loadModel('build')->getById($task->build);
|
||||
$task->project = $build->project;
|
||||
}
|
||||
else
|
||||
{
|
||||
$task->project = $execution->project;
|
||||
}
|
||||
|
||||
$task = $this->loadModel('file')->processImgURL($task, $this->config->testtask->editor->edit['id'], $this->post->uid);
|
||||
|
||||
@@ -1777,7 +1799,7 @@ class testtaskModel extends model
|
||||
public function getToAndCcList($testtask)
|
||||
{
|
||||
/* Set toList and ccList. */
|
||||
$toList = $testtask->owner;
|
||||
$toList = $testtask->owner . ',' . $testtask->members . ',';
|
||||
$ccList = str_replace(' ', '', trim($testtask->mailto, ','));
|
||||
|
||||
if(empty($toList))
|
||||
@@ -1795,6 +1817,7 @@ class testtaskModel extends model
|
||||
$ccList = substr($ccList, $commaPos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return array($toList, $ccList);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ $status = strtolower($status);
|
||||
<th class='text-left'> <?php common::printOrderLink('execution', $orderBy, $vars, $lang->testtask->execution);?></th>
|
||||
<th class='c-status text-left'><?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
|
||||
<th class='c-user text-left'> <?php common::printOrderLink('owner', $orderBy, $vars, $lang->testtask->owner);?></th>
|
||||
<th class='c-user text-left'> <?php common::printOrderLink('members', $orderBy, $vars, $lang->testtask->members);?></th>
|
||||
<th class='c-date text-left'> <?php common::printOrderLink('begin', $orderBy, $vars, $lang->testtask->begin);?></th>
|
||||
<th class='c-date text-left'> <?php common::printOrderLink('end', $orderBy, $vars, $lang->testtask->end);?></th>
|
||||
<?php
|
||||
@@ -114,6 +115,10 @@ $status = strtolower($status);
|
||||
</span>
|
||||
</td>
|
||||
<td title="<?php echo zget($users, $task->owner);?>"><?php echo zget($users, $task->owner);?></td>
|
||||
<?php $members = array()?>
|
||||
<?php foreach(explode(',', $task->members) as $member) $members[] = zget($users, $member);?>
|
||||
<?php $members = implode(',', $members)?>
|
||||
<td title="<?php echo $members;?>"><?php echo $members;?></td>
|
||||
<td><?php echo $task->begin?></td>
|
||||
<td><?php echo $task->end?></td>
|
||||
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $task) . "</td>";?>
|
||||
|
||||
@@ -73,6 +73,10 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->testtask->members;?></th>
|
||||
<td><?php echo html::select('members[]', $users, '', "class='form-control picker-select' multiple")?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->testtask->begin;?></th>
|
||||
<td>
|
||||
|
||||
@@ -56,6 +56,12 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->testtask->members;?></th>
|
||||
<td>
|
||||
<?php echo html::select('members[]', $users, $task->members, "class='form-control picker-select' multiple");?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->testtask->begin;?></th>
|
||||
<td>
|
||||
|
||||
@@ -152,6 +152,11 @@ class transfer extends control
|
||||
{
|
||||
$filter = '';
|
||||
if($model == 'task') $filter = 'estimate';
|
||||
if($model == 'bug')
|
||||
{
|
||||
$this->loadModel('bug');
|
||||
$this->config->bug->datatable->fieldList['execution']['dataSource'] = array('module' => 'product', 'method' =>'getAllExecutionPairsByProduct', 'params' => '$productID&$branch');
|
||||
}
|
||||
if($model == 'story' and $this->session->storyType == 'requirement') $this->loadModel('story')->replaceUserRequirementLang();
|
||||
|
||||
$this->loadModel($model);
|
||||
@@ -198,6 +203,7 @@ class transfer extends control
|
||||
{
|
||||
$this->loadModel($model);
|
||||
$fields = $this->config->$model->templateFields;
|
||||
if($model == 'bug') $this->config->bug->datatable->fieldList['execution']['dataSource'] = array('module' => 'product', 'method' =>'getAllExecutionPairsByProduct', 'params' => '$productID&$branch');
|
||||
|
||||
if($this->config->edition != 'open')
|
||||
{
|
||||
|
||||
@@ -707,6 +707,11 @@ class upgradeModel extends model
|
||||
$this->processHistoryDataForMetric();
|
||||
$this->loadModel('metric')->updateMetricDate();
|
||||
break;
|
||||
case '18_8':
|
||||
/* Upgrade members for testtask. */
|
||||
$this->upgradeTesttaskMembers();
|
||||
$this->loadModel('program')->refreshStats(true);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->deletePatch();
|
||||
@@ -9878,4 +9883,23 @@ class upgradeModel extends model
|
||||
}
|
||||
$this->dao->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade testtask members.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function upgradeTesttaskMembers()
|
||||
{
|
||||
$memberGroup = $this->dao->select('task,lastRunner')->from(TABLE_TESTRUN)
|
||||
->where('lastRunner')->ne('')
|
||||
->fetchGroup('task', 'lastRunner');
|
||||
|
||||
foreach($memberGroup as $taskID => $members)
|
||||
{
|
||||
$members = implode(',', array_keys($members));
|
||||
$this->dao->update(TABLE_TESTTASK)->set('members')->eq($members)->where('id')->eq($taskID)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,15 +90,15 @@ EOD;
|
||||
$lang->weekly->blockHelpNotice = <<<EOD
|
||||
<h2>Progress this week</h2>
|
||||
Calculation method:
|
||||
<br />1) Progress of this week=total consumed man hours/(total consumed man hours+remaining man hours)
|
||||
<br />1) Project progress = consumed task hours / (consumed task hours + remaining task hours) * 100%
|
||||
<p>Statistical range:</p>
|
||||
1) The total consumed man hours include task, demand, bug, use case, version, test sheet, problem, risk, document and review time
|
||||
<br />2) To avoid repeated calculation, the task only contains child tasks, not parent tasks
|
||||
<br />3) Including deleted tasks, requirements, bugs, use cases, versions, test sheets, problems, risks, documents, and consumed man hours in reviews
|
||||
<br />4) This includes the work hours consumed by deleted tasks, requirements, bugs, use cases, versions, test sheets, and documents
|
||||
<br />5) Including work hours consumed by cancelled tasks, problems and risks
|
||||
<br />6) Exclude remaining work on canceled tasks
|
||||
<br />7) Exclude remaining work for deleted tasks in progress
|
||||
1) Only the working hour consumption data of the task is counted
|
||||
<br />2) To avoid repeated calculation, the task working hours only include child tasks, not parent tasks
|
||||
<br />3) Including the working hours consumed by canceled tasks
|
||||
<br />4) Excluding the working hours consumed in deleted tasks
|
||||
<br />5) Excluding the working hours consumed in deleted tasks in progress
|
||||
<br />6) Excluding the remaining working hours of canceled tasks
|
||||
<br />7) Excluding the remaining working hours of deleted tasks in progress
|
||||
<h2>PV Planned Value</h2>
|
||||
Calculation method:
|
||||
<br />1) The estimated start date and end date of the task are within the range of the start and end dates of this week, and the estimated work hours are accumulated
|
||||
|
||||
@@ -90,15 +90,15 @@ EOD;
|
||||
$lang->weekly->blockHelpNotice = <<<EOD
|
||||
<h2>Progress this week</h2>
|
||||
Calculation method:
|
||||
<br />1) Progress of this week=total consumed man hours/(total consumed man hours+remaining man hours)
|
||||
<br />1) Project progress = consumed task hours / (consumed task hours + remaining task hours) * 100%
|
||||
<p>Statistical range:</p>
|
||||
1) The total consumed man hours include task, demand, bug, use case, version, test sheet, problem, risk, document and review time
|
||||
<br />2) To avoid repeated calculation, the task only contains child tasks, not parent tasks
|
||||
<br />3) Including deleted tasks, requirements, bugs, use cases, versions, test sheets, problems, risks, documents, and consumed man hours in reviews
|
||||
<br />4) This includes the work hours consumed by deleted tasks, requirements, bugs, use cases, versions, test sheets, and documents
|
||||
<br />5) Including work hours consumed by cancelled tasks, problems and risks
|
||||
<br />6) Exclude remaining work on canceled tasks
|
||||
<br />7) Exclude remaining work for deleted tasks in progress
|
||||
1) Only the working hour consumption data of the task is counted
|
||||
<br />2) To avoid repeated calculation, the task working hours only include child tasks, not parent tasks
|
||||
<br />3) Including the working hours consumed by canceled tasks
|
||||
<br />4) Excluding the working hours consumed in deleted tasks
|
||||
<br />5) Excluding the working hours consumed in deleted tasks in progress
|
||||
<br />6) Excluding the remaining working hours of canceled tasks
|
||||
<br />7) Excluding the remaining working hours of deleted tasks in progress
|
||||
<h2>PV Planned Value</h2>
|
||||
Calculation method:
|
||||
<br />1) The estimated start date and end date of the task are within the range of the start and end dates of this week, and the estimated work hours are accumulated
|
||||
|
||||
@@ -90,15 +90,15 @@ EOD;
|
||||
$lang->weekly->blockHelpNotice = <<<EOD
|
||||
<h2>Progress this week</h2>
|
||||
Calculation method:
|
||||
<br />1) Progress of this week=total consumed man hours/(total consumed man hours+remaining man hours)
|
||||
<br />1) Project progress = consumed task hours / (consumed task hours + remaining task hours) * 100%
|
||||
<p>Statistical range:</p>
|
||||
1) The total consumed man hours include task, demand, bug, use case, version, test sheet, problem, risk, document and review time
|
||||
<br />2) To avoid repeated calculation, the task only contains child tasks, not parent tasks
|
||||
<br />3) Including deleted tasks, requirements, bugs, use cases, versions, test sheets, problems, risks, documents, and consumed man hours in reviews
|
||||
<br />4) This includes the work hours consumed by deleted tasks, requirements, bugs, use cases, versions, test sheets, and documents
|
||||
<br />5) Including work hours consumed by cancelled tasks, problems and risks
|
||||
<br />6) Exclude remaining work on canceled tasks
|
||||
<br />7) Exclude remaining work for deleted tasks in progress
|
||||
1) Only the working hour consumption data of the task is counted
|
||||
<br />2) To avoid repeated calculation, the task working hours only include child tasks, not parent tasks
|
||||
<br />3) Including the working hours consumed by canceled tasks
|
||||
<br />4) Excluding the working hours consumed in deleted tasks
|
||||
<br />5) Excluding the working hours consumed in deleted tasks in progress
|
||||
<br />6) Excluding the remaining working hours of canceled tasks
|
||||
<br />7) Excluding the remaining working hours of deleted tasks in progress
|
||||
<h2>PV Planned Value</h2>
|
||||
Calculation method:
|
||||
<br />1) The estimated start date and end date of the task are within the range of the start and end dates of this week, and the estimated work hours are accumulated
|
||||
|
||||
@@ -90,13 +90,13 @@ EOD;
|
||||
$lang->weekly->blockHelpNotice = <<<EOD
|
||||
<h2>本周进度</h2>
|
||||
计算方式:
|
||||
<br />1)本周进度=总消耗工时/(总消耗工时+剩余工时)
|
||||
<br />1)项目进度=已消耗任务工时 /(已消耗任务工时 + 剩余任务工时)*100%
|
||||
<p>统计范围:</p>
|
||||
1)总消耗的工时包括任务、需求、Bug、用例、版本、测试单、问题、风险、文档、评审的耗时
|
||||
<br />2)为避免重复计算,任务只包含子任务,不包括父任务
|
||||
<br />3)包括已删除任务、需求、Bug、用例、版本、测试单、问题、风险、文档、评审中已消耗的工时
|
||||
<br />4)包括已删除执行中任务、需求、Bug、用例、版本、测试单、文档已消耗的工时
|
||||
<br />5)包括已取消任务、问题、风险已消耗的工时
|
||||
1)只统计任务的工时消耗数据。
|
||||
<br />2)为避免重复计算,任务工时只包含子任务,不包括父任务
|
||||
<br />3)包括已取消任务消耗的工时
|
||||
<br />4)不包括已删除任务中消耗的工时
|
||||
<br />5)不包括已删除执行中任务消耗的工时
|
||||
<br />6)不包括已取消任务的剩余工时
|
||||
<br />7)不包括已删除执行中任务的剩余工时
|
||||
<h2>PV 计划完成的工作</h2>
|
||||
|
||||
Reference in New Issue
Block a user