Budget.php 26 KB

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