diff --git a/rate-o-mat.pl b/rate-o-mat.pl index a7be57b..9fc4de9 100755 --- a/rate-o-mat.pl +++ b/rate-o-mat.pl @@ -634,8 +634,8 @@ EOS " ?," . #_reseller_cost," . " 1," . " if(? > 0," . #_fraud_use_reseller_rates - " if(? >= ?,1,0)," . #_reseller_cost _fraud_interval_limit - " if(? >= ?,1,0))," . #_customer_cost _fraud_interval_limit + " if(coalesce(? + 0.0 >= ? + 0.0,0),1,0)," . #_reseller_cost _fraud_interval_limit + " if(coalesce(? + 0.0 >= ? + 0.0,0),1,0))," . #_customer_cost _fraud_interval_limit " ?," . #_fraud_limit_type," . " ?," . #_cdr_start_time," . " ?," . #_cdr_id," . @@ -644,23 +644,23 @@ EOS ") ON DUPLICATE KEY UPDATE " . #billing_profile_id = _billing_profile_id, " id = LAST_INSERT_ID(id)," . #_customer_cost," . - " customer_cost = customer_cost + ?," . #_customer_cost," . - " reseller_cost = reseller_cost + ?," . #_reseller_cost," . - " cdr_count = cdr_count + 1," . " fraud_limit_exceeded = if(? > 0," . #_fraud_use_reseller_rates - " if(reseller_cost + ? >= ?,1,0)," . #_reseller_cost _fraud_interval_limit - " if(customer_cost + ? >= ?,1,0))," . #_customer_cost _fraud_interval_limit + " if(coalesce(? + reseller_cost >= ? + 0.0,0),1,0)," . #_reseller_cost _fraud_interval_limit + " if(coalesce(? + customer_cost >= ? + 0.0,0),1,0))," . #_customer_cost _fraud_interval_limit + " customer_cost = ? + customer_cost," . #_customer_cost," . + " reseller_cost = ? + reseller_cost," . #_reseller_cost," . + " cdr_count = cdr_count + 1," . " fraud_limit_type = ?," . #_fraud_limit_type - " first_cdr_start_time = if(? < first_cdr_start_time," . #_cdr_start_time + " first_cdr_start_time = if(? + 0.0 < first_cdr_start_time," . #_cdr_start_time " ?," . #_cdr_start_time " first_cdr_start_time)," . - " first_cdr_id = if(? < first_cdr_id," . #_cdr_id + " first_cdr_id = if(? + 0 < first_cdr_id," . #_cdr_id " ?," . #_cdr_id " first_cdr_id)," . - " last_cdr_start_time = if(? > last_cdr_start_time," . #_cdr_start_time + " last_cdr_start_time = if(? + 0.0 > last_cdr_start_time," . #_cdr_start_time " ?," . #_cdr_start_time " last_cdr_start_time)," . - " last_cdr_id = if(? > last_cdr_id," . #_cdr_id + " last_cdr_id = if(? + 0 > last_cdr_id," . #_cdr_id " ?," . #_cdr_id " last_cdr_id)"; @@ -1285,7 +1285,7 @@ sub add_period_costs { $month_lock = undef; } - $upsert_sth->execute($contract_id, + my @bind_params = ($contract_id, "month", $month_period_date, $direction, @@ -1293,8 +1293,8 @@ sub add_period_costs { $reseller_cost, $fraud_use_reseller_rates, - $reseller_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), - $customer_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), + $reseller_cost, $fraud_limit, + $customer_cost, $fraud_limit, $fraud_limit_type, $stime, @@ -1302,17 +1302,29 @@ sub add_period_costs { $stime, $cdr_id, - $customer_cost, - $reseller_cost, $fraud_use_reseller_rates, - $reseller_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), - $customer_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), + $reseller_cost, $fraud_limit, + $customer_cost, $fraud_limit, + + $customer_cost, + $reseller_cost, $fraud_limit_type, - $stime + 0.0,$stime,$cdr_id,$cdr_id, - $stime + 0.0,$stime,$cdr_id,$cdr_id, + $stime,$stime,$cdr_id,$cdr_id, + $stime,$stime,$cdr_id,$cdr_id, + ); + + DEBUG sub { "month fraud check: ".(Dumper { + fraud_limit => $fraud_limit, + fraud_limit_type => $fraud_limit_type, + month_lock => $month_lock, + bind => \@bind_params, + }) }; + + $upsert_sth->execute( + @bind_params ) or FATAL "Error executing upsert cdr month period costs statement: ".$upsert_sth->errstr; $get_sth->execute() or FATAL "Error executing get cdr day period costs statement: ".$get_sth->errstr; @@ -1338,7 +1350,8 @@ sub add_period_costs { $daily_lock = undef; } - $upsert_sth->execute($contract_id, + @bind_params = ( + $contract_id, "day", $day_period_date, $direction, @@ -1346,8 +1359,8 @@ sub add_period_costs { $reseller_cost, $fraud_use_reseller_rates, - $reseller_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), - $customer_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), + $reseller_cost, $fraud_limit, + $customer_cost, $fraud_limit, $fraud_limit_type, $stime, @@ -1355,17 +1368,28 @@ sub add_period_costs { $stime, $cdr_id, + $fraud_use_reseller_rates, + $reseller_cost, $fraud_limit, + $customer_cost, $fraud_limit, + $customer_cost, $reseller_cost, - $fraud_use_reseller_rates, - $reseller_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), - $customer_cost + 0.0, (defined $fraud_limit ? $fraud_limit + 0.0 : POSIX::INT_MAX), - $fraud_limit_type, - $stime + 0.0,$stime,$cdr_id,$cdr_id, - $stime + 0.0,$stime,$cdr_id,$cdr_id, + $stime,$stime,$cdr_id,$cdr_id, + $stime,$stime,$cdr_id,$cdr_id, + ); + + DEBUG sub { "day fraud check: ".(Dumper { + fraud_limit => $fraud_limit, + fraud_limit_type => $fraud_limit_type, + daily_lock => $daily_lock, + bind => \@bind_params, + }) }; + + $upsert_sth->execute( + @bind_params ) or FATAL "Error executing upsert cdr day period costs statement: ".$upsert_sth->errstr; $get_sth->execute() or FATAL "Error executing get cdr day period costs statement: ".$get_sth->errstr; @@ -2003,7 +2027,8 @@ sub update_cdr { if ($sth->rows > 0) { DEBUG "cdr ID $cdr->{id} updated"; - my $fraud_lock = add_period_costs(0, + my $fraud_lock; + $fraud_lock = add_period_costs(0, $cdr->{id}, $cdr->{source_account_id}, $cdr->{start_time}, @@ -2011,7 +2036,7 @@ sub update_cdr { $cdr->{source_customer_billing_profile_id}, $cdr->{source_customer_cost}, $cdr->{source_reseller_cost}, - ); + ) unless $dupdbh; write_cdr_cols($cdr,$cdr->{id}, $acc_cash_balance_col_model_key, $acc_time_balance_col_model_key, diff --git a/t/Utils/Api.pm b/t/Utils/Api.pm index d86f9b2..404e180 100644 --- a/t/Utils/Api.pm +++ b/t/Utils/Api.pm @@ -785,6 +785,7 @@ sub _setup_fees { my $peaktime_specials = delete $params{peaktime_special}; my $interval_free_time = delete $params{interval_free_time}; my $interval_free_cash = delete $params{interval_free_cash}; + my @fraud_params = map { $_ => delete $params{$_} } grep { rindex($_,'fraud') == 0 ; } keys %params; my $profile = create_billing_profile( reseller_id => $reseller->{id}, (defined $prepaid ? (prepaid => $prepaid) : ()), @@ -792,6 +793,7 @@ sub _setup_fees { (defined $peaktime_specials ? (peaktime_special => $peaktime_specials) : ()), (defined $interval_free_time ? (interval_free_time => $interval_free_time) : ()), (defined $interval_free_cash ? (interval_free_cash => $interval_free_cash) : ()), + @fraud_params, ); my $zone = create_billing_zone( billing_profile_id => $profile->{id}, diff --git a/t/rateomat-50-fraud.t b/t/rateomat-50-fraud.t new file mode 100644 index 0000000..560031e --- /dev/null +++ b/t/rateomat-50-fraud.t @@ -0,0 +1,154 @@ +use strict; +use warnings; + +use File::Basename; +use Cwd; +use lib Cwd::abs_path(File::Basename::dirname(__FILE__)); + +use Utils::Api qw(); +use Utils::Rateomat qw(); +use Test::More; + +### testcase outline: +### onnet prepaid/postpaid calls of callers to callees with both using +### dedicated reseller fees. +### +### this tests verify all combinations of prepaid/postpaid subscriber customers with +### balance > 0.0/no balance produce correct customer/reseller call cost, cash balance +### and cash balance interval values. + +local $ENV{RATEOMAT_WRITE_CDR_RELATION_DATA} = 1; +local $ENV{RATEOMAT_PREPAID_UPDATE_BALANCE} = 1; + +my $init_secs = 60; +my $follow_secs = 30; +my $provider_a = create_provider('testa.com'); +my $provider_b = create_provider('testb.com'); + +my $total_caller_reseller_call_costs = 0.0; +my $total_callee_reseller_call_costs = 0.0; + +{ + my $balance = 0; + my $caller_fee = $provider_a->{subscriber_fees}->[0]; + my $caller = Utils::Api::setup_subscriber($provider_a,$caller_fee->{profile},$balance,{ cc => 888, ac => '1', sn => '' }); + my $callee_fee = $provider_b->{subscriber_fees}->[0]; + my $callee = Utils::Api::setup_subscriber($provider_b,$callee_fee->{profile},$balance,{ cc => 888, ac => '2', sn => '' }); + + my $caller_call_costs = $caller_fee->{fees}->[0]->{onpeak_init_rate} * + $caller_fee->{fees}->[0]->{onpeak_init_interval} + + $caller_fee->{fees}->[0]->{onpeak_follow_rate} * + $caller_fee->{fees}->[0]->{onpeak_follow_interval}; + my $caller_reseller_call_costs = $provider_a->{provider_fee}->{fees}->[0]->{onpeak_init_rate} * + $provider_a->{provider_fee}->{fees}->[0]->{onpeak_init_interval} + + $provider_a->{provider_fee}->{fees}->[0]->{onpeak_follow_rate} * + $provider_a->{provider_fee}->{fees}->[0]->{onpeak_follow_interval}; + + my $callee_call_costs = $callee_fee->{fees}->[1]->{onpeak_init_rate} * + $callee_fee->{fees}->[1]->{onpeak_init_interval} + + $callee_fee->{fees}->[1]->{onpeak_follow_rate} * + $callee_fee->{fees}->[1]->{onpeak_follow_interval}; + my $callee_reseller_call_costs = $provider_b->{provider_fee}->{fees}->[1]->{onpeak_init_rate} * + $provider_b->{provider_fee}->{fees}->[1]->{onpeak_init_interval} + + $provider_b->{provider_fee}->{fees}->[1]->{onpeak_follow_rate} * + $provider_b->{provider_fee}->{fees}->[1]->{onpeak_follow_interval}; + + my $start_time = Utils::Api::current_unix() - 5; + my @cdr_ids = map { $_->{id}; } @{ Utils::Rateomat::create_cdrs([ map { + Utils::Rateomat::prepare_cdr($_->{subscriber},undef,$_->{reseller}, + $callee->{subscriber},undef,$callee->{reseller}, + '192.168.0.1',$start_time += 1,$init_secs + $follow_secs); + } ($caller,$caller) ]) }; + + if (ok((scalar @cdr_ids) > 0 && Utils::Rateomat::run_rateomat_threads(),'rate-o-mat executed')) { + my $caller_cdr_map = {}; + + ok(Utils::Rateomat::check_cdrs('x: ', + map { + my $cdr = Utils::Rateomat::get_cdrs($_); + $caller_cdr_map->{$cdr->{source_account_id}} = $_; + $_ => { id => $_, + rating_status => 'ok', + source_customer_cost => Utils::Rateomat::decimal_to_string($caller_call_costs), + destination_customer_cost => Utils::Rateomat::decimal_to_string($callee_call_costs), + source_reseller_cost => Utils::Rateomat::decimal_to_string($caller_reseller_call_costs), + destination_reseller_cost => Utils::Rateomat::decimal_to_string($callee_reseller_call_costs), + }; + } @cdr_ids + ),'cdrs were all processed'); + + + } +} + +done_testing(); +exit; + +sub create_provider { + my $domain = shift; + return Utils::Api::setup_provider($domain, + [ #subscriber rates: + { prepaid => 0, + fraud_interval_limit => 301, #160, + fraud_interval_lock => 3, + fraud_interval_notify => '', + fraud_daily_limit => 301, + fraud_daily_lock => 4, + fraud_daily_notify => undef, + fraud_use_reseller_rates => 1, #1, + fees => [{ #outgoing: + direction => 'out', + destination => '^8882.+', + onpeak_init_rate => 6, + onpeak_init_interval => $init_secs, + onpeak_follow_rate => 1, + onpeak_follow_interval => $follow_secs, + offpeak_init_rate => 6, + offpeak_init_interval => $init_secs, + offpeak_follow_rate => 1, + offpeak_follow_interval => $follow_secs, + }, + { #incoming: + direction => 'in', + destination => '.', + source => '^8881.+', + onpeak_init_rate => 5, + onpeak_init_interval => $init_secs, + onpeak_follow_rate => 1, + onpeak_follow_interval => $follow_secs, + offpeak_init_rate => 5, + offpeak_init_interval => $init_secs, + offpeak_follow_rate => 1, + offpeak_follow_interval => $follow_secs, + }]}, + ], + undef, # no billing networks in this test suite + # provider rate: + { prepaid => 0, + fees => [{ #outgoing: + direction => 'out', + destination => '^888.+', + onpeak_init_rate => 2, + onpeak_init_interval => $init_secs, + onpeak_follow_rate => 1, + onpeak_follow_interval => $follow_secs, + offpeak_init_rate => 2, + offpeak_init_interval => $init_secs, + offpeak_follow_rate => 1, + offpeak_follow_interval => $follow_secs, + }, + { #incoming: + direction => 'in', + destination => '.', + source => '^888.+', + onpeak_init_rate => 1, + onpeak_init_interval => $init_secs, + onpeak_follow_rate => 1, + onpeak_follow_interval => $follow_secs, + offpeak_init_rate => 1, + offpeak_init_interval => $init_secs, + offpeak_follow_rate => 1, + offpeak_follow_interval => $follow_secs, + }]}, + ); +}