ChangeOwner.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('Response');
  4. Lib::loadClass('SchemaFactory');
  5. class Route_UrlAction_ChangeOwner extends RouteBase {
  6. function defaultAction() {
  7. if ('changeOwnerPostTask' === V::get('_postTask', '', $_POST)) {
  8. UI::layout([ $this, 'changeOwnerView' ]);
  9. } else {
  10. UI::layout([ $this, 'defaultView' ]);
  11. }
  12. }
  13. function titleView() {
  14. echo UI::h('h3', [], [
  15. "Zmiana osoby odpowiedzialnej",
  16. '<br>',
  17. UI::h('small', [], "Zmiany w polu 'Osoba odpowiedzialna' we wszystkich rekordach wybranej tabeli."),
  18. ]);
  19. }
  20. function defaultView() {
  21. $this->titleView();
  22. $SELECTED_TABLE_ID = V::get('id_table', '', $_GET);
  23. if (!$SELECTED_TABLE_ID) {
  24. $fromNs = V::get('_fromNamespace', '', $_GET);
  25. if (!empty($fromNs)) {
  26. try {
  27. $acl = ACL::getAclByNamespace($fromNs);
  28. $SELECTED_TABLE_ID = $acl->getID();
  29. } catch (Exception $e) {
  30. DBG::log($e);
  31. }
  32. }
  33. }
  34. $SELECTED_FROM_OWNER_ID = V::get('id_from', '', $_GET);
  35. $SELECTED_TO_OWNER_ID = V::get('id_to', '', $_GET);
  36. $SELECTED_TABLE_INFO = ($SELECTED_TABLE_ID) ? $this->getTableInfo($SELECTED_TABLE_ID) : [];
  37. $SELECTED_FROM_OWNER_INFO = ($SELECTED_FROM_OWNER_ID) ? $this->getUserInfo($SELECTED_FROM_OWNER_ID) : [];
  38. $SELECTED_TO_OWNER_INFO = ($SELECTED_TO_OWNER_ID) ? $this->getUserInfo($SELECTED_TO_OWNER_ID) : [];
  39. DBG::log($SELECTED_TABLE_INFO, 'array', "SELECTED_TABLE_INFO ({$SELECTED_TABLE_ID})");
  40. DBG::log($SELECTED_FROM_OWNER_INFO, 'array', "SELECTED_FROM_OWNER_INFO ({$SELECTED_FROM_OWNER_ID})");
  41. DBG::log($SELECTED_TO_OWNER_INFO, 'array', "SELECTED_TO_OWNER_INFO ({$SELECTED_TO_OWNER_ID})");
  42. echo UI::h('div', [ 'id' => "p5-change-owner-widget" ]);
  43. echo UI::h('style', [], "
  44. .dropdown-menu>li>a { max-width: 800px; }
  45. ");
  46. echo UI::h('script', [ 'src' => "static/vendor.js" ]); // window.p5VendorJs: {React, ReactDOM, createReactClass, Redux}
  47. UI::inlineJS(__FILE__ . '.view.js', [
  48. 'HTML_ID' => "p5-change-owner-widget",
  49. 'URL_SEARCH_TABLE_LIST' => $this->getLink('searchTableListAjaxTask'),
  50. 'URL_SEARCH_USER_LIST' => $this->getLink('searchUserListAjaxTask'),
  51. 'URL_FETCH_INFO' => $this->getLink('fetchInfoAjaxTask'),
  52. 'SELECTED_TABLE_ID' => $SELECTED_TABLE_ID,
  53. 'SELECTED_TABLE_INFO' => $SELECTED_TABLE_INFO,
  54. 'SELECTED_FROM_OWNER_ID' => $SELECTED_FROM_OWNER_ID,
  55. 'SELECTED_FROM_OWNER_INFO' => $SELECTED_FROM_OWNER_INFO,
  56. 'SELECTED_TO_OWNER_ID' => $SELECTED_TO_OWNER_ID,
  57. 'SELECTED_TO_OWNER_INFO' => $SELECTED_TO_OWNER_INFO,
  58. 'CHANGE_OWNER_POST_TASK_NAME' => 'changeOwnerPostTask',
  59. 'URL_BASE' => $this->getLink(),
  60. ]);
  61. }
  62. function changeOwnerView() {
  63. $this->titleView();
  64. $id_table = V::get('id_table', '', $_POST);
  65. $id_from = V::get('id_from', '', $_POST);
  66. $id_to = V::get('id_to', '', $_POST);
  67. if (!$id_table) throw new Exception("Missing id_table");
  68. if (!$id_from) throw new Exception("Missing id_from");
  69. if (!$id_to) throw new Exception("Missing id_to");
  70. $stats = $this->fetchInfoAjaxTask([
  71. 'id_table' => $id_table,
  72. 'id_from' => $id_from,
  73. 'id_to' => $id_to,
  74. ]);
  75. DBG::log($stats, 'array', '$stats');
  76. $tableInfo = $this->getTableInfo($id_table);
  77. DBG::log($tableInfo, 'array', '$tableInfo');
  78. $tableLabel = (!empty($tableInfo)) ? $tableInfo[0]['label'] : "";
  79. echo UI::h('div', [ 'class' => "alert alert-info" ], "Wprowadzanie zmian w tabeli <em>[{$id_table}]</em> {$tableLabel}");
  80. $totalToUpdate = $stats['body']['fromInfo']['total'];
  81. if (!$totalToUpdate) throw new Exception("Brak rekordów do aktualizacji");
  82. $namespace = DB::getPDO()->fetchValue(" select o.namespace from `CRM_#CACHE_ACL_OBJECT` o where o.idZasob = '{$id_table}' limit 1 ");
  83. $acl = ACL::getAclByNamespace($namespace);
  84. $rootTableName = $acl->getRootTableName();
  85. $primaryKeyField = $acl->getPrimaryKeyField();
  86. $sqlTable = DB::getPDO()->identifierQuote($rootTableName);
  87. { // TODO: loop / sql limit
  88. try {
  89. $rowsToUpdate = DB::getPDO()->fetchAll("
  90. select t.ID, t.L_APPOITMENT_USER
  91. from {$sqlTable} t
  92. where t.L_APPOITMENT_USER = :id_from
  93. limit 100
  94. ", [
  95. ':id_from' => $id_from,
  96. ]);
  97. UI::table(['rows'=>$rowsToUpdate]);
  98. } catch (Exception $e) {
  99. // msg: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 't.L_APPOITMENT_USER' in 'field list'"
  100. DBG::log($e);
  101. if ($e->getCode() === '42S22') {
  102. throw new Exception("Brak pola Osoba odpowiedzialna w tabeli '{$namespace}'");
  103. }
  104. throw $e;
  105. }
  106. foreach ($rowsToUpdate as $item) {
  107. try {
  108. DB::getPDO()->update($rootTableName, $primaryKeyField, $item['ID'], [
  109. 'L_APPOITMENT_USER' => $id_to,
  110. 'A_RECORD_UPDATE_DATE' => "NOW()",
  111. 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
  112. ]);
  113. DB::getPDO()->insert($rootTableName . "_HIST", [
  114. 'ID_USERS2' => $item['ID'],
  115. 'L_APPOITMENT_USER' => $id_to,
  116. 'A_RECORD_UPDATE_DATE' => "NOW()",
  117. 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
  118. ]);
  119. } catch (Exception $e) {
  120. DBG::log($e);
  121. UI::alert('danger', [], "Nie udało się zaktualizować rekordu '{$item['ID']}': " . $e->getMessage());
  122. }
  123. }
  124. }
  125. UI::alert('success', "Koniec");
  126. }
  127. function getTableInfo($idTable) {
  128. $userObject = SchemaFactory::loadDefaultObject('UserObject');
  129. $userObject->setIdUser(User::getID());
  130. // $userObject->setIdProcesFilter($filterIdProces);
  131. $items = $userObject->getItems([
  132. 'cols' => [ 'ID_TABLE', 'name', 'label' ], // , 'opis' ],
  133. 'primaryKey' => $idTable,
  134. ]);
  135. // UI::table([ 'rows' => $items ]);
  136. $items = array_map(function ($item) {
  137. // [16] => Array:
  138. // [ID_TABLE] => 16
  139. // [ID_USER] => 4517
  140. // [ID_PROCES] =>
  141. // [db] => 36
  142. // [name] => BUILDINGS
  143. // [label] => Budynki
  144. // [opis] => Budynki - tabela budynków w zasięgu lub zainteresowanych podłączeniem do sieci BIALL-NET.
  145. // [_rootTableName] => BUILDINGS
  146. // [namespace] => default_db/BUILDINGS
  147. return [
  148. 'id' => $item['ID_TABLE'],
  149. 'label' => $this->getTableLabelFromRow($item),
  150. '_item_name' => $item['name'],
  151. '_item_label' => $item['label'],
  152. '_item_name_substr' => substr($item['name'], 0, strlen($item['label'])),
  153. '_item' => $item,
  154. ];
  155. }, $items);
  156. return (!empty($items)) ? [ reset($items) ] : [];
  157. }
  158. function getUserInfo($userLogin) {
  159. return DB::getPDO()->fetchAll("
  160. select u.ADM_ACCOUNT as id
  161. , u.ADM_NAME as label
  162. from ADMIN_USERS u
  163. where u.ADM_ACCOUNT = :login
  164. ", [ ':login' => $userLogin ]);
  165. }
  166. function searchTableListAjaxTaskAction() { Response::sendTryCatchJson([$this, 'searchTableListAjaxTask'], $args = 'JSON_FROM_REQUEST_BODY'); }
  167. function searchTableListAjaxTask($args) {
  168. $query = V::get('query', '', $args);
  169. $wordsQuery = $this->convertQueryToSearchWords($query);
  170. $items = $this->fetchAllowedTableList($wordsQuery);
  171. return [
  172. 'type' => 'success',
  173. 'msg' => 'OK',
  174. '__DBG__args' => $args,
  175. 'body' => [
  176. 'items' => array_values($items),
  177. ]
  178. ];
  179. }
  180. function searchUserListAjaxTaskAction() { Response::sendTryCatchJson([$this, 'searchUserListAjaxTask'], $args = 'JSON_FROM_REQUEST_BODY'); }
  181. function searchUserListAjaxTask($args) {
  182. $query = V::get('query', '', $args);
  183. $wordsQuery = $this->convertQueryToSearchWords($query);
  184. $sqlListWhere = [];
  185. $pdo = DB::getPDO();
  186. foreach ($wordsQuery as $word) {
  187. $sqlListWhere[] = implode(" or ", [
  188. "u.ID like " . $pdo->quote("%{$word}%"),
  189. "u.ADM_ACCOUNT like " . $pdo->quote("%{$word}%"),
  190. "u.ADM_NAME like " . $pdo->quote("%{$word}%"),
  191. ]);
  192. }
  193. $sqlWhere = (!empty($sqlListWhere)) ? implode(" and ", $sqlListWhere) : "1=1";
  194. $items = DB::getPDO()->fetchAll("
  195. select u.ADM_ACCOUNT as id
  196. , u.ADM_NAME as label
  197. from ADMIN_USERS u
  198. where {$sqlWhere}
  199. ", [ ':id' => $idUser ]);
  200. return [
  201. 'type' => 'success',
  202. 'msg' => 'OK',
  203. 'body' => [
  204. 'items' => array_values($items),
  205. ]
  206. ];
  207. }
  208. function convertQueryToSearchWords($query) {
  209. $query = trim($query);
  210. if (strlen($query) < 3) throw new Exception("Query must be more then 2 chars");
  211. $wordsQuery = [];
  212. if (false !== strpos($query, ' ')) {
  213. $words = explode(' ', $query);
  214. DBG::log($words, 'array', "\$words from \$query({$query}) - 1");
  215. $words = array_map(function ($word) {
  216. return trim(trim($word), '0');
  217. }, $words);
  218. DBG::log($words, 'array', "\$words from \$query({$query}) - 2");
  219. $words = array_filter($words, function ($word) {
  220. return (strlen($word) > 2);
  221. });
  222. DBG::log($words, 'array', "\$words from \$query({$query}) - 3");
  223. if (empty($words)) throw new Exception("Query words must be more then 2 chars");
  224. $wordsQuery = $words;
  225. } else {
  226. $wordsQuery[] = $query;
  227. }
  228. return $wordsQuery;
  229. }
  230. function fetchAllowedTableList($wordsQuery) {
  231. $userObject = SchemaFactory::loadDefaultObject('UserObject');
  232. $userObject->setIdUser(User::getID());
  233. // $userObject->setIdProcesFilter($filterIdProces);
  234. $items = $userObject->getItems([
  235. 'cols' => [ 'ID_TABLE', 'name', 'label' ], // , 'opis' ],
  236. ]);
  237. // UI::table([ 'rows' => $items ]);
  238. $items = array_filter($items, function ($item) use ($wordsQuery) {
  239. foreach ($wordsQuery as $word) {
  240. // DBG::log([
  241. // 'is1' => ($item['ID_TABLE'] == $word),
  242. // 'is2' => (false !== stripos($item['name'], $word)),
  243. // 'is3' => (false !== stripos($item['label'], $word)),
  244. // '$item' => $item,
  245. // ], 'array', "DBG:array_filter({$item['ID_TABLE']}) '{$word}'");
  246. if ($item['ID_TABLE'] == $word) return true;
  247. if (false !== stripos($item['name'], $word)) return true;
  248. if (false !== stripos($item['label'], $word)) return true;
  249. }
  250. return false;
  251. });
  252. // UI::table([ 'rows' => $items ]);
  253. return array_map(function ($item) {
  254. // [16] => Array:
  255. // [ID_TABLE] => 16
  256. // [ID_USER] => 4517
  257. // [ID_PROCES] =>
  258. // [db] => 36
  259. // [name] => BUILDINGS
  260. // [label] => Budynki
  261. // [opis] => Budynki - tabela budynków w zasięgu lub zainteresowanych podłączeniem do sieci BIALL-NET.
  262. // [_rootTableName] => BUILDINGS
  263. // [namespace] => default_db/BUILDINGS
  264. return [
  265. 'id' => $item['ID_TABLE'],
  266. '_item_name' => $item['name'],
  267. '_item_label' => $item['label'],
  268. '_item_name_substr' => substr($item['name'], 0, strlen($item['label'])),
  269. 'label' => $this->getTableLabelFromRow($item),
  270. '_item' => $item,
  271. ];
  272. }, $items);
  273. }
  274. function getTableLabelFromRow($item) {
  275. $label = $item['name'];
  276. if (!empty($item['label'])) {
  277. $tableName = (false !== strpos($item['name'], '/')) ? substr($item['name'], strrpos($item['name'], '/') + 1) : $item['name'];
  278. if (strlen($item['label']) < strlen($tableName) && substr($tableName, 0, strlen($item['label'])) === $item['label']) {
  279. $label = $tableName;
  280. } else {
  281. $label = $item['label'];
  282. }
  283. }
  284. // if (!empty($item['opis']) && !empty($item['label'])) {
  285. // }
  286. return $label;
  287. }
  288. function fetchInfoAjaxTaskAction() { Response::sendTryCatchJson([$this, 'fetchInfoAjaxTask'], $args = 'JSON_FROM_REQUEST_BODY'); }
  289. function fetchInfoAjaxTask($args) {
  290. $id_table = V::get('id_table', '', $args);
  291. $id_from = V::get('id_from', '', $args);
  292. $id_to = V::get('id_to', '', $args);
  293. if (!$id_table) throw new Exception("Missing id table");
  294. $responseBody = [];
  295. $responseBody['by_owner'] = []; // [ { login, total }, ... ]
  296. $responseBody['fromInfo'] = []; // { id, name, status, total, ... }
  297. $responseBody['toInfo'] = []; // { id, name, status, total, ... }
  298. $responseBody['errors'] = []; // [ { type, msg } ]
  299. $responseBody['error_missing_owner_field'] = false; // bool
  300. $namespace = DB::getPDO()->fetchValue(" select o.namespace from `CRM_#CACHE_ACL_OBJECT` o where o.idZasob = '{$id_table}' limit 1 ");
  301. $acl = ACL::getAclByNamespace($namespace);
  302. $rootTableName = $acl->getRootTableName();
  303. $sqlTable = DB::getPDO()->identifierQuote($rootTableName);
  304. try {
  305. $responseBody['by_owner'] = DB::getPDO()->fetchAll("
  306. select t.L_APPOITMENT_USER as id, count(*) as total
  307. from {$sqlTable} t
  308. where t.L_APPOITMENT_USER = :id_from or t.L_APPOITMENT_USER = :id_to
  309. group by t.L_APPOITMENT_USER
  310. limit 10
  311. ", [
  312. ':id_from' => ($id_from) ? $id_from : "XXXXX",
  313. ':id_to' => ($id_to) ? $id_to : "XXXXX",
  314. ]);
  315. } catch (Exception $e) {
  316. // msg: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 't.L_APPOITMENT_USER' in 'field list'"
  317. DBG::log($e);
  318. if ($e->getCode() === '42S22') {
  319. $responseBody['error_missing_owner_field'] = true;
  320. }
  321. $responseBody['errors']['by_owner'] = $e->getMessage();
  322. }
  323. $fromInfo = $this->_fetchOwnerInfo($id_from);
  324. $toInfo = $this->_fetchOwnerInfo($id_to);
  325. $responseBody['fromInfo'] = (!$fromInfo) ? null : array_merge($fromInfo, [
  326. 'total' => array_reduce($responseBody['by_owner'], function ($ret, $item) use ($id_from) {
  327. return ($item['id'] === $id_from) ? $item['total'] : $ret;
  328. }, 0)
  329. ]);
  330. $responseBody['toInfo'] = (!$toInfo) ? null : array_merge($toInfo, [
  331. 'total' => array_reduce($responseBody['by_owner'], function ($ret, $item) use ($id_to) {
  332. return ($item['id'] === $id_to) ? $item['total'] : $ret;
  333. }, 0)
  334. ]);
  335. return [
  336. 'type' => 'success',
  337. 'msg' => 'OK',
  338. '__DBG__namespace' => $namespace,
  339. '__DBG__acl' => $acl,
  340. '__DBG__args' => $args,
  341. 'body' => $responseBody,
  342. ];
  343. }
  344. function _fetchOwnerInfo($id) { // @param $id: string || null
  345. return DB::getPDO()->fetchFirst("
  346. select t.ADM_ACCOUNT as id
  347. , t.ADM_NAME as name
  348. , t.EMPLOYEE_TYPE as type
  349. , t.A_STATUS as status
  350. from `ADMIN_USERS` t
  351. where t.ADM_ACCOUNT = :id_from
  352. ", [
  353. ':id_from' => $id,
  354. ]);
  355. }
  356. }