api.exhorse.php 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585
  1. <?php
  2. /**
  3. * System-wide previous periods statistics archive aka Existential Horse
  4. */
  5. class ExistentialHorse {
  6. /**
  7. * System alter.ini config as key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * All of available internet users as login=>userdata
  14. *
  15. * @var array
  16. */
  17. protected $allInetUsers = array();
  18. /**
  19. * Temporary storage for horseRun saving data
  20. *
  21. * @var array
  22. */
  23. protected $storeTmp = array();
  24. /**
  25. * Is complex tariffs feature enabled?
  26. *
  27. * @var bool
  28. */
  29. protected $complexFlag = false;
  30. /**
  31. * Array of complex tariffs strings masks
  32. *
  33. * @var array
  34. */
  35. protected $complexMasks = array();
  36. /**
  37. * Contains all current month internet user signups
  38. *
  39. * @var array
  40. */
  41. protected $monthSignups = array();
  42. /**
  43. * Contains users to cities bindings as login=>cityname
  44. *
  45. * @var array
  46. */
  47. protected $usersCities = array();
  48. /**
  49. * Contains current year and month in format Y-m
  50. *
  51. * @var string
  52. */
  53. protected $curmonth = '';
  54. /**
  55. * Is UKB subsystem enabled?
  56. *
  57. * @var bool
  58. */
  59. protected $ukvFlag = false;
  60. /**
  61. * Contains UKV illegal users tariff id
  62. *
  63. * @var int
  64. */
  65. protected $ukvIllegal = '';
  66. /**
  67. * Contains social users tariff id
  68. *
  69. * @var int
  70. */
  71. protected $ukvSocial = '';
  72. /**
  73. * Contains complex users tariff id
  74. *
  75. * @var int
  76. */
  77. protected $ukvComplex = '';
  78. /**
  79. * Contains month count for debt limit calculation
  80. *
  81. * @var int
  82. */
  83. protected $ukvDebtLimit = '';
  84. /**
  85. * Is any PBX integration enabled?
  86. *
  87. * @var bool
  88. */
  89. protected $pbxFlag = false;
  90. /**
  91. * Is TelePony enabled?
  92. *
  93. * @var bool
  94. */
  95. protected $teleponyFlag = false;
  96. /**
  97. * Is Askozia PBX integration enabled?
  98. *
  99. * @var bool
  100. */
  101. protected $askoziaFlag = false;
  102. /**
  103. * PON enabled flag
  104. *
  105. * @var bool
  106. */
  107. protected $ponFlag = false;
  108. /**
  109. * Docsis enabled flag
  110. *
  111. * @var bool
  112. */
  113. protected $docsisFlag = false;
  114. /**
  115. * Year to display results
  116. *
  117. * @var string
  118. */
  119. protected $showYear = '';
  120. /**
  121. * System messages helper object placeholder
  122. *
  123. * @var object
  124. */
  125. protected $messages = '';
  126. /**
  127. * Castypes array with cash values
  128. *
  129. * @var array
  130. */
  131. protected $cashIds = array();
  132. /**
  133. * Horse-data database abstraction layer placeholder
  134. *
  135. * @var object
  136. */
  137. protected $horseDb = '';
  138. /**
  139. * Some predefined urls, routes etc..
  140. */
  141. const URL_ME = '?module=exhorse';
  142. const PROUTE_YEAR = 'yearsel';
  143. const COLOR_GOOD = '009f04';
  144. const COLOR_BAD = 'b50000';
  145. const ICON_RISE = 'skins/rise_icon.png';
  146. const ICON_DRAIN = 'skins/drain_icon.png';
  147. /**
  148. * Some database data sources here
  149. */
  150. const TABLE_HORSE = 'exhorse';
  151. const TABLE_SIGNUPS = 'userreg';
  152. const TABLE_PAYMENTS = 'payments';
  153. const TABLE_CATV_USERS = 'ukv_users';
  154. const TABLE_CATV_TARIFFS = 'ukv_tariffs';
  155. const TABLE_CATV_PAYMENTS = 'ukv_payments';
  156. const TABLE_SWITCHES = 'switches';
  157. const TABLE_SWDEAD = 'switchdeadlog';
  158. const TABLE_ONU = 'pononu';
  159. const TABLE_DOCSIS = 'modems';
  160. const TABLE_WDYC = 'wdycinfo';
  161. const TABLE_SIGREQ = 'sigreq';
  162. const TABLE_CAPABS = 'capab';
  163. // /\,%,_
  164. // \%%%/,\
  165. // _.-"%%|//%
  166. // .' .-" /%%%
  167. // _.-'_.-" 0) \%%%
  168. // /.\.' \%%%
  169. // \ / _, %%%
  170. // `"---"~`\ _,*'\%%' _,--""""-,%%,
  171. // )*^ `""~~` \%%%,
  172. // _/ \%%%
  173. // _.-`/ |%%,___
  174. // _.-" / , , ,|%% .`\
  175. // /\ / / `\ \%' \ /
  176. // \ \ _,/ /`~-._ _,`\ \`""~~`
  177. // `"` /-.,_ /' `~"----"~ `\ \
  178. // \___,' \.-"`/
  179. // `--'
  180. /**
  181. * Creates new existential horse instance
  182. *
  183. * @return void
  184. */
  185. public function __construct() {
  186. $this->loadConfig();
  187. $this->initTmp();
  188. $this->initDb();
  189. $this->initMessages();
  190. }
  191. /**
  192. * Creates message helper object for further usage
  193. *
  194. * @return void
  195. */
  196. protected function initMessages() {
  197. $this->messages = new UbillingMessageHelper();
  198. }
  199. /**
  200. * Inits database abstraction layer
  201. *
  202. * @return void
  203. */
  204. protected function initDb() {
  205. $this->horseDb = new NyanORM(self::TABLE_HORSE);
  206. }
  207. /**
  208. * Preloads alter config, for further usage as key=>value
  209. *
  210. * @global object $ubillingConfig
  211. *
  212. * @return void
  213. */
  214. protected function loadConfig() {
  215. global $ubillingConfig;
  216. $this->altCfg = $ubillingConfig->getAlter();
  217. //sets current month
  218. $this->curmonth = curmonth();
  219. //loading complex tariffs config
  220. if ($this->altCfg['COMPLEX_ENABLED']) {
  221. $this->complexFlag = true;
  222. if (!empty($this->altCfg['COMPLEX_MASKS'])) {
  223. $masksRaw = explode(",", $this->altCfg['COMPLEX_MASKS']);
  224. if (!empty($masksRaw)) {
  225. foreach ($masksRaw as $eachmask) {
  226. $this->complexMasks[] = trim($eachmask);
  227. }
  228. }
  229. } else {
  230. throw new Exception(self::CPL_EMPTY_EX);
  231. }
  232. }
  233. //loading UKV options
  234. if ($this->altCfg['UKV_ENABLED']) {
  235. $this->ukvFlag = true;
  236. $this->ukvComplex = $this->altCfg['UKV_COMPLEX_TARIFFID'];
  237. $this->ukvIllegal = $this->altCfg['UKV_ILLEGAL_TARIFFID'];
  238. $this->ukvSocial = $this->altCfg['UKV_SOCIAL_TARIFFID'];
  239. $this->ukvDebtLimit = $this->altCfg['UKV_MONTH_DEBTLIMIT'];
  240. }
  241. //Askozia PBX integration
  242. if ($this->altCfg['ASKOZIA_ENABLED']) {
  243. $this->askoziaFlag = true;
  244. $this->pbxFlag = true;
  245. }
  246. //Asterisk integration (?)
  247. if ($this->altCfg['ASTERISK_ENABLED']) {
  248. $this->pbxFlag = true;
  249. }
  250. //TelePony integration
  251. if ($this->altCfg['TELEPONY_ENABLED']) {
  252. $this->pbxFlag = true;
  253. $this->teleponyFlag = true;
  254. }
  255. //PONizer enabled?
  256. if ($this->altCfg['PON_ENABLED']) {
  257. $this->ponFlag = true;
  258. }
  259. //is DOCSIS support enabled?
  260. if ($this->altCfg['DOCSIS_SUPPORT']) {
  261. $this->docsisFlag = true;
  262. }
  263. //custom cashtypeids for cash stats
  264. $this->cashIds = array(1 => 1); // default cash cashtypeid
  265. if (isset($this->altCfg['EXHORSE_CASHIDS'])) {
  266. if (!empty($this->altCfg['EXHORSE_CASHIDS'])) {
  267. $rawCashIds = explode(',', $this->altCfg['EXHORSE_CASHIDS']);
  268. if (!empty($rawCashIds)) {
  269. $rawCashIds = array_flip($rawCashIds);
  270. $this->cashIds = $rawCashIds;
  271. }
  272. }
  273. }
  274. }
  275. /**
  276. * Sets year to render results
  277. *
  278. * @param string $month
  279. *
  280. * @return void
  281. */
  282. public function setYear($year) {
  283. $this->showYear = ubRouting::filters($year, 'int');
  284. }
  285. /**
  286. * Inits empty temporary array with default struct.
  287. * All keys of this struct will be mapped as-is to database record.
  288. *
  289. * @return void
  290. */
  291. protected function initTmp() {
  292. $this->storeTmp['u_totalusers'] = 0;
  293. $this->storeTmp['u_activeusers'] = 0;
  294. $this->storeTmp['u_inactiveusers'] = 0;
  295. $this->storeTmp['u_frozenusers'] = 0;
  296. $this->storeTmp['u_complextotal'] = 0;
  297. $this->storeTmp['u_complexactive'] = 0;
  298. $this->storeTmp['u_complexinactive'] = 0;
  299. $this->storeTmp['u_signups'] = 0;
  300. $this->storeTmp['u_citysignups'] = '';
  301. $this->storeTmp['f_totalmoney'] = 0;
  302. $this->storeTmp['f_paymentscount'] = 0;
  303. $this->storeTmp['f_cashmoney'] = 0;
  304. $this->storeTmp['f_cashcount'] = 0;
  305. $this->storeTmp['f_arpu'] = 0;
  306. $this->storeTmp['f_arpau'] = 0;
  307. $this->storeTmp['c_totalusers'] = 0;
  308. $this->storeTmp['c_activeusers'] = 0;
  309. $this->storeTmp['c_inactiveusers'] = 0;
  310. $this->storeTmp['c_illegal'] = 0;
  311. $this->storeTmp['c_complex'] = 0;
  312. $this->storeTmp['c_social'] = 0;
  313. $this->storeTmp['c_totalmoney'] = 0;
  314. $this->storeTmp['c_paymentscount'] = 0;
  315. $this->storeTmp['c_arpu'] = 0;
  316. $this->storeTmp['c_arpau'] = 0;
  317. $this->storeTmp['c_totaldebt'] = 0;
  318. $this->storeTmp['c_signups'] = 0;
  319. $this->storeTmp['a_totalcalls'] = 0;
  320. $this->storeTmp['a_totalanswered'] = 0;
  321. $this->storeTmp['a_totalcallsduration'] = 0;
  322. $this->storeTmp['a_averagecallduration'] = 0;
  323. $this->storeTmp['e_switches'] = 0;
  324. $this->storeTmp['e_pononu'] = 0;
  325. $this->storeTmp['e_docsis'] = 0;
  326. $this->storeTmp['a_recallunsuccess'] = 0;
  327. $this->storeTmp['a_recalltrytime'] = 0;
  328. $this->storeTmp['e_deadswintervals'] = 0;
  329. $this->storeTmp['t_sigreq'] = 0;
  330. $this->storeTmp['t_tickets'] = 0;
  331. $this->storeTmp['t_tasks'] = 0;
  332. $this->storeTmp['t_capabtotal'] = 0;
  333. $this->storeTmp['t_capabundone'] = 0;
  334. $this->storeTmp['a_outtotalcalls'] = 0;
  335. $this->storeTmp['a_outtotalanswered'] = 0;
  336. $this->storeTmp['a_outtotalcallsduration'] = 0;
  337. $this->storeTmp['a_outaveragecallduration'] = 0;
  338. }
  339. /**
  340. * Loads all of available inet users into protected property
  341. *
  342. * @return void
  343. */
  344. protected function loadUserData() {
  345. $this->allInetUsers = zb_UserGetAllStargazerDataAssoc();
  346. }
  347. /**
  348. * Loads signups by current month
  349. *
  350. * @return void
  351. */
  352. protected function loadSignups() {
  353. $signupsDb = new NyanORM(self::TABLE_SIGNUPS);
  354. $signupsDb->where('date', 'LIKE', $this->curmonth . "-%");
  355. $this->monthSignups = $signupsDb->getAll();
  356. }
  357. /**
  358. * Preloads user and cities mappings into protected property
  359. *
  360. * @return void
  361. */
  362. protected function loadUserCities() {
  363. $this->usersCities = zb_AddressGetCityUsers();
  364. }
  365. /**
  366. * Detects user activity state
  367. *
  368. * @param array $userData
  369. *
  370. * @return int 1 - active, 0 - inactive, -1 - frozen
  371. */
  372. protected function isActive($userData) {
  373. /**
  374. * Ой, чий то кінь стоїть,
  375. * Що сива гривонька.
  376. * Сподобалась мені,
  377. * Сподобалась мені
  378. * Тая дівчинонька.
  379. */
  380. $result = '';
  381. if (($userData['Cash'] >= '-' . $userData['Credit']) AND ( $userData['AlwaysOnline'] == 1) AND ( $userData['Passive'] == 0) AND ( $userData['Down'] == 0)) {
  382. $result = 1;
  383. }
  384. if (($userData['Cash'] < '-' . $userData['Credit']) AND ( $userData['AlwaysOnline'] == 1) AND ( $userData['Passive'] == 0) AND ( $userData['Down'] == 0)) {
  385. $result = 0;
  386. }
  387. if (($userData['Cash'] < '-' . $userData['Credit']) AND ( $userData['AlwaysOnline'] == 1) AND ( $userData['Passive'] == 0) AND ( $userData['Down'] == 1)) {
  388. $result = 0;
  389. }
  390. if ($userData['Passive'] == 1) {
  391. $result = -1;
  392. }
  393. return ($result);
  394. }
  395. /**
  396. * Detect is user complex or not?
  397. *
  398. * @param array $userArray
  399. *
  400. * @return bool
  401. */
  402. protected function isComplex($userArray) {
  403. $result = false;
  404. if ($this->complexFlag) {
  405. if (!empty($this->complexMasks)) {
  406. foreach ($this->complexMasks as $io => $eachMask) {
  407. if (ispos($userArray['Tariff'], $eachMask)) {
  408. $result = true;
  409. break;
  410. }
  411. }
  412. }
  413. }
  414. return ($result);
  415. }
  416. /**
  417. * Do some user preprocessing
  418. *
  419. * @return void
  420. */
  421. protected function preprocessUserData() {
  422. $this->loadUserData();
  423. $this->loadUserCities();
  424. $this->loadSignups();
  425. if (!empty($this->allInetUsers)) {
  426. foreach ($this->allInetUsers as $io => $eachUser) {
  427. //active users
  428. if ($this->isActive($eachUser) == 1) {
  429. $this->storeTmp['u_activeusers']++;
  430. }
  431. //inactive users
  432. if ($this->isActive($eachUser) == 0) {
  433. $this->storeTmp['u_inactiveusers']++;
  434. }
  435. //just frozen bodies
  436. if ($this->isActive($eachUser) == -1) {
  437. $this->storeTmp['u_frozenusers']++;
  438. }
  439. //complex users detection
  440. if ($this->isComplex($eachUser)) {
  441. $this->storeTmp['u_complextotal']++;
  442. //active complex users
  443. if ($this->isActive($eachUser) == 1) {
  444. $this->storeTmp['u_complexactive']++;
  445. }
  446. }
  447. //total users count
  448. $this->storeTmp['u_totalusers']++;
  449. }
  450. //inactive complex users
  451. $this->storeTmp['u_complexinactive'] = $this->storeTmp['u_complextotal'] - $this->storeTmp['u_complexactive'];
  452. }
  453. //collecting monthly signups
  454. if (!empty($this->monthSignups)) {
  455. $cityTmp = array();
  456. foreach ($this->monthSignups as $io => $eachSignup) {
  457. if (!empty($eachSignup['login'])) {
  458. if (isset($this->usersCities[$eachSignup['login']])) {
  459. $userCity = $this->usersCities[$eachSignup['login']];
  460. if (isset($cityTmp[$userCity])) {
  461. $cityTmp[$userCity]++;
  462. } else {
  463. $cityTmp[$userCity] = 1;
  464. }
  465. }
  466. }
  467. //count each signup
  468. $this->storeTmp['u_signups']++;
  469. }
  470. $this->storeTmp['u_citysignups'] = base64_encode(serialize($cityTmp));
  471. }
  472. }
  473. /**
  474. * Preprocess monthly signups data
  475. *
  476. * @return void
  477. */
  478. protected function preprocessFinanceData() {
  479. $paymentsDb = new NyanORM(self::TABLE_PAYMENTS);
  480. $paymentsDb->where('date', 'LIKE', $this->curmonth . '-%');
  481. $paymentsDb->where('summ', '>', '0');
  482. $allPayments = $paymentsDb->getAll();
  483. if (!empty($allPayments)) {
  484. foreach ($allPayments as $io => $each) {
  485. //total money counting
  486. $this->storeTmp['f_totalmoney'] += round($each['summ'], 2);
  487. //total payments count increment
  488. $this->storeTmp['f_paymentscount']++;
  489. //cash money processing
  490. if (($each['summ'] >= 0) AND ( isset($this->cashIds[$each['cashtypeid']]))) {
  491. $this->storeTmp['f_cashmoney'] += round($each['summ'], 2);
  492. $this->storeTmp['f_cashcount']++;
  493. }
  494. }
  495. //omg omg omg division by zero :)
  496. if ($this->storeTmp['f_paymentscount'] != 0) {
  497. //just ARPU - average revenue per user
  498. $this->storeTmp['f_arpu'] = round($this->storeTmp['f_totalmoney'] / $this->storeTmp['f_paymentscount'], 2);
  499. //ARPAU - average revenue per active user
  500. if ($this->storeTmp['u_activeusers'] != 0) {
  501. $this->storeTmp['f_arpau'] = round($this->storeTmp['f_totalmoney'] / $this->storeTmp['u_activeusers'], 2);
  502. }
  503. }
  504. }
  505. }
  506. /**
  507. * Performs all UKV users/payments/signups preprocessing
  508. *
  509. * @return void
  510. */
  511. protected function preprocessUkvData() {
  512. if ($this->ukvFlag) {
  513. //loading users
  514. $ukvUsersDb = new NyanORM(self::TABLE_CATV_USERS);
  515. $allUkvUsers = $ukvUsersDb->getAll('id');
  516. if (empty($allUkvUsers)) {
  517. $allUkvUsers = array();
  518. }
  519. //loading tariffs
  520. $allUkvTariffs = array();
  521. $ukvTariffPrices = array();
  522. $ukvTariffsDb = new NyanORM(self::TABLE_CATV_TARIFFS);
  523. $allUkvTariffs = $ukvTariffsDb->getAll('id');
  524. if (!empty($allUkvTariffs)) {
  525. foreach ($allUkvTariffs as $io => $each) {
  526. $ukvTariffPrices[$each['id']] = $each['price'];
  527. }
  528. }
  529. //loding monthly payments
  530. $allUkvPayments = array();
  531. $ukvPaymentsDb = new NyanORM(self::TABLE_CATV_PAYMENTS);
  532. $ukvPaymentsDb->where('date', 'LIKE', $this->curmonth . '-%');
  533. $ukvPaymentsDb->where('summ', '>', '0');
  534. $ukvPaymentsDb->where('visible', '=', '1');
  535. $allUkvPayments = $ukvPaymentsDb->getAll();
  536. //counting monthly signups and other shit
  537. if (!empty($allUkvUsers)) {
  538. foreach ($allUkvUsers as $io => $eachUser) {
  539. //total users count
  540. $this->storeTmp['c_totalusers']++;
  541. //total debt
  542. if ($eachUser['cash'] < 0) {
  543. $this->storeTmp['c_totaldebt'] += round($eachUser['cash'], 2);
  544. }
  545. //active users counting
  546. if (isset($ukvTariffPrices[$eachUser['tariffid']])) {
  547. $tariffPrice = $ukvTariffPrices[$eachUser['tariffid']];
  548. $debtLimit = $this->ukvDebtLimit * $tariffPrice;
  549. if (($eachUser['cash'] >= '-' . $debtLimit) AND ( $eachUser['active'] == 1)) {
  550. $this->storeTmp['c_activeusers']++;
  551. }
  552. }
  553. //illegal users count
  554. if (!empty($this->ukvIllegal)) {
  555. if ($eachUser['tariffid'] == $this->ukvIllegal) {
  556. $this->storeTmp['c_illegal']++;
  557. }
  558. }
  559. //complex users count
  560. if (!empty($this->ukvComplex)) {
  561. if ($this->complexFlag) {
  562. if ($eachUser['tariffid'] == $this->ukvComplex) {
  563. $this->storeTmp['c_complex']++;
  564. }
  565. }
  566. }
  567. //counting social users
  568. if (!empty($this->ukvSocial)) {
  569. if ($eachUser['tariffid'] == $this->ukvSocial) {
  570. $this->storeTmp['c_social']++;
  571. }
  572. }
  573. //current month ssignups
  574. if (ispos($eachUser['regdate'], $this->curmonth . '-')) {
  575. $this->storeTmp['c_signups']++;
  576. }
  577. }
  578. //inactive users
  579. $this->storeTmp['c_inactiveusers'] = $this->storeTmp['c_totalusers'] - $this->storeTmp['c_activeusers'];
  580. }
  581. //preprocessing payments data
  582. if (!empty($allUkvPayments)) {
  583. foreach ($allUkvPayments as $io => $eachPayment) {
  584. //total summ
  585. $this->storeTmp['c_totalmoney'] += round($eachPayment['summ'], 2);
  586. //payments count
  587. $this->storeTmp['c_paymentscount']++;
  588. }
  589. //div by zero lol
  590. if ($this->storeTmp['c_paymentscount'] != 0) {
  591. $this->storeTmp['c_arpu'] = round($this->storeTmp['c_totalmoney'] / $this->storeTmp['c_paymentscount'], 2);
  592. }
  593. if ($this->storeTmp['c_activeusers'] != 0) {
  594. if (($this->complexFlag) AND ( $this->complexMasks)) {
  595. $this->storeTmp['c_arpau'] = round($this->storeTmp['c_totalmoney'] / ($this->storeTmp['c_activeusers'] - $this->storeTmp['c_complex']), 2);
  596. } else {
  597. //no complex services enabled
  598. $this->storeTmp['c_arpau'] = round($this->storeTmp['c_totalmoney'] / $this->storeTmp['c_activeusers'], 2);
  599. }
  600. }
  601. }
  602. }
  603. }
  604. /**
  605. * Collects and stores equipment data
  606. *
  607. * @return void
  608. */
  609. protected function preprocessEquipmentData() {
  610. //collecting switches count
  611. $switchesDb = new NyanORM(self::TABLE_SWITCHES);
  612. $switchesDb->where('desc', 'NOT LIKE', '%NP%');
  613. $switchesCount = $switchesDb->getFieldsCount();
  614. $this->storeTmp['e_switches'] = $switchesCount;
  615. //collecting dead switches intervals count
  616. $deadSwitchesCount = 0;
  617. $swDeadDb = new NyanORM(self::TABLE_SWDEAD);
  618. $swDeadDb->where('date', 'LIKE', $this->curmonth . '-%');
  619. $allDead = $swDeadDb->getAll();
  620. if (!empty($allDead)) {
  621. foreach ($allDead as $io => $each) {
  622. if (!empty($each['swdead'])) {
  623. $deadTmp = unserialize($each['swdead']);
  624. $deadSwitchesCount = $deadSwitchesCount + sizeof($deadTmp);
  625. }
  626. }
  627. }
  628. $this->storeTmp['e_deadswintervals'] = $deadSwitchesCount;
  629. //collecting PON ONU count
  630. if ($this->ponFlag) {
  631. $onuDb = new NyanORM(self::TABLE_ONU);
  632. $onuCount = $onuDb->getFieldsCount();
  633. $this->storeTmp['e_pononu'] = $onuCount;
  634. }
  635. //collecting docsis modems count
  636. if ($this->docsisFlag) {
  637. $modemsDb = new NyanORM(self::TABLE_DOCSIS);
  638. $modemsCount = $modemsDb->getFieldsCount();
  639. $this->storeTmp['e_docsis'] = $modemsCount;
  640. }
  641. }
  642. /**
  643. * Askozia PBX data fetching and processing
  644. *
  645. * @return void
  646. */
  647. protected function preprocessAskoziaData() {
  648. if ($this->askoziaFlag) {
  649. //gettin Askozia config
  650. $askoziaUrl = zb_StorageGet('ASKOZIAPBX_URL');
  651. $askoziaLogin = zb_StorageGet('ASKOZIAPBX_LOGIN');
  652. $askoziaPassword = zb_StorageGet('ASKOZIAPBX_PASSWORD');
  653. if ((!empty($askoziaUrl)) AND (!empty($askoziaLogin)) AND (!empty($askoziaPassword))) {
  654. $callsTmp = array();
  655. $normalCalls = array();
  656. $callFlows = array();
  657. //working time setup
  658. $rawWorkTime = $this->altCfg['WORKING_HOURS'];
  659. $rawWorkTime = explode('-', $rawWorkTime);
  660. $workStartTime = $rawWorkTime[0];
  661. $workEndTime = $rawWorkTime[1];
  662. $fields = array(
  663. 'extension_number' => 'all',
  664. 'cdr_filter' => 'incomingoutgoing',
  665. 'period_from' => $this->curmonth . '-01',
  666. 'period_to' => curdate(),
  667. 'date_format' => 'Y-m-d',
  668. 'time_format' => 'H:i:s',
  669. 'page_format' => 'A4',
  670. 'SubmitCSVCDR' => 'Download CSV');
  671. $ch = curl_init();
  672. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  673. curl_setopt($ch, CURLOPT_URL, $askoziaUrl . '/status_cdr.php');
  674. curl_setopt($ch, CURLOPT_USERPWD, $askoziaLogin . ":" . $askoziaPassword);
  675. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  676. curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
  677. $rawResult = curl_exec($ch);
  678. curl_close($ch);
  679. if (!empty($rawResult)) {
  680. $callsTmp = explodeRows($rawResult);
  681. if (!empty($callsTmp)) {
  682. foreach ($callsTmp as $eachline) {
  683. $explode = explode(';', $eachline); //in 2.2.8 delimiter changed from ," to ;
  684. if (!empty($eachline)) {
  685. $normalCalls[] = str_replace('"', '', $explode);
  686. }
  687. }
  688. }
  689. if (!empty($normalCalls)) {
  690. unset($normalCalls[0]);
  691. foreach ($normalCalls as $io => $each) {
  692. //Askozia CFE fix
  693. if (sizeof($each) > 25) {
  694. array_splice($each, 3, 1);
  695. }
  696. if (@!ispos($each[16], 'out')) {
  697. @$startTime = explode(' ', $each[9]);
  698. @$startTime = $startTime[1];
  699. //only working time
  700. if (zb_isTimeBetween($workStartTime, $workEndTime, $startTime)) {
  701. //calls with less then 24 hours duration
  702. if ($each['13'] < 86400) {
  703. if (ispos($each[14], 'ANSWERED') AND (!ispos($each[7], 'VoiceMail'))) {
  704. $this->storeTmp['a_totalanswered']++;
  705. }
  706. $this->storeTmp['a_totalcalls']++;
  707. //call duration in seconds increment
  708. $this->storeTmp['a_totalcallsduration'] += $each[13];
  709. }
  710. }
  711. }
  712. /*
  713. * CFLOWS FIX
  714. * ************** */
  715. //some flow started for income call
  716. if (@!ispos($each['16'], 'out')) {
  717. $incomeNumber = $each[1];
  718. if ($each[2] == 'CALLFLOW-START' OR ispos($each[8], 'CALLFLOW-START')) {
  719. $callFlows[$incomeNumber . '|' . $each[11]] = 'NO ANSWER';
  720. } else {
  721. if (isset($callFlows[$incomeNumber . '|' . @$each[11]])) {
  722. $callFlows[$incomeNumber . '|' . $each[11]] = $each[14];
  723. } else {
  724. foreach ($callFlows as $cflowid => $cflowdata) {
  725. if (ispos($cflowid, $incomeNumber)) {
  726. $flowtime = explode('|', $cflowid);
  727. $flowtime = $flowtime[1];
  728. $flowtime = strtotime($flowtime);
  729. $callTimeTmp = strtotime($each[11]);
  730. if (($callTimeTmp - $flowtime) <= 10) {
  731. if (ispos($each[14], 'ANS')) {
  732. $callFlows[$cflowid] = $each[14];
  733. }
  734. }
  735. }
  736. }
  737. }
  738. }
  739. }
  740. }
  741. if (!empty($callFlows)) {
  742. $this->storeTmp['a_totalanswered'] = 0;
  743. $this->storeTmp['a_totalcalls'] = sizeof($callFlows);
  744. foreach ($callFlows as $cflowid => $cflowdata) {
  745. $flowTime = explode('|', $cflowid);
  746. $flowTime = explode(' ', $flowTime[1]);
  747. $flowTime = $flowTime[1];
  748. if ($cflowdata == 'ANSWERED') {
  749. if (zb_isTimeBetween($workStartTime, $workEndTime, $flowTime)) {
  750. $this->storeTmp['a_totalanswered']++;
  751. }
  752. }
  753. }
  754. }
  755. //average calls duration
  756. $this->storeTmp['a_averagecallduration'] = $this->storeTmp['a_totalcallsduration'] / $this->storeTmp['a_totalanswered'];
  757. }
  758. }
  759. }
  760. }
  761. }
  762. /**
  763. * Telepony CDR data fetching and processing
  764. *
  765. * @return void
  766. */
  767. protected function preprocessTeleponyData() {
  768. if ($this->teleponyFlag) {
  769. $telepony = new TelePony();
  770. if ($this->altCfg['TELEPONY_CDR']) {
  771. $teleponyData = $telepony->getHorseMonthData();
  772. //incoming calls
  773. $this->storeTmp['a_totalanswered'] = $teleponyData['a_totalanswered'];
  774. $this->storeTmp['a_totalcalls'] = $teleponyData['a_totalcalls'];
  775. $this->storeTmp['a_totalcallsduration'] = $teleponyData['a_totalcallsduration'];
  776. $this->storeTmp['a_averagecallduration'] = $teleponyData['a_averagecallduration'];
  777. //outgoing calls
  778. $this->storeTmp['a_outtotalanswered'] = $teleponyData['a_outtotalanswered'];
  779. $this->storeTmp['a_outtotalcalls'] = $teleponyData['a_outtotalcalls'];
  780. $this->storeTmp['a_outtotalcallsduration'] = $teleponyData['a_outtotalcallsduration'];
  781. $this->storeTmp['a_outaveragecallduration'] = $teleponyData['a_outaveragecallduration'];
  782. }
  783. }
  784. }
  785. /**
  786. * WDYC data loading and preprocessing
  787. *
  788. * @return void
  789. */
  790. protected function preprocessWdycData() {
  791. if ($this->altCfg['WDYC_ENABLED']) {
  792. $totalMissed = 0;
  793. $totalRecalls = 0;
  794. $totalUnsucc = 0;
  795. $totalCalls = 0;
  796. $totalReactTime = 0;
  797. $wdycDb = new NyanORM(self::TABLE_WDYC);
  798. $wdycDb->where('date', 'LIKE', $this->curmonth . '-%');
  799. $allWdycStat = $wdycDb->getAll();
  800. if (!empty($allWdycStat)) {
  801. foreach ($allWdycStat as $io => $each) {
  802. $totalMissed += $each['missedcount'];
  803. $totalRecalls += $each['recallscount'];
  804. $totalUnsucc += $each['unsucccount'];
  805. $totalReactTime += $each['totaltrytime'];
  806. }
  807. }
  808. $totalCalls = $totalRecalls + $totalMissed;
  809. $this->storeTmp['a_recallunsuccess'] = zb_PercentValue($totalCalls, $totalMissed);
  810. $recallCallsTotals = $totalRecalls + $totalUnsucc;
  811. if ($recallCallsTotals != 0) {
  812. $this->storeTmp['a_recalltrytime'] = round(($totalReactTime / $recallCallsTotals));
  813. } else {
  814. $this->storeTmp['a_recalltrytime'] = 0;
  815. }
  816. }
  817. }
  818. /**
  819. * Preprocessing tickets, sigreqs, capabs and other single data inputs
  820. *
  821. * @return void
  822. */
  823. protected function preprocessMisc() {
  824. //signup requests count per month
  825. if ($this->altCfg['SIGREQ_ENABLED']) {
  826. $sigReqDb = new NyanORM(self::TABLE_SIGREQ);
  827. $sigReqDb->where('date', 'LIKE', $this->curmonth . '-%');
  828. $sigreqCount = $sigReqDb->getFieldsCount();
  829. $this->storeTmp['t_sigreq'] = $sigreqCount;
  830. }
  831. //tickets per month count
  832. $ticketsTmp = zb_AnalyticsTicketingGetCountYear($this->curmonth);
  833. $this->storeTmp['t_tickets'] = $ticketsTmp[date("m")];
  834. //tasks in taskmanager for current month
  835. $taskTmp = zb_AnalyticsTaskmanGetCountYear(curyear());
  836. $this->storeTmp['t_tasks'] = $taskTmp[date("m")];
  837. //capabdir stats
  838. if ($this->altCfg['CAPABDIR_ENABLED']) {
  839. $capabUndone = 0;
  840. $capabTotal = 0;
  841. $capabDb = new NyanORM(self::TABLE_CAPABS);
  842. $capabDb->where('date', 'LIKE', $this->curmonth . '-%');
  843. $capabTmp = $capabDb->getAll();
  844. if (!empty($capabTmp)) {
  845. $capabTotal = sizeof($capabTmp);
  846. foreach ($capabTmp as $io => $each) {
  847. if ($each['stateid'] == 0) {
  848. $capabUndone++;
  849. }
  850. }
  851. }
  852. $this->storeTmp['t_capabtotal'] = $capabTotal;
  853. $this->storeTmp['t_capabundone'] = $capabUndone;
  854. }
  855. }
  856. /**
  857. * Saves current storeTmp into database
  858. *
  859. * @return void
  860. */
  861. protected function saveHorseData() {
  862. $curTime = curdatetime();
  863. $this->horseDb->data('date', $curTime);
  864. if (!empty($this->storeTmp)) {
  865. foreach ($this->storeTmp as $eachField => $eachValue) {
  866. $this->horseDb->data($eachField, $eachValue);
  867. }
  868. }
  869. $this->horseDb->create();
  870. log_register('EXHORSE SAVE DATA');
  871. }
  872. /**
  873. * Do all data preprocessing and store results into database
  874. *
  875. * @return void
  876. */
  877. public function runHorse() {
  878. $this->preprocessUserData();
  879. $this->preprocessFinanceData();
  880. $this->preprocessUkvData();
  881. $this->preprocessEquipmentData();
  882. $this->preprocessAskoziaData();
  883. $this->preprocessTeleponyData();
  884. $this->preprocessWdycData();
  885. $this->preprocessMisc();
  886. $this->saveHorseData();
  887. $this->cleanupDb();
  888. }
  889. /**
  890. * Loads stats data from database
  891. *
  892. * @param bool $allTime - load stored data for all time, ignoring this->showYear
  893. *
  894. * @return array
  895. */
  896. protected function loadStoredData($allTime = false) {
  897. $result = array();
  898. if (!empty($this->showYear)) {
  899. //from oldest to newest
  900. $this->horseDb->orderBy('id', 'ASC');
  901. //setting date filter if not all of data is required
  902. if (!$allTime) {
  903. $this->horseDb->where('date', 'LIKE', $this->showYear . '-%');
  904. }
  905. $all = $this->horseDb->getAll();
  906. if (!empty($all)) {
  907. foreach ($all as $io => $each) {
  908. $timestamp = strtotime($each['date']);
  909. $year = date("Y", $timestamp);
  910. $month = date("m", $timestamp);
  911. $result[$year][$month] = $each;
  912. }
  913. }
  914. }
  915. return ($result);
  916. }
  917. /**
  918. * Cleans previous days data if current month day is the last
  919. * or all previous records for this this month.. i hope.
  920. *
  921. * @return void
  922. */
  923. public function cleanupDb() {
  924. //In normal cases - cleanup previous data on last day of month
  925. if (!ubRouting::checkGet('ebobo')) {
  926. //now
  927. $curDay = date("d");
  928. //last day of month?
  929. if ($curDay == date("t")) {
  930. $curMonth = date("Y-m");
  931. $this->horseDb->where('date', 'LIKE', $curMonth . '-%');
  932. $this->horseDb->where('date', 'NOT LIKE', $curMonth . '-' . $curDay . '%');
  933. $this->horseDb->delete();
  934. log_register('EXHORSE CLEANUP MONTH `' . $curMonth . '`');
  935. }
  936. } else {
  937. //Pautina mode for some reason
  938. $query = 'DELETE `ex` FROM `' . self::TABLE_HORSE . '` AS `ex` LEFT JOIN (SELECT MAX(date) AS mDate FROM `' . self::TABLE_HORSE . '` GROUP BY DATE_FORMAT(date,"%Y-%m") ) AS `tmp` ON (`tmp`.`mDate`=`ex`.`date`) WHERE `tmp`.`mDate` IS NULL';
  939. nr_query($query);
  940. }
  941. }
  942. /**
  943. * Renders report for some year
  944. *
  945. * @return string
  946. */
  947. public function renderReport() {
  948. $result = '';
  949. $months = months_array_localized();
  950. $inputs = wf_YearSelectorPreset('yearsel', __('Year'), false, $this->showYear, true) . ' ';
  951. $chartsFlag = (wf_CheckPost(array('showcharts'))) ? true : false;
  952. $allTimeFlag = ($this->showYear == '1488') ? true : false; // dont ask me why
  953. $inputs .= wf_CheckInput('showcharts', __('Graphs'), false, $chartsFlag) . ' ';
  954. $inputs .= wf_Submit(__('Show'));
  955. $yearForm = wf_Form('', 'POST', $inputs, 'glamour');
  956. $yearForm .= wf_CleanDiv();
  957. $result .= $yearForm;
  958. $riseOfTheNorthStar = array(); //userbase rise and drain stats
  959. $riseOfTheNorthStar['total'] = 0;
  960. $riseOfTheNorthStar['active'] = 0;
  961. $riseOfTheNorthStar['signups'] = 0;
  962. $totalSignups = 0;
  963. //first year month hack
  964. if (!$allTimeFlag) {
  965. $horseBase = new NyanORM(self::TABLE_HORSE);
  966. $previousDecember = (($this->showYear - 1) . '-12-%'); // december of previous year
  967. $horseBase->where('date', 'LIKE', $previousDecember);
  968. $horseBase->selectable(array('u_totalusers', 'u_activeusers', 'u_signups'));
  969. $prevYearStats = $horseBase->getAll();
  970. if (!empty($prevYearStats)) {
  971. $riseOfTheNorthStar['total'] = $prevYearStats[0]['u_totalusers'];
  972. $riseOfTheNorthStar['active'] = $prevYearStats[0]['u_activeusers'];
  973. $riseOfTheNorthStar['signups'] = $prevYearStats[0]['u_signups'];
  974. }
  975. }
  976. //data loading
  977. $yearData = $this->loadStoredData($allTimeFlag);
  978. //charts presets
  979. $chartsOptions = "
  980. 'focusTarget': 'category',
  981. 'hAxis': {
  982. 'color': 'none',
  983. 'baselineColor': 'none',
  984. },
  985. 'vAxis': {
  986. 'color': 'none',
  987. 'baselineColor': 'none',
  988. },
  989. 'curveType': 'function',
  990. 'pointSize': 5,
  991. 'crosshair': {
  992. trigger: 'none'
  993. },";
  994. $usersChartData = array(0 => array(__('Month'), __('Total'), __('Active'), __('Inactive'), __('Frozen')));
  995. $usersSignupsChartData = array(0 => array(__('Month'), __('Signups')));
  996. $complexChartData = array(0 => array(__('Month'), __('Total'), __('Active'), __('Inactive')));
  997. $financeChartsData = array(0 => array(__('Month'), __('Money'), __('Payments count')));
  998. $arpuChartsData = array(0 => array(__('Month'), __('ARPU'), __('ARPAU')));
  999. $ukvChartData = array(0 => array(__('Month'), __('Total'), __('Active'), __('Inactive'), __('Illegal'), __('Complex'), __('Social'), __('Signups')));
  1000. $ukvfChartData = array(0 => array(__('Month'), __('Money'), __('Payments count'), __('Debt')));
  1001. $ukvarpuChartData = array(0 => array(__('Month'), __('ARPU'), __('ARPAU')));
  1002. $universeChartData = array(0 => array(__('Month'), __('Signup requests'), __('Tickets'), __('Tasks'), __('Signup capabilities'), __('Undone')));
  1003. $telephonyChartData = array(0 => array(__('Month'), __('Total calls'), __('Total answered'), __('No answer')));
  1004. $equipChartData = array(0 => array(__('Month'), __('Switches')));
  1005. $citySignupsTmp = array();
  1006. if ($this->ponFlag AND $this->docsisFlag) {
  1007. $equipChartData = array(0 => array(__('Month'), __('Switches'), __('PON ONU'), __('DOCSIS modems')));
  1008. }
  1009. if (!$this->docsisFlag AND $this->ponFlag) {
  1010. $equipChartData = array(0 => array(__('Month'), __('Switches'), __('PON ONU')));
  1011. }
  1012. if ($this->docsisFlag AND !$this->ponFlag) {
  1013. $equipChartData = array(0 => array(__('Month'), __('Switches'), __('DOCSIS modems')));
  1014. }
  1015. if (!empty($yearData)) {
  1016. //internet users
  1017. $cells = wf_TableCell(__('Month'));
  1018. $cells .= wf_TableCell(__('Total'));
  1019. $cells .= wf_TableCell(__('Movement') . ' (' . __('Clean') . '/' . __('Dirty') . ')');
  1020. $cells .= wf_TableCell(__('Active'));
  1021. $cells .= wf_TableCell(__('Inactive'));
  1022. $cells .= wf_TableCell(__('Frozen'));
  1023. $cells .= wf_TableCell(__('Signups'));
  1024. $rows = wf_TableRow($cells, 'row1');
  1025. foreach ($yearData as $yearNum => $monthArr) {
  1026. foreach ($monthArr as $monthNum => $each) {
  1027. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1028. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1029. $fontEnd = wf_tag('font', true);
  1030. if (!empty($riseOfTheNorthStar['active'])) {
  1031. $starDelimiter = ' / ';
  1032. $riseTotal = $each['u_activeusers'] - $riseOfTheNorthStar['active'];
  1033. if ($riseTotal > 0) {
  1034. $fontColor = wf_tag('font', false, '', 'color="#' . self::COLOR_GOOD . '"');
  1035. $riseUsersIcon = wf_img_sized(self::ICON_RISE, __('Increased'), '10', '10');
  1036. } else {
  1037. $fontColor = wf_tag('font', false, '', 'color="#' . self::COLOR_BAD . '"');
  1038. $riseUsersIcon = wf_img_sized(self::ICON_DRAIN, __('Decreased'), '10', '10');
  1039. }
  1040. } else {
  1041. $fontColor = '';
  1042. $riseUsersIcon = '';
  1043. $riseTotal = '';
  1044. $fontEnd = '';
  1045. $starDelimiter = '';
  1046. }
  1047. if (!empty($riseOfTheNorthStar['active'])) {
  1048. $riseActive = ($each['u_activeusers'] - ($riseOfTheNorthStar['active'] + $each['u_signups']));
  1049. if ($riseActive > 0) {
  1050. $fontColorActive = wf_tag('font', false, '', 'color="#' . self::COLOR_GOOD . '"');
  1051. $riseActiveIcon = wf_img_sized(self::ICON_RISE, __('Increased'), '10', '10');
  1052. } else {
  1053. $fontColorActive = wf_tag('font', false, '', 'color="#' . self::COLOR_BAD . '"');
  1054. $riseActiveIcon = wf_img_sized(self::ICON_DRAIN, __('Decreased'), '10', '10');
  1055. }
  1056. } else {
  1057. $fontColorActive = '';
  1058. $riseActiveIcon = '';
  1059. $riseActive = '';
  1060. $fontEnd = '';
  1061. }
  1062. $cells .= wf_TableCell($each['u_totalusers']);
  1063. $cells .= wf_TableCell($riseUsersIcon . ' ' . $fontColor . $riseTotal . $fontEnd . $starDelimiter . $riseActiveIcon . ' ' . $fontColorActive . $riseActive . $fontEnd);
  1064. $cells .= wf_TableCell($each['u_activeusers'] . ' (' . zb_PercentValue($each['u_totalusers'], $each['u_activeusers']) . '%)');
  1065. $cells .= wf_TableCell($each['u_inactiveusers'] . ' (' . zb_PercentValue($each['u_totalusers'], $each['u_inactiveusers']) . '%)');
  1066. $cells .= wf_TableCell($each['u_frozenusers'] . ' (' . zb_PercentValue($each['u_totalusers'], $each['u_frozenusers']) . '%)');
  1067. if (!empty($each['u_citysignups'])) {
  1068. $signupData = '';
  1069. $sigDataTmp = base64_decode($each['u_citysignups']);
  1070. $sigDataTmp = unserialize($sigDataTmp);
  1071. $citySigs = '';
  1072. $cityRows = '';
  1073. if (!empty($sigDataTmp)) {
  1074. $cityCells = wf_TableCell(__('City'));
  1075. $cityCells .= wf_TableCell(__('Signups'));
  1076. $cityRows .= wf_TableRow($cityCells, 'row1');
  1077. foreach ($sigDataTmp as $sigCity => $cityCount) {
  1078. $cityCells = wf_TableCell($sigCity);
  1079. $cityCells .= wf_TableCell($cityCount);
  1080. $cityRows .= wf_TableRow($cityCells, 'row5');
  1081. $citySignupsTmp[$sigCity][$yearDisplay . $months[$monthNum]] = $cityCount;
  1082. }
  1083. $containerStyle = 'max-height:500px; min-width:400px;';
  1084. $citySigs .= wf_AjaxContainer('ctsigs', 'style="' . $containerStyle . '"', wf_TableBody($cityRows, '100%', 0, 'sortable'));
  1085. }
  1086. $signupData .= wf_modalAuto($each['u_signups'], __('Cities'), $citySigs);
  1087. } else {
  1088. $signupData = $each['u_signups'];
  1089. }
  1090. $totalSignups += $each['u_signups']; //just signups counter for selected period
  1091. $cells .= wf_TableCell($signupData);
  1092. $rows .= wf_TableRow($cells, 'row3');
  1093. //chart data
  1094. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1095. $usersChartData[] = array($yearDisplay . $months[$monthNum], $each['u_totalusers'], $each['u_activeusers'], $each['u_inactiveusers'], $each['u_frozenusers']);
  1096. $usersSignupsChartData[] = array($yearDisplay . $months[$monthNum], $each['u_signups']);
  1097. //rise and drain stats
  1098. $riseOfTheNorthStar['total'] = $each['u_totalusers'];
  1099. $riseOfTheNorthStar['active'] = $each['u_activeusers'];
  1100. $riseOfTheNorthStar['signups'] = $each['u_signups'];
  1101. }
  1102. }
  1103. $result .= wf_tag('h2') . __('Internets users') . wf_tag('h2', true);
  1104. $result .= wf_TableBody($rows, '100%', 0, '') . ' ';
  1105. $result .= __('Total users registered') . ': ' . $totalSignups;
  1106. if ($chartsFlag) {
  1107. $result .= wf_gchartsLine($usersChartData, __('Internets users'), '100%', '300px', $chartsOptions);
  1108. $result .= wf_gchartsLine($usersSignupsChartData, __('Signups'), '100%', '300px', $chartsOptions);
  1109. if (!empty($citySignupsTmp)) {
  1110. $allSignupCities = array_keys($citySignupsTmp);
  1111. $citySignupsChartData[0] = $allSignupCities;
  1112. array_unshift($citySignupsChartData[0], __('Month'));
  1113. $csCount = 0;
  1114. foreach ($months as $csMonth => $csMonthName) {
  1115. $csCount++;
  1116. $monthLabel = $yearDisplay . $csMonthName;
  1117. $citySignupsChartData[$csCount] = array($monthLabel);
  1118. foreach ($citySignupsTmp as $eachCsCity => $csData) {
  1119. if (isset($citySignupsTmp[$eachCsCity][$monthLabel])) {
  1120. $emCount = $citySignupsTmp[$eachCsCity][$monthLabel];
  1121. } else {
  1122. $emCount = 0;
  1123. }
  1124. $citySignupsChartData[$csCount][] += $emCount;
  1125. }
  1126. }
  1127. $result .= wf_gchartsLine($citySignupsChartData, __('Cities'), '100%', '300px', $chartsOptions);
  1128. }
  1129. }
  1130. //complex data
  1131. if ($this->complexFlag) {
  1132. $result .= wf_tag('h2') . __('Complex services') . wf_tag('h2', true);
  1133. $cells = wf_TableCell(__('Month'));
  1134. $cells .= wf_TableCell(__('Total'));
  1135. $cells .= wf_TableCell(__('Active'));
  1136. $cells .= wf_TableCell(__('Inactive'));
  1137. $rows = wf_TableRow($cells, 'row1');
  1138. foreach ($yearData as $yearNum => $monthArr) {
  1139. foreach ($monthArr as $monthNum => $each) {
  1140. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1141. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1142. $cells .= wf_TableCell($each['u_complextotal']);
  1143. $cells .= wf_TableCell($each['u_complexactive'] . ' (' . zb_PercentValue($each['u_complextotal'], $each['u_complexactive']) . '%)');
  1144. $cells .= wf_TableCell($each['u_complexinactive'] . ' (' . zb_PercentValue($each['u_complextotal'], $each['u_complexinactive']) . '%)');
  1145. $rows .= wf_TableRow($cells, 'row3');
  1146. //chart data
  1147. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1148. $complexChartData[] = array($yearDisplay . $months[$monthNum], $each['u_complextotal'], $each['u_complexactive'], $each['u_complexinactive']);
  1149. }
  1150. }
  1151. $result .= wf_TableBody($rows, '100%', 0, '');
  1152. if ($chartsFlag) {
  1153. $result .= wf_gchartsLine($complexChartData, __('Complex services'), '100%', '300px', $chartsOptions);
  1154. }
  1155. }
  1156. if (cfr('REPORTFINANCE')) {
  1157. //finance data
  1158. $result .= wf_tag('h2') . __('Financial highlights') . wf_tag('h2', true);
  1159. $cells = wf_TableCell(__('Month'));
  1160. $cells .= wf_TableCell(__('Money'));
  1161. $cells .= wf_TableCell(__('Payments count'));
  1162. $cells .= wf_TableCell(__('Cash payments'));
  1163. $cells .= wf_TableCell(__('Cash payments count'));
  1164. $cells .= wf_TableCell(__('ARPU'));
  1165. $cells .= wf_TableCell(__('ARPAU'));
  1166. $rows = wf_TableRow($cells, 'row1');
  1167. foreach ($yearData as $yearNum => $monthArr) {
  1168. foreach ($monthArr as $monthNum => $each) {
  1169. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1170. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1171. $cells .= wf_TableCell(zb_CashBigValueFormat($each['f_totalmoney']));
  1172. $cells .= wf_TableCell($each['f_paymentscount']);
  1173. $cells .= wf_TableCell($each['f_cashmoney'] . ' (' . zb_PercentValue($each['f_totalmoney'], $each['f_cashmoney']) . '%)');
  1174. $cells .= wf_TableCell($each['f_cashcount'] . ' (' . zb_PercentValue($each['f_paymentscount'], $each['f_cashcount']) . '%)');
  1175. $cells .= wf_TableCell($each['f_arpu']);
  1176. $cells .= wf_TableCell($each['f_arpau']);
  1177. $rows .= wf_TableRow($cells, 'row3');
  1178. //chart data
  1179. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1180. $financeChartsData[] = array($yearDisplay . $months[$monthNum], $each['f_totalmoney'], $each['f_paymentscount']);
  1181. $arpuChartsData[] = array($yearDisplay . $months[$monthNum], $each['f_arpu'], $each['f_arpau']);
  1182. }
  1183. }
  1184. $result .= wf_TableBody($rows, '100%', 0, '');
  1185. if ($chartsFlag) {
  1186. $result .= wf_gchartsLine($financeChartsData, __('Financial highlights'), '100%', '300px', $chartsOptions);
  1187. $result .= wf_gchartsLine($arpuChartsData, __('ARPU'), '100%', '300px', $chartsOptions);
  1188. }
  1189. }
  1190. // UKV cable users
  1191. if ($this->ukvFlag) {
  1192. $result .= wf_tag('h2') . __('UKV users') . wf_tag('h2', true);
  1193. $cells = wf_TableCell(__('Month'));
  1194. $cells .= wf_TableCell(__('Total'));
  1195. $cells .= wf_TableCell(__('Active'));
  1196. $cells .= wf_TableCell(__('Inactive'));
  1197. $cells .= wf_TableCell(__('Illegal'));
  1198. if ($this->complexFlag) {
  1199. $cells .= wf_TableCell(__('Complex'));
  1200. }
  1201. $cells .= wf_TableCell(__('Social'));
  1202. $cells .= wf_TableCell(__('Signups'));
  1203. $rows = wf_TableRow($cells, 'row1');
  1204. foreach ($yearData as $yearNum => $monthArr) {
  1205. foreach ($monthArr as $monthNum => $each) {
  1206. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1207. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1208. $cells .= wf_TableCell($each['c_totalusers']);
  1209. $cells .= wf_TableCell($each['c_activeusers'] . ' (' . zb_PercentValue($each['c_totalusers'], $each['c_activeusers']) . '%)');
  1210. $cells .= wf_TableCell($each['c_inactiveusers'] . ' (' . zb_PercentValue($each['c_totalusers'], $each['c_inactiveusers']) . '%)');
  1211. $cells .= wf_TableCell($each['c_illegal'] . ' (' . zb_PercentValue($each['c_totalusers'], $each['c_illegal']) . '%)');
  1212. if ($this->complexFlag) {
  1213. $cells .= wf_TableCell($each['c_complex'] . ' (' . zb_PercentValue($each['c_totalusers'], $each['c_complex']) . '%)');
  1214. }
  1215. $cells .= wf_TableCell($each['c_social'] . ' (' . zb_PercentValue($each['c_totalusers'], $each['c_social']) . '%)');
  1216. $cells .= wf_TableCell($each['c_signups']);
  1217. $rows .= wf_TableRow($cells, 'row3');
  1218. //chart data
  1219. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1220. $ukvChartData[] = array($yearDisplay . $months[$monthNum], $each['c_totalusers'], $each['c_activeusers'], $each['c_inactiveusers'], $each['c_illegal'], $each['c_complex'], $each['c_social'], $each['c_signups']);
  1221. }
  1222. }
  1223. $result .= wf_TableBody($rows, '100%', 0, '');
  1224. if ($chartsFlag) {
  1225. $result .= wf_gchartsLine($ukvChartData, __('UKV users'), '100%', '300px', $chartsOptions);
  1226. }
  1227. if (cfr('REPORTFINANCE')) {
  1228. //UKV financial data
  1229. $result .= wf_tag('h2') . __('UKV finance') . wf_tag('h2', true);
  1230. $cells = wf_TableCell(__('Month'));
  1231. $cells .= wf_TableCell(__('Money'));
  1232. $cells .= wf_TableCell(__('Payments count'));
  1233. $cells .= wf_TableCell(__('ARPU'));
  1234. $cells .= wf_TableCell(__('ARPAU'));
  1235. $cells .= wf_TableCell(__('Debt'));
  1236. $rows = wf_TableRow($cells, 'row1');
  1237. foreach ($yearData as $yearNum => $monthArr) {
  1238. foreach ($monthArr as $monthNum => $each) {
  1239. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1240. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1241. $cells .= wf_TableCell(zb_CashBigValueFormat($each['c_totalmoney']));
  1242. $cells .= wf_TableCell($each['c_paymentscount']);
  1243. $cells .= wf_TableCell($each['c_arpu']);
  1244. $cells .= wf_TableCell($each['c_arpau']);
  1245. $cells .= wf_TableCell($each['c_totaldebt']);
  1246. $rows .= wf_TableRow($cells, 'row3');
  1247. //chart data
  1248. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1249. $ukvfChartData[] = array($yearDisplay . $months[$monthNum], $each['c_totalmoney'], $each['c_paymentscount'], $each['c_totaldebt']);
  1250. $ukvarpuChartData[] = array($yearDisplay . $months[$monthNum], $each['c_arpu'], $each['c_arpau']);
  1251. }
  1252. }
  1253. $result .= wf_TableBody($rows, '100%', 0, '');
  1254. if ($chartsFlag) {
  1255. $result .= wf_gchartsLine($ukvfChartData, __('UKV finance'), '100%', '300px', $chartsOptions);
  1256. $result .= wf_gchartsLine($ukvarpuChartData, __('UKV') . ' ' . __('ARPU'), '100%', '300px', $chartsOptions);
  1257. }
  1258. }
  1259. }
  1260. //PBX integration
  1261. if ($this->pbxFlag) {
  1262. //incoming calls
  1263. $result .= wf_tag('h2') . __('Telephony') . wf_tag('h2', true);
  1264. $result .= wf_img('skins/calls/incoming.png') . ' ' . __('Incoming calls');
  1265. $cells = wf_TableCell(__('Month'));
  1266. $cells .= wf_TableCell(__('Incoming calls'));
  1267. $cells .= wf_TableCell(__('Total answered'));
  1268. $cells .= wf_TableCell(__('No answer'));
  1269. $cells .= wf_TableCell(__('Total duration'));
  1270. $cells .= wf_TableCell(__('Average duration'));
  1271. $cells .= wf_TableCell(__('Answers percent'));
  1272. $cells .= wf_TableCell(__('No reaction percent'));
  1273. $cells .= wf_TableCell(__('Reaction time'));
  1274. $rows = wf_TableRow($cells, 'row1');
  1275. foreach ($yearData as $yearNum => $monthArr) {
  1276. foreach ($monthArr as $monthNum => $each) {
  1277. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1278. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1279. $cells .= wf_TableCell($each['a_totalcalls']);
  1280. $cells .= wf_TableCell($each['a_totalanswered']);
  1281. $cells .= wf_TableCell($each['a_totalcalls'] - $each['a_totalanswered']);
  1282. $cells .= wf_TableCell(zb_formatTime($each['a_totalcallsduration']));
  1283. $cells .= wf_TableCell(zb_formatTime($each['a_averagecallduration']));
  1284. $cells .= wf_TableCell(zb_PercentValue($each['a_totalcalls'], $each['a_totalanswered']) . '%');
  1285. $reactionPercent = ($each['a_recallunsuccess'] != NULL) ? $each['a_recallunsuccess'] . '%' : '';
  1286. $cells .= wf_TableCell($reactionPercent);
  1287. $cells .= wf_TableCell(zb_formatTime($each['a_recalltrytime']));
  1288. $rows .= wf_TableRow($cells, 'row3');
  1289. //chart data
  1290. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1291. $telephonyChartData[] = array($yearDisplay . $months[$monthNum], $each['a_totalcalls'], $each['a_totalanswered'], ($each['a_totalcalls'] - $each['a_totalanswered']));
  1292. }
  1293. }
  1294. $result .= wf_TableBody($rows, '100%', 0, '');
  1295. if ($chartsFlag) {
  1296. $result .= wf_gchartsLine($telephonyChartData, __('Telephony'), '100%', '300px', $chartsOptions);
  1297. }
  1298. //outcoming calls
  1299. $result .= wf_img('skins/calls/outgoing.png') . ' ' . __('Outgoing calls') . wf_delimiter(0);
  1300. $cells = wf_TableCell(__('Month'));
  1301. $cells .= wf_TableCell(__('Outgoing calls'));
  1302. $cells .= wf_TableCell(__('Total answered'));
  1303. $cells .= wf_TableCell(__('No answer'));
  1304. $cells .= wf_TableCell(__('Total duration'));
  1305. $cells .= wf_TableCell(__('Average duration'));
  1306. $cells .= wf_TableCell(__('Answers percent'));
  1307. $rows = wf_TableRow($cells, 'row1');
  1308. foreach ($yearData as $yearNum => $monthArr) {
  1309. foreach ($monthArr as $monthNum => $each) {
  1310. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1311. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1312. $cells .= wf_TableCell($each['a_outtotalcalls']);
  1313. $cells .= wf_TableCell($each['a_outtotalanswered']);
  1314. $cells .= wf_TableCell($each['a_outtotalcalls'] - $each['a_outtotalanswered']);
  1315. $cells .= wf_TableCell(zb_formatTime($each['a_outtotalcallsduration']));
  1316. $cells .= wf_TableCell(zb_formatTime($each['a_outaveragecallduration']));
  1317. $cells .= wf_TableCell(zb_PercentValue($each['a_outtotalcalls'], $each['a_outtotalanswered']) . '%');
  1318. $rows .= wf_TableRow($cells, 'row3');
  1319. //chart data
  1320. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1321. }
  1322. }
  1323. $result .= wf_TableBody($rows, '100%', 0, '');
  1324. }
  1325. //Users relationship
  1326. $result .= wf_tag('h2') . __('Relationships with the universe') . wf_tag('h2', true);
  1327. $cells = wf_TableCell(__('Month'));
  1328. if ($this->altCfg['SIGREQ_ENABLED']) {
  1329. $cells .= wf_TableCell(__('Signup requests'));
  1330. }
  1331. $cells .= wf_TableCell(__('Helpdesk tickets'));
  1332. $cells .= wf_TableCell(__('Tasks'));
  1333. if ($this->altCfg['CAPABDIR_ENABLED']) {
  1334. $cells .= wf_TableCell(__('Signup capabilities'));
  1335. $cells .= wf_TableCell(__('Undone') . ' ' . __('Signup capabilities'));
  1336. }
  1337. $rows = wf_TableRow($cells, 'row1');
  1338. foreach ($yearData as $yearNum => $monthArr) {
  1339. foreach ($monthArr as $monthNum => $each) {
  1340. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1341. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1342. if ($this->altCfg['SIGREQ_ENABLED']) {
  1343. $cells .= wf_TableCell($each['t_sigreq']);
  1344. }
  1345. $cells .= wf_TableCell($each['t_tickets']);
  1346. $cells .= wf_TableCell($each['t_tasks']);
  1347. if ($this->altCfg['CAPABDIR_ENABLED']) {
  1348. $cells .= wf_TableCell($each['t_capabtotal']);
  1349. $cells .= wf_TableCell($each['t_capabundone']);
  1350. }
  1351. $rows .= wf_TableRow($cells, 'row3');
  1352. //chart data
  1353. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1354. $universeChartData[] = array($yearDisplay . $months[$monthNum], $each['t_sigreq'], $each['t_tickets'], $each['t_tasks'], $each['t_capabtotal'], $each['t_capabundone']);
  1355. }
  1356. }
  1357. $result .= wf_TableBody($rows, '100%', 0, '');
  1358. if ($chartsFlag) {
  1359. //deleting NULL values ugly hack
  1360. if (!empty($universeChartData)) {
  1361. foreach ($universeChartData as $io => $each) {
  1362. if (!empty($each)) {
  1363. foreach ($each as $ia => $val) {
  1364. if ($val == NULL) {
  1365. $universeChartData[$io][$ia] = 0;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. }
  1371. $result .= wf_gchartsLine($universeChartData, __('Relationships with the universe'), '100%', '300px', $chartsOptions);
  1372. }
  1373. //Equipment
  1374. $result .= wf_tag('h2') . __('Equipment') . wf_tag('h2', true);
  1375. $cells = wf_TableCell(__('Month'));
  1376. $cells .= wf_TableCell(__('Switches'));
  1377. $cells .= wf_TableCell(__('Time') . ' ☠ ');
  1378. if ($this->ponFlag) {
  1379. $cells .= wf_TableCell(__('PON ONU'));
  1380. }
  1381. if ($this->docsisFlag) {
  1382. $cells .= wf_TableCell(__('DOCSIS Modems'));
  1383. }
  1384. $rows = wf_TableRow($cells, 'row1');
  1385. foreach ($yearData as $yearNum => $monthArr) {
  1386. foreach ($monthArr as $monthNum => $each) {
  1387. $yearDisplay = ($allTimeFlag) ? $yearNum . ' ' : '';
  1388. $cells = wf_TableCell($yearDisplay . $months[$monthNum]);
  1389. $cells .= wf_TableCell($each['e_switches']);
  1390. $switchesRepingInterval = (@$this->altCfg['SWITCH_PING_INTERVAL']) ? $this->altCfg['SWITCH_PING_INTERVAL'] : 20;
  1391. $deadSwitchTime = ($switchesRepingInterval * $each['e_deadswintervals']) * 60;
  1392. $cells .= wf_TableCell(zb_formatTime($deadSwitchTime));
  1393. if ($this->ponFlag) {
  1394. $cells .= wf_TableCell($each['e_pononu']);
  1395. }
  1396. if ($this->docsisFlag) {
  1397. $cells .= wf_TableCell($each['e_docsis']);
  1398. }
  1399. $rows .= wf_TableRow($cells, 'row3');
  1400. //chart data
  1401. $yearDisplay = ($monthNum == '01') ? $yearDisplay : '';
  1402. $equipChartRow = array($yearDisplay . $months[$monthNum], $each['e_switches']);
  1403. if ($this->ponFlag AND $this->docsisFlag) {
  1404. $equipChartRow = array($yearDisplay . $months[$monthNum], $each['e_switches'], $each['e_pononu'], $each['e_docsis']);
  1405. }
  1406. if ($this->ponFlag AND !$this->docsisFlag) {
  1407. $equipChartRow = array($yearDisplay . $months[$monthNum], $each['e_switches'], $each['e_pononu']);
  1408. }
  1409. if ($this->docsisFlag AND !$this->ponFlag) {
  1410. $equipChartRow = array($yearDisplay . $months[$monthNum], $each['e_switches'], $each['e_docsis']);
  1411. }
  1412. $equipChartData[] = $equipChartRow;
  1413. }
  1414. }
  1415. $result .= wf_TableBody($rows, '100%', 0, '');
  1416. if ($chartsFlag) {
  1417. $result .= wf_gchartsLine($equipChartData, __('Equipment'), '100%', '300px', $chartsOptions);
  1418. }
  1419. } else {
  1420. $result .= $this->messages->getStyledMessage(__('Nothing found'), 'info');
  1421. }
  1422. return ($result);
  1423. }
  1424. }