Budget.php 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203
  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. $transferToId = V::get('TRANSFER_OPPOSITE_ID_PROJECT_TO', 0, $vKorespInfo);
  616. $transferFromId = V::get('TRANSFER_OPPOSITE_ID_PROJECT_FROM', 0, $vKorespInfo);
  617. $rowStyle = ($transferToId > 0)? 'font-style:italic;color:#bbb;' : '';
  618. ?>
  619. <tr>
  620. <td class="p2 r nr"><?php echo $vKorespInfo->ID; ?></td>
  621. <td class="p2"
  622. style="max-width:400px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;<?php echo $rowStyle; ?>"
  623. title="<?php echo $vKorespInfo->K_ZAWARTOS; ?>"><?php echo $vKorespInfo->K_ZAWARTOS; ?></td>
  624. <?php $vKorespCostOut = number_format($vKorespInfo->COST, 2); ?>
  625. <td class="cell-cost cell-cost-only_child"><?php echo $vKorespCostOut; ?></td>
  626. <td class="p2 cell-transfer_opposite_id_project">
  627. <?php if ($transferFromId > 0) : ?>
  628. Transfer z projektu nr <?php echo $transferFromId; ?>
  629. <?php endif; ?>
  630. <?php if ($transferToId > 0) : ?>
  631. Transfer do projektu nr <?php echo $transferToId; ?>
  632. <?php endif; ?>
  633. </td>
  634. </tr>
  635. <?php endforeach; ?>
  636. </tbody>
  637. <?php endforeach; ?>
  638. <?php endif; ?>
  639. <?php endforeach; ?>
  640. </table>
  641. <script>
  642. jQuery(document).ready(function(){
  643. jQuery('.ttip').tooltip();
  644. });
  645. function selectProject(n) {
  646. var $n = jQuery(n);
  647. var $p = $n.parent().parent();
  648. if (n.checked) {
  649. $p.addClass('row-selected');
  650. } else {
  651. $p.removeClass('row-selected');
  652. }
  653. markSubProjects($p, $p.data('path'), n.checked);
  654. function markSubProjects($p, path, checked) {
  655. var $nextRow = $p.next('tr'),
  656. nextPath = $nextRow.data('path'),
  657. nextCheckbox = $nextRow.find('input[type="checkbox"]').get(0);
  658. if (0 !== nextPath.indexOf(path)) {
  659. return;
  660. }
  661. if (checked) {
  662. $nextRow.addClass('row-selected');
  663. } else {
  664. $nextRow.removeClass('row-selected');
  665. }
  666. nextCheckbox.checked = checked;
  667. markSubProjects($nextRow, path, checked);
  668. }
  669. }
  670. function showHideAll(n) {
  671. if (n.checked) {
  672. jQuery('#zestawienie-kosztow-projektow').find('tbody').addClass('showOnlySelected');
  673. } else {
  674. jQuery('#zestawienie-kosztow-projektow').find('tbody').removeClass('showOnlySelected');
  675. }
  676. }
  677. </script>
  678. <?php
  679. }
  680. public function getCost($idProject, $month) {
  681. if (!array_key_exists($idProject, $this->_costs)) {
  682. return null;
  683. }
  684. if (!array_key_exists($month, $this->_costs[$idProject]->costsByMonth)) {
  685. return null;
  686. }
  687. return $this->_costs[$idProject]->costsByMonth[$month];
  688. }
  689. public function getPlan($idProject, $month) {
  690. if (!array_key_exists($idProject, $this->_plan)) {
  691. return 0;
  692. }
  693. if (!array_key_exists($month, $this->_plan[$idProject])) {
  694. return 0;
  695. }
  696. return $this->_plan[$idProject][$month];
  697. }
  698. public function fetchDataByYear($year) {
  699. $this->_fetchCostsByYear($year);
  700. $this->_fetchPlanByYear($year);
  701. $this->_fetchProjectInfo();
  702. $this->_buildProjectTree();
  703. $this->_reacountCostsFromKoresp();
  704. return count($this->_projectInfo) > 1;// $this->_projectInfo[0] - Wszystkie projekty
  705. }
  706. public function _fetchPlanByYear($year) {
  707. $db = DB::getDB();
  708. $this->_plan = array();
  709. $sql = "
  710. select plan.`ID`
  711. , plan.`ID_PROJECT` AS `ID_PROJECT`
  712. , plan.`MONTH_1_VALUE`
  713. , plan.`MONTH_2_VALUE`
  714. , plan.`MONTH_3_VALUE`
  715. , plan.`MONTH_4_VALUE`
  716. , plan.`MONTH_5_VALUE`
  717. , plan.`MONTH_6_VALUE`
  718. , plan.`MONTH_7_VALUE`
  719. , plan.`MONTH_8_VALUE`
  720. , plan.`MONTH_9_VALUE`
  721. , plan.`MONTH_10_VALUE`
  722. , plan.`MONTH_11_VALUE`
  723. , plan.`MONTH_12_VALUE`
  724. from `projects_budget_year_month` plan
  725. where plan.`year`='{$year}'
  726. -- TODO: acl
  727. ";
  728. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  729. $res = $db->query($sql);
  730. while ($r = $db->fetch($res)) {
  731. $plan = array();
  732. for ($i = 1; $i <= 12; $i++) {
  733. $plan[$i] = V::get("MONTH_{$i}_VALUE", 0, $r);
  734. }
  735. $this->_plan[$r->ID_PROJECT] = $plan;
  736. }
  737. return $this->_plan;
  738. }
  739. public function _fetchCostsByYear($year) {
  740. $db = DB::getDB();
  741. $this->_costs = array();
  742. $sql = "
  743. select k.`ID`
  744. , k.`ID_PROJECT` AS `ID_PROJECT`
  745. , date_format(k.`K_DATA_OTRZYMANEJ_KORESP`,'%Y-%m') AS `MONTH`
  746. , k.`COST_VALUE` AS `COST`
  747. , k.`INCOME_VALUE` AS `INCOME`
  748. , k.`TRANSFER_OPPOSITE_ID_PROJECT`
  749. , k.`path`
  750. , IF(k.`TRANSFER_OPPOSITE_ID_PROJECT`>0
  751. , (select p.`path`
  752. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  753. where p.`ID`=k.`TRANSFER_OPPOSITE_ID_PROJECT`
  754. limit 1
  755. )
  756. , '') as TRANSFER_OPPOSITE_PROJECT_PATH
  757. , k.`K_ZAWARTOS`
  758. from `IN7_DZIENNIK_KORESP` k
  759. where ((k.`COST_VALUE` != 0) or (k.`INCOME_VALUE` != 0))
  760. and k.`K_DATA_OTRZYMANEJ_KORESP` like '{$year}-%'
  761. -- TODO: acl
  762. ";
  763. //echo'<pre style="border:1px solid red;overflow:auto;max-height:400px">';print_r($sql);echo'</pre>';
  764. $res = $db->query($sql);
  765. while ($r = $db->fetch($res)) {
  766. $vProjId = ($r->ID_PROJECT > 0)? $r->ID_PROJECT : 0;
  767. $vProjPaths = array();
  768. $vProjIds = array(0 => true);
  769. if ($r->ID_PROJECT > 0) {
  770. $vProjPaths[] = $r->ID_PROJECT;
  771. if (!empty($r->path)) {
  772. $vProjPaths[] = $r->path;
  773. }
  774. }
  775. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  776. $vProjPaths[] = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  777. if (!empty($r->TRANSFER_OPPOSITE_PROJECT_PATH)) {
  778. $vProjPaths[] = $r->TRANSFER_OPPOSITE_PROJECT_PATH;
  779. }
  780. }
  781. if (!empty($vProjPaths)) {
  782. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $vProjPaths = ' . json_encode($vProjPaths) . '</p>';
  783. $vProjPaths = implode('-', $vProjPaths);
  784. $projIds = explode('-', $vProjPaths);
  785. //echo'<p>DBG:$r->ID_PROJECT['.$r->ID_PROJECT.']: $projIds = ' . json_encode($projIds) . '</p>';
  786. foreach ($projIds as $vProjId) {
  787. if ($vProjId > 0) $vProjIds[$vProjId] = true;
  788. }
  789. }
  790. {
  791. $projectZeroInfo = new stdClass();
  792. $projectZeroInfo->ID_PROJECT = 0;
  793. $projectZeroInfo->costsByMonth = array();
  794. $projectZeroInfo->korespByMonth = array();
  795. $this->_costs[0] = $projectZeroInfo;
  796. }
  797. foreach ($vProjIds as $vProjId => $vBool) {
  798. if (!array_key_exists($vProjId, $this->_costs)) {
  799. $projectInfo = new stdClass();
  800. $projectInfo->ID_PROJECT = $vProjId;
  801. $projectInfo->costsByMonth = array();
  802. $projectInfo->korespByMonth = array();
  803. $this->_costs[$vProjId] = $projectInfo;
  804. }
  805. }
  806. $korespInfo = new stdClass();
  807. $korespInfo->ID = $r->ID;
  808. $korespInfo->MONTH = $r->MONTH;
  809. $korespInfo->K_ZAWARTOS = $r->K_ZAWARTOS;
  810. $monthNum = intval(substr($r->MONTH, 5, 2));
  811. if ($r->ID_PROJECT > 0) {
  812. if ($r->TRANSFER_OPPOSITE_ID_PROJECT > 0) {
  813. $korespOppositeInfo = clone $korespInfo;
  814. $korespInfo->COST = -1 * $r->COST;
  815. $korespInfo->INCOME = -1 * $r->INCOME;
  816. $korespInfo->TRANSFER_OPPOSITE_ID_PROJECT_TO = $r->TRANSFER_OPPOSITE_ID_PROJECT;
  817. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  818. $korespOppositeInfo->COST = $r->COST;
  819. $korespOppositeInfo->INCOME = $r->INCOME;
  820. $korespOppositeInfo->TRANSFER_OPPOSITE_ID_PROJECT_FROM = $r->ID_PROJECT;
  821. $this->_costs[$r->TRANSFER_OPPOSITE_ID_PROJECT]->korespByMonth[$monthNum][] = $korespOppositeInfo;
  822. } else {
  823. $korespInfo->COST = $r->COST;
  824. $korespInfo->INCOME = $r->INCOME;
  825. $this->_costs[$r->ID_PROJECT]->korespByMonth[$monthNum][] = $korespInfo;
  826. }
  827. } else {
  828. $korespInfo->COST = $r->COST;
  829. $korespInfo->INCOME = $r->INCOME;
  830. $this->_costs[0]->korespByMonth[$monthNum][] = $korespInfo;
  831. }
  832. }
  833. return $this->_costs;
  834. }
  835. private function _fetchProjectInfo() {
  836. $db = DB::getDB();
  837. $hasAccessForAllProjects = true;
  838. $projectIds = array();
  839. $projectsFromCostIds = array_keys($this->_costs);
  840. foreach ($projectsFromCostIds as $idProject) $projectIds[$idProject] = true;
  841. $projectsFromPlanIds = array_keys($this->_plan);
  842. foreach ($projectsFromPlanIds as $idProject) $projectIds[$idProject] = true;
  843. foreach ($projectIds as $idProject => $vBool) $this->_projectInfo[$idProject] = new stdClass();
  844. $projectIds = array_keys($projectIds);
  845. $sqlProjIds = "'" . implode("','", $projectIds) . "'";
  846. $sql = "
  847. select p.`ID`
  848. , p.`P_ID`
  849. , p.`path`
  850. , p.`M_DIST_DESC`
  851. , p.`A_ADM_COMPANY` as aclGroupWrite
  852. , p.`A_CLASSIFIED` as aclGroupRead
  853. , p.`L_APPOITMENT_USER` as aclOwner
  854. from `IN7_MK_BAZA_DYSTRYBUCJI` p
  855. where p.`ID` in({$sqlProjIds})
  856. ";
  857. $res = $db->query($sql);
  858. while ($r = $db->fetch($res)) {
  859. $this->_projectInfo[$r->ID]->path = $r->path;
  860. $this->_projectInfo[$r->ID]->M_DIST_DESC = $r->M_DIST_DESC;
  861. $this->_projectInfo[$r->ID]->aclGroupRead = $r->aclGroupRead;
  862. $this->_projectInfo[$r->ID]->hasAccess = $this->_userHasAccessToProject($r);
  863. if (!$this->_projectInfo[$r->ID]->hasAccess) $hasAccessForAllProjects = false;
  864. }
  865. $this->_projectInfo[0]->path = '0';
  866. $this->_projectInfo[0]->M_DIST_DESC = "Wszystkie projekty";
  867. $this->_projectInfo[0]->hasAccess = $hasAccessForAllProjects;
  868. }
  869. public function hasAccessToProject($idProject) {
  870. if ($idProject >= 0) {
  871. if (array_key_exists($idProject, $this->_projectInfo)) {
  872. return V::get('hasAccess', false, $this->_projectInfo[$idProject]);
  873. }
  874. }
  875. return false;
  876. }
  877. public function hasGroupsAccessToProjects($idProject, $groups) {
  878. $selectedUserGroupNames = array();
  879. $userGroups = User::getLdapGroupsNames();
  880. foreach ($groups as $idGroup) {
  881. $selectedUserGroupNames[$idGroup] = $userGroups[$idGroup];
  882. }
  883. if ($idProject >= 0) {
  884. if (array_key_exists($idProject, $this->_projectInfo)) {
  885. $alcGroupRead = V::get('aclGroupRead', null, $this->_projectInfo[$idProject]);
  886. if (!$alcGroupRead) {
  887. return false;
  888. }
  889. if (in_array($alcGroupRead, $selectedUserGroupNames)) {
  890. return true;
  891. }
  892. }
  893. }
  894. return false;
  895. }
  896. private function _userHasAccessToProject($project) {
  897. $groups = User::getLdapGroupsNames();
  898. $userLogin = User::getLogin();
  899. if ($project->aclOwner == $userLogin) {
  900. return true;
  901. }
  902. else if (in_array($project->aclGroupRead, $groups)) {
  903. return true;
  904. }
  905. return false;
  906. }
  907. public function getUsedUserGroups() {
  908. $groups = array();
  909. $userGroups = User::getLdapGroupsNames();
  910. foreach ($this->_projectInfo as $projectInfo) {
  911. if (!empty($projectInfo->aclGroupRead)) {
  912. $groupKey = array_search($projectInfo->aclGroupRead, $userGroups);
  913. if ($groupKey !== false) {
  914. $groups[$groupKey] = $projectInfo->aclGroupRead;
  915. }
  916. }
  917. }
  918. return $groups;
  919. }
  920. private function _reacountCostsFromKoresp() {
  921. $projMonthHasCostSelfIds = array();
  922. foreach ($this->_costs as $kProjId => $vProjInfo) {
  923. $projectPath = $this->_projectInfo[$kProjId]->path;
  924. foreach ($vProjInfo->korespByMonth as $kMonthNum => $vKorespList) {
  925. $this->_createCostIfNotDefined($kProjId, $kMonthNum);
  926. foreach ($vKorespList as $vKoresp) {
  927. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF += $vKoresp->COST;
  928. $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF += $vKoresp->INCOME;
  929. }
  930. $projHasCostSelfIds[$kProjId][$kMonthNum] = $projectPath;
  931. }
  932. }
  933. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">$projHasCostSelfIds: ';print_r($projHasCostSelfIds);echo'</pre>';
  934. foreach ($projHasCostSelfIds as $kProjId => $vProjMonthsList) {
  935. if ($kProjId <= 0) continue;
  936. foreach ($vProjMonthsList as $kMonthNum => $vProjPath) {
  937. $vProjPathIds = explode('-', $vProjPath);
  938. $vProjPathIds = array_reverse($vProjPathIds);
  939. $vProjMonthCostSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->COST_SELF;
  940. $vProjMonthIncomeSelf = $this->_costs[$kProjId]->costsByMonth[$kMonthNum]->INCOME_SELF;
  941. foreach ($vProjPathIds as $vProjId) {
  942. if ($vProjId == $kProjId) continue;
  943. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  944. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_CHILD += $vProjMonthCostSelf;
  945. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_CHILD += $vProjMonthIncomeSelf;
  946. }
  947. }
  948. }
  949. // recount total
  950. foreach ($this->_costs as $vProjId => $vProjInfo) {
  951. foreach ($vProjInfo->costsByMonth as $kMonthNum => $vCost) {
  952. $this->_createCostIfNotDefined($vProjId, $kMonthNum);
  953. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->COST_TOTAL = $vCost->COST_SELF + $vCost->COST_CHILD;
  954. $this->_costs[$vProjId]->costsByMonth[$kMonthNum]->INCOME_TOTAL = $vCost->INCOME_SELF + $vCost->INCOME_CHILD;
  955. }
  956. }
  957. }
  958. private function _createCostIfNotDefined($projId, $monthNum) {
  959. if (empty($this->_costs[$projId]->costsByMonth[$monthNum])) {
  960. $vEmptyCost = new stdClass();
  961. $vEmptyCost->COST_SELF = 0;
  962. $vEmptyCost->INCOME_SELF = 0;
  963. $vEmptyCost->COST_CHILD = 0;
  964. $vEmptyCost->INCOME_CHILD = 0;
  965. $vEmptyCost->COST_TOTAL = 0;
  966. $vEmptyCost->INCOME_TOTAL = 0;
  967. $this->_costs[$projId]->costsByMonth[$monthNum] = $vEmptyCost;
  968. }
  969. }
  970. public function _buildProjectTree() {
  971. $this->_projectPathsOrder = array();
  972. foreach ($this->_projectInfo as $idProject => $projectInfo) {
  973. $this->_projectPathsOrder[$projectInfo->path] = $idProject;
  974. }
  975. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths: ';print_r($this->_projectPathsOrder);echo'</pre>';
  976. uksort($this->_projectPathsOrder, array($this, 'sortPathsCallback'));
  977. //echo'<pre style="width:600px;border:1px solid red;max-height:300px;overflow:auto;">projPaths sorted: ';print_r($this->_projectPathsOrder);echo'</pre>';
  978. return $this->_projectPathsOrder;
  979. }
  980. public function sortPathsCallback($a, $b) {
  981. $ea = explode('-', $a);
  982. $eb = explode('-', $b);
  983. $la = count($ea);
  984. $lb = count($eb);
  985. $lmin = min($la, $lb);
  986. for ($i = 0; $i < $lmin; $i++) {
  987. if ($ea[$i] < $eb[$i]) {
  988. return -1;
  989. } else if ($ea[$i] > $eb[$i]) {
  990. return 1;
  991. }
  992. }
  993. return $la - $lb;
  994. }
  995. public function updatePaths() {
  996. $sqlList = array();
  997. $sqlList['updateAllPaths'] = <<<SQL
  998. update `projects_budget_year_month` b
  999. set path = (select coalesce(
  1000. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=b.`ID_PROJECT` limit 1)
  1001. , '?'));
  1002. SQL;
  1003. $db = DB::getDB();
  1004. if ($db->has_errors()) {
  1005. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  1006. }
  1007. foreach ($sqlList as $sqlName => $sql) {
  1008. $res = $db->query($sql);
  1009. if ($db->has_errors()) {
  1010. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  1011. }
  1012. }
  1013. }
  1014. public function reinstall() {
  1015. $sqlList = array();
  1016. $sqlList['RemoveTrigger_BudgetPlan_BeforeInsert'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_INSERT`";
  1017. $sqlList['CreateTrigger_BudgetPlan_BeforeInsert'] = "
  1018. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_INSERT` BEFORE INSERT ON `projects_budget_year_month`
  1019. FOR EACH ROW BEGIN
  1020. IF NEW.ID_PROJECT IS NOT NULL and NEW.ID_PROJECT>0 THEN
  1021. SET NEW.path = (select coalesce(
  1022. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  1023. , '?'
  1024. ));
  1025. END IF;
  1026. END
  1027. ";
  1028. $sqlList['RemoveTrigger_BudgetPlan_BeforeUpdate'] = "DROP TRIGGER IF EXISTS `projects_budget_year_month_BEFORE_UPDATE `";
  1029. // throws errors:
  1030. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected ID=PARENT_ID' doesn't exist
  1031. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Parent item not exists' doesn't exist
  1032. // #1146 - Table '{DATABASE_NAME}.P5-MSG:Route_FixZasobPath:ERROR: Loop detected in path' doesn't exist
  1033. $sqlList['CreateTrigger_BudgetPlan_BeforeUpdate'] = "
  1034. CREATE DEFINER=`root`@`localhost` TRIGGER `projects_budget_year_month_BEFORE_UPDATE` BEFORE UPDATE ON `projects_budget_year_month`
  1035. FOR EACH ROW BEGIN
  1036. IF NEW.ID_PROJECT IS NULL THEN
  1037. SET NEW.path = '';
  1038. ELSEIF OLD.ID_PROJECT IS NULL or NEW.ID_PROJECT<>OLD.ID_PROJECT THEN
  1039. IF NEW.ID_PROJECT>0 THEN
  1040. SET NEW.path = (select coalesce(
  1041. (select p.`path` from `IN7_MK_BAZA_DYSTRYBUCJI` p where p.`ID`=NEW.`ID_PROJECT` limit 1)
  1042. , '?'));
  1043. ELSE
  1044. SET NEW.path = '';
  1045. END IF;
  1046. END IF;
  1047. END
  1048. ";
  1049. $db = DB::getDB();
  1050. if ($db->has_errors()) {
  1051. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  1052. }
  1053. foreach ($sqlList as $sqlName => $sql) {
  1054. $res = $db->query($sql);
  1055. if ($db->has_errors()) {
  1056. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  1057. }
  1058. }
  1059. }
  1060. public function planAction() {
  1061. SE_Layout::gora();
  1062. SE_Layout::menu();
  1063. $args = array();
  1064. $args['year'] = V::get('year', '', $_REQUEST, 'int');
  1065. if ($args['year'] > 0) {
  1066. $_REQUEST['ff_YEAR'] = $_GET['ff_YEAR'] = $args['year'];
  1067. }
  1068. $this->menu($args['year']);
  1069. if ($args['year'] > 0) {
  1070. ?>
  1071. <div class="container">
  1072. <a class="btn btn-xs btn-default" href="#">Utwórz plan na kolejny rok na podstawie danych z <?php echo $args['year']; ?> roku</a>
  1073. </div>
  1074. <?php
  1075. }
  1076. $zasobObj = ProcesHelper::getZasobTableInfoByUri('default_db/projects_budget_year_month');
  1077. if (!$zasobObj) {
  1078. ?>
  1079. <div class="alert alert-danger">
  1080. Zasob Tabela Plan budżetu (projects_budget_year_month) nie istnieje
  1081. </div>
  1082. <?php
  1083. // TODO: btn utwórz
  1084. SE_Layout::dol();
  1085. return;
  1086. }
  1087. $userAcl = User::getAcl();
  1088. $userAcl->fetchGroups();
  1089. if (!$userAcl->hasTableAcl($zasobObj->ID)) {
  1090. ?>
  1091. <div class="alert alert-danger">
  1092. Brak uprawnień do tabeli Plan budżetu (projects_budget_year_month)
  1093. </div>
  1094. <?php
  1095. SE_Layout::dol();
  1096. return;
  1097. }
  1098. $tblAcl = $userAcl->getTableAcl($zasobObj->ID);
  1099. $forceTblAclInit = ('1' == V::get('_force', '', $_GET));
  1100. $tblAcl->init($forceTblAclInit);
  1101. $forceFilterInit = array();
  1102. $filterInit = new stdClass();
  1103. $filterInit->currSortCol = 'ID';
  1104. $filterInit->currSortFlip = 'desc';
  1105. foreach ($_GET as $k => $v) {
  1106. if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && !empty($v)) {// filter prefix
  1107. $filterInit->$k = $v;
  1108. }
  1109. else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && !empty($v)) {// special filter prefix
  1110. $filterInit->$k = $v;
  1111. }
  1112. else if (strlen($k) > 4 && substr($k, 0, 3) == 'ff_' && !empty($v)) {// force filter prefix
  1113. $fldName = substr($k, 3);
  1114. $forceFilterInit[$fldName] = $v;
  1115. }
  1116. }
  1117. $tbl = new TableAjax($tblAcl);
  1118. $tblLabel = array();
  1119. if (!empty($zasobObj->DESC_PL)) $tblLabel []= $zasobObj->DESC_PL;
  1120. if (!empty($zasobObj->OPIS)) $tblLabel []= $zasobObj->OPIS;
  1121. $tblLabel = implode(" - ", $tblLabel);
  1122. $tbl->setLabel($tblLabel);
  1123. $tbl->setFilterInit($filterInit);
  1124. if (!empty($forceFilterInit)) $tbl->setForceFilterInit($forceFilterInit);
  1125. $tbl->addRowFunction('edit');
  1126. $tbl->addRowFunction('hist');
  1127. $tbl->addRowFunction('files');
  1128. $tbl->addRowFunction('cp');
  1129. $tbl->showProcesInit(false);
  1130. echo $tbl->render();
  1131. SE_Layout::dol();
  1132. }
  1133. }