]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Added new options to nibble bill for minimum charges and rounding #FS-7560
authorAndrew Cassidy <andrew@orbital.net>
Mon, 4 May 2015 11:52:08 +0000 (12:52 +0100)
committerAndrew Cassidy <andee@bytz.co.uk>
Mon, 1 Jun 2015 18:39:03 +0000 (19:39 +0100)
These options are:

1) nibble_rounding: The number of decimal places to round the cost of the call to
2) nibble_minimum: The minimum charge to apply to a call

Rounding is applied before the minimum charging, and these steps only occur at call end to make sure they're accurate.

Checks for minimum_charge before call starts

"Billing done" flag to the data struct, and used that to exit early in the event processing during hangup has already occured.

src/mod/applications/mod_nibblebill/mod_nibblebill.c

index 39a799c1ec9ac8752cf83b57d4704d4c23f85856..a06e0c8d94fedad2eb21579100a4785f87b0411e 100644 (file)
@@ -50,6 +50,7 @@
  */
 
 #include <switch.h>
+#include <math.h>
 
 typedef struct {
        switch_time_t lastts;           /* Last time we did any billing */
@@ -59,6 +60,7 @@ typedef struct {
        double bill_adjustments;        /* Adjustments to make to the next billing, based on pause/resume events */
 
        int lowbal_action_executed;     /* Set to 1 once lowbal_action has been executed */
+       int final_bill_done;            /* Set to 1 one the final rounding has been done on a call to prevent spurious rebills on hangup */
 } nibble_data_t;
 
 
@@ -454,6 +456,12 @@ static switch_status_t do_billing(switch_core_session_t *session)
        double nobal_amt = globals.nobal_amt;
        double lowbal_amt = globals.lowbal_amt;
        double balance;
+       double minimum_charge = 0;
+       double rounding_factor = 1;
+       double excess = 0;
+       double rounded_billed = 0;
+       int billsecs = 0;
+       double balance_check = 0;
 
        if (!session) {
                /* Why are we here? */
@@ -480,6 +488,16 @@ static switch_status_t do_billing(switch_core_session_t *session)
                lowbal_amt = atof(switch_channel_get_variable(channel, "lowbal_amt"));
        }
        
+       if (!zstr(switch_channel_get_variable(channel, "nibble_rounding"))) {
+               rounding_factor = pow(10, atof(switch_channel_get_variable(channel, "nibble_rounding")));
+       }
+
+       if (!zstr(switch_channel_get_variable(channel, "nibble_minimum"))) {
+               minimum_charge = atof(switch_channel_get_variable(channel, "nibble_minimum"));
+       }
+
+
+
        /* Return if there's no billing information on this session */
        if (!billrate || !billaccount) {
                return SWITCH_STATUS_SUCCESS;
@@ -501,8 +519,9 @@ static switch_status_t do_billing(switch_core_session_t *session)
 
                /* See if this person has enough money left to continue the call */
                balance = get_balance(billaccount, channel);
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Comparing %f to hangup balance of %f\n", balance, nobal_amt);
-               if (balance <= nobal_amt) {
+               balance_check = balance - minimum_charge;
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Comparing %f to hangup balance of %f, taking into account minimum charge of %f\n", balance, nobal_amt, minimum_charge);
+               if (balance_check <= nobal_amt) {
                        /* Not enough money - reroute call to nobal location */
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Balance of %f fell below allowed amount of %f! (Account %s)\n",
                                                          balance, nobal_amt, billaccount);
@@ -530,6 +549,12 @@ static switch_status_t do_billing(switch_core_session_t *session)
                return SWITCH_STATUS_SUCCESS;
        }
 
+       if (nibble_data && nibble_data->final_bill_done) {
+               switch_mutex_lock(globals.mutex);
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Received heartbeat, but final bill has been committed - ignoring\n");
+               return SWITCH_STATUS_SUCCESS;
+       }
+
        /* Have we done any billing on this channel yet? If no, set up vars for doing so */
        if (!nibble_data) {
                nibble_data = switch_core_session_alloc(session, sizeof(*nibble_data));
@@ -543,8 +568,10 @@ static switch_status_t do_billing(switch_core_session_t *session)
        switch_time_exp_lt(&tm, nibble_data->lastts);
        switch_strftime_nocheck(date, &retsize, sizeof(date), "%Y-%m-%d %T", &tm);
 
+       billsecs = (int) ((ts - nibble_data->lastts) / 1000000);
+
        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%d seconds passed since last bill time of %s\n",
-                                         (int) ((ts - nibble_data->lastts) / 1000000), date);
+                                         billsecs, date);
 
        if ((ts - nibble_data->lastts) >= 0) {
                /* If billincrement is set we bill by it and not by time elapsed */
@@ -576,6 +603,29 @@ static switch_status_t do_billing(switch_core_session_t *session)
                } else {
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failed to log to database!\n");
                }
+
+               /* Do Rounding and minimum charge during hangup */
+               if (switch_channel_get_state(channel) == CS_HANGUP) {
+                       /* we're going to make an assumption that final billing is done here. So we'll see how this goes. */
+                       /* round total billed up as required */
+
+                       rounded_billed = ceilf(nibble_data->total * rounding_factor) / rounding_factor;
+
+                       if (rounded_billed < minimum_charge)
+                       {
+                               excess = minimum_charge - nibble_data->total;
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Applying minimum charge of %f (%f excess)\n", minimum_charge, excess);
+                       }
+                       else if (nibble_data->total < rounded_billed)
+                       {
+                               excess = rounded_billed - nibble_data->total;
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Rounding to precision %f, total %f (%f excess)\n", rounding_factor, rounded_billed, excess);
+                       }
+                       bill_event(excess, billaccount, channel);
+                       nibble_data->total += excess;
+                       switch_channel_set_variable_printf(channel, "nibble_total_billed", "%f", nibble_data->total);
+                       nibble_data->final_bill_done = 1;
+               }
        } else {
                if (switch_strlen_zero(billincrement))
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Just tried to bill %s negative minutes! That should be impossible.\n", uuid);
@@ -951,10 +1001,11 @@ static switch_status_t process_hangup(switch_core_session_t *session)
        do_billing(session);
 
        billaccount = switch_channel_get_variable(channel, globals.var_name_account);
+
        if (billaccount) {
                switch_channel_set_variable_printf(channel, "nibble_current_balance", "%f", get_balance(billaccount, channel));
-       }                       
-       
+       }
+
        return SWITCH_STATUS_SUCCESS;
 }