model.php 77 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784
  1. <?php
  2. /**
  3. * The model file of dashboard module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package dashboard
  9. * @version $Id: model.php 4129 2013-01-18 01:58:14Z wwccss $
  10. * @link https://www.zentao.net
  11. */
  12. ?>
  13. <?php
  14. class myModel extends model
  15. {
  16. /**
  17. * 设置菜单。
  18. * Set menu.
  19. *
  20. * @access public
  21. * @return void
  22. */
  23. public function setMenu()
  24. {
  25. /* Adjust the menu order according to the user role. */
  26. $flowModule = $this->config->global->flow . '_my';
  27. $customMenu = isset($this->config->customMenu->{$flowModule}) ? $this->config->customMenu->{$flowModule} : array();
  28. if(empty($customMenu))
  29. {
  30. $role = $this->app->user->role;
  31. if($role == 'qa')
  32. {
  33. $taskOrder = '15';
  34. unset($this->lang->my->menuOrder[$taskOrder]);
  35. $bugOrder = '20';
  36. $this->lang->my->menuOrder[32] = 'task';
  37. $this->lang->my->dividerMenu = str_replace(',task,', ',' . $this->lang->my->menuOrder[$bugOrder] . ',', $this->lang->my->dividerMenu);
  38. }
  39. elseif($role == 'po')
  40. {
  41. $requirementOrder = 29;
  42. unset($this->lang->my->menuOrder[$requirementOrder]);
  43. $this->lang->my->menuOrder[15] = 'story';
  44. $this->lang->my->menuOrder[16] = 'requirement';
  45. $this->lang->my->menuOrder[30] = 'task';
  46. $this->lang->my->dividerMenu = str_replace(',task,', ',story,', $this->lang->my->dividerMenu);
  47. }
  48. elseif($role == 'pm')
  49. {
  50. $projectOrder = 35;
  51. unset($this->lang->my->menuOrder[$projectOrder]);
  52. $this->lang->my->menuOrder[17] = 'myProject';
  53. }
  54. }
  55. }
  56. /**
  57. * 获取产品相关统计。
  58. * Get product related data.
  59. *
  60. * @param array $productKeys
  61. * @access private
  62. * @return array
  63. */
  64. private function getProductRelatedData($productKeys)
  65. {
  66. $storyGroups = $this->dao->select('id,product,status,stage,estimate')
  67. ->from(TABLE_STORY)
  68. ->where('deleted')->eq(0)
  69. ->andWhere('product')->in($productKeys)
  70. ->groupBy('product')
  71. ->fetchGroup('product', 'id');
  72. $summaryStories = array();
  73. foreach($storyGroups as $productID => $stories)
  74. {
  75. $summaryStory = new stdclass();
  76. $summaryStory->total = count($stories);
  77. $finishedTotal = 0;
  78. $leftTotal = 0;
  79. $estimateCount = 0;
  80. foreach($stories as $story)
  81. {
  82. $estimateCount += $story->estimate;
  83. $story->status == 'closed' || $story->stage == 'released' || $story->stage == 'closed' ? $finishedTotal ++ : $leftTotal ++;
  84. }
  85. $summaryStory->finishedTotal = $finishedTotal;
  86. $summaryStory->leftTotal = $leftTotal;
  87. $summaryStory->estimateCount = $estimateCount;
  88. $summaryStory->finishedRate = $summaryStory->total == 0 ? 0 : ($finishedTotal / $summaryStory->total) * 100;
  89. $summaryStories[$productID] = $summaryStory;
  90. }
  91. $plans = $this->dao->select('product, COUNT(1) AS count')->from(TABLE_PRODUCTPLAN)->where('deleted')->eq(0)->andWhere('product')->in($productKeys)->andWhere('end')->gt(helper::now())->groupBy('product')->fetchPairs();
  92. $releases = $this->dao->select('product, COUNT(1) AS count')->from(TABLE_RELEASE)->where('deleted')->eq(0)->andWhere('product')->in($productKeys)->groupBy('product')->fetchPairs();
  93. $executions = $this->dao->select('t1.product,t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
  94. ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
  95. ->where('t1.product')->in($productKeys)
  96. ->andWhere('t2.type')->in('stage,sprint')
  97. ->andWhere('t2.deleted')->eq(0)
  98. ->orderBy('t1.project')
  99. ->fetchAll('product');
  100. $this->loadModel('execution');
  101. foreach($executions as $productID => $execution)
  102. {
  103. $execution = $this->execution->getById($execution->id);
  104. $executions[$productID]->progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0;
  105. }
  106. return array($summaryStories, $plans, $releases, $executions);
  107. }
  108. /**
  109. * 获取进行中的我的项目。
  110. * Get my projects.
  111. *
  112. * @access public
  113. * @return object
  114. */
  115. public function getDoingProjects()
  116. {
  117. $doingProjects = $this->loadModel('project')->getOverviewList('doing');
  118. $maxCount = 5;
  119. $myProjects = array();
  120. foreach($doingProjects as $key => $project)
  121. {
  122. if($project->PM == $this->app->user->account)
  123. {
  124. $myProjects[$key] = $project;
  125. unset($doingProjects[$key]);
  126. }
  127. if(count($myProjects) >= $maxCount) break;
  128. }
  129. if(count($myProjects) < $maxCount && !empty($doingProjects))
  130. {
  131. foreach($doingProjects as $key => $project)
  132. {
  133. $myProjects[$key] = $project;
  134. if(count($myProjects) >= $maxCount) break;
  135. }
  136. }
  137. foreach($myProjects as $key => $project)
  138. {
  139. $workhour = $this->project->getWorkhour($project->id);
  140. $project->progress = ($workhour->totalConsumed + $workhour->totalLeft) ? round($workhour->totalConsumed / ($workhour->totalConsumed + $workhour->totalLeft) * 100, 1) : 0;
  141. $project->delay = helper::diffDate(helper::today(), $project->end) > 0;
  142. $project->link = common::hasPriv('project', 'view') ? helper::createLink('project', 'view', "projectID={$project->id}") : '';
  143. }
  144. $data = new stdClass();
  145. $data->doingCount = count($myProjects);
  146. $data->projects = array_values($myProjects);
  147. return $data;
  148. }
  149. /**
  150. * 获取我的概述。
  151. * Get overview.
  152. *
  153. * @access public
  154. * @return object
  155. */
  156. public function getOverview()
  157. {
  158. $inAdminGroup = $this->dao->select('t1.*')->from(TABLE_USERGROUP)->alias('t1')
  159. ->leftJoin(TABLE_GROUP)->alias('t2')->on('t1.group=t2.id')
  160. ->where('t1.account')->eq($this->app->user->account)
  161. ->andWhere('t2.role')->eq('admin')
  162. ->fetch();
  163. $overview = new stdclass();
  164. if(!empty($inAdminGroup) || $this->app->user->admin)
  165. {
  166. $allConsumed = 0;
  167. $thisYearConsumed = 0;
  168. $projects = $this->loadModel('project')->getOverviewList('all', 0, 'id_desc', 0);
  169. $projectsConsumed = $this->project->getProjectsConsumed(array_keys($projects), 'THIS_YEAR');
  170. foreach($projects as $project)
  171. {
  172. $allConsumed += $project->consumed;
  173. $thisYearConsumed += $projectsConsumed[$project->id]->totalConsumed;
  174. }
  175. $overview->projectTotal = count($projects);
  176. $overview->allConsumed = round($allConsumed, 1);
  177. $overview->thisYearConsumed = round($thisYearConsumed, 1);
  178. }
  179. else
  180. {
  181. $overview->myTaskTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_TASK)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->fetch('count');
  182. $overview->myStoryTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_STORY)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->andWhere('type')->eq('story')->fetch('count');
  183. $overview->myBugTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_BUG)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->fetch('count');
  184. }
  185. return $overview;
  186. }
  187. /**
  188. * 获取我的贡献。
  189. * Get my contribute.
  190. *
  191. * @access public
  192. * @return object
  193. */
  194. public function getContribute()
  195. {
  196. $account = $this->app->user->account;
  197. $inTeam = $this->dao->select('root')->from(TABLE_TEAM)->where('type')->eq('project')->andWhere('account')->eq($account)->fetchPairs('root', 'root');
  198. $contribute = new stdclass();
  199. $contribute->myTaskTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_TASK)->where('assignedTo')->eq($account)->andWhere('deleted')->eq(0)->fetch('count');
  200. $contribute->myStoryTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_STORY)->where('assignedTo')->eq($account)->andWhere('deleted')->eq(0)->andWhere('type')->eq('story')->fetch('count');
  201. $contribute->myBugTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_BUG)->where('assignedTo')->eq($account)->andWhere('deleted')->eq(0)->fetch('count');
  202. $contribute->docCreatedTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_DOC)->where('addedBy')->eq($account)->andWhere('deleted')->eq('0')->fetch('count');
  203. $contribute->ownerProductTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_PRODUCT)->where('PO')->eq($account)->andWhere('deleted')->eq('0')->fetch('count');
  204. $contribute->involvedProjectTotal = (int)$this->dao->select('COUNT(DISTINCT id) AS count')->from(TABLE_PROJECT)
  205. ->where('deleted')->eq('0')
  206. ->andWhere('type')->eq('project')
  207. ->andWhere('id', true)->in($inTeam)
  208. ->orWhere('openedBy')->eq($account)
  209. ->orWhere('PO')->eq($account)
  210. ->orWhere('PM')->eq($account)
  211. ->orWhere('QD')->eq($account)
  212. ->orWhere('RD')->eq($account)
  213. ->markRight(1)
  214. ->fetch('count');
  215. return $contribute;
  216. }
  217. /**
  218. * 获取最新动态。
  219. * Get latest actions.
  220. *
  221. * @access public
  222. * @return array
  223. */
  224. public function getActions()
  225. {
  226. $maxCount = 5;
  227. $actions = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc,id_desc', 50);
  228. $actions = $this->action->processDynamicForAPI($actions);
  229. $actions = array_slice($actions, 0, $maxCount);
  230. return $actions;
  231. }
  232. /**
  233. * 获取由我指派的对象。
  234. * Get assigned by me objects.
  235. *
  236. * @param string $account
  237. * @param int $pager
  238. * @param string $orderBy
  239. * @param string $objectType
  240. * @access public
  241. * @return array
  242. */
  243. public function getAssignedByMe($account, $pager = null, $orderBy = 'id_desc', $objectType = '')
  244. {
  245. $module = $objectType == 'requirement' ? 'story' : $objectType;
  246. $objectIdList = $this->dao->select('objectID')->from(TABLE_ACTION)
  247. ->where('actor')->eq($account)
  248. ->andWhere('objectType')->eq($module)
  249. ->andWhere('action')->eq('assigned')
  250. ->fetchPairs('objectID');
  251. if(empty($objectIdList)) return array();
  252. if($objectType == 'task') return $this->getTaskAssignedByMe($pager, $orderBy, $objectIdList);
  253. if($objectType == 'requirement' || $objectType == 'story' || $objectType == 'bug') return $this->myTao->getProductRelatedAssignedByMe($objectIdList, $objectType, $module, $orderBy, $pager);
  254. if(in_array($objectType, array('risk', 'issue', 'nc')))
  255. {
  256. return $this->dao->select('t1.*')->from($this->config->objectTables[$module])->alias('t1')
  257. ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
  258. ->where('t1.deleted')->eq(0)
  259. ->andWhere('t2.deleted')->eq(0)
  260. ->andWhere('t1.id')->in($objectIdList)
  261. ->orderBy('t1.' . $orderBy)
  262. ->page($pager)
  263. ->fetchAll('id');
  264. }
  265. if($objectType == 'feedback' || $objectType == 'ticket')
  266. {
  267. return $this->dao->select('t1.*,t2.dept')->from($this->config->objectTables[$module])->alias('t1')
  268. ->leftJoin(TABLE_USER)->alias('t2')->on('t1.openedBy = t2.account')
  269. ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product = t3.id')
  270. ->where('t1.deleted')->eq(0)
  271. ->andWhere('t3.deleted')->eq(0)
  272. ->andWhere('t1.id')->in($objectIdList)
  273. ->orderBy('t1.' . $orderBy)
  274. ->page($pager)
  275. ->fetchAll('id');
  276. }
  277. if($objectType == 'reviewissue')
  278. {
  279. return $this->dao->select('t1.*, t2.type')->from($this->config->objectTables[$module])->alias('t1')
  280. ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id')
  281. ->where('t1.deleted')->eq(0)
  282. ->andWhere('t1.id')->in($objectIdList)
  283. ->orderBy('t1.' . $orderBy)
  284. ->page($pager)
  285. ->fetchAll('id', false);
  286. }
  287. return $this->dao->select('*')->from($this->config->objectTables[$module])
  288. ->where('deleted')->eq(0)
  289. ->andWhere('id')->in($objectIdList)
  290. ->orderBy($orderBy)
  291. ->page($pager)
  292. ->fetchAll('id');
  293. }
  294. /**
  295. * 获取由我指派的任务。
  296. * Get tasks assigned by me.
  297. *
  298. * @param object $pager
  299. * @param string $orderBy
  300. * @param array $objectIdList
  301. * @access private
  302. * @return array
  303. */
  304. private function getTaskAssignedByMe($pager = null, $orderBy = 'id_desc', $objectIdList = array())
  305. {
  306. // 处理优先级排序
  307. if(strpos($orderBy, 'pri_') !== false) $orderBy = str_replace('pri_', 'priOrder_', $orderBy);
  308. // 转换字段名称
  309. $tableFields = array(
  310. 'projectName' => 't3.name',
  311. 'executionName' => 't2.name',
  312. 'executionType' => 't2.type',
  313. 'executionMultiple' => 't2.multiple'
  314. );
  315. $orderBy = str_replace(array_keys($tableFields), array_values($tableFields), $orderBy);
  316. $objectList = $this->dao->select("t1.*, t3.id as project, t2.name as executionName, t2.multiple as executionMultiple, t3.name as projectName, t2.type as executionType, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder")->from(TABLE_TASK)->alias('t1')
  317. ->leftJoin(TABLE_EXECUTION)->alias('t2')->on("t1.execution = t2.id")
  318. ->leftJoin(TABLE_PROJECT)->alias('t3')->on("t2.project = t3.id")
  319. ->where('t1.deleted')->eq(0)
  320. ->andWhere('t2.deleted')->eq(0)
  321. ->andWhere('t1.id')->in($objectIdList)
  322. ->andWhere("FIND_IN_SET('{$this->config->vision}', t1.vision)")
  323. ->orderBy($orderBy)
  324. ->page($pager, 't1.id')
  325. ->fetchAll('id');
  326. if($objectList) return $this->loadModel('task')->processTasks($objectList);
  327. return $objectList;
  328. }
  329. /**
  330. * 构建用例搜索表单。
  331. * Build case search form.
  332. *
  333. * @param int $queryID
  334. * @param string $actionURL
  335. * @access public
  336. * @return void
  337. */
  338. public function buildTestCaseSearchForm($queryID, $actionURL)
  339. {
  340. $products = $this->dao->select('id,name')->from(TABLE_PRODUCT)
  341. ->where('deleted')->eq(0)
  342. ->andWhere('shadow')->eq(0)
  343. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi()
  344. ->orderBy('order_asc')
  345. ->fetchPairs();
  346. $scene = $this->loadModel('testcase')->getSceneMenu(0);
  347. $queryName = $this->app->rawMethod . 'Testcase';
  348. $this->app->loadModuleConfig('testcase');
  349. $this->config->testcase->search['module'] = $queryName;
  350. $this->config->testcase->search['queryID'] = $queryID;
  351. $this->config->testcase->search['actionURL'] = $actionURL;
  352. $this->config->testcase->search['params']['product']['values'] = $products;
  353. $this->config->testcase->search['params']['scene']['values'] = $scene;
  354. $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries();
  355. unset($this->config->testcase->search['fields']['story']);
  356. unset($this->config->testcase->search['fields']['module']);
  357. unset($this->config->testcase->search['fields']['branch']);
  358. $this->loadModel('search')->setSearchParams($this->config->testcase->search);
  359. }
  360. /**
  361. * 通过搜索获取用例。
  362. * Get testcases by search.
  363. *
  364. * @param int $queryID
  365. * @param string $type
  366. * @param string $orderBy
  367. * @param object $pager
  368. * @access public
  369. * @return array
  370. */
  371. public function getTestcasesBySearch($queryID, $type, $orderBy, $pager = null)
  372. {
  373. $queryName = $type == 'contribute' ? 'contributeTestcaseQuery' : 'workTestcaseQuery';
  374. $queryForm = $type == 'openedbyme' ? 'contributeTestcaseForm' : 'workTestcaseForm';
  375. if($queryID)
  376. {
  377. $query = $this->loadModel('search')->getQuery($queryID);
  378. if($query)
  379. {
  380. $this->session->set($queryName, $query->sql);
  381. $this->session->set($queryForm, $query->form);
  382. }
  383. else
  384. {
  385. $this->session->set($queryName, ' 1 = 1');
  386. }
  387. }
  388. else
  389. {
  390. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  391. }
  392. $myTestcaseQuery = $this->session->{$queryName};
  393. $myTestcaseQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $myTestcaseQuery);
  394. if($type == 'contribute')
  395. {
  396. $cases = $this->dao->select('*')->from(TABLE_CASE)->alias('t1')
  397. ->where($myTestcaseQuery)
  398. ->andWhere('t1.openedBy')->eq($this->app->user->account)
  399. ->andWhere('t1.deleted')->eq(0)
  400. ->orderBy($orderBy)
  401. ->page($pager)
  402. ->fetchAll('id');
  403. }
  404. else
  405. {
  406. $cases = $this->dao->select('t1.*,t3.name AS taskName')->from(TABLE_CASE)->alias('t1')
  407. ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.id = t2.case')
  408. ->leftJoin(TABLE_TESTTASK)->alias('t3')->on('t2.task = t3.id')
  409. ->where($myTestcaseQuery)
  410. ->andWhere('t2.assignedTo')->eq($this->app->user->account)
  411. ->andWhere('t1.deleted')->eq(0)
  412. ->andWhere('t3.deleted')->eq(0)
  413. ->andWhere('t3.status')->ne('done')
  414. ->orderBy($orderBy)
  415. ->page($pager)
  416. ->fetchAll('id');
  417. }
  418. return $cases;
  419. }
  420. /**
  421. * 构建任务搜索表单。
  422. * Build search form for task page of work.
  423. *
  424. * @param int $queryID
  425. * @param string $actionURL
  426. * @param string $module
  427. * @param bool $cacheSearchFunc 是否缓存构造搜索参数的方法。默认缓存可以提高性能,构造搜索表单时再加载真实值。
  428. * @access public
  429. * @return array
  430. */
  431. public function buildTaskSearchForm($queryID, $actionURL, $module, $cacheSearchFunc = true)
  432. {
  433. $this->loadModel('execution');
  434. $searchConfig = $this->config->execution->search;
  435. if($cacheSearchFunc)
  436. {
  437. $this->cacheSearchFunc($module, __METHOD__, func_get_args());
  438. return $searchConfig;
  439. }
  440. $searchConfig['module'] = $module;
  441. $searchConfig['actionURL'] = $actionURL;
  442. $searchConfig['queryID'] = $queryID;
  443. if($module == 'workTask')
  444. {
  445. unset($searchConfig['fields']['closedReason']);
  446. unset($searchConfig['fields']['closedBy']);
  447. unset($searchConfig['fields']['canceledBy']);
  448. unset($searchConfig['fields']['closedDate']);
  449. unset($searchConfig['fields']['canceledDate']);
  450. unset($searchConfig['params']['status']['values']['cancel']);
  451. unset($searchConfig['params']['status']['values']['closed']);
  452. }
  453. $projects = $this->loadModel('project')->getPairsByProgram();
  454. $searchConfig['params']['project']['values'] = $projects + array('all' => $this->lang->project->allProjects);
  455. $executions = $this->execution->getPairs(0, 'all', 'multiple');
  456. $searchConfig['params']['execution']['values'] = $executions + array('all' => $this->lang->execution->allExecutions);
  457. $searchConfig['params']['module']['values'] = $this->loadModel('tree')->getAllModulePairs();
  458. $this->loadModel('search')->setSearchParams($searchConfig);
  459. return $searchConfig;
  460. }
  461. /**
  462. * 通过搜索获取任务。
  463. * Get tasks by search.
  464. *
  465. * @param string $account
  466. * @param int $limit
  467. * @param object $pager
  468. * @param string $orderBy
  469. * @param int $queryID
  470. * @access public
  471. * @return array
  472. */
  473. public function getTasksBySearch($account, $limit = 0, $pager = null, $orderBy = 'id_desc', $queryID = 0)
  474. {
  475. $moduleName = $this->app->rawMethod == 'work' ? 'workTask' : 'contributeTask';
  476. $queryName = $moduleName . 'Query';
  477. $formName = $moduleName . 'Form';
  478. $taskIdList = array();
  479. if($moduleName == 'contributeTask')
  480. {
  481. $tasksAssignedByMe = $this->getAssignedByMe($account, null, 'id_desc', 'task');
  482. $taskIdList = array_keys($tasksAssignedByMe);
  483. }
  484. if($queryID)
  485. {
  486. $query = $this->loadModel('search')->getQuery($queryID);
  487. if($query)
  488. {
  489. $this->session->set($queryName, $query->sql);
  490. $this->session->set($formName, $query->form);
  491. }
  492. else
  493. {
  494. $this->session->set($queryName, ' 1 = 1');
  495. }
  496. }
  497. else
  498. {
  499. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  500. }
  501. $tasks = $this->myTao->fetchTasksBySearch($this->session->{$queryName}, $moduleName, $account, $taskIdList, $orderBy, $limit, $pager);
  502. $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'task', false);
  503. $taskTeam = $this->dao->select('*')->from(TABLE_TASKTEAM)->where('task')->in(array_keys($tasks))->fetchGroup('task');
  504. foreach($taskTeam as $taskID => $team) $tasks[$taskID]->team = $team;
  505. if($tasks) return $this->loadModel('task')->processTasks($tasks);
  506. return array();
  507. }
  508. /**
  509. * 构建 bug 搜索表单。
  510. * Build search form for bug page of work.
  511. *
  512. * @param int $queryID
  513. * @param string $actionURL
  514. * @access public
  515. * @return void
  516. */
  517. public function buildBugSearchForm($queryID, $actionURL)
  518. {
  519. $rawMethod = $this->app->rawMethod;
  520. $this->loadModel('bug');
  521. $products = $this->loadModel('product')->getPairs('', 0, '', 'all');
  522. $this->config->bug->search['module'] = $rawMethod . 'Bug';
  523. $this->config->bug->search['actionURL'] = $actionURL;
  524. $this->config->bug->search['queryID'] = $queryID;
  525. if($rawMethod == 'work')
  526. {
  527. unset($this->config->bug->search['fields']['closedDate']);
  528. unset($this->config->bug->search['fields']['closedBy']);
  529. }
  530. $this->config->bug->search['params']['project']['values'] = $this->loadModel('project')->getPairsByProgram() + array('all' => $this->lang->bug->allProject);
  531. $this->config->bug->search['params']['execution']['values'] = $this->loadModel('execution')->getPairs(0, 'all', 'multiple');
  532. $this->config->bug->search['params']['product']['values'] = $products;
  533. $this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs();
  534. $this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getAllModulePairs();
  535. $this->config->bug->search['params']['severity']['values'] = array(0 => '') + $this->lang->bug->severityList;
  536. $this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs(array_keys($products), 'all', 'releasetag');
  537. $this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
  538. unset($this->config->bug->search['fields']['branch']);
  539. $this->loadModel('search')->setSearchParams($this->config->bug->search);
  540. }
  541. /*
  542. * 构建风险搜索表单。
  543. * Build risk search form.
  544. *
  545. * @param int $queryID
  546. * @param string $actionURL
  547. * @access public
  548. * @return void
  549. */
  550. /**
  551. * @param int $queryID
  552. * @param string $actionURL
  553. */
  554. public function buildRiskSearchForm($queryID, $actionURL)
  555. {
  556. $projects = $this->dao->select('id, name')->from(TABLE_PROJECT)
  557. ->where('type')->eq('project')
  558. ->andWhere('deleted')->eq('0')
  559. ->andWhere('vision')->eq($this->config->vision)
  560. ->andWhere('model')->ne('kanban')
  561. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi()
  562. ->orderBy('order_asc')
  563. ->fetchPairs();
  564. $queryName = $this->app->rawMethod . 'Risk';
  565. $this->app->loadConfig('risk');
  566. $this->config->risk->search['module'] = $queryName;
  567. $this->config->risk->search['actionURL'] = $actionURL;
  568. $this->config->risk->search['queryID'] = $queryID;
  569. $this->config->risk->search['params']['project']['values'] = array('') + $projects;
  570. if(!isset($this->config->risk->search['fields'])) $this->config->risk->search['fields'] = array();
  571. unset($this->config->risk->search['fields']['module']);
  572. $this->loadModel('search')->setSearchParams($this->config->risk->search);
  573. }
  574. /*
  575. * 构建评审意见搜索表单。
  576. * Build reviewissue search form.
  577. *
  578. * @param int $queryID
  579. * @param string $actionURL
  580. * @access public
  581. * @return void
  582. */
  583. /**
  584. * @param int $queryID
  585. * @param string $actionURL
  586. */
  587. public function buildReviewissueSearchForm($queryID, $actionURL)
  588. {
  589. $projects = $this->dao->select('id, name')->from(TABLE_PROJECT)->where('type')->eq('project')
  590. ->andWhere('deleted')->eq('0')
  591. ->andWhere('vision')->eq($this->config->vision)
  592. ->andWhere('model')->ne('kanban')
  593. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi()
  594. ->orderBy('order_asc')
  595. ->fetchPairs();
  596. $queryName = $this->app->rawMethod . 'Reviewissue';
  597. $this->app->loadConfig('reviewissue');
  598. $this->config->reviewissue->search['module'] = $queryName;
  599. $this->config->reviewissue->search['actionURL'] = $actionURL;
  600. $this->config->reviewissue->search['queryID'] = $queryID;
  601. $this->config->reviewissue->search['params']['project']['values'] = array('') + $projects;
  602. $this->config->reviewissue->search['params']['type']['values'] = arrayUnion(array('' => ''), $this->lang->reviewissue->issueType);
  603. if(!isset($this->config->reviewissue->search['fields'])) $this->config->reviewissue->search['fields'] = array();
  604. unset($this->config->reviewissue->search['fields']['module']);
  605. $this->loadModel('search')->setSearchParams($this->config->reviewissue->search);
  606. }
  607. /**
  608. * 通过搜索获取风险。
  609. * Get risks by search.
  610. *
  611. * @param int $queryID
  612. * @param string $type
  613. * @param string $orderBy
  614. * @param object $pager
  615. * @access public
  616. * @return array
  617. */
  618. public function getRisksBySearch($queryID, $type, $orderBy, $pager = null)
  619. {
  620. $queryName = $type == 'contribute' ? 'contributeRiskQuery' : 'workRiskQuery';
  621. if($queryID && $queryID != 'myQueryID')
  622. {
  623. $query = $this->loadModel('search')->getQuery($queryID);
  624. if($query)
  625. {
  626. $this->session->set($queryName, $query->sql);
  627. $this->session->set($queryName . 'Form', $query->form);
  628. }
  629. else
  630. {
  631. $this->session->set($queryName, ' 1 = 1');
  632. }
  633. }
  634. else
  635. {
  636. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  637. }
  638. $riskQuery = $this->session->{$queryName};
  639. if($type == 'contribute')
  640. {
  641. $assignedByMe = $this->getAssignedByMe($this->app->user->account, null, 'id_desc', 'risk');
  642. $risks = $this->dao->select('*')->from(TABLE_RISK)
  643. ->where($riskQuery)
  644. ->andWhere('deleted')->eq('0')
  645. ->andWhere('createdBy',1)->eq($this->app->user->account)
  646. ->orWhere('id')->in(array_keys($assignedByMe))
  647. ->orWhere('closedBy')->eq($this->app->user->account)
  648. ->markRight(1)
  649. ->orderBy($orderBy)
  650. ->page($pager)
  651. ->fetchAll('id');
  652. }
  653. elseif($type == 'work')
  654. {
  655. $risks = $this->dao->select('*')->from(TABLE_RISK)
  656. ->where($riskQuery)
  657. ->andWhere('deleted')->eq('0')
  658. ->andWhere('assignedTo')->eq($this->app->user->account)
  659. ->orderBy($orderBy)
  660. ->page($pager)
  661. ->fetchAll('id');
  662. }
  663. return $risks;
  664. }
  665. /**
  666. * 通过搜索获取评审问题。
  667. * Get review issues by search.
  668. *
  669. * @param int $queryID
  670. * @param string $type
  671. * @param string $orderBy
  672. * @param object $pager
  673. * @access public
  674. * @return array
  675. */
  676. public function getReviewissuesBySearch($queryID, $type, $orderBy, $pager = null)
  677. {
  678. $queryName = $type == 'contribute' ? 'contributeReviewissueQuery' : 'workReviewissueQuery';
  679. if($queryID && $queryID != 'myQueryID')
  680. {
  681. $query = $this->loadModel('search')->getQuery($queryID);
  682. if($query)
  683. {
  684. $this->session->set($queryName, $query->sql);
  685. $this->session->set($queryName . 'Form', $query->form);
  686. }
  687. else
  688. {
  689. $this->session->set($queryName, ' 1 = 1');
  690. }
  691. }
  692. else
  693. {
  694. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  695. }
  696. $reviewissueQuery = preg_replace('/`([^`]+)`/', 't1.$0', $this->session->{$queryName});
  697. if(strpos($reviewissueQuery, 't1.`review`') !== false) $reviewissueQuery = str_replace('t1.`review`', 't2.`object`', $reviewissueQuery);
  698. if(strpos($reviewissueQuery, 't1.`type`') !== false) $reviewissueQuery = str_replace('t1.`type`', 't2.`type`', $reviewissueQuery);
  699. if($type == 'contribute')
  700. {
  701. $assignedByMe = $this->getAssignedByMe($this->app->user->account, null, 'id_desc', 'reviewissue');
  702. $reviewissues = $this->dao->select('t1.*, t2.type')->from(TABLE_REVIEWISSUE)->alias('t1')
  703. ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id')
  704. ->where($reviewissueQuery)
  705. ->andWhere('t1.deleted')->eq('0')
  706. ->andWhere('t1.createdBy',1)->eq($this->app->user->account)
  707. ->orWhere('t1.id')->in(array_keys($assignedByMe))
  708. ->markRight(1)
  709. ->orderBy("t1.$orderBy")
  710. ->page($pager)
  711. ->fetchAll('id', false);
  712. }
  713. elseif($type == 'work')
  714. {
  715. $reviewissues = $this->dao->select('t1.*, t2.type')->from(TABLE_REVIEWISSUE)->alias('t1')
  716. ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id')
  717. ->where($reviewissueQuery)
  718. ->andWhere('t1.deleted')->eq('0')
  719. ->andWhere('t1.assignedTo')->eq($this->app->user->account)
  720. ->orderBy("t1.$orderBy")
  721. ->page($pager)
  722. ->fetchAll('id', false);
  723. }
  724. return $reviewissues;
  725. }
  726. /**
  727. * 构建需求搜索表单。
  728. * Build Story search form.
  729. *
  730. * @param int $queryID
  731. * @param string $actionURL
  732. * @param string $currentMethod
  733. * @access public
  734. * @return void
  735. */
  736. public function buildStorySearchForm($queryID, $actionURL, $currentMethod)
  737. {
  738. $products = $this->dao->select('id,name')->from(TABLE_PRODUCT)
  739. ->where('deleted')->eq(0)
  740. ->andWhere('shadow')->eq(0)
  741. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi()
  742. ->orderBy('order_asc')
  743. ->fetchPairs();
  744. $productIdList = array_keys($products);
  745. $branchParam = '';
  746. $queryName = $currentMethod . 'Story';
  747. $this->app->loadConfig('product');
  748. $this->config->product->search['module'] = $queryName;
  749. $this->config->product->search['queryID'] = $queryID;
  750. $this->config->product->search['actionURL'] = $actionURL;
  751. $this->config->product->search['params']['product']['values'] = $products;
  752. $this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productIdList, $branchParam);
  753. $this->config->product->search['fields']['title'] = $this->lang->story->title;
  754. $this->config->product->search['params']['grade']['values'] = $this->loadModel('story')->getGradePairs('story', 'enable');
  755. unset($this->config->product->search['fields']['module'], $this->config->product->search['fields']['branch'], $this->config->product->search['fields']['roadmap']);
  756. $this->loadModel('search')->setSearchParams($this->config->product->search);
  757. }
  758. /**
  759. * 通过搜索获取需求。
  760. * Get stories by search.
  761. *
  762. * @param int $queryID
  763. * @param string $type
  764. * @param string $orderBy
  765. * @param object $pager
  766. * @access public
  767. * @return array
  768. */
  769. public function getStoriesBySearch($queryID, $type, $orderBy, $pager = null)
  770. {
  771. $queryName = $type == 'contribute' ? 'contributeStoryQuery' : 'workStoryQuery';
  772. $queryForm = $type == 'contribute' ? 'contributeStoryForm' : 'workStoryForm';
  773. if($queryID)
  774. {
  775. $query = $this->loadModel('search')->getQuery($queryID);
  776. if($query)
  777. {
  778. $this->session->set($queryName, $query->sql);
  779. $this->session->set($queryForm, $query->form);
  780. }
  781. else
  782. {
  783. $this->session->set($queryName, ' 1 = 1');
  784. }
  785. }
  786. else
  787. {
  788. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  789. }
  790. $myStoryQuery = $this->session->{$queryName};
  791. $myStoryQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $myStoryQuery);
  792. if(strpos($myStoryQuery, 'result') !== false) $myStoryQuery = str_replace('t1.`result`', 't5.`result`', $myStoryQuery);
  793. return $this->myTao->fetchStoriesBySearch($myStoryQuery, $type, $orderBy, $pager, $type == 'contribute' ? $this->getAssignedByMe($this->app->user->account, null, $orderBy, 'story') : array());
  794. }
  795. /**
  796. * 构建业务需求搜索表单。
  797. * Build epic search form.
  798. *
  799. * @param int $queryID
  800. * @param string $actionURL
  801. * @access public
  802. * @return void
  803. */
  804. public function buildEpicSearchForm($queryID, $actionURL)
  805. {
  806. $products = $this->dao->select('id,name')->from(TABLE_PRODUCT)
  807. ->where('deleted')->eq(0)
  808. ->andWhere('shadow')->eq(0)
  809. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi()
  810. ->orderBy('order_asc')
  811. ->fetchPairs();
  812. $productIdList = array_keys($products);
  813. $queryName = $this->app->rawMethod . 'Epic';
  814. $this->app->loadConfig('product');
  815. $this->config->product->search['module'] = $queryName;
  816. $this->config->product->search['queryID'] = $queryID;
  817. $this->config->product->search['actionURL'] = $actionURL;
  818. $this->config->product->search['params']['product']['values'] = $products;
  819. $this->config->product->search['params']['grade']['values'] = $this->loadModel('story')->getGradePairs('epic', 'enable');
  820. $this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productIdList);
  821. if(isset($this->config->product->search['fields']['roadmap'])) $this->config->product->search['params']['roadmap']['values'] = $this->loadModel('roadmap')->getPairs();
  822. $this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->ERCommon, $this->lang->story->title);
  823. $this->lang->story->create = str_replace($this->lang->SRCommon, $this->lang->ERCommon, $this->lang->story->create);
  824. $this->config->product->search['fields']['title'] = $this->lang->story->title;
  825. unset($this->config->product->search['fields']['plan']);
  826. unset($this->config->product->search['fields']['stage']);
  827. unset($this->config->product->search['fields']['module']);
  828. unset($this->config->product->search['fields']['branch']);
  829. $this->loadModel('search')->setSearchParams($this->config->product->search);
  830. }
  831. /**
  832. * 构建用户需求搜索表单。
  833. * Build Requirement search form.
  834. *
  835. * @param int $queryID
  836. * @param string $actionURL
  837. * @access public
  838. * @return void
  839. */
  840. public function buildRequirementSearchForm($queryID, $actionURL)
  841. {
  842. $products = $this->dao->select('id,name')->from(TABLE_PRODUCT)
  843. ->where('deleted')->eq(0)
  844. ->andWhere('shadow')->eq(0)
  845. ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi()
  846. ->orderBy('order_asc')
  847. ->fetchPairs();
  848. $productIdList = array_keys($products);
  849. $queryName = $this->app->rawMethod . 'Requirement';
  850. $this->app->loadConfig('product');
  851. $this->config->product->search['module'] = $queryName;
  852. $this->config->product->search['queryID'] = $queryID;
  853. $this->config->product->search['actionURL'] = $actionURL;
  854. $this->config->product->search['params']['product']['values'] = $products;
  855. $this->config->product->search['params']['grade']['values'] = $this->loadModel('story')->getGradePairs('requirement', 'enable');
  856. $this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productIdList);
  857. if(isset($this->config->product->search['fields']['roadmap'])) $this->config->product->search['params']['roadmap']['values'] = $this->loadModel('roadmap')->getPairs();
  858. $this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
  859. $this->lang->story->create = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->create);
  860. $this->config->product->search['fields']['title'] = $this->lang->story->title;
  861. unset($this->config->product->search['fields']['plan']);
  862. unset($this->config->product->search['fields']['stage']);
  863. unset($this->config->product->search['fields']['module']);
  864. unset($this->config->product->search['fields']['branch']);
  865. $this->loadModel('search')->setSearchParams($this->config->product->search);
  866. }
  867. /**
  868. * 构建工单搜索表单。
  869. * Build ticket search form.
  870. *
  871. * @param int $queryID
  872. * @param string $actionURL
  873. * @access public
  874. * @return bool
  875. */
  876. public function buildTicketSearchForm($queryID, $actionURL)
  877. {
  878. if($this->config->edition == 'open') return false;
  879. $this->loadModel('ticket');
  880. $grantProducts = $this->loadModel('feedback')->getGrantProducts();
  881. $queryName = $this->app->rawMethod . 'Ticket';
  882. $this->config->ticket->search['module'] = $queryName;
  883. $this->config->ticket->search['queryID'] = $queryID;
  884. $this->config->ticket->search['actionURL'] = $actionURL;
  885. $this->config->ticket->search['params']['product']['values'] = $grantProducts;
  886. $this->config->ticket->search['params']['module']['values'] = $this->loadModel('feedback')->getModuleList('ticket', true, 'no');
  887. $this->config->ticket->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs(array_keys($grantProducts), 'all', 'releasetag');
  888. $this->config->ticket->search['params']['feedback']['values'] = $this->feedback->getPairs();
  889. $this->loadModel('search')->setSearchParams($this->config->ticket->search);
  890. return true;
  891. }
  892. /**
  893. * 通过搜索获取业务需求。
  894. * Get epics by search.
  895. *
  896. * @param int $queryID
  897. * @param string $type
  898. * @param string $orderBy
  899. * @param object $pager
  900. * @access public
  901. * @return array
  902. */
  903. public function getEpicsBySearch($queryID, $type, $orderBy, $pager = null)
  904. {
  905. $queryName = $type == 'contribute' ? 'contributeEpicQuery' : 'workEpicQuery';
  906. $queryForm = $type == 'contribute' ? 'contributeEpicForm' : 'workEpicForm';
  907. if($queryID)
  908. {
  909. $query = $this->loadModel('search')->getQuery($queryID);
  910. if($query)
  911. {
  912. $this->session->set($queryName, $query->sql);
  913. $this->session->set($queryForm, $query->form);
  914. }
  915. else
  916. {
  917. $this->session->set($queryName, ' 1 = 1');
  918. }
  919. }
  920. else
  921. {
  922. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  923. }
  924. $myEpicQuery = $this->session->{$queryName};
  925. $myEpicQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $myEpicQuery);
  926. if(strpos($myEpicQuery, 'result') !== false) $myEpicQuery = str_replace('t1.`result`', 't3.`result`', $myEpicQuery);
  927. $epicsAssignedByMe = $type == 'contribute' ? $this->getAssignedByMe($this->app->user->account, null, 'id_desc', 'epic') : array();
  928. $epicIdList = array_keys($epicsAssignedByMe);
  929. return $this->myTao->fetchEpicsBySearch($myEpicQuery, $type, $orderBy, $pager, $epicIdList, 'epic');
  930. }
  931. /**
  932. * 通过搜索获取用户需求。
  933. * Get requirements by search.
  934. *
  935. * @param int $queryID
  936. * @param string $type
  937. * @param string $orderBy
  938. * @param int $pager
  939. * @access public
  940. * @return array
  941. */
  942. public function getRequirementsBySearch($queryID, $type, $orderBy, $pager = null)
  943. {
  944. $queryName = $type == 'contribute' ? 'contributeRequirementQuery' : 'workRequirementQuery';
  945. $queryForm = $type == 'contribute' ? 'contributeRequirementForm' : 'workRequirementForm';
  946. if($queryID)
  947. {
  948. $query = $this->loadModel('search')->getQuery($queryID);
  949. if($query)
  950. {
  951. $this->session->set($queryName, $query->sql);
  952. $this->session->set($queryForm, $query->form);
  953. }
  954. else
  955. {
  956. $this->session->set($queryName, ' 1 = 1');
  957. }
  958. }
  959. else
  960. {
  961. if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1');
  962. }
  963. $myRequirementQuery = $this->session->{$queryName};
  964. $myRequirementQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $myRequirementQuery);
  965. if(strpos($myRequirementQuery, 'result') !== false) $myRequirementQuery = str_replace('t1.`result`', 't3.`result`', $myRequirementQuery);
  966. $requirementsAssignedByMe = $type == 'contribute' ? $this->getAssignedByMe($this->app->user->account, null, 'id_desc', 'requirement') : array();
  967. $requirementIdList = array_keys($requirementsAssignedByMe);
  968. return $this->myTao->fetchRequirementsBySearch($myRequirementQuery, $type, $orderBy, $pager, $requirementIdList, 'requirement');
  969. }
  970. /**
  971. * 为菜单获取待评审的类型。
  972. * Get reviewing type list for menu.
  973. *
  974. * @access public
  975. * @return object
  976. */
  977. public function getReviewingTypeList()
  978. {
  979. $typeList = array();
  980. if($this->config->edition == 'ipd' && $this->getReviewingDemands('id_desc', true)) $typeList[] = 'demand';
  981. if($this->getReviewingStories('id_desc', true, 'story')) $typeList[] = 'story';
  982. if($this->getReviewingStories('id_desc', true, 'epic')) $typeList[] = 'epic';
  983. if($this->getReviewingStories('id_desc', true, 'requirement')) $typeList[] = 'requirement';
  984. if($this->config->vision != 'or' && $this->getReviewingCases('id_desc', true)) $typeList[] = 'testcase';
  985. if($this->config->vision != 'or' && $this->getReviewingApprovals('id_desc', true)) $typeList[] = 'project';
  986. if($this->getReviewingFeedbacks('id_desc', true)) $typeList[] = 'feedback';
  987. if($this->config->vision != 'or' && $this->getReviewingOA('status', true)) $typeList[] = 'oa';
  988. if($this->config->vision != 'or' && $this->getReviewingMRs('id_desc')) $typeList[] = 'mr';
  989. $typeList = array_merge($typeList, $this->getReviewingFlows('all', 'id_desc', true));
  990. $flows = $this->config->edition == 'open' ? array() : $this->dao->select('module,name')->from(TABLE_WORKFLOW)->where('module')->in($typeList)->andWhere('buildin')->eq(0)->fetchPairs('module', 'name');
  991. $menu = new stdclass();
  992. $menu->all = $this->lang->my->featureBar['audit']['all'];
  993. foreach($typeList as $type)
  994. {
  995. $this->app->loadLang($type);
  996. $menu->{$type} = isset($this->lang->my->featureBar['audit'][$type]) ? $this->lang->my->featureBar['audit'][$type] : zget($flows, $type, isset($this->lang->{$type}->common) ? $this->lang->{$type}->common : $type);
  997. }
  998. return $menu;
  999. }
  1000. /**
  1001. * 获取我的审批列表。
  1002. * Get reviewing list for me.
  1003. *
  1004. * @param string $browseType
  1005. * @param string $orderBy
  1006. * @param object $pager
  1007. * @access public
  1008. * @return array
  1009. */
  1010. public function getReviewingList($browseType, $orderBy = 'time_desc', $pager = null)
  1011. {
  1012. $vision = $this->config->vision;
  1013. $reviewList = array();
  1014. if($browseType == 'all' || $browseType == 'demand') $reviewList = array_merge($reviewList, $this->getReviewingDemands());
  1015. if($browseType == 'all' || $browseType == 'story') $reviewList = array_merge($reviewList, $this->getReviewingStories());
  1016. if($browseType == 'all' || $browseType == 'epic') $reviewList = array_merge($reviewList, $this->getReviewingStories('id_desc', false, 'epic'));
  1017. if($browseType == 'all' || $browseType == 'requirement') $reviewList = array_merge($reviewList, $this->getReviewingStories('id_desc', false, 'requirement'));
  1018. if($vision != 'or' && ($browseType == 'all' || $browseType == 'testcase') && common::hasPriv('testcase', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingCases());
  1019. if($vision != 'or' && ($browseType == 'all' || $browseType == 'project')) $reviewList = array_merge($reviewList, $this->getReviewingApprovals());
  1020. if($vision != 'or' && ($browseType == 'all' || $browseType == 'oa')) $reviewList = array_merge($reviewList, $this->getReviewingOA());
  1021. if($vision != 'or' && ($browseType == 'all' || $browseType == 'mr')) $reviewList = array_merge($reviewList, $this->getReviewingMRs());
  1022. if($browseType == 'all' || !in_array($browseType, $this->config->my->noFlowAuditModules)) $reviewList = array_merge($reviewList, $this->getReviewingFlows($browseType));
  1023. if(($browseType == 'all' || $browseType == 'feedback') && common::hasPriv('feedback', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingFeedbacks());
  1024. if(empty($reviewList)) return array();
  1025. $field = $orderBy;
  1026. $direction = 'asc';
  1027. if(strpos($orderBy, '_') !== false) list($field, $direction) = explode('_', $orderBy);
  1028. /* Sort review. */
  1029. $reviewGroup = array();
  1030. foreach($reviewList as $review)
  1031. {
  1032. if(!isset($review->{$field})) $field = 'time';
  1033. $reviewGroup[$review->{$field}][] = $review;
  1034. }
  1035. if($direction == 'asc') ksort($reviewGroup);
  1036. if($direction == 'desc') krsort($reviewGroup);
  1037. $reviewList = array();
  1038. foreach($reviewGroup as $reviews) $reviewList = array_merge($reviewList, $reviews);
  1039. /* Pager. */
  1040. if(!is_null($pager))
  1041. {
  1042. $pager->setRecTotal(count($reviewList));
  1043. $pager->setPageTotal();
  1044. $pager->setPageID($pager->pageID);
  1045. $reviewList = array_chunk($reviewList, (int)$pager->recPerPage);
  1046. $reviewList = $reviewList[$pager->pageID - 1];
  1047. }
  1048. return $reviewList;
  1049. }
  1050. /**
  1051. * 获取待评审的需求池需求。
  1052. * Get reviewing demands.
  1053. *
  1054. * @param string $orderBy
  1055. * @param bool $checkExists
  1056. * @access public
  1057. * @return array|bool
  1058. */
  1059. public function getReviewingDemands($orderBy = 'id_desc', $checkExists = false)
  1060. {
  1061. if($this->config->edition != 'ipd') return array();
  1062. $this->loadModel('demand');
  1063. $demands = $this->dao->select('t1.*')->from(TABLE_DEMAND)->alias('t1')
  1064. ->leftJoin(TABLE_DEMANDREVIEW)->alias('t2')->on('t1.id = t2.demand and t1.version = t2.version')
  1065. ->where('t1.deleted')->eq(0)
  1066. ->andWhere('t2.reviewer')->eq($this->app->user->account)
  1067. ->andWhere('t2.result')->eq('')
  1068. ->andWhere('t1.vision')->eq($this->config->vision)
  1069. ->andWhere('t1.status')->eq('reviewing')
  1070. ->orderBy($orderBy)
  1071. ->beginIF($checkExists)->limit(1)->fi()
  1072. ->fetchAll('id');
  1073. if(!empty($demands)) $demands = $this->demand->mergeReviewer($demands);
  1074. $reviewAction = $this->loadModel('workflowaction')->getByModuleAndAction('demand', 'review');
  1075. $reviewList = array();
  1076. foreach($demands as $id => $demand)
  1077. {
  1078. if(!$this->demand->isClickable($demand, 'review')) continue;
  1079. if($reviewAction && $reviewAction->extensionType != 'none' && $reviewAction->status == 'enable' && !empty($reviewAction->conditions) && !$this->flow->checkConditions($reviewAction->conditions, $demand)) continue;
  1080. $data = new stdclass();
  1081. $data->id = $demand->id;
  1082. $data->title = $demand->title;
  1083. $data->type = 'demand';
  1084. $data->time = $demand->createdDate;
  1085. $data->status = $demand->status;
  1086. $data->product = $demand->product;
  1087. $data->project = 0;
  1088. $reviewList[$id] = $data;
  1089. }
  1090. if($checkExists) return !empty($reviewList);
  1091. $actions = $this->dao->select('objectID, `date`')->from(TABLE_ACTION)->where('objectType')->eq('demand')->andWhere('objectID')->in(array_keys($reviewList))->andWhere('action')->eq('submitreview')->orderBy('`date`')->fetchPairs('objectID', 'date');
  1092. foreach($actions as $demandID => $date) $reviewList[$demandID]->time = $date;
  1093. return array_values($reviewList);
  1094. }
  1095. /**
  1096. * 获取待审批的需求。
  1097. * Get reviewing stories.
  1098. *
  1099. * @param string $orderBy
  1100. * @param bool $checkExists
  1101. * @access public
  1102. * @return array|bool
  1103. */
  1104. public function getReviewingStories($orderBy = 'id_desc', $checkExists = false, $type = 'story')
  1105. {
  1106. $this->loadModel($type);
  1107. $stories = $this->dao->select('t1.*')->from(TABLE_STORY)->alias('t1')
  1108. ->leftJoin(TABLE_STORYREVIEW)->alias('t2')->on('t1.id = t2.story and t1.version = t2.version')
  1109. ->where('t1.deleted')->eq(0)
  1110. ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi()
  1111. ->andWhere('t2.reviewer')->eq($this->app->user->account)
  1112. ->andWhere('t2.result')->eq('')
  1113. ->andWhere('t1.type')->eq($type)
  1114. ->andWhere('t1.vision')->eq($this->config->vision)
  1115. ->andWhere('t1.status')->eq('reviewing')
  1116. ->orderBy($orderBy)
  1117. ->beginIF($checkExists)->limit(1)->fi()
  1118. ->fetchAll('id');
  1119. if($this->config->edition != 'open')
  1120. {
  1121. $reviewAction = $this->loadModel('workflowaction')->getByModuleAndAction($type, 'review');
  1122. if($reviewAction)
  1123. {
  1124. $this->loadModel('flow');
  1125. foreach($stories as $storyID => $story)
  1126. {
  1127. if($reviewAction->extensionType != 'none' && $reviewAction->status == 'enable' && !empty($reviewAction->conditions) && !$this->flow->checkConditions($reviewAction->conditions, $story)) unset($stories[$storyID]);
  1128. }
  1129. }
  1130. }
  1131. if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
  1132. $reviewList = array();
  1133. foreach($stories as $storyID => $story)
  1134. {
  1135. if(!$this->story->isClickable($story, 'review')) continue;
  1136. $data = new stdclass();
  1137. $data->id = $story->id;
  1138. $data->title = $story->title;
  1139. $data->type = 'story';
  1140. $data->storyType = $story->type;
  1141. $data->time = $story->openedDate;
  1142. $data->status = $story->status;
  1143. $data->product = $story->product;
  1144. $data->project = 0;
  1145. $data->parent = $story->parent;
  1146. $reviewList[$storyID] = $data;
  1147. }
  1148. if($checkExists) return !empty($reviewList);
  1149. $actions = $this->dao->select('objectID,`date`')->from(TABLE_ACTION)->where('objectType')->eq('story')->andWhere('objectID')->in(array_keys($reviewList))->andWhere('action')->eq('submitreview')->orderBy('`date`')->fetchPairs();
  1150. foreach($actions as $storyID => $date) $reviewList[$storyID]->time = $date;
  1151. return array_values($reviewList);
  1152. }
  1153. /**
  1154. * 获取待评审的用例。
  1155. * Get reviewing cases.
  1156. *
  1157. * @param string $orderBy
  1158. * @param bool $checkExists
  1159. * @access public
  1160. * @return array|bool
  1161. */
  1162. public function getReviewingCases($orderBy = 'id_desc', $checkExists = false)
  1163. {
  1164. $cases = $this->dao->select('*')->from(TABLE_CASE)
  1165. ->where('deleted')->eq('0')
  1166. ->andWhere('status')->eq('wait')
  1167. ->beginIF(!$this->app->user->admin)->andWhere('(product')->in($this->app->user->view->products)->orWhere('lib')->gt(0)->markRight(1)->fi()
  1168. ->orderBy($orderBy)
  1169. ->beginIF($checkExists)->limit(1)->fi()
  1170. ->fetchAll('id');
  1171. if($this->config->edition != 'open')
  1172. {
  1173. $reviewAction = $this->loadModel('workflowaction')->getByModuleAndAction('testcase', 'review');
  1174. if($reviewAction)
  1175. {
  1176. $this->loadModel('flow');
  1177. foreach($cases as $caseID => $case)
  1178. {
  1179. if($reviewAction->extensionType != 'none' && $reviewAction->status == 'enable' && !empty($reviewAction->conditions) && !$this->flow->checkConditions($reviewAction->conditions, $case)) unset($cases[$caseID]);
  1180. }
  1181. }
  1182. }
  1183. $this->loadModel('testcase');
  1184. $reviewList = array();
  1185. foreach($cases as $case)
  1186. {
  1187. if(!$this->testcase->isClickable($case, 'review')) continue;
  1188. $data = new stdclass();
  1189. $data->id = $case->id;
  1190. $data->title = $case->title;
  1191. $data->type = 'testcase';
  1192. $data->time = $case->openedDate;
  1193. $data->status = $case->status;
  1194. $data->product = isset($case->product) ? $case->product : 0;
  1195. $data->project = isset($case->project) ? $case->project : 0;
  1196. $reviewList[] = $data;
  1197. }
  1198. if($checkExists) return !empty($reviewList);
  1199. return $reviewList;
  1200. }
  1201. /**
  1202. * 获取待评审的MR。
  1203. * Get reviewing mrs.
  1204. *
  1205. * @param string $orderBy
  1206. * @access public
  1207. * @return array
  1208. */
  1209. public function getReviewingMRs($orderBy = 'id_desc')
  1210. {
  1211. return $this->dao->select("`id`, `title`, IF(`isFlow`='1', 'pullreq', 'mr') AS type, `createdDate` AS time, `approvalStatus` AS status, 0 AS product, 0 AS project")->from(TABLE_MR)
  1212. ->where('deleted')->eq('0')
  1213. ->andWhere('approvalStatus')->notIn(array('approved', 'rejected'))
  1214. ->andWhere('status')->ne('closed')
  1215. ->andWhere('assignee')->eq($this->app->user->account)
  1216. ->orderBy($orderBy)
  1217. ->fetchAll('id');
  1218. }
  1219. /**
  1220. * 获取待评审的审批。
  1221. * Get reviewing approvals.
  1222. *
  1223. * @param string $orderBy
  1224. * @param bool $checkExists
  1225. * @access public
  1226. * @return array|bool
  1227. */
  1228. public function getReviewingApprovals($orderBy = 'id_desc', $checkExists = false)
  1229. {
  1230. if($this->config->edition != 'max' and $this->config->edition != 'ipd') return array();
  1231. $projectchangePendingList = $this->loadModel('approval')->getPendingReviews('projectchange');
  1232. $projectchangeReviewingList = $this->dao->select('id')->from(TABLE_REVIEW)->where('type')->eq('projectchange')->andWhere('object')->in($projectchangePendingList)->fetchPairs();
  1233. $pendingList = $this->loadModel('approval')->getPendingReviews('review');
  1234. $pendingList = arrayUnion($pendingList, $projectchangeReviewingList);
  1235. $projectReviews = $this->loadModel('review')->getByList(0, $pendingList, $orderBy);
  1236. if($checkExists) return !empty($projectReviews);
  1237. $this->app->loadLang('project');
  1238. $this->session->set('reviewList', $this->app->getURI(true));
  1239. $reviewList = array();
  1240. foreach($projectReviews as $review)
  1241. {
  1242. if(!isset($pendingList[$review->id])) continue;
  1243. if(!$this->review->isClickable($review, 'review')) continue;
  1244. $data = new stdclass();
  1245. $data->id = $review->id;
  1246. $data->title = $review->title;
  1247. $data->type = $review->type == 'projectchange' ? 'projectchange' : 'projectreview';
  1248. $data->time = date('Y-m-d H:i:s', strtotime($review->createdDate));
  1249. $data->status = $review->status;
  1250. $data->product = $review->product;
  1251. $data->project = $review->project;
  1252. $reviewList[] = $data;
  1253. }
  1254. return $reviewList;
  1255. }
  1256. /**
  1257. * 获取审批中的工作流设置。
  1258. * Get reviewing for flows setting.
  1259. *
  1260. * @param string $objectType
  1261. * @param string $orderBy
  1262. * @param bool $checkExists
  1263. * @access public
  1264. * @return array|bool
  1265. */
  1266. public function getReviewingFlows($objectType = 'all', $orderBy = 'id_desc', $checkExists = false)
  1267. {
  1268. if($this->config->edition == 'open') return array();
  1269. $dataList = $this->dao->select('t2.objectType,t2.objectID')->from(TABLE_APPROVALNODE)->alias('t1')
  1270. ->leftJoin(TABLE_APPROVALOBJECT)->alias('t2')->on('t2.approval = t1.approval')
  1271. ->where('t2.objectType')->ne('review')
  1272. ->andWhere('objectType')->ne('projectchange')
  1273. ->beginIF($objectType != 'all')->andWhere('t2.objectType')->eq($objectType)->fi()
  1274. ->andWhere('t1.account')->eq($this->app->user->account)
  1275. ->andWhere('t1.status')->eq('doing')
  1276. ->andWhere('t1.type')->eq('review')
  1277. ->beginIF(!helper::hasFeature('project_cm'))->andWhere('objectType')->ne('baseline')->fi()
  1278. ->beginIF(!helper::hasFeature('project_risk'))->andWhere('objectType')->ne('risk')->fi()
  1279. ->beginIF(!helper::hasFeature('project_issue'))->andWhere('objectType')->ne('issue')->fi()
  1280. ->beginIF(!helper::hasFeature('project_opportunity'))->andWhere('objectType')->ne('opportunity')->fi()
  1281. ->beginIF($this->config->systemMode == 'light' || (!helper::hasFeature('program') && $this->config->edition != 'ipd'))->andWhere('objectType')->ne('charter')->fi()
  1282. ->orderBy("t2.{$orderBy}")
  1283. ->beginIF($checkExists)->limit(1)->fi()
  1284. ->fetchAll();
  1285. $objectIdList = array();
  1286. foreach($dataList as $data) $objectIdList[$data->objectType][$data->objectID] = $data->objectID;
  1287. $this->loadModel('flow');
  1288. $this->loadModel('workflowaction');
  1289. $flows = $this->dao->select('module,`table`,name,titleField,app')->from(TABLE_WORKFLOW)->where('module')->in(array_keys($objectIdList))->andWhere('vision')->eq($this->config->vision)->fetchAll('module');
  1290. $objectGroup = array();
  1291. foreach($objectIdList as $objectType => $idList)
  1292. {
  1293. $table = zget($this->config->objectTables, $objectType, '');
  1294. if(!in_array($objectType, $this->config->my->notFlowModules))
  1295. {
  1296. if(!isset($flows[$objectType])) continue;
  1297. if(!$table) $table = $flows[$objectType]->table;
  1298. }
  1299. if(empty($table)) continue;
  1300. $objectGroup[$objectType] = $this->dao->select('*')->from($table)->where('id')->in($idList)->andWhere('deleted')->eq('0')->fetchAll('id');
  1301. $action = $this->workflowaction->getByModuleAndAction($objectType, 'approvalreview');
  1302. if($action)
  1303. {
  1304. foreach($objectGroup[$objectType] as $objectID => $object)
  1305. {
  1306. if(!$this->flow->checkConditions($action->conditions, $object)) unset($objectIdList[$objectType][$objectID], $objectGroup[$objectType][$objectID]);
  1307. }
  1308. }
  1309. }
  1310. if($checkExists) return array_keys(array_filter($objectGroup));
  1311. $this->app->loadConfig('action');
  1312. return $this->myTao->buildReviewingFlows($objectGroup, $flows, $this->config->action->objectNameFields);
  1313. }
  1314. /**
  1315. * 获取评审中的反馈。
  1316. * Get reviewing feedbacks.
  1317. *
  1318. * @param string $orderBy
  1319. * @param bool $checkExists
  1320. * @access public
  1321. * @return array|bool
  1322. */
  1323. public function getReviewingFeedbacks($orderBy = 'id_desc', $checkExists = false)
  1324. {
  1325. if($this->config->edition == 'open') return array();
  1326. $this->session->set('feedbackProduct', 'all');
  1327. $feedbacks = $this->loadModel('feedback')->getList('review', $orderBy);
  1328. $this->loadModel('flow');
  1329. $reviewAction = $this->loadModel('workflowaction')->getByModuleAndAction('feedback', 'review');
  1330. $reviewList = array();
  1331. foreach($feedbacks as $feedback)
  1332. {
  1333. if(!$this->feedback->isClickable($feedback, 'review')) continue;
  1334. if($reviewAction && $reviewAction->extensionType != 'none' && $reviewAction->status == 'enable' && !empty($reviewAction->conditions) && !$this->flow->checkConditions($reviewAction->conditions, $feedback)) continue;
  1335. $data = new stdclass();
  1336. $data->id = $feedback->id;
  1337. $data->title = $feedback->title;
  1338. $data->type = 'feedback';
  1339. $data->time = $feedback->openedDate;
  1340. $data->status = $feedback->status;
  1341. $data->product = isset($feedback->product) ? $feedback->product : 0;
  1342. $data->project = isset($feedback->project) ? $feedback->project : 0;
  1343. $reviewList[] = $data;
  1344. }
  1345. if($checkExists) return !empty($reviewList);
  1346. return $reviewList;
  1347. }
  1348. /**
  1349. * 获取评审中的OA。
  1350. * Get reviewing OA.
  1351. *
  1352. * @param string $orderBy
  1353. * @param bool $checkExists
  1354. * @access public
  1355. * @return array|bool
  1356. */
  1357. public function getReviewingOA($orderBy = 'status', $checkExists = false)
  1358. {
  1359. if($this->config->edition == 'open') return array();
  1360. /* Get dept info. */
  1361. $allDeptList = $this->loadModel('dept')->getDeptPairs();
  1362. $allDeptList['0'] = '/';
  1363. $managedDeptList = array();
  1364. $tmpDept = $this->dept->getDeptManagedByMe($this->app->user->account);
  1365. foreach($tmpDept as $d) $managedDeptList[$d->id] = $d->name;
  1366. $oa = array();
  1367. if(common::hasPriv('attend', 'review')) $oa['attend'] = $this->getReviewingAttends($allDeptList, $managedDeptList);
  1368. if(common::hasPriv('leave', 'review') && common::hasPriv('leave', 'view')) $oa['leave'] = $this->getReviewingLeaves($allDeptList, $managedDeptList, $orderBy);
  1369. if(common::hasPriv('overtime', 'review') && common::hasPriv('overtime', 'view')) $oa['overtime'] = $this->getReviewingOvertimes($allDeptList, $managedDeptList, $orderBy);
  1370. if(common::hasPriv('makeup', 'review') && common::hasPriv('makeup', 'view')) $oa['makeup'] = $this->getReviewingMakeups($allDeptList, $managedDeptList, $orderBy);
  1371. if(common::hasPriv('lieu', 'review') && common::hasPriv('lieu', 'view')) $oa['lieu'] = $this->getReviewingLieus($allDeptList, $managedDeptList, $orderBy);
  1372. if($checkExists)
  1373. {
  1374. foreach($oa as $type => $reviewings)
  1375. {
  1376. if(!empty($reviewings)) return true;
  1377. }
  1378. }
  1379. $reviewList = array();
  1380. $users = $this->loadModel('user')->getPairs('noletter');
  1381. foreach($oa as $type => $reviewings)
  1382. {
  1383. foreach($reviewings as $object)
  1384. {
  1385. $review = new stdclass();
  1386. $review->id = $object->id;
  1387. $review->type = $type;
  1388. $review->time = $type == 'attend' ? $object->date : $object->createdDate;
  1389. $review->status = $type == 'attend' ? $object->reviewStatus : $object->status;
  1390. $review->title = '';
  1391. $review->product = isset($object->product) ? $object->product : 0;
  1392. $review->project = isset($object->project) ? $object->project : 0;
  1393. if($type == 'attend')
  1394. {
  1395. $review->title = sprintf($this->lang->my->auditField->oaTitle[$type], zget($users, $object->account), $object->date);
  1396. }
  1397. elseif(isset($this->lang->my->auditField->oaTitle[$type]))
  1398. {
  1399. $review->title = sprintf($this->lang->my->auditField->oaTitle[$type], zget($users, $object->createdBy), $object->begin . ' ' . substr($object->start, 0, 5) . ' ~ ' . $object->end . ' ' . substr($object->finish, 0, 5));
  1400. }
  1401. $reviewList[] = $review;
  1402. }
  1403. }
  1404. return $reviewList;
  1405. }
  1406. /**
  1407. * 获取评审列表。
  1408. * Get reviewed list.
  1409. *
  1410. * @param string $browseType
  1411. * @param string $orderBy
  1412. * @param object $pager
  1413. * @access public
  1414. * @return array
  1415. */
  1416. public function getReviewedList($browseType, $orderBy = 'time_desc', $pager = null)
  1417. {
  1418. $field = $orderBy;
  1419. $direction = 'asc';
  1420. if(strpos($orderBy, '_') !== false) list($field, $direction) = explode('_', $orderBy);
  1421. $actionField = 'date';
  1422. if($field == 'type') $actionField = 'objectType';
  1423. if($field == 'id') $actionField = 'objectID';
  1424. $orderBy = $actionField . '_' . $direction;
  1425. $condition = "(`action` = 'reviewed' or `action` = 'approvalreview')";
  1426. if($browseType == 'createdbyme')
  1427. {
  1428. $condition = "objectType != 'projectchange' AND (";
  1429. $condition .= "(objectType in('story','case','feedback') and action = 'submitreview') OR ";
  1430. $condition .= "(objectType = 'review' and action = 'opened') OR ";
  1431. $condition .= "(objectType = 'deploy' and action = 'created') OR ";
  1432. $condition .= "(objectType = 'deploy' and action = 'submit') OR ";
  1433. $condition .= "(objectType = 'attend' and action = 'commited') OR ";
  1434. $condition .= "(`action` = 'approvalsubmit') OR ";
  1435. $condition .= "(objectType in('leave','makeup','overtime','lieu') and action = 'created'))";
  1436. $condition = "($condition)";
  1437. }
  1438. $actionIdList = $this->dao->select('MAX(`id`) as `id`')->from(TABLE_ACTION)
  1439. ->where('actor')->eq($this->app->user->account)
  1440. ->andWhere('vision')->eq($this->config->vision)
  1441. ->andWhere($condition)
  1442. ->groupBy('objectType,objectID')
  1443. ->fetchPairs();
  1444. $objectTypeList = array();
  1445. $actions = $this->dao->select('objectType,objectID,actor,action,`date`,extra')->from(TABLE_ACTION)->where('id')->in($actionIdList)->orderBy($orderBy)->fetchAll();
  1446. foreach($actions as $action) $objectTypeList[$action->objectType][] = $action->objectID;
  1447. $flows = $this->config->edition == 'open' ? array() : $this->dao->select('module,`table`,name,titleField')->from(TABLE_WORKFLOW)->where('module')->in(array_keys($objectTypeList))->andWhere('buildin')->eq(0)->fetchAll('module');
  1448. $objectGroup = array();
  1449. foreach($objectTypeList as $objectType => $idList)
  1450. {
  1451. $table = zget($this->config->objectTables, $objectType, '');
  1452. if(empty($table) && isset($flows[$objectType])) $table = $flows[$objectType]->table;
  1453. if(empty($table)) continue;
  1454. if(in_array($objectType, array('story', 'testcase', 'case')))
  1455. {
  1456. $objectGroup[$objectType] = $this->dao->select('t1.*')
  1457. ->from($table)->alias('t1')
  1458. ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
  1459. ->where('t1.id')->in($idList)->andWhere('t1.deleted')->eq('0')
  1460. ->andWhere('t2.deleted')->eq('0')
  1461. ->fetchAll('id');
  1462. }
  1463. else
  1464. {
  1465. $objectGroup[$objectType] = $this->dao->select('*')->from($table)
  1466. ->where('id')->in($idList)
  1467. ->beginIF(strpos(',attend,overtime,makeup,leave,lieu,', ",{$objectType},") === false)->andWhere('deleted')->eq('0')->fi()
  1468. ->fetchAll('id');
  1469. }
  1470. }
  1471. $reviewedList = $this->buildReviewedList($objectGroup, $actions, $flows);
  1472. if(!is_null($pager))
  1473. {
  1474. $pager->setRecTotal(count($reviewedList));
  1475. $pager->setPageTotal();
  1476. $pager->setPageID($pager->pageID);
  1477. $reviewedList = array_chunk($reviewedList, (int)$pager->recPerPage);
  1478. $reviewedList = !empty($reviewedList[$pager->pageID - 1]) ? $reviewedList[$pager->pageID - 1] : array();
  1479. }
  1480. return !empty($reviewedList) ? $reviewedList : array();
  1481. }
  1482. /**
  1483. * 构建评审列表。
  1484. * Build reviewed list.
  1485. *
  1486. * @param array $objectGroup
  1487. * @param array $actions
  1488. * @param array $flows
  1489. * @access private
  1490. * @return array
  1491. */
  1492. private function buildReviewedList($objectGroup, $actions, $flows)
  1493. {
  1494. $this->app->loadConfig('action');
  1495. $reviewList = array();
  1496. $users = $this->loadModel('user')->getPairs('noletter');
  1497. foreach($actions as $action)
  1498. {
  1499. $objectType = $action->objectType;
  1500. if(!isset($objectGroup[$objectType]) || !$action->objectID) continue;
  1501. if(!isset($objectGroup[$objectType][$action->objectID])) continue;
  1502. $object = $objectGroup[$objectType][$action->objectID];
  1503. $review = new stdclass();
  1504. $review->id = $object->id;
  1505. $review->type = $objectType;
  1506. $review->time = substr($action->date, 0, 19);
  1507. $review->result = strtolower($action->extra);
  1508. $review->status = $objectType == 'attend' ? $object->reviewStatus : (isset($object->status) && !isset($flows[$objectType]) ? $object->status : 'done');
  1509. if(strpos($review->result, ',') !== false) list($review->result) = explode(',', $review->result);
  1510. $review->product = isset($object->product) ? $object->product : 0;
  1511. $review->project = isset($object->project) ? $object->project : 0;
  1512. if($objectType == 'story') $review->storyType = $object->type;
  1513. if($review->type == 'review') $review->type = 'projectreview';
  1514. if($review->type == 'case') $review->type = 'testcase';
  1515. if(empty($review->result) && isset($object->reviewResult)) $review->result = zget($this->lang->my->reviewResultList, $object->reviewResult, '');
  1516. $review->title = '';
  1517. if(isset($object->title))
  1518. {
  1519. $review->title = $object->title;
  1520. }
  1521. elseif($objectType == 'attend')
  1522. {
  1523. $review->title = sprintf($this->lang->my->auditField->oaTitle[$objectType], zget($users, $object->account), $object->date);
  1524. }
  1525. elseif(isset($this->lang->my->auditField->oaTitle[$objectType]))
  1526. {
  1527. $review->title = sprintf($this->lang->my->auditField->oaTitle[$objectType], zget($users, $object->createdBy), $object->begin . ' ' . substr($object->start, 0, 5) . ' ~ ' . $object->end . ' ' . substr($object->finish, 0, 5));
  1528. }
  1529. else
  1530. {
  1531. $title = '';
  1532. $titleFieldName = zget($this->config->action->objectNameFields, $objectType, '');
  1533. if(empty($titleFieldName) && isset($flows[$objectType]))
  1534. {
  1535. if(!empty($flows[$objectType]->titleField)) $titleFieldName = $flows[$objectType]->titleField;
  1536. if(empty($flows[$objectType]->titleField)) $title = $flows[$objectType]->name;
  1537. }
  1538. $review->title = empty($titleFieldName) || !isset($object->{$titleFieldName}) ? "{$title} #{$object->id}" : $object->{$titleFieldName};
  1539. }
  1540. $reviewList[] = $review;
  1541. }
  1542. return $reviewList;
  1543. }
  1544. /**
  1545. * 获取工作流键值对。
  1546. * Get flow paris.
  1547. *
  1548. * @access public
  1549. * @return array
  1550. */
  1551. public function getFlowPairs()
  1552. {
  1553. return $this->dao->select('module,name')->from(TABLE_WORKFLOW)->where('buildin')->eq(0)->fetchPairs();
  1554. }
  1555. /**
  1556. * 获取我的产品。
  1557. * Get my charged products.
  1558. *
  1559. * @param string $type undone|ownbyme
  1560. * @access public
  1561. * @return object
  1562. */
  1563. public function getProducts($type = 'undone')
  1564. {
  1565. $products = $this->dao->select('t1.*, t2.name as programName')->from(TABLE_PRODUCT)->alias('t1')
  1566. ->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
  1567. ->where('t1.deleted')->eq(0)
  1568. ->beginIF($type == 'undone')->andWhere('t1.status')->eq('normal')->fi()
  1569. ->beginIF($type == 'ownbyme')->andWhere('t1.PO')->eq($this->app->user->account)->fi()
  1570. ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
  1571. ->filterTpl('skip')
  1572. ->orderBy('t1.order_asc')
  1573. ->fetchAll('id');
  1574. list($summaryStories, $plans, $releases, $executions) = $this->getProductRelatedData(array_keys($products));
  1575. $allCount = count($products);
  1576. $unclosedCount = 0;
  1577. foreach($products as $key => $product)
  1578. {
  1579. $product->plans = isset($plans[$product->id]) ? $plans[$product->id] : 0;
  1580. $product->releases = isset($releases[$product->id]) ? $releases[$product->id] : 0;
  1581. if(isset($executions[$product->id])) $product->executions = $executions[$product->id];
  1582. $product->storyEstimateCount = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->estimateCount : 0;
  1583. $product->storyTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->total : 0;
  1584. $product->storyFinishedTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->finishedTotal : 0;
  1585. $product->storyLeftTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->leftTotal : 0;
  1586. $product->storyFinishedRate = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->finishedRate : 0;
  1587. $product->latestExecution = isset($executions[$product->id]) ? $executions[$product->id] : '';
  1588. if($product->status != 'closed') $unclosedCount ++;
  1589. if($product->status == 'closed') unset($products[$key]);
  1590. }
  1591. /* Sort by storyCount, get 5 records */
  1592. $products = json_decode(json_encode($products), true);
  1593. array_multisort(helper::arrayColumn($products, 'storyEstimateCount'), SORT_DESC, $products);
  1594. $products = array_slice($products, 0, 5);
  1595. $data = new stdClass();
  1596. $data->allCount = $allCount;
  1597. $data->unclosedCount = $unclosedCount;
  1598. $data->products = array_values($products);
  1599. return $data;
  1600. }
  1601. }