Budget.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('ProcesHelper');
  4. Lib::loadClass('TableAjax');
  5. Lib::loadClass('UserStorageFactory');
  6. Lib::loadClass('Response');
  7. Lib::loadClass('UI');
  8. class Route_Budget extends RouteBase {
  9. public $_costs = array();
  10. public $_plan = array();
  11. public $_projectInfo = array();
  12. public $_projectPathsOrder = array();
  13. public function handleAuth() {
  14. if (!User::logged()) {
  15. throw new HttpException('Unauthorized', 401);
  16. }
  17. }
  18. public function defaultAction() {
  19. $args = array();
  20. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  21. $args['_print'] = V::get('_print', '', $_REQUEST, 'int');
  22. UI::gora();
  23. UI::menu();
  24. if (!$args['_print']) {
  25. $this->printMenu($args['year']);
  26. }
  27. UI::dol();
  28. }
  29. public function yearBudgetAction() {
  30. $args = array();
  31. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  32. $args['groups'] = V::get('fltrGroups', array(), $_REQUEST, 'array', array('V', 'filterPositiveInteger'));
  33. $args['_print'] = V::get('_print', '', $_REQUEST, 'int');
  34. $hasData = false;
  35. $groups = null;
  36. if ($args['year'] > 0) {
  37. $hasData = $this->fetchDataByYear($args['year'], $args['groups']);
  38. $groups = $this->getUsedUserGroups();
  39. }
  40. UI::gora();
  41. UI::menu();
  42. if (!$args['_print']) {
  43. $this->printMenu($args['year'], $groups, $args['groups']);
  44. }
  45. if (empty($args['year'])) {
  46. UI::alert('warning', "Nie wybrano roku.");
  47. UI::dol();
  48. exit;
  49. }
  50. if (!$hasData) {
  51. UI::alert('warning', "Brak danych na wybrany rok.");
  52. return;
  53. }
  54. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">$costs: ';print_r($costs);echo'</pre>';
  55. $this->printCostsForYear($args['year'], $args['groups']);
  56. UI::dol();
  57. }
  58. public function printMenu($selectedYear, $groups = array(), $selectedGroups = array()) {
  59. //UI::menu();
  60. $year = ($selectedYear)? $selectedYear : date("Y");
  61. UI::startTag('div', ['class'=>"jumbotron"]);
  62. UI::startContainer();
  63. UI::startTag('form', ['class'=>"form-inline", 'method'=>"POST"]);
  64. UI::emptyTag('input', ['type'=>"hidden", 'name'=>"_task", 'value'=>"yearBudget"]);
  65. UI::tag('label', ['for'=>"year"], "Zestawienie kosztów projektów. Wybierz rok:");
  66. UI::startTag('div', ['class'=>"input-group date", 'id'=>"fldZestYear"]);
  67. UI::emptyTag('input', ['type'=>"text", 'name'=>"year", 'class'=>"form-control", 'value'=>""]);
  68. UI::tag('span', ['class'=>"input-group-addon"], "<span class=\"glyphicon glyphicon-time\"></span>");
  69. UI::endTag('div');
  70. if (!empty($groups)) {
  71. UI::startTag('div', ['style'=>"margin:8px 0"]);
  72. UI::tag('label', ['for'=>"fltrGroups"], "Pokaż tylko projekty dostępne dla grup:");
  73. UI::startTag('select', ['multiple'=>"multiple", 'name'=>"fltrGroups[]", 'size'=>min(5, count($groups) + 1), 'class'=>"form-control"]);
  74. UI::tag('option', ['value'=>""], " [ Wszystkie ] ");
  75. foreach ($groups as $idGroup => $groupLdapName) {
  76. if (in_array($idGroup, $selectedGroups)) {
  77. UI::tag('option', ['value'=>$idGroup, 'selected'=>"selected"], $groupLdapName);
  78. } else {
  79. UI::tag('option', ['value'=>$idGroup], $groupLdapName);
  80. }
  81. }
  82. UI::endTag('select');
  83. UI::endTag('div');
  84. }
  85. UI::tag('button', ['type'=>"submit", 'id'=>"fldZestYearBtn", 'class'=>"btn btn-primary", 'autocomplete'=>"off"], "Pokaż");
  86. UI::endTag('form');
  87. UI::startTag('div', ['style'=>"text-align:right"]);
  88. echo "Edytuj ";
  89. UI::tag('a', ['href'=>"index.php?_route=Budget&_task=plan&year={$year}",
  90. 'class'=>"btn btn-xs btn-default",
  91. 'title'=>"Plan budżetu (projects_budget_year_month)"], "plan budżetu");
  92. echo "na rok {$year}";
  93. UI::endTag('div');
  94. UI::endContainer();
  95. UI::endTag('div');
  96. UI::inlineJS(__FILE__ . '.menu.js', [
  97. 'year' => $year,
  98. 'month' => intval(date("m"))
  99. ]);
  100. }
  101. function printCostsForYear($year, $groups) {
  102. $months = array();
  103. for ($i = 0; $i < 12; $i++) {
  104. $months[] = $i + 1;
  105. }
  106. $projectPathsOrder = array_keys($this->_projectPathsOrder);
  107. foreach ($projectPathsOrder as $key => $value) {
  108. $projectPathsOrder[$key] = "" . $value;
  109. }
  110. UI::tag('div', ['id' => "widget-budget"]);
  111. UI::inlineCSS(__FILE__ . '.budget.css', []);
  112. echo UI::h('script', [ 'src' => "static/vendor.js" ]); // window.p5VendorJs: { React, ReactDOM, createReactClass, Redux }
  113. UI::inlineJS(__FILE__ . '.budget.js', []); // window.p5VendorJs.p5UI__Budget
  114. UI::inlineJS(__FILE__ . '.init-budget.js', [ // render window.p5VendorJs.p5UI__Budget
  115. 'HTML_ID' => "widget-budget",
  116. 'year' => $year,
  117. 'today' => date('Y-m-d'),
  118. 'projectPathsOrder' => $projectPathsOrder,
  119. 'projectPathsMap' => $this->_projectPathsOrder,
  120. 'projectInfo' => $this->_projectInfo,
  121. 'costs' => $this->_costs,
  122. 'plan' => $this->_plan,
  123. 'DBG' => false,
  124. ]);
  125. }
  126. public function getCost($idProject, $month) {
  127. if (!array_key_exists($idProject, $this->_costs)) {
  128. return null;
  129. }
  130. if (!array_key_exists($month, $this->_costs[$idProject]->costsByMonth)) {
  131. return null;
  132. }
  133. return $this->_costs[$idProject]->costsByMonth[$month];
  134. }
  135. public function getPlan($idProject, $month) {
  136. if (!array_key_exists($idProject, $this->_plan)) {
  137. return 0;
  138. }
  139. if (!array_key_exists($month, $this->_plan[$idProject])) {
  140. return 0;
  141. }
  142. return $this->_plan[$idProject][$month];
  143. }
  144. public function fetchDataByYear($year, $groups) {
  145. $this->_fetchCostsByYear($year);
  146. $this->_fetchPlanByYear($year);
  147. $this->_fetchProjectInfo();
  148. $this->_buildProjectTree();
  149. $this->_reacountCostsFromKoresp();
  150. $this->_filterProjectsByGroups($groups);
  151. return count($this->_projectInfo) > 1;// $this->_projectInfo[0] - Wszystkie projekty
  152. }
  153. public function _filterProjectsByGroups($groups) {
  154. if (!empty($groups)) {
  155. foreach ($this->_projectInfo as $idProject => $projInfo) {
  156. if (!$this->hasGroupsAccessToProjects($idProject, $groups)) {
  157. $this->_projectInfo[$idProject]->filteredByGroups = true;
  158. }
  159. }
  160. }
  161. }
  162. public function _fetchPlanByYear($year) {
  163. $db = DB::getDB();
  164. $this->_plan = array();
  165. $sql = "
  166. select plan.`ID`
  167. , plan.`ID_PROJECT` AS `ID_PROJECT`
  168. , plan.`MONTH_1_VALUE`
  169. , plan.`MONTH_2_VALUE`
  170. , plan.`MONTH_3_VALUE`
  171. , plan.`MONTH_4_VALUE`
  172. , plan.`MONTH_5_VALUE`
  173. , plan.`MONTH_6_VALUE`
  174. , plan.`MONTH_7_VALUE`
  175. , plan.`MONTH_8_VALUE`
  176. , plan.`MONTH_9_VALUE`
  177. , plan.`MONTH_10_VALUE`
  178. , plan.`MONTH_11_VALUE`
  179. , plan.`MONTH_12_VALUE`
  180. from `projects_budget_year_month` plan
  181. where plan.`year`='{$year}'
  182. -- TODO: acl
  183. ";
  184. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  185. $res = $db->query($sql);
  186. while ($r = $db->fetch($res)) {
  187. $plan = array();
  188. for ($i = 1; $i <= 12; $i++) {
  189. $plan[$i] = V::get("MONTH_{$i}_VALUE", 0, $r);
  190. }
  191. $this->_plan[$r->ID_PROJECT] = $plan;
  192. }
  193. return $this->_plan;
  194. }
  195. public function getCostsCategoryDBGAction() {// TODO: RMME
  196. UI::gora();
  197. $year = V::get('year', 0, $_GET, 'int');
  198. if ($year <= 0) throw new Exception("Year not set!");
  199. $costsCat = $this->_fetchCostsCategoryByYear($year);
  200. DBG::table("costsCat", $costsCat, __CLASS__, __FUNCTION__, __LINE__);
  201. /* TEST COST_TYPE for proj 0-4132-3934
  202. SELECT k.ID, k.COST_VALUE, k.INCOME_VALUE, k.COST_TYPE, k.ID_PROJECT
  203. FROM `IN7_DZIENNIK_KORESP` k
  204. WHERE k.`ID_PROJECT` =3934
  205. and k.K_DATA_OTRZYMANEJ_KORESP like '2016-01-%'
  206. */
  207. $rows = DB::getPDO()->fetchAll("
  208. SELECT k.ID
  209. , k.COST_VALUE
  210. , k.INCOME_VALUE
  211. , k.COST_TYPE
  212. , k.ID_PROJECT
  213. FROM `IN7_DZIENNIK_KORESP` k
  214. where ((k.COST_VALUE != 0) or (k.INCOME_VALUE != 0))
  215. and k.K_DATA_OTRZYMANEJ_KORESP like '{$year}-%'
  216. ORDER BY k.`ID` desc
  217. ");
  218. DBG::table("rows", $rows, __CLASS__, __FUNCTION__, __LINE__);
  219. }
  220. public function getCostsCategoryAction() {
  221. $year = V::get('year', 0, $_GET, 'int');
  222. try {
  223. if ($year <= 0) throw new Exception("Year not set!");
  224. $costsCat = $this->_fetchCostsCategoryByYear($year);
  225. Response::sendJsonExit($costsCat);
  226. } catch (Exception $e) {
  227. $response = array();
  228. $response['type'] = 'danger';
  229. $response['msg'] = $e->getMessage();
  230. Response::sendJsonExit($response);
  231. }
  232. }
  233. public function _fetchCostsCategoryByYear($year) {
  234. // TODO: mv KATEGORIA_KOSZTU (enum) to COST_TYPE (varchar(32)) in pro-netmedia.pl
  235. return DB::getPDO()->fetchAll("
  236. select k.ID
  237. , k.COST_TYPE
  238. from IN7_DZIENNIK_KORESP k
  239. where ((k.COST_VALUE != 0) or (k.INCOME_VALUE != 0))
  240. and k.K_DATA_OTRZYMANEJ_KORESP like '{$year}-%'
  241. and k.COST_TYPE != ''
  242. -- TODO: acl
  243. ");
  244. }
  245. public function _fetchCostsByYear($year) {
  246. $db = DB::getDB();
  247. $this->_costs = array();
  248. $sql = "
  249. select k.`ID`
  250. , k.`ID_PROJECT` AS `ID_PROJECT`
  251. , date_format(k.`K_DATA_OTRZYMANEJ_KORESP`,'%Y-%m') AS `MONTH`
  252. , k.`COST_VALUE` AS `COST`
  253. , k.`INCOME_VALUE` AS `INCOME`
  254. , k.`TRANSFER_OPPOSITE_ID_PROJECT`
  255. , k.`path`
  256. , IF(k.`TRANSFER_OPPOSITE_ID_PROJECT`>0
  257. , (select p.`path`
  258. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  259. where p.`ID`=k.`TRANSFER_OPPOSITE_ID_PROJECT`
  260. limit 1
  261. )
  262. , '') as TRANSFER_OPPOSITE_PROJECT_PATH
  263. , k.`K_ZAWARTOS`
  264. , k.`K_DATA_OTRZYMANEJ_KORESP`
  265. , k.`K_NR_OTRZYM_KORESP`
  266. , k.`K_OD_KOGO`
  267. from `IN7_DZIENNIK_KORESP` k
  268. where ((k.`COST_VALUE` != 0) or (k.`INCOME_VALUE` != 0))
  269. and k.`K_DATA_OTRZYMANEJ_KORESP` like '{$year}-%'
  270. -- TODO: acl
  271. ";
  272. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  273. $res = $db->query($sql);
  274. while ($r = $db->fetch($res)) {
  275. $vProjId = ($r->ID_PROJECT > 0)? $r->ID_PROJECT : 0;
  276. $vProjPaths = array();
  277. $vProjIds = array(0 => true);
  278. if ($r->ID_PROJECT > 0) {
  279. $vProjPaths[] = $r->ID_PROJECT;
  280. if (!empty($r->path)) {
  281. $vProjPaths[] = $r->path;
  282. }
  283. }
  284. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  285. $vProjPaths[] = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  286. if (!empty($r->TRANSFER_OPPOSITE_PROJECT_PATH)) {
  287. $vProjPaths[] = $r->TRANSFER_OPPOSITE_PROJECT_PATH;
  288. }
  289. }
  290. if (!empty($vProjPaths)) {
  291. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $vProjPaths = ' . json_encode($vProjPaths) . '</p>';
  292. $vProjPaths = implode('-', $vProjPaths);
  293. $projIds = explode('-', $vProjPaths);
  294. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $projIds = ' . json_encode($projIds) . '</p>';
  295. foreach ($projIds as $vProjId) {
  296. if ($vProjId > 0) $vProjIds[$vProjId] = true;
  297. }
  298. }
  299. {
  300. $projectZeroInfo = new stdClass();
  301. $projectZeroInfo->ID_PROJECT = 0;
  302. $projectZeroInfo->costsByMonth = array();
  303. $projectZeroInfo->korespByMonth = array();
  304. $this->_costs[0] = $projectZeroInfo;
  305. }
  306. foreach ($vProjIds as $vProjId => $vBool) {
  307. if (!array_key_exists($vProjId, $this->_costs)) {
  308. $projectInfo = new stdClass();
  309. $projectInfo->ID_PROJECT = $vProjId;
  310. $projectInfo->costsByMonth = array();
  311. $projectInfo->korespByMonth = array();
  312. $this->_costs[$vProjId] = $projectInfo;
  313. }
  314. }
  315. $korespInfo = new stdClass();
  316. $korespInfo->ID = $r->ID;
  317. $korespInfo->MONTH = $r->MONTH;
  318. $korespInfo->K_ZAWARTOS = $r->K_ZAWARTOS;
  319. $korespInfo->K_DATA_OTRZYMANEJ_KORESP = $r->K_DATA_OTRZYMANEJ_KORESP;
  320. $korespInfo->K_NR_OTRZYM_KORESP = $r->K_NR_OTRZYM_KORESP;
  321. $korespInfo->K_OD_KOGO = $r->K_OD_KOGO;
  322. $monthNum = intval(substr($r->MONTH, 5, 2));
  323. if ($r->ID_PROJECT > 0) {
  324. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  325. $korespOppositeInfo = clone $korespInfo;
  326. $korespInfo->COST = $r->COST;
  327. $korespInfo->INCOME = $r->INCOME;
  328. $korespInfo->TRANSFER_OPPOSITE_ID_PROJECT_TO = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  329. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  330. $korespOppositeInfo->COST = -1 * $r->COST;
  331. $korespOppositeInfo->INCOME = -1 * $r->INCOME;
  332. $korespOppositeInfo->TRANSFER_OPPOSITE_ID_PROJECT_FROM = $r->ID_PROJECT;
  333. $this->_costs[$r->TRANSFER_OPPOSITE_ID_PROJECT]->korespByMonth[$monthNum][] = $korespOppositeInfo;
  334. } else {
  335. $korespInfo->COST = $r->COST;
  336. $korespInfo->INCOME = $r->INCOME;
  337. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  338. }
  339. } else {
  340. $korespInfo->COST = $r->COST;
  341. $korespInfo->INCOME = $r->INCOME;
  342. $this->_costs[0]->korespByMonth[$monthNum][] = $korespInfo;
  343. }
  344. }
  345. return $this->_costs;
  346. }
  347. public function _fetchProjectInfo() {
  348. $db = DB::getDB();
  349. $hasAccessForAllProjects = true;
  350. $projectIds = array();
  351. $projectsFromCostIds = array_keys($this->_costs);
  352. foreach ($projectsFromCostIds as $idProject) $projectIds[$idProject] = true;
  353. $projectsFromPlanIds = array_keys($this->_plan);
  354. foreach ($projectsFromPlanIds as $idProject) $projectIds[$idProject] = true;
  355. foreach ($projectIds as $idProject => $vBool) $this->_projectInfo[$idProject] = new stdClass();
  356. $projectIds = array_keys($projectIds);
  357. $sqlProjIds = "'" . implode("','", $projectIds) . "'";
  358. $sql = "
  359. select p.`ID`
  360. , p.`P_ID`
  361. , p.`path`
  362. , p.`M_DIST_DESC`
  363. , p.`A_ADM_COMPANY` as aclGroupWrite
  364. , p.`A_CLASSIFIED` as aclGroupRead
  365. , p.`L_APPOITMENT_USER` as aclOwner
  366. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  367. where p.`ID` in({$sqlProjIds})
  368. ";
  369. $res = $db->query($sql);
  370. while ($r = $db->fetch($res)) {
  371. $this->_projectInfo[$r->ID]->path = $r->path;
  372. $this->_projectInfo[$r->ID]->M_DIST_DESC = $r->M_DIST_DESC;
  373. $this->_projectInfo[$r->ID]->aclGroupRead = $r->aclGroupRead;
  374. $this->_projectInfo[$r->ID]->hasAccess = $this->_userHasAccessToProject($r);
  375. if (!$this->_projectInfo[$r->ID]->hasAccess) $hasAccessForAllProjects = false;
  376. }
  377. $this->_projectInfo[0]->path = "0";
  378. $this->_projectInfo[0]->M_DIST_DESC = "Wszystkie projekty";
  379. $this->_projectInfo[0]->hasAccess = $hasAccessForAllProjects;
  380. // TODO: fetch every missing parent project by path
  381. }
  382. public function hasAccessToProject($idProject) {
  383. if ($idProject >= 0) {
  384. if (array_key_exists($idProject, $this->_projectInfo)) {
  385. return V::get('hasAccess', false, $this->_projectInfo[$idProject]);
  386. }
  387. }
  388. return false;
  389. }
  390. public function hasGroupsAccessToProjects($idProject, $groups) {
  391. $selectedUserGroupNames = array();
  392. $userGroups = $this->_getLdapGroupsNames();
  393. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). userGroups", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  394. foreach ($groups as $idGroup) {
  395. $selectedUserGroupNames[$idGroup] = $userGroups[$idGroup];
  396. }
  397. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). selectedUserGroupNames", $selectedUserGroupNames, __CLASS__, __FUNCTION__, __LINE__);
  398. if ($idProject >= 0) {
  399. if (array_key_exists($idProject, $this->_projectInfo)) {
  400. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). _projectInfo[$idProject]", $this->_projectInfo[$idProject], __CLASS__, __FUNCTION__, __LINE__);
  401. $alcGroupRead = V::get('aclGroupRead', null, $this->_projectInfo[$idProject]);
  402. if (!$alcGroupRead) {
  403. return false;
  404. }
  405. if (in_array($alcGroupRead, $selectedUserGroupNames)) {
  406. return true;
  407. }
  408. }
  409. }
  410. return false;
  411. }
  412. public function _userHasAccessToProject($project) {
  413. $groups = $this->_getLdapGroupsNames();
  414. $userLogin = User::getLogin();
  415. if ($project->aclOwner == $userLogin) {
  416. return true;
  417. }
  418. else if (in_array($project->aclGroupRead, $groups)) {
  419. return true;
  420. }
  421. return false;
  422. }
  423. public function getUsedUserGroups() {
  424. $groups = array();
  425. $userGroups = $this->_getLdapGroupsNames();
  426. DBG::_('DBG', '>2', "getUsedUserGroups(). userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  427. foreach ($this->_projectInfo as $projectInfo) {
  428. if (!empty($projectInfo->aclGroupRead)) {
  429. $groupKey = array_search($projectInfo->aclGroupRead, $userGroups);
  430. if ($groupKey !== false) {
  431. $groups[$groupKey] = $projectInfo->aclGroupRead;
  432. }
  433. }
  434. }
  435. DBG::_('DBG', '>2', "getUsedUserGroups(). groups:", $groups, __CLASS__, __FUNCTION__, __LINE__);
  436. return $groups;
  437. }
  438. public function _getLdapGroupsNames() {
  439. $userGroupsByZasobId = array();
  440. $usrStorageMacOSX = UserStorageFactory::getStorage('MacOSX');
  441. if (!$usrStorageMacOSX) {
  442. // throw new Exception("Error storage 'MacOSX' not exists!");
  443. $userGroups = User::getGroups();
  444. $usrStorageDB = UserStorageFactory::getStorage('DB');
  445. //DBG::_(true, true, "userGroups", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  446. $rows = DB::getPDO()->fetchAll("
  447. select distinct a.perm_group_uid
  448. from (
  449. SELECT DISTINCT `A_ADM_COMPANY` as perm_group_uid FROM `IN7_MK_BAZA_DYSTRYBUCJI`
  450. union
  451. SELECT DISTINCT `A_CLASSIFIED` as perm_group_uid FROM `IN7_MK_BAZA_DYSTRYBUCJI`
  452. union
  453. SELECT DISTINCT `A_ADM_COMPANY` as perm_group_uid FROM `IN7_DZIENNIK_KORESP`
  454. union
  455. SELECT DISTINCT `A_CLASSIFIED` as perm_group_uid FROM `IN7_DZIENNIK_KORESP`
  456. ) as a
  457. ");
  458. //DBG::_(true, true, "rows", $rows, __CLASS__, __FUNCTION__, __LINE__);
  459. DBG::_('DBG', '>3', "userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  460. $parentGroups = array();
  461. foreach ($userGroups as $idGroup => $group) {
  462. $parentGroups = $usrStorageDB->fetchParentGroups($idGroup);
  463. foreach ($parentGroups as $idParentGroup => $parentGroup) {
  464. $parentGroups[$idParentGroup] = true;
  465. }
  466. }
  467. DBG::_('DBG', '>3', "parentGroups:", $parentGroups, __CLASS__, __FUNCTION__, __LINE__);
  468. $grandParentGroups = array();
  469. foreach ($parentGroups as $idGroup => $group) {
  470. $parentGroups = $usrStorageDB->fetchParentGroups($idGroup);
  471. foreach ($parentGroups as $idParentGroup => $parentGroup) {
  472. $grandParentGroups[$idParentGroup] = true;
  473. }
  474. }
  475. DBG::_('DBG', '>3', "grandParentGroups:", $grandParentGroups, __CLASS__, __FUNCTION__, __LINE__);
  476. $userGroupIdsAll = array();
  477. foreach ($userGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  478. foreach ($parentGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  479. foreach ($grandParentGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  480. DBG::_('DBG', '>3', "userGroupIdsAll:", $userGroupIdsAll, __CLASS__, __FUNCTION__, __LINE__);
  481. foreach ($rows as $row) {
  482. $perm_group_uid = $row['perm_group_uid'];
  483. if (empty($perm_group_uid)) continue;
  484. $parts = explode('_', $perm_group_uid, 2);
  485. DBG::_('DBG', '>3', "loop - $perm_group_uid:", $parts, __CLASS__, __FUNCTION__, __LINE__);
  486. $idGroup = $parts[0];
  487. DBG::_('DBG', '>3', "loop - $perm_group_uid - idGroup({$idGroup}):", array_key_exists($idGroup, $userGroups), __CLASS__, __FUNCTION__, __LINE__);
  488. if (array_key_exists($idGroup, $userGroupIdsAll)) {
  489. $userGroupsByZasobId[$idGroup] = $perm_group_uid;
  490. }
  491. }
  492. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroupsByZasobId:", $userGroupsByZasobId, __CLASS__, __FUNCTION__, __LINE__);
  493. } else {
  494. $userGroups = User::getLdapGroupsNames();
  495. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  496. foreach ($userGroups as $uidGroup) {
  497. $idZasob = $usrStorageMacOSX->getGroupIdFromUid($uidGroup);
  498. if ($idZasob) {
  499. $userGroupsByZasobId[$idZasob] = $uidGroup;
  500. } else {
  501. //$userGroupsByZasobId[$uidGroup] = $uidGroup;
  502. }
  503. }
  504. }
  505. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroupsByZasobId:", $userGroupsByZasobId, __CLASS__, __FUNCTION__, __LINE__);
  506. return $userGroupsByZasobId;
  507. }
  508. public function _reacountCostsFromKoresp() {
  509. $projHasCostSelfIds = array();
  510. foreach ($this->_costs as $kProjId => $vProjInfo) {
  511. $projectPath = $this->_projectInfo[$kProjId]->path;
  512. foreach ($vProjInfo->korespByMonth as $kMonthNum => $vKorespList) {
  513. $this->_createCostIfNotDefined($kProjId, $kMonthNum);
  514. foreach ($vKorespList as $vKoresp) {
  515. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF += $vKoresp->COST;
  516. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF += $vKoresp->INCOME;
  517. }
  518. $projHasCostSelfIds[$kProjId][$kMonthNum] = $projectPath;
  519. }
  520. }
  521. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">$projHasCostSelfIds: ';print_r($projHasCostSelfIds);echo'</pre>';
  522. foreach ($projHasCostSelfIds as $kProjId => $vProjMonthsList) {
  523. if ($kProjId <= 0) continue;
  524. foreach ($vProjMonthsList as $kMonthNum => $vProjPath) {
  525. $vProjPathIds = explode('-', $vProjPath);
  526. $vProjPathIds = array_reverse($vProjPathIds);
  527. $vProjMonthCostSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF;
  528. $vProjMonthIncomeSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF;
  529. foreach ($vProjPathIds as $vProjId) {
  530. if ($vProjId == $kProjId) continue;
  531. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  532. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_CHILD += $vProjMonthCostSelf;
  533. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_CHILD += $vProjMonthIncomeSelf;
  534. }
  535. }
  536. }
  537. // recount total
  538. foreach ($this->_costs as $vProjId => $vProjInfo) {
  539. foreach ($vProjInfo->costsByMonth as $kMonthNum => $vCost) {
  540. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  541. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_TOTAL = $vCost->COST_SELF + $vCost->COST_CHILD;
  542. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_TOTAL = $vCost->INCOME_SELF + $vCost->INCOME_CHILD;
  543. }
  544. }
  545. }
  546. public function _createCostIfNotDefined($projId, $monthNum) {
  547. if (empty($this->_costs[$projId]->costsByMonth[$monthNum])) {
  548. $vEmptyCost = new stdClass();
  549. $vEmptyCost->COST_SELF = 0;
  550. $vEmptyCost->INCOME_SELF = 0;
  551. $vEmptyCost->COST_CHILD = 0;
  552. $vEmptyCost->INCOME_CHILD = 0;
  553. $vEmptyCost->COST_TOTAL = 0;
  554. $vEmptyCost->INCOME_TOTAL = 0;
  555. $this->_costs[$projId]->costsByMonth[$monthNum] = $vEmptyCost;
  556. }
  557. }
  558. public function _buildProjectTree() {
  559. $this->_projectPathsOrder = array();
  560. foreach ($this->_projectInfo as $idProject => $projectInfo) {
  561. $this->_projectPathsOrder[$projectInfo->path] = $idProject;
  562. }
  563. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths: ';print_r($this->_projectPathsOrder);echo'</pre>';
  564. uksort($this->_projectPathsOrder, array($this, 'sortPathsCallback'));
  565. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths sorted: ';print_r($this->_projectPathsOrder);echo'</pre>';
  566. return $this->_projectPathsOrder;
  567. }
  568. public function sortPathsCallback($a, $b) {
  569. $ea = explode('-', $a);
  570. $eb = explode('-', $b);
  571. $la = count($ea);
  572. $lb = count($eb);
  573. $lmin = min($la, $lb);
  574. for ($i = 0; $i < $lmin; $i++) {
  575. if ($ea[$i] < $eb[$i]) {
  576. return -1;
  577. } else if ($ea[$i] > $eb[$i]) {
  578. return 1;
  579. }
  580. }
  581. return $la - $lb;
  582. }
  583. public function updatePaths() {
  584. $sqlList = array();
  585. $sqlList['updateAllPaths'] = <<<SQL
  586. update `projects_budget_year_month` b
  587. set path = (select coalesce(
  588. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=b.`ID_PROJECT` limit 1)
  589. , '?'));
  590. SQL;
  591. $db = DB::getDB();
  592. if ($db->has_errors()) {
  593. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  594. }
  595. foreach ($sqlList as $sqlName => $sql) {
  596. $res = $db->query($sql);
  597. if ($db->has_errors()) {
  598. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  599. }
  600. }
  601. }
  602. public function reinstall() {
  603. $sqlList = array();
  604. $sqlList['RemoveTrigger_BudgetPlan_BeforeInsert'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_INSERT`";
  605. $sqlList['CreateTrigger_BudgetPlan_BeforeInsert'] = "
  606. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_INSERT` BEFORE INSERT ON `projects_budget_year_month`
  607. FOR EACH ROW BEGIN
  608. IF NEW.ID_PROJECT IS NOT NULL and NEW.ID_PROJECT>0 THEN
  609. SET NEW.path = (select coalesce(
  610. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  611. , '?'
  612. ));
  613. END IF;
  614. END
  615. ";
  616. $sqlList['RemoveTrigger_BudgetPlan_BeforeUpdate'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_UPDATE `";
  617. // throws errors:
  618. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected ID=PARENT_ID' doesn't exist
  619. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Parent item not exists' doesn't exist
  620. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected in path' doesn't exist
  621. $sqlList['CreateTrigger_BudgetPlan_BeforeUpdate'] = "
  622. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_UPDATE` BEFORE UPDATE ON `projects_budget_year_month`
  623. FOR EACH ROW BEGIN
  624. IF NEW.ID_PROJECT IS NULL THEN
  625. SET NEW.path = '';
  626. ELSEIF OLD.ID_PROJECT IS NULL or NEW.ID_PROJECT<>OLD.ID_PROJECT THEN
  627. IF NEW.ID_PROJECT>0 THEN
  628. SET NEW.path = (select coalesce(
  629. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  630. , '?'));
  631. ELSE
  632. SET NEW.path = '';
  633. END IF;
  634. END IF;
  635. END
  636. ";
  637. $db = DB::getDB();
  638. if ($db->has_errors()) {
  639. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  640. }
  641. foreach ($sqlList as $sqlName => $sql) {
  642. $res = $db->query($sql);
  643. if ($db->has_errors()) {
  644. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  645. }
  646. }
  647. }
  648. public function planAction() {
  649. UI::gora();
  650. UI::menu();
  651. $args = array();
  652. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  653. if ($args['year'] > 0) {
  654. $_REQUEST['ff_YEAR'] = $_GET['ff_YEAR'] = $args['year'];
  655. }
  656. $this->printMenu($args['year']);
  657. if ($args['year'] > 0) {
  658. UI::startContainer();
  659. UI::tag('a', ['class'=>"btn btn-xs btn-default", 'href'=>"#"], "Utwórz plan na kolejny rok na podstawie danych z {$args['year']} roku");
  660. UI::endContainer();
  661. }
  662. $zasobObj = ProcesHelper::getZasobTableInfoByUri('default_db/projects_budget_year_month');
  663. if (!$zasobObj) {
  664. UI::alert('danger', "Zasob Tabela Plan budżetu (projects_budget_year_month) nie istnieje");
  665. // TODO: btn utwórz
  666. UI::dol();
  667. return;
  668. }
  669. $userAcl = User::getAcl();
  670. $userAcl->fetchGroups();
  671. if (!$userAcl->hasTableAcl($zasobObj->ID)) {
  672. UI::alert('danger', "Brak uprawnień do tabeli Plan budżetu (projects_budget_year_month)");
  673. UI::dol();
  674. return;
  675. }
  676. $tblAcl = $userAcl->getTableAcl($zasobObj->ID);
  677. $forceTblAclInit = ('1' == V::get('_force', '', $_GET));
  678. $tblAcl->init($forceTblAclInit);
  679. $forceFilterInit = array();
  680. $filterInit = new stdClass();
  681. $filterInit->currSortCol = 'ID';
  682. $filterInit->currSortFlip = 'desc';
  683. foreach ($_GET as $k => $v) {
  684. if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && !empty($v)) {// filter prefix
  685. $filterInit->$k = $v;
  686. }
  687. else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && !empty($v)) {// special filter prefix
  688. $filterInit->$k = $v;
  689. }
  690. else if (strlen($k) > 4 && substr($k, 0, 3) == 'ff_' && !empty($v)) {// force filter prefix
  691. $fldName = substr($k, 3);
  692. $forceFilterInit[$fldName] = $v;
  693. }
  694. }
  695. $syncUrl = Request::getPathUri() . 'index.php?_route=ViewTableAjax&namespace=' . $tblAcl->getNamespace();
  696. $tbl = new TableAjax($tblAcl);
  697. $tbl->setSyncUrl($syncUrl);
  698. $tblLabel = array();
  699. if (!empty($zasobObj->DESC_PL)) $tblLabel []= $zasobObj->DESC_PL;
  700. if (!empty($zasobObj->OPIS)) $tblLabel []= $zasobObj->OPIS;
  701. $tblLabel = implode(" - ", $tblLabel);
  702. $tbl->setLabel($tblLabel);
  703. $tbl->setFilterInit($filterInit);
  704. if (!empty($forceFilterInit)) $tbl->setForceFilterInit($forceFilterInit);
  705. $tbl->addRowFunction('edit');
  706. $tbl->addRowFunction('hist');
  707. $tbl->addRowFunction('files');
  708. $tbl->addRowFunction('cp');
  709. echo $tbl->render();
  710. UI::dol();
  711. }
  712. }