Budget.php 30 KB

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