UserContact.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('Windykacja_View');
  4. // Tool for Windykacja status table to store contacts with clients
  5. class Route_UrlAction_UserContact extends RouteBase {
  6. function defaultAction() { UI::layout([ $this, 'defaultView' ]); }
  7. function defaultView() {
  8. $idUser = V::get('id_user', 0, $_GET, 'int');
  9. if ($idUser <= 0) throw new Exception("Missing id client");
  10. echo UI::h('h3', [ 'style' => "margin-bottom: 24px; border-bottom: 1px solid #eee" ], "Dodaj notatkę ze spotkania z klientem {$idUser}");
  11. Windykacja_View::viewUserInfo($idUser);
  12. $postTask = V::get('_postTask', '', $_POST);
  13. switch ($postTask) {
  14. case 'saveConcat': $this->saveContactPostTask($idUser); break;
  15. case '': break;
  16. default: throw new Exception("Not implemented postTask '{$postTask}'");
  17. }
  18. $this->viewSaveContactForm($idUser);
  19. echo UI::h('hr');
  20. $this->viewUserPaymentContactsHistory($idUser);
  21. }
  22. function viewSaveContactForm($idUser) {
  23. $windykInfo = Windykacja_View::getWindykacjaInfo($idUser);
  24. $windykStatusInfo = ($windykInfo) ? $windykInfo['A_STATUS_INFO'] : '';
  25. $windykReminder = ($windykInfo && (
  26. !empty($windykInfo['L_APPOITMENT_USER'])
  27. || ( !empty($windykInfo['L_APPOITMENT_DATE']) && '0000-00-00' != $windykInfo['L_APPOITMENT_DATE'] )
  28. || !empty($windykInfo['L_APPOITMENT_INFO'])
  29. )) ? [
  30. 'Pracownik' => $windykInfo['L_APPOITMENT_USER'],
  31. 'Data przypomnienia' => $windykInfo['L_APPOITMENT_DATE'],
  32. 'Adnotacje' => $windykInfo['L_APPOITMENT_INFO'],
  33. ] : null;
  34. $args = [];
  35. $args['L_APPOITMENT_USER'] = V::get('L_APPOITMENT_USER', User::getLogin(), $_POST);
  36. $args['L_APPOITMENT_DATE'] = V::get('L_APPOITMENT_DATE', date("Y-m-d H:i"), $_POST);
  37. $args['L_APPOITMENT_TYPE'] = V::get('L_APPOITMENT_TYPE', '', $_POST);
  38. $args['L_APPOITMENT_INFO'] = V::get('L_APPOITMENT_INFO', '', $_POST);
  39. $args['change_windykacja_status'] = V::get('change_windykacja_status', '', $_POST);
  40. $args['A_STATUS_INFO'] = V::get('A_STATUS_INFO', $windykStatusInfo, $_POST);
  41. $args['add_reminder'] = V::get('add_reminder', '', $_POST);
  42. $args['REMINDER_USER'] = V::get('REMINDER_USER', User::getLogin(), $_POST);
  43. $args['REMINDER_DATE'] = V::get('REMINDER_DATE', '', $_POST);
  44. $args['REMINDER_INFO'] = V::get('REMINDER_INFO', '', $_POST);
  45. echo UI::h('form', [ 'method' => "POST" ], [
  46. UI::h('input', [ 'type' => "hidden", 'name' => '_postTask', 'value' => "saveConcat" ]),
  47. UI::h('input', [ 'type' => "hidden", 'name' => 'id_user', 'value' => $idUser ]),
  48. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  49. UI::h('div', [ 'class' => "col-md-6" ], [
  50. UI::h('label', [], "Pracownik"),
  51. UI::h('input', [ 'class' => "form-control", 'type' => "text", 'name' => 'L_APPOITMENT_USER', 'value' => $args['L_APPOITMENT_USER'] ]),
  52. ]),
  53. UI::h('div', [ 'class' => "col-md-3" ], [
  54. UI::h('label', [], "Data spotkania"),
  55. UI::h('div', [ 'class' => "input-group" ], [
  56. UI::h('input', [ 'class' => "form-control se_type-datetime", 'data-format' => "yyyy-MM-dd hh:mm", 'type' => "text", 'name' => 'L_APPOITMENT_DATE', 'value' => $args['L_APPOITMENT_DATE'] ]),
  57. UI::h('span', [ 'class' => "input-group-addon" ], [
  58. UI::h('span', [ 'class' => "glyphicon glyphicon-calendar" ]),
  59. ]),
  60. ]),
  61. ]),
  62. UI::h('div', [ 'class' => "col-md-3" ], [
  63. UI::h('label', [], "Rodzaj spotkania"),
  64. UI::h('select', [ 'class' => "form-control", 'name' => 'L_APPOITMENT_TYPE' ], [
  65. UI::h('option', [ 'value' => "" ]),
  66. UI::h('option', array_merge([ 'value' => 'LIVE' ], ('LIVE' === $args['L_APPOITMENT_TYPE']) ? [ 'selected' => "selected" ] : []), "LIVE"),
  67. UI::h('option', array_merge([ 'value' => 'TEL' ], ('TEL' === $args['L_APPOITMENT_TYPE']) ? [ 'selected' => "selected" ] : []), "TEL"),
  68. UI::h('option', array_merge([ 'value' => 'MAIL' ], ('MAIL' === $args['L_APPOITMENT_TYPE']) ? [ 'selected' => "selected" ] : []), "MAIL"),
  69. UI::h('option', array_merge([ 'value' => 'SMS' ], ('SMS' === $args['L_APPOITMENT_TYPE']) ? [ 'selected' => "selected" ] : []), "SMS"),
  70. UI::h('option', array_merge([ 'value' => 'INNE' ], ('INNE' === $args['L_APPOITMENT_TYPE']) ? [ 'selected' => "selected" ] : []), "INNE"),
  71. ]),
  72. ]),
  73. ]),
  74. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  75. UI::h('div', [ 'class' => "col-md-12" ], [
  76. UI::h('label', [], "Notatka ze spotkania"),
  77. UI::h('textarea', [ 'class' => "form-control", 'type' => "text", 'name' => 'L_APPOITMENT_INFO', 'rows' => 3 ], $args['L_APPOITMENT_INFO']),
  78. ]),
  79. ]),
  80. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  81. UI::h('div', [ 'class' => "col-md-12", 'style' => "border:1px solid #eee" ], [
  82. UI::h('div', [ 'class' => "checkbox" ], [
  83. UI::h('label', [ 'style' => "font-size:14px; line-height:22px" ], [
  84. UI::h('input', array_merge([
  85. 'type' => "checkbox",
  86. 'name' => "change_windykacja_status",
  87. 'onClick' => "document.getElementById('fld__change_windykacja_status').style.display = event.target.checked ? 'block' : 'none'",
  88. ], ($args['change_windykacja_status']) ? [ 'checked' => "checked" ] : [])),
  89. " Zmień status windykacji (ustalenia z klientem)",
  90. ]),
  91. ]),
  92. UI::h('div', [
  93. 'id' => "fld__change_windykacja_status",
  94. 'style' => "display:" . ($args['change_windykacja_status'] ? 'block' : 'none') . "; padding-bottom:12px",
  95. ], [
  96. // UI::h('label', [], "Status windykacji:"),
  97. UI::h('p', [], [
  98. "Obecny status: ",
  99. ($windykStatusInfo) ? $windykStatusInfo : "<em>brak</em>",
  100. ]),
  101. UI::h('input', [ 'class' => "form-control", 'type' => "text", 'name' => 'A_STATUS_INFO', 'value' => $args['A_STATUS_INFO'] ]),
  102. ]),
  103. ]),
  104. ]),
  105. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  106. UI::h('div', [ 'class' => "col-md-12", 'style' => "border:1px solid #eee" ], [
  107. UI::h('div', [ 'class' => "checkbox" ], [
  108. UI::h('label', [ 'style' => "font-size:14px; line-height:22px" ], [
  109. UI::h('input', array_merge([
  110. 'type' => "checkbox",
  111. 'name' => "add_reminder",
  112. 'onClick' => "document.getElementById('fld__add_reminder').style.display = event.target.checked ? 'block' : 'none'",
  113. ], ($args['add_reminder']) ? [ 'checked' => "checked" ] : [])),
  114. " Przypomnienie",
  115. ]),
  116. ]),
  117. UI::h('div', [
  118. 'id' => "fld__add_reminder",
  119. 'style' => "display:" . ($args['add_reminder'] ? 'block' : 'none') . "; padding-bottom:4px",
  120. ], [
  121. ($windykReminder) ? UI::h('div', [ 'style' => "border-left: 5px solid orange; padding:6px 12px; margin-bottom:12px" ], [
  122. UI::h('p', [], "Obecne przypomnienie (Uwaga: zostanie nadpisane)"),
  123. UI::hTable([ 'rows' => [ $windykReminder ], 'disable_lp' => true, 'style' => "margin-bottom:0" ]),
  124. ]) : '',
  125. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  126. UI::h('div', [ 'class' => "col-md-6" ], [
  127. UI::h('label', [], "Pracownik"),
  128. UI::h('input', [ 'class' => "form-control", 'type' => "text", 'name' => 'REMINDER_USER', 'value' => $args['REMINDER_USER'] ]),
  129. ]),
  130. UI::h('div', [ 'class' => "col-md-3" ], [
  131. UI::h('label', [], "Data przypomnienia"),
  132. UI::h('div', [ 'class' => "input-group" ], [
  133. UI::h('input', [ 'class' => "form-control se_type-datetime", 'data-format' => "yyyy-MM-dd hh:mm", 'type' => "text", 'name' => 'REMINDER_DATE', 'value' => $args['REMINDER_DATE'] ]),
  134. UI::h('span', [ 'class' => "input-group-addon" ], [
  135. UI::h('span', [ 'class' => "glyphicon glyphicon-calendar" ]),
  136. ]),
  137. ]),
  138. ]),
  139. ]),
  140. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  141. UI::h('div', [ 'class' => "col-md-9" ], [
  142. UI::h('label', [], "Adnotacje"),
  143. UI::h('textarea', [ 'class' => "form-control", 'type' => "text", 'name' => 'REMINDER_INFO' ], $args['REMINDER_INFO']),
  144. ]),
  145. ]),
  146. ]),
  147. ]),
  148. ]),
  149. // <div class="form-group">
  150. // <div class="col-sm-offset-2 col-sm-10">
  151. // <div class="checkbox">
  152. // <label>
  153. // <input type="checkbox"> Remember me
  154. // </label>
  155. // </div>
  156. // </div>
  157. // </div>
  158. UI::h('div', [ 'class' => "row", 'style' => "padding-bottom: 12px" ], [
  159. UI::h('div', [ 'class' => "col-md-12" ], [
  160. UI::h('button', [ 'class' => "btn btn-primary" ], "Zapisz notatkę"),
  161. ]),
  162. ]),
  163. ]);
  164. }
  165. function saveContactPostTask($idUser) {
  166. $args = [];
  167. $args['L_APPOITMENT_USER'] = V::get('L_APPOITMENT_USER', '', $_POST);
  168. $args['L_APPOITMENT_DATE'] = V::get('L_APPOITMENT_DATE', '', $_POST);
  169. $args['L_APPOITMENT_TYPE'] = V::get('L_APPOITMENT_TYPE', '', $_POST);
  170. $args['L_APPOITMENT_INFO'] = V::get('L_APPOITMENT_INFO', '', $_POST);
  171. $args['change_windykacja_status'] = V::get('change_windykacja_status', '', $_POST);
  172. $args['A_STATUS_INFO'] = V::get('A_STATUS_INFO', '', $_POST);
  173. $args['add_reminder'] = V::get('add_reminder', '', $_POST);
  174. $args['REMINDER_USER'] = V::get('REMINDER_USER', '', $_POST);
  175. $args['REMINDER_DATE'] = V::get('REMINDER_DATE', '', $_POST);
  176. $args['REMINDER_INFO'] = V::get('REMINDER_INFO', '', $_POST);
  177. try {
  178. if (empty($args['L_APPOITMENT_USER'])) throw new Exception("Brak pracownika");
  179. if (empty($args['L_APPOITMENT_DATE'])) throw new Exception("Brak daty spotkania");
  180. if (empty($args['L_APPOITMENT_TYPE'])) throw new Exception("Brak rodzaju spotkania");
  181. if (empty($args['L_APPOITMENT_INFO'])) throw new Exception("Brak treści notatki");
  182. if ($args['change_windykacja_status']) {
  183. if (empty($args['A_STATUS_INFO'])) throw new Exception("Brak statusu windykacji");
  184. }
  185. if ($args['add_reminder']) {
  186. if (empty($args['REMINDER_USER'])) throw new Exception("Brak pracownika w przypomnieniu");
  187. if (empty($args['REMINDER_DATE'])) throw new Exception("Brak daty przypomnienia");
  188. if (empty($args['REMINDER_INFO'])) throw new Exception("Brak adnotacji przypomnienia");
  189. }
  190. $windykInfo = Windykacja_View::getWindykacjaInfo($idUser);
  191. // create new record with L_APPOITMENT_* in table `USERS2_CONTACT`
  192. $newContactItem = [
  193. 'ID_BILLING_USERS' => $idUser,
  194. 'L_APPOITMENT_USER' => $args['L_APPOITMENT_USER'],
  195. 'L_APPOITMENT_DATE' => $args['L_APPOITMENT_DATE'],
  196. 'L_APPOITMENT_TYPE' => $args['L_APPOITMENT_TYPE'],
  197. 'L_APPOITMENT_INFO' => $args['L_APPOITMENT_INFO'],
  198. 'SALDO' => $windykInfo['SALDO'],
  199. 'A_RECORD_CREATE_DATE' => "NOW()",
  200. 'A_RECORD_CREATE_AUTHOR' => User::getLogin(),
  201. ];
  202. $idCreated = DB::getPDO()->insert('USERS2_CONTACT', $newContactItem);
  203. DB::getPDO()->insert('USERS2_CONTACT_HIST', array_merge($newContactItem, [ 'ID_USERS2' => $idCreated ]));
  204. // update A_STATUS_INFO in `USERS2_WINDYKACJA_STATUS` if $args['change_windykacja_status']
  205. if ($args['change_windykacja_status']) {
  206. if ($windykInfo['A_STATUS_INFO'] != $args['A_STATUS_INFO']) {
  207. $toUpdateWindyk = [
  208. 'A_STATUS_INFO' => $args['A_STATUS_INFO'],
  209. 'A_RECORD_UPDATE_DATE' => "NOW()",
  210. 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
  211. ];
  212. DB::getPDO()->update('USERS2_WINDYKACJA_STATUS', 'ID_BILLING_USERS', $idUser, $toUpdateWindyk);
  213. DB::getPDO()->insert('USERS2_WINDYKACJA_STATUS_HIST', array_merge($toUpdateWindyk, [
  214. 'ID_USERS2' => $windykInfo['ID'],
  215. ]));
  216. }
  217. }
  218. // update L_APPOITMENT_* fields using REMINDER_* in `USERS2_WINDYKACJA_STATUS` if $args['add_reminder']
  219. if ($args['add_reminder']) {
  220. DB::getPDO()->update('USERS2_WINDYKACJA_STATUS', 'ID_BILLING_USERS', $idUser, [
  221. 'L_APPOITMENT_USER' => $args['REMINDER_USER'],
  222. 'L_APPOITMENT_DATE' => $args['REMINDER_DATE'],
  223. 'L_APPOITMENT_INFO' => $args['REMINDER_INFO'],
  224. 'A_RECORD_UPDATE_DATE' => "NOW()",
  225. 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
  226. ]);
  227. }
  228. } catch (Exception $e) {
  229. DBG::log($e);
  230. UI::alert('danger', $e->getMessage());
  231. return;
  232. }
  233. throw new AlertSuccessException("Wprowadzono notatkę ze spotkania z klientem " . UI::h('a', [ 'href' => $this->getLink('', [ 'id_user' => $idUser ]) ], "wróć"));
  234. }
  235. function viewUserPaymentContactsHistory($idUser, $params = []) {
  236. $markEventsFromSpotkanieNr = V::get('markEventsFromSpotkanieNr', 0, $params, 'int');
  237. Lib::loadClass('Windykacja_StatsModel');
  238. $billDocs = Windykacja_StatsModel::getBillDocsByDate($idUser);
  239. // DBG::nicePrint($billDocs, '$billDocs');
  240. $lastHist = DB::getPDO()->fetchAll("
  241. select t.ID
  242. , t.ID_BILLING_USERS
  243. -- , t.CLIENT_INFO
  244. , t.A_STATUS
  245. -- , t.A_STATUS_INFO
  246. , t.L_APPOITMENT_USER
  247. , t.L_APPOITMENT_DATE
  248. , t.L_APPOITMENT_TYPE
  249. , t.L_APPOITMENT_INFO
  250. , IF('0000-00-00 00:00:00' = t.A_RECORD_UPDATE_DATE, t.A_RECORD_CREATE_DATE, t.A_RECORD_UPDATE_DATE) as A_RECORD_UPDATE_DATE
  251. from USERS2_CONTACT t
  252. where t.ID_BILLING_USERS = :id
  253. limit 11
  254. ", [ ':id' => $idUser ]);
  255. // DBG::nicePrint($lastHist, '$lastHist');
  256. if (!empty($lastHist)) {
  257. foreach ($lastHist as $bill_doc) {
  258. $billDocs->add_event(substr($bill_doc['A_RECORD_UPDATE_DATE'], 0, 10), 'HIST_CONTACT', $bill_doc);
  259. }
  260. }
  261. // TODO: add zmiania statusu: select h.* from Windyk_HIST where A_STATUS_INFO != 'N/S;'
  262. $umowy_l2 = Windykacja_StatsModel::getClientUmowy($idUser);
  263. //echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;">billDocs ';print_r($billDocs);echo'</pre>';
  264. //echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;">billDocs ';print_r(reset($billDocs));echo'</pre>';
  265. //echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;">umowy_l2 ';print_r($umowy_l2);echo'</pre>';
  266. if (!empty($umowy_l2)) {
  267. foreach ($umowy_l2 as $h) {
  268. $billDocs->add_event($h['P_DEALDATE'], 'UMOWA', $h);
  269. if ($h['P_DEALDATE_TERM'] != '0000-00-00') {// < date('Y-m-d')) {
  270. $billDocs->add_event($h['P_DEALDATE_TERM'], 'KONIEC UMOWY', $h);
  271. }
  272. }
  273. }
  274. $user_hist_events = Windykacja_StatsHelper::getUserHistStatusEvents($idUser);
  275. if (!empty($user_hist_events)) {
  276. foreach ($user_hist_events as $bill_doc) {
  277. $billDocs->add_event($bill_doc['A_STATUS_UPDATE_DATE'], 'HIST_STATUS', $bill_doc);
  278. }
  279. }
  280. $user_hist_events = Windykacja_StatsHelper::getUserHistPhoneEvents($idUser);
  281. if (!empty($user_hist_events)) {
  282. foreach ($user_hist_events as $bill_doc) {
  283. $billDocs->add_event(substr($bill_doc['A_RECORD_UPDATE_DATE'], 0, 10), 'HIST_PHONE', $bill_doc);
  284. }
  285. }
  286. $user_hist_events = Windykacja_StatsHelper::getUserBadAddressEvents($user->ID);
  287. if (!empty($user_hist_events)) {
  288. foreach ($user_hist_events as $bill_doc) {
  289. $billDocs->add_event(substr($bill_doc['A_RECORD_UPDATE_DATE'], 0, 10), 'HIST_BAD_ADDRESS', $bill_doc);
  290. }
  291. }
  292. // add today
  293. $bill_doc = array();
  294. $bill_doc['saldo'] = $billDocs->get_saldo();
  295. $billDocs->add_event(date('Y-m-d'), 'TODAY', $bill_doc);
  296. $billDocs->sort_docs();
  297. $billDocs->set_saldo_for_all_docs();
  298. $listBillDocs = $billDocs->get_docs(); // [ day => [ Windykacja_EventDoc, ... ], ... ]
  299. // convert to: [ year => [ Windykacja_EventDoc, ... ] ]
  300. $groupedByYearBillDocs = array_reduce($listBillDocs, function ($ret, $listDayEvents) {
  301. foreach ($listDayEvents as $event) {
  302. $year = substr($event->get_date(), 0, 4);
  303. if (empty($ret[$year])) $ret[$year] = [];
  304. array_unshift($ret[$year], $event);
  305. }
  306. return $ret;
  307. }, []);
  308. $groupedByYearBillDocs = array_reverse($groupedByYearBillDocs, true);
  309. // DBG::nicePrint($listBillDocs, '$listBillDocs');
  310. // DBG::nicePrint($groupedByYearBillDocs, '$groupedByYearBillDocs');
  311. // UI::table([ 'caption' => "Historia kontaktów z klientem (TODO: w trakcie prac. Zobacz " .
  312. // UI::h('a', [ 'href' => "index.php?MENU_INIT=USERS2_WINDYKACJA_STATUS&q=&_f=&_user_id={$idUser}" ], "Panel windykacji") .
  313. // ")", 'rows' => $lastHist ]);
  314. // if (count($lastHist) > 10) UI::alert('info', "Przeglądaj wszystkie wpisy TODO: link to view table with ff_ID_BILLING_USERS");
  315. echo UI::h('div', [], array_merge(
  316. [
  317. UI::h('h3', [], "Historia klienta"),
  318. ],
  319. array_map(function ($listEventDocs, $year) use ($markEventsFromSpotkanieNr) {
  320. return UI::h('details', array_merge([ 'style' => "margin:8px 0" ],
  321. (date("Y") == $year) ? [ 'open' => "" ] : []
  322. ), [
  323. UI::h('summary', [ 'style' => "height:20px; position:relative; cursor:pointer; outline:none" ], [
  324. UI::h('div', [ 'style' => "position:absolute; top:9px; left:20px; width: 100%; border-bottom:2px solid #000" ]),
  325. UI::h('span', [ 'style' => "position:absolute; top:0; left:12px; padding:0 12px 0 0; line-height:20px; background-color:#fff; font-weight:bold" ], [
  326. "{$year}:",
  327. ]),
  328. ]),
  329. UI::h('div', [ 'style' => "padding: 8px 0px 8px 54px" ],
  330. array_merge(
  331. [
  332. UI::hTable([
  333. 'disable_lp' => true,
  334. '@class' => 'table table-hover', // table-bordered
  335. 'cols' => [
  336. 'data',
  337. 'winien',
  338. 'ma',
  339. 'saldo',
  340. '#',
  341. 'uwagi',
  342. ],
  343. '@style[data]' => "width:80px; text-align:right",
  344. '@style[winien]' => "width:90px; text-align:right",
  345. '@style[ma]' => "width:90px; text-align:right",
  346. '@style[saldo]' => "width:90px; text-align:right; background-color:#f5f5f5",
  347. '@style[#]' => "width:140px; padding-left:12px; text-align:left",
  348. 'rows' => array_map(function ($eventDoc) use ($markEventsFromSpotkanieNr) {
  349. $saldo = $eventDoc->get_saldo();
  350. $overallSaldo = $eventDoc->get_saldo_overall();
  351. $rowClass = "";
  352. // $markEventsFromSpotkanieNr
  353. if ($markEventsFromSpotkanieNr
  354. && $eventDoc->get_class() == 'EVENT' && $eventDoc->get_type() == 'HIST_CONTACT'
  355. && $eventDoc->get('ID') == $markEventsFromSpotkanieNr
  356. ) {
  357. $rowClass = "info";
  358. } else {
  359. $rowClass = ('EVENT' === $eventDoc->get_class() && 'TODAY' === $eventDoc->get_type()) ? "warning" : "";
  360. }
  361. return [
  362. 'data' => $eventDoc->get_date(),
  363. 'winien' => UI::h('span', [
  364. 'style' => "color:" . ( (-1 * $saldo > 0) ? "#000" : "#ddd" ),
  365. ], ($saldo < 0) ? UI::price(-1 * $saldo) : 0
  366. ),
  367. 'ma' => UI::h('span', [
  368. 'style' => "color:" . ( ($saldo > 0) ? "#000" : "#ddd" ),
  369. ], ($saldo > 0) ? UI::price($saldo) : 0
  370. ),
  371. 'saldo' => UI::h('span', [
  372. 'style' => "color:" . ( ($overallSaldo >= 0) ? "green" : "red" ),
  373. ], UI::price($overallSaldo)
  374. ),
  375. '#' => $this->viewUserPaymentContactsHistory_eventNr($eventDoc),
  376. 'uwagi' => $this->viewUserPaymentContactsHistory_eventLabel($eventDoc),
  377. '@class' => $rowClass,
  378. '@style[data]' => "text-align:right",
  379. '@style[winien]' => "text-align:right",
  380. '@style[ma]' => "text-align:right",
  381. '@style[saldo]' => "text-align:right; background-color:#f5f5f5",
  382. '@style[#]' => "padding-left:12px",
  383. ];
  384. }, $listEventDocs),
  385. ]),
  386. ],
  387. []
  388. )
  389. ),
  390. ]);
  391. }, $groupedByYearBillDocs, array_keys($groupedByYearBillDocs))
  392. ));
  393. }
  394. function viewUserPaymentContactsHistory_eventNr($v_doc) { // return html (string)
  395. /* links fomr l1:
  396. * FV: https://l1.webone.pl/modules/make_billing/edit.php?mod=make_billing&form=html_faktura&adm=edit&uid=4014&fpos=&dz=&close=0&doc=799413&doctype=1
  397. * KOR: https://l1.webone.pl/modules/make_billing/edit.php?mod=make_billing&form=html_faktura_korekta&adm=edit&uid=4014&fpos=&dz=&close=0&doc=809557&doctype=3
  398. * https://biuro.biall-net.pl/dev-pl/se-master/index.php?FUNCTION_INIT=bm_show_document&ARG1_VAL=809557
  399. */
  400. if ($v_doc->get_class() == 'BILLING' && 'FVAT' == $v_doc->get_type()) {
  401. $nr = $v_doc->get_type();
  402. Lib::loadClass('Windykacja_StatsModel');
  403. $type_desc = Windykacja_StatsModel::get_billing_type_desc($v_doc->get_type());
  404. if ($type_desc) {
  405. $type_desc .= "\n Wystawiona: ".$v_doc->get('BILL_DATE')."\n Termin płatności: ".$v_doc->get('PAYMENT_TERM');
  406. $nr = '<span title="'.$type_desc.'">'.$nr.'</span>';
  407. // TODO: podglad faktury
  408. //if ($h['type'] == 'FVAT') $nr .= ' <a href="'."?MENU_INIT=USERS2_WINDYKACJA_STATUS&_user_id=".$user->ID."&task=view_faktura&id=".$h['ID'].'" target="_blank">'.'<img src="'."icon/search.png".'" alt="'."U".'" title="'."Podgląd faktury".'" />'.'</a>';
  409. }
  410. $docNr = $v_doc->get('nr');
  411. $docNrLabel = $v_doc->get('NUMBER') . '/' . $v_doc->get('ID_BILLING_PREFIXES');
  412. $nr .= ' <a href="index.php?FUNCTION_INIT=bm_show_document&ARG1_VAL=' . $docNr . '" target="_blank">'."({$docNrLabel})".'</a>';
  413. return $nr;
  414. }
  415. if ($v_doc->get_class() == 'BILLING' && 'KORV' == $v_doc->get_type()) {
  416. $nr = $v_doc->get_type();
  417. Lib::loadClass('Windykacja_StatsModel');
  418. $type_desc = Windykacja_StatsModel::get_billing_type_desc($v_doc->get_type());
  419. if ($type_desc) {
  420. $nr = '<span title="'.$type_desc.'">'.$nr.'</span>';
  421. }
  422. $docNr = $v_doc->get('nr');
  423. $docNrLabel = $v_doc->get('NUMBER') . '/' . $v_doc->get('ID_BILLING_PREFIXES');
  424. $remoteFvNum = $v_doc->get('FV_NUMBER');
  425. $korNr = $v_doc->get('ID_BILLING_NUMBERS');
  426. $korTitle = "Korekta dotycząca faktury nr: {$remoteFvNum}";
  427. $nr .= ' <a href="index.php?FUNCTION_INIT=bm_show_document&ARG1_VAL=' . $korNr . '" target="_blank" title="'.$korTitle.'">'."({$docNrLabel})".'</a>';
  428. return $nr;
  429. }
  430. if ($v_doc->get_class() == 'BILLING') {
  431. $nr = $v_doc->get_type();
  432. Lib::loadClass('Windykacja_StatsModel');
  433. $type_desc = Windykacja_StatsModel::get_billing_type_desc($v_doc->get_type());
  434. if ($type_desc) {
  435. $nr = '<span title="'.$type_desc.'">'.$nr.'</span>';
  436. }
  437. $docNr = $v_doc->get('nr');
  438. $docNrLabel = $v_doc->get('NUMBER') . '/' . $v_doc->get('ID_BILLING_PREFIXES');
  439. return $nr;
  440. }
  441. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'TODAY') return "";
  442. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_STATUS') return "";
  443. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_PHONE') return "";
  444. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_BAD_ADDRESS') return "";
  445. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_CONTACT') return "Spotkanie " . UI::h('a', [
  446. 'href' => "index.php?_route=ViewTableAjax&namespace=default_db/USERS2_CONTACT#EDIT/" . $v_doc->get('ID'),
  447. 'target' => "_blank",
  448. 'title' => "Notatka ze spotkania",
  449. ], "(" . $v_doc->get('ID') . ")");
  450. if ($v_doc->get_class() == 'EVENT') return UI::h('span', [ 'title' => $v_doc->get('ID') ], $v_doc->get_type()); // TODO: dla KP,KW: ID_BILLING_NUMBERS zamiast ID
  451. return "???";
  452. }
  453. function viewUserPaymentContactsHistory_eventLabel($v_doc) { // return html (string)
  454. if ($v_doc->get_class() == 'BILLING') return '';
  455. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'TODAY') return '';
  456. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_STATUS') {
  457. $id_koresp = intval($v_doc->get('ID_KORESP'));
  458. $add = '';
  459. if ($id_koresp > 0) {
  460. $koresp_edit_link = "index.php?_route=ViewTableAjax&namespace=default_db/IN7_DZIENNIK_KORESP#EDIT/{$id_koresp}";
  461. $add = " " . '<a href="' . $koresp_edit_link . '" title="' . "Edycja KORESP. ({$id_koresp})" . '" target="_blank">' . "KOR" . '</a>';
  462. }
  463. switch ($v_doc->get('A_STATUS')) {
  464. case 'WAITING': return "powrót do oczekujących {$add}";
  465. case 'waiting-wezwanie2':return "wezwanie do zapłaty (Nr koresp.: ".$v_doc->get('ID_KORESP').") <em>(termin: ".$v_doc->get('PAY_TERM').")</em> {$add}";
  466. case 'waiting-krd': return "wezwanie do zapłaty ost. (Nr koresp.: ".$v_doc->get('ID_KORESP').") <em>(termin: ".$v_doc->get('PAY_TERM').")</em> {$add}";
  467. case 'N/S;': return "Nr koresp.: ".$v_doc->get('ID_KORESP')." <em>(" . $v_doc->get('K_ZAWARTOS') . ")</em>" .
  468. (
  469. ($v_doc->get('ID_KORESP') > 0 && substr($v_doc->get('params'), 0, strlen('rozwiazanie umowy')) == 'rozwiazanie umowy')
  470. ? " " . '<a href="'."?MENU_INIT=USERS2_WINDYKACJA_STATUS&q=".V::get('q','', $_REQUEST)."&task="."bok_rozwiazanie_umowy_print"."&user_id={$user->ID}&id_koresp=".$v_doc->get('ID_KORESP').'" target="_blank" title="' . "Generuj druk rozwiązania umowy" . '">' . "druk" . '</a>'
  471. : ""
  472. ) . " {$add}"
  473. ;
  474. // TODO: get opis from koresp
  475. //echo '('.$v_doc->get('ID_KORESP').', '.$v_doc->get('K_ZAWARTOS').'='.substr($v_doc->get('K_ZAWARTOS'), 0, strlen('rozwiazanie umowy')).')';
  476. default: return "zmiana statusu na: " . $v_doc->get('A_STATUS') . " ";
  477. }
  478. }
  479. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_PHONE') {
  480. $lastUpdateDate = $v_doc->get('A_RECORD_UPDATE_DATE');
  481. $lastPhoneStatus = ('N/S;' === $v_doc->get('LAST_PHONE_STATUS')) ? '' : $v_doc->get('LAST_PHONE_STATUS');
  482. if (!empty($lastPhoneStatus)) {
  483. $date = $v_doc->get('PAY_TERM');
  484. if ($date == 'N/S;') $date = '';
  485. switch ($lastPhoneStatus) {
  486. case 'nie_zaplaci': return "nie zapłaci";
  487. case 'zaplaci_w_terminie': return "zapłaci w terminie {$date} " . '<em style="font-size:small;">' . "(ustalono {$lastUpdateDate})" . '</em>';
  488. case 'zaplaci_za_1mc': return "zapłaci miesiąc później {$date} " . '<em style="font-size:small;">' . "(ustalono {$lastUpdateDate})" . '</em>';
  489. case 'zaplaci_za_2mc': return "zapłaci 2 miesiące później {$date} " . '<em style="font-size:small;">' . "(ustalono {$lastUpdateDate})" . '</em>';
  490. case 'zaplaci_za_3mc': return "zapłaci 3 miesiące później {$date} " . '<em style="font-size:small;">' . "(ustalono {$lastUpdateDate})" . '</em>';
  491. default: return "kontakt z klientem: {$lastPhoneStatus} " . '<em style="font-size:small;">' . "(ustalono {$lastUpdateDate})" . '</em>';
  492. }
  493. }
  494. $lastSmsStatus = $v_doc->get('LAST_SMS_STATUS');
  495. if ($lastSmsStatus == 'N/S;') $lastSmsStatus = '';
  496. if (!empty($lastSmsStatus)) {
  497. $lastSmsId = $v_doc->get('LAST_SMS_MSG_ID');
  498. return "SMS: {$lastSmsStatus} " . '<em style="font-size:small;" title="' . "(wysłano {$lastUpdateDate}, {$lastSmsId})" . '">' . "(wysłano {$lastUpdateDate})" . '</em>';
  499. }
  500. $lastMailStatus = $v_doc->get('LAST_MAIL_STATUS');
  501. if ($lastMailStatus == 'N/S;') $lastMailStatus = '';
  502. if (!empty($lastMailStatus)) {
  503. $lastMailId = $v_doc->get('LAST_MAIL_MSG_ID');
  504. return "MAIL: {$lastMailStatus} " . '<em style="font-size:small;" title="' . "(wysłano {$lastUpdateDate}, {$lastMailId})" . '">' . "(wysłano {$lastUpdateDate})" . '</em>';
  505. }
  506. }
  507. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_BAD_ADDRESS') {
  508. $koresp_edit_link = "index.php?_route=ViewTableAjax&namespace=default_db/IN7_DZIENNIK_KORESP#EDIT/";
  509. $add = ($v_doc->get('ID_KORESP') > 0)
  510. ? " - ID Koresp. (" . '<a href="' . $koresp_edit_link . $v_doc->get('ID_KORESP') . '" target="_blank">' . $v_doc->get('ID_KORESP') . '</a>' . ")"
  511. : ""
  512. ;
  513. return ($v_doc->get('BAD_ADDRESS'))
  514. ? "błędny adres zameldowania {$add}"
  515. : "poprawa adresu zameldowania {$add}"
  516. ;
  517. }
  518. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'UMOWA') {
  519. return "umowa nr <b>".$v_doc->get('P_DEALNUMBER')."</b> <em>(termin ".$v_doc->get('P_DEALDATE_TERM').")</em>";
  520. }
  521. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'KONIEC UMOWY') {
  522. return "zakończenie umowy nr <b>".$v_doc->get('P_DEALNUMBER')."</b> <em>(z dnia ".$v_doc->get('P_DEALDATE').")</em>";
  523. }
  524. if ($v_doc->get_class() == 'EVENT' && $v_doc->get_type() == 'HIST_CONTACT') {
  525. return implode(" ", [
  526. $v_doc->get('L_APPOITMENT_TYPE'),
  527. $v_doc->get('L_APPOITMENT_INFO'),
  528. UI::h('em', [], "(" . $v_doc->get('L_APPOITMENT_USER') . ")"),
  529. ]);
  530. }
  531. return "...";
  532. }
  533. function prepareTable() {
  534. DB::getPDO()->execSql("
  535. CREATE TABLE IF NOT EXISTS `USERS2_CONTACT` (
  536. `ID` int(11) NOT NULL AUTO_INCREMENT,
  537. `ID_BILLING_USERS` int(11) NOT NULL DEFAULT '0',
  538. `CLIENT_INFO` varchar(255) NOT NULL DEFAULT '',
  539. `A_STATUS` enum('WAITING','NORMAL','DELETED') NOT NULL DEFAULT 'WAITING',
  540. `A_STATUS_INFO` varchar(255) NOT NULL,
  541. `L_APPOITMENT_DATE` date NOT NULL DEFAULT '0000-00-00',
  542. `L_APPOITMENT_USER` varchar(20) NOT NULL DEFAULT '',
  543. `L_APPOITMENT_PERIOD` varchar(4) NOT NULL,
  544. `L_APPOITMENT_INFO` varchar(255) NOT NULL,
  545. `L_APPOITMENT_TYPE` enum('','LIVE','TEL','MAIL','SMS','INNE') DEFAULT NULL,
  546. `A_RECORD_CREATE_DATE` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  547. `A_RECORD_CREATE_AUTHOR` varchar(20) NOT NULL DEFAULT '',
  548. `A_RECORD_UPDATE_DATE` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  549. `A_RECORD_UPDATE_AUTHOR` varchar(20) NOT NULL DEFAULT '',
  550. `A_PROBLEM` enum('','WARNING','PROBLEM','SERIOUS','UNVERIFIED') DEFAULT NULL,
  551. `A_PROBLEM_DESC` varchar(255) NOT NULL DEFAULT '',
  552. `A_PROBLEM_DATE` varchar(30) NOT NULL DEFAULT '',
  553. `T_WORKPOINTS` varchar(100) NOT NULL DEFAULT '',
  554. `T_WORKPOINTS_VALUE` varchar(100) NOT NULL DEFAULT '',
  555. `T_WORKPOINTS_TYPE` varchar(100) NOT NULL DEFAULT '',
  556. `T_WORKPOINTS_USER` varchar(100) NOT NULL DEFAULT '',
  557. `T_WORKPOINTS_DATE` varchar(100) NOT NULL DEFAULT '',
  558. `A_CLASSIFIED` varchar(100) NOT NULL DEFAULT '',
  559. `A_ADM_COMPANY` varchar(100) NOT NULL DEFAULT '',
  560. `ROZLICZ_MONTH` date NOT NULL DEFAULT '0000-00-00',
  561. `ROZLICZ_CONFIRM` int(11) NOT NULL DEFAULT '0',
  562. `SALDO` decimal(10,2) NOT NULL DEFAULT '0.00',
  563. `ILE_ODZYSKANO` decimal(10,2) NOT NULL DEFAULT '0.00',
  564. PRIMARY KEY (`ID`)
  565. ) ENGINE=MyISAM DEFAULT CHARSET=latin2;
  566. ");
  567. DB::getPDO()->execSql("
  568. CREATE TABLE IF NOT EXISTS `USERS2_CONTACT_HIST` (
  569. `ID` int(11) NOT NULL AUTO_INCREMENT,
  570. `ID_USERS2` int(11) NOT NULL,
  571. `ID_BILLING_USERS` varchar(11) NOT NULL DEFAULT 'N/S;',
  572. `CLIENT_INFO` varchar(255) NOT NULL DEFAULT 'N/S;',
  573. `A_STATUS` varchar(32) NOT NULL DEFAULT 'N/S;',
  574. `A_STATUS_INFO` varchar(255) NOT NULL DEFAULT 'N/S;',
  575. `L_APPOITMENT_DATE` varchar(20) NOT NULL DEFAULT 'N/S;',
  576. `L_APPOITMENT_USER` varchar(20) NOT NULL DEFAULT 'N/S;',
  577. `L_APPOITMENT_PERIOD` varchar(4) NOT NULL DEFAULT 'N/S;',
  578. `L_APPOITMENT_INFO` varchar(255) NOT NULL DEFAULT 'N/S;',
  579. `L_APPOITMENT_TYPE` varchar(16) NOT NULL DEFAULT 'N/S;',
  580. `A_RECORD_CREATE_DATE` varchar(20) NOT NULL DEFAULT 'N/S;',
  581. `A_RECORD_CREATE_AUTHOR` varchar(20) NOT NULL DEFAULT 'N/S;',
  582. `A_RECORD_UPDATE_DATE` varchar(20) NOT NULL DEFAULT 'N/S;',
  583. `A_RECORD_UPDATE_AUTHOR` varchar(20) NOT NULL DEFAULT 'N/S;',
  584. `A_PROBLEM` varchar(16) NOT NULL DEFAULT 'N/S;',
  585. `A_PROBLEM_DESC` varchar(255) NOT NULL DEFAULT 'N/S;',
  586. `A_PROBLEM_DATE` varchar(30) NOT NULL DEFAULT 'N/S;',
  587. `T_WORKPOINTS` varchar(100) NOT NULL DEFAULT 'N/S;',
  588. `T_WORKPOINTS_VALUE` varchar(100) NOT NULL DEFAULT 'N/S;',
  589. `T_WORKPOINTS_TYPE` varchar(100) NOT NULL DEFAULT 'N/S;',
  590. `T_WORKPOINTS_USER` varchar(100) NOT NULL DEFAULT 'N/S;',
  591. `T_WORKPOINTS_DATE` varchar(100) NOT NULL DEFAULT 'N/S;',
  592. `A_CLASSIFIED` varchar(100) NOT NULL DEFAULT 'N/S;',
  593. `A_ADM_COMPANY` varchar(100) NOT NULL DEFAULT 'N/S;',
  594. `ROZLICZ_MONTH` varchar(20) NOT NULL DEFAULT 'N/S;',
  595. `ROZLICZ_CONFIRM` varchar(11) NOT NULL DEFAULT 'N/S;',
  596. `SALDO` varchar(11) NOT NULL DEFAULT 'N/S;',
  597. `ILE_ODZYSKANO` varchar(11) NOT NULL DEFAULT 'N/S;',
  598. PRIMARY KEY (`ID`),
  599. KEY `ID_USERS2` (`ID_USERS2`)
  600. ) ENGINE=MyISAM DEFAULT CHARSET=latin2;
  601. ");
  602. }
  603. }
  604. /*
  605. insert into USERS2_CONTACT (
  606. A_RECORD_CREATE_DATE,
  607. A_RECORD_CREATE_AUTHOR,
  608. ID_BILLING_USERS,
  609. CLIENT_INFO,
  610. L_APPOITMENT_TYPE,
  611. L_APPOITMENT_DATE,
  612. L_APPOITMENT_USER,
  613. L_APPOITMENT_INFO
  614. )
  615. select
  616. h.A_RECORD_UPDATE_DATE as A_RECORD_CREATE_DATE,
  617. h.A_RECORD_UPDATE_AUTHOR as A_RECORD_CREATE_AUTHOR,
  618. ( select w.ID_BILLING_USERS from USERS2_WINDYKACJA_STATUS w where w.ID = h.ID_USERS2 ) as ID_BILLING_USERS,
  619. '' as CLIENT_INFO,
  620. 'TEL' as L_APPOITMENT_TYPE,
  621. IF('N/S;' = h.L_APPOITMENT_DATE and h.LAST_PHONE_STATUS_DATE != 'N/S;', h.LAST_PHONE_STATUS_DATE, h.L_APPOITMENT_DATE) as L_APPOITMENT_DATE,
  622. IF('N/S;' = h.L_APPOITMENT_DATE and h.LAST_PHONE_STATUS_DATE != 'N/S;', h.A_RECORD_UPDATE_AUTHOR, h.L_APPOITMENT_USER) as L_APPOITMENT_USER,
  623. IF('N/S;' = h.L_APPOITMENT_DATE and h.LAST_PHONE_STATUS_DATE != 'N/S;', h.LAST_PHONE_STATUS, h.L_APPOITMENT_INFO) as L_APPOITMENT_INFO
  624. -- , '===' as X, h.*
  625. from USERS2_WINDYKACJA_STATUS_HIST h
  626. where
  627. h.A_RECORD_UPDATE_DATE like '2019-08-%'
  628. and h.A_RECORD_UPDATE_AUTHOR not like '%update%'
  629. select sum(ILE_ODZYSKANO) from USERS2_CONTACT;
  630. select count(DISTINCT ID_BILLING_USERS) from USERS2_CONTACT;
  631. select ID_BILLING_USERS, count(*) as cnt from USERS2_CONTACT group by ID_BILLING_USERS;
  632. */