|
Support Incident Tracker GIT4.x
|
00001 <?php 00002 // billing.inc.php - functions relating to billing 00003 // 00004 // SiT (Support Incident Tracker) - Support call tracking system 00005 // Copyright (C) 2010-2011 The Support Incident Tracker Project 00006 // Copyright (C) 2000-2009 Salford Software Ltd. and Contributors 00007 // 00008 // This software may be used and distributed according to the terms 00009 // of the GNU General Public License, incorporated herein by reference. 00010 00011 // Prevent script from being run directly (ie. it must always be included 00012 if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) 00013 { 00014 exit; 00015 } 00016 00017 define ("BILLING_APPROVED", 0); 00018 define ("BILLING_AWAITINGAPPROVAL", 5); 00019 define ("BILLING_RESERVED", 10); 00020 00027 function does_contact_have_billable_contract($contactid) 00028 { 00029 global $now; 00030 $return = NO_BILLABLE_CONTRACT; 00031 00032 $siteid = contact_siteid($contactid); 00033 $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl "; 00034 $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} "; 00035 $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'"; 00036 $result = mysql_query($sql); 00037 00038 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 00039 00040 if (mysql_num_rows($result) > 0) 00041 { 00042 // We have some billable/timed contracts 00043 $return = SITE_HAS_BILLABLE_CONTRACT; 00044 00045 // check if the contact is listed on one of these 00046 00047 while ($obj = mysql_fetch_object($result)) 00048 { 00049 $sqlcontact = "SELECT * FROM `{$GLOBALS['dbSupportContacts']}` "; 00050 $sqlcontact .= "WHERE maintenanceid = {$obj->id} AND contactid = {$contactid}"; 00051 00052 $resultcontact = mysql_query($sqlcontact); 00053 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_ERROR); 00054 00055 if (mysql_num_rows($resultcontact) > 0) 00056 { 00057 $return = CONTACT_HAS_BILLABLE_CONTRACT; 00058 break; 00059 } 00060 } 00061 } 00062 00063 return $return; 00064 } 00065 00066 00073 function get_billable_contract_id($contactid) 00074 { 00075 global $now; 00076 00077 $return = -1; 00078 00079 $siteid = contact_siteid($contactid); 00080 $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl "; 00081 $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} "; 00082 $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'"; 00083 00084 $result = mysql_query($sql); 00085 00086 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 00087 00088 if (mysql_num_rows($result) > 0) 00089 { 00090 $return = mysql_fetch_object($result)->id; 00091 } 00092 00093 return $return; 00094 } 00095 00096 00103 function get_site_billable_contract_id($siteid) 00104 { 00105 global $now; 00106 00107 $return = -1; 00108 00109 $sql = "SELECT DISTINCT m.id FROM `{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl "; 00110 $sql .= "WHERE m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = {$siteid} "; 00111 $sql .= "AND m.expirydate > {$now} AND m.term != 'yes'"; 00112 00113 $result = mysql_query($sql); 00114 00115 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 00116 00117 if (mysql_num_rows($result) > 0) 00118 { 00119 $return = mysql_fetch_object($result)->id; 00120 } 00121 00122 return $return; 00123 } 00124 00125 00132 function get_service_percentage($maintid) 00133 { 00134 global $dbService; 00135 00136 $sql = "SELECT * FROM `{$dbService}` "; 00137 $sql .= "WHERE contractid = {$maintid}"; 00138 $result = mysql_query($sql); 00139 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 00140 00141 if (mysql_num_rows($result) > 0) 00142 { 00143 $num = 0; 00144 while ($service = mysql_fetch_object($result)) 00145 { 00146 $total += (float) $service->balance / (float) $service->creditamount; 00147 $num++; 00148 } 00149 $return = (float) $total / (float) $num; 00150 } 00151 else 00152 { 00153 $return = FALSE; 00154 } 00155 00156 return $return; 00157 } 00158 00159 00167 function is_contract_timed($contractid) 00168 { 00169 global $dbMaintenance, $dbServiceLevels; 00170 $timed = FALSE; 00171 $sql = "SELECT timed FROM `{$dbMaintenance}` AS m, `{$dbServiceLevels}` AS sl "; 00172 $sql .= "WHERE m.servicelevel = sl.tag AND m.id = {$contractid}"; 00173 $result = mysql_query($sql); 00174 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 00175 00176 list($timed) = mysql_fetch_row($result); 00177 if ($timed == 'yes') 00178 { 00179 return TRUE; 00180 } 00181 else 00182 { 00183 return FALSE; 00184 } 00185 } 00186 00187 00194 function update_last_billed_time($serviceid, $date) 00195 { 00196 global $dbService; 00197 00198 $rtnvalue = FALSE; 00199 00200 if (!empty($serviceid) AND !empty($date)) 00201 { 00202 $rtnvalue = TRUE; 00203 $sql .= "UPDATE `{$dbService}` SET lastbilled = '{$date}' WHERE serviceid = {$serviceid}"; 00204 mysql_query($sql); 00205 if (mysql_error()) 00206 { 00207 trigger_error(mysql_error(), E_USER_ERROR); 00208 $rtnvalue = FALSE; 00209 } 00210 00211 if (mysql_affected_rows() < 1) 00212 { 00213 trigger_error("Approval failed", E_USER_ERROR); 00214 $rtnvalue = FALSE; 00215 } 00216 } 00217 00218 return $rtnvalue; 00219 } 00220 00221 00230 function get_billable_multiplier($dayofweek, $hour, $billingmatrix = 'Default') 00231 { 00232 $sql = "SELECT `{$dayofweek}` AS rate FROM {$GLOBALS['dbBillingMatrix']} WHERE hour = {$hour} AND tag = '{$billingmatrix}'"; 00233 00234 $result = mysql_query($sql); 00235 if (mysql_error()) 00236 { 00237 trigger_error(mysql_error(),E_USER_WARNING); 00238 return FALSE; 00239 } 00240 00241 $rate = 1; 00242 00243 if (mysql_num_rows($result) > 0) 00244 { 00245 $obj = mysql_fetch_object($result); 00246 $rate = $obj->rate; 00247 } 00248 00249 return $rate; 00250 } 00251 00252 00259 function get_all_available_multipliers($matrixid='') 00260 { 00261 $days = array('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'holiday'); 00262 00263 foreach ($days AS $d) 00264 { 00265 $sql = "SELECT DISTINCT({$d}) AS day FROM `{$GLOBALS['dbBillingMatrix']}` "; 00266 if (!empty($matrixid)) $sql .= " WHERE tag = '{$matrixid}'"; 00267 $result = mysql_query($sql); 00268 if (mysql_error()) 00269 { 00270 trigger_error(mysql_error(),E_USER_WARNING); 00271 return FALSE; 00272 } 00273 00274 while ($obj = mysql_fetch_object($result)) 00275 { 00276 $a[$obj->day] = $obj->day; 00277 } 00278 } 00279 00280 ksort($a); 00281 00282 return $a; 00283 } 00284 00285 00293 function get_unit_rate($contractid, $date='') 00294 { 00295 $serviceid = get_serviceid($contractid, $date); 00296 00297 if ($serviceid != -1) 00298 { 00299 $unitrate = get_service_unitrate($serviceid); 00300 } 00301 else 00302 { 00303 $unitrate = -1; 00304 } 00305 00306 return $unitrate; 00307 } 00308 00309 00316 function get_service_unitrate($serviceid) 00317 { 00318 $rtnvalue = FALSE; 00319 $sql = "SELECT unitrate FROM `{$GLOBALS['dbService']}` AS p WHERE serviceid = {$serviceid}"; 00320 00321 $result = mysql_query($sql); 00322 if (mysql_error()) 00323 { 00324 trigger_error(mysql_error(),E_USER_WARNING); 00325 return FALSE; 00326 } 00327 00328 if (mysql_num_rows($result) > 0) 00329 { 00330 list($rtnvalue) = mysql_fetch_row($result); 00331 } 00332 00333 return $rtnvalue; 00334 } 00335 00336 00343 function get_service_incidentrate($serviceid) 00344 { 00345 $rtnvalue = FALSE; 00346 $sql = "SELECT incidentrate FROM `{$GLOBALS['dbService']}` AS p WHERE serviceid = {$serviceid}"; 00347 00348 $result = mysql_query($sql); 00349 if (mysql_error()) 00350 { 00351 trigger_error(mysql_error(),E_USER_WARNING); 00352 return FALSE; 00353 } 00354 00355 if (mysql_num_rows($result) > 0) 00356 { 00357 list($rtnvalue) = mysql_fetch_row($result); 00358 } 00359 00360 return $rtnvalue; 00361 } 00362 00363 00370 function get_serviceid($contractid, $date = '') 00371 { 00372 global $now, $CONFIG; 00373 if (empty($date)) $date = $now; 00374 00375 $sql = "SELECT serviceid FROM `{$GLOBALS['dbService']}` AS s "; 00376 $sql .= "WHERE contractid = {$contractid} AND UNIX_TIMESTAMP(startdate) <= {$date} "; 00377 $sql .= "AND UNIX_TIMESTAMP(enddate) > {$date} "; 00378 $sql .= "AND (balance > 0 OR (select count(1) FROM `{$GLOBALS['dbService']}` WHERE contractid = s.contractid AND balance > 0) = 0) "; 00379 00380 if (!$CONFIG['billing_allow_incident_approval_against_overdrawn_service']) 00381 { 00382 $sql .= "AND balance > 0 "; 00383 } 00384 00385 $sql .= "ORDER BY priority DESC, enddate ASC, balance DESC LIMIT 1"; 00386 00387 $result = mysql_query($sql); 00388 if (mysql_error()) 00389 { 00390 trigger_error(mysql_error(),E_USER_WARNING); 00391 return FALSE; 00392 } 00393 00394 $serviceid = -1; 00395 00396 if (mysql_num_rows($result) > 0) 00397 { 00398 list($serviceid) = mysql_fetch_row($result); 00399 } 00400 00401 return $serviceid; 00402 } 00403 00404 00417 function get_contract_balance($contractid, $includenonapproved = FALSE, $showonlycurrentlyvalid = TRUE, $includereserved = TRUE) 00418 { 00419 global $dbService, $now; 00420 $balance = 0.00; 00421 00422 $sql = "SELECT SUM(balance) FROM `{$dbService}` "; 00423 $sql .= "WHERE contractid = {$contractid} "; 00424 if ($showonlycurrentlyvalid) 00425 { 00426 $sql .= "AND UNIX_TIMESTAMP(startdate) <= {$now} "; 00427 $sql .= "AND UNIX_TIMESTAMP(enddate) >= {$now} "; 00428 } 00429 $result = mysql_query($sql); 00430 if (mysql_error()) trigger_error(mysql_error(), E_USER_WARNING); 00431 list($balance) = mysql_fetch_row($result); 00432 00433 if ($includenonapproved) 00434 { 00435 // Need to get sum of non approved incidents for this contract and deduct 00436 $balance += contract_transaction_total($contractid, BILLING_AWAITINGAPPROVAL); 00437 } 00438 00439 if ($includereserved) 00440 { 00441 $balance += contract_transaction_total($contractid, BILLING_RESERVED); 00442 } 00443 00444 return $balance; 00445 } 00446 00447 00454 function get_overdraft($contractid) 00455 { 00456 $rtnvalue = FALSE; 00457 $sql = "SELECT DISTINCT sl.tag FROM `{$GLOBALS['dbServiceLevels']}` AS sl, `{$GLOBALS['dbMaintenance']}` AS m "; 00458 $sql .= "WHERE m.servicelevel = sl.tag AND m.id = {$contractid}"; 00459 $result = mysql_query($sql); 00460 if (mysql_error()) trigger_error("Error getting servicelevel details. ".mysql_error(), E_USER_WARNING); 00461 00462 if (mysql_num_rows($result) == 1) 00463 { 00464 list($tag) = mysql_fetch_row($result); 00465 $sql = "SELECT DISTINCT limit FROM `{$GLOBALS['dbBillingPeriods']}` "; 00466 $sql .= "WHERE AND tag = '{$tag}'"; 00467 $result = mysql_query($sql); 00468 if (mysql_error()) trigger_error("Error getting servicelevel details. ".mysql_error(), E_USER_WARNING); 00469 if (mysql_num_rows($result) == 1) 00470 { 00471 list($rtnvalue) = mysql_fetch_row($result); 00472 } 00473 } 00474 00475 return $rtnvalue; 00476 } 00477 00478 00489 function reserve_monies($serviceid, $linktype, $linkref, $amount, $description) 00490 { 00491 global $now, $sit; 00492 $rtnvalue = FALSE; 00493 $balance = get_service_balance($serviceid, TRUE, TRUE); 00494 // TODO take into account overdraft limit 00495 00496 $amount *= -1; 00497 00498 if ($balance != FALSE) 00499 { 00500 $sql = "INSERT INTO `{$GLOBALS['dbTransactions']}` (serviceid, amount, description, userid, dateupdated, transactionstatus) "; 00501 $sql .= "VALUES ('{$serviceid}', '{$amount}', '{$description}', '{$_SESSION['userid']}', '".date('Y-m-d H:i:s', $now)."', '".BILLING_RESERVED."')"; 00502 $result = mysql_query($sql); 00503 if (mysql_error()) 00504 { 00505 trigger_error("Error inserting transaction. ".mysql_error(), E_USER_WARNING); 00506 $rtnvalue = FALSE; 00507 } 00508 00509 $rtnvalue = mysql_insert_id(); 00510 00511 if ($rtnvalue != FALSE) 00512 { 00513 00514 $sql = "INSERT INTO `{$GLOBALS['dbLinks']}` VALUES ({$linktype}, {$rtnvalue}, {$linkref}, 'left', '{$_SESSION['userid']}')"; 00515 mysql_query($sql); 00516 if (mysql_error()) 00517 { 00518 trigger_error(mysql_error(),E_USER_ERROR); 00519 $rtnvalue = FALSE; 00520 } 00521 if (mysql_affected_rows() < 1) 00522 { 00523 trigger_error("Link reservation failed",E_USER_ERROR); 00524 $rtnvalue = FALSE; 00525 } 00526 } 00527 } 00528 00529 return $rtnvalue; 00530 } 00531 00532 00541 function transition_reserved_monites($transactionid, $amount, $description='') 00542 { 00543 $rtnvalue = TRUE; 00544 $sql = "UPDATE `{$GLOBALS['dbTransactions']}` SET amount = {$amount}, transactionstatus = ".BILLING_AWAITINGAPPROVAL." "; 00545 if (!empty($description)) 00546 { 00547 $sql .= ", description = '{$description}' "; 00548 } 00549 $sql .= "WHERE transactionid = {$transactionid} AND transactionstatus = ".BILLING_RESERVED; 00550 mysql_query($sql); 00551 00552 if (mysql_error()) 00553 { 00554 trigger_error(mysql_error(), E_USER_ERROR); 00555 $rtnvalue = FALSE; 00556 } 00557 if (mysql_affected_rows() < 1) 00558 { 00559 trigger_error("Transition reserved monies failed {$sql}",E_USER_ERROR); 00560 $rtnvalue = FALSE; 00561 } 00562 00563 return $rtnvalue; 00564 } 00565 00566 00573 function unreserve_monies($transactionid, $linktype) 00574 { 00575 $rtnvalue = FALSE; 00576 $sql = "DELETE FROM `{$GLOBALS['dbTransactions']}` WHERE transactionid = {$transactionid} AND transactionstatus = ".BILLING_RESERVED; 00577 mysql_query($sql); 00578 00579 if (mysql_error()) trigger_error("Error unreserving monies ".mysql_error(), E_USER_ERROR); 00580 if (mysql_affected_rows() == 1) $rtnvalue = TRUE; 00581 00582 if ($rtnvalue != FALSE) 00583 { 00584 $sql = "DELETE FROM `{$GLOBALS['dbLinks']}` WHERE linktype = {$linktype} AND origcolref = {$transactionid}"; 00585 mysql_query($sql); 00586 if (mysql_error()) 00587 { 00588 trigger_error(mysql_error(),E_USER_ERROR); 00589 $rtnvalue = FALSE; 00590 } 00591 if (mysql_affected_rows() < 1) 00592 { 00593 trigger_error("Link deletion failed",E_USER_ERROR); 00594 $rtnvalue = FALSE; 00595 } 00596 } 00597 00598 return $rtnvalue; 00599 } 00600 00601 00610 function update_reservation($transactionid, $amount, $description='') 00611 { 00612 return update_transaction($transactionid, $amount, $description, BILLING_RESERVED); 00613 } 00614 00615 00625 function update_transaction($transactionid, $amount, $description='', $status) 00626 { 00627 if ($status == BILLING_APPROVED) 00628 { 00629 trigger_error("You cant change a approved transaction", E_USER_ERROR); 00630 exit; 00631 } 00632 00633 $rtnvalue = FALSE; 00634 // Note we dont need to check its awaiting reservation as we check this when doing the update 00635 if (is_numeric($transactionid)) 00636 { 00637 $sql = "UPDATE `{$GLOBALS['dbTransactions']}` SET amount = '{$amount}' "; 00638 if (!empty($description)) 00639 { 00640 $sql .= ", description = '{$description}' "; 00641 } 00642 $sql .= "WHERE transactionid = {$transactionid} AND transactionstatus = {$status}"; 00643 mysql_query($sql); 00644 if (mysql_error()) 00645 { 00646 trigger_error(mysql_error(),E_USER_ERROR); 00647 $rtnvalue = FALSE; 00648 } 00649 if (mysql_affected_rows() > 0) 00650 { 00651 $rtnvalue = TRUE; 00652 } 00653 } 00654 00655 return $rtnvalue; 00656 } 00657 00658 00665 function close_billable_incident($incidentid) 00666 { 00667 global $now, $sit; 00668 $rtnvalue = TRUE; 00669 $sql = "SELECT i.maintenanceid FROM `{$GLOBALS['dbIncidents']}` AS i, `{$GLOBALS['dbServiceLevels']}` AS sl "; 00670 $sql .= "WHERE i.servicelevel = sl.tag AND i.priority = sl.priority AND i.id = {$incidentid} AND sl.timed = 'yes'"; 00671 $result = mysql_query($sql); 00672 if (mysql_error()) 00673 { 00674 trigger_error("Error identifying if incident was timed ".mysql_error(), E_USER_WARNING); 00675 $rtnvalue = FALSE; 00676 } 00677 00678 if (mysql_num_rows($result) > 0) 00679 { 00680 // Was logged against a timed contract 00681 list($contractid) = mysql_fetch_row($result); 00682 $duration = 0; 00683 $sql = "SELECT SUM(duration) FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid = {$incidentid}"; 00684 $result = mysql_query($sql); 00685 if (mysql_error()) 00686 { 00687 trigger_error("Error getting duration for billable incident. ".mysql_error(), E_USER_WARNING); 00688 $rtnvalue = FALSE; 00689 } 00690 list($duration) = mysql_fetch_row($result); 00691 if ($duration > 0) 00692 { 00693 // There where activities on this update so add to the transactions table 00694 00695 $bills = get_incident_billable_breakdown_array($incidentid); 00696 00697 $multipliers = get_all_available_multipliers(); 00698 00699 $totalunits = 0; 00700 $totalbillableunits = 0; 00701 $totalrefunds = 0; 00702 00703 foreach ($bills AS $bill) 00704 { 00705 foreach ($multipliers AS $m) 00706 { 00707 $a[$m] += $bill[$m]['count']; 00708 } 00709 } 00710 00711 foreach ($multipliers AS $m) 00712 { 00713 $s .= sprintf($GLOBALS['strXUnitsAtX'], $a[$m], $m); 00714 $totalunits += $a[$m]; 00715 $totalbillableunits += ($m * $a[$m]); 00716 } 00717 00718 $unitrate = get_unit_rate(incident_maintid($incidentid)); 00719 00720 $totalrefunds = $bills['refunds']; 00721 // $numberofunits += $bills['refunds']; 00722 00723 $cost = (($totalbillableunits + $totalrefunds) * $unitrate) * -1; 00724 00725 $desc = trim("{$numberofunits} {$strUnits} @ {$CONFIG['currency_symbol']}{$unitrate} for incident {$incidentid}. {$s}"); //FIXME i18n 00726 00727 // $rtn = update_contract_balance(incident_maintid($incidentid), $desc, $cost); 00728 00729 // Add transaction 00730 $serviceid = get_serviceid($contractid); 00731 if ($serviceid < 1) trigger_error("Invalid service ID",E_USER_ERROR); 00732 $date = date('Y-m-d H:i:s', $now); 00733 00734 $sql = "INSERT INTO `{$GLOBALS['dbTransactions']}` (serviceid, totalunits, totalbillableunits, totalrefunds, amount, description, userid, dateupdated, transactionstatus) "; 00735 $sql .= "VALUES ('{$serviceid}', '{$totalunits}', '{$totalbillableunits}', '{$totalrefunds}', '{$cost}', '{$desc}', '{$_SESSION['userid']}', '{$date}', '".BILLING_AWAITINGAPPROVAL."')"; 00736 00737 $result = mysql_query($sql); 00738 if (mysql_error()) 00739 { 00740 trigger_error("Error inserting transaction. ".mysql_error(), E_USER_WARNING); 00741 $rtnvalue = FALSE; 00742 } 00743 00744 $transactionid = mysql_insert_id(); 00745 00746 if ($transactionid != FALSE) 00747 { 00748 00749 $sql = "INSERT INTO `{$GLOBALS['dbLinks']}` VALUES (6, {$transactionid}, {$incidentid}, 'left', {$_SESSION['userid']})"; 00750 mysql_query($sql); 00751 if (mysql_error()) 00752 { 00753 trigger_error(mysql_error(),E_USER_ERROR); 00754 $rtnvalue = FALSE; 00755 } 00756 if (mysql_affected_rows() < 1) 00757 { 00758 trigger_error("Link transaction on closure failed",E_USER_ERROR); 00759 $rtnvalue = FALSE; 00760 } 00761 } 00762 00763 } 00764 } 00765 00766 return $rtnvalue; 00767 } 00768 00769 00775 function approve_incident_transaction($transactionid) 00776 { 00777 global $dbLinks, $sit, $CONFIG, $strUnits; 00778 00779 $rtnvalue = TRUE; 00780 00781 // Check transaction exists, and is awaiting approval and is an incident 00782 $sql = "SELECT l.linkcolref, t.serviceid FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t "; 00783 $sql .= "WHERE t.transactionid = l.origcolref AND t.transactionstatus = ".BILLING_AWAITINGAPPROVAL." AND l.linktype = 6 AND t.transactionid = {$transactionid}"; 00784 $result = mysql_query($sql); 00785 if (mysql_error()) trigger_error("Error identify incident transaction. ".mysql_error(), E_USER_WARNING); 00786 if (mysql_num_rows($result) > 0) 00787 { 00788 list($incidentid, $serviceid) = mysql_fetch_row($result); 00789 00790 $bills = get_incident_billable_breakdown_array($incidentid); 00791 00792 $multipliers = get_all_available_multipliers(); 00793 00794 $totalunits = 0; 00795 $totalbillableunits = 0; 00796 $totalrefunds = 0; 00797 00798 foreach ($bills AS $bill) 00799 { 00800 foreach ($multipliers AS $m) 00801 { 00802 $a[$m] += $bill[$m]['count']; 00803 } 00804 } 00805 00806 foreach ($multipliers AS $m) 00807 { 00808 $s .= sprintf($GLOBALS['strXUnitsAtX'], $a[$m], $m); 00809 $totalbillableunits += ($m * $a[$m]); 00810 $totalunits += $a[$m]; 00811 } 00812 00813 $unitrate = get_unit_rate(incident_maintid($incidentid)); 00814 00815 $totalrefunds += $bills['refunds']; 00816 00817 $cost = (($totalbillableunits += $totalrefunds) * $unitrate) * -1; 00818 00819 $desc = trim("Incident {$incidentid}: {$totalbillableunits}: {$strUnits} @ {$CONFIG['currency_symbol']}{$unitrate}. {$s}"); //FIXME i18n 00820 00821 $rtn = update_contract_balance(incident_maintid($incidentid), $desc, $cost, $serviceid, $transactionid, $totalunits, $totalbillableunits, $totalrefunds); 00822 00823 if ($rtn == FALSE) 00824 { 00825 $rtnvalue = FALSE; 00826 } 00827 } 00828 else 00829 { 00830 $rtnvalue = FALSE; 00831 } 00832 00833 return $rtnvalue; 00834 } 00835 00836 00852 function update_contract_balance($contractid, $description, $amount, $serviceid='', $transactionid='', $totalunits=0, $totalbillableunits=0, $totalrefunds=0) 00853 { 00854 global $now, $dbService, $dbTransactions; 00855 $rtnvalue = TRUE; 00856 00857 if (empty($totalunits)) $totalunits = -1; 00858 if (empty($totalbillableunits)) $totalbillableunits = -1; 00859 if (empty($totalrefunds)) $totalrefunds = 0; 00860 00861 if ($serviceid == '') 00862 { 00863 // Find the correct service record to update 00864 $serviceid = get_serviceid($contractid); 00865 if ($serviceid < 1) trigger_error("Invalid service ID",E_USER_ERROR); 00866 } 00867 00868 if (trim($amount) == '') $amount = 0; 00869 $date = date('Y-m-d H:i:s', $now); 00870 00871 // Update the balance 00872 $sql = "UPDATE `{$dbService}` SET balance = (balance + {$amount}) WHERE serviceid = '{$serviceid}' LIMIT 1"; 00873 mysql_query($sql); 00874 if (mysql_error()) 00875 { 00876 trigger_error(mysql_error(),E_USER_ERROR); 00877 $rtnvalue = FALSE; 00878 } 00879 00880 if (mysql_affected_rows() < 1 AND $amount != 0) 00881 { 00882 trigger_error("Contract balance update failed",E_USER_ERROR); 00883 $rtnvalue = FALSE; 00884 } 00885 00886 if ($rtnvalue != FALSE) 00887 { 00888 // Log the transaction 00889 if (empty($transactionid)) 00890 { 00891 $sql = "INSERT INTO `{$dbTransactions}` (serviceid, totalunits, totalbillableunits, totalrefunds, amount, description, userid, dateupdated, transactionstatus) "; 00892 $sql .= "VALUES ('{$serviceid}', '{$totalunits}', '{$totalbillableunits}', '{$totalrefunds}', '{$amount}', '{$description}', '{$_SESSION['userid']}', '{$date}', '".BILLING_APPROVED."')"; 00893 $result = mysql_query($sql); 00894 00895 $rtnvalue = mysql_insert_id(); 00896 } 00897 else 00898 { 00899 $sql = "UPDATE `{$dbTransactions}` SET serviceid = {$serviceid}, totalunits = {$totalunits}, totalbillableunits = {$totalbillableunits}, totalrefunds = {$totalrefunds} "; 00900 $sql .= ", amount = {$amount}, userid = {$_SESSION['userid']} , dateupdated = '{$date}', transactionstatus = '".BILLING_APPROVED."' "; 00901 if (!empty($description)) 00902 { 00903 $sql .= ", description = '{$description}' "; 00904 } 00905 $sql .= "WHERE transactionid = {$transactionid}"; 00906 $result = mysql_query($sql); 00907 $rtnvalue = $transactionid; 00908 } 00909 00910 if (mysql_error()) 00911 { 00912 trigger_error(mysql_error(),E_USER_ERROR); 00913 $rtnvalue = FALSE; 00914 } 00915 if (mysql_affected_rows() < 1) 00916 { 00917 trigger_error("Transaction insert failed",E_USER_ERROR); 00918 $rtnvalue = FALSE; 00919 } 00920 } 00921 00922 return $rtnvalue; 00923 } 00924 00925 00932 function maintid_from_transaction($transactionid) 00933 { 00934 $rtnvalue = -1; 00935 $sql = "SELECT i.maintenanceid FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbIncidents']}` AS i WHERE "; 00936 $sql .= "l.origcolref = {$transactionid} AND l.linkcolref = i.id AND l.linktype = 6"; 00937 $result = mysql_query($sql); 00938 if (mysql_error()) trigger_error("Error getting maintid for transaction. ".mysql_error(), E_USER_WARNING); 00939 00940 if (mysql_num_rows($result) > 0) 00941 { 00942 list($rtnvalue) = mysql_fetch_row($result); 00943 } 00944 00945 return $rtnvalue; 00946 } 00947 00948 00956 function contract_transaction_total($contractid, $status) 00957 { 00958 $rtnvalue = FALSE; 00959 00960 $sql = "SELECT SUM(t.amount) FROM `{$GLOBALS['dbTransactions']}` AS t, `{$GLOBALS['dbService']}` AS s "; 00961 $sql .= "WHERE s.serviceid = t.serviceid AND s.contractid = {$contractid} AND t.transactionstatus = '{$status}'"; 00962 $result = mysql_query($sql); 00963 if (mysql_error()) trigger_error("Error getting total for type {$status}. ".mysql_error(), E_USER_WARNING); 00964 if (mysql_num_rows($result) > 0) 00965 { 00966 list($rtnvalue) = mysql_fetch_row($result); 00967 } 00968 00969 return $rtnvalue; 00970 } 00971 00972 00980 function service_transaction_total($serviceid, $status) 00981 { 00982 $rtnvalue = FALSE; 00983 $sql = "SELECT SUM(amount) FROM `{$GLOBALS['dbTransactions']}` "; 00984 $sql .= "WHERE serviceid = {$serviceid} AND transactionstatus = '{$status}'"; 00985 $result = mysql_query($sql); 00986 if (mysql_error()) trigger_error("Error getting total for type {$status}. ".mysql_error(), E_USER_WARNING); 00987 if (mysql_num_rows($result) > 0) 00988 { 00989 list($rtnvalue) = mysql_fetch_row($result); 00990 } 00991 return $rtnvalue; 00992 } 00993 00994 01004 function get_service_balance($serviceid, $includeawaitingapproval = TRUE, $includereserved = TRUE) 01005 { 01006 global $dbService; 01007 01008 $balance = FALSE; 01009 01010 $sql = "SELECT balance FROM `{$dbService}` WHERE serviceid = {$serviceid}"; 01011 $result = mysql_query($sql); 01012 if (mysql_error()) trigger_error(mysql_error(), E_USER_WARNING); 01013 if (mysql_num_rows($result) == 1) 01014 { 01015 list($balance) = mysql_fetch_row($result); 01016 if ($includeawaitingapproval) 01017 { 01018 $balance += service_transaction_total($serviceid, BILLING_AWAITINGAPPROVAL); 01019 } 01020 01021 if ($includereserved) 01022 { 01023 $balance += service_transaction_total($serviceid, BILLING_RESERVED); 01024 } 01025 } 01026 return $balance; 01027 } 01028 01029 01035 function is_billable_incident_approved($incidentid) 01036 { 01037 $sql = "SELECT DISTINCT origcolref, linkcolref "; 01038 $sql .= "FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t "; 01039 $sql .= "WHERE l.linktype = 6 "; 01040 $sql .= "AND l.origcolref = t.transactionid "; 01041 $sql .= "AND linkcolref = {$incidentid} "; 01042 $sql .= "AND direction = 'left' "; 01043 $sql .= "AND t.transactionstatus = '".BILLING_APPROVED."'"; 01044 $result = mysql_query($sql); 01045 if (mysql_error()) trigger_error(mysql_error(), E_USER_WARNING); 01046 01047 if (mysql_num_rows($result) > 0) return TRUE; 01048 else return FALSE; 01049 } 01050 01051 01058 function get_incident_transactionid($incidentid) 01059 { 01060 $rtnvalue = FALSE; 01061 $sql = "SELECT origcolref "; 01062 $sql .= "FROM `{$GLOBALS['dbLinks']}` AS l, `{$GLOBALS['dbTransactions']}` AS t "; 01063 $sql .= "WHERE l.linktype = 6 "; 01064 $sql .= "AND l.origcolref = t.transactionid "; 01065 $sql .= "AND linkcolref = {$incidentid} "; 01066 $sql .= "AND direction = 'left' "; 01067 $result = mysql_query($sql); 01068 if (mysql_error()) trigger_error(mysql_error(), E_USER_WARNING); 01069 01070 if (mysql_num_rows($result) > 0) 01071 { 01072 list($rtnvalue) = mysql_fetch_row($result); 01073 } 01074 01075 return $rtnvalue; 01076 } 01077 01078 01086 function contract_service_table($contractid, $billing) 01087 { 01088 global $CONFIG, $dbService, $now; 01089 01090 $sql = "SELECT * FROM `{$dbService}` WHERE contractid = {$contractid} ORDER BY enddate DESC"; 01091 $result = mysql_query($sql); 01092 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 01093 if (mysql_num_rows($result) > 0) 01094 { 01095 $shade = 'shade1'; 01096 $html = "\n<table class='maintable' id='contractservicetable'>"; 01097 $html .= "<tr>"; 01098 if ($billing) $html .= "<th></th>"; 01099 $html .= "<th>{$GLOBALS['strStartDate']}</th><th>{$GLOBALS['strEndDate']}</th>"; 01100 if ($billing) 01101 { 01102 $html .= "<th>{$GLOBALS['strAvailableBalance']}</th>"; 01103 } 01104 $html .= "<th>{$GLOBALS['strActions']}</th>"; 01105 $html .= "</tr>\n"; 01106 while ($service = mysql_fetch_object($result)) 01107 { 01108 $service->startdate = mysql2date($service->startdate . '09:00'); 01109 $service->enddate = mysql2date($service->enddate . '17:00'); 01110 $service->lastbilled = mysql2date($service->lastbilled); 01111 01112 $expired = false; 01113 $future = false; 01114 if ($service->enddate < $now) $expired = true; 01115 if ($service->startdate > $now) $future = true; 01116 01117 if ($future) 01118 { 01119 $shade = 'notice'; 01120 } 01121 elseif ($expired) 01122 { 01123 $shade = 'expired'; 01124 } 01125 $html .= "<tr class='{$shade}'>"; 01126 01127 if ($billing) 01128 { 01129 $balance = get_service_balance($service->serviceid); 01130 $awaitingapproval = service_transaction_total($service->serviceid, BILLING_AWAITINGAPPROVAL) * -1; 01131 $reserved = service_transaction_total($service->serviceid, BILLING_RESERVED) * -1; 01132 01133 $span = "<strong>{$GLOBALS['strServiceID']}:</strong> {$service->serviceid}<br />"; 01134 if (!empty($service->title)) 01135 { 01136 $span .= "<strong>{$GLOBALS['strTitle']}</strong>: {$service->title}<br />"; 01137 } 01138 01139 if (!empty($service->notes)) 01140 { 01141 $span .= "<strong>{$GLOBALS['strNotes']}</strong>: {$service->notes}<br />"; 01142 } 01143 01144 if (!empty($service->cust_ref)) 01145 { 01146 $span .= "<strong>{$GLOBALS['strCustomerReference']}</strong>: {$service->cust_ref}"; 01147 if ($service->cust_ref_date != "1970-01-01") 01148 { 01149 $span .= " - <strong>{$GLOBALS['strCustomerReferenceDate']}</strong>: {$service->cust_ref_date}"; 01150 } 01151 $span .= "<br />"; 01152 } 01153 01154 if ($service->creditamount != 0) 01155 { 01156 $span .= "<strong>{$GLOBALS['strCreditAmount']}</strong>: {$CONFIG['currency_symbol']}".number_format($service->creditamount, 2)."<br />"; 01157 } 01158 01159 if ($service->unitrate != 0) 01160 { 01161 $span .= "<strong>{$GLOBALS['strUnitRate']}</strong>: {$CONFIG['currency_symbol']}{$service->unitrate}<br />"; 01162 } 01163 01164 $span .= "<strong>{$GLOBALS['strBillingMatrix']}</string>: {$service->billingmatrix}<br />"; 01165 01166 if ($balance != $service->balance) 01167 { 01168 $span .= "<strong>{$GLOBALS['strBalance']}</strong>: {$CONFIG['currency_symbol']}".number_format($service->balance, 2)."<br />"; 01169 if ($awaitingapproval != FALSE) 01170 { 01171 $span .= "<strong>{$GLOBALS['strAwaitingApproval']}</strong>: {$CONFIG['currency_symbol']}".number_format($awaitingapproval, 2)."<br />"; 01172 } 01173 01174 if ($reserved != FALSE) 01175 { 01176 $span .= "<strong>{$GLOBALS['strReserved']}</strong>: {$CONFIG['currency_symbol']}".number_format($reserved, 2)."<br />"; 01177 } 01178 01179 $span .= "<strong>{$GLOBALS['strAvailableBalance']}</strong>: "; 01180 if (!$expired) 01181 { 01182 $span .= "{$CONFIG['currency_symbol']}".number_format($balance, 2); 01183 } 01184 else 01185 { 01186 $span .= $GLOBALS['strExpired']; 01187 } 01188 $span .= "<br />"; 01189 } 01190 01191 if ($service->lastbilled > 0) 01192 { 01193 $span .= "<strong>{$GLOBALS['strLastBilled']}</strong>: ".ldate($CONFIG['dateformat_date'], $service->lastbilled)."<br />"; 01194 } 01195 01196 if ($service->foc == 'yes') 01197 { 01198 $span .= "<strong>{$GLOBALS['strFreeOfCharge']}</strong>"; 01199 } 01200 01201 $html .= "<td><a name='billingicon' class='info'>".icon('billing', 16); 01202 if (!empty($span)) 01203 { 01204 $html .= "<span>{$span}</span>"; 01205 } 01206 $html .= "</a></td>"; 01207 $html .= "<td><a href='transactions.php?serviceid={$service->serviceid}' class='info'>".ldate($CONFIG['dateformat_date'], $service->startdate); 01208 if (!empty($span)) 01209 { 01210 $html .= "<span>{$span}</span>"; 01211 } 01212 $html .= "</a></td>"; 01213 } 01214 else 01215 { 01216 $html .= "<td>".ldate($CONFIG['dateformat_date'],$service->startdate); 01217 $html .= "</td>"; 01218 } 01219 $html .= "<td>"; 01220 $html .= ldate($CONFIG['dateformat_date'], $service->enddate)."</td>"; 01221 01222 if ($billing) 01223 { 01224 $html .= "<td>{$CONFIG['currency_symbol']}"; 01225 if (!$expired) $html .= number_format($balance, 2); 01226 else $html .= "0"; 01227 $html .= "</td>"; 01228 } 01229 01230 $html .= "<td>"; 01231 $operations[$GLOBALS['strEditService']] = array('url' => "contract_edit_service.php?mode=editservice&serviceid={$service->serviceid}&contractid={$contractid}", 'perm' => PERM_SERVICE_EDIT); 01232 if ($billing) 01233 { 01234 $operations[$GLOBALS['strEditBalance']] = array('url' => "contract_edit_service.php?mode=showform&sourceservice={$service->serviceid}&contractid={$contractid}", 'perm' => PERM_SERVICE_BALANCE_EDIT); 01235 $operations[$GLOBALS['strViewTransactions']] = array('url' => "transactions.php?serviceid={$service->serviceid}", 'perm' => PERM_BILLING_TRANSACTION_VIEW); 01236 } 01237 $html .= html_action_links($operations); 01238 $html .= "</td></tr>\n"; 01239 01240 if ($shade == 'shade1') $shade = 'shade2'; 01241 else $shade = 'shade1'; 01242 } 01243 $html .= "</table>\n"; 01244 } 01245 return $html; 01246 } 01247 01248 01258 function get_incident_billing_details($incidentid) 01259 { 01260 /* 01261 $array[owner][] = array(owner, starttime, duration) 01262 */ 01263 $sql = "SELECT * FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid = {$incidentid} AND duration IS NOT NULL"; 01264 $result = mysql_query($sql); 01265 if (mysql_error()) 01266 { 01267 trigger_error(mysql_error(),E_USER_WARNING); 01268 return FALSE; 01269 } 01270 01271 if (mysql_num_rows($result) > 0) 01272 { 01273 while($obj = mysql_fetch_object($result)) 01274 { 01275 if ($obj->duration > 0) 01276 { 01277 $temparray['owner'] = $obj->userid; 01278 $temparray['starttime'] = ($obj->timestamp-($obj->duration*60)); 01279 $temparray['duration'] = $obj->duration; 01280 $billing[$obj->userid][] = $temparray; 01281 } 01282 else 01283 { 01284 if (empty($billing['refunds'])) $billing['refunds'] = 0; 01285 $billing['refunds'] += $obj->duration; 01286 } 01287 } 01288 } 01289 01290 return $billing; 01291 } 01292 01293 01305 function group_billing_periods(&$count, $countType, $activity, $period) 01306 { 01307 $duration = $activity['duration'] * 60; 01308 $startTime = $activity['starttime']; 01309 01310 if (!empty($count[$countType])) 01311 { 01312 while ($duration > 0) 01313 { 01314 $saved = "false"; 01315 foreach ($count[$countType] AS $ind) 01316 { 01317 /* 01318 echo "<pre>"; 01319 print_r($ind); 01320 echo "</pre>"; 01321 */ 01322 //echo "IN:{$ind}:START:{$act['starttime']}:ENG:{$engineerPeriod}<br />"; 01323 01324 if($ind <= $activity['starttime'] AND $ind <= ($activity['starttime'] + $period)) 01325 { 01326 //echo "IND:{$ind}:START:{$act['starttime']}<br />"; 01327 // already have something which starts in this period just need to check it fits in the period 01328 if($ind + $period > $activity['starttime'] + $duration) 01329 { 01330 $remainderInPeriod = ($ind + $period) - $activity['starttime']; 01331 $duration -= $remainderInPeriod; 01332 01333 $saved = "true"; 01334 } 01335 } 01336 } 01337 //echo "Saved: {$saved}<br />"; 01338 // This section runs when there are no engineer or customer billing period totals yet (first iteration) 01339 if ($saved == "false" AND $activity['duration'] > 0) 01340 { 01341 //echo "BB:".$activity['starttime'].":SAVED:{$saved}:DUR:{$activity['duration']}<br />"; 01342 // need to add a new block 01343 $count[$countType][$startTime] = $startTime; 01344 01345 $startTime += $period; 01346 01347 $duration -= $period; 01348 } 01349 } 01350 } 01351 else 01352 { 01353 $count[$countType][$activity['starttime']] = $activity['starttime']; 01354 $localDur = $duration - $period; 01355 01356 while ($localDur > 0) 01357 { 01358 $startTime += $period; 01359 $count[$countType][$startTime] = $startTime; 01360 $localDur -= $period; // was just - 01361 } 01362 } 01363 } 01364 01365 01375 function make_incident_billing_array($incidentid, $totals = TRUE) 01376 { 01377 01378 $billing = get_incident_billing_details($incidentid); 01379 01380 // echo "<pre>"; 01381 // print_r($billing); 01382 // echo "</pre><hr />"; 01383 01384 $sql = "SELECT servicelevel, priority FROM `{$GLOBALS['dbIncidents']}` WHERE id = {$incidentid}"; 01385 $result = mysql_query($sql); 01386 if (mysql_error()) 01387 { 01388 trigger_error(mysql_error(),E_USER_WARNING); 01389 return FALSE; 01390 } 01391 01392 $incident = mysql_fetch_object($result); 01393 $servicelevel_tag = $incident->servicelevel; 01394 $priority = $incident->priority; 01395 01396 if (!empty($billing)) 01397 { 01398 $billingSQL = "SELECT * FROM `{$GLOBALS['dbBillingPeriods']}` WHERE tag='{$servicelevel_tag}' AND priority='{$priority}'"; 01399 01400 /* 01401 echo "<pre>"; 01402 print_r($billing); 01403 echo "</pre>"; 01404 01405 echo "<pre>"; 01406 print_r(make_billing_array($incidentid)); 01407 echo "</pre>"; 01408 */ 01409 01410 //echo $billingSQL; 01411 01412 $billingresult = mysql_query($billingSQL); 01413 // echo $billingSQL; 01414 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 01415 $billingObj = mysql_fetch_object($billingresult); 01416 01417 unset($billingresult); 01418 01419 $engineerPeriod = $billingObj->engineerperiod * 60; //to seconds 01420 $customerPeriod = $billingObj->customerperiod * 60; 01421 01422 if (empty($engineerPeriod) OR $engineerPeriod == 0) $engineerPeriod = 3600; 01423 if (empty($customerPeriod) OR $customerPeriod == 0) $customerPeriod = 3600; 01424 01425 /* 01426 echo "<pre>"; 01427 print_r($billing); 01428 echo "</pre>"; 01429 */ 01430 01431 $count = array(); 01432 01433 01434 // Loop over each activity that happened during the duration of the incident 01435 // Grouped by Engineer - and then calculate totals 01436 foreach ($billing AS $engineer) 01437 { 01438 /* 01439 [eng][starttime] 01440 */ 01441 01442 if (is_array($engineer)) 01443 { 01444 $owner = ""; 01445 $duration = 0; 01446 01447 unset($count); 01448 $count = array(); 01449 01450 $count['engineer']; 01451 $count['customer']; 01452 01453 foreach ($engineer AS $activity) 01454 { 01455 $owner = user_realname($activity['owner']); 01456 $duration += $activity['duration']; 01457 01458 /* 01459 echo "<pre>"; 01460 print_r($count); 01461 echo "</pre>"; 01462 */ 01463 01464 group_billing_periods($count, 'engineer', $activity, $engineerPeriod); 01465 01466 // Optimisation no need to compute again if we already have the details 01467 if ($engineerPeriod != $customerPeriod) 01468 { 01469 group_billing_periods($count, 'customer', $activity, $customerPeriod); 01470 } 01471 else 01472 { 01473 $count['customer'] = $count['engineer']; 01474 } 01475 } 01476 01477 $tduration += $duration; 01478 $totalengineerperiods += sizeof($count['engineer']); 01479 $totalcustomerperiods += sizeof($count['customer']); 01480 /* 01481 echo "<pre>"; 01482 print_r($count); 01483 echo "</pre>"; 01484 */ 01485 01486 $billing_a[$activity['owner']]['owner'] = $owner; 01487 $billing_a[$activity['owner']]['duration'] = $duration; 01488 $billing_a[$activity['owner']]['engineerperiods'] = $count['engineer']; 01489 $billing_a[$activity['owner']]['customerperiods'] = $count['customer']; 01490 } 01491 01492 if ($totals == TRUE) 01493 { 01494 if (empty($totalengineerperiods)) $totalengineerperiods = 0; 01495 if (empty($totalcustomerperiods)) $totalcustomerperiods = 0; 01496 if (empty($tduration)) $tduration = 0; 01497 01498 $billing_a[-1]['totalduration'] = $tduration; 01499 $billing_a[-1]['totalengineerperiods'] = $totalengineerperiods; 01500 $billing_a[-1]['totalcustomerperiods'] = $totalcustomerperiods; 01501 $billing_a[-1]['customerperiod'] = $customerPeriod; 01502 $billing_a[-1]['engineerperiod'] = $engineerPeriod; 01503 } 01504 01505 if (!empty($billing['refunds'])) $billing_a[-1]['refunds'] = $billing['refunds']/$customerPeriod; // return refunds as a number of units 01506 else $billing_a[-1]['refunds'] = 0; 01507 01508 } 01509 01510 } 01511 01512 //echo "<pre>"; 01513 //print_r($billing_a); 01514 //echo "</pre>"; 01515 01516 return $billing_a; 01517 } 01518 01519 01528 function billable_units_site($siteid, $startdate=0, $enddate=0) 01529 { 01530 $sql = "SELECT i.id FROM `{$GLOBALS['dbIncidents']}` AS i, `{$GLOBALS['dbContacts']}` AS c "; 01531 $sql .= "WHERE c.id = i.contact AND c.siteid = {$siteid} "; 01532 if ($startdate != 0) 01533 { 01534 $sql .= "AND closed >= {$startdate} "; 01535 } 01536 01537 if ($enddate != 0) 01538 { 01539 $sql .= "AND closed <= {$enddate} "; 01540 } 01541 01542 $result = mysql_query($sql); 01543 if (mysql_error()) 01544 { 01545 trigger_error(mysql_error(),E_USER_WARNING); 01546 return FALSE; 01547 } 01548 01549 $units = 0; 01550 01551 if (mysql_num_rows($result) > 0) 01552 { 01553 while ($obj = mysql_fetch_object($result)) 01554 { 01555 $a = make_incident_billing_array($obj->id); 01556 $units += $a[-1]['totalcustomerperiods']; 01557 } 01558 } 01559 01560 return $units; 01561 } 01562 01563 01571 function get_incident_billable_breakdown_array($incidentid) 01572 { 01573 $billable = make_incident_billing_array($incidentid, FALSE); 01574 01575 $billingmatrix = ''; 01576 01577 $serviceid = get_serviceid(incident_maintid($incidentid)); 01578 $sql = "SELECT billingmatrix FROM `{$GLOBALS['dbService']}` WHERE serviceid = {$serviceid}"; 01579 $result = mysql_query($sql); 01580 if (mysql_error()) 01581 { 01582 trigger_error("Unable to get billing matrix for service {$serviceid} ".mysql_error(),E_USER_WARNING); 01583 } 01584 list($billingmatrix) = mysql_fetch_row($result); 01585 01586 //echo "<pre>"; 01587 //print_r($billable); 01588 //echo "</pre>"; 01589 01590 if (!empty($billable)) 01591 { 01592 foreach ($billable AS $engineer) 01593 { 01594 if (is_array($engineer) AND empty($engineer['refunds'])) 01595 { 01596 $engineerName = $engineer['owner']; 01597 foreach ($engineer['customerperiods'] AS $period) 01598 { 01599 // $period is the start time 01600 $day = date('D', $period); 01601 $hour = date('H', $period); 01602 01603 $dayNumber = date('d', $period); 01604 $month = date('n', $period); 01605 $year = date('Y', $period); 01606 // echo "DAY {$day} HOUR {$hour}"; 01607 01608 $dayofweek = strtolower($day); 01609 01610 if (is_day_bank_holiday($dayNumber, $month, $year)) 01611 { 01612 $dayofweek = "holiday"; 01613 } 01614 01615 $multiplier = get_billable_multiplier($dayofweek, $hour, $billingmatrix); 01616 01617 $billing[$engineerName]['owner'] = $engineerName; 01618 $billing[$engineerName][$multiplier]['multiplier'] = $multiplier; 01619 if (empty($billing[$engineerName][$multiplier]['count'])) 01620 { 01621 $billing[$engineerName][$multiplier]['count'] = 0; 01622 } 01623 01624 $billing[$engineerName][$multiplier]['count']++; 01625 } 01626 } 01627 } 01628 01629 if (!empty($billable[-1]['refunds'])) $billing['refunds'] = $billable[-1]['refunds']; 01630 01631 } 01632 01633 return $billing; 01634 } 01635 01636 01643 function contract_unit_balance($contractid, $includenonapproved = FALSE, $includereserved = TRUE, $showonlycurrentlyvalid = TRUE) 01644 { 01645 global $now, $dbService; 01646 01647 $unitbalance = 0; 01648 01649 $sql = "SELECT * FROM `{$dbService}` WHERE contractid = {$contractid} "; 01650 01651 if ($showonlycurrentlyvalid) 01652 { 01653 $sql .= "AND UNIX_TIMESTAMP(startdate) <= {$now} "; 01654 $sql .= "AND UNIX_TIMESTAMP(enddate) >= {$now} "; 01655 } 01656 $sql .= "ORDER BY enddate DESC"; 01657 01658 $result = mysql_query($sql); 01659 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 01660 01661 if (mysql_num_rows($result) > 0) 01662 { 01663 while ($service = mysql_fetch_object($result)) 01664 { 01665 $multiplier = get_billable_multiplier(strtolower(date('D', $now)), date('G', $now)); 01666 $unitamount = $service->unitrate * $multiplier; 01667 if ($unitamount > 0 AND $service->balance != 0) $unitbalance += round($service->balance / $unitamount); 01668 } 01669 01670 if ($includenonapproved) 01671 { 01672 $awaiting = contract_transaction_total($contractid, BILLING_AWAITINGAPPROVAL); 01673 if ($awaiting != 0) $unitbalance += round($awaiting / $unitamount); 01674 } 01675 01676 if ($includereserved) 01677 { 01678 $reserved = contract_transaction_total($contractid, BILLING_RESERVED); 01679 if ($reserved != 0) $unitbalance += round($reserved / $unitamount); 01680 } 01681 } 01682 01683 return $unitbalance; 01684 } 01685 01686 01693 function contract_balance($contractid, $includenonapproved = FALSE, $includereserved = TRUE, $showonlycurrentlyvalid = TRUE) 01694 { 01695 global $now, $dbService; 01696 01697 $unitbalance = 0; 01698 01699 $sql = "SELECT * FROM `{$dbService}` WHERE contractid = {$contractid} "; 01700 01701 if ($showonlycurrentlyvalid) 01702 { 01703 $sql .= "AND UNIX_TIMESTAMP(startdate) <= {$now} "; 01704 $sql .= "AND UNIX_TIMESTAMP(enddate) >= {$now} "; 01705 } 01706 $sql .= "ORDER BY enddate DESC"; 01707 01708 $result = mysql_query($sql); 01709 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 01710 01711 if (mysql_num_rows($result) > 0) 01712 { 01713 while ($service = mysql_fetch_object($result)) 01714 { 01715 $balance += round($service->balance); 01716 } 01717 } 01718 01719 if ($includenonapproved) 01720 { 01721 $awaiting = contract_transaction_total($contractid, BILLING_AWAITINGAPPROVAL); 01722 if ($awaiting != 0) $balance += round($awaiting); 01723 } 01724 01725 if ($includereserved) 01726 { 01727 $reserved = contract_transaction_total($contractid, BILLING_RESERVED); 01728 if ($reserved != 0) $balance += round($reserved); 01729 } 01730 01731 return $balance; 01732 } 01733 01734 01749 function transactions_report($serviceid, $startdate, $enddate, $sites, $display, $sitebreakdown=TRUE, $showfoc=TRUE, $focaszero=FALSE, $includeawaitingapproval = TRUE, $includereserved = TRUE) 01750 { 01751 global $CONFIG; 01752 01753 $csv_currency = html_entity_decode($CONFIG['currency_symbol'], ENT_NOQUOTES, "ISO-8859-15"); // Note using -15 as -1 doesnt support euro 01754 01755 $sql = "SELECT DISTINCT t.*, m.site, p.foc, p.cust_ref, p.cust_ref_date, p.title, p.notes "; 01756 $sql .= "FROM `{$GLOBALS['dbTransactions']}` AS t, `{$GLOBALS['dbService']}` AS p, "; 01757 $sql .= "`{$GLOBALS['dbMaintenance']}` AS m, `{$GLOBALS['dbServiceLevels']}` AS sl, `{$GLOBALS['dbSites']}` AS s "; 01758 $sql .= "WHERE t.serviceid = p.serviceid AND p.contractid = m.id "; // AND t.date <= '{$enddateorig}' "; 01759 $sql .= "AND m.servicelevel = sl.tag AND sl.timed = 'yes' AND m.site = s.id "; 01761 if ($serviceid > 0) $sql .= "AND t.serviceid = {$serviceid} "; 01762 if (!empty($startdate)) $sql .= "AND t.dateupdated >= '{$startdate}' "; 01763 if (!empty($enddate)) $sql .= "AND t.dateupdated <= '{$enddate}' "; 01764 $orsql[] = "t.transactionstatus = ".BILLING_APPROVED; 01765 if ($includeawaitingapproval) $orsql[] = "t.transactionstatus = ".BILLING_AWAITINGAPPROVAL; 01766 if ($includereserved) $orsql[] = "t.transactionstatus = ".BILLING_RESERVED; 01767 $o = implode(" OR ", $orsql); 01768 $sql .= "AND ($o) "; 01769 01770 if (!$showfoc) $sql .= "AND p.foc = 'no' "; 01771 01772 if (!empty($sites)) 01773 { 01774 $sitestr = ''; 01775 01776 foreach ($sites AS $s) 01777 { 01778 $s = clean_int($s); 01779 if (empty($sitestr)) $sitestr .= "m.site = {$s} "; 01780 else $sitestr .= "OR m.site = {$s} "; 01781 } 01782 01783 $sql .= "AND {$sitestr} "; 01784 } 01785 01786 if (!empty($site)) $sql .= "AND m.site = {$site} "; 01787 01788 $sql .= "ORDER BY t.dateupdated, s.name "; 01789 01790 $result = mysql_query($sql); 01791 if (mysql_error()) trigger_error("MySQL Query Error ".mysql_error(), E_USER_WARNING); 01792 01793 if (mysql_num_rows($result) > 0) 01794 { 01795 $shade = 'shade1'; 01796 01797 $total = 0; 01798 $totalcredit = 0; 01799 $totaldebit = 0; 01800 01801 $details = ''; 01802 01803 while ($transaction = mysql_fetch_object($result)) 01804 { 01805 if ($display == 'html') 01806 { 01807 if ($serviceid > 0 AND empty($details)) 01808 { 01809 if (!empty($transaction->cust_ref)) 01810 { 01811 $details .= "<tr>"; 01812 $details .= "<th>{$GLOBALS['strCustomerReference']}</th><td>{$transaction->cust_ref}</td>"; 01813 if ($transaction->cust_ref_date != "1970-01-01") 01814 { 01815 $details .= "<th>{$GLOBALS['strCustomerReferenceDate']}</th><td>{$transaction->cust_ref_date}</td>"; 01816 } 01817 $details .= "</tr>"; 01818 } 01819 01820 if (!empty($transaction->title)) 01821 { 01822 $details .= "<tr><th>{$GLOBALS['strTitle']}</th><td>{$transaction->title}</td></tr>"; 01823 } 01824 01825 if (!empty($transaction->notes)) 01826 { 01827 $details .= "<tr><th>{$GLOBALS['strNotes']}</th><td>{$transaction->notes}</td></tr>"; 01828 } 01829 } 01830 01831 $str = "<tr class='$shade'>"; 01832 $str .= "<td>" . date($CONFIG['dateformat_datetime'], mysql2date($transaction->dateupdated)) . "</td>"; 01833 $str .= "<td>{$transaction->transactionid}</td>"; 01834 $str .= "<td>{$transaction->serviceid}</td>"; 01835 $str .= "<td>".site_name($transaction->site)."</td>"; 01836 $str .= "<td>{$transaction->description}</td>"; 01837 $str .= "<td>"; 01838 switch ($transaction->transactionstatus) 01839 { 01840 case BILLING_APPROVED: $str .= $GLOBALS['strApproved']; 01841 break; 01842 case BILLING_AWAITINGAPPROVAL: $str .= $GLOBALS['strAwaitingApproval']; 01843 break; 01844 case BILLING_RESERVED: $str .= $GLOBALS['strReserved']; 01845 break; 01846 } 01847 $str .= "</td>"; 01848 } 01849 elseif ($display == 'csv') 01850 { 01851 if ($serviceid > 0 AND empty($details)) 01852 { 01853 if (!empty($transaction->cust_ref)) 01854 { 01855 $details .= "\"{$GLOBALS['strCustomerReference']}\",\"{$transaction->cust_ref}\","; 01856 if ($transaction->cust_ref_date != "1970-01-01") 01857 { 01858 $details .= "\"{$GLOBALS['strCustomerReferenceDate']}\",\"{$transaction->cust_ref_date}\","; 01859 } 01860 $details .= "\n"; 01861 } 01862 01863 if (!empty($transaction->title)) 01864 { 01865 $details .= "\"{$GLOBALS['strTitle']}\",\"{$transaction->title}\"\n"; 01866 } 01867 01868 if (!empty($transaction->notes)) 01869 { 01870 $details .= "\"{$GLOBALS['strNotes']}\",\"{$transaction->notes}\"\n"; 01871 } 01872 } 01873 01874 $str = "\"" . date($CONFIG['dateformat_datetime'], mysql2date($transaction->dateupdated)) . "\","; 01875 $str .= "\"{$transaction->transactionid}\","; 01876 $str .= "\"{$transaction->serviceid}\",\""; 01877 $str .= site_name($transaction->site)."\","; 01878 $str .= "\"".html_entity_decode($transaction->description)."\","; 01879 $str .= "\""; 01880 switch ($transaction->transactionstatus) 01881 { 01882 case BILLING_APPROVED: 01883 $str .= $GLOBALS['strApproved']; 01884 break; 01885 case BILLING_AWAITINGAPPROVAL: 01886 $str .= $GLOBALS['strAwaitingApproval']; 01887 break; 01888 case BILLING_RESERVED: 01889 $str .= $GLOBALS['strReserved']; 01890 break; 01891 } 01892 $str .= "\","; 01893 } 01894 01895 if ($focaszero AND $transaction->foc == 'yes') 01896 { 01897 $transaction->amount = 0; 01898 } 01899 01900 $total += $transaction->amount; 01901 if ($transaction->amount < 0) 01902 { 01903 $totaldebit += $transaction->amount; 01904 if ($display == 'html') 01905 { 01906 $str .= "<td></td><td>{$CONFIG['currency_symbol']}".number_format($transaction->amount, 2)."</td>"; 01907 } 01908 elseif ($display == 'csv') 01909 { 01910 $str .= ",\"{$csv_currency}".number_format($transaction->amount, 2)."\","; 01911 } 01912 } 01913 else 01914 { 01915 $totalcredit += $transaction->amount; 01916 if ($display == 'html') 01917 { 01918 $str .= "<td>{$CONFIG['currency_symbol']}".number_format($transaction->amount, 2)."</td><td></td>"; 01919 } 01920 elseif ($display == 'csv') 01921 { 01922 $str .= "\"{$csv_currency}".number_format($transaction->amount, 2)."\",,"; 01923 } 01924 } 01925 01926 if ($display == 'html') $str .= "</tr>"; 01927 elseif ($display == 'csv') $str .= "\n"; 01928 01929 if ($sitebreakdown == TRUE) 01930 { 01931 $table[$transaction->site]['site'] = site_name($transaction->site); 01932 $table[$transaction->site]['str'] .= $str; 01933 if ($transaction->amount < 0) 01934 { 01935 $table[$transaction->site]['debit'] += $transaction->amount; 01936 } 01937 else 01938 { 01939 $table[$transaction->site]['credit'] += $transaction->amount; 01940 } 01941 } 01942 else 01943 { 01944 $table .= $str; 01945 } 01946 if ($shade == 'shade1') $shade = 'shade2'; 01947 else $shade = 'shade1'; 01948 } 01949 01950 if ($sitebreakdown == TRUE) 01951 { 01952 foreach ($table AS $e) 01953 { 01954 if ($display == 'html') 01955 { 01956 $text .= "<h3>{$e['site']}</h3>"; 01957 $text .= "<table align='center' width='60%'>"; 01958 //echo "<tr><th colspan='7'>{$e['site']}</th></tr>"; 01959 $text .= "<tr><th>{$GLOBALS['strDate']}</th><th>{$GLOBALS['strID']}</th><th>{$GLOBALS['strServiceID']}</th>"; 01960 $text .= "<th>{$GLOBALS['strSite']}</th><th>{$GLOBALS['strDescription']}</th><th>{$GLOBALS['strCredit']}</th><th>{$GLOBALS['strDebit']}</th></tr>"; 01961 $text .= $e['str']; 01962 $text .= "<tr><td colspan='5' align='right'>{$GLOBALS['strTotal']}</td>"; 01963 $text .= "<td>{$CONFIG['currency_symbol']}".number_format($e['credit'], 2)."</td>"; 01964 $text .= "<td>{$CONFIG['currency_symbol']}".number_format($e['debit'], 2)."</td></tr>"; 01965 $text .= "</table>"; 01966 } 01967 elseif ($display == 'csv') 01968 { 01969 $text .= "\"{$e['site']}\"\n\n"; 01970 $text .= "\"{$GLOBALS['strDate']}\",\"{$GLOBALS['strID']}\",\"{$GLOBALS['strServiceID']}\","; 01971 $text .= "\"{$GLOBALS['strSite']}\",\"{$GLOBALS['strDescription']}\",\"{$GLOBALS['strCredit']}\",\"{$GLOBALS['strDebit']}\"\n"; 01972 $text .= $e['str']; 01973 $text .= ",,,,{$GLOBALS['strTotal']},"; 01974 $text .= "\"{$csv_currency}".number_format($e['credit'], 2)."\",\""; 01975 $text .="{$csv_currency}".number_format($e['debit'], 2)."\"\n"; 01976 } 01977 } 01978 } 01979 else 01980 { 01981 if ($display == 'html') 01982 { 01983 if (!empty($details)) 01984 { 01985 // Dont need to worry about this in the above section as sitebreakdown and serviceid are multually exclusive 01986 $text .= "<div><table class='maintable'>{$details}</table></div>"; 01987 } 01988 01989 $text .= "<table class='maintable'>"; 01990 $text .= "<tr><th>{$GLOBALS['strDate']}</th><th>{$GLOBALS['strID']}</th><th>{$GLOBALS['strServiceID']}</th>"; 01991 $text .= "<th>{$GLOBALS['strSite']}</th>"; 01992 $text .= "<th>{$GLOBALS['strDescription']}</th><th>{$GLOBALS['strStatus']}</th><th>{$GLOBALS['strCredit']}</th><th>{$GLOBALS['strDebit']}</th></tr>"; 01993 $text .= $table; 01994 $text .= "<tfoot><tr><td colspan='6' align='right'>{$GLOBALS['strTOTALS']}</td>"; 01995 $text .= "<td>{$CONFIG['currency_symbol']}".number_format($totalcredit, 2)."</td>"; 01996 $text .= "<td>{$CONFIG['currency_symbol']}".number_format($totaldebit, 2)."</td></tr></tfoot>"; 01997 $text .= "</table>"; 01998 } 01999 elseif ($display == 'csv') 02000 { 02001 if (!empty($details)) 02002 { 02003 $text .= $details; 02004 } 02005 $text .= "\"{$GLOBALS['strDate']}\",\"{$GLOBALS['strID']}\",\"{$GLOBALS['strServiceID']}\","; 02006 $text .= "\"{$GLOBALS['strSite']}\","; 02007 $text .= "\"{$GLOBALS['strDescription']}\",\"{$GLOBALS['strStatus']}\",\"{$GLOBALS['strCredit']}\",\"{$GLOBALS['strDebit']}\"\n"; 02008 $text .= $table; 02009 $text .= ",,,,{$GLOBALS['strTOTALS']},"; 02010 $text .= "\"{$csv_currency}".number_format($totalcredit, 2)."\",\""; 02011 $text .= "{$csv_currency}".number_format($totaldebit, 2)."\"\n"; 02012 } 02013 } 02014 } 02015 else 02016 { 02017 if ($display == 'html') 02018 { 02019 $text = "<p align='center'>{$GLOBALS['strNoTransactionsMatchYourSearch']}</p>"; 02020 } 02021 elseif ($display == 'csv') 02022 { 02023 $text = $GLOBALS['strNoTransactionsMatchYourSearch']."\n"; 02024 } 02025 } 02026 02027 return $text; 02028 } 02029 02030 02039 function service_dropdown_contract($contractid, $name, $selected=0) 02040 { 02041 global $now, $CONFIG; 02042 $sql = "SELECT * FROM `{$GLOBALS['dbService']}` WHERE contractid = {$contractid} "; 02043 $sql .= "AND UNIX_TIMESTAMP(startdate) <= {$now} AND UNIX_TIMESTAMP(enddate) >= {$now}"; 02044 $result = mysql_query($sql); 02045 if (mysql_error()) trigger_error("Error getting services. ".mysql_error(), E_USER_WARNING); 02046 02047 $html = FALSE; 02048 02049 if (mysql_num_rows($result) > 0) 02050 { 02051 $html = "<select name='{$name}' id={$name}>\n"; 02052 $html .= "<option value='0' "; 02053 if ($selected == 0) $html .= " selected='selected' "; 02054 $html .= "></option>"; 02055 while ($obj = mysql_fetch_object($result)) 02056 { 02057 $html .= "<option value='{$obj->serviceid}' "; 02058 if ($selected == $obj->serviceid) $html .= " selected='selected' "; 02059 $html .= ">{$CONFIG['currency_symbol']}".get_service_balance($obj->serviceid, TRUE, TRUE); 02060 $html .= " ({$obj->startdate} - {$obj->enddate})</option>"; 02061 } 02062 $html .= "</select>\n"; 02063 } 02064 02065 return $html; 02066 } 02067 02068 02077 function service_dropdown_site($siteid, $name, $selected=0) 02078 { 02079 global $now, $CONFIG; 02080 $sql = "SELECT s.* FROM `{$GLOBALS['dbService']}` AS s, `{$GLOBALS['dbMaintenance']}` AS m "; 02081 $sql .= "WHERE s.contractid = m.id AND m.site = {$siteid} "; 02082 $sql .= "AND UNIX_TIMESTAMP(s.startdate) <= {$now} AND UNIX_TIMESTAMP(s.enddate) >= {$now}"; 02083 $result = mysql_query($sql); 02084 if (mysql_error()) trigger_error("Error getting services. ".mysql_error(), E_USER_WARNING); 02085 02086 $html = FALSE; 02087 02088 if (mysql_num_rows($result) > 0) 02089 { 02090 $html = "<select name='{$name}' id={$name}>\n"; 02091 $html .= "<option value='0' "; 02092 if ($selected == 0) $html .= " selected='selected' "; 02093 $html .= "></option>"; 02094 while ($obj = mysql_fetch_object($result)) 02095 { 02096 $html .= "<option value='{$obj->serviceid}' "; 02097 if ($selected == $obj->serviceid) $html .= " selected='selected' "; 02098 $html .= ">{$CONFIG['currency_symbol']}".get_service_balance($obj->serviceid, TRUE, TRUE); 02099 $html .= " ({$obj->startdate} - {$obj->enddate})</option>"; 02100 } 02101 $html .= "</select>\n"; 02102 } 02103 else 02104 { 02105 $html = "No services currently valid"; 02106 } 02107 02108 return $html; 02109 } 02110 02111 02118 function is_transaction_approved($transactionid) 02119 { 02120 $sql = "SELECT transactionid FROM `{$GLOBALS['dbTransactions']}` WHERE transactionid = {$transactionid} AND transactionstaus = ".BILLING_APPROVED; 02121 $result = mysql_query($sql); 02122 if (mysql_error()) trigger_error("Error getting services. ".mysql_error(), E_USER_WARNING); 02123 02124 if (mysql_num_rows($result) > 0) return TRUE; 02125 else return FALSE; 02126 } 02127 02128 ?>