Odyssey
runBilling.i
1 <?php
2 /**
3  * File: runBilling.i
4  * Purpose: This file contains the whole process for running billing.
5  *
6  * The process takes what is in the contracts (either locally or from my.homecu.net)
7  * uses the calculations for each product type and creates preinvoice records.
8  * Afterwards, the preinvoice records are imported into QuickBooks using the web connector.
9  */
10 
11 /**
12  * function GetGoogleCookie($devmode)
13  * Gets the google cookie. If DEV it is hardcoded.
14  *
15  * @param $devmode -- 1 if devmode is true
16 
17  * @return string
18  */
19 function GetGoogleCookie($devmode) {
20  if ($devmode == 1) {
21  $googleCookie = "c2FtdWVsQGhvbWVjdS5jb218cVpoMVBFeGVZYlkrTlo5anlUMUwyNWhsYVYrM01XZlUxcW9iOEVXdVpURXdnUi9FLzcvRWprR0FIYWlDSU9rTnI3Zlk3L0tyM3EvcmxPck9nYTFnc1lhMkU5S3Y2d2h6SDJIOHZzT0VNYit0MEo5SEFhYmxKT25xSHdmNkJDZHRETzVENVhVVDB5aWdPam4wQTNWVTNHWWRsd0xLb3F4Vlg4L1FQRmxQRVorREZoS2VvSjRNL053OUdPcVFKcy9VWGc9PXwxNTY3MDk4ODkzfDdMQU56WVl1b0FFQXA4NTZWbGhwMjk2THRPTktqVFcrY3BzaWVkdURzZnZ1Z0ZjOFZmc1N4VzQrbE81R2w4dThUTk9GUWJYZEkxaXRvN3QzTzFkMHN0aDFHOXYrRjczQ2k4NGh3dCt4RnZQbDV3PT0=|1567095293|FEDshz4VY7clGFw5M5z4IT3Afwc=";
22  } else {
23  $googleCookie = "{$_COOKIE['homecu_dev_oauth2_proxy']}";
24  }
25  return $googleCookie;
26 }
27 
28 /**
29  * function RunCurlOdyssey($url, $devmode)
30  * Runs the CURL options for Odyssey. Odyssey requires Google authentication.
31  *
32  * @param $url -- the URL to run for the CURL command.
33  *
34  * @return (see RunCurl)
35  */
36 function RunCurlOdyssey($url, $devmode) {
37  $googleCookie = GetGoogleCookie($devmode);
38  $curlCookies = "HCUTicket=" . urlencode($_COOKIE['HCUTicket']) . ";homecu_dev_oauth2_proxy=$googleCookie";
39 
40  $rawresp = RunCurl($url, $curlCookies);
41 
42  return $rawresp;
43 }
44 
45 /**
46  * function RunCurlMammoth($url)
47  * Runs the CURL options for Mammoth. Mammoth doesn't require Google authentication.
48  *
49  * @param $url -- the URL to run for the CURL command.
50  *
51  * @return (see RunCurl)
52  */
53 function RunCurlMammoth($url) {
54  $curlCookies = "HCUTicket=" . urlencode($_COOKIE['HCUTicket']);
55 
56  $rawresp = RunCurl($url, $curlCookies);
57 
58  return $rawresp;
59 }
60 
61 /**
62  * function RunCurl($url, $cookies)
63  * Runs the CURL options.
64  * There are two additions to the "default" list of CURL options:
65  * 1) Handling a connection that times out.
66  * 2) Handling a connection with max allocated memory.
67  *
68  * @param $url -- the URL to run for the CURL command.
69  * @param $cookies -- the cookies to use for the CURL command.
70  *
71  * @return $httpCode -- the http code of the CURL command: 504, 200, or 0.
72  * @return $rawresp -- the response from the curl command.
73  */
74 function RunCurl($url, $cookies) {
75  $ch = curl_init();
76  curl_setopt($ch, CURLOPT_URL, $url);
77  curl_setopt($ch, CURLOPT_COOKIE, $cookies);
78  curl_setopt($ch, CURLOPT_USERPWD, "nobody:no1home");
79  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); // In seconds
80  curl_setopt($ch, CURLOPT_TIMEOUT, 30); // In seconds
81  curl_setopt($ch, CURLOPT_HEADER, 0);
82  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
83 
84  $rawresp = curl_exec($ch);
85 
86  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
87 
88  $returnArray = array("httpCode" => $httpCode, "rawresp" => $rawresp);
89  curl_close($ch);
90 
91  return $returnArray;
92 }
93 
94 /**
95  * function GetLimitInBytes()
96  * Get the max allowed bytes in PHP.
97  * @see https://stackoverflow.com/questions/10208698/checking-memory-limit-in-php
98  *
99  * @return the number of bytes for the memory limit.
100  */
101 function GetLimitInBytes() {
102  $val = trim(ini_get("memory_limit"));
103  $last = strtolower($val[strlen($val) - 1]);
104  switch($last) {
105  // The 'G' modifier is available since PHP 5.1.0
106  case 'g':
107  $val *= 1024;
108  case 'm':
109  $val *= 1024;
110  case 'k':
111  $val *= 1024;
112  }
113  return $val;
114 }
115 
116 /**
117  * function GatherBillingInfoChildren($dbh, $sysenv, $staffId)
118  * This is the starting part of the process. This function runs each count.
119  * Each count will be in a different call.
120  *
121  * @param $dbh -- the database connection
122  * @param $sysenv -- the environment variables (from cu_top)
123  * @param $staffId -- the HomeCU login user
124  *
125  * @return $status -- "000" if there are no errors. Nonzero otherwise.
126  * @return $error -- "" if there are no errors. Nonempty otherwise.
127  * @return $type -- "child" where we are at in the process.
128  */
129 function GatherBillingInfoChildren($dbh, $sysenv, $staffId) {
130  try {
131 
132  extract ($sysenv["BILL"]);
133 
134  if (!isset($sid) || trim($sid) == "") {
135  throw new exception ("Sid is required.", 1);
136  }
137 
138  if (!isset($childType) || trim($childType) == "") {
139  throw new exception ("Child type is required.", 2);
140  }
141 
142  $monthFormat = "Y-m-d|";
143  $timeFormat = "H:i:s|";
144 
145  if (!isset($reportDate) || trim($reportDate) == "") {
146  throw new exception ("Month is required.", 3);
147  }
148  if (!isset($cutoffDate) || trim($cutoffDate) == "") {
149  throw new exception ("Cutoff Date is required.", 4);
150  }
151  if (!isset($cutoffTime) || trim($cutoffTime) == "") {
152  throw new exception ("Cutoff time is required.", 5);
153  }
154 
155  if (!DateTime::createFromFormat($monthFormat, $reportDate)) {
156  throw new exception ("Month is invalid.", 6);
157  }
158  if (!DateTime::createFromFormat($monthFormat, $cutoffDate)) {
159  throw new exception ("Cutoff date is invalid.", 7);
160  }
161  if (!DateTime::createFromFormat($timeFormat, $cutoffTime)) {
162  throw new exception ("Cutoff time is invalid.", 8);
163  }
164 
165  $startFile = GetStartFile();
166  $contractFile = GetContractFile();
167  $estatementFile = GetEstatementCountFile();
168  $ivrFile = GetIVRCountFile();
169  $ipayFile = GetIPayCountFile();
170  $timetrackFile = GetTimeTrackCountFile();
171 
172  $dateTime = new DateTime($reportDate);
173  $monthBit = 1 << intval($dateTime->format("n")) - 1;
174  $sysenv["BILL"]["monthBit"] = $monthBit;
175 
176  $cutoffDate = str_replace("-", "", $cutoffDate);
177  $date = DateTime::createFromFormat("Y-m-d", $reportDate);
178  $date->modify("-1 month");
179  $regularReportDate = $date->format("Y-m") . "-01";
180  $ivrStartDate = $date->format("m/01/Y");
181  $ivrEndDate = $date->format("m/t/Y");
182  $iPayReportDate = $date->format("Y_m");
183 
184  switch($childType) {
185  case "contracts":
186 
187  if ($sysenv["devmode"] == 1) {
188 
189  if (isset($callMy) && $callMy == "Y") {
190  $queryResults = GetMonitorBillingList($dbh, $sysenv);
191  } else {
192  $queryResults = GetPotentiallyFromDifferentServer($dbh, $sysenv);
193  }
194  } else {
195  $queryResults = GetPotentiallyFromDifferentServer($dbh, $sysenv);
196  }
197 
198  $fileArray = array("sid" => $sid, "reportDate" => $reportDate, "monthBit" => $monthBit,
199  "contracts" => $queryResults, "ready" => true);
200  file_put_contents($contractFile, HCU_JsonEncode($fileArray));
201  break;
202 
203  case "estatements":
204 
205  $countResults = GetEstatementCounts($cutoffDate, $cutoffTime, $sysenv["devmode"]);
206 
207  $countResults["sid"] = $sid;
208  $countResults["cutoffDate"] = $cutoffDate;
209  $countResults["cutoffTime"] = $cutoffTime;
210  $countResults["ready"] = true;
211 
212  file_put_contents($estatementFile, HCU_JsonEncode($countResults));
213  break;
214 
215  case "ivr":
216 
217  $countResults = GetIVRCounts($ivrStartDate, $ivrEndDate);
218 
219  $countResults["sid"] = $sid;
220  $countResults["ivrStartDate"] = $ivrStartDate;
221  $countResults["ivrEndDate"] = $ivrEndDate;
222  $countResults["ready"] = true;
223 
224  file_put_contents($ivrFile, HCU_JsonEncode($countResults));
225  break;
226 
227  case "ipay":
228 
229  $countResults = GetIPayCounts ($iPayReportDate, $sysenv["devmode"]);
230 
231  $countResults["sid"] = $sid;
232  $countResults["iPayReportDate"] = $iPayReportDate;
233  $countResults["ready"] = true;
234 
235  file_put_contents($ipayFile, HCU_JsonEncode($countResults));
236  break;
237 
238  case "timetrack":
239 
240  $countResults = GetTimetrackCounts($regularReportDate, ""); // Counts are always gathered
241 
242  $countResults["sid"] = $sid;
243  $countResults["regularReportDate"] = $regularReportDate;
244  $countResults["ready"] = true;
245 
246  file_put_contents($timetrackFile, HCU_JsonEncode($countResults));
247  break;
248 
249  default:
250  throw new exception ("Child process not found.", 9);
251  break;
252 
253  }
254 
255  $returnArray = array("status" => "000", "error" => "", "type" => "child");
256  } catch (exception $e) {
257  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
258  }
259  return $returnArray;
260 }
261 
262 /**
263  * function GatherBillingInfo($dbh, $sysenv, $staffId, $mode)
264  * This is the starting part of the process. It creates the start file.
265  * On return of this data call, the children function is called per count type.
266  *
267  * @param $dbh -- the database connection
268  * @param $sysenv -- the environment variables (from cu_top)
269  * @param $staffId -- the HomeCU login user
270  * @param $mode --
271  * "odyssey" if using localhost and needed to get contract info from my.homecu.net.
272  * "regular" to get contract info from same database.
273  *
274  * @return $status -- "000" if there are no errors. Nonzero otherwise.
275  * @return $error -- "" if there are no errors. Nonempty otherwise.
276  * @return $type -- "gather" where we are at in the process.
277  * @return $sid -- the number "U" in UTC when the process was started.
278  * @return $callMy -- "Y" if using localhost and needed to get contract info from my.homecu.net.
279  * @return $reportDate -- the report date requested. Value is passed through to the next step.
280  * @return $cutoffDate -- the cutoff date requested. Value is passed through to the next step.
281  * @return $cutoffTime -- the cutoff time requested. Value is passed through to the next step.
282  */
283 function GatherBillingInfo($dbh, $sysenv, $staffId, $mode) {
284  try {
285 
286  extract ($sysenv["BILL"]);
287 
288  $monthFormat = "Y-m-d|";
289  $timeFormat = "H:i:s|";
290 
291  if (!isset($reportDate) || trim($reportDate) == "") {
292  throw new exception ("Month is required.", 4);
293  }
294  if (!isset($cutoffDate) || trim($cutoffDate) == "") {
295  throw new exception ("Cutoff Date is required.", 5);
296  }
297  if (!isset($cutoffTime) || trim($cutoffTime) == "") {
298  throw new exception ("Cutoff time is required.", 6);
299  }
300 
301  if (!DateTime::createFromFormat($monthFormat, $reportDate)) {
302  throw new exception ("Month is invalid.", 1);
303  }
304  if (!DateTime::createFromFormat($monthFormat, $cutoffDate)) {
305  throw new exception ("Cutoff date is invalid.", 2);
306  }
307  if (!DateTime::createFromFormat($timeFormat, $cutoffTime)) {
308  throw new exception ("Cutoff time is invalid.", 3);
309  }
310 
311  // Remove ALL temp files.
312  $startFile = GetStartFile();
313  $contractFile = GetContractFile();
314  $estatementFile = GetEstatementCountFile();
315  $ivrFile = GetIVRCountFile();
316  $ipayFile = GetIPayCountFile();
317  $timetrackFile = GetTimeTrackCountFile();
318 
319  if (file_exists($startFile)) {
320  throw new exception ("Another run billing process is running.", 9);
321  }
322 
323  $sid = new DateTimeZone("UTC");
324  $sid = new DateTime("now", $sid);
325  $sid = $sid->format("U");
326 
327  // Instead of removing files and checking for existence, use ready variable.
328  $defaultString = HCU_JsonEncode(array("sid" => $sid, "ready" => false));
329 
330  file_put_contents($contractFile, $defaultString);
331  file_put_contents($estatementFile, $defaultString);
332  file_put_contents($ivrFile, $defaultString);
333  file_put_contents($ipayFile, $defaultString);
334  file_put_contents($timetrackFile, $defaultString);
335 
336  $fileArray = array("sid" => $sid, "reportDate" => $reportDate, "cutoffDate" => $cutoffDate, "cutoffTime" => $cutoffTime);
337  file_put_contents($startFile, HCU_JsonEncode($fileArray));
338 
339  $returnArray = array("status" => "000", "error" => "", "sid" => $sid, "type" => "gather", "callMy" => $mode === "odyssey" ? "Y" : "N",
340  "reportDate" => $reportDate, "cutoffDate" => $cutoffDate, "cutoffTime" => $cutoffTime);
341  } catch (exception $e) {
342  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "type" => "gather");
343  }
344  return $returnArray;
345 }
346 
347 /**
348  * function CheckBillingInfo($dbh, $sysenv, $staffId)
349  * Checks to see that all the count files have been created.
350  *
351  * @param $dbh -- the database connection
352  * @param $sysenv -- the environment variables (from cu_top)
353  * @param $staffId -- the HomeCU login user
354  *
355  * @return $status -- "000" if there are no errors. Nonzero otherwise.
356  * @return $error -- "" if there are no errors. Nonempty otherwise.
357  * @return $type -- "check" where we are at in the process.
358  * @return $sid -- the number "U" in UTC when the process was started.
359  * @return $ready -- true if all the required files have been created. False otherwise.
360  */
361 function CheckBillingInfo($dbh, $sysenv, $staffId) {
362  try {
363 
364  $sid = $sysenv["BILL"]["sid"];
365  if (!isset($sid) || trim($sid) == "") {
366  throw new exception ("Sid is required.", 1);
367  }
368 
369  $results = VerifyFiles($sysenv);
370  $ready = $results["status"] === "000";
371 
372  $returnArray = array("status" => "000", "error" => "", "sid" => $sid, "ready" => $ready, "type" => "check");
373 
374  } catch (exception $e) {
375  $sysenv["logger"]->error("Run billing failed: " . $e->getMessage());
376  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "type" => "check");
377  }
378  return $returnArray;
379 }
380 
381 /**
382  * function RunBillingCleanup($dbh, $sysenv, $staffId)
383  * This removes the start file after completion.
384  * If it isn't devmode, it also removes the count files.
385  *
386  * @param $dbh -- the database connection
387  * @param $sysenv -- the environment variables (from cu_top)
388  * @param $staffId -- the HomeCU login user
389  *
390  * @return $status -- "000" if there are no errors. Nonzero otherwise.
391  * @return $error -- "" if there are no errors. Nonempty otherwise.
392  * @return $type -- "cleanup" where we are at in the process.
393  */
394 function RunBillingCleanup($dbh, $sysenv, $staffId) {
395  try {
396  extract ($sysenv["BILL"]);
397 
398  if (!isset($sid) || trim($sid) == "") {
399  throw new exception ("Sid is required.", 1);
400  }
401 
402  $startFile = GetStartFile();
403  $contractFile = GetContractFile();
404  $estatementFile = GetEstatementCountFile();
405  $ivrFile = GetIVRCountFile();
406  $ipayFile = GetIPayCountFile();
407  $timetrackFile = GetTimeTrackCountFile();
408 
409  $startOnly = $sysenv["devmode"] !== 1; // Want to see what is going on on devmode.
410 
411  if (file_exists($startFile)) {
412  unlink($startFile);
413  }
414 
415  if (!$startOnly) {
416  if (file_exists($contractFile)) {
417  unlink($contractFile);
418  }
419 
420  if (file_exists($estatementFile)) {
421  unlink($estatementFile);
422  }
423 
424  if (file_exists($ivrFile)) {
425  unlink($ivrFile);
426  }
427 
428  if (file_exists($ipayFile)) {
429  unlink($ipayFile);
430  }
431 
432  if (file_exists($timetrackFile)) {
433  unlink($timetrackFile);
434  }
435  }
436 
437  $returnArray = array("status" => "000", "error" => "", "type" => "cleanup");
438  } catch (exception $e) {
439  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "type" => "cleanup");
440  }
441  return $returnArray;
442 }
443 
444 /**
445  * function VerifyFiles
446  * This function ensures that:
447  * the files exist, are ready, & have counts
448  *
449  * @param $sysenv -- the environment variables (from cu_top)
450  *
451  * @return $status -- "000" if there are no errors. Nonzero otherwise.
452  * @return $error -- "" if there are no errors. Nonempty otherwise.
453  * @return $data -- all the counts from the files.
454  */
455 function VerifyFiles($sysenv) {
456  try {
457  $startFile = GetStartFile();
458  $contractFile = GetContractFile();
459  $estatementFile = GetEstatementCountFile();
460  $ivrFile = GetIVRCountFile();
461  $ipayFile = GetIPayCountFile();
462  $timetrackFile = GetTimeTrackCountFile();
463  $data = array();
464 
465  extract ($sysenv["BILL"]);
466 
467  if (!isset($sid) || trim($sid) == "") {
468  throw new exception ("Sid is required.", 1);
469  }
470 
471  if (!file_exists($startFile)) {
472  throw new exception ("Start file does not exist.", 2);
473  }
474 
475  $contents = file_get_contents($startFile);
476  if ($contents === false) {
477  throw new exception ("Contents do not exist in start file.", 3);
478  }
479  $contents = HCU_JsonDecode($contents);
480 
481  $testSid = HCU_array_key_value("sid", $contents);
482 
483  if ($testSid !== $sid) {
484  throw new exception ("Sid doesn't match in start file.", 4);
485  }
486 
487  $reportDate = HCU_array_key_value("reportDate", $contents);
488  if ($reportDate === false) {
489  throw new exception ("ReportDate is not found.", 20);
490  }
491 
492  $data["reportDate"] = $reportDate;
493 
494  if (!file_exists($contractFile)) {
495  throw new exception ("Contract file doesn't exist.", 5);
496  }
497 
498  $contents = file_get_contents($contractFile);
499  if ($contents === false) {
500  throw new exception ("Contract file doesn't exist.", 6);
501  }
502  $contents = HCU_JsonDecode($contents);
503 
504  $testSid = HCU_array_key_value("sid", $contents);
505 
506  if ($testSid !== $sid) {
507  throw new exception ("Sid doesn't match in contract file.", 7);
508  }
509 
510  $contractResults = HCU_array_key_value("contracts", $contents);
511  $contractResults = $contractResults === false ? array() : $contractResults;
512  $membershipCounts = HCU_array_key_value("membershipCounts", $contractResults);
513  $membershipCounts = $membershipCounts === false ? array() : $membershipCounts;
514  $contractResults = HCU_array_key_value("record", $contractResults);
515  $contractResults = $contractResults === false ? array() : $contractResults;
516 
517  $data["contractResults"] = $contractResults;
518  $data["membershipCounts"] = $membershipCounts;
519 
520  if (!file_exists($estatementFile)) {
521  throw new exception ("eStatement file doesn't exist.", 8);
522  }
523 
524  $contents = file_get_contents($estatementFile);
525  if ($contents === false) {
526  throw new exception ("eStatement file doesn't exist.", 9);
527  }
528  $contents = HCU_JsonDecode($contents);
529 
530  $testSid = HCU_array_key_value("sid", $contents);
531 
532  if ($testSid !== $sid) {
533  throw new exception ("Sid doesn't match in eStatement file.", 10);
534  }
535 
536  $ready = HCU_array_key_value("ready", $contents);
537  if (!$ready) {
538  throw new exception ("eStatements are not ready.", 22);
539  }
540 
541  $eStatementCounts = HCU_array_key_value("counts", $contents);
542  $eStatementCounts = $eStatementCounts === false ? array() : $eStatementCounts;
543 
544  $data["eStatementCounts"] = $eStatementCounts;
545 
546  if (!file_exists($ivrFile)) {
547  throw new exception ("IVR file doesn't exist.", 11);
548  }
549 
550  $contents = file_get_contents($ivrFile);
551  if ($contents === false) {
552  throw new exception ("IVR file doesn't exist.", 12);
553  }
554  $contents = HCU_JsonDecode($contents);
555 
556  $testSid = HCU_array_key_value("sid", $contents);
557 
558  if ($testSid !== $sid) {
559  throw new exception ("Sid doesn't match in IVR file.", 13);
560  }
561 
562  $ready = HCU_array_key_value("ready", $contents);
563  if (!$ready) {
564  throw new exception ("IVRs are not ready.", 23);
565  }
566 
567  $ivrCounts = HCU_array_key_value("counts", $contents);
568  $ivrCounts = $ivrCounts === false ? array() : $ivrCounts;
569 
570  $data["ivrCounts"] = $ivrCounts;
571 
572  if (!file_exists($ipayFile)) {
573  throw new exception ("iPay file doesn't exist.", 14);
574  }
575 
576  $contents = file_get_contents($ipayFile);
577  if ($contents === false) {
578  throw new exception ("iPay file doesn't exist.", 15);
579  }
580  $contents = HCU_JsonDecode($contents);
581 
582  $testSid = HCU_array_key_value("sid", $contents);
583 
584  if ($testSid !== $sid) {
585  throw new exception ("Sid doesn't match in iPay file.", 16);
586  }
587 
588  $ready = HCU_array_key_value("ready", $contents);
589  if (!$ready) {
590  throw new exception ("iPays are not ready.", 24);
591  }
592 
593  $iPayCounts = HCU_array_key_value("counts", $contents);
594  $iPayCounts = $iPayCounts === false ? array() : $iPayCounts;
595 
596  $data["iPayCounts"] = $iPayCounts;
597 
598  if (!file_exists($timetrackFile)) {
599  throw new exception ("Timetrack file doesn't exist.", 17);
600  }
601 
602  $contents = file_get_contents($timetrackFile);
603  if ($contents === false) {
604  throw new exception ("Timetrack file doesn't exist.", 18);
605  }
606  $contents = HCU_JsonDecode($contents);
607 
608  $testSid = HCU_array_key_value("sid", $contents);
609 
610  if ($testSid !== $sid) {
611  throw new exception ("Sid doesn't match in timetrack file.", 19);
612  }
613 
614  $ready = HCU_array_key_value("ready", $contents);
615  if (!$ready) {
616  throw new exception ("Timetracks are not ready.", 25);
617  }
618 
619  $timetrackCounts = HCU_array_key_value("counts", $contents);
620  $timetrackCounts = $timetrackCounts === false ? array() : $timetrackCounts;
621 
622  $data["timetrackCounts"] = $timetrackCounts;
623 
624 
625  $returnArray = array("status" => "000", "error" => "", "data" => $data);
626  } catch (exception $e) {
627  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
628  }
629  return $returnArray;
630 }
631 
632 /**
633  * function ExtractContractsAndCounts($sid)
634  * This extracts the contracts and counts for processing.
635  * After counts are extracted, they are put in an array by Credit Union.
636  * All files are validated by checking the $sid saved in each file. (It should be the same.)
637  *
638  * @param $sid -- the files need to match this $sid from the previous step of this process.
639  *
640  * @return $status -- "000" if there are no errors. Nonzero otherwise.
641  * @return $error -- "" if there are no errors. Nonempty otherwise.
642  * @return $contractResults -- this is a list of records to loop through to run billing.
643  * @return $counts -- a single array to check when processing different features of billing.
644  * @return $reportDate -- the requested report date (comes from the initial call.)
645  */
646 function ExtractContractsAndCounts($sysenv) {
647  try {
648 
649  $results = VerifyFiles($sysenv);
650  if ($results["status"] !== "000") {
651  throw new exception ($results["error"], $results["status"]);
652  }
653  extract($results["data"]);
654 
655  // Combine counts like previous function GetCounts.
656  $countByCU = array();
657  foreach($contractResults as $record) {
658  $cu = strtoupper(trim($record["cu"]));
659  if (!isset($countByCU[$cu])) {
660  $countByCU[$cu] = array("cuinfo" => array(), "eStatements" => array(), "ivr" => array(), "timetrack" => array(), "iPay" => array());
661  }
662  }
663 
664  foreach($membershipCounts as $record) {
665  $cu = strtoupper(trim($record["cu"]));
666  if (isset($countByCU[$cu])) {
667  $countByCU[$cu]["cuinfo"] = $record;
668  }
669  }
670 
671  foreach($eStatementCounts as $key => $value) {
672  $cu = strtoupper(trim($key));
673  if (isset($countByCU[$cu])) {
674  $countByCU[$cu]["eStatements"] = $value;
675  }
676  }
677 
678  foreach($ivrCounts as $record) {
679  $cu = strtoupper(trim($record["cu"]));
680  if (isset($countByCU[$cu])) {
681 
682  // Now, there could be multiple IVR records for the CU divided by the DNID.
683  if (!array_key_exists("ivr", $countByCU[$cu])) {
684  $countByCU[$cu]["ivr"] = array("undivided" => $record);
685  unset($countByCU[$cu]["ivr"]["DNID"]);
686  // Add up the values in the "undivided" record so that it functions the same as if the "ByDNID" parameter didn't exist in the report.
687  } else {
688  $undivided = HCU_array_key_value("undivided", $countByCU[$cu]["ivr"]);
689  $undivided = $undivided === false ? array("calls" => 0, "minutes" => 0) : $undivided;
690  $undivided["calls"] += $record["calls"];
691  $undivided["minutes"] += $record["minutes"];
692  $countByCU[$cu]["ivr"]["undivided"] = $undivided;
693  }
694 
695  $aDNID = $record["DNID"];
696  unset($record["DNID"]);
697  $countByCU[$cu]["ivr"][$aDNID] = $record;
698  }
699  }
700 
701  foreach($iPayCounts as $cu => $record) {
702  if (HCU_array_key_exists($cu, $cuIpayMinimumMap)) {
703  $cuIpayRec = $cuIpayMinimumMap[$cu];
704  $record["Monthly Minimum"] = HCU_array_key_exists("minimum", $cuIpayRec) ? intval($cuIpayRec["minimum"]) : 0;
705  $record["Per Transaction"] = HCU_array_key_exists("transfee", $cuIpayRec) ? floatval($cuIpayRec["transfee"]) : 0;
706  }
707  $countByCU[$cu]["iPay"] = $record;
708  }
709 
710  $countByCU["timetrack"] = $timetrackCounts;
711 
712  $returnArray = array("status" => "000", "error" => "", "contractResults" => $contractResults, "counts" => $countByCU, "reportDate" => $reportDate);
713  } catch (exception $e) {
714  $sysenv["logger"]->error("Run Billing Failed: " . $e->getMessage());
715  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
716  }
717  return $returnArray;
718 }
719 
720 /**
721  * function RunBillingMonth($dbh, $sysenv, $staffId)
722  * This is the actual function that creates records
723  * in cubillmonth and cubillpreinvoice tables.
724  *
725  * @param $dbh -- the database connection
726  * @param $sysenv -- the environment variables (from cu_top)
727  * @param $staffId -- the HomeCU login user
728  *
729  * @return $status -- "000" if there are no errors. Nonzero otherwise.
730  * @return $error -- "" if there are no errors. Nonempty otherwise.
731  * @return $type -- "run" where we are at in the process.
732  * @return $sid -- the number "U" in UTC when the process was started.
733  */
734 function RunBillingMonth($dbh, $sysenv, $staffId) {
735  try {
736 
737  $sid = HCU_array_key_value("sid", $sysenv["BILL"]);
738 
739  $results = ExtractContractsAndCounts($sysenv);
740  if ($results["status"] !== "000") {
741  throw new exception ("Run billing failed to extract values for contracts or counts.", 2);
742  }
743 
744  $reportDate = $results["reportDate"];
745  $counts = $results["counts"];
746  $contractResults = $results["contractResults"];
747 
748  $results = StartProcess($dbh, $reportDate);
749  if ($results["status"] !== "000") {
750  throw new exception ("Process was not started successfully.", 7);
751  }
752 
753  foreach($contractResults as $record) {
754 
755  $results = ProcessFeature($dbh, $reportDate, $record, $counts, $staffId);
756  if ($results["status"] != "000") {
757  throw new exception ($results["error"], 9);
758  }
759  }
760 
761  $results = ProcessTimetrack($dbh, $counts["timetrack"], $reportDate);
762  if ($results["status"] !== "000") {
763  throw new exception ("Processing timetrack failed.", 12);
764  }
765 
766  // Update month if there is no lasterror in it.
767  $sql = "update cubillmonth set processing_flag = 100, last_step_date = now()
768  where billing_date = '" . prep_save($reportDate) . "'
769  and (last_error_code is null or last_error_code = '') and (last_error_msg is null or last_error_msg = '')";
770  $sth = db_query($sql, $dbh);
771  if (!$sth) {
772  throw new exception ("Update query failed.", 10);
773  }
774 
775  // Update hooked issues to "Billed"
776  $updateSQL = "update cutrack set billed = 'Y' where track_id in (select t.track_id from cutrack t
777  inner join cubillpreinvoice pi on t.billing_feature_id = pi.feature_detail_id
778  and pi.billing_date = '" . prep_save($reportDate) . "' and billing_part in (0,2))";
779  // billing_part 1 means bill half before starting workflow so ignore this.
780  $sth = db_query($sql, $dbh);
781  if (!$sth) {
782  throw new exception ("Update query failed.", 11);
783  }
784 
785  $returnArray = array("status" => "000", "error" => "", "sid" => $sid, "type" => "run");
786 
787  } catch (exception $e) {
788  if ($e->getCode() != 2) {
789  $sysenv["logger"]->error("Run Billing Failed: " . $e->getMessage());
790  }
791  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "sid" => $sid, "type" => "run");
792  }
793  return $returnArray;
794 }
795 
796 /**
797  * function StartProcess($dbh, $reportDate)
798  * This starts the process of running billing.
799  * It creates the cubillmonth record
800  * and removes previous cubillpreinvoice records for the month.
801  *
802  * @param $dbh -- the database connection
803  * @param $reportDate -- the report date to save.
804  */
805 function StartProcess($dbh, $reportDate) {
806  try {
807 
808  if (!db_work ($dbh, HOMECU_WORK_BEGIN)) {
809  throw new exception("begin query failed.", 1);
810  }
811 
812  $sql = "insert into cubillmonth (billing_date, started_date, last_error_code, last_error_msg)
813  select '$reportDate', now(), '', '' where not exists (select 'FOUND' from cubillmonth where billing_date = '" . prep_save($reportDate) . "')";
814  $sth = db_query($sql, $dbh);
815  if (!$sth) {
816  throw new exception ("cubillmonth insert failed.", 2);
817  }
818 
819  // Delete the old preinvoices but only if QBWC is not running.
820  $sql = "delete from cubillpreinvoice
821  where billing_date = '" . prep_save($reportDate) . "'
822  and not exists (select 'FOUND' from cubillmonth where billing_date = '" . prep_save($reportDate) . "' and processing_flag > 100)";
823  $sth = db_query($sql, $dbh);
824  if (!$sth) {
825  throw new exception ("cubillpreinvoice deletion failed.", 3);
826  }
827 
828  $sql = "update cubillmonth set processing_flag = 10, last_step_date = now(), last_error_code = '', last_error_msg = ''
829  where billing_date = '" . prep_save($reportDate) . "'";
830  $sth = db_query($sql, $dbh);
831  if (!$sth) {
832  throw new exception ("cubillmonth update failed.", 4);
833  }
834 
835  $sql = "update cubillfeaturedetail set processing_flag = 0 where end_date is null or end_date > '" . prep_save($reportDate) . "'";
836  $sth = db_query($sql, $dbh);
837  if (!$sth) {
838  throw new exception ("cubillfeaturedetail update failed.", 5);
839  }
840 
841  if (!db_work ($dbh, HOMECU_WORK_COMMIT)) {
842  throw new exception("commit query failed.", 7);
843  }
844 
845  $returnArray = array("status" => "000", "error" => "");
846  } catch (exception $e) {
847 
848  db_work($dbh, HOMECU_WORK_ROLLBACK);
849  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
850  }
851  return $returnArray;
852 }
853 
854 /**
855  * function ProcessFeature($dbh, $reportDate, $record, $counts, $staffId)
856  * This process a billing item based on the calcId of the record.
857  *
858  * @param $dbh -- the database connection
859  * @param $reportDate -- the report date to save.
860  * @param $record -- the record from the $contractResults array.
861  * @param $counts -- the count array.
862  * @param $staffId -- the HomeCU login user
863  *
864  * @return $status -- "000" if there are no errors. Nonzero otherwise.
865  * @return $error -- "" if there are no errors. Nonempty otherwise.
866  * @return $warnings -- there are some features that give warnings. This weren't shown on Mammoth.
867  */
868 function ProcessFeature($dbh, $reportDate, $record, $counts, $staffId) {
869  try {
870  $cu = strtoupper(trim($record["cu"]));
871 
872  $membershipRec = HCU_array_key_value("cuinfo", $counts[$cu]);
873  $membershipCount = $membershipRec === false || !HCU_array_key_exists("membership", $membershipRec) ? 0 : intval($membershipRec["membership"]);
874  $assetCount = $membershipRec === false || !HCU_array_key_exists("assets", $membershipRec) ? 0 : floatval($membershipRec["assets"]);
875  $eStatementCount = HCU_array_key_exists("eStatements", $counts[$cu]) ? intval($counts[$cu]["eStatements"]) : 0;
876  $iPayCount = HCU_array_key_exists("iPay", $counts[$cu]) ? $counts[$cu]["iPay"] : array();
877  $ivrCount = HCU_array_key_exists("ivr", $counts[$cu]) ? $counts[$cu]["ivr"] : array();
878 
879  // Expand billing template
880  $template = HCU_JsonDecode($record["billingTemplate"]);
881 
882  unset($record["billingTemplate"]);
883 
884  $record["description"] = $template["descr"];
885  $record["qty1"] = intval($template["qty1"]);
886  $record["qty2"] = intval($template["qty2"]);
887  $record["fixed"] = floatval($template["fixed"]);
888  $record["variable"] = floatval($template["variable"]);
889  $record["calcId"] = intval($record["calcId"]);
890  $record["aDNID"] = HCU_array_key_exists("aDNID", $template) ? trim($template["aDNID"]) : "";
891 
892  $frequency = $record["frequency"];
893 
894  $alreadyAdded = false;
895 
896  $calcId = $record["calcId"];
897  $salesItemId = $record["salesItemId"];
898 
899  $warnings = array();
900 
901  // Do not continue if QBWC is running on the same records.
902  $results = ProcessFeatureStart($dbh, $reportDate, $record);
903  if ($results["status"] !== "000") {
904  if ($results["status"] < 0) {
905  $warnings[] = $results["error"];
906  throw new exception ($results["error"], -1);
907  } else {
908  throw new exception ("Process feature start failed.", 2);
909  }
910  }
911 
912  switch($calcId) {
913  case 1: // HB monthly
914  case 51: // Mobile web monthly
915  case 61: // Android monthly
916  case 71: // iPhone monthly
917  $calcs = CalcMembership($membershipCount, $record);
918  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
919  if ($results["status"] !== "000") {
920  throw new exception ($results["error"], 4);
921  }
922  break;
923  case 5151:
924  $results = ProcessFeatureEnd($dbh, $membershipCount, $record["variable"] * $membershipCount, $reportDate, false, $record, $staffId, $frequency);
925  if ($results["status"] !== "000") {
926  throw new exception ($results["error"], 5);
927  }
928  break;
929  case 3: // HB sunset
930  case 4: // HB sunset
931  $calcs = CalcSunset($membershipCount, $record);
932  if ($calcs["processIt"]) {
933  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
934  if ($results["status"] !== "000") {
935  throw new exception ($results["error"], 6);
936  }
937  }
938  break;
939  case 52: // Mobile web setup
940  case 62: // Android setup
941  case 72: // iPhone setup
942  case 2: // HB setup
943  case 32: // IVR setup
944  case 41: // Check image monthly
945  case 42: // Check image setup
946  case 82: // RDC setup
947  case 92: // Secure form setup
948  case 102: // Website dev setup
949  case 112: // TXT banking setup
950  case 122: // SSL Setup
951  case 31: // IVR monthly
952  case 81: // RDC monthly
953  case 91: // Secure form monthly
954  case 101: // Website monthly
955  case 111: // TXT banking monthly
956  case 24: // Free eStatements
957  $results = ProcessFeatureEnd($dbh, 1, $record["fixed"], $reportDate, false, $record, $staffId, $frequency);
958  if ($results["status"] !== "000") {
959  throw new exception ($results["error"], 7);
960  }
961  break;
962  case 21: // Estmnt tier 1
963  case 22: // Estmnt tier 2
964  case 23: // Estmnt tier 3
965  $calcs = CalcEstatements($eStatementCount, $record);
966  if ($calcs["warning"] != "") {
967  $warnings[] = $calcs["warning"];
968  }
969  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, $calcs["dontAddRecord"], $record, $staffId, $frequency);
970  if ($results["status"] !== "000") {
971  throw new exception ($results["error"], 8);
972  }
973  break;
974  case 33: // IVR minutes
975  case 34: // IVR calls
976  case 35:
977  case 36:
978  $calcs = CalcIVR($ivrCount, $record);
979  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
980  if ($results["status"] !== "000") {
981  throw new exception ($results["error"], 9);
982  }
983 
984  if ($record["calcId"] != 36) {
985  $amount2 = $record["fixed"];
986  $record["description"] .= $record["actualDescription"] . " Monthly Rate";
987 
988  $results = ProcessFeatureEnd($dbh, 1, $amount2, $reportDate, false, $record, $staffId, $frequency);
989  if ($results["status"] !== "000") {
990  throw new exception ($results["error"], 10);
991  }
992  }
993  break;
994  case 1001: // email
995  case 1002: // premium email
996  case 1003: // email credit
997  throw new exception ("Email is unused now.", -13);
998  break;
999  case 121: // SSL Monthly
1000  $calcs = CalcSSL($assetCount, $record, $cu);
1001  if ($calcs["warning"] != "") {
1002  $warnings[] = $calcs["warning"];
1003  }
1004  if ($calcs["processIt"]) {
1005  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
1006  if ($results["status"] !== "000") {
1007  throw new exception ($results["error"], 11);
1008  }
1009  }
1010 
1011  break;
1012  case 200:
1013  $calcs = CalcConditionalMemb($membershipCount, $record);
1014  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
1015  if ($results["status"] !== "000") {
1016  throw new exception ($results["error"], 12);
1017  }
1018  break;
1019  case 2001:
1020  case 2002:
1021  case 2003:
1022  case 2004:
1023  case 2005:
1024  case 2006:
1025  // Prevent E_NOTICEs when CU is set up for Ipay but there are no counts.
1026  if (count($iPayCount) > 0) {
1027  $calcs = CalcIpay($iPayCount, $record);
1028  if ($calcs["processIt"]) {
1029  $results = ProcessFeatureEnd($dbh, $calcs["count"], $calcs["amount"], $reportDate, false, $record, $staffId, $frequency);
1030  if ($results["status"] !== "000") {
1031  throw new exception ($results["error"], 6);
1032  }
1033  }
1034  }
1035 
1036 
1037  break;
1038  default:
1039  // this is an error
1040  throw new exception ("CalcId doesn't exist! (CALC ID: $calcId, SALES ITEM ID: $salesItemId)", 3);
1041  break;
1042  }
1043 
1044  $returnArray = array("status" => "000", "error" => "", "warnings" => $warnings);
1045  } catch (exception $e) {
1046  if ($e->getCode() < 0) {
1047  $returnArray = array("status" => "000", "error" => "", "warnings" => $warnings); // Stopped because of the warning but show it as a warning, not an error.
1048  } else {
1049  $sql = "update cubillmonth set last_step_date = now(), last_error_code = 'Error', last_error_msg = '" . prep_save($e->getMessage(), 250)
1050  . "' where billing_date = '" . prep_save($reportDate) . "'";
1051  $sth = db_query($sql, $dbh);
1052  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "warnings" => $warnings);
1053  }
1054  }
1055 
1056  return $returnArray;
1057 }
1058 
1059 /**
1060  * function ProcessFeatureStart($dbh, $reportDate, $record)
1061  * This starts processing a feature. It checks if QBWC is currently running on the record.
1062  * Then it updates the processing_flag of the feature detail record that is currently being billed.
1063  *
1064  * @param $dbh -- the database connection
1065  * @param $reportDate -- the report date to save.
1066  * @param $record -- the record from the $contractResults array.
1067  *
1068  * @return $status -- "000" if there are no errors. Nonzero otherwise.
1069  * @return $error -- "" if there are no errors. Nonempty otherwise.
1070  */
1071 function ProcessFeatureStart($dbh, $reportDate, $record) {
1072  try {
1073  $featureDetailId = $record["featureDetailId"];
1074  $cu = strtoupper(trim($record["cu"]));
1075  $warnings = array();
1076 
1077  $sql = "select pi.id from cubillpreinvoice pi
1078  where pi.processing_flag >= 10 and pi.processing_flag < 20 and pi.feature_detail_id = " . intval($featureDetailId) . "
1079  and upper(pi.cu_id) = '" . prep_save($cu, 10) . "' and pi.billing_date = '" . prep_save($reportDate) . "'";
1080  $sth = db_query($sql, $dbh);
1081  if (!$sth) {
1082  throw new exception ("Select query failed.", 1);
1083  }
1084 
1085  if (db_num_rows($sth) > 0) {
1086  throw new exception ("QWBC is running for CU: $cu and feature detail: $featureDetailId", -2);
1087  }
1088 
1089  if (!db_work ($dbh, HOMECU_WORK_BEGIN)) {
1090  throw new exception("begin query failed.", 3);
1091  }
1092 
1093  $sql = "delete from cubillpreinvoice where feature_detail_id = " . intval($featureDetailId) . " and upper(cu_id) = '" . prep_save($cu, 10) . "'
1094  and billing_date = '" . prep_save($reportDate) . "'";
1095  $sth = db_query($sql, $dbh);
1096  if (!$sth) {
1097  throw new exception ("Delete query failed.", 104);
1098  }
1099 
1100  $sql = "update cubillfeaturedetail set processing_flag = 1 where id = " . intval($featureDetailId);
1101  $sth = db_query($sql, $dbh);
1102  if (!$sth) {
1103  throw new exception ("Update query failed.", 105);
1104  }
1105 
1106  if (!db_work ($dbh, HOMECU_WORK_COMMIT)) {
1107  throw new exception("commit query failed.", 106);
1108  }
1109 
1110  $returnArray = array("status" => "000", "error" => "");
1111  } catch (exception $e) {
1112  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
1113  }
1114  return $returnArray;
1115 }
1116 
1117 /**
1118  * function ProcessFeatureEnd($dbh, $count, $amount, $reportDate, $alreadyAdded, $record, $staffId, $frequency)
1119  * This creates the preinvoice record knowing the count and amount values.
1120  *
1121  * @param $dbh -- the database connection
1122  * @param $count -- the calculated count (depending on the feature)
1123  * @param $amount -- the price per count
1124  * @param $reportDate -- the report date to save
1125  * @param $alreadyAdded -- if true, don't add the preinvoice. (This function also updates to processing_flag of the billing item to 20 (processed).)
1126  * @param $record -- the record from the $contractResults array.
1127  * @param $staffId -- the HomeCU login user
1128  * @param $frequency -- some billing features are billed once or twice a year. In this case, use the counts for this month and times by 12 or 6.
1129  *
1130  * @return $status -- "000" if there are no errors. Nonzero otherwise.
1131  * @return $error -- "" if there are no errors. Nonempty otherwise.
1132  */
1133 function ProcessFeatureEnd($dbh, $count, $amount, $reportDate, $alreadyAdded, $record, $staffId, $frequency) {
1134  try {
1135  $featureDetailId = $record["featureDetailId"];
1136  $addendum = DateTime::createFromFormat("Y-m-d", $reportDate);
1137  $addendum = $addendum->format("M Y");
1138  $cu = trim(strtoupper($record["cu"]));
1139  $description = trim($record["description"]);
1140  $billingItem = $record["billingId"];
1141  $salesItemId = $record["salesItemId"];
1142  $billHalf = $record["billHalf"] == "Y";
1143  $billingStatus = intval($record["billingStatus"]);
1144  $partiallyBilledDate = $record["partiallyBilledDate"];
1145 
1146  if (!$alreadyAdded) {
1147 
1148 
1149  if ($billHalf) { // In the case of 2 where the workflow is completed before the end of the month, it is like billing full.
1150  switch($billingStatus) {
1151  case 0: // First part billed
1152  case 4: // Even if no workflow exists, still bill for half.
1153  // TODO: error or no error that is the question.
1154  if($partiallyBilledDate == "" || $partiallyBilledDate != $reportDate) {
1155  throw new exception ("Do not create record.", -1);
1156  }
1157 
1158  $description = $record["actualDescription"] . " (50% deposit)";
1159 
1160  $amount = $amount / 2.0;
1161  $insertSQL = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date, description,
1162  quantity, amount, billing_part)
1163  values (" . intval($featureDetailId) . ", " . intval($salesItemId) . ", '" . prep_save($billingItem, 20)
1164  . "', '" . prep_save($cu, 10) . "', '" . prep_save($reportDate) . "', '" . prep_save($description, 100)
1165  . "', " . floatval($count) . ", '" . floatval($amount) . "', 1)";
1166  $sth = db_query($insertSQL, $dbh);
1167  if (!$sth) {
1168  throw new exception ("Insert query failed.", 2);
1169  }
1170 
1171  $updateFeatureToPendingSQL = "update cubillfeaturedetail set billing_status = " . ($billingStatus == 0 ? "1" : "5")
1172  . " where id = " . intval($featureDetailId);
1173  $sth = db_query($updateFeatureToPendingSQL, $dbh);
1174  if (!$sth) {
1175  throw new exception ("Update query failed.", 3);
1176  }
1177 
1178  break;
1179  case 3: // workflow completed before billed partially
1180  $insertSQL = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date,
1181  description, quantity, amount)
1182  values (" . intval($featureDetailId) . ", " . intval($salesItemId) . ", '" . prep_save($billingItem, 20)
1183  . "', '" . prep_save($cu, 10) . "', '" . prep_save($reportDate) . "', '" . prep_save($description, 100)
1184  . "', " . floatval($count) . ", '" . floatval($amount) . "')";
1185  $sth = db_query($insertSQL, $dbh);
1186  if (!$sth) {
1187  throw new exception ("Insert query failed.", 4);
1188  }
1189  break;
1190  case 2: // remainder
1191  $findSQL = "select amount from cubillpreinvoice where feature_detail_id = " . intval($featureDetailId) . " and billing_part = 1";
1192  $sth = db_query($findSQL, $dbh);
1193  if (!$sth) {
1194  throw new exception ("Find query failed.", 5);
1195  }
1196  $findAmount = db_fetch_row($sth, 0)[0];
1197  $amount = $amount - floatval($findAmount);
1198 
1199  $description = $record["actualDescription"] . " (remainder)";
1200  $insertSQL = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date,
1201  description, quantity, amount, billing_part)
1202  values (" . intval($featureDetailId) . ", " . intval($salesItemId) . ", '" . prep_save($billingItem, 20)
1203  . "', '" . prep_save($cu, 10) . "', '" . prep_save($reportDate) . "', '" . prep_save($description, 100)
1204  . "', " . floatval($count) . ", '" . floatval($amount) . "', 2)";
1205  $sth = db_query($insertSQL, $dbh);
1206  if (!$sth) {
1207  throw new exception ("Insert query failed.", 6);
1208  }
1209  break;
1210  }
1211  } else {
1212  if ($billingStatus != 4 && $billingStatus != 0) {
1213  switch($frequency) {
1214  case 1:
1215  $count *= 6;
1216  $amount *= 6;
1217  break;
1218  case 2:
1219  $count *= 12;
1220  $amount *= 12;
1221  break;
1222  }
1223 
1224  if ($record["productName"] == "ESP" && $description == "") {
1225  $description = $record["actualDescription"] . " ($addendum)";
1226  }
1227  $insertSQL = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date, description, quantity, amount)
1228  values (" . intval($featureDetailId) . ", " . intval($salesItemId) . ", '" . prep_save($billingItem, 20)
1229  . "', '" . prep_save($cu, 10) . "', '" . prep_save($reportDate) . "', '" . prep_save($description, 100)
1230  . "', " . floatval($count) . ", '" . floatval($amount) . "')";
1231  $sth = db_query($insertSQL, $dbh);
1232  if (!$sth) {
1233  throw new exception ("Insert query failed.", 7);
1234  }
1235  }
1236 
1237  if ($partiallyBilledDate != "") {
1238  $dateTime = new DateTime($partiallyBilledDate);
1239  $dateFormat = $dateTime->format("m/d/Y");
1240  $description = "Credit paid $dateFormat";
1241  $amount = 0 - floatval($record["partiallyBilledAmount"]);
1242 
1243  $insertSQL = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date, description, quantity, amount)
1244  values (" . intval($featureDetailId) . ", " . intval($salesItemId) . ", '" . prep_save($billingItem, 20)
1245  . "', '" . prep_save($cu, 10) . "', '" . prep_save($reportDate) . "', '" . prep_save($description, 100)
1246  . "', 1, '" . floatval($amount) . "')";
1247  $sth = db_query($insertSQL, $dbh);
1248  if (!$sth) {
1249  throw new exception ("Insert query failed.", 8);
1250  }
1251 
1252  }
1253  }
1254  }
1255 
1256  $setupRecurring = getSetupRecurring();
1257  $additionalUpdate = ($billingStatus == 4 ? ", billing_status = 5" : "");
1258  $processSQL = "update cubillfeaturedetail set processing_flag = 20 $additionalUpdate where id = " . intval($featureDetailId);
1259  $sth = db_query($processSQL, $dbh);
1260  if (!$sth) {
1261  throw new exception ("Update query failed.", 9);
1262  }
1263 
1264  $returnArray = array("status" => "000", "error" => "");
1265 
1266  } catch (exception $e) {
1267  $returnArray = array("status" => $e->getCode(), "error" => $e->getCode());
1268  }
1269  return $returnArray;
1270 }
1271 
1272 /**
1273  * function CalcMembership($membershipCount, $record)
1274  * Calculate using the membership calculation.
1275  *
1276  * @param $membershipCount -- number of members for the CU.
1277  * @param $record -- the record from the $contractResults array.
1278  *
1279  * @return $amount -- the amount to charge.
1280  * @return $count -- the number to charge.
1281  */
1282 function CalcMembership($membershipCount, $record) {
1283  $calcCount = $membershipCount;
1284  $count = 1;
1285 
1286  // the count is the number of members; formula is fixed up to qty1, plus variable for groups of qty2 over qty1.
1287  $amount = $record["fixed"];
1288  if ( $record["qty1"] > 0 ) {
1289  $variableAmount = (int) ceil( ( $calcCount - $record["qty2"] ) / $record["qty1"] );
1290  } else {
1291  $variableAmount = 0;
1292  }
1293 
1294  if ( $variableAmount > 0 ) {
1295  $amount += $variableAmount * $record["variable"];
1296  }
1297 
1298  return array("amount" => $amount, "count" => $count);
1299 }
1300 
1301 /**
1302  * function CalcSunset($membershipCount, $record)
1303  * Calculate using the sunset calculation.
1304  *
1305  * @param $membershipCount -- number of members for the CU.
1306  * @param $record -- the record from the $contractResults array.
1307  *
1308  * @return $amount -- the amount to charge.
1309  * @return $count -- the number to charge.
1310  */
1311 function CalcSunset($membershipCount, $record) {
1312  try {
1313  if ($record["calcId"] == 3) { // (lower)
1314  $calcCount = $membershipCount;
1315  $count = $calcCount;
1316  if ( $calcCount > 2500 ) {
1317  throw new exception ("Don't process it", 1);
1318  }
1319 
1320  // HB sunset, membership < cutoff
1321  // straight variable calculation up to an amount
1322  if ( $count < $record["qty1"] ) {
1323  $amount = $record["variable"] * $count;
1324  } else {
1325  $amount = 0;
1326  }
1327  } else { // 4 (upper)
1328  $calcCount = $membershipCount;
1329  $count = $calcCount;
1330  if ( $calcCount <= 2500 ) {
1331  throw new exception ("Don't process it", 2);
1332  }
1333 
1334  // HB sunset, membership >= cutoff
1335  // this is like regular home banking; qty2 is both the cutoff and the grouping
1336  $amount = $record["fixed"];
1337  if ( $count >= $record["qty1"] && $record["qty1"] != 0) {
1338  $variableAmount = (int) ceil( ( $count - $record["qty2"] ) / $record["qty1"] );
1339  } else {
1340  $amount = 0;
1341  }
1342 
1343  if ( $variableAmount > 0 ) {
1344  $amount += $variableAmount * $record["variable"];
1345  }
1346  }
1347 
1348  $returnArray = array("processIt" => true, "amount" => $amount, "count" => $count);
1349  } catch (exception $e) {
1350  $returnArray = array("processIt" => false);
1351  }
1352 
1353  return $returnArray;
1354 
1355 }
1356 
1357 /**
1358  * function CalcEstatements($eStatementCount, $record)
1359  * Calculate using the tiered estatement calculation
1360  *
1361  * @param $eStatementCount -- number of eStatements for the CU.
1362  * @param $record -- the record from the $contractResults array.
1363  *
1364  * @return $amount -- the amount to charge.
1365  * @return $count -- the number to charge.
1366  */
1367 function CalcEstatements($eStatementCount, $record) {
1368  $count = intval($eStatementCount);
1369  $bottom = intval($record["qty1"]);
1370  $top = intval($record["qty2"]);
1371  $cu = $record["cu"];
1372  $calcID = $record["calcId"];
1373  $rate = floatval($record["variable"]);
1374 
1375  $warning = "";
1376 
1377  $dontAddRecord = false;
1378  $range = $top - $bottom;
1379  if ($top != -1 && $count >= $range + $bottom) {
1380  $count = $range;
1381  if ($bottom != 0) {
1382  $count++;
1383  }
1384  } else if ($count < $bottom) {
1385  $warning = "No record created at this level of eStatements because there isn't enough count. (CALC ID: $calcID, cu: $cu)";
1386  $dontAddRecord = true;
1387  $count = 0;
1388  } else {
1389  $count = $count - $bottom;
1390  if ($bottom != 0) {
1391  $count++;
1392  }
1393  }
1394  $amount = $count * $rate;
1395 
1396  return array("amount" => $amount, "count" => $count, "dontAddRecord" => $dontAddRecord, "warning" => $warning);
1397 }
1398 
1399 /**
1400  * function CalcIVR($ivrCounts, $record)
1401  * Calculate using the IVR calculation.
1402  *
1403  * @param $ivrCounts -- holds both the number of calls and the total call duration for the CU.
1404  * @param $record -- the record from the $contractResults array.
1405  *
1406  * @return $amount -- the amount to charge.
1407  * @return $count -- the number to charge.
1408  */
1409 function CalcIVR($ivrCounts, $record) {
1410  // Check to see if there is a DNID in the record. If there is, then get that count. Otherwise, use the undivided record.
1411 
1412  $ivrCounts = $record["aDNID"] != "" ? (HCU_array_key_exists($record["aDNID"], $ivrCounts) ? $ivrCounts[$record["aDNID"]] : array()) :
1413  (HCU_array_key_exists("undivided", $ivrCounts) ? $ivrCounts["undivided"] : array());
1414  $ivrCountMinutes = HCU_array_key_exists("minutes", $ivrCounts) ? $ivrCounts["minutes"] : 0;
1415  $ivrCountCalls = HCU_array_key_exists("calls", $ivrCounts) ? $ivrCounts["calls"] : 0;
1416 
1417  switch($record["calcId"]) {
1418  case 35:
1419  $count = $ivrCountMinutes > $record["qty1"] ? $record["qty1"] : $ivrCountMinutes;
1420  $amount1 = $record["variable"] * $count;
1421  break;
1422  case 36:
1423  $count = $ivrCountMinutes > $record["qty1"] ? $ivrCountMinutes - $record["qty1"] : 0;
1424  $amount1 = $record["variable"] * $count;
1425  break;
1426  case 33:
1427  $count = $ivrCountMinutes;
1428  $amount1 = $record["variable"] * $count;
1429  break;
1430  case 34:
1431  $count = $ivrCountCalls;
1432  $amount1 = $record["variable"] * $count;
1433  break;
1434  }
1435 
1436  return array("count" => $count, "amount" => $amount1);
1437 }
1438 
1439 /**
1440  * function ProcessTimetrack($dbh, $timetrackCount, $reportDate)
1441  * Calculate using the Timetrack calculation.
1442  * This is different because timetrack records are created from the counts.
1443  * There are no contract details that specify timetrack.
1444  * The sales item for timetrack tells the process the regular and rush job rates.
1445  *
1446  * @param $dbh -- the database connection.
1447  * @param $timetrackCount -- the counts from timetrack.
1448  * @param $reportDate -- the report date to record.
1449  *
1450  * @return $status -- "000" if there are no errors. Nonzero otherwise.
1451  * @return $error -- "" if there are no errors. Nonempty otherwise.
1452  */
1453 function ProcessTimetrack($dbh, $timetrackCount, $reportDate) {
1454  try {
1455 
1456  $salesItemId = 65;
1457  $sql = "select billing_template from cubillsalesitem where id = " . intval($salesItemId);
1458  $sth = db_query($sql, $dbh);
1459  if (!$sth) {
1460  throw new exception ("Select query failed.", 1);
1461  }
1462 
1463  $timetrackTemplate = db_num_rows($sth) > 0 ? db_fetch_row($sth, 0)[0] : "";
1464  if ($timetrackTemplate == "") {
1465  $regularRate = 85.0;
1466  $rushRate = 127.5;
1467  } else {
1468  $timetrackTemplate = HCU_JsonDecode($timetrackTemplate);
1469  $regularRate = floatval($timetrackTemplate["fixed"]);
1470  $rushRate = floatval($timetrackTemplate["variable"]);
1471  }
1472 
1473  foreach($timetrackCount as $cu => $timetrackRecords) {
1474  $cu = trim(strtoupper($cu));
1475  $sql = "insert into cubillpreinvoice (feature_detail_id, sales_item_id, billing_system_id, cu_id, billing_date, description, quantity, amount) values ";
1476  $sqlParts = array();
1477 
1478  $sqlValid = false;
1479 
1480  $timetrackBillingItems = getTimetrackBillingItems();
1481  foreach($timetrackRecords as $counts) {
1482 
1483  $staffName = $counts["staff"];
1484 
1485  if ($staffName == "test") {
1486  continue; // Do not bill for the test user.
1487  }
1488 
1489  $billAmount = floatval($counts["billable"]) * floatval($regularRate);
1490  $rushAmount = floatval($counts["rushable"]) * floatval($rushRate);
1491  $billCount = floatval($counts["billable"]);
1492  $rushCount = floatval($counts["rushable"]);
1493 
1494  // Get the actual description for the rush description (If there isn't any entry in the configuration,
1495  // then it is HTML/Support but different people have different codes.)
1496  $staffKey = strtolower($staffName);
1497  $billingItemDescription = HCU_array_key_value($staffKey, $timetrackBillingItems);
1498  $actualDescription = $billingItemDescription !== false ? trim($billingItemDescription) : "";
1499  $rushDescription = "$actualDescription (Rush)";
1500 
1501  if($billAmount > 0) {
1502  $sqlParts[] = "(0, " . intval($salesItemId) . ", '" . prep_save($staffName, 20) . "', '" . prep_save($cu, 10)
1503  . "', '" . prep_save($reportDate) . "', '', " . floatval($billCount) . ", " . floatval($billAmount) . ")";
1504  $sqlValid = true;
1505  }
1506 
1507  if ($rushAmount > 0) {
1508  $sqlParts[] = "(0, " . intval($salesItemId) . ", '" . prep_save($staffName, 20) . "', '" . prep_save($cu, 10)
1509  . "', '" . prep_save($reportDate) . "', '" . prep_save($rushDescription, 100) . "', " . floatval($rushCount) . ", " . floatval($rushAmount) . ")";
1510  $sqlValid = true;
1511  }
1512  }
1513 
1514  if ($sqlValid) {
1515  $sql .= implode(",", $sqlParts);
1516  $sth = db_query($sql, $dbh);
1517  if (!$sth) {
1518  throw new exception ("Insert query failed.", 2);
1519  }
1520  }
1521  }
1522 
1523  $returnArray = array("status" => "000", "error" => "");
1524 
1525  } catch (exception $e) {
1526  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
1527  }
1528  return $returnArray;
1529 }
1530 
1531 /**
1532  * function CalcSSL($assetCount, $record, $cu)
1533  * Calculate using the SSL calculation.
1534  *
1535  * @param $assetCount -- how much assets the CU has.
1536  * @param $record -- the record from the $contractResults array.
1537  * @param $cu -- the Credit Union. Used for the warning.
1538  *
1539  * @param $processIt -- if true, then create the cubillpreinvoice record.
1540  * @return $amount -- the amount to charge.
1541  * @return $count -- the number to charge.
1542  * @return $warning -- "" if there is no warning, nonempty otherwise.
1543  */
1544 function CalcSSL($assetCount, $record, $cu) {
1545  $rangeFrom = floatval($record["qty1"]);
1546  $rangeTo = floatval($record["qty2"]);
1547  $cu = $record["cu"];
1548  $warning = "";
1549  $count = 1;
1550  $amount = $record["fixed"];
1551  $processIt = false;
1552 
1553  // Make sure that it is in range. -1 is positive infinity for the second number
1554  if ($assetCount >= $rangeFrom && ($rangeTo == -1 || $assetCount < $rangeTo)) {
1555  $processIt = true;
1556  } else {
1557  if ($rangeTo != -1) {
1558  $warning = "SSL record not in range: $rangeFrom to $rangeTo for CU: $cu";
1559  } else {
1560  $warning = "SSL record not created because it is less than $rangeFrom for CU: $cu";
1561  }
1562  }
1563 
1564  return array("processIt" => $processIt, "count" => $count, "amount" => $amount, "warning" => $warning);
1565 }
1566 
1567 /**
1568  * function CalcIpay($iPayCount, $record)
1569  * Calculate using the iPay calculation.
1570  *
1571  * @param $iPayCount -- all the iPay counts.
1572  * @param $record -- the record from the $contractResults array.
1573  *
1574  * @param $processIt -- if true, then create the cubillpreinvoice record.
1575  * @return $amount -- the amount to charge.
1576  * @return $count -- the number to charge.
1577  */
1578 function CalcIpay($iPayCount, $record) {
1579  $variableAmount = floatval($record["variable"]);
1580  $fixedAmount = floatval($record["fixed"]);
1581  $calcId = intval($record["calcId"]);
1582 
1583  switch($calcId) {
1584  case 2001: // This is the per transaction. Only process this if it is greater than the minimum.
1585  $count = intval($iPayCount["Total Transactions Plus"]);
1586  $amount = $variableAmount == $iPayCount["Per Transaction"] ? $variableAmount * $count : 0;
1587  $amount = $amount < $iPayCount["Monthly Minimum"] ? 0 : $amount;
1588  break;
1589  case 2002:
1590  $count = intval($iPayCount["Stop Payment Count"]);
1591  $amount = $variableAmount * $count;
1592  break;
1593  case 2003:
1594  $count = 0; // Letter Mailed Express Mail doesn't show up in iPay file. If/when it does, add it back in for the count.
1595  $amount = 0;
1596  break;
1597  case 2004:
1598  $count = intval($iPayCount["NSF Count"]);
1599  $amount = $variableAmount * $count;
1600  break;
1601  case 2005:
1602  $count = intval($iPayCount["Check Copies Count"]);
1603  $amount = $variableAmount * $count;
1604  break;
1605  case 2006: // This is the Monthly Minimum. Only process this if the amount from transaction is less than the amount.
1606  $count = 1;
1607  $amount = floatval($iPayCount["Per Transaction"]) * intval($iPayCount["Total Transactions Plus"]);
1608  $amount = $fixedAmount == $iPayCount["Monthly Minimum"] ? ($amount < $fixedAmount ? $fixedAmount : 0) : 0;
1609  break;
1610 
1611  }
1612 
1613  $processIt = $amount > 0;
1614 
1615  return array("processIt" => $processIt, "count" => $count, "amount" => $amount);
1616 }
1617 
1618 /**
1619  * function CalcConditionalMemb($membershipCount, $record)
1620  * Calculate using this calculation.
1621  *
1622  * @param $membershipCount -- number of members for the CU.
1623  * @param $record -- the record from the $contractResults array.
1624  *
1625  * @param $processIt -- if true, then create the cubillpreinvoice record.
1626  * @return $amount -- the amount to charge.
1627  * @return $count -- the number to charge.
1628  */
1629 function CalcConditionalMemb($membershipCount, $record) {
1630  $memberDivide = floatval($record["qty1"]);
1631  $ifLess = floatval($record["fixed"]);
1632  $ifMore = floatval($record["variable"]);
1633  $cu = $record["cu"];
1634 
1635  // Make sure that it is in range. -1 is positive infinity for the second number
1636  $value = $membershipCount <= $memberDivide ? $ifLess : $ifMore;
1637 
1638  return array("count" => 1, "amount" => $value);
1639 }
1640 
1641 /**
1642  * function GetEstatementCounts($cutoffDate, $cutoffTime, $homecu_dev_oauth2_proxy)
1643  * Gets the eStatement counts from www3, www5, www6, and my.homecu.net.
1644  * Then it aggregates the data into one array.
1645  *
1646  * @param $cutoffDate -- the cutoff date (get everything after this date)
1647  * @param $cutoffTime -- the cutoff time (get everything after this time on the cutoff date)
1648  * @param $devmode -- the devmode
1649  *
1650  * @return array -- the eStatement counts
1651  */
1652 function GetEstatementCounts($cutoffDate, $cutoffTime, $devmode) {
1653 
1654  try {
1655  // set a parameter string with the cutoff date and time
1656  $q = "cdate=$cutoffDate&ctime=$cutoffTime";
1657 
1658  // list of servers to check
1659  $odysseyServer = 'my';
1660  $servers = array('www3','www5','www6', $odysseyServer);
1661  $cuList = array();
1662  foreach ($servers as $ask) {
1663 
1664  $isOdyssey = $ask == $odysseyServer;
1665  if ($isOdyssey) {
1666  $cmd = "https://$ask.homecu.net/monitor/cgi-bin/stmntcount.mp?csv=raw&$q";
1667  } else {
1668  $cmd = "https://$ask.homecu.net/hcuadm/stmntcount.pl?csv=raw&$q";
1669  }
1670 
1671  $results = $isOdyssey ? RunCurlOdyssey($cmd, $devmode) : RunCurlMammoth($cmd);
1672  switch ($results["httpCode"]) {
1673  case 504:
1674  case 0:
1675  throw new exception ("Server $ask timed out.", 1);
1676  break;
1677  case 200:
1678  $rawresp = $results["rawresp"];
1679  break;
1680  default:
1681  throw new exception ("Status not found.", 2);
1682  break;
1683  }
1684 
1685  $first = true;
1686  $second = false;
1687 
1688  if ($rawresp) {
1689  $lines = explode("\r\n",$rawresp);
1690 
1691  foreach($lines as $line) {
1692  if (trim($line) != "") {
1693  if ($first) {
1694  $first = false;
1695  $second = true;
1696  continue;
1697  }
1698  if ($second) {
1699  $second = false;
1700  continue;
1701  }
1702 
1703  $explodedLine = explode("\t", $line);
1704 
1705  if ($explodedLine[0] != "s") {
1706  continue;
1707  }
1708 
1709  $cu = strtolower(trim($explodedLine[1]));
1710  $count = intval($explodedLine[2]);
1711 
1712  // In case CU has counts on several servers.
1713  if (isset($cuList[$cu])) {
1714  $cuList[$cu] += $count;
1715  } else {
1716  $cuList[$cu] = $count;
1717  }
1718  }
1719  }
1720  }
1721  }
1722 
1723  $returnArray = array("status" => "000", "error" => "", "counts" => $cuList);
1724  } catch (exception $e) {
1725  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "counts" => array());
1726  }
1727 
1728  return $returnArray;
1729 } // end GetEstatementCounts
1730 
1731 /**
1732  * function GetIVRCounts($reportStartDate, $reportEndDate)
1733  * Gets the IVR counts from ivr.homecu.net.
1734  *
1735  * @param $reportStartDate -- the beginning date of the range.
1736  * @param $reportEndDate -- the ending date of the range.
1737  *
1738  * @return array -- the IVR counts
1739  */
1740 function GetIVRCounts($reportStartDate, $reportEndDate) {
1741 
1742  try {
1743  $cmd = "http://ivr.homecu.net/hcuadm/ivr_report?rv=RAW&byDNID=Y&reportstartdate=$reportStartDate&reportenddate=$reportEndDate";
1744 
1745 
1746  $results = RunCurlMammoth($cmd);
1747  switch ($results["httpCode"]) {
1748  case 504:
1749  case 0:
1750  throw new exception ("IVR server timed out.", 1);
1751  break;
1752  case 200:
1753  $rawresp = $results["rawresp"];
1754  break;
1755  default:
1756  throw new exception ("Status not found.", 2);
1757  break;
1758  }
1759 
1760  $cuList = array();
1761  $first = true;
1762  if ($rawresp) {
1763  $lines = explode("\r\n",$rawresp);
1764  foreach($lines as $line) {
1765  if (trim($line) != "") {
1766  // first line is the column headers
1767  if ($first) {
1768  $first = false;
1769  continue;
1770  }
1771 
1772  $explodedLine = explode(",", $line);
1773 
1774  // skip any "Total" lines
1775  if ( $explodedLine[0] == "T" ) {
1776  continue;
1777  }
1778 
1779  $cuList[] = array( "cu" => trim($explodedLine[0]), "DNID" => $explodedLine[1], "calls" => $explodedLine[2], "minutes" => $explodedLine[3] );
1780  }
1781  }
1782  }
1783 
1784  $returnArray = array("status" => "000", "error" => "", "counts" => $cuList);
1785  } catch (exception $e) {
1786  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "counts" => array());
1787  }
1788 
1789  return $returnArray;
1790 } // end GetIVRCounts
1791 
1792 /**
1793  * function GetIPayCounts($iPayReportDate, $homecu_dev_oauth2_proxy)
1794  * This function returns a curl call to the miPayReport.prg page to get the counts for iPay.
1795  *
1796  * @param $iPayReportDate -- date in the YYYY_mm format to get the CSV.
1797  * @param $devmode -- the devmode
1798  *
1799  * @return $cuList: map of CU and the record in the CSV file.
1800  */
1801 function GetIPayCounts($iPayReportDate, $devmode) {
1802 
1803  try {
1804  $cmd = "https://my.homecu.net/hcuadm/miPayReport.prg?operation=csv&month=$iPayReportDate";
1805 
1806 
1807  $results = RunCurlOdyssey($cmd, $devmode);
1808  switch ($results["httpCode"]) {
1809  case 504:
1810  case 0:
1811  throw new exception ("Monitor server timed out.", 1);
1812  break;
1813  case 200:
1814  $rawresp = $results["rawresp"];
1815  break;
1816  default:
1817  throw new exception ("Status not found.", 2);
1818  break;
1819  }
1820 
1821  $cuList = array();
1822  if ($rawresp) {
1823  $lines = explode("\n",$rawresp);
1824  $first = true;
1825  $header = array();
1826  foreach($lines as $line) {
1827 
1828  if (trim($line) == "") {
1829  continue;
1830  }
1831  // first line is the column headers
1832  if ($first) {
1833  $first = false;
1834  $header = str_getcsv($line);
1835  continue;
1836  }
1837 
1838  $parts = str_getcsv($line);
1839 
1840  // skip any "Error" lines
1841  if ( $parts[0] == "E") {
1842  continue;
1843  }
1844 
1845  $parts = array_combine($header, $parts);
1846 
1847  $cuList [$parts["CU Code"]] = $parts;
1848  }
1849  }
1850 
1851  $returnArray = array("status" => "000", "error" => "", "counts" => $cuList);
1852  } catch (exception $e) {
1853  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "counts" => array());
1854  }
1855 
1856  return $returnArray;
1857 } // end GetEmailCounts
1858 
1859 /**
1860  * function GetTimetrackCounts($reportDate, $endDate)
1861  * Get the counts from timetrack.
1862  *
1863  * @param $reportDate -- the beginning date of the report range.
1864  * @param $endDate -- the ending date of the report range.
1865  *
1866  * @return array -- the timetrack counts.
1867  */
1868 function GetTimetrackCounts($reportDate, $endDate) {
1869 
1870  try {
1871  $url = "https://www.homecu.net/timetrack/main.php?mainAction=report&month=$reportDate";
1872 
1873  $url .= $endDate == "" ? "" : "&endmonth=$endDate";
1874 
1875  $results = RunCurlMammoth($url);
1876  switch ($results["httpCode"]) {
1877  case 504:
1878  case 0:
1879  throw new exception ("Monitor server timed out.", 1);
1880  break;
1881  case 200:
1882  $raw = $results["rawresp"];
1883  break;
1884  default:
1885  throw new exception ("Status not found.", 2);
1886  break;
1887  }
1888 
1889  $first = true;
1890  $counts = array();
1891 
1892  if ($raw != "") {
1893  // CU,Staff Name,Rush Hours,Bill Hours
1894  foreach(explode("\r\n", $raw) as $line) {
1895  $lineParts = explode(",", $line);
1896  if ($lineParts[0] == "ERROR") {
1897  $errors[] = $lineParts[1];
1898  } else if ($first) { // Skip over column header.
1899  $first = false;
1900  } else { // Here is the meat
1901  $cu = strtolower(trim($lineParts[0]));
1902  if ($cu == "") {
1903  continue; // There is a line with no CU and staff = Kim. This is not going to relate to anything.
1904  }
1905  if (!isset($counts[$cu])) {
1906  $counts[$cu] = array();
1907  }
1908 
1909  $counts[$cu][] = array("staff" => $lineParts[1], "rushable" => $lineParts[2], "billable" => $lineParts[3]);
1910  }
1911  }
1912  }
1913 
1914  $returnArray = array("status" => "000", "error" => "", "counts" => $counts);
1915  } catch (exception $e) {
1916  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "counts" => array());
1917  }
1918 
1919  return $returnArray;
1920 }
1921 
1922 /**
1923  * function GetTimetrackCookie (
1924  * Create a cookie for timetrack using the test user.
1925  *
1926  * @return string -- the cookie for timetrack
1927  */
1928 function GetTimetrackCookie () {
1929  $secret = "Apache";
1930  $id = 13; // TEST DUDE
1931  $month = "01";
1932  $day = "01";
1933  $year = 1990;
1934  $date = "1990-01-01";
1935  $userName = "TEST DUDE";
1936  $ipAddress = "";
1937  $ticketDomain = "www.homecu.net";
1938  $now = time();
1939 
1940  $hash = MD5($secret . MD5(join (':', array($secret, $id, $month, $year, $day, $date, $userName, $ipAddress, $now))));
1941  $cookie = "c_sid=$id&c_mno=$month&c_syr=$year&c_day=$day&c_date=$date&c_nme=$userName&Cip=$ipAddress&Ctime=$now&Ch=$hash";
1942 
1943  return $cookie;
1944 }
1945 
1946 /**
1947  * function getMonitorBillingList($dbh, $showSQL, $reportDate, $monthBit, $logFile)
1948  * Get billing list, membership counts from the monitor server if the server is DEV and debug option is set.
1949  *
1950  * @param integer $dbh -- the database connection
1951  * @param boolean $showSQL -- if true, SQL will be added to the return array
1952  * @param integer $monthBit -- the bit to run the billing list for. It is going to be 1-12 obviously.
1953  * @param string $logFile -- points to the billing count file. There will be a file per month.
1954  *
1955  * @return SQLs, errors, membership counts, and the billing list.
1956  */
1957 function GetMonitorBillingList($dbh, $sysenv) {
1958  try {
1959  extract ($sysenv["BILL"]);
1960 
1961  $reportDate = isset($reportDate) ? trim($reportDate) : "";
1962  $monthBit = isset($monthBit) ? trim($monthBit) : "";
1963 
1964  $url = "https://my.homecu.net/billing/index.prg?operation=getPotentiallyFromDifferentServer&reportDate=$reportDate&monthBit=$monthBit";
1965  $results = RunCurlOdyssey($url, $sysenv["devmode"]);
1966 
1967  switch ($results["httpCode"]) {
1968  case 504:
1969  case 0:
1970  throw new exception ("Monitor server timed out.", 3);
1971  break;
1972  case 200:
1973  $raw = $results["rawresp"];
1974  break;
1975  default:
1976  throw new exception ("Status not found.", 4);
1977  }
1978 
1979  if (trim($raw) == "") {
1980  throw new exception ("Monitor has no results.", 1);
1981  }
1982 
1983  $returnArray = HCU_JsonDecode($raw);
1984  if (count($returnArray) == 0) {
1985  throw new exception ("Monitor has no results.", 2);
1986  }
1987  } catch (exception $e) {
1988  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "record" => array(), "membershipCounts" => array());
1989  }
1990  return $returnArray;
1991 }
1992 
1993 /**
1994  * function GetPotentiallyFromDifferentServer($dbh, $sysenv)
1995  * This gets the billing features from the billing contract configuration.
1996  * For the most part, this will come from the same database as the current container.
1997  * However, there is an option to get this information from my.homecu.net on the DEV environment.
1998  *
1999  * @param $dbh -- the database connection
2000  * @param $sysenv -- the environment variables (from cu_top)
2001  *
2002  * @return $status -- "000" if there are no errors. Nonzero otherwise.
2003  * @return $error -- "" if there are no errors. Nonempty otherwise.
2004  * @return $record -- the result of the query to get all billing features for billing.
2005  * @return $membershipCounts -- the assets and membership of the CU.
2006  */
2007 function GetPotentiallyFromDifferentServer($dbh, $sysenv) {
2008  try {
2009  global $SYS_TYPE_CLOSED;
2010 
2011  extract ($sysenv["BILL"]);
2012 
2013  // If called from localhost, don't show an E_NOTICE.
2014  $recordResults = array();
2015  $memberResults = array();
2016 
2017  $sql = "select fd.id \"featureDetailId\", fd.sales_order_detail_id \"detailId\", trim(so.cu_id) \"cu\", trim(sd.prod_id) \"productName\",
2018  trim(si.billing_system_id) \"billingId\", si.calc_id \"calcId\", trim(fd.billing_template) \"billingTemplate\", si.id \"salesItemId\",
2019  trim(si.display_name) \"actualDescription\", fd.bill_half_boolean \"billHalf\", fd.billing_status \"billingStatus\",
2020  fd.partially_billed_date \"partiallyBilledDate\", fd.partially_billed_amount \"partiallyBilledAmount\", trim(p.home_cu_desc) \"prodText\",
2021  trim(ci.name) \"cuName\", fd.frequency \"frequency\", fd.months_to_bill \"monthsToBill\"
2022  from cubillfeaturedetail fd
2023  left join cubillsalesorderdetail sd on fd.sales_order_detail_id = sd.id
2024  left join cubillsalesorder so on sd.sales_order_id = so.id
2025  left join cuprodlist p on sd.prod_id = p.home_cu_code
2026  left join cubillsalesitem si on fd.sales_item_id = si.id
2027  left join cuinfo ci on lower(so.cu_id) = lower(ci.user_name)
2028  where (
2029  (fd.start_date <= '" . prep_save($reportDate) . "' and (fd.end_date is null or fd.end_date >= '" . prep_save($reportDate) . "')
2030  and fd.billing_status in (2,3) and fd.frequency between 0 and 2 and (fd.frequency = 0 or fd.months_to_bill & " . intval($monthBit) . " <> 0))
2031  or (fd.partially_billed_date is not null and fd.billing_status in (0,4) and fd.partially_billed_date = '" . prep_save($reportDate) . "'))
2032  order by so.cu_id, si.calc_id";
2033 
2034  $sth = db_query($sql, $dbh);
2035  if (!$sth) {
2036  throw new exception ("Record query failed.", 1);
2037  }
2038  $recordResults = db_fetch_all($sth);
2039  $recordResults = $recordResults === false ? array() : $recordResults;
2040 
2041  $sql = "select trim(user_name) \"cu\", dec_31_mem \"membership\", dec_31_assets \"assets\" from cuinfo";
2042  $sth = db_query($sql, $dbh);
2043  if (!$sth) {
2044  throw new exception ("Member query failed.", 2);
2045  }
2046  $memberResults = db_fetch_all($sth);
2047  $memberResults = $memberResults === false ? array() : $memberResults;
2048 
2049  $returnArray = array("status" => "000", "error" => "", "record" => $recordResults, "membershipCounts" => $memberResults);
2050 
2051  } catch (exception $e) {
2052  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "record" => array(), "membershipCounts" => array());
2053  }
2054  return $returnArray;
2055 }
2056 
2057 /**
2058  * function DownloadRunBillingFiles($dbh, $sysenv)
2059  * This function downloads the run billing files.
2060  * If there is a problem so that the run billing process fails halfway through,
2061  * I can use this URL to get the files without bothering Mike, Mark, Kenny, or Miki.
2062  *
2063  * @param $dbh -- the database connection.
2064  * @param $sysenv -- the system environment.
2065  *
2066  * @return file | json of error
2067  */
2068 function DownloadRunBillingFiles($dbh, $sysenv) {
2069  try {
2070 
2071  $cmd = "cd /home/homecu/tmp >/dev/null;
2072  mkdir runBilling >/dev/null;
2073  cp .runBilling* runBilling >/dev/null;
2074  tar -zcvf runBilling.tar.gz runBilling >/dev/null;";
2075  $results = shell_exec($cmd);
2076  if ($results != "") {
2077  throw new exception ($results, 1);
2078  }
2079 
2080  $file = "/home/homecu/tmp/runBilling.tar.gz";
2081  $dir = "/home/homecu/tmp/runBilling";
2082 
2083  if (!file_exists($file)) {
2084  throw new exception ("File doesn't exist.", 1);
2085  }
2086 
2087  header('Content-Description: File Transfer');
2088  header('Content-Type: application/octet-stream');
2089  header('Content-Disposition: attachment; filename="' . basename($file) . '"');
2090  header('Expires: 0');
2091  header('Cache-Control: must-revalidate');
2092  header('Pragma: public');
2093  header('Content-Length: ' . filesize($file));
2094  flush(); // Flush system output buffer
2095  readfile($file);
2096  exit;
2097  } catch (exception $e) {
2098  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
2099  }
2100  return $returnArray;
2101 }