Budget.php 43 KB

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