PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/billing.inc.php

https://github.com/sitracker/sitracker
PHP | 1545 lines | 1059 code | 224 blank | 262 comment | 220 complexity | 8163c8d5f4cbf88ddbf15387936f5406 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // billing.inc.php - functions relating to billing
  3. //
  4. // SiT (Support Incident Tracker) - Support call tracking system
  5. // Copyright (C) 2010-2014 The Support Incident Tracker Project
  6. // Copyright (C) 2000-2009 Salford Software Ltd. and Contributors
  7. //
  8. // This software may be used and distributed according to the terms
  9. // of the GNU General Public License, incorporated herein by reference.
  10. // Prevent script from being run directly (ie. it must always be included
  11. if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))
  12. {
  13. exit;
  14. }
  15. require_once (APPLICATION_LIBPATH . 'billing.class.php');
  16. define ("BILLING_APPROVED", 0);
  17. define ("BILLING_AWAITINGAPPROVAL", 5);
  18. define ("BILLING_RESERVED", 10);
  19. define ("BILLING_TYPE_UNIT", 'unit');
  20. define ("BILLING_TYPE_INCIDENT", 'incident');
  21. /**
  22. * Returns if the contact has a timed contract or if the site does in the case of the contact not.
  23. * @author Paul Heaney
  24. * @param int $contactid
  25. * @return either NO_BILLABLE_CONTRACT, CONTACT_HAS_BILLABLE_CONTRACT or SITE_HAS_BILLABLE_CONTRACT the latter is if the site has a billable contract by the contact isn't a named contact
  26. */
  27. function does_contact_have_billable_contract($contactid)
  28. {
  29. global $now, $db;
  30. $return = NO_BILLABLE_CONTRACT;
  31. $siteid = contact_siteid($contactid);
  32. $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl ";
  33. $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} ";
  34. $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'";
  35. $result = mysqli_query($db, $sql);
  36. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  37. if (mysqli_num_rows($result) > 0)
  38. {
  39. // We have some billable/timed contracts
  40. $return = SITE_HAS_BILLABLE_CONTRACT;
  41. // check if the contact is listed on one of these
  42. while ($obj = mysqli_fetch_object($result))
  43. {
  44. $sqlcontact = "SELECT * FROM `{$GLOBALS['dbSupportContacts']}` ";
  45. $sqlcontact .= "WHERE maintenanceid = {$obj->id} AND contactid = {$contactid}";
  46. $resultcontact = mysqli_query($db, $sqlcontact);
  47. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  48. if (mysqli_num_rows($resultcontact) > 0)
  49. {
  50. $return = CONTACT_HAS_BILLABLE_CONTRACT;
  51. break;
  52. }
  53. }
  54. }
  55. return $return;
  56. }
  57. /**
  58. * Gets the billable contract ID for a contact, if multiple exist then the first one is choosen
  59. * @author Paul Heaney
  60. * @param int $contactid - The contact ID you want to find the contract for
  61. * @return int the ID of the contract, -1 if not found
  62. */
  63. function get_billable_contract_id($contactid)
  64. {
  65. global $now, $db;
  66. $return = -1;
  67. $siteid = contact_siteid($contactid);
  68. $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl ";
  69. $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} ";
  70. $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'";
  71. $result = mysqli_query($db, $sql);
  72. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  73. if (mysqli_num_rows($result) > 0)
  74. {
  75. $return = mysqli_fetch_object($result)->id;
  76. }
  77. return $return;
  78. }
  79. /**
  80. * Get sthe billable contract ID for a site, if multiple exist then the first one is choosen
  81. * @author Paul Heaney
  82. * @param int $siteid - The site ID you want to find the contract for
  83. * @return int the ID of the contract, -1 if not found
  84. */
  85. function get_site_billable_contract_id($siteid)
  86. {
  87. global $now, $db;
  88. $return = -1;
  89. $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl ";
  90. $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} ";
  91. $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'";
  92. $result = mysqli_query($db, $sql);
  93. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  94. if (mysqli_num_rows($result) > 0)
  95. {
  96. $return = mysqli_fetch_object($result)->id;
  97. }
  98. return $return;
  99. }
  100. /**
  101. * Returns the percentage remaining for ALL services on a contract
  102. * @author Kieran Hogg
  103. * @param int $mainid - contract ID
  104. * @return mixed - percentage between 0 and 1 if services, FALSE if not
  105. */
  106. function get_service_percentage($maintid)
  107. {
  108. global $dbService, $db;
  109. $sql = "SELECT * FROM `{$dbService}` ";
  110. $sql .= "WHERE contractid = {$maintid}";
  111. $result = mysqli_query($db, $sql);
  112. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  113. if (mysqli_num_rows($result) > 0)
  114. {
  115. $total = 0;
  116. $num = 0;
  117. while ($service = mysqli_fetch_object($result))
  118. {
  119. if (((float) $service->balance > 0) OR ((float) $service->creditamount > 0))
  120. {
  121. $total += (float) $service->balance / (float) $service->creditamount;
  122. }
  123. $num++;
  124. }
  125. $return = (float) $total / (float) $num;
  126. }
  127. else
  128. {
  129. $return = FALSE;
  130. }
  131. return $return;
  132. }
  133. /**
  134. * Does a contract have a service level which is timed / billed
  135. * @author Ivan Lucas
  136. * @param int $contractid
  137. * @return Whether the contract should be billed
  138. * @return bool TRUE: Yes timed. should be billed, FALSE: No, not timed. Should not be billed
  139. */
  140. function is_contract_timed($contractid)
  141. {
  142. global $dbMaintenance, $dbServiceLevels, $db;
  143. $timed = FALSE;
  144. $sql = "SELECT timed FROM `{$dbMaintenance}` AS m, `{$dbServiceLevels}` AS sl ";
  145. $sql .= "WHERE m.servicelevel = sl.tag AND m.id = {$contractid}";
  146. $result = mysqli_query($db, $sql);
  147. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  148. list($timed) = mysqli_fetch_row($result);
  149. if ($timed == 'yes')
  150. {
  151. return TRUE;
  152. }
  153. else
  154. {
  155. return FALSE;
  156. }
  157. }
  158. /**
  159. * Set the last billing time on a service
  160. * @param int $serviceid - service ID
  161. * @param string $date - Date (in format YYYY-MM-DD) to set the last billing time to
  162. * @return boolean - TRUE if sucessfully updated, false otherwise
  163. */
  164. function update_last_billed_time($serviceid, $date)
  165. {
  166. global $dbService, $db;
  167. $rtnvalue = FALSE;
  168. if (!empty($serviceid) AND !empty($date))
  169. {
  170. $rtnvalue = TRUE;
  171. $sql .= "UPDATE `{$dbService}` SET lastbilled = '{$date}' WHERE serviceid = {$serviceid}";
  172. mysqli_query($db, $sql);
  173. if (mysqli_error($db))
  174. {
  175. trigger_error(mysqli_error($db), E_USER_ERROR);
  176. $rtnvalue = FALSE;
  177. }
  178. if (mysqli_affected_rows($db) < 1)
  179. {
  180. trigger_error("Approval failed", E_USER_ERROR);
  181. $rtnvalue = FALSE;
  182. }
  183. }
  184. return $rtnvalue;
  185. }
  186. /**
  187. * Function to find the most applicable unit rate for a particular contract
  188. * @author Paul Heaney
  189. * @param int $contractid - The contract id
  190. * @param string $date UNIX timestamp. The function will look for service that is current as of this timestamp
  191. * @return int the unit rate, -1 if non found
  192. */
  193. function get_unit_rate($contractid, $date='')
  194. {
  195. $serviceid = get_serviceid($contractid, $date);
  196. if ($serviceid != -1)
  197. {
  198. $unitrate = get_service_unitrate($serviceid);
  199. }
  200. else
  201. {
  202. $unitrate = -1;
  203. }
  204. return $unitrate;
  205. }
  206. /**
  207. * Returns the unit rate for a service
  208. * @author Paul Heaney
  209. * @param int $serviceid - The serviceID to get the unit rate for
  210. * @return mixed FALSE if no service found else the unit rate
  211. */
  212. function get_service_unitrate($serviceid)
  213. {
  214. global $db;
  215. $rtnvalue = FALSE;
  216. $sql = "SELECT rate FROM `{$GLOBALS['dbService']}` WHERE serviceid = {$serviceid}";
  217. $result = mysqli_query($db, $sql);
  218. if (mysqli_error($db))
  219. {
  220. trigger_error(mysqli_error($db), E_USER_WARNING);
  221. return FALSE;
  222. }
  223. if (mysqli_num_rows($result) > 0)
  224. {
  225. list($rtnvalue) = mysqli_fetch_row($result);
  226. }
  227. return $rtnvalue;
  228. }
  229. /**
  230. * @author Paul Heaney
  231. * @param int $contractid The Contract ID
  232. * @param int $date UNIX timestamp. The function will look for service that is current as of this timestamp
  233. * @return mixed. Service ID, or -1 if not found, or FALSE on error
  234. */
  235. function get_serviceid($contractid, $date = '')
  236. {
  237. global $now, $CONFIG, $db;
  238. if (empty($date)) $date = $now;
  239. $sql = "SELECT serviceid FROM `{$GLOBALS['dbService']}` AS s ";
  240. $sql .= "WHERE contractid = {$contractid} AND UNIX_TIMESTAMP(startdate) <= {$date} ";
  241. $sql .= "AND UNIX_TIMESTAMP(enddate) > {$date} ";
  242. $sql .= "AND (balance > 0 OR (select count(1) FROM `{$GLOBALS['dbService']}` WHERE contractid = s.contractid AND balance > 0) = 0) ";
  243. if (!$CONFIG['billing_allow_incident_approval_against_overdrawn_service'])
  244. {
  245. $sql .= "AND balance > 0 ";
  246. }
  247. $sql .= "ORDER BY priority DESC, enddate ASC, balance DESC LIMIT 1";
  248. $result = mysqli_query($db, $sql);
  249. if (mysqli_error($db))
  250. {
  251. trigger_error(mysqli_error($db),E_USER_WARNING);
  252. return FALSE;
  253. }
  254. $serviceid = -1;
  255. if (mysqli_num_rows($result) > 0)
  256. {
  257. list($serviceid) = mysqli_fetch_row($result);
  258. }
  259. return $serviceid;
  260. }
  261. /**
  262. * Get the current contract balance
  263. * @author Ivan Lucas
  264. * @param int $contractid. Contract ID of the contract to credit
  265. * @param bool $includenonapproved. Include incidents which have not been approved
  266. * @param bool $showonlycurrentlyvalue - Show only contracts which have valid NOW() - i.e. startdate less than NOW() and endate greate than NOW()
  267. * @param bool $includereserved - Deduct the reseved amount from the returned balance
  268. * @return int The total balance remaining on the contract
  269. * @note The balance is a sum of all the current service that have remaining balance
  270. * @todo add a param that makes this optionally show the incident pool balance
  271. in the case of non-timed type contracts
  272. */
  273. function get_contract_balance($contractid, $includenonapproved = FALSE, $showonlycurrentlyvalid = TRUE, $includereserved = TRUE)
  274. {
  275. global $dbService, $now, $db;
  276. $balance = 0.00;
  277. $sql = "SELECT SUM(balance) FROM `{$dbService}` ";
  278. $sql .= "WHERE contractid = {$contractid} ";
  279. if ($showonlycurrentlyvalid)
  280. {
  281. $date = ldate('Y-m-d', $now);
  282. $sql .= "AND '{$date}' BETWEEN startdate AND enddate ";
  283. }
  284. $result = mysqli_query($db, $sql);
  285. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  286. list($balance) = mysqli_fetch_row($result);
  287. if ($includenonapproved)
  288. {
  289. // Need to get sum of non approved incidents for this contract and deduct
  290. $balance += contract_transaction_total($contractid, BILLING_AWAITINGAPPROVAL);
  291. }
  292. if ($includereserved)
  293. {
  294. $balance += contract_transaction_total($contractid, BILLING_RESERVED);
  295. }
  296. return $balance;
  297. }
  298. /**
  299. * Updates the amount and optionally the description on a transaction awaiting reservation
  300. * @author Paul Heaney
  301. * @param int $transactionid The transaction ID to update
  302. * @param int $amount The amount to set the transaction to
  303. * @param string $description (optional) the description to set on the transaction
  304. * @return bool TRUE on a sucessful update FALSE otherwise
  305. */
  306. function update_reservation($transactionid, $amount, $description='')
  307. {
  308. return update_transaction($transactionid, $amount, $description, BILLING_RESERVED);
  309. }
  310. /**
  311. * Updates a transacction that is either waiting approval or reserved
  312. * @author Paul Heaney
  313. * @param int $transactionid The transaction ID to update
  314. * @param float $amount The amount to set the transaction to
  315. * @param string $description (optional) the description to set on the transaction
  316. * @param int $status either BILLING_RESERVED or BILLING_AWAITINGAPPROVAL
  317. * @return bool TRUE on a sucessful update FALSE otherwise
  318. */
  319. function update_transaction($transactionid, $amount = 0.00, $description = '', $status = BILLING_AWAITINGAPPROVAL)
  320. {
  321. global $db;
  322. if ($status == BILLING_APPROVED)
  323. {
  324. trigger_error("You cant change a approved transaction", E_USER_ERROR);
  325. exit;
  326. }
  327. $rtnvalue = FALSE;
  328. // Note we dont need to check its awaiting reservation as we check this when doing the update
  329. if (is_numeric($transactionid))
  330. {
  331. $sql = "UPDATE `{$GLOBALS['dbTransactions']}` SET amount = '{$amount}' ";
  332. if (!empty($description))
  333. {
  334. $sql .= ", description = '{$description}' ";
  335. }
  336. $sql .= "WHERE transactionid = {$transactionid} AND transactionstatus = {$status}";
  337. mysqli_query($db, $sql);
  338. if (mysqli_error($db))
  339. {
  340. trigger_error(mysqli_error($db),E_USER_ERROR);
  341. $rtnvalue = FALSE;
  342. }
  343. if (mysqli_affected_rows($db) > 0)
  344. {
  345. $rtnvalue = TRUE;
  346. }
  347. }
  348. return $rtnvalue;
  349. }
  350. /**
  351. * Do the necessary tasks to billable incidents on closure, including creating transactions
  352. * @author Paul Heaney
  353. * @param int $incidentid The incident ID to do the close on, if its not a billable incident then no actions are performed
  354. * @return bool TRUE on sucessful closure, false otherwise
  355. */
  356. function close_billable_incident($incidentid)
  357. {
  358. $toReturn = TRUE; // Default to it not being a billiable incidents
  359. $billableincident = get_billable_object_from_incident_id($incidentid);
  360. if ($billableincident)
  361. {
  362. $toReturn = $billableincident->close_incident($incidentid);
  363. }
  364. return $toReturn;
  365. }
  366. /**
  367. * Function to return a billable incident object based on incident ID
  368. * @author Paul Heaney
  369. * @param int $incidentid The incident ID to get a billable incident for
  370. * @return mixed Billable if incident is billable else FALSE
  371. * @todo This may be removed following the billable code refactor
  372. */
  373. function get_billable_object_from_incident_id($incidentid)
  374. {
  375. global $db;
  376. $toReturn = FALSE;
  377. $sql = "SELECT m.billingtype FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbIncidents']}` AS i WHERE i.maintenanceid = m.id AND i.id = {$incidentid}";
  378. $result = mysqli_query($db, $sql);
  379. if (mysqli_error($db))
  380. {
  381. trigger_error("Error finding type of incident billing ".mysqli_error($db), E_USER_WARNING);
  382. $toReturn = FALSE;
  383. }
  384. if (mysqli_num_rows($result) > 0)
  385. {
  386. list($billingtype) = mysqli_fetch_row($result);
  387. $toReturn = get_billable_incident_object($billingtype);
  388. }
  389. return $toReturn;
  390. }
  391. /**
  392. * Function to return a billable incident object based on contract ID
  393. * @author Paul Heaney
  394. * @param int $incidentid The contract ID to get a object for
  395. * @return mixed Billable if incident is billable else FALSE
  396. * @todo This may be removed following the billable code refactor
  397. */
  398. function get_billable_object_from_contract_id($contractid)
  399. {
  400. global $db;
  401. $toReturn = FALSE;
  402. $sql = "SELECT m.billingtype FROM `{$GLOBALS['dbMaintenance']}` AS m WHERE m.id = {$contractid}";
  403. $result = mysqli_query($db, $sql);
  404. if (mysqli_error($db))
  405. {
  406. trigger_error("Error finding type of contract billing " . mysqli_error($db), E_USER_WARNING);
  407. $toReturn = FALSE;
  408. }
  409. if (mysqli_num_rows($result) > 0)
  410. {
  411. list($billingtype) = mysqli_fetch_row($result);
  412. $toReturn = get_billable_incident_object($billingtype);
  413. }
  414. return $toReturn;
  415. }
  416. /**
  417. * Returns a billable object given a particular billing type
  418. * @author Paul Heaney
  419. * @param String $billingtype The billing type to return an object off
  420. * @return mixed Billable if incident is billable else FALSE
  421. * @todo This may be removed following the billable code refactor - we store the Object name in the DB now perhaps remove? Only benefit its it centralises error handling
  422. */
  423. function get_billable_incident_object($billingtype)
  424. {
  425. global $db;
  426. $toReturn = FALSE;
  427. if (class_exists($billingtype) and is_subclass_of($billingtype, 'Billable'))
  428. {
  429. $toReturn = new $billingtype();
  430. }
  431. else
  432. {
  433. // Try and identity from old internal name
  434. switch ($billingtype)
  435. {
  436. case BILLING_TYPE_UNIT:
  437. $toReturn = new UnitBillable();
  438. break;
  439. case BILLING_TYPE_INCIDENT:
  440. $toReturn = new IncidentBillable();
  441. break;
  442. case '':
  443. // Its not a billable incident
  444. $toReturn = FALSE;
  445. break;
  446. default:
  447. $toReturn = FALSE;
  448. trigger_error("Unknown billable type of '{$billingtype}'");
  449. }
  450. }
  451. return $toReturn;
  452. }
  453. /**
  454. * Function to approve an incident, this adds a transaction and confirms the 'bill' is correct.
  455. * @author Paul Heaney
  456. * @param int incidentid ID of the incident to approve
  457. */
  458. function approve_incident_transaction($transactionid)
  459. {
  460. global $db;
  461. $rtnvalue = FALSE;;
  462. $sql = "SELECT l.linkcolref FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t ";
  463. $sql .= "WHERE t.transactionid = l.origcolref AND t.transactionstatus = ".BILLING_AWAITINGAPPROVAL." AND l.linktype = 6 AND t.transactionid = {$transactionid}";
  464. $result = mysqli_query($db, $sql);
  465. if (mysqli_error($db)) trigger_error("Error identify incident transaction. ".mysqli_error($db), E_USER_WARNING);
  466. if (mysqli_num_rows($result) > 0)
  467. {
  468. list($incidentid) = mysqli_fetch_row($result);
  469. $billable = get_billable_object_from_incident_id($incidentid);
  470. $rtnvalue = $billable->approve_incident_transaction($transactionid);
  471. }
  472. return $rtnvalue;
  473. }
  474. /**
  475. * Update contract balance by an amount and log a transaction to record the change
  476. * @author Ivan Lucas
  477. * @param int $contractid. Contract ID of the contract to credit
  478. * @param string $description. A useful description of the transaction
  479. * @param float $amount. The amount to credit or debit to the contract balance
  480. positive for credit and negative for debit
  481. * @param int $serviceid. optional serviceid to use. This is calculated if ommitted.
  482. * @param int $transaction - the transaction you are approving
  483. * @param int $totalunits - The number of units that are being approved - before the multiplier
  484. * @param int $totalbillableunits - The number of units charged to the customer (after the multiplier)
  485. * @param int $totalrefunds - Total number of units refunded to the customer
  486. * @return boolean - status of the balance update
  487. * @note The actual service to credit will be calculated automatically if not specified
  488. */
  489. function update_contract_balance($contractid, $description, $amount, $serviceid='', $transactionid='', $totalunits=0, $totalbillableunits=0, $totalrefunds=0)
  490. {
  491. global $now, $dbService, $dbTransactions, $db;
  492. $rtnvalue = TRUE;
  493. if (empty($totalunits)) $totalunits = -1;
  494. if (empty($totalbillableunits)) $totalbillableunits = -1;
  495. if (empty($totalrefunds)) $totalrefunds = 0;
  496. if ($serviceid == '')
  497. {
  498. // Find the correct service record to update
  499. $serviceid = get_serviceid($contractid);
  500. if ($serviceid < 1) trigger_error("Invalid service ID", E_USER_ERROR);
  501. }
  502. if (trim($amount) == '') $amount = 0;
  503. $date = date('Y-m-d H:i:s', $now);
  504. // Update the balance
  505. $sql = "UPDATE `{$dbService}` SET balance = (balance + {$amount}) WHERE serviceid = '{$serviceid}' LIMIT 1";
  506. mysqli_query($db, $sql);
  507. if (mysqli_error($db))
  508. {
  509. trigger_error(mysqli_error($db), E_USER_ERROR);
  510. $rtnvalue = FALSE;
  511. }
  512. if (mysqli_affected_rows($db) < 1 AND $amount != 0)
  513. {
  514. trigger_error("Contract balance update failed", E_USER_ERROR);
  515. $rtnvalue = FALSE;
  516. }
  517. if ($rtnvalue != FALSE)
  518. {
  519. // Log the transaction
  520. if (empty($transactionid))
  521. {
  522. $sql = "INSERT INTO `{$dbTransactions}` (serviceid, totalunits, totalbillableunits, totalrefunds, amount, description, userid, dateupdated, transactionstatus) ";
  523. $sql .= "VALUES ('{$serviceid}', '{$totalunits}', '{$totalbillableunits}', '{$totalrefunds}', '{$amount}', '{$description}', '{$_SESSION['userid']}', '{$date}', '".BILLING_APPROVED."')";
  524. $result = mysqli_query($db, $sql);
  525. $rtnvalue = mysqli_insert_id($id);
  526. }
  527. else
  528. {
  529. $sql = "UPDATE `{$dbTransactions}` SET serviceid = {$serviceid}, totalunits = {$totalunits}, totalbillableunits = {$totalbillableunits}, totalrefunds = {$totalrefunds} ";
  530. $sql .= ", amount = {$amount}, userid = {$_SESSION['userid']} , dateupdated = '{$date}', transactionstatus = '".BILLING_APPROVED."' ";
  531. if (!empty($description))
  532. {
  533. $sql .= ", description = '{$description}' ";
  534. }
  535. $sql .= "WHERE transactionid = {$transactionid}";
  536. $result = mysqli_query($db, $sql);
  537. $rtnvalue = $transactionid;
  538. }
  539. if (mysqli_error($db))
  540. {
  541. trigger_error(mysqli_error($db), E_USER_ERROR);
  542. $rtnvalue = FALSE;
  543. }
  544. if (mysqli_affected_rows($db) < 1)
  545. {
  546. trigger_error("Transaction insert failed", E_USER_ERROR);
  547. $rtnvalue = FALSE;
  548. }
  549. }
  550. return $rtnvalue;
  551. }
  552. /**
  553. * Gets the maintenanceID for a incident transaction
  554. * @author Paul Heaney
  555. * @param int $transactionid The transaction ID to get the maintenance id from
  556. * @return int The maintenanceid or -1
  557. */
  558. function maintid_from_transaction($transactionid)
  559. {
  560. global $db;
  561. $rtnvalue = -1;
  562. $sql = "SELECT i.maintenanceid FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbIncidents']}` AS i WHERE ";
  563. $sql .= "l.origcolref = {$transactionid} AND l.linkcolref = i.id AND l.linktype = 6";
  564. $result = mysqli_query($db, $sql);
  565. if (mysqli_error($db)) trigger_error("Error getting maintid for transaction. ".mysqli_error($db), E_USER_WARNING);
  566. if (mysqli_num_rows($result) > 0)
  567. {
  568. list($rtnvalue) = mysqli_fetch_row($result);
  569. }
  570. return $rtnvalue;
  571. }
  572. /**
  573. * Returns the total value of incidents in a particular status
  574. * @author Paul Heaney
  575. * @param int $contractid. Contract ID of the contract to find total value of inicdents awaiting approval
  576. * @param int $status The type you are after e.g. BILLING_AWAITINGAPPROVAL, BILLING_APPROVED, BILLING_RESERVED
  577. * @return int The total value of all incidents awaiting approval logged against the contract
  578. */
  579. function contract_transaction_total($contractid, $status)
  580. {
  581. global $db;
  582. $rtnvalue = FALSE;
  583. $sql = "SELECT SUM(t.amount) FROM `{$GLOBALS['dbTransactions']}` AS t, `{$GLOBALS['dbService']}` AS s ";
  584. $sql .= "WHERE s.serviceid = t.serviceid AND s.contractid = {$contractid} AND t.transactionstatus = '{$status}'";
  585. $result = mysqli_query($db, $sql);
  586. if (mysqli_error($db)) trigger_error("Error getting total for type {$status}. ".mysqli_error($db), E_USER_WARNING);
  587. if (mysqli_num_rows($result) > 0)
  588. {
  589. list($rtnvalue) = mysqli_fetch_row($result);
  590. }
  591. return $rtnvalue;
  592. }
  593. /**
  594. * Get the total of all transactions on a particular service of a certain type
  595. * @author Paul Heaney
  596. * @param int $serviceid The serviceID to report on
  597. * @param int $status The status' to get the transaction report for'
  598. * @return int The sum in currency of the transactons
  599. */
  600. function service_transaction_total($serviceid, $status)
  601. {
  602. global $db;
  603. $rtnvalue = FALSE;
  604. $sql = "SELECT SUM(amount) FROM `{$GLOBALS['dbTransactions']}` ";
  605. $sql .= "WHERE serviceid = {$serviceid} AND transactionstatus = '{$status}'";
  606. $result = mysqli_query($db, $sql);
  607. if (mysqli_error($db)) trigger_error("Error getting total for type {$status}. ".mysqli_error($db), E_USER_WARNING);
  608. if (mysqli_num_rows($result) > 0)
  609. {
  610. list($rtnvalue) = mysqli_fetch_row($result);
  611. }
  612. return $rtnvalue;
  613. }
  614. /**
  615. * Get the current balance of a service
  616. * @author Paul Heaney
  617. * @param int $serviceid. Service ID of the service to get the balance for
  618. * @param int $includeawaitingapproval. Deduct the total awaiting approval from the balance
  619. * @param int $includereserved. Deduct the total reserved from the balance
  620. * @return int The remaining balance on the service
  621. * @todo Add param to take into account unapproved balances
  622. */
  623. function get_service_balance($serviceid, $includeawaitingapproval = TRUE, $includereserved = TRUE)
  624. {
  625. global $dbService, $db;
  626. $balance = FALSE;
  627. $sql = "SELECT balance FROM `{$dbService}` WHERE serviceid = {$serviceid}";
  628. $result = mysqli_query($db, $sql);
  629. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  630. if (mysqli_num_rows($result) == 1)
  631. {
  632. list($balance) = mysqli_fetch_row($result);
  633. if ($includeawaitingapproval)
  634. {
  635. $balance += service_transaction_total($serviceid, BILLING_AWAITINGAPPROVAL);
  636. }
  637. if ($includereserved)
  638. {
  639. $balance += service_transaction_total($serviceid, BILLING_RESERVED);
  640. }
  641. }
  642. return $balance;
  643. }
  644. /**
  645. * Function to identify if incident has been approved for billing
  646. * @author Paul Heaney
  647. * @return TRUE for approved, FALSE otherwise
  648. */
  649. function is_billable_incident_approved($incidentid)
  650. {
  651. global $db;
  652. $sql = "SELECT DISTINCT origcolref, linkcolref ";
  653. $sql .= "FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t ";
  654. $sql .= "WHERE l.linktype = 6 ";
  655. $sql .= "AND l.origcolref = t.transactionid ";
  656. $sql .= "AND linkcolref = {$incidentid} ";
  657. $sql .= "AND direction = 'left' ";
  658. $sql .= "AND t.transactionstatus = '".BILLING_APPROVED."'";
  659. $result = mysqli_query($db, $sql);
  660. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  661. if (mysqli_num_rows($result) > 0) return TRUE;
  662. else return FALSE;
  663. }
  664. /**
  665. * Gets the transactionID for an incident
  666. * @author paulh Paul Heaney
  667. * @param int $incidentid The incidentID
  668. * @return mixed the transactionID or FALSE if not found;
  669. */
  670. function get_incident_transactionid($incidentid)
  671. {
  672. global $db;
  673. $rtnvalue = FALSE;
  674. $sql = "SELECT origcolref ";
  675. $sql .= "FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t ";
  676. $sql .= "WHERE l.linktype = 6 ";
  677. $sql .= "AND l.origcolref = t.transactionid ";
  678. $sql .= "AND linkcolref = {$incidentid} ";
  679. $sql .= "AND direction = 'left' ";
  680. $result = mysqli_query($db, $sql);
  681. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  682. if (mysqli_num_rows($result) > 0)
  683. {
  684. list($rtnvalue) = mysqli_fetch_row($result);
  685. }
  686. return $rtnvalue;
  687. }
  688. /**
  689. * HTML table showing a summary of current contract service periods
  690. * @author Ivan Lucas
  691. * @param int $contractid. Contract ID of the contract to show service for
  692. * @param bool $billing. Show billing info when TRUE, hide it when FALSE
  693. * @return string. HTML table
  694. */
  695. function contract_service_table($contractid, $billing)
  696. {
  697. global $CONFIG, $dbService, $dbMaintenance, $now, $db;
  698. $sql = "SELECT * FROM `{$dbService}` WHERE contractid = {$contractid} ORDER BY enddate DESC";
  699. $result = mysqli_query($db, $sql);
  700. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  701. if (mysqli_num_rows($result) > 0)
  702. {
  703. $billingObj = get_billable_object_from_contract_id($contractid);
  704. $billingTypeName = $GLOBALS['strNone'];
  705. if ($billing AND ($billingObj instanceof Billable))
  706. {
  707. $billingTypeName = $billingObj->display_name();
  708. }
  709. $html = "<strong>{$GLOBALS['strBilling']}</strong>: {$billingTypeName}";
  710. $shade = 'shade1';
  711. $html .= "\n<table class='maintable' id='contractservicetable'>";
  712. $html .= "<tr>";
  713. if ($billing) $html .= "<th></th>";
  714. $html .= "<th>{$GLOBALS['strStartDate']}</th><th>{$GLOBALS['strEndDate']}</th>";
  715. if ($billing)
  716. {
  717. $html .= "<th>{$GLOBALS['strAvailableBalance']}</th>";
  718. }
  719. $html .= "<th>{$GLOBALS['strActions']}</th>";
  720. $html .= "</tr>\n";
  721. while ($service = mysqli_fetch_object($result))
  722. {
  723. $service->startdate = mysql2date($service->startdate . ' 00:00');
  724. $service->enddate = mysql2date($service->enddate . ' 23:59');
  725. $service->lastbilled = mysql2date($service->lastbilled);
  726. $expired = false;
  727. $future = false;
  728. if ($service->enddate < $now) $expired = true;
  729. if ($service->startdate > $now) $future = true;
  730. if ($future)
  731. {
  732. $shade = 'notice';
  733. }
  734. elseif ($expired)
  735. {
  736. $shade = 'expired';
  737. }
  738. $html .= "<tr class='{$shade}'>";
  739. if ($billing)
  740. {
  741. $balance = get_service_balance($service->serviceid);
  742. $awaitingapproval = service_transaction_total($service->serviceid, BILLING_AWAITINGAPPROVAL) * -1;
  743. $reserved = service_transaction_total($service->serviceid, BILLING_RESERVED) * -1;
  744. $span = "<strong>{$GLOBALS['strServiceID']}:</strong> {$service->serviceid}<br />";
  745. if (!empty($service->title))
  746. {
  747. $span .= "<strong>{$GLOBALS['strTitle']}</strong>: {$service->title}<br />";
  748. }
  749. if (!empty($service->notes))
  750. {
  751. $span .= "<strong>{$GLOBALS['strNotes']}</strong>: {$service->notes}<br />";
  752. }
  753. if (!empty($service->cust_ref))
  754. {
  755. $span .= "<strong>{$GLOBALS['strCustomerReference']}</strong>: {$service->cust_ref}";
  756. if ($service->cust_ref_date != "1970-01-01")
  757. {
  758. $span .= " - <strong>{$GLOBALS['strCustomerReferenceDate']}</strong>: {$service->cust_ref_date}";
  759. }
  760. $span .= "<br />";
  761. }
  762. if ($service->creditamount != 0)
  763. {
  764. $span .= "<strong>{$GLOBALS['strCreditAmount']}</strong>: ".$billingObj->format_amount($service->creditamount)."<br />";
  765. }
  766. if ($service->rate != 0)
  767. {
  768. $span .= "<strong>{$GLOBALS['strUnitRate']}</strong>: ".$billingObj->format_amount($service->rate)."<br />";
  769. }
  770. $sql1 = "SELECT billingmatrix FROM `{$dbMaintenance}` WHERE id = {$contractid}";
  771. $result1 = mysqli_query($db, $sql1);
  772. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  773. $maintenanceobj = mysqli_fetch_object($result1);
  774. if ($billingObj->uses_billing_matrix)
  775. {
  776. $span .= "<strong>{$GLOBALS['strBillingMatrix']}</strong>: {$maintenanceobj->billingmatrix}<br />";
  777. }
  778. if ($balance != $service->balance)
  779. {
  780. $span .= "<strong>{$GLOBALS['strBalance']}</strong>: ".$billingObj->format_amount($service->balance)."<br />";
  781. if ($awaitingapproval != FALSE)
  782. {
  783. $span .= "<strong>{$GLOBALS['strAwaitingApproval']}</strong>: ".$billingObj->format_amount($awaitingapproval)."<br />";
  784. }
  785. if ($reserved != FALSE)
  786. {
  787. $span .= "<strong>{$GLOBALS['strReserved']}</strong>: ".$billingObj->format_amount($reserved)."<br />";
  788. }
  789. $span .= "<strong>{$GLOBALS['strAvailableBalance']}</strong>: ";
  790. if (!$expired)
  791. {
  792. $span .= $billingObj->format_amount($balance);
  793. }
  794. else
  795. {
  796. $span .= $GLOBALS['strExpired'];
  797. }
  798. $span .= "<br />";
  799. }
  800. if ($service->lastbilled > 0)
  801. {
  802. $span .= "<strong>{$GLOBALS['strLastBilled']}</strong>: ".ldate($CONFIG['dateformat_date'], $service->lastbilled)."<br />";
  803. }
  804. if ($service->foc == 'yes')
  805. {
  806. $span .= "<strong>{$GLOBALS['strFreeOfCharge']}</strong>";
  807. }
  808. $html .= "<td><a name='billingicon' class='info'>".icon('billing', 16);
  809. if (!empty($span))
  810. {
  811. $html .= "<span>{$span}</span>";
  812. }
  813. $html .= "</a></td>";
  814. $html .= "<td><a href='transactions.php?serviceid={$service->serviceid}' class='info'>".ldate($CONFIG['dateformat_date'], $service->startdate);
  815. if (!empty($span))
  816. {
  817. $html .= "<span>{$span}</span>";
  818. }
  819. $html .= "</a></td>";
  820. }
  821. else
  822. {
  823. $html .= "<td>".ldate($CONFIG['dateformat_date'], $service->startdate);
  824. $html .= "</td>";
  825. }
  826. $html .= "<td>";
  827. $html .= ldate($CONFIG['dateformat_date'], $service->enddate)."</td>";
  828. if ($billing)
  829. {
  830. $html .= "<td>";
  831. if (!$expired AND ($billingObj instanceof Billable) ) $html .= $billingObj->format_amount($balance);
  832. else $html .= "0";
  833. $html .= "</td>";
  834. }
  835. $html .= "<td>";
  836. $operations[$GLOBALS['strEditService']] = array('url' => "contract_edit_service.php?mode=editservice&amp;serviceid={$service->serviceid}&amp;contractid={$contractid}", 'perm' => PERM_SERVICE_EDIT);
  837. if ($billing)
  838. {
  839. $operations[$GLOBALS['strEditBalance']] = array('url' => "contract_edit_service.php?mode=showform&amp;sourceservice={$service->serviceid}&amp;contractid={$contractid}", 'perm' => PERM_SERVICE_BALANCE_EDIT);
  840. $operations[$GLOBALS['strViewTransactions']] = array('url' => "transactions.php?serviceid={$service->serviceid}", 'perm' => PERM_BILLING_TRANSACTION_VIEW);
  841. }
  842. $html .= html_action_links($operations);
  843. $html .= "</td></tr>\n";
  844. if ($shade == 'shade1') $shade = 'shade2';
  845. else $shade = 'shade1';
  846. }
  847. $html .= "</table>\n";
  848. }
  849. return $html;
  850. }
  851. /**
  852. * Returns the amount of billable units used for a site with the option of filtering by date
  853. * @author Paul Heaney
  854. * @param int $siteid The siteid to report on
  855. * @param int $startdate unixtimestamp on the start date to filter by
  856. * @param int $enddate unixtimestamp on the end date to filter by
  857. * @return String describing billable units used
  858. **/
  859. function amount_used_site($siteid, $startdate=0, $enddate=0)
  860. {
  861. global $db;
  862. $sql = "SELECT i.id, m.billingtype FROM `{$GLOBALS['dbIncidents']}` AS i, `{$GLOBALS['dbMaintenance']}` AS m ";
  863. $sql .= "WHERE m.id = i.maintenanceid AND m.site = {$siteid} ";
  864. if ($startdate != 0)
  865. {
  866. $sql .= "AND i.closed >= {$startdate} ";
  867. }
  868. if ($enddate != 0)
  869. {
  870. $sql .= "AND i.closed <= {$enddate} ";
  871. }
  872. $result = mysqli_query($db, $sql);
  873. if (mysqli_error($db))
  874. {
  875. trigger_error(mysqli_error($db), E_USER_WARNING);
  876. return FALSE;
  877. }
  878. $units = array();
  879. if (mysqli_num_rows($result) > 0)
  880. {
  881. while ($obj = mysqli_fetch_object($result))
  882. {
  883. $billable = get_billable_incident_object($obj->billingtype);
  884. if ($billable instanceof Billable)
  885. {
  886. $units[$obj->billingtype] += $billable->amount_used_incident($obj->id);
  887. }
  888. }
  889. }
  890. $str = '';
  891. foreach ($units AS $var => $val)
  892. {
  893. $str .= "{$var} = {$val}, ";
  894. }
  895. return $str;
  896. }
  897. /**
  898. * @author Ivan Lucas
  899. * @param int $contractid. Contract ID of the contract to show a balance for
  900. * @return int. Number of available units according to the service balances and unit rates
  901. * @todo Check this is correct
  902. **/
  903. function contract_unit_balance($contractid, $includenonapproved = FALSE, $includereserved = TRUE, $showonlycurrentlyvalid = TRUE)
  904. {
  905. $toReturn = FALSE;
  906. $billable = get_billable_object_from_contract_id($contractid);
  907. if ($billable)
  908. {
  909. $toReturn = $billable->contract_unit_balance($contractid, $includenonapproved, $includereserved, $showonlycurrentlyvalid);
  910. }
  911. return $toReturn;
  912. }
  913. /**
  914. * @author Ivan Lucas
  915. * @param int $contractid. Contract ID of the contract to show a balance for
  916. * @return int. Number of available units according to the service balances and unit rates
  917. * @todo Check this is correct
  918. **/
  919. function contract_balance($contractid, $includenonapproved = FALSE, $includereserved = TRUE, $showonlycurrentlyvalid = TRUE)
  920. {
  921. global $now, $dbService, $db;
  922. $unitbalance = 0;
  923. $sql = "SELECT * FROM `{$dbService}` WHERE contractid = {$contractid} ";
  924. if ($showonlycurrentlyvalid)
  925. {
  926. $date = ldate('Y-m-d', $now);
  927. $sql .= "AND '{$date}' BETWEEN startdate AND enddate ";
  928. }
  929. $sql .= "ORDER BY enddate DESC";
  930. $result = mysqli_query($db, $sql);
  931. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  932. if (mysqli_num_rows($result) > 0)
  933. {
  934. while ($service = mysqli_fetch_object($result))
  935. {
  936. $balance += round($service->balance);
  937. }
  938. }
  939. if ($includenonapproved)
  940. {
  941. $awaiting = contract_transaction_total($contractid, BILLING_AWAITINGAPPROVAL);
  942. if ($awaiting != 0) $balance += round($awaiting);
  943. }
  944. if ($includereserved)
  945. {
  946. $reserved = contract_transaction_total($contractid, BILLING_RESERVED);
  947. if ($reserved != 0) $balance += round($reserved);
  948. }
  949. return $balance;
  950. }
  951. /**
  952. * Function to display/generate the transactions table
  953. * @author Paul Heaney
  954. * @param int $serviceid - The service ID to show transactons for
  955. * @param Date $startdate - Date in format yyyy-mm-dd when you want to start the report from
  956. * @param Date $enddate - Date in format yyyy-mm-dd when you want to end the report, empty means today
  957. * @param int[] $sites - Array of sites to report on
  958. * @param String $display either csv or html
  959. * @param boolean $sitebreakdown - Breakdown per site
  960. * @param boolean $showfoc - Show free of charge as well (defaults to true);
  961. * @param boolean $includeawaitingapproval - Include transactions awaiting approval
  962. * @param boolean $includereserved - Include reserved transactions
  963. * @return String -either HTML or CSV
  964. */
  965. function transactions_report($serviceid, $startdate, $enddate, $sites, $display, $sitebreakdown=TRUE, $showfoc=TRUE, $focaszero=FALSE, $includeawaitingapproval = TRUE, $includereserved = TRUE)
  966. {
  967. global $CONFIG, $db;
  968. $csv_currency = html_entity_decode($CONFIG['currency_symbol'], ENT_NOQUOTES);
  969. $sql = "SELECT DISTINCT t.*, m.site, m.billingtype, p.foc, p.cust_ref, p.cust_ref_date, p.title, p.notes ";
  970. $sql .= "FROM `{$GLOBALS['dbTransactions']}` AS t, `{$GLOBALS['dbService']}` AS p, ";
  971. $sql .= "`{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl, `{$GLOBALS['dbSites']}` AS s ";
  972. $sql .= "WHERE t.serviceid = p.serviceid AND p.contractid = m.id "; // AND t.date <= '{$enddateorig}' ";
  973. $sql .= "AND m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = s.id ";
  974. //// $sql .= "AND t.date > p.lastbilled AND m.site = {$objsite->site} ";
  975. if ($serviceid > 0) $sql .= "AND t.serviceid = {$serviceid} ";
  976. if (!empty($startdate)) $sql .= "AND t.dateupdated >= '{$startdate}' ";
  977. if (!empty($enddate)) $sql .= "AND t.dateupdated <= '{$enddate}' ";
  978. $orsql[] = "t.transactionstatus = ".BILLING_APPROVED;
  979. if ($includeawaitingapproval) $orsql[] = "t.transactionstatus = ".BILLING_AWAITINGAPPROVAL;
  980. if ($includereserved) $orsql[] = "t.transactionstatus = ".BILLING_RESERVED;
  981. $o = implode(" OR ", $orsql);
  982. $sql .= "AND ($o) ";
  983. if (!$showfoc) $sql .= "AND p.foc = 'no' ";
  984. if (!empty($sites))
  985. {
  986. $sitestr = '';
  987. foreach ($sites AS $s)
  988. {
  989. $s = clean_int($s);
  990. if (empty($sitestr)) $sitestr .= "m.site = {$s} ";
  991. else $sitestr .= "OR m.site = {$s} ";
  992. }
  993. $sql .= "AND {$sitestr} ";
  994. }
  995. if (!empty($site)) $sql .= "AND m.site = {$site} ";
  996. $sql .= "ORDER BY t.dateupdated, s.name ";
  997. $result = mysqli_query($db, $sql);
  998. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_WARNING);
  999. if (mysqli_num_rows($result) > 0)
  1000. {
  1001. $shade = 'shade1';
  1002. $total = 0;
  1003. $totalcredit = 0;
  1004. $totaldebit = 0;
  1005. $details = '';
  1006. while ($transaction = mysqli_fetch_object($result))
  1007. {
  1008. $billingObj = new $transaction->billingtype();
  1009. if ($display == 'html')
  1010. {
  1011. if ($serviceid > 0 AND empty($details))
  1012. {
  1013. if (!empty($transaction->cust_ref))
  1014. {
  1015. $details .= "<tr>";
  1016. $details .= "<th>{$GLOBALS['strCustomerReference']}</th><td>{$transaction->cust_ref}</td>";
  1017. if ($transaction->cust_ref_date != "1970-01-01")
  1018. {
  1019. $details .= "<th>{$GLOBALS['strCustomerReferenceDate']}</th><td>{$transaction->cust_ref_date}</td>";
  1020. }
  1021. $details .= "</tr>";
  1022. }
  1023. if (!empty($transaction->title))
  1024. {
  1025. $details .= "<tr><th>{$GLOBALS['strTitle']}</th><td>{$transaction->title}</td></tr>";
  1026. }
  1027. if (!empty($transaction->notes))
  1028. {
  1029. $details .= "<tr><th>{$GLOBALS['strNotes']}</th><td>{$transaction->notes}</td></tr>";
  1030. }
  1031. }
  1032. $str = "<tr class='$shade'>";
  1033. $str .= "<td>" . date($CONFIG['dateformat_datetime'], mysql2date($transaction->dateupdated)) . "</td>";
  1034. $str .= "<td>{$transaction->transactionid}</td>";
  1035. $str .= "<td>{$transaction->serviceid}</td>";
  1036. $str .= "<td>".site_name($transaction->site)."</td>";
  1037. $str .= "<td>{$transaction->description}</td>";
  1038. $str .= "<td>";
  1039. switch ($transaction->transactionstatus)
  1040. {
  1041. case BILLING_APPROVED: $str .= $GLOBALS['strApproved'];
  1042. break;
  1043. case BILLING_AWAITINGAPPROVAL: $str .= $GLOBALS['strAwaitingApproval'];
  1044. break;
  1045. case BILLING_RESERVED: $str .= $GLOBALS['strReserved'];
  1046. break;
  1047. }
  1048. $str .= "</td>";
  1049. }
  1050. elseif ($display == 'csv')
  1051. {
  1052. if ($serviceid > 0 AND empty($details))
  1053. {
  1054. if (!empty($transaction->cust_ref))
  1055. {
  1056. $details .= "\"{$GLOBALS['strCustomerReference']}\",\"{$transaction->cust_ref}\",";
  1057. if ($transaction->cust_ref_date != "1970-01-01")
  1058. {
  1059. $details .= "\"{$GLOBALS['strCustomerReferenceDate']}\",\"{$transaction->cust_ref_date}\",";
  1060. }
  1061. $details .= "\n";
  1062. }
  1063. if (!empty($transaction->title))
  1064. {
  1065. $details .= "\"{$GLOBALS['strTitle']}\",\"{$transaction->title}\"\n";
  1066. }
  1067. if (!empty($transaction->notes))
  1068. {
  1069. $details .= "\"{$GLOBALS['strNotes']}\",\"{$transaction->notes}\"\n";
  1070. }
  1071. }
  1072. $str = "\"" . date($CONFIG['dateformat_datetime'], mysql2date($transaction->dateupdated)) . "\",";
  1073. $str .= "\"{$transaction->transactionid}\",";
  1074. $str .= "\"{$transaction->serviceid}\",\"";
  1075. $str .= site_name($transaction->site)."\",";
  1076. $str .= "\"".html_entity_decode($transaction->description)."\",";
  1077. $str .= "\"";
  1078. switch ($transaction->transactionstatus)
  1079. {
  1080. case BILLING_APPROVED:
  1081. $str .= $GLOBALS['strApproved'];
  1082. break;
  1083. case BILLING_AWAITINGAPPROVAL:
  1084. $str .= $GLOBALS['strAwaitingApproval'];
  1085. break;
  1086. case BILLING_RESERVED:
  1087. $str .= $GLOBALS['strReserved'];
  1088. break;
  1089. }
  1090. $str .= "\",";
  1091. }
  1092. if ($focaszero AND $transaction->foc == 'yes')
  1093. {
  1094. $transaction->amount = 0;
  1095. }
  1096. $total += $transaction->amount;
  1097. if ($transaction->amount < 0)
  1098. {
  1099. $totaldebit += $transaction->amount;
  1100. if ($display == 'html')
  1101. {
  1102. $str .= "<td></td><td>".$billingObj->format_amount(number_format($transaction->amount, 2))."</td>";
  1103. }
  1104. elseif ($display == 'csv')
  1105. {
  1106. $str .= ",\"".$billingObj->format_amount(number_format($transaction->amount, 2))."\",";
  1107. }
  1108. }
  1109. else
  1110. {
  1111. $totalcredit += $transaction->amount;
  1112. if ($display == 'html')
  1113. {
  1114. $str .= "<td>".$billingObj->format_amount(number_format($transaction->amount, 2))."</td><td></td>";
  1115. }
  1116. elseif ($display == 'csv')
  1117. {
  1118. $str .= "\"".$billingObj->format_amount(number_format($transaction->amount, 2))."\",,";
  1119. }
  1120. }
  1121. if ($display == 'html') $str .= "</tr>";
  1122. elseif ($display == 'csv') $str .= "\n";
  1123. if ($sitebreakdown == TRUE)
  1124. {
  1125. $table[$transaction->site]['site'] = site_name($transaction->site);
  1126. $table[$transaction->site]['str'] .= $str;
  1127. if ($transaction->amount < 0)
  1128. {
  1129. $table[$transaction->site]['debit'] += $transaction->amount;
  1130. }
  1131. else
  1132. {
  1133. $table[$transaction->site]['credit'] += $transaction->amount;
  1134. }
  1135. }
  1136. else
  1137. {
  1138. $table .= $str;
  1139. }
  1140. if ($shade == 'shade1') $shade = 'shade2';
  1141. else $shade = 'shade1';
  1142. }
  1143. if ($sitebreakdown == TRUE)
  1144. {
  1145. foreach ($table AS $e)
  1146. {
  1147. if ($display == 'html')
  1148. {
  1149. $text .= "<h3>{$e['site']}</h3>";
  1150. $text .= "<table align='center' width='60%'>";
  1151. //echo "<tr><th colspan='7'>{$e['site']}</th></tr>";
  1152. $text .= "<tr><th>{$GLOBALS['strDate']}</th><th>{$GLOBALS['strID']}</th><th>{$GLOBALS['strServiceID']}</th>";
  1153. $text .= "<th>{$GLOBALS['strSite']}</th><th>{$GLOBALS['strDescription']}</th><th>{$GLOBALS['strCredit']}</th><th>{$GLOBALS['strDebit']}</th></tr>";
  1154. $text .= $e['str'];
  1155. $text .= "<tr><td colspan='5' align='right'>{$GLOBALS['strTotal']}</td>";
  1156. $text .= "<td>{$CONFIG['currency_symbol']}".number_format($e['credit'], 2)."</td>";
  1157. $text .= "<td>{$CONFIG['currency_symbol']}".number_format($e['debit'], 2)."</td></tr>";
  1158. $text .= "</table>";
  1159. }
  1160. elseif ($display == 'csv')
  1161. {
  1162. $text .= "\"{$e['site']}\"\n\n";
  1163. $text .= "\"{$GLOBALS['strDate']}\",\"{$GLOBALS['strID']}\",\"{$GLOBALS['strServiceID']}\",";
  1164. $text .= "\"{$GLOBALS['strSite']}\",\"{$GLOBALS['strDescription']}\",\"{$GLOBALS['strCredit']}\",\"{$GLOBALS['strDebit']}\"\n";
  1165. $text .= $e['str'];
  1166. $text .= ",,,,{$GLOBALS['strTotal']},";
  1167. $text .= "\"{$csv_currency}".number_format($e['credit'], 2)."\",\"";
  1168. $text .="{$csv_currency}".number_format($e['debit'], 2)."\"\n";
  1169. }
  1170. }
  1171. }
  1172. else
  1173. {
  1174. if ($display == 'html')
  1175. {
  1176. if (!empty($details))
  1177. {
  1178. // Dont need to worry about this in the above section as sitebreakdown and serviceid are multually exclusive
  1179. $text .= "<div><table class='maintable'>{$details}</table></div>";
  1180. }
  1181. $text .= "<table class='maintable'>";
  1182. $text .= "<tr><th>{$GLOBALS['strDate']}</th><th>{$GLOBALS['strID']}</th><th>{$GLOBALS['strServiceID']}</th>";
  1183. $text .= "<th>{$GLOBALS['strSite']}</th>";
  1184. $text .= "<th>{$GLOBALS['strDescription']}</th><th>{$GLOBALS['strStatus']}</th><th>{$GLOBALS['strCredit']}</th…

Large files files are truncated, but you can click here to view the full file