Budget.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  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->menu($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->menu($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 menu($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 css() {
  102. ?>
  103. <style type="text/css">
  104. .c { text-align:center; }
  105. .r { text-align:right; }
  106. .zestawienie-kosztow-tbl { border-collapse:collapse; border:1px solid #aaa; }
  107. .zestawienie-kosztow-tbl td { border:1px solid #aaa; }
  108. .zestawienie-kosztow-tbl .p2 { padding:0 2px; }
  109. .zestawienie-kosztow-tbl .nr { color:#7A7A7A; }
  110. .zestawienie-kosztow-tbl thead th { border:1px solid #aaa; }
  111. .zestawienie-kosztow-tbl tbody tr:hover td { background:#cafbfd; }
  112. .row-selected td {background-color:#d8fded;}
  113. .showOnlySelected tr { display:none; }
  114. .showOnlySelected tr.row-selected { display:table-row; }
  115. .cell-cost { padding:0 2px; min-width:30px; text-align:right; }
  116. .cell-cost-only_self { color:#197fe6; }
  117. .cell-cost-self_and_child { color:#33b2cc; }
  118. .cell-cost-only_child { color:#59a680; }
  119. .cell-plan { padding:0 2px; min-width:30px; text-align:right; color:#777; }
  120. .cell-procent { padding:0 2px; min-width:20px; text-align:right; color:#777; }
  121. .cell-procent-below100 { color:#777; }
  122. .cell-procent-100 { color:#777; }
  123. .cell-procent-over100 { color:#ff9b00; }
  124. .cell-procent-over150 { color:#f00; }
  125. /* print table background colors */
  126. table td, table th { -webkit-print-color-adjust:exact; }
  127. table { page-break-after:auto }
  128. tr { page-break-inside:avoid; page-break-after:auto; position:relative; }
  129. td { page-break-inside:avoid; page-break-after:auto; position:relative; }
  130. thead { display:table-header-group }
  131. tfoot { display:table-footer-group }
  132. #zestawienie-kosztow-projektow.hidden_month_1 .col_month_1 {display:none}
  133. #zestawienie-kosztow-projektow.hidden_month_2 .col_month_2 {display:none}
  134. #zestawienie-kosztow-projektow.hidden_month_3 .col_month_3 {display:none}
  135. #zestawienie-kosztow-projektow.hidden_month_4 .col_month_4 {display:none}
  136. #zestawienie-kosztow-projektow.hidden_month_5 .col_month_5 {display:none}
  137. #zestawienie-kosztow-projektow.hidden_month_6 .col_month_6 {display:none}
  138. #zestawienie-kosztow-projektow.hidden_month_7 .col_month_7 {display:none}
  139. #zestawienie-kosztow-projektow.hidden_month_8 .col_month_8 {display:none}
  140. #zestawienie-kosztow-projektow.hidden_month_9 .col_month_9 {display:none}
  141. #zestawienie-kosztow-projektow.hidden_month_10 .col_month_10 {display:none}
  142. #zestawienie-kosztow-projektow.hidden_month_11 .col_month_11 {display:none}
  143. #zestawienie-kosztow-projektow.hidden_month_12 .col_month_12 {display:none}
  144. .thead__cols_summary,
  145. .row__summary__cost,
  146. .row__summary__plan {background-color:#fcf8e3;}
  147. .thead_col_month .col_month_remove { opacity:0.2; }
  148. .thead_col_month:hover .col_month_remove { opacity:0.6; }
  149. </style>
  150. <?php
  151. }
  152. function printCostsForYear($year, $groups) {
  153. $months = array();
  154. for ($i = 0; $i < 12; $i++) {
  155. $months[] = $i + 1;
  156. }
  157. $this->css();
  158. //DBG::_('DBG', '>1', "costs", $this->_costs, __CLASS__, __FUNCTION__, __LINE__);
  159. //echo'<pre>' . json_encode(array_keys($this->_projectPathsOrder)) . '</pre>';
  160. //echo'<pre>' . json_encode($this->_projectPathsOrder) . '</pre>';
  161. //echo'<pre>' . json_encode($this->_projectInfo) . '</pre>';
  162. //echo'<pre>' . json_encode($this->_costs) . '</pre>';
  163. //echo'<pre>' . json_encode($this->_plan) . '</pre>';
  164. $projectPathsOrder = array_keys($this->_projectPathsOrder);
  165. foreach ($projectPathsOrder as $key => $value) {
  166. $projectPathsOrder[$key] = "" . $value;
  167. }
  168. UI::tag('div', ['id' => "widget-budget"]);
  169. UI::tag('script', ['src'=>"stuff/vendors.js"]);
  170. UI::tag('script', ['src'=>"stuff/bundle.se_route_budget.js?v=2.1"]);
  171. UI::inlineJS(__FILE__ . '.yearBudget.js', [
  172. 'year' => $year,
  173. 'today' => date('Y-m-d'),
  174. 'projectPathsOrder' => $projectPathsOrder,
  175. 'projectPathsMap' => $this->_projectPathsOrder,
  176. 'projectInfo' => $this->_projectInfo,
  177. 'costs' => $this->_costs,
  178. 'plan' => $this->_plan,
  179. 'dbg' => false,
  180. ]);
  181. }
  182. public function getCost($idProject, $month) {
  183. if (!array_key_exists($idProject, $this->_costs)) {
  184. return null;
  185. }
  186. if (!array_key_exists($month, $this->_costs[$idProject]->costsByMonth)) {
  187. return null;
  188. }
  189. return $this->_costs[$idProject]->costsByMonth[$month];
  190. }
  191. public function getPlan($idProject, $month) {
  192. if (!array_key_exists($idProject, $this->_plan)) {
  193. return 0;
  194. }
  195. if (!array_key_exists($month, $this->_plan[$idProject])) {
  196. return 0;
  197. }
  198. return $this->_plan[$idProject][$month];
  199. }
  200. public function fetchDataByYear($year, $groups) {
  201. $this->_fetchCostsByYear($year);
  202. $this->_fetchPlanByYear($year);
  203. $this->_fetchProjectInfo();
  204. $this->_buildProjectTree();
  205. $this->_reacountCostsFromKoresp();
  206. $this->_filterProjectsByGroups($groups);
  207. return count($this->_projectInfo) > 1;// $this->_projectInfo[0] - Wszystkie projekty
  208. }
  209. public function _filterProjectsByGroups($groups) {
  210. if (!empty($groups)) {
  211. foreach ($this->_projectInfo as $idProject => $projInfo) {
  212. if (!$this->hasGroupsAccessToProjects($idProject, $groups)) {
  213. $this->_projectInfo[$idProject]->filteredByGroups = true;
  214. }
  215. }
  216. }
  217. }
  218. public function _fetchPlanByYear($year) {
  219. $db = DB::getDB();
  220. $this->_plan = array();
  221. $sql = "
  222. select plan.`ID`
  223. , plan.`ID_PROJECT` AS `ID_PROJECT`
  224. , plan.`MONTH_1_VALUE`
  225. , plan.`MONTH_2_VALUE`
  226. , plan.`MONTH_3_VALUE`
  227. , plan.`MONTH_4_VALUE`
  228. , plan.`MONTH_5_VALUE`
  229. , plan.`MONTH_6_VALUE`
  230. , plan.`MONTH_7_VALUE`
  231. , plan.`MONTH_8_VALUE`
  232. , plan.`MONTH_9_VALUE`
  233. , plan.`MONTH_10_VALUE`
  234. , plan.`MONTH_11_VALUE`
  235. , plan.`MONTH_12_VALUE`
  236. from `projects_budget_year_month` plan
  237. where plan.`year`='{$year}'
  238. -- TODO: acl
  239. ";
  240. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  241. $res = $db->query($sql);
  242. while ($r = $db->fetch($res)) {
  243. $plan = array();
  244. for ($i = 1; $i <= 12; $i++) {
  245. $plan[$i] = V::get("MONTH_{$i}_VALUE", 0, $r);
  246. }
  247. $this->_plan[$r->ID_PROJECT] = $plan;
  248. }
  249. return $this->_plan;
  250. }
  251. public function getCostsCategoryDBGAction() {// TODO: RMME
  252. UI::gora();
  253. $year = V::get('year', 0, $_GET, 'int');
  254. if ($year <= 0) throw new Exception("Year not set!");
  255. $costsCat = $this->_fetchCostsCategoryByYear($year);
  256. DBG::table("costsCat", $costsCat, __CLASS__, __FUNCTION__, __LINE__);
  257. /* TEST COST_TYPE for proj 0-4132-3934
  258. SELECT k.ID, k.COST_VALUE, k.INCOME_VALUE, k.COST_TYPE, k.ID_PROJECT
  259. FROM `IN7_DZIENNIK_KORESP` k
  260. WHERE k.`ID_PROJECT` =3934
  261. and k.K_DATA_OTRZYMANEJ_KORESP like '2016-01-%'
  262. */
  263. $rows = DB::getPDO()->fetchAll("
  264. SELECT k.ID
  265. , k.COST_VALUE
  266. , k.INCOME_VALUE
  267. , k.COST_TYPE
  268. , k.ID_PROJECT
  269. FROM `IN7_DZIENNIK_KORESP` k
  270. where ((k.COST_VALUE != 0) or (k.INCOME_VALUE != 0))
  271. and k.K_DATA_OTRZYMANEJ_KORESP like '{$year}-%'
  272. ORDER BY k.`ID` desc
  273. ");
  274. DBG::table("rows", $rows, __CLASS__, __FUNCTION__, __LINE__);
  275. }
  276. public function getCostsCategoryAction() {
  277. $year = V::get('year', 0, $_GET, 'int');
  278. try {
  279. if ($year <= 0) throw new Exception("Year not set!");
  280. $costsCat = $this->_fetchCostsCategoryByYear($year);
  281. Response::sendJsonExit($costsCat);
  282. } catch (Exception $e) {
  283. $response = array();
  284. $response['type'] = 'danger';
  285. $response['msg'] = $e->getMessage();
  286. Response::sendJsonExit($response);
  287. }
  288. }
  289. public function _fetchCostsCategoryByYear($year) {
  290. // TODO: mv KATEGORIA_KOSZTU (enum) to COST_TYPE (varchar(32)) in pro-netmedia.pl
  291. return DB::getPDO()->fetchAll("
  292. select k.ID
  293. , k.COST_TYPE
  294. from IN7_DZIENNIK_KORESP k
  295. where ((k.COST_VALUE != 0) or (k.INCOME_VALUE != 0))
  296. and k.K_DATA_OTRZYMANEJ_KORESP like '{$year}-%'
  297. and k.COST_TYPE != ''
  298. -- TODO: acl
  299. ");
  300. }
  301. public function _fetchCostsByYear($year) {
  302. $db = DB::getDB();
  303. $this->_costs = array();
  304. $sql = "
  305. select k.`ID`
  306. , k.`ID_PROJECT` AS `ID_PROJECT`
  307. , date_format(k.`K_DATA_OTRZYMANEJ_KORESP`,'%Y-%m') AS `MONTH`
  308. , k.`COST_VALUE` AS `COST`
  309. , k.`INCOME_VALUE` AS `INCOME`
  310. , k.`TRANSFER_OPPOSITE_ID_PROJECT`
  311. , k.`path`
  312. , IF(k.`TRANSFER_OPPOSITE_ID_PROJECT`>0
  313. , (select p.`path`
  314. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  315. where p.`ID`=k.`TRANSFER_OPPOSITE_ID_PROJECT`
  316. limit 1
  317. )
  318. , '') as TRANSFER_OPPOSITE_PROJECT_PATH
  319. , k.`K_ZAWARTOS`
  320. , k.`K_DATA_OTRZYMANEJ_KORESP`
  321. , k.`K_NR_OTRZYM_KORESP`
  322. , k.`K_OD_KOGO`
  323. from `IN7_DZIENNIK_KORESP` k
  324. where ((k.`COST_VALUE` != 0) or (k.`INCOME_VALUE` != 0))
  325. and k.`K_DATA_OTRZYMANEJ_KORESP` like '{$year}-%'
  326. -- TODO: acl
  327. ";
  328. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  329. $res = $db->query($sql);
  330. while ($r = $db->fetch($res)) {
  331. $vProjId = ($r->ID_PROJECT > 0)? $r->ID_PROJECT : 0;
  332. $vProjPaths = array();
  333. $vProjIds = array(0 => true);
  334. if ($r->ID_PROJECT > 0) {
  335. $vProjPaths[] = $r->ID_PROJECT;
  336. if (!empty($r->path)) {
  337. $vProjPaths[] = $r->path;
  338. }
  339. }
  340. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  341. $vProjPaths[] = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  342. if (!empty($r->TRANSFER_OPPOSITE_PROJECT_PATH)) {
  343. $vProjPaths[] = $r->TRANSFER_OPPOSITE_PROJECT_PATH;
  344. }
  345. }
  346. if (!empty($vProjPaths)) {
  347. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $vProjPaths = ' . json_encode($vProjPaths) . '</p>';
  348. $vProjPaths = implode('-', $vProjPaths);
  349. $projIds = explode('-', $vProjPaths);
  350. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $projIds = ' . json_encode($projIds) . '</p>';
  351. foreach ($projIds as $vProjId) {
  352. if ($vProjId > 0) $vProjIds[$vProjId] = true;
  353. }
  354. }
  355. {
  356. $projectZeroInfo = new stdClass();
  357. $projectZeroInfo->ID_PROJECT = 0;
  358. $projectZeroInfo->costsByMonth = array();
  359. $projectZeroInfo->korespByMonth = array();
  360. $this->_costs[0] = $projectZeroInfo;
  361. }
  362. foreach ($vProjIds as $vProjId => $vBool) {
  363. if (!array_key_exists($vProjId, $this->_costs)) {
  364. $projectInfo = new stdClass();
  365. $projectInfo->ID_PROJECT = $vProjId;
  366. $projectInfo->costsByMonth = array();
  367. $projectInfo->korespByMonth = array();
  368. $this->_costs[$vProjId] = $projectInfo;
  369. }
  370. }
  371. $korespInfo = new stdClass();
  372. $korespInfo->ID = $r->ID;
  373. $korespInfo->MONTH = $r->MONTH;
  374. $korespInfo->K_ZAWARTOS = $r->K_ZAWARTOS;
  375. $korespInfo->K_DATA_OTRZYMANEJ_KORESP = $r->K_DATA_OTRZYMANEJ_KORESP;
  376. $korespInfo->K_NR_OTRZYM_KORESP = $r->K_NR_OTRZYM_KORESP;
  377. $korespInfo->K_OD_KOGO = $r->K_OD_KOGO;
  378. $monthNum = intval(substr($r->MONTH, 5, 2));
  379. if ($r->ID_PROJECT > 0) {
  380. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  381. $korespOppositeInfo = clone $korespInfo;
  382. $korespInfo->COST = $r->COST;
  383. $korespInfo->INCOME = $r->INCOME;
  384. $korespInfo->TRANSFER_OPPOSITE_ID_PROJECT_TO = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  385. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  386. $korespOppositeInfo->COST = -1 * $r->COST;
  387. $korespOppositeInfo->INCOME = -1 * $r->INCOME;
  388. $korespOppositeInfo->TRANSFER_OPPOSITE_ID_PROJECT_FROM = $r->ID_PROJECT;
  389. $this->_costs[$r->TRANSFER_OPPOSITE_ID_PROJECT]->korespByMonth[$monthNum][] = $korespOppositeInfo;
  390. } else {
  391. $korespInfo->COST = $r->COST;
  392. $korespInfo->INCOME = $r->INCOME;
  393. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  394. }
  395. } else {
  396. $korespInfo->COST = $r->COST;
  397. $korespInfo->INCOME = $r->INCOME;
  398. $this->_costs[0]->korespByMonth[$monthNum][] = $korespInfo;
  399. }
  400. }
  401. return $this->_costs;
  402. }
  403. public function _fetchProjectInfo() {
  404. $db = DB::getDB();
  405. $hasAccessForAllProjects = true;
  406. $projectIds = array();
  407. $projectsFromCostIds = array_keys($this->_costs);
  408. foreach ($projectsFromCostIds as $idProject) $projectIds[$idProject] = true;
  409. $projectsFromPlanIds = array_keys($this->_plan);
  410. foreach ($projectsFromPlanIds as $idProject) $projectIds[$idProject] = true;
  411. foreach ($projectIds as $idProject => $vBool) $this->_projectInfo[$idProject] = new stdClass();
  412. $projectIds = array_keys($projectIds);
  413. $sqlProjIds = "'" . implode("','", $projectIds) . "'";
  414. $sql = "
  415. select p.`ID`
  416. , p.`P_ID`
  417. , p.`path`
  418. , p.`M_DIST_DESC`
  419. , p.`A_ADM_COMPANY` as aclGroupWrite
  420. , p.`A_CLASSIFIED` as aclGroupRead
  421. , p.`L_APPOITMENT_USER` as aclOwner
  422. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  423. where p.`ID` in({$sqlProjIds})
  424. ";
  425. $res = $db->query($sql);
  426. while ($r = $db->fetch($res)) {
  427. $this->_projectInfo[$r->ID]->path = $r->path;
  428. $this->_projectInfo[$r->ID]->M_DIST_DESC = $r->M_DIST_DESC;
  429. $this->_projectInfo[$r->ID]->aclGroupRead = $r->aclGroupRead;
  430. $this->_projectInfo[$r->ID]->hasAccess = $this->_userHasAccessToProject($r);
  431. if (!$this->_projectInfo[$r->ID]->hasAccess) $hasAccessForAllProjects = false;
  432. }
  433. $this->_projectInfo[0]->path = "0";
  434. $this->_projectInfo[0]->M_DIST_DESC = "Wszystkie projekty";
  435. $this->_projectInfo[0]->hasAccess = $hasAccessForAllProjects;
  436. // TODO: fetch every missing parent project by path
  437. }
  438. public function hasAccessToProject($idProject) {
  439. if ($idProject >= 0) {
  440. if (array_key_exists($idProject, $this->_projectInfo)) {
  441. return V::get('hasAccess', false, $this->_projectInfo[$idProject]);
  442. }
  443. }
  444. return false;
  445. }
  446. public function hasGroupsAccessToProjects($idProject, $groups) {
  447. $selectedUserGroupNames = array();
  448. $userGroups = $this->_getLdapGroupsNames();
  449. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). userGroups", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  450. foreach ($groups as $idGroup) {
  451. $selectedUserGroupNames[$idGroup] = $userGroups[$idGroup];
  452. }
  453. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). selectedUserGroupNames", $selectedUserGroupNames, __CLASS__, __FUNCTION__, __LINE__);
  454. if ($idProject >= 0) {
  455. if (array_key_exists($idProject, $this->_projectInfo)) {
  456. DBG::_('DBG', '>1', "hasGroupsAccessToProjects({$idProject}). _projectInfo[$idProject]", $this->_projectInfo[$idProject], __CLASS__, __FUNCTION__, __LINE__);
  457. $alcGroupRead = V::get('aclGroupRead', null, $this->_projectInfo[$idProject]);
  458. if (!$alcGroupRead) {
  459. return false;
  460. }
  461. if (in_array($alcGroupRead, $selectedUserGroupNames)) {
  462. return true;
  463. }
  464. }
  465. }
  466. return false;
  467. }
  468. public function _userHasAccessToProject($project) {
  469. $groups = $this->_getLdapGroupsNames();
  470. $userLogin = User::getLogin();
  471. if ($project->aclOwner == $userLogin) {
  472. return true;
  473. }
  474. else if (in_array($project->aclGroupRead, $groups)) {
  475. return true;
  476. }
  477. return false;
  478. }
  479. public function getUsedUserGroups() {
  480. $groups = array();
  481. $userGroups = $this->_getLdapGroupsNames();
  482. DBG::_('DBG', '>2', "getUsedUserGroups(). userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  483. foreach ($this->_projectInfo as $projectInfo) {
  484. if (!empty($projectInfo->aclGroupRead)) {
  485. $groupKey = array_search($projectInfo->aclGroupRead, $userGroups);
  486. if ($groupKey !== false) {
  487. $groups[$groupKey] = $projectInfo->aclGroupRead;
  488. }
  489. }
  490. }
  491. DBG::_('DBG', '>2', "getUsedUserGroups(). groups:", $groups, __CLASS__, __FUNCTION__, __LINE__);
  492. return $groups;
  493. }
  494. public function _getLdapGroupsNames() {
  495. $userGroupsByZasobId = array();
  496. $usrStorageMacOSX = UserStorageFactory::getStorage('MacOSX');
  497. if (!$usrStorageMacOSX) {
  498. // throw new Exception("Error storage 'MacOSX' not exists!");
  499. $userGroups = User::getGroups();
  500. $usrStorageDB = UserStorageFactory::getStorage('DB');
  501. //DBG::_(true, true, "userGroups", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  502. $rows = DB::getPDO()->fetchAll("
  503. select distinct a.perm_group_uid
  504. from (
  505. SELECT DISTINCT `A_ADM_COMPANY` as perm_group_uid FROM `IN7_MK_BAZA_DYSTRYBUCJI`
  506. union
  507. SELECT DISTINCT `A_CLASSIFIED` as perm_group_uid FROM `IN7_MK_BAZA_DYSTRYBUCJI`
  508. union
  509. SELECT DISTINCT `A_ADM_COMPANY` as perm_group_uid FROM `IN7_DZIENNIK_KORESP`
  510. union
  511. SELECT DISTINCT `A_CLASSIFIED` as perm_group_uid FROM `IN7_DZIENNIK_KORESP`
  512. ) as a
  513. ");
  514. //DBG::_(true, true, "rows", $rows, __CLASS__, __FUNCTION__, __LINE__);
  515. DBG::_('DBG', '>3', "userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  516. $parentGroups = array();
  517. foreach ($userGroups as $idGroup => $group) {
  518. $parentGroups = $usrStorageDB->fetchParentGroups($idGroup);
  519. foreach ($parentGroups as $idParentGroup => $parentGroup) {
  520. $parentGroups[$idParentGroup] = true;
  521. }
  522. }
  523. DBG::_('DBG', '>3', "parentGroups:", $parentGroups, __CLASS__, __FUNCTION__, __LINE__);
  524. $grandParentGroups = array();
  525. foreach ($parentGroups as $idGroup => $group) {
  526. $parentGroups = $usrStorageDB->fetchParentGroups($idGroup);
  527. foreach ($parentGroups as $idParentGroup => $parentGroup) {
  528. $grandParentGroups[$idParentGroup] = true;
  529. }
  530. }
  531. DBG::_('DBG', '>3', "grandParentGroups:", $grandParentGroups, __CLASS__, __FUNCTION__, __LINE__);
  532. $userGroupIdsAll = array();
  533. foreach ($userGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  534. foreach ($parentGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  535. foreach ($grandParentGroups as $idGroup => $group) $userGroupIdsAll[$idGroup] = true;
  536. DBG::_('DBG', '>3', "userGroupIdsAll:", $userGroupIdsAll, __CLASS__, __FUNCTION__, __LINE__);
  537. foreach ($rows as $row) {
  538. $perm_group_uid = $row['perm_group_uid'];
  539. if (empty($perm_group_uid)) continue;
  540. $parts = explode('_', $perm_group_uid, 2);
  541. DBG::_('DBG', '>3', "loop - $perm_group_uid:", $parts, __CLASS__, __FUNCTION__, __LINE__);
  542. $idGroup = $parts[0];
  543. DBG::_('DBG', '>3', "loop - $perm_group_uid - idGroup({$idGroup}):", array_key_exists($idGroup, $userGroups), __CLASS__, __FUNCTION__, __LINE__);
  544. if (array_key_exists($idGroup, $userGroupIdsAll)) {
  545. $userGroupsByZasobId[$idGroup] = $perm_group_uid;
  546. }
  547. }
  548. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroupsByZasobId:", $userGroupsByZasobId, __CLASS__, __FUNCTION__, __LINE__);
  549. } else {
  550. $userGroups = User::getLdapGroupsNames();
  551. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroups:", $userGroups, __CLASS__, __FUNCTION__, __LINE__);
  552. foreach ($userGroups as $uidGroup) {
  553. $idZasob = $usrStorageMacOSX->getGroupIdFromUid($uidGroup);
  554. if ($idZasob) {
  555. $userGroupsByZasobId[$idZasob] = $uidGroup;
  556. } else {
  557. //$userGroupsByZasobId[$uidGroup] = $uidGroup;
  558. }
  559. }
  560. }
  561. DBG::_('DBG', '>3', "_getLdapGroupsNames(). userGroupsByZasobId:", $userGroupsByZasobId, __CLASS__, __FUNCTION__, __LINE__);
  562. return $userGroupsByZasobId;
  563. }
  564. public function _reacountCostsFromKoresp() {
  565. $projHasCostSelfIds = array();
  566. foreach ($this->_costs as $kProjId => $vProjInfo) {
  567. $projectPath = $this->_projectInfo[$kProjId]->path;
  568. foreach ($vProjInfo->korespByMonth as $kMonthNum => $vKorespList) {
  569. $this->_createCostIfNotDefined($kProjId, $kMonthNum);
  570. foreach ($vKorespList as $vKoresp) {
  571. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF += $vKoresp->COST;
  572. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF += $vKoresp->INCOME;
  573. }
  574. $projHasCostSelfIds[$kProjId][$kMonthNum] = $projectPath;
  575. }
  576. }
  577. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">$projHasCostSelfIds: ';print_r($projHasCostSelfIds);echo'</pre>';
  578. foreach ($projHasCostSelfIds as $kProjId => $vProjMonthsList) {
  579. if ($kProjId <= 0) continue;
  580. foreach ($vProjMonthsList as $kMonthNum => $vProjPath) {
  581. $vProjPathIds = explode('-', $vProjPath);
  582. $vProjPathIds = array_reverse($vProjPathIds);
  583. $vProjMonthCostSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF;
  584. $vProjMonthIncomeSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF;
  585. foreach ($vProjPathIds as $vProjId) {
  586. if ($vProjId == $kProjId) continue;
  587. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  588. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_CHILD += $vProjMonthCostSelf;
  589. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_CHILD += $vProjMonthIncomeSelf;
  590. }
  591. }
  592. }
  593. // recount total
  594. foreach ($this->_costs as $vProjId => $vProjInfo) {
  595. foreach ($vProjInfo->costsByMonth as $kMonthNum => $vCost) {
  596. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  597. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_TOTAL = $vCost->COST_SELF + $vCost->COST_CHILD;
  598. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_TOTAL = $vCost->INCOME_SELF + $vCost->INCOME_CHILD;
  599. }
  600. }
  601. }
  602. public function _createCostIfNotDefined($projId, $monthNum) {
  603. if (empty($this->_costs[$projId]->costsByMonth[$monthNum])) {
  604. $vEmptyCost = new stdClass();
  605. $vEmptyCost->COST_SELF = 0;
  606. $vEmptyCost->INCOME_SELF = 0;
  607. $vEmptyCost->COST_CHILD = 0;
  608. $vEmptyCost->INCOME_CHILD = 0;
  609. $vEmptyCost->COST_TOTAL = 0;
  610. $vEmptyCost->INCOME_TOTAL = 0;
  611. $this->_costs[$projId]->costsByMonth[$monthNum] = $vEmptyCost;
  612. }
  613. }
  614. public function _buildProjectTree() {
  615. $this->_projectPathsOrder = array();
  616. foreach ($this->_projectInfo as $idProject => $projectInfo) {
  617. $this->_projectPathsOrder[$projectInfo->path] = $idProject;
  618. }
  619. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths: ';print_r($this->_projectPathsOrder);echo'</pre>';
  620. uksort($this->_projectPathsOrder, array($this, 'sortPathsCallback'));
  621. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths sorted: ';print_r($this->_projectPathsOrder);echo'</pre>';
  622. return $this->_projectPathsOrder;
  623. }
  624. public function sortPathsCallback($a, $b) {
  625. $ea = explode('-', $a);
  626. $eb = explode('-', $b);
  627. $la = count($ea);
  628. $lb = count($eb);
  629. $lmin = min($la, $lb);
  630. for ($i = 0; $i < $lmin; $i++) {
  631. if ($ea[$i] < $eb[$i]) {
  632. return -1;
  633. } else if ($ea[$i] > $eb[$i]) {
  634. return 1;
  635. }
  636. }
  637. return $la - $lb;
  638. }
  639. public function updatePaths() {
  640. $sqlList = array();
  641. $sqlList['updateAllPaths'] = <<<SQL
  642. update `projects_budget_year_month` b
  643. set path = (select coalesce(
  644. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=b.`ID_PROJECT` limit 1)
  645. , '?'));
  646. SQL;
  647. $db = DB::getDB();
  648. if ($db->has_errors()) {
  649. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  650. }
  651. foreach ($sqlList as $sqlName => $sql) {
  652. $res = $db->query($sql);
  653. if ($db->has_errors()) {
  654. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  655. }
  656. }
  657. }
  658. public function reinstall() {
  659. $sqlList = array();
  660. $sqlList['RemoveTrigger_BudgetPlan_BeforeInsert'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_INSERT`";
  661. $sqlList['CreateTrigger_BudgetPlan_BeforeInsert'] = "
  662. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_INSERT` BEFORE INSERT ON `projects_budget_year_month`
  663. FOR EACH ROW BEGIN
  664. IF NEW.ID_PROJECT IS NOT NULL and NEW.ID_PROJECT>0 THEN
  665. SET NEW.path = (select coalesce(
  666. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  667. , '?'
  668. ));
  669. END IF;
  670. END
  671. ";
  672. $sqlList['RemoveTrigger_BudgetPlan_BeforeUpdate'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_UPDATE `";
  673. // throws errors:
  674. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected ID=PARENT_ID' doesn't exist
  675. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Parent item not exists' doesn't exist
  676. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected in path' doesn't exist
  677. $sqlList['CreateTrigger_BudgetPlan_BeforeUpdate'] = "
  678. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_UPDATE` BEFORE UPDATE ON `projects_budget_year_month`
  679. FOR EACH ROW BEGIN
  680. IF NEW.ID_PROJECT IS NULL THEN
  681. SET NEW.path = '';
  682. ELSEIF OLD.ID_PROJECT IS NULL or NEW.ID_PROJECT<>OLD.ID_PROJECT THEN
  683. IF NEW.ID_PROJECT>0 THEN
  684. SET NEW.path = (select coalesce(
  685. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  686. , '?'));
  687. ELSE
  688. SET NEW.path = '';
  689. END IF;
  690. END IF;
  691. END
  692. ";
  693. $db = DB::getDB();
  694. if ($db->has_errors()) {
  695. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  696. }
  697. foreach ($sqlList as $sqlName => $sql) {
  698. $res = $db->query($sql);
  699. if ($db->has_errors()) {
  700. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  701. }
  702. }
  703. }
  704. public function planAction() {
  705. UI::gora();
  706. UI::menu();
  707. $args = array();
  708. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  709. if ($args['year'] > 0) {
  710. $_REQUEST['ff_YEAR'] = $_GET['ff_YEAR'] = $args['year'];
  711. }
  712. $this->menu($args['year']);
  713. if ($args['year'] > 0) {
  714. UI::startContainer();
  715. UI::tag('a', ['class'=>"btn btn-xs btn-default", 'href'=>"#"], "Utwórz plan na kolejny rok na podstawie danych z {$args['year']} roku");
  716. UI::endContainer();
  717. }
  718. $zasobObj = ProcesHelper::getZasobTableInfoByUri('default_db/projects_budget_year_month');
  719. if (!$zasobObj) {
  720. UI::alert('danger', "Zasob Tabela Plan budżetu (projects_budget_year_month) nie istnieje");
  721. // TODO: btn utwórz
  722. UI::dol();
  723. return;
  724. }
  725. $userAcl = User::getAcl();
  726. $userAcl->fetchGroups();
  727. if (!$userAcl->hasTableAcl($zasobObj->ID)) {
  728. UI::alert('danger', "Brak uprawnień do tabeli Plan budżetu (projects_budget_year_month)");
  729. UI::dol();
  730. return;
  731. }
  732. $tblAcl = $userAcl->getTableAcl($zasobObj->ID);
  733. $forceTblAclInit = ('1' == V::get('_force', '', $_GET));
  734. $tblAcl->init($forceTblAclInit);
  735. $forceFilterInit = array();
  736. $filterInit = new stdClass();
  737. $filterInit->currSortCol = 'ID';
  738. $filterInit->currSortFlip = 'desc';
  739. foreach ($_GET as $k => $v) {
  740. if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && !empty($v)) {// filter prefix
  741. $filterInit->$k = $v;
  742. }
  743. else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && !empty($v)) {// special filter prefix
  744. $filterInit->$k = $v;
  745. }
  746. else if (strlen($k) > 4 && substr($k, 0, 3) == 'ff_' && !empty($v)) {// force filter prefix
  747. $fldName = substr($k, 3);
  748. $forceFilterInit[$fldName] = $v;
  749. }
  750. }
  751. $syncUrl = Request::getPathUri() . 'index.php?_route=ViewTableAjax&namespace=' . $tblAcl->getNamespace();
  752. $tbl = new TableAjax($tblAcl);
  753. $tbl->setSyncUrl($syncUrl);
  754. $tblLabel = array();
  755. if (!empty($zasobObj->DESC_PL)) $tblLabel []= $zasobObj->DESC_PL;
  756. if (!empty($zasobObj->OPIS)) $tblLabel []= $zasobObj->OPIS;
  757. $tblLabel = implode(" - ", $tblLabel);
  758. $tbl->setLabel($tblLabel);
  759. $tbl->setFilterInit($filterInit);
  760. if (!empty($forceFilterInit)) $tbl->setForceFilterInit($forceFilterInit);
  761. $tbl->addRowFunction('edit');
  762. $tbl->addRowFunction('hist');
  763. $tbl->addRowFunction('files');
  764. $tbl->addRowFunction('cp');
  765. $tbl->showProcesInit(false);
  766. echo $tbl->render();
  767. UI::dol();
  768. }
  769. }