There is serious bug in RateEngine class that affects the ordering of trunks to use. It chooses incorrect trunk that costs more than the best route.
For example, let's assume there are two trunks, TrunkA and TrunkB.
TrunkB rates are same to all Turkmenistan numbers:
993 0.10
TrunkA's rates are little different, they charge more for mobile phone numbers.
9936 0.13
993 0.10
Now let's say we want to dial 99365xxxxxx. First, RateEngine will find the rate with the longest prefix (9936 - 0.13 TrunkA), and discard
all other rates whose prefix length is less than the longest. In this case, best route is TrunkB, but it is discarded, because len(993) < len(9936).
Below is the new rate_engine_findrates() function, which fixes above problem.
cheers,
Atamurad Hezretkuliyev
Code:
/*
RATE ENGINE
CALCUL THE RATE ACCORDING TO THE RATEGROUP, LCR - RATECARD
*/
function rate_engine_findrates (&$A2B, $phonenumber, $tariffgroupid){
global $agi;
// Check if we want to force the call plan
if (is_numeric($A2B->agiconfig['force_callplan_id']) && ($A2B->agiconfig['force_callplan_id'] > 0)){
$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "force the call plan : ".$A2B->agiconfig['force_callplan_id']);
$tariffgroupid = $A2B->tariff = $A2B->agiconfig['force_callplan_id'];
}
if ($this->webui){
$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: ($tariffgroupid, $phonenumber)]");
}
/*** 0 ) CODE TO RETURN THE DAY OF WEEK + CREATE THE CLAUSE ***/
$daytag = date("w", time()); // Day of week ( Sunday = 0 .. Saturday = 6 )
$hours = date("G", time()); // Hours in 24h format ( 0-23 )
$minutes = date("i", time()); // Minutes (00-59)
if (!$daytag) $daytag=6;
else $daytag--;
if ($this -> debug_st) echo "$daytag $hours $minutes <br>";
// Race condiction on $minutes ?!
$minutes_since_monday = ($daytag * 1440) + ($hours * 60) + $minutes;
if ($this -> debug_st) echo "$minutes_since_monday<br> ";
//$sql_clause_days = " AND ".$daytable[$daytag]."='1' ";
$sql_clause_days = " AND (starttime <= ".$minutes_since_monday." AND endtime >=".$minutes_since_monday.") ";
/*
SELECT tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, destination,
cc_ratecard.id,
dialprefix, destination, buyrate, buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
connectcharge, disconnectcharge, stepchargea, chargea, timechargea, billingblocka, stepchargeb, chargeb,
timechargeb, billingblockb, stepchargec, chargec, timechargec, billingblockc,
cc_tariffplan.id_trunk AS tp_id_trunk, tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,
tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,
cc_ratecard.id_trunk AS rc_id_trunk, rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,
rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix
FROM cc_tariffgroup
LEFT JOIN cc_tariffgroup_plan ON idtariffgroup=id
LEFT JOIN cc_tariffplan ON cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan
LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
LEFT JOIN trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
LEFT JOIN trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk
WHERE dialprefix=SUBSTRING('346586699595',1,length(dialprefix))
AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
ORDER BY LENGTH(dialprefix) DESC
*/
if (strlen($A2B->dnid)>=1) $mydnid = $A2B->dnid;
if (strlen($A2B->CallerID)>=1) $mycallerid = $A2B->CallerID;
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: CALLERID]\n".$A2B->CallerID."\n",0);
if ($A2B->config["database"]['dbtype'] != "postgres"){
$DNID_QUERY = "SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix))";
$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $DNID_QUERY);
if (!is_array($result_sub) || count($result_sub)==0) $nb_dnid = 0;
else $nb_dnid = $result_sub[0][0];
$DNID_SUB_QUERY = "AND 0 = $nb_dnid";
$CALLERID_QUERY = "SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix))";
$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $CALLERID_QUERY);
if (!is_array($result_sub) || count($result_sub)==0) $nb_callerid = 0;
else $nb_callerid = $result_sub[0][0];
$CID_SUB_QUERY = "AND 0 = $nb_callerid";
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CALLERID_QUERY]\n".$CALLERID_QUERY."\n[RESULT]".print_r($result_sub, 1)."\n",0);
}else{
$DNID_SUB_QUERY = "AND 0 = (SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) ) ";
$CID_SUB_QUERY = "AND 0 = (SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) ) ";
}
$QUERY = "SELECT tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, destination,
cc_ratecard.id, dialprefix, destination, buyrate, buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
connectcharge, disconnectcharge, stepchargea, chargea, timechargea, billingblocka, stepchargeb, chargeb,
timechargeb, billingblockb, stepchargec, chargec, timechargec, billingblockc,
cc_tariffplan.id_trunk AS tp_id_trunk, tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,
tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,
cc_ratecard.id_trunk AS rc_id_trunk, rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,
rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix,
musiconhold,
tp_trunk.failover_trunk AS tp_failover_trunk,
rt_trunk.failover_trunk AS rt_failover_trunk,
tp_trunk.addparameter AS tp_addparameter_trunk,
rt_trunk.addparameter AS rt_addparameter_trunk,
id_outbound_cidgroup,
freetimetocall_package_offer, freetimetocall, packagetype, billingtype, startday, id_cc_package_offer
FROM cc_tariffgroup
RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup.id=$tariffgroupid
INNER JOIN cc_tariffplan ON (cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan )
LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
LEFT JOIN cc_trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
LEFT JOIN cc_trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk
LEFT JOIN cc_package_offer ON cc_package_offer.id=cc_tariffgroup.id_cc_package_offer
WHERE (dialprefix=SUBSTRING('$phonenumber',1,length(dialprefix)) OR dialprefix='defaultprefix')
AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
$sql_clause_days
AND idtariffgroup='$tariffgroupid'
AND ( dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) OR (dnidprefix='all' $DNID_SUB_QUERY))
AND ( calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) OR (calleridprefix='all' $CID_SUB_QUERY))
ORDER BY LENGTH(dialprefix) DESC";
//-- if ($this -> debug_st) echo $QUERY."\n\n";
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[RATE ENGINE QUERY]\n".$QUERY."\n",0);
$A2B->instance_table = new Table();
$result = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $QUERY);
if (!is_array($result) || count($result)==0) return 0; // NO RATE FOR THIS NUMBER
if ($this -> debug_st) echo "::> Count Total result ".count($result)."\n\n";
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[rate-engine: Count Total result ".count($result)."]");
// CHECK IF THERE IS OTHER RATE THAT 'DEFAULT', IF YES REMOVE THE DEFAULT RATES
// NOT NOT REMOVE SHIFT THEM TO THE END :P
$ind_stop_default = -1;
for ($i=0;$i<count($result);$i++){
if ( $result[$i][7] != 'defaultprefix'){
$ind_stop_default = $i;
break;
}
}
// IMPORTANT TO CUT THE PART OF THE defaultprefix CAUSE WE WILL APPLY THE SORT ACCORDING TO THE RATE
// DEFAULPERFIX IS AN ESCAPE IN CASE OF NO RATE IS DEFINED, NOT BE COUNT WITH OTHER DURING THE
// SORT OF RATE
if ($ind_stop_default>0) {
$result_defaultprefix = array_slice ($result, 0, $ind_stop_default);
$result = array_slice ($result, $ind_stop_default, count($result)-$ind_stop_default);
}
//2) TAKE THE VALUE OF LCTYPE
//LCR : According to the buyer price -0 buyrate [col 6]
//LCD : According to the seller price -1 rateinitial [col 9]
$LCtype = $result[0][1];
// 3) REMOVE THOSE THAT USE THE SAME TRUNK - MAKE A DISTINCT
$mylistoftrunk = array();
for ($i=0;$i<count($result);$i++){
if ($result[$i][34]==-1) $mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][29];
else $mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][34];
// Check if we already have the same trunk in the ratecard
if ($i==0 || !in_array ($mycurrenttrunk , $mylistoftrunk)) {
$distinct_result[] = $result[$i];
}
$mylistoftrunk[]= $mycurrenttrunk;
}
//if ($ind_stop_default>0) array_rotate($result, $ind_stop_default);
// Thanks for the fix from the wiki :D next time email me, lol
if ($LCtype==0){
//$result = $this -> array_csort($result,'6',SORT_ASC); GOTTYA!
$distinct_result = $this -> array_csort($distinct_result,'9',SORT_ASC); //1
}else{
//$result = $this -> array_csort($result,'9',SORT_ASC); GOTTYA!
$distinct_result = $this -> array_csort($distinct_result,'12',SORT_ASC); //1
}
// WE ADD THE DEFAULTPREFIX WE REMOVE BEFORE
if ($ind_stop_default>0) {
$result = array_merge ($result, $result_defaultprefix);
}
//4) $result[K][34]==-1 USE THE VALUES OF THE TRUNK OF $result[K][29]
$this -> ratecard_obj = $distinct_result;
$this -> number_trunk = count($distinct_result);
// if an extracharge DID number was called increase rates with the extracharge fee
if (strlen($A2B->dnid)>2 && is_array($A2B->agiconfig['extracharge_did']) && in_array($A2B->dnid, $A2B->agiconfig['extracharge_did']))
{
$fee=$A2B->agiconfig['extracharge_fee'][array_search($A2B->dnid, $A2B->agiconfig['extracharge_did'])];
$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Extracharge DID found: ".$A2B->dnid.", extra fee: ".$fee."]");
for ($i=0; $i<count($this->ratecard_obj); $i++)
{
$this->ratecard_obj[$i][9] +=$fee;
$this->ratecard_obj[$i][12]+=$fee;
}
}
if ($this -> debug_st)echo "::> Count Total result ".count($distinct_result)."\n\n";
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Count Total result ".count($distinct_result)."]");
if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: number_trunk ".$this -> number_trunk."]");
return 1;
}