529 lines
20 KiB
PHP
529 lines
20 KiB
PHP
<?php
|
|
class pivotZen extends pivot
|
|
{
|
|
/**
|
|
* 获取分组里一个透视表的默认方法和参数。
|
|
* Get default method name and parameters of a pivot in a group.
|
|
*
|
|
* @param int $dimension
|
|
* @param int $group
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getDefaultMethodAndParams(int $dimensionID, int $groupID): array
|
|
{
|
|
$currentGroup = $this->loadModel('tree')->getByID($groupID);
|
|
if(empty($currentGroup) || $currentGroup->grade != 1) return array('', '');
|
|
|
|
$groups = $this->pivot->getGroupsByDimensionAndPath($dimensionID, $currentGroup->path);
|
|
if(!$groups) return array('', '');
|
|
|
|
foreach($groups as $group)
|
|
{
|
|
if($this->config->edition == 'open' && $group->grade == 1) continue;
|
|
|
|
$pivotID = $this->pivot->getPivotID($group->id);
|
|
if($pivotID) return array('show', "groupID={$group->id}&pivotID={$pivotID}");
|
|
}
|
|
|
|
$firstDimension = $this->loadModel('dimension')->getFirst();
|
|
if($dimensionID != $firstDimension->id) return array('', '');
|
|
|
|
if(empty($this->lang->pivotList->{$currentGroup->collector}->lists)) return array('', '');
|
|
|
|
foreach($this->lang->pivotList->{$currentGroup->collector}->lists as $item)
|
|
{
|
|
$items = explode('|', $item);
|
|
if(count($items) != 3) continue;
|
|
|
|
$method = $items[2];
|
|
|
|
if(common::hasPriv('pivot', $method)) return array($method, '');
|
|
}
|
|
|
|
return array('', '');
|
|
}
|
|
|
|
/**
|
|
* 获取菜单项。
|
|
* Get menu items.
|
|
*
|
|
* @param array $menus
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getMenuItems(array $menus): array
|
|
{
|
|
$items = array();
|
|
foreach($menus as $menu)
|
|
{
|
|
if(isset($menu->url)) $items[] = $menu;
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* 获取侧边栏菜单。
|
|
* Get sidebar menus of pivot.
|
|
*
|
|
* @param int $dimensionID
|
|
* @param object $groupID
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getSidebarMenus(int $dimensionID, int $groupID): array
|
|
{
|
|
$currentGroup = $this->loadModel('tree')->getByID($groupID);
|
|
if(empty($currentGroup) || $currentGroup->grade != 1) return array();
|
|
|
|
$groups = $this->pivot->getGroupsByDimensionAndPath($dimensionID, $currentGroup->path);
|
|
if(!$groups) return array();
|
|
|
|
$this->loadModel('bi');
|
|
/* $firstAction = $this->loadModel('action')->getAccountFirstAction($this->app->user->account);
|
|
$builtins = array_column($this->config->bi->builtin->pivots, 'id', 'id');*/
|
|
$menus = array();
|
|
foreach($groups as $group)
|
|
{
|
|
if($this->config->edition == 'open' && $group->grade == 1) continue;
|
|
|
|
$pivots = $this->pivot->getAllPivotByGroupID($group->id);
|
|
$pivots = $this->pivot->filterInvisiblePivot($pivots);
|
|
$pivots = $this->loadModel('mark')->getMarks($pivots, 'pivot', 'view');
|
|
$pivots = $this->pivot->isVersionChange($pivots, false);
|
|
if(empty($pivots)) continue;
|
|
|
|
if($group->grade > 1) $menus[] = (object)array('id' => $group->id, 'parent' => 0, 'name' => $group->name);
|
|
|
|
if($pivots) $pivots = $this->pivot->processPivot($pivots, false);
|
|
|
|
foreach($pivots as $pivot)
|
|
{
|
|
/* $this->setNewMark($pivot, $firstAction, $builtins);*/
|
|
$params = helper::safe64Encode("groupID={$group->id}&pivotID={$pivot->id}&mark=view");
|
|
$url = inlink('preview', "dimension={$dimensionID}&group={$currentGroup->id}&method=show¶ms={$params}");
|
|
$menus[] = (object)array('id' => $group->id . '_' . $pivot->id, 'parent' => $group->grade > 1 ? $group->id : 0, 'name' => $pivot->name, 'url' => $url);
|
|
}
|
|
}
|
|
|
|
$firstDimension = $this->loadModel('dimension')->getFirst();
|
|
if($dimensionID != $firstDimension->id) return $menus;
|
|
|
|
$builtinMenus = $this->getBuiltinMenus($dimensionID, $currentGroup);
|
|
return array_merge($menus, $builtinMenus);
|
|
}
|
|
|
|
/**
|
|
* 设置 “新” 标签。
|
|
* Set new mark.
|
|
*
|
|
* @param object $pivot
|
|
* @param object $firstAction
|
|
* @access protected
|
|
* @return void
|
|
*/
|
|
protected function setNewMark(object $pivot, object $firstAction, array $builtins): void
|
|
{
|
|
/* 如果不是内置透视表,则不需要展示“新”标签。*/
|
|
/* If the pivot is not built-in, no need to display the "new" tag. */
|
|
if($pivot->builtin == 0) return;
|
|
/* 版本没有改变,此时讨论是不是新透视表。*/
|
|
/* The version has not changed, so it is judged whether it is a new pivot. */
|
|
if(!$pivot->versionChange)
|
|
{
|
|
if(!isset($builtins[$pivot->id])) return;
|
|
if(!$pivot->mark && $pivot->createdDate < $firstAction->date) $pivot->mark = true;
|
|
$isMainVersion = filter_var($pivot->version, FILTER_VALIDATE_INT) !== false;
|
|
if(!$pivot->mark && $isMainVersion) $pivot->name = array('text' => $pivot->name, 'html' => $pivot->name . ' <span class="label ghost size-sm bg-secondary-50 text-secondary-500 rounded-full">' . $this->lang->pivot->new . '</span>');
|
|
}
|
|
else
|
|
{
|
|
$maxVersion = $this->pivot->getMaxVersion($pivot->id);
|
|
if(!$this->loadModel('mark')->isMark('pivot', $pivot->id, $maxVersion, 'view'))
|
|
{
|
|
$pivot->name = array('text' => $pivot->name, 'html' => $pivot->name . ' <span class="label ghost size-sm bg-secondary-50 text-secondary-500 rounded-full">' . $this->lang->pivot->newVersion . '</span>');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 在第一个维度上显示内置透视表。
|
|
* Display the built-in pivots in the first dimension.
|
|
*
|
|
* @param int $dimensionID
|
|
* @param object $currengGroup
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getBuiltinMenus(int $dimensionID, object $currentGroup): array
|
|
{
|
|
$collector = $currentGroup->collector;
|
|
if(empty($this->lang->pivotList->$collector->lists)) return array();
|
|
|
|
$menus = array();
|
|
|
|
ksort($this->lang->pivotList->$collector->lists);
|
|
foreach($this->lang->pivotList->$collector->lists as $item)
|
|
{
|
|
$items = explode('|', $item);
|
|
if(count($items) != 3) continue;
|
|
|
|
$label = $items[0];
|
|
$method = $items[2];
|
|
|
|
if(!common::hasPriv('pivot', $method)) continue;
|
|
|
|
$url = inlink('preview', "dimension={$dimensionID}&group={$currentGroup->id}&method={$method}");
|
|
|
|
$menus[] = (object)array('id' => $method, 'parent' => 0, 'name' => $label, 'url' => $url);
|
|
}
|
|
|
|
return $menus;
|
|
}
|
|
|
|
/**
|
|
* Preview pivots of a group.
|
|
*
|
|
* @param int $groupID
|
|
* @param int $pivotID
|
|
* @param string $mark
|
|
* @param string|null $version
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function show(int $groupID, int $pivotID, string $mark = '', ?string $version = null): void
|
|
{
|
|
$this->pivot->checkAccess($pivotID, 'preview');
|
|
|
|
if(is_null($version))
|
|
{
|
|
$pivot = $this->pivot->getByID($pivotID, true);
|
|
}
|
|
else
|
|
{
|
|
$pivot = $this->pivot->getPivotSpec($pivotID, $version, true);
|
|
}
|
|
$driver = $pivot->driver;
|
|
|
|
$this->pivot->isVersionChange($pivot);
|
|
if($mark && $pivot->builtin == 1)
|
|
{
|
|
$markVersion = $pivot->versionChange ? $this->pivot->getMaxVersion($pivot->id) : $pivot->version;
|
|
|
|
if(!$this->loadModel('mark')->isMark('pivot', $pivot->id, $markVersion, 'view'))
|
|
{
|
|
$this->loadModel('mark')->setMark(array($pivot->id), 'pivot', $markVersion, 'view');
|
|
}
|
|
}
|
|
|
|
if(isset($_POST['filterValues']) and $_POST['filterValues'])
|
|
{
|
|
$filterValuesData = is_string($this->post->filterValues) ? explode(',', $this->post->filterValues) : $this->post->filterValues;
|
|
foreach($filterValuesData as $key => $value) $pivot->filters[$key]['default'] = $value;
|
|
}
|
|
$showOrigin = false;
|
|
if(isset($_POST['summary']) and $_POST['summary']) $showOrigin = $this->post->summary == 'notuse';
|
|
|
|
list($sql, $filterFormat) = $this->pivot->getFilterFormat($pivot->sql, $pivot->filters);
|
|
|
|
$fields = json_decode(json_encode($pivot->fieldSettings), true);
|
|
$langs = json_decode($pivot->langs, true) ?? array();
|
|
|
|
$settingShowOrigin = isset($pivot->settings['summary']) && $pivot->settings['summary'] == 'notuse';
|
|
if($showOrigin || $settingShowOrigin)
|
|
{
|
|
list($data, $configs) = $this->pivot->genOriginSheet($fields, $pivot->settings, $sql, $filterFormat, $langs, $driver);
|
|
}
|
|
else
|
|
{
|
|
list($data, $configs) = $this->pivot->genSheet($fields, $pivot->settings, $sql, $filterFormat, $langs, $driver);
|
|
}
|
|
|
|
$this->view->hasVersionMark = $this->loadModel('mark')->hasMark('pivot', $pivotID, 'all', 'version');
|
|
$this->view->pivotName = $pivot->name;
|
|
$this->view->title = $pivot->name;
|
|
$this->view->currentMenu = $groupID . '_' . $pivot->id;
|
|
$this->view->currentGroup = $groupID;
|
|
$this->view->pivot = $pivot;
|
|
$this->view->showOrigin = $showOrigin;
|
|
$this->view->data = $data;
|
|
$this->view->configs = $configs;
|
|
}
|
|
|
|
/**
|
|
* Bug create pivot.
|
|
*
|
|
* @param string $begin
|
|
* @param string $end
|
|
* @param int $product
|
|
* @param int $execution
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function bugCreate(string $begin = '', string $end = '', int $product = 0, int $execution = 0): void
|
|
{
|
|
$this->app->loadLang('bug');
|
|
$begin = $begin ? date('Y-m-d', strtotime($begin)) : date('Y-m-01', strtotime('last month'));
|
|
$end = date('Y-m-d', strtotime($end ?: 'now'));
|
|
|
|
$this->view->title = $this->lang->pivot->bugCreate;
|
|
$this->view->pivotName = $this->lang->pivot->bugCreate;
|
|
$this->view->bugs = $this->pivot->getBugs($begin, $end, $product ? $product : 0, $execution ? $execution : 0);
|
|
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
|
|
$this->view->executions = $this->pivot->getProjectExecutions();
|
|
$this->view->products = $this->loadModel('product')->getPairs('', 0, '', 'all');
|
|
$this->view->begin = $begin;
|
|
$this->view->end = $end;
|
|
$this->view->execution = $execution;
|
|
$this->view->product = $product;
|
|
$this->view->currentMenu = 'bugcreate';
|
|
}
|
|
|
|
/**
|
|
* Bug assign pivot.
|
|
*
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function bugAssign(): void
|
|
{
|
|
$this->session->set('productList', $this->app->getURI(true), 'product');
|
|
|
|
$this->view->title = $this->lang->pivot->bugAssign;
|
|
$this->view->pivotName = $this->lang->pivot->bugAssign;
|
|
$this->view->bugs = $this->pivot->getBugAssign();
|
|
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
|
|
$this->view->currentMenu = 'bugassign';
|
|
}
|
|
|
|
/**
|
|
* Product information pivot.
|
|
*
|
|
* @params string $conditions
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function productSummary(string $conditions = '', int|string $productID = 0, string $productStatus = 'normal', string $productType = 'normal'): void
|
|
{
|
|
$this->app->loadLang('story');
|
|
$this->app->loadLang('product');
|
|
$this->app->loadLang('productplan');
|
|
$this->session->set('productList', $this->app->getURI(true), 'product');
|
|
|
|
$filters = array('productID' => $productID, 'productStatus' => $productStatus, 'productType' => $productType);
|
|
$products = $this->pivot->getProducts($conditions, 'story', $filters);
|
|
|
|
$this->view->filters = $filters;
|
|
$this->view->title = $this->lang->pivot->productSummary;
|
|
$this->view->pivotName = $this->lang->pivot->productSummary;
|
|
$this->view->products = $this->processProductsForProductSummary($products);
|
|
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
|
|
$this->view->conditions = $conditions;
|
|
$this->view->currentMenu = 'productsummary';
|
|
}
|
|
|
|
/**
|
|
* 把计划数组展开为独立的产品对象并添加跨行合并属性。
|
|
* Expand the plans property to single product objects and add rowspan property.
|
|
*
|
|
* @params array $products
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function processProductsForProductSummary(array $products): array
|
|
{
|
|
$productList = array();
|
|
|
|
foreach($products as $product)
|
|
{
|
|
if(!isset($product->plans))
|
|
{
|
|
$product->planTitle = '';
|
|
$product->planBegin = '';
|
|
$product->planEnd = '';
|
|
$product->storyDraft = 0;
|
|
$product->storyReviewing = 0;
|
|
$product->storyActive = 0;
|
|
$product->storyChanging = 0;
|
|
$product->storyClosed = 0;
|
|
$product->storyTotal = 0;
|
|
|
|
$productList[] = $product;
|
|
|
|
continue;
|
|
}
|
|
|
|
$first = true;
|
|
foreach($product->plans as $plan)
|
|
{
|
|
$newProduct = clone $product;
|
|
$newProduct->planTitle = $plan->title;
|
|
$newProduct->planBegin = $plan->begin == '2030-01-01' ? $this->lang->productplan->future : $plan->begin;
|
|
$newProduct->planEnd = $plan->end == '2030-01-01' ? $this->lang->productplan->future : $plan->end;
|
|
$newProduct->storyDraft = isset($plan->status['draft']) ? $plan->status['draft'] : 0;
|
|
$newProduct->storyReviewing = isset($plan->status['reviewing']) ? $plan->status['reviewing'] : 0;
|
|
$newProduct->storyActive = isset($plan->status['active']) ? $plan->status['active'] : 0;
|
|
$newProduct->storyChanging = isset($plan->status['changing']) ? $plan->status['changing'] : 0;
|
|
$newProduct->storyClosed = isset($plan->status['closed']) ? $plan->status['closed'] : 0;
|
|
$newProduct->storyTotal = $newProduct->storyDraft + $newProduct->storyReviewing + $newProduct->storyActive + $newProduct->storyChanging + $newProduct->storyClosed;
|
|
|
|
if($first) $newProduct->rowspan = count($newProduct->plans);
|
|
|
|
$productList[] = $newProduct;
|
|
|
|
$first = false;
|
|
}
|
|
}
|
|
|
|
return $productList;
|
|
}
|
|
|
|
/**
|
|
* Project deviation pivot.
|
|
*
|
|
* @param string $begin
|
|
* @param string $end
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function projectDeviation(string $begin = '', string $end = ''): void
|
|
{
|
|
$this->session->set('executionList', $this->app->getURI(true), 'execution');
|
|
|
|
$begin = $begin ? date('Y-m-d', strtotime($begin)) : date('Y-m-01');
|
|
$end = $end ? date('Y-m-d', strtotime($end)) : date('Y-m-d', strtotime(date('Y-m-01', strtotime('next month')) . ' -1 day'));
|
|
|
|
$this->view->title = $this->lang->pivot->projectDeviation;
|
|
$this->view->pivotName = $this->lang->pivot->projectDeviation;
|
|
$this->view->executions = $this->pivot->getExecutions($begin, $end);
|
|
$this->view->begin = $begin;
|
|
$this->view->end = $end;
|
|
$this->view->currentMenu = 'projectdeviation';
|
|
}
|
|
|
|
/**
|
|
* 组织透视表。
|
|
* Workload pivot.
|
|
*
|
|
* @param string $begin
|
|
* @param string $end
|
|
* @param int $days
|
|
* @param float $workhour
|
|
* @param int $dept
|
|
* @param string $assign
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function workload(string $begin = '', string $end = '', int $days = 0, float $workhour= 0, int $dept = 0, string $assign = 'assign'): void
|
|
{
|
|
$this->app->loadConfig('execution');
|
|
$this->session->set('executionList', $this->app->getURI(true), 'execution');
|
|
|
|
$begin = $begin ? strtotime($begin) : time();
|
|
$end = $end ? strtotime($end) : time() + (7 * 24 * 3600);
|
|
$end += 24 * 3600;
|
|
$beginWeekDay = date('w', $begin);
|
|
$begin = date('Y-m-d', $begin);
|
|
$end = date('Y-m-d', $end);
|
|
|
|
if(empty($workhour)) $workhour = $this->config->execution->defaultWorkhours;
|
|
$diffDays = helper::diffDate($end, $begin);
|
|
if($days > $diffDays) $days = $diffDays;
|
|
if(empty($days))
|
|
{
|
|
$weekDay = $beginWeekDay;
|
|
$days = $diffDays;
|
|
for($i = 0; $i < $diffDays; $i++, $weekDay++)
|
|
{
|
|
$weekDay = $weekDay % 7;
|
|
if(($this->config->execution->weekend == 2 && $weekDay == 6) || $weekDay == 0) $days--;
|
|
}
|
|
}
|
|
|
|
$allHour = $workhour * $days;
|
|
$users = $this->loadModel('user')->getPairs('noletter|noclosed');
|
|
|
|
$this->view->title = $this->lang->pivot->workload;
|
|
$this->view->pivotName = $this->lang->pivot->workload;
|
|
$this->view->workload = $this->pivot->getWorkload($dept, $assign, $users, $allHour);
|
|
$this->view->depts = $this->loadModel('dept')->getOptionMenu();
|
|
$this->view->users = $users;
|
|
$this->view->dept = $dept;
|
|
$this->view->begin = $begin;
|
|
$this->view->end = date('Y-m-d', strtotime($end) - 24 * 3600);
|
|
$this->view->days = $days;
|
|
$this->view->workhour = $workhour;
|
|
$this->view->assign = $assign;
|
|
$this->view->currentMenu = 'workload';
|
|
}
|
|
|
|
/**
|
|
* 获取下钻列配置。
|
|
* Get drill.
|
|
*
|
|
* @param int $pivotID
|
|
* @param string $colName
|
|
* @param string $status
|
|
* @access public
|
|
* @return object
|
|
*/
|
|
public function getDrill(int $pivotID, string $version, string $colName, string $status = 'published'): object
|
|
{
|
|
if($status == 'published')
|
|
{
|
|
$drills = $this->pivot->fetchPivotDrills($pivotID, $version, $colName);
|
|
return reset($drills);
|
|
}
|
|
|
|
$cache = $this->getCache($pivotID);
|
|
$drills = json_decode(json_encode($cache->drills), true);
|
|
foreach($drills as $drill)
|
|
{
|
|
$drill = (object)$drill;
|
|
$drill->condition = (array)$drill->condition;
|
|
if($drill->field == $colName) return $drill;
|
|
}
|
|
return new stdclass();
|
|
}
|
|
|
|
/**
|
|
* 获取筛选器的下拉选项 URL。
|
|
* Get filter options url.
|
|
*
|
|
* @param array $filter
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public function getFilterOptionUrl(array $filter, string $sql = '', array $fieldSettings = array()): object
|
|
{
|
|
$field = $filter['field'];
|
|
$from = zget($filter, 'from', 'result');
|
|
$value = zget($filter, 'default', '');
|
|
$values = is_array($value) ? implode(',', $value) : $value;
|
|
|
|
$url = helper::createLink('pivot', 'ajaxGetSysOptions', "search={search:base64}");
|
|
$data = array();
|
|
$data['values'] = $values;
|
|
if($from == 'query')
|
|
{
|
|
$data['type'] = $filter['typeOption'];
|
|
}
|
|
else
|
|
{
|
|
$fieldSetting = $fieldSettings[$field];
|
|
$fieldSetting = (array)$fieldSetting;
|
|
$fieldType = $fieldSetting['type'];
|
|
|
|
$data['type'] = $fieldType;
|
|
$data['object'] = $fieldSetting['object'];
|
|
$data['field'] = $fieldType != 'options' && $fieldType != 'object' ? $field : $fieldSetting['field'];
|
|
$data['saveAs'] = zget($filter, 'saveAs', $field);
|
|
$data['sql'] = $sql;
|
|
$data['originalField'] = zget($fieldSetting, 'field', $data['field']);
|
|
}
|
|
|
|
return (object)array('url' => $url, 'method' => 'post', 'data' => $data);
|
|
}
|
|
}
|