Budget.php 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. class Route_Budget extends RouteBase {
  4. private $_costs = array();
  5. private $_plan = array();
  6. private $_projectInfo = array();
  7. private $_projectPathsOrder = array();
  8. public function handleAuth() {
  9. if (!User::logged()) {
  10. throw new HttpException('Unauthorized', 401);
  11. }
  12. }
  13. public function defaultAction() {
  14. $args = array();
  15. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  16. $args['_print'] = V::get('_print', '', $_REQUEST, 'int');
  17. SE_Layout::gora();
  18. SE_Layout::menu();
  19. if (!$args['_print']) {
  20. $this->menu($args['year']);
  21. }
  22. SE_Layout::dol();
  23. }
  24. public function yearBudgetAction() {
  25. $args = array();
  26. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  27. $args['groups'] = V::get('fltrGroups', array(), $_REQUEST, 'array', array('V', 'filterPositiveInteger'));
  28. $args['_print'] = V::get('_print', '', $_REQUEST, 'int');
  29. $hasData = false;
  30. $groups = null;
  31. if ($args['year'] > 0) {
  32. $hasData = $this->fetchDataByYear($args['year']);
  33. $groups = $this->getUsedUserGroups();
  34. }
  35. SE_Layout::gora();
  36. SE_Layout::menu();
  37. if (!$args['_print']) {
  38. $this->menu($args['year'], $groups, $args['groups']);
  39. }
  40. if (empty($args['year'])) {
  41. ?>
  42. <div class="alert alert-warning">
  43. Nie wybrano roku.
  44. </div>
  45. <?php
  46. SE_Layout::dol();
  47. exit;
  48. }
  49. if (!$hasData) {
  50. ?>
  51. <div class="alert alert-warning">
  52. Brak danych na wybrany rok.
  53. </div>
  54. <?php
  55. return;
  56. }
  57. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">$costs: ';print_r($costs);echo'</pre>';
  58. $this->printCostsForYear($args['year'], $args['groups']);
  59. SE_Layout::dol();
  60. }
  61. private function menu($selectedYear, $groups = array(), $selectedGroups = array()) {
  62. //SE_Layout::menu();
  63. $year = ($selectedYear)? $selectedYear : date("Y");
  64. ?>
  65. <div class="jumbotron">
  66. <div class="container">
  67. <form class="form-inline" method="POST">
  68. <input type="hidden" name="_task" value="yearBudget" />
  69. <label for="year">Zestawienie kosztów projektów. Wybierz rok:</label>
  70. <div class="input-group date" id="fldZestYear">
  71. <input type="text" name="year" class="form-control" value="" />
  72. <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
  73. </div>
  74. <?php if (!empty($groups)) : ?>
  75. <div style="margin:8px 0">
  76. <label for="fltrGroups">Pokaż tylko projekty dostępne dla grup:</label>
  77. <select multiple name="fltrGroups[]" size="<?php echo min(5, count($groups) + 1); ?>" class="form-control">
  78. <option value=""> [ Wszystkie ] </option>
  79. <?php foreach ($groups as $idGroup => $groupLdapName) : ?>
  80. <option
  81. value="<?php echo $idGroup; ?>"
  82. <?php if (in_array($idGroup, $selectedGroups)) { echo 'selected="selected"'; } ?>
  83. ><?php echo $groupLdapName; ?></option>
  84. <?php endforeach; ?>
  85. </select>
  86. </div>
  87. <?php endif; ?>
  88. <button type="submit" id="fldZestYearBtn" class="btn btn-primary" autocomplete="off">
  89. Pokaż
  90. </button>
  91. </form>
  92. </div>
  93. </div>
  94. <script type="text/javascript">
  95. jQuery(document).ready(function () {
  96. jQuery('#fldZestYearBtn').on('click', function () {
  97. jQuery(this).text(jQuery(this).text() + '...').attr('disabled', 'disabled');
  98. jQuery(this).parent().submit();
  99. })
  100. jQuery("#fldZestYear").datetimepicker({
  101. format: "YYYY",
  102. defaultDate: new Date(<?php echo $year; ?>, <?php echo intval(date("m")); ?>, 1),
  103. // minDate: new Date(2014, 11, 1),
  104. // maxDate: "<?php echo date("Y"); ?>"
  105. });
  106. });
  107. </script>
  108. <?php
  109. }
  110. function css() {
  111. ?>
  112. <style type="text/css">
  113. .c { text-align:center; }
  114. .r { text-align:right; }
  115. .zestawienie-kosztow-tbl { border-collapse:collapse; border:1px solid #aaa; }
  116. .zestawienie-kosztow-tbl td { border:1px solid #aaa; }
  117. .zestawienie-kosztow-tbl .p2 { padding:0 2px; }
  118. .zestawienie-kosztow-tbl .nr { color:#7A7A7A; }
  119. .zestawienie-kosztow-tbl thead th { border:1px solid #aaa; }
  120. .zestawienie-kosztow-tbl tbody tr:hover td { background:#cafbfd; }
  121. .row-selected td {background-color:#d8fded;}
  122. .showOnlySelected tr { display:none; }
  123. .showOnlySelected tr.row-selected { display:table-row; }
  124. .cell-cost { padding:0 2px; min-width:30px; text-align:right; }
  125. .cell-cost-only_self { color:#197fe6; }
  126. .cell-cost-self_and_child { color:#33b2cc; }
  127. .cell-cost-only_child { color:#59a680; }
  128. .cell-plan { padding:0 2px; min-width:30px; text-align:right; color:#777; }
  129. .cell-procent { padding:0 2px; min-width:20px; text-align:right; color:#777; }
  130. .cell-procent-below100 { color:#777; }
  131. .cell-procent-100 { color:#777; }
  132. .cell-procent-over100 { color:#ff9b00; }
  133. .cell-procent-over150 { color:#f00; }
  134. /* print table background colors */
  135. table td, table th { -webkit-print-color-adjust:exact; }
  136. table { page-break-after:auto }
  137. tr { page-break-inside:avoid; page-break-after:auto; position:relative; }
  138. td { page-break-inside:avoid; page-break-after:auto; position:relative; }
  139. thead { display:table-header-group }
  140. tfoot { display:table-footer-group }
  141. #zestawienie-kosztow-projektow.hidden_month_1 .col_month_1 {display:none}
  142. #zestawienie-kosztow-projektow.hidden_month_2 .col_month_2 {display:none}
  143. #zestawienie-kosztow-projektow.hidden_month_3 .col_month_3 {display:none}
  144. #zestawienie-kosztow-projektow.hidden_month_4 .col_month_4 {display:none}
  145. #zestawienie-kosztow-projektow.hidden_month_5 .col_month_5 {display:none}
  146. #zestawienie-kosztow-projektow.hidden_month_6 .col_month_6 {display:none}
  147. #zestawienie-kosztow-projektow.hidden_month_7 .col_month_7 {display:none}
  148. #zestawienie-kosztow-projektow.hidden_month_8 .col_month_8 {display:none}
  149. #zestawienie-kosztow-projektow.hidden_month_9 .col_month_9 {display:none}
  150. #zestawienie-kosztow-projektow.hidden_month_10 .col_month_10 {display:none}
  151. #zestawienie-kosztow-projektow.hidden_month_11 .col_month_11 {display:none}
  152. #zestawienie-kosztow-projektow.hidden_month_12 .col_month_12 {display:none}
  153. .thead__cols_summary,
  154. .row__summary__cost,
  155. .row__summary__plan {background-color:#fcf8e3;}
  156. .thead_col_month .col_month_remove { opacity:0.2; }
  157. .thead_col_month:hover .col_month_remove { opacity:0.6; }
  158. </style>
  159. <?php
  160. }
  161. function printCostsForYear($year, $groups) {
  162. $months = array();
  163. for ($i = 0; $i < 12; $i++) {
  164. $months[] = $i + 1;
  165. }
  166. $this->css();
  167. if(V::get('DBG','',$_GET)){echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;display:none">';print_r($this->_costs);echo'</pre>';}
  168. ?>
  169. <div class="container">
  170. <div style="float:right;color:#aaa;"><?php echo date("Y-m-d"); ?></div>
  171. <h1>Zestawienie kosztów projektów na rok <?php echo $year; ?></h1>
  172. </div>
  173. <table cellspacing="0" cellpadding="0" border="0" id="zestawienie-kosztow-projektow" class="zestawienie-kosztow-tbl">
  174. <thead>
  175. <tr>
  176. <td colspan="3" class="p2">
  177. <span class="pull-right"><b>miesiąc</b> <span class="month_cols_picker"></span></span>
  178. </td>
  179. <?php foreach ($months as $month) { ?>
  180. <th class="c col_month_<?php echo $month; ?> thead_col_month" colspan="3"><?php echo sprintf("%02d", $month); ?></th>
  181. <?php } ?>
  182. </tr>
  183. <tr>
  184. <td colspan="3" class="p2">
  185. <span class="pull-left">
  186. <input type="checkbox" onclick="return showHideAll(this);"/> pokaż tylko zaznaczone
  187. </span>
  188. </td>
  189. <?php foreach ($months as $month) { ?>
  190. <th class="c col_month_<?php echo $month; ?>" title="Koszty wprowadzone do korespondencji">Koszty</th>
  191. <th class="c col_month_<?php echo $month; ?>" title="Plan budżetu">Plan</th>
  192. <th class="c col_month_<?php echo $month; ?>" title="Procent przekroczenia planu">%</th>
  193. <?php } ?>
  194. </tr>
  195. </thead>
  196. <tbody>
  197. <?php $t = 1; ?>
  198. <?php foreach ($this->_projectPathsOrder as $projPath => $projId) : ?>
  199. <?php
  200. $projectID = $projId;
  201. $projectDesc = $this->_projectInfo[$projId]->M_DIST_DESC;
  202. $projectPath = $this->_projectInfo[$projId]->path;
  203. $projectAccess = $this->hasAccessToProject($projectID);
  204. if (!empty($groups)) {
  205. if (!$projectAccess) {
  206. //echo '<pre>TODO: filtered by acl for project';print_r($this->_projectInfo[$projId]);echo'</pre>';
  207. continue;
  208. }
  209. if (!$this->hasGroupsAccessToProjects($projectID, $groups)) {
  210. //echo '<pre>TODO: filtered by acl and groups';print_r($this->_projectInfo[$projId]);echo'</pre>';
  211. continue;
  212. }
  213. }
  214. ?>
  215. <tr class="row-<?php echo ($t = 1 - $t); ?>"
  216. data-proj_id="<?php echo $projectID; ?>"
  217. data-path="<?php echo $projectPath; ?>">
  218. <td class="p2 r nr">
  219. <input type="checkbox" name="selectedProject" onclick="return selectProject(this);" value="<?php echo $projectID; ?>" />
  220. </td>
  221. <td class="p2 l nr"><nobr><?php echo $projectPath; ?></nobr></td>
  222. <?php if (!$projectAccess) : ?>
  223. <td class="p2">***</td>
  224. <?php else : ?>
  225. <td class="p2" style="max-width:300px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" title="<?php echo $projectDesc; ?>"><?php echo $projectDesc; ?></td>
  226. <?php endif; ?>
  227. <?php foreach ($months as $month) : ?>
  228. <?php $vMonthCost = $this->getCost($projectID, $month); ?>
  229. <?php $monthCostTotal = ($vMonthCost)? $vMonthCost->COST_TOTAL : 0; ?>
  230. <?php if (!$projectAccess) : ?>
  231. <td class="col_month_<?php echo $month; ?>" style="min-width:30px;text-align:right;">***</td>
  232. <?php elseif (!$vMonthCost) : ?>
  233. <td class="col_month_<?php echo $month; ?>" style="min-width:30px">&nbsp;</td>
  234. <?php else : ?>
  235. <?php
  236. $vCostChildOut = number_format($vMonthCost->COST_CHILD, 2);
  237. $vCostSelfOut = number_format($vMonthCost->COST_SELF, 2);
  238. $vCostTotalOut = number_format($vMonthCost->COST_TOTAL, 2);
  239. $vCostTotalOutJs = number_format($vMonthCost->COST_TOTAL, 2, '.', '');
  240. $title = "Koszt projektu {$vCostSelfOut} / koszt podprojektów $vCostChildOut";
  241. $cellCostCls = '';
  242. if ($vMonthCost->COST_CHILD > 0) {
  243. if ($vMonthCost->COST_SELF > 0) {
  244. $cellCostCls = 'cell-cost-self_and_child';
  245. } else {
  246. $cellCostCls = 'cell-cost-only_child';
  247. }
  248. } else {
  249. $cellCostCls = 'cell-cost-only_self';
  250. }
  251. ?>
  252. <td class="cell-cost <?php echo $cellCostCls; ?> col_month_<?php echo $month; ?>"
  253. data-month_num="<?php echo $month; ?>"
  254. data-cost="<?php echo $vCostTotalOutJs; ?>"
  255. data-proj_path="<?php echo $projectPath; ?>"
  256. data-proj_id="<?php echo $projectID; ?>">
  257. <span class="ttip" title="<?php echo $title; ?>"><?php echo $vCostTotalOut; ?></span>
  258. </td>
  259. <?php endif; ?>
  260. <?php $monthPlan = $this->getPlan($projectID, $month); ?>
  261. <?php $monthPlanOut = number_format($monthPlan, 2); ?>
  262. <?php $monthPlanOutJs = number_format($monthPlan, 2, '.', ''); ?>
  263. <td class="cell-plan col_month_<?php echo $month; ?>"
  264. data-month_num="<?php echo $month; ?>"
  265. data-plan="<?php echo $monthPlanOutJs; ?>">
  266. <?php if (!$projectAccess) : ?>
  267. ***
  268. <?php elseif ($monthPlan > 0) : ?>
  269. <?php echo $monthPlan; ?>
  270. <?php else : ?>
  271. &nbsp;
  272. <?php endif; ?>
  273. </td>
  274. <?php
  275. $cellProcentCls = '';
  276. $procentOut = '&nbsp;';
  277. $monthPlan = $this->getPlan($projectID, $month);
  278. if ($monthPlan > 0) {
  279. $procentOut = round(($monthCostTotal * 100) / $monthPlan);
  280. if ($procentOut > 150) {
  281. $cellProcentCls = 'cell-procent-over150';
  282. } else if ($procentOut > 100) {
  283. $cellProcentCls = 'cell-procent-over100';
  284. } else if ($procentOut == 100) {
  285. $cellProcentCls = 'cell-procent-100';
  286. } else {
  287. $cellProcentCls = 'cell-procent-below100';
  288. }
  289. }
  290. ?>
  291. <?php if (!$projectAccess) : ?>
  292. <td class="col_month_<?php echo $month; ?>" style="min-width:30px;text-align:right;">***</td>
  293. <?php else : ?>
  294. <td class="cell-procent <?php echo $cellProcentCls; ?> col_month_<?php echo $month; ?>"><?php echo $procentOut; ?></td>
  295. <?php endif; ?>
  296. <?php endforeach; ?>
  297. </tr>
  298. <?php endforeach; ?>
  299. </tbody>
  300. </table>
  301. <script>
  302. jQuery(document).ready(function() {
  303. jQuery('#zestawienie-kosztow-projektow')
  304. .find('.cell-cost')
  305. .on('click', function(e) {
  306. var $n = jQuery(this),
  307. $modal = jQuery('#projectMonthCostDetails'),
  308. projId = $n.data('proj_id'),
  309. monthNum = $n.data('month_num'),
  310. projPath = '' + $n.data('proj_path'),
  311. projPathLength = projPath.length,
  312. modalBody = document.createElement('table'),
  313. totalDescrCell = document.createElement('td'),
  314. totalCostCell = document.createElement('td'),
  315. totalEmptyCell = document.createElement('td'),
  316. totalRow = document.createElement('tr');
  317. modalBody.className = 'zestawienie-kosztow-tbl';
  318. modalBody.style.width = '100%';
  319. // $n.data() = {proj_id: 3943, proj_path: "0-4132-3943", month_num: 2}
  320. // table#proj-koresp-info > tbody#row-proj-2055-koresp-by-month-2
  321. jQuery('#proj-koresp-info').find('tbody').each(function(i, tbody) {
  322. var vPath = jQuery(tbody).data('proj_path'),
  323. vMonthNum = jQuery(tbody).data('month_num');
  324. if (monthNum != vMonthNum) return;
  325. if (projPathLength > vPath.length) return;
  326. if (projPath == vPath
  327. || (projPathLength < vPath.length && projPath + '-' == vPath.substr(0, projPathLength + 1))
  328. ) {
  329. modalBody.appendChild(tbody.cloneNode(true));
  330. }
  331. });
  332. {
  333. totalDescrCell.className = 'p2';
  334. totalDescrCell.style.textAlign = 'right';
  335. totalDescrCell.setAttribute('colspan', '2');
  336. totalDescrCell.appendChild(document.createTextNode('Suma:'));
  337. totalCostCell.className = 'cell-cost';
  338. totalCostCell.style.textAlign = 'right';
  339. totalCostCell.appendChild(document.createTextNode($n.data('cost')));
  340. totalRow.appendChild(totalDescrCell);
  341. totalRow.appendChild(totalCostCell);
  342. modalBody.appendChild(totalRow);
  343. }
  344. $modal.find('.modal-title').text('Koszty projektu nr ' + projId + ' w miesiącu <?php echo $year; ?>-' + ((monthNum > 9)? monthNum : '0' + monthNum));
  345. $modal.find('.modal-body').html(modalBody);
  346. $modal.modal({});
  347. })
  348. jQuery('#zestawienie-kosztow-projektow').data('visible_months', <?php echo json_encode($months); ?>);
  349. jQuery('#zestawienie-kosztow-projektow').on('show_month', function(e, argMonthNum) {
  350. var this$Node = $(this),
  351. monthNum = parseInt(argMonthNum),
  352. visibleMonths = this$Node.data('visible_months'),
  353. dbgLvl = parseInt(this$Node.data('dbg_lvl'))
  354. ;
  355. if(dbgLvl){console.log('event:show_month argMonthNum', argMonthNum);}
  356. monthNum = parseInt(monthNum);
  357. if (monthNum < 1 || monthNum > 12) return;
  358. if (-1 === visibleMonths.indexOf(monthNum)) {
  359. visibleMonths.push(monthNum);
  360. this$Node.data('visible_months', visibleMonths);
  361. if(dbgLvl>1){console.log('event:show_month removed monthNum', monthNum, 'visibleMonths', visibleMonths);}
  362. this$Node.trigger('visible_months_changed');
  363. // TODO: trigger render_sum
  364. } else {
  365. if(dbgLvl>1){console.log('event:hide_month monthNum('+monthNum+') already in visibleMonths', visibleMonths);}
  366. }
  367. });
  368. jQuery('#zestawienie-kosztow-projektow').on('hide_month', function(e, argMonthNum) {
  369. var this$Node = $(this),
  370. monthNum = parseInt(argMonthNum),
  371. monthInd,
  372. visibleMonths = this$Node.data('visible_months'),
  373. dbgLvl = parseInt(this$Node.data('dbg_lvl'))
  374. ;
  375. if(dbgLvl){console.log('event:hide_month argMonthNum', argMonthNum);}
  376. if (monthNum < 1 || monthNum > 12) return;
  377. monthInd = visibleMonths.indexOf(monthNum);
  378. if (-1 === monthInd) {
  379. if(dbgLvl>1){console.log('event:hide_month monthNum('+monthNum+') not in visibleMonths', visibleMonths);}
  380. } else {
  381. visibleMonths.splice(monthInd, 1);
  382. this$Node.data('visible_months', visibleMonths);
  383. if(dbgLvl>1){console.log('event:hide_month removed monthNum', monthNum, 'visibleMonths', visibleMonths);}
  384. this$Node.trigger('visible_months_changed');
  385. }
  386. });
  387. jQuery('#zestawienie-kosztow-projektow').on('visible_months_changed', function(e) {
  388. var this$Node = $(this);
  389. this$Node.trigger('render_cols_select');
  390. this$Node.trigger('render_cols');
  391. this$Node.trigger('render_sum');
  392. });
  393. jQuery('#zestawienie-kosztow-projektow').on('initial_render', function(e) {
  394. var this$Node = $(this);
  395. this$Node.trigger('render_cols_select');
  396. this$Node.trigger('render_cols');
  397. this$Node.trigger('render_sum');
  398. var theadFirstRow$Node = $('#zestawienie-kosztow-projektow').find('thead').find('tr:first'),
  399. close$Node;
  400. for (var i = 1; i <= 12; i++) {
  401. close$Node = $('<i class="pull-right glyphicon glyphicon-remove col_month_remove"></i>').appendTo(theadFirstRow$Node.find('.col_month_' + i));
  402. close$Node.css({padding:'2px', cursor: 'pointer', color:'red'});
  403. close$Node.data('month_num', i);
  404. close$Node.on('click', function(e) {
  405. var this$Node = $(this),
  406. monthNum = this$Node.data('month_num'),
  407. root$Node = $('#zestawienie-kosztow-projektow'),
  408. dbgLvl = parseInt(root$Node.data('dbg_lvl'))
  409. ;
  410. if(dbgLvl){console.log('click month col remove btn:', monthNum);}
  411. root$Node.trigger('hide_month', monthNum);
  412. });
  413. }
  414. });
  415. jQuery('#zestawienie-kosztow-projektow').on('render_cols_select', function(e) {
  416. var this$Node = $(this),
  417. dbgLvl = parseInt(this$Node.data('dbg_lvl'))
  418. ;
  419. if(dbgLvl){console.log('event:render_cols_select');}
  420. var this$Node = $(this),
  421. visibleMonths = this$Node.data('visible_months'),
  422. nodeClass = 'month_cols_picker',
  423. currentNode,
  424. node
  425. ;
  426. currentNode = this$Node.find('thead').find('.' + nodeClass);
  427. node = $('<div class="btn-group pull-right dropdown columnpicker ' + nodeClass + '"></div>');
  428. var btn = $('<button class="btn btn-sm btn-link dropdown-toggle" data-toggle="dropdown" href="#">wybierz&nbsp;</button>').appendTo(node);
  429. btn.css({padding: '0 10px'});
  430. var span = $('<span class="caret"></span>').appendTo(btn);
  431. var ul$Node = $('<ul class="dropdown-menu">').appendTo(node);
  432. for (var i = 1; i <= 12; i++) {
  433. var li$Node = $('<li class="checkbox" style="margin:0 10px;"></li>').appendTo(ul$Node),
  434. label$Node = $('<label></label>').appendTo(li$Node),
  435. input$Node;
  436. input$Node = $('<input type="checkbox" title="' + i + '" value="' + i + '" >').appendTo(label$Node);
  437. label$Node.append(' ' + i);
  438. input$Node.prop('checked', (-1 !== visibleMonths.indexOf(i)));
  439. }
  440. {// show months all
  441. if (visibleMonths.length < 12) {
  442. var li$Node = $('<li class="checkbox" style="margin:0 10px;"></li>').appendTo(ul$Node),
  443. btnViewAll$Node = $('<button class="btn btn-link">Pokaż wszystkie</button>').appendTo(li$Node)
  444. ;
  445. btnViewAll$Node.on('click', function(e) {
  446. var root$Node = $('#zestawienie-kosztow-projektow'),
  447. visibleMonths = root$Node.data('visible_months')
  448. ;
  449. if (visibleMonths.length < 12) {
  450. visibleMonths = [];
  451. for (var i = 1; i <= 12; i++) visibleMonths.push(i);
  452. root$Node.data('visible_months', visibleMonths);
  453. root$Node.trigger('visible_months_changed');
  454. }
  455. });
  456. }
  457. }
  458. node.on('change', 'input', function(e) {
  459. var this$Node = $(this),
  460. monthNum = this$Node.val(),
  461. dbgLvl = parseInt($('#zestawienie-kosztow-projektow').data('dbg_lvl'))
  462. ;
  463. e.preventDefault();
  464. e.stopPropagation();
  465. if(dbgLvl>1){console.log('change checkbox val:', this$Node.val(), 'checked:', this$Node.prop('checked'));}
  466. if (this$Node.prop('checked')) {
  467. jQuery('#zestawienie-kosztow-projektow').trigger('show_month', monthNum);
  468. } else {
  469. jQuery('#zestawienie-kosztow-projektow').trigger('hide_month', monthNum);
  470. }
  471. });
  472. currentNode.replaceWith(node);
  473. });
  474. // @require css: #zestawienie-kosztow-projektow.hidden_month_1 .col_month_1 {display:none}
  475. jQuery('#zestawienie-kosztow-projektow').on('render_cols', function(e) {
  476. var this$Node = $(this),
  477. thead$Node = this$Node.find('thead'),
  478. tbody$Node = this$Node.find('tbody'),
  479. visibleMonths = this$Node.data('visible_months'),
  480. dbgLvl = parseInt($('#zestawienie-kosztow-projektow').data('dbg_lvl'))
  481. ;
  482. if(dbgLvl){console.log('event:render_cols visibleMonths', visibleMonths);}
  483. for (var i = 1; i <= 12; i++) {
  484. if (-1 === visibleMonths.indexOf(i)) {
  485. this$Node.addClass('hidden_month_' + i);
  486. } else {
  487. this$Node.removeClass('hidden_month_' + i);
  488. }
  489. }
  490. });
  491. jQuery('#zestawienie-kosztow-projektow').on('render_sum', function(e) {
  492. var this$Node = $(this),
  493. thead$Node = this$Node.find('thead'),
  494. tbody$Node = this$Node.find('tbody'),
  495. visibleMonths = this$Node.data('visible_months'),
  496. dbgLvl = parseInt($('#zestawienie-kosztow-projektow').data('dbg_lvl'))
  497. ;
  498. if(dbgLvl){console.log('event:render_sum visibleMonths', visibleMonths);}
  499. if (!thead$Node.find('.thead__cols_summary').length) {
  500. $('<th colspan="2" class="thead__cols_summary c">Suma</th>').appendTo(thead$Node.children('tr:first'));
  501. $('<th class="thead__cols_summary c">Koszty</th>').appendTo(thead$Node.children('tr:last'));
  502. $('<th class="thead__cols_summary c">Plan</th>').appendTo(thead$Node.children('tr:last'));
  503. tbody$Node.find('tr').each(function(ind, n) {
  504. var this$Node = $(this);
  505. $('<td class="row__summary__cost r"></td>').appendTo(this$Node);
  506. $('<td class="row__summary__plan r"></td>').appendTo(this$Node);
  507. });
  508. }
  509. tbody$Node.find('tr').each(function(ind, n) {
  510. var this$Node = $(this),
  511. rowSumCost = 0,
  512. rowSumPlan = 0
  513. ;
  514. this$Node.find('.cell-cost').each(function(ind, n) {
  515. var monthNum = $(this).data('month_num'),
  516. costValueOut = $(this).data('cost'),
  517. costValue = 0
  518. ;
  519. monthNum = parseInt(monthNum);
  520. if (monthNum < 1 || monthNum > 12) return;
  521. if (-1 === visibleMonths.indexOf(monthNum)) {
  522. if(dbgLvl>2){console.log('HIDE: .cell-cost monthNum', monthNum, 'costValueOut', costValueOut, 'visibleMonths', visibleMonths);}
  523. return;
  524. } else {
  525. costValue = parseFloat(costValueOut);
  526. if (isNaN(costValue) || !costValue) costValue = 0;
  527. if (costValue) rowSumCost += costValue;
  528. }
  529. if(dbgLvl>2){console.log('.cell-cost monthNum', monthNum, 'costValueOut', costValueOut, 'visibleMonths', visibleMonths);}
  530. });
  531. this$Node.find('.cell-plan').each(function(ind, n) {
  532. var monthNum = $(this).data('month_num'),
  533. planValueOut = $(this).data('plan'),
  534. planValue = 0
  535. ;
  536. monthNum = parseInt(monthNum);
  537. if (monthNum < 1 || monthNum > 12) return;
  538. if (-1 === visibleMonths.indexOf(monthNum)) {
  539. if(dbgLvl>2){console.log('HIDE: .cell-cost monthNum', monthNum, 'planValueOut', planValueOut, 'visibleMonths', visibleMonths);}
  540. return;
  541. } else {
  542. planValue = parseFloat(planValueOut);
  543. if (isNaN(planValue) || !planValue) planValue = 0;
  544. if (planValue) rowSumPlan += planValue;
  545. }
  546. if(dbgLvl>2){console.log('.cell-cost monthNum', monthNum, 'planValueOut', planValueOut, 'visibleMonths', visibleMonths);}
  547. });
  548. this$Node.find('.row__summary__cost').html((rowSumCost)? rowSumCost.toFixed(2) : '');
  549. this$Node.find('.row__summary__plan').html((rowSumPlan)? rowSumPlan.toFixed(2) : '');
  550. });
  551. });
  552. jQuery('#zestawienie-kosztow-projektow').trigger('initial_render');
  553. });
  554. </script>
  555. <!-- Modal -->
  556. <div class="modal fade" id="projectMonthCostDetails" tabindex="-1" role="dialog" aria-labelledby="projectCostDMonthetailsLabel">
  557. <div class="modal-dialog" role="document" style="min-width:800px;">
  558. <div class="modal-content">
  559. <div class="modal-header">
  560. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  561. <h4 class="modal-title" id="projectCostDMonthetailsLabel">Modal title</h4>
  562. </div>
  563. <div class="modal-body">
  564. TODO: projectMonthCostDetails...
  565. </div>
  566. <div class="modal-footer">
  567. <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
  568. </div>
  569. </div>
  570. </div>
  571. </div>
  572. <table id="proj-koresp-info" style="display:none">
  573. <?php foreach ($this->_projectPathsOrder as $projPath => $projId) : ?>
  574. <?php
  575. $projectInfo = $this->_costs[$projId];
  576. if (!$projectInfo) {
  577. continue;
  578. }
  579. $projectID = $projId;
  580. $projectDesc = $this->_projectInfo[$projId]->M_DIST_DESC;
  581. $projectPath = $this->_projectInfo[$projId]->path;
  582. ?>
  583. <?php if (!empty($projectInfo->korespByMonth)) : ?>
  584. <?php foreach ($projectInfo->korespByMonth as $kMonth => $vKorespMonthList) : ?>
  585. <tbody id="row-proj-<?php echo $projectID; ?>-koresp-by-month-<?php echo $kMonth; ?>"
  586. data-month_num="<?php echo $kMonth; ?>"
  587. data-proj_path="<?php echo $projectPath; ?>"
  588. data-proj_id="<?php echo $projectID; ?>">
  589. <tr>
  590. <td style="padding:3px;font-size:1em;background:#eee;"><nobr><?php echo $projectPath; ?></nobr></td>
  591. <td colspan="3" style="padding:3px;font-size:1.1em;background:#eee;max-width:500px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" title="<?php echo $projectDesc; ?>">
  592. <!-- Koszty projektu nr <?php echo $projectID; ?> -->
  593. <!-- - miesiąc <?php echo $year; ?>-<?php echo sprintf("%02d", $kMonth); ?> -->
  594. <?php echo $projectDesc; ?>
  595. </td>
  596. </tr>
  597. <?php foreach ($vKorespMonthList as $kKorespIdx => $vKorespInfo) : ?>
  598. <?php /*
  599. * [ID] => 41235
  600. * [MONTH] => 2015-02
  601. * [K_ZAWARTOS] => Faktura za pomiar powykonawczy sieci telekomunikacyjnej
  602. * [COST] => 1000.00
  603. * [INCOME] => 0.00
  604. ? [TRANSFER_OPPOSITE_ID_PROJECT] => int
  605. */ ?>
  606. <tr>
  607. <td class="p2 r nr"><?php echo $vKorespInfo->ID; ?></td>
  608. <td class="p2" style="max-width:400px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" title="<?php echo $vKorespInfo->K_ZAWARTOS; ?>"><?php echo $vKorespInfo->K_ZAWARTOS; ?></td>
  609. <?php $vKorespCostOut = number_format($vKorespInfo->COST, 2); ?>
  610. <td class="cell-cost cell-cost-only_child"><?php echo $vKorespCostOut; ?></td>
  611. <td class="cell-transfer_opposite_id_project">
  612. <?php $dekretId = V::get('TRANSFER_OPPOSITE_ID_PROJECT', 0, $vKorespInfo) ?>
  613. <?php if ($dekretId > 0) : ?>
  614. Dekret z projektu nr <?php echo $dekretId; ?>
  615. <?php endif; ?>
  616. </td>
  617. </tr>
  618. <?php endforeach; ?>
  619. </tbody>
  620. <?php endforeach; ?>
  621. <?php endif; ?>
  622. <?php endforeach; ?>
  623. </table>
  624. <script>
  625. jQuery(document).ready(function(){
  626. jQuery('.ttip').tooltip();
  627. });
  628. function selectProject(n) {
  629. var $n = jQuery(n);
  630. var $p = $n.parent().parent();
  631. if (n.checked) {
  632. $p.addClass('row-selected');
  633. } else {
  634. $p.removeClass('row-selected');
  635. }
  636. markSubProjects($p, $p.data('path'), n.checked);
  637. function markSubProjects($p, path, checked) {
  638. var $nextRow = $p.next('tr'),
  639. nextPath = $nextRow.data('path'),
  640. nextCheckbox = $nextRow.find('input[type="checkbox"]').get(0);
  641. if (0 !== nextPath.indexOf(path)) {
  642. return;
  643. }
  644. if (checked) {
  645. $nextRow.addClass('row-selected');
  646. } else {
  647. $nextRow.removeClass('row-selected');
  648. }
  649. nextCheckbox.checked = checked;
  650. markSubProjects($nextRow, path, checked);
  651. }
  652. }
  653. function showHideAll(n) {
  654. if (n.checked) {
  655. jQuery('#zestawienie-kosztow-projektow').find('tbody').addClass('showOnlySelected');
  656. } else {
  657. jQuery('#zestawienie-kosztow-projektow').find('tbody').removeClass('showOnlySelected');
  658. }
  659. }
  660. </script>
  661. <?php
  662. }
  663. public function getCost($idProject, $month) {
  664. if (!array_key_exists($idProject, $this->_costs)) {
  665. return null;
  666. }
  667. if (!array_key_exists($month, $this->_costs[$idProject]->costsByMonth)) {
  668. return null;
  669. }
  670. return $this->_costs[$idProject]->costsByMonth[$month];
  671. }
  672. public function getPlan($idProject, $month) {
  673. if (!array_key_exists($idProject, $this->_plan)) {
  674. return 0;
  675. }
  676. if (!array_key_exists($month, $this->_plan[$idProject])) {
  677. return 0;
  678. }
  679. return $this->_plan[$idProject][$month];
  680. }
  681. public function fetchDataByYear($year) {
  682. $this->_fetchCostsByYear($year);
  683. $this->_fetchPlanByYear($year);
  684. $this->_fetchProjectInfo();
  685. $this->_buildProjectTree();
  686. $this->_reacountCostsFromKoresp();
  687. return count($this->_projectInfo) > 1;// $this->_projectInfo[0] - Wszystkie projekty
  688. }
  689. public function _fetchPlanByYear($year) {
  690. $db = DB::getDB();
  691. $this->_plan = array();
  692. $sql = "
  693. select plan.`ID`
  694. , plan.`ID_PROJECT` AS `ID_PROJECT`
  695. , plan.`MONTH_1_VALUE`
  696. , plan.`MONTH_2_VALUE`
  697. , plan.`MONTH_3_VALUE`
  698. , plan.`MONTH_4_VALUE`
  699. , plan.`MONTH_5_VALUE`
  700. , plan.`MONTH_6_VALUE`
  701. , plan.`MONTH_7_VALUE`
  702. , plan.`MONTH_8_VALUE`
  703. , plan.`MONTH_9_VALUE`
  704. , plan.`MONTH_10_VALUE`
  705. , plan.`MONTH_11_VALUE`
  706. , plan.`MONTH_12_VALUE`
  707. from `projects_budget_year_month` plan
  708. where plan.`year`='{$year}'
  709. -- TODO: acl
  710. ";
  711. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  712. $res = $db->query($sql);
  713. while ($r = $db->fetch($res)) {
  714. $plan = array();
  715. for ($i = 1; $i <= 12; $i++) {
  716. $plan[$i] = V::get("MONTH_{$i}_VALUE", 0, $r);
  717. }
  718. $this->_plan[$r->ID_PROJECT] = $plan;
  719. }
  720. return $this->_plan;
  721. }
  722. public function _fetchCostsByYear($year) {
  723. $db = DB::getDB();
  724. $this->_costs = array();
  725. $sql = "
  726. select k.`ID`
  727. , k.`ID_PROJECT` AS `ID_PROJECT`
  728. , date_format(k.`K_DATA_OTRZYMANEJ_KORESP`,'%Y-%m') AS `MONTH`
  729. , k.`COST_VALUE` AS `COST`
  730. , k.`INCOME_VALUE` AS `INCOME`
  731. , 0 as `TRANSFER_OPPOSITE_ID_PROJECT` -- TRANSFER_OPPOSITE_ID_PROJECT
  732. , k.`path`
  733. -- , IF(k.`TRANSFER_OPPOSITE_ID_PROJECT`>0
  734. -- , (select p.`path`
  735. -- from `IN7_MK_BAZA_DYSTRYBUCJI` p
  736. -- where p.`ID`=k.`TRANSFER_OPPOSITE_ID_PROJECT`
  737. -- limit 1
  738. -- )
  739. -- , '') as TRANSFER_OPPOSITE_PROJECT_PATH
  740. , '' as TRANSFER_OPPOSITE_PROJECT_PATH
  741. , k.`K_ZAWARTOS`
  742. from `IN7_DZIENNIK_KORESP` k
  743. where ((k.`COST_VALUE` > 0) or (k.`INCOME_VALUE` > 0))
  744. and k.`K_DATA_OTRZYMANEJ_KORESP` like '{$year}-%'
  745. -- TODO: acl
  746. ";
  747. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  748. $res = $db->query($sql);
  749. while ($r = $db->fetch($res)) {
  750. $vProjId = ($r->ID_PROJECT > 0)? $r->ID_PROJECT : 0;
  751. $vProjPaths = array();
  752. $vProjIds = array(0 => true);
  753. if ($r->ID_PROJECT > 0) {
  754. $vProjPaths[] = $r->ID_PROJECT;
  755. if (!empty($r->path)) {
  756. $vProjPaths[] = $r->path;
  757. }
  758. }
  759. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  760. $vProjPaths[] = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  761. if (!empty($r->TRANSFER_OPPOSITE_PROJECT_PATH)) {
  762. $vProjPaths[] = $r->TRANSFER_OPPOSITE_PROJECT_PATH;
  763. }
  764. }
  765. if (!empty($vProjPaths)) {
  766. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $vProjPaths = ' . json_encode($vProjPaths) . '</p>';
  767. $vProjPaths = implode('-', $vProjPaths);
  768. $projIds = explode('-', $vProjPaths);
  769. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $projIds = ' . json_encode($projIds) . '</p>';
  770. foreach ($projIds as $vProjId) {
  771. if ($vProjId > 0) $vProjIds[$vProjId] = true;
  772. }
  773. }
  774. {
  775. $projectZeroInfo = new stdClass();
  776. $projectZeroInfo->ID_PROJECT = 0;
  777. $projectZeroInfo->costsByMonth = array();
  778. $projectZeroInfo->korespByMonth = array();
  779. $this->_costs[0] = $projectZeroInfo;
  780. }
  781. foreach ($vProjIds as $vProjId => $vBool) {
  782. if (!array_key_exists($vProjId, $this->_costs)) {
  783. $projectInfo = new stdClass();
  784. $projectInfo->ID_PROJECT = $vProjId;
  785. $projectInfo->costsByMonth = array();
  786. $projectInfo->korespByMonth = array();
  787. $this->_costs[$vProjId] = $projectInfo;
  788. }
  789. }
  790. $korespInfo = new stdClass();
  791. $korespInfo->ID = $r->ID;
  792. $korespInfo->MONTH = $r->MONTH;
  793. $korespInfo->K_ZAWARTOS = $r->K_ZAWARTOS;
  794. $monthNum = intval(substr($r->MONTH, 5, 2));
  795. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  796. $korespInfo->COST = $r->COST;
  797. $korespInfo->INCOME = $r->INCOME;
  798. $korespInfo->TRANSFER_OPPOSITE_ID_PROJECT = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  799. $this->_costs[$r->TRANSFER_OPPOSITE_ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  800. } else if ($r->ID_PROJECT) {
  801. $korespInfo->COST = $r->COST;
  802. $korespInfo->INCOME = $r->INCOME;
  803. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  804. } else {
  805. $korespInfo->COST = $r->COST;
  806. $korespInfo->INCOME = $r->INCOME;
  807. $this->_costs[0]->korespByMonth[$monthNum][] = $korespInfo;
  808. }
  809. }
  810. return $this->_costs;
  811. }
  812. private function _fetchProjectInfo() {
  813. $db = DB::getDB();
  814. $hasAccessForAllProjects = true;
  815. $projectIds = array();
  816. $projectsFromCostIds = array_keys($this->_costs);
  817. foreach ($projectsFromCostIds as $idProject) $projectIds[$idProject] = true;
  818. $projectsFromPlanIds = array_keys($this->_plan);
  819. foreach ($projectsFromPlanIds as $idProject) $projectIds[$idProject] = true;
  820. foreach ($projectIds as $idProject => $vBool) $this->_projectInfo[$idProject] = new stdClass();
  821. $projectIds = array_keys($projectIds);
  822. $sqlProjIds = "'" . implode("','", $projectIds) . "'";
  823. $sql = "
  824. select p.`ID`
  825. , p.`P_ID`
  826. , p.`path`
  827. , p.`M_DIST_DESC`
  828. , p.`A_ADM_COMPANY` as aclGroupWrite
  829. , p.`A_CLASSIFIED` as aclGroupRead
  830. , p.`L_APPOITMENT_USER` as aclOwner
  831. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  832. where p.`ID` in({$sqlProjIds})
  833. ";
  834. $res = $db->query($sql);
  835. while ($r = $db->fetch($res)) {
  836. $this->_projectInfo[$r->ID]->path = $r->path;
  837. $this->_projectInfo[$r->ID]->M_DIST_DESC = $r->M_DIST_DESC;
  838. $this->_projectInfo[$r->ID]->aclGroupRead = $r->aclGroupRead;
  839. $this->_projectInfo[$r->ID]->hasAccess = $this->_userHasAccessToProject($r);
  840. if (!$this->_projectInfo[$r->ID]->hasAccess) $hasAccessForAllProjects = false;
  841. }
  842. $this->_projectInfo[0]->path = '0';
  843. $this->_projectInfo[0]->M_DIST_DESC = "Wszystkie projekty";
  844. $this->_projectInfo[0]->hasAccess = $hasAccessForAllProjects;
  845. }
  846. public function hasAccessToProject($idProject) {
  847. if ($idProject >= 0) {
  848. if (array_key_exists($idProject, $this->_projectInfo)) {
  849. return V::get('hasAccess', false, $this->_projectInfo[$idProject]);
  850. }
  851. }
  852. return false;
  853. }
  854. public function hasGroupsAccessToProjects($idProject, $groups) {
  855. $selectedUserGroupNames = array();
  856. $userGroups = User::getLdapGroupsNames();
  857. foreach ($groups as $idGroup) {
  858. $selectedUserGroupNames[$idGroup] = $userGroups[$idGroup];
  859. }
  860. if ($idProject >= 0) {
  861. if (array_key_exists($idProject, $this->_projectInfo)) {
  862. $alcGroupRead = V::get('aclGroupRead', null, $this->_projectInfo[$idProject]);
  863. if (!$alcGroupRead) {
  864. return false;
  865. }
  866. if (in_array($alcGroupRead, $selectedUserGroupNames)) {
  867. return true;
  868. }
  869. }
  870. }
  871. return false;
  872. }
  873. private function _userHasAccessToProject($project) {
  874. $groups = User::getLdapGroupsNames();
  875. $userLogin = User::getLogin();
  876. if ($project->aclOwner == $userLogin) {
  877. return true;
  878. }
  879. else if (in_array($project->aclGroupRead, $groups)) {
  880. return true;
  881. }
  882. return false;
  883. }
  884. public function getUsedUserGroups() {
  885. $groups = array();
  886. $userGroups = User::getLdapGroupsNames();
  887. foreach ($this->_projectInfo as $projectInfo) {
  888. if (!empty($projectInfo->aclGroupRead)) {
  889. $groupKey = array_search($projectInfo->aclGroupRead, $userGroups);
  890. if ($groupKey !== false) {
  891. $groups[$groupKey] = $projectInfo->aclGroupRead;
  892. }
  893. }
  894. }
  895. return $groups;
  896. }
  897. private function _reacountCostsFromKoresp() {
  898. $projMonthHasCostSelfIds = array();
  899. foreach ($this->_costs as $kProjId => $vProjInfo) {
  900. $projectPath = $this->_projectInfo[$kProjId]->path;
  901. foreach ($vProjInfo->korespByMonth as $kMonthNum => $vKorespList) {
  902. $this->_createCostIfNotDefined($kProjId, $kMonthNum);
  903. foreach ($vKorespList as $vKoresp) {
  904. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF += $vKoresp->COST;
  905. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF += $vKoresp->INCOME;
  906. }
  907. $projHasCostSelfIds[$kProjId][$kMonthNum] = $projectPath;
  908. }
  909. }
  910. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">$projHasCostSelfIds: ';print_r($projHasCostSelfIds);echo'</pre>';
  911. foreach ($projHasCostSelfIds as $kProjId => $vProjMonthsList) {
  912. if ($kProjId <= 0) continue;
  913. foreach ($vProjMonthsList as $kMonthNum => $vProjPath) {
  914. $vProjPathIds = explode('-', $vProjPath);
  915. $vProjPathIds = array_reverse($vProjPathIds);
  916. $vProjMonthCostSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF;
  917. $vProjMonthIncomeSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF;
  918. foreach ($vProjPathIds as $vProjId) {
  919. if ($vProjId == $kProjId) continue;
  920. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  921. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_CHILD += $vProjMonthCostSelf;
  922. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_CHILD += $vProjMonthIncomeSelf;
  923. }
  924. }
  925. }
  926. // recount total
  927. foreach ($this->_costs as $vProjId => $vProjInfo) {
  928. foreach ($vProjInfo->costsByMonth as $kMonthNum => $vCost) {
  929. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  930. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_TOTAL = $vCost->COST_SELF + $vCost->COST_CHILD;
  931. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_TOTAL = $vCost->INCOME_SELF + $vCost->INCOME_CHILD;
  932. }
  933. }
  934. }
  935. private function _createCostIfNotDefined($projId, $monthNum) {
  936. if (empty($this->_costs[$projId]->costsByMonth[$monthNum])) {
  937. $vEmptyCost = new stdClass();
  938. $vEmptyCost->COST_SELF = 0;
  939. $vEmptyCost->INCOME_SELF = 0;
  940. $vEmptyCost->COST_CHILD = 0;
  941. $vEmptyCost->INCOME_CHILD = 0;
  942. $vEmptyCost->COST_TOTAL = 0;
  943. $vEmptyCost->INCOME_TOTAL = 0;
  944. $this->_costs[$projId]->costsByMonth[$monthNum] = $vEmptyCost;
  945. }
  946. }
  947. public function _buildProjectTree() {
  948. $this->_projectPathsOrder = array();
  949. foreach ($this->_projectInfo as $idProject => $projectInfo) {
  950. $this->_projectPathsOrder[$projectInfo->path] = $idProject;
  951. }
  952. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths: ';print_r($this->_projectPathsOrder);echo'</pre>';
  953. uksort($this->_projectPathsOrder, array($this, 'sortPathsCallback'));
  954. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths sorted: ';print_r($this->_projectPathsOrder);echo'</pre>';
  955. return $this->_projectPathsOrder;
  956. }
  957. public function sortPathsCallback($a, $b) {
  958. $ea = explode('-', $a);
  959. $eb = explode('-', $b);
  960. $la = count($ea);
  961. $lb = count($eb);
  962. $lmin = min($la, $lb);
  963. for ($i = 0; $i < $lmin; $i++) {
  964. if ($ea[$i] < $eb[$i]) {
  965. return -1;
  966. } else if ($ea[$i] > $eb[$i]) {
  967. return 1;
  968. }
  969. }
  970. return $la - $lb;
  971. }
  972. public function updatePaths() {
  973. $sqlList = array();
  974. $sqlList['updateAllPaths'] = <<<SQL
  975. update `projects_budget_year_month` b
  976. set path = (select coalesce(
  977. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=b.`ID_PROJECT` limit 1)
  978. , '?'));
  979. SQL;
  980. $db = DB::getDB();
  981. if ($db->has_errors()) {
  982. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  983. }
  984. foreach ($sqlList as $sqlName => $sql) {
  985. $res = $db->query($sql);
  986. if ($db->has_errors()) {
  987. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  988. }
  989. }
  990. }
  991. public function reinstall() {
  992. $sqlList = array();
  993. $sqlList['RemoveTrigger_BudgetPlan_BeforeInsert'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_INSERT`";
  994. $sqlList['CreateTrigger_BudgetPlan_BeforeInsert'] = "
  995. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_INSERT` BEFORE INSERT ON `projects_budget_year_month`
  996. FOR EACH ROW BEGIN
  997. IF NEW.ID_PROJECT IS NOT NULL and NEW.ID_PROJECT>0 THEN
  998. SET NEW.path = (select coalesce(
  999. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  1000. , '?'
  1001. ));
  1002. END IF;
  1003. END
  1004. ";
  1005. $sqlList['RemoveTrigger_BudgetPlan_BeforeUpdate'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_UPDATE `";
  1006. // throws errors:
  1007. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected ID=PARENT_ID' doesn't exist
  1008. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Parent item not exists' doesn't exist
  1009. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected in path' doesn't exist
  1010. $sqlList['CreateTrigger_BudgetPlan_BeforeUpdate'] = "
  1011. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_UPDATE` BEFORE UPDATE ON `projects_budget_year_month`
  1012. FOR EACH ROW BEGIN
  1013. IF NEW.ID_PROJECT IS NULL THEN
  1014. SET NEW.path = '';
  1015. ELSEIF OLD.ID_PROJECT IS NULL or NEW.ID_PROJECT<>OLD.ID_PROJECT THEN
  1016. IF NEW.ID_PROJECT>0 THEN
  1017. SET NEW.path = (select coalesce(
  1018. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  1019. , '?'));
  1020. ELSE
  1021. SET NEW.path = '';
  1022. END IF;
  1023. END IF;
  1024. END
  1025. ";
  1026. $db = DB::getDB();
  1027. if ($db->has_errors()) {
  1028. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  1029. }
  1030. foreach ($sqlList as $sqlName => $sql) {
  1031. $res = $db->query($sql);
  1032. if ($db->has_errors()) {
  1033. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  1034. }
  1035. }
  1036. }
  1037. }