Support Incident Tracker GIT4.x
billing.inc.php
Go to the documentation of this file.
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&amp;serviceid={$service->serviceid}&amp;contractid={$contractid}", 'perm' => PERM_SERVICE_EDIT);
01232             if ($billing)
01233             {
01234                 $operations[$GLOBALS['strEditBalance']] = array('url' => "contract_edit_service.php?mode=showform&amp;sourceservice={$service->serviceid}&amp;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 ?>