Hi guys,
Sorry for the delay. I'll now describe what I've done. Your comments are really appreciated
I think there are 2 main parts to it, the first one is:
Code:
@@ -774,13 +788,91 @@
// CHECKING THE TIMEOUT
- $res_all_calcultimeout = $RateEngine->rate_engine_all_calcultimeout($this, $this->credit);
+ if ($this->mode == 'callback') {
+ // get the ring timeout.
+ $dialparams = $this->agiconfig['dialcommand_param'];
+ $ringtimeout = strtok($dialparams, "|");
+ $this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - RING TIMEOUT = $ringtimeout");
+
+ // determine leg1 answered time up to this point.
+ global $G_startime;
+ global $G_callback_timeout_computed_time;
+ $G_callback_timeout_computed_time = time();
+ $answeredtime_leg1 = $G_callback_timeout_computed_time - $G_startime;
+ $this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - LEG1 ANSWERED TIME SO FAR = $answeredtime_leg1");
+
+ // add the ring timeout.
+ $answeredtime_leg1 = $answeredtime_leg1 + $ringtimeout;
+ $this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - RING TIMEOUT + LEG1 ANSWERED TIME SO FAR = $answeredtime_leg1");
+
+ // compute the cost of leg1 up to this point.
+ // we can ignore freetimetocall stuff.
+ $freetimetocall_used = 0;
+ $RateEngine_leg1->usedratecard = 0;
+ $RateEngine_leg1->rate_engine_calculcost($this, $answeredtime_leg1, 0, $freetimetocall_used);
+ $cost_leg1 = $RateEngine_leg1->lastcost;
+ $credit = $this->credit + $cost_leg1;
+ $this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - ACTUAL CREDIT = $this->credit; LEG1 COST SO FAR = $cost_leg1; EFFECTIVE CREDIT = $credit");
+
+ // RateEngine_leg1 and RateEngine, both belong to the same call plan.
+ // need to halve the free time to call.
+ $RateEngine_leg1->ratecard_obj[0][46] = $RateEngine_leg1->ratecard_obj[0][46] / 2;
+
+ // compute the timeout for leg2.
+ for ($K = 0; $K < count($RateEngine->ratecard_obj); $K++) {
+ $r = 0.5;
+ $r_min = 0.0;
+ $r_max = 1.0;
+ $credit_leg1 = $r * $credit;
+ $credit_leg2 = $credit - $credit_leg1;
+ $timeout_leg1_old = 0;
+ $timeout_leg2_old = 0;
+
+ // RateEngine_leg1 and RateEngine, both belong to the same call plan.
+ // need to halve the free time to call.
+ $RateEngine->ratecard_obj[$K][46] = $RateEngine->ratecard_obj[$K][46] / 2;
+
+ for (;;) {
+ $timeout_leg1 = $RateEngine_leg1->rate_engine_calcultimeout($this, $credit_leg1);
+ $timeout_leg2 = $RateEngine->rate_engine_calcultimeout($this, $credit_leg2, $K);
+ //$this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - CREDIT LEG1 = $credit_leg1; TIMEOUT LEG1 = $timeout_leg1; CREDIT LEG2 = $credit_leg2; TIMEOUT LEG2 = $timeout_leg2");
+
+ if ((substr($timeout_leg1, 0, 5) == 'ERROR') || (substr($timeout_leg2, 0, 5) == 'ERROR')) {
+ $prompt = "prepaid-no-enough-credit";
+ $agi->stream_file($prompt, '#');
+ return -1;
+ }
+
+ if ($timeout_leg1 > $timeout_leg2) {
+ $r_max = $r;
+ $r = ($r + $r_min) / 2;
+ }
+ else {
+ $r_min = $r;
+ $r = ($r + $r_max) / 2;
+ }
+
+ if (($timeout_leg1 == $timeout_leg1_old) && ($timeout_leg2 == $timeout_leg2_old)) {
+ $this->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - CALCULTIMEOUT: K = $K - TIMEOUT = $timeout_leg2");
+ break;
+ }
+
+ $credit_leg1 = $r * $credit;
+ $credit_leg2 = $credit - $credit_leg1;
+ $timeout_leg1_old = $timeout_leg1;
+ $timeout_leg2_old = $timeout_leg2;
+ }
+ }
+ }
+ else {
+ $res_all_calcultimeout = $RateEngine->rate_engine_all_calcultimeout($this, $this->credit);
- $this -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "RES_ALL_CALCULTIMEOUT ::> $res_all_calcultimeout");
- if (!$res_all_calcultimeout){
- $prompt="prepaid-no-enough-credit";
- $agi-> stream_file($prompt, '#');
- return -1;
+ $this -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "RES_ALL_CALCULTIMEOUT ::> $res_all_calcultimeout");
+ if (!$res_all_calcultimeout){
+ $prompt="prepaid-no-enough-credit";
+ $agi-> stream_file($prompt, '#');
+ return -1;
+ }
}
This is where the timeout for the second leg is computed. The basic idea is to divide the available credit into 2 parts, one is used to fund the first leg and the other is used to fund the second leg.
Ideally, we want to divide the credit such that it gives the same amount of timeout for both legs. However, it is not always possible, especially when ur billing block is large. Consider the following example:
available credit = 5 cents.
rate for leg1 = 1 cent/minute.
billing block for leg1 = 60 seconds.
rate for leg2 = 1 cent/minute.
billing block for leg2 = 60 seconds.
Here, we can't divide the credit into 2.5 cents each, because the billing block is 60 seconds. So, we can either divide the credit into 3 cents - 2 cents, or 2 cents - 3 cents.
My patch will divide the credit into 3 cents - 2 cents in this case, 3 cents for first leg and 2 cents for second leg. Effectively, the call will last only for 2 minutes. And after the call is terminated, there should be 1 cent left in the credit.
So, here's what I've done:
1. Get the maximum "ring" time for the second leg. This is obtained from $this->agiconfig[ 'dialcommand_param' ].
2. Get leg 1 answered time so far, up to the point where we're about to compute the timeout for the second leg.
3. Find the sum of times obtain from one and two, and compute the cost for that duration using first leg's rate.
4. Substract the cost computed in 3 from the available credit. This will be the effective credit that will be used for the timeout computation.
5. Since both rates belong to the same call plan, we divide the free time to call, if any, by 2, for each ratecard object before computing the timeout.
6. Then there's the loop that compute the timeout.
The second part is:
Code:
@@ -918,6 +932,15 @@
if ($typecall==1) $timeout = $A2B -> config["callback"]['predictivedialer_maxtime_tocall'];
+ // if callback, need to fine tune the timeout.
+ global $G_callback_timeout_computed_time;
+ if ($G_callback_timeout_computed_time > 0) {
+ $elapsedtime = time() - $G_callback_timeout_computed_time;
+ $computedtimeout = $timeout;
+ $timeout = $timeout - $elapsedtime;
+ $A2B->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - COMPUTED TIMEOUT = $computedtimeout; ELAPSED TIME SINCE THEN = $elapsedtime; EFFECTIVE TIMEOUT = $timeout");
+ }
+
$dialparams = str_replace("%timeout%", $timeout *1000, $A2B->agiconfig['dialcommand_param']);
//$dialparams = "|30|HS($timeout)"; // L(".$timeout*1000.":61000:30000)
@@ -1038,6 +1061,15 @@
$destination= substr($destination, strlen($removeprefix));
}
+ // if callback, need to fine tune the timeout.
+ global $G_callback_timeout_computed_time;
+ if ($G_callback_timeout_computed_time > 0) {
+ $elapsedtime = time() - $G_callback_timeout_computed_time;
+ $computedtimeout = $timeout;
+ $timeout = $timeout - $elapsedtime;
+ $A2B->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "CALLBACK - COMPUTED TIMEOUT = $computedtimeout; ELAPSED TIME SINCE THEN = $elapsedtime; EFFECTIVE TIMEOUT = $timeout");
+ }
+
$dialparams = str_replace("%timeout%", $timeout *1000, $A2B->agiconfig['dialcommand_param']);
if ($pos_dialingnumber !== false){
Here, we compute the elapsed time since the timeout was computed, and substract that from the computed timeout. This is necessary because after the timeout was computed, there's a possibility that 5 to 10 seconds will elapse due to the IVR saying the time to call.
I've also modified the check for minimum credit to call:
Code:
@@ -396,9 +399,20 @@
$this -> ratecard_obj[$K]['timeout']=0;
// CHECK IF THE USER IS ALLOW TO CALL WITH ITS CREDIT AMOUNT
+ $freetimetocall_used = 0;
+ $this->usedratecard = $K;
+ $this->rate_engine_calculcost($A2B, 1, 0, 0);
+ $mincredit = -($this->lastcost);
+ //global $agi;
+ //$A2B->debug(VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "MIN CREDIT REQUIRED = $mincredit");
+ if ($credit < $mincredit) {
+ return "ERROR CT1"; // NOT ENOUGH CREDIT.
+ }
+ /*
if ($credit < $A2B->agiconfig['min_credit_2call']){
return "ERROR CT1"; //NO ENOUGH CREDIT TO CALL THIS NUMBER
}
+ */
// if ($rateinitial==0) return "ERROR RATEINITIAL($rateinitial)";
$TIMEOUT = 0;
Here, we compute the cost of 1 second call and that's the minimum credit required.
Now, if you have:
dialcommand_param = "|60|HL(%timeout%:60000:30000)"
in a2billing.conf, it means that we will reserve 60 seconds worth of conversation. Asiby, u may not like it but I agree with krzykat that there's nothing we can do about it unless there's a way for us to modify the timeout right after the second leg is established. Only then we know the exact ring time.
If u're worried about the clients losing money, please take note that we are not taking anything from the clients. After the call terminated, we will still charge the client accordingly, we don't charge extra due to the 60 seconds reserving thing.
Imagine a client has 5 cents credit in his account. Now, he wants to call a country which has the following rate:
rate = 3 cents/minute.
billing block = 120 seconds.
Of course, the client will not be able to call this particular destination, but he did not lose any money, the money is still there, he can use it to call other destinations.
Same thing here, some of the credit that we previously reserved will still be in the account, which the client can use for normal (non-callback) calls.
Man, please pardon my english.. hope I don't confuse anyone
regards,
-rs-