TT#70859 extra_second fees

charge the "extra rate" at "extra second" of call time.

while it is included in the value of
"(source|destination)_(carrier|reselller|customer)_cost", it also
has to be recorded explicitly, using
(source|destination)_(carrier|reseller|customer)_extra_rate
dcr_tag.

Change-Id: I016977a7ed54f45a67513595980a14f9847c3a4b
changes/24/36024/5
Rene Krenn 5 years ago
parent 1312e315e2
commit 9e25252d36

@ -530,9 +530,9 @@ EOS
"onpeak_follow_rate, onpeak_follow_interval, ".
"offpeak_init_rate, offpeak_init_interval, ".
"offpeak_follow_rate, offpeak_follow_interval, ".
"billing_zones_history_id, use_free_time ".
#"onpeak_extra_second, onpeak_extra_rate, ".
#"offpeak_extra_second, offpeak_extra_rate ".
"billing_zones_history_id, use_free_time, ".
"onpeak_extra_second, onpeak_extra_rate, ".
"offpeak_extra_second, offpeak_extra_rate ".
"FROM billing.billing_fees_history WHERE id = billing.get_billing_fee_id(?,?,?,?,?,null)"
) or FATAL "Error preparing profile info statement: ".$billdbh->errstr;
@ -1896,10 +1896,10 @@ sub get_profile_info {
$b_info->{off_follow_interval} = $res[10] == 0 ? 1 : $res[10];
$b_info->{zone_id} = $res[11];
$b_info->{use_free_time} = $res[12];
#$b_info->{on_extra_second} = $res[13];
#$b_info->{on_extra_rate} = $res[14];
#$b_info->{off_extra_second} = $res[15];
#$b_info->{off_extra_rate} = $res[16];
$b_info->{on_extra_second} = $res[13];
$b_info->{on_extra_rate} = $res[14];
$b_info->{off_extra_second} = $res[15];
$b_info->{off_extra_rate} = $res[16];
$sth->finish;
@ -2069,7 +2069,8 @@ sub update_cdr {
write_cdr_cols($cdr,$cdr->{id},
$acc_cash_balance_col_model_key,
$acc_time_balance_col_model_key,
$acc_relation_col_model_key);
$acc_relation_col_model_key,
$acc_tag_col_model_key);
if ($dupdbh) {
$sth_duplicate_cdr->execute(@$cdr{@cdr_fields})
or FATAL "Error executing duplicate cdr statement: ".$sth_duplicate_cdr->errstr;
@ -2090,7 +2091,8 @@ sub update_cdr {
write_cdr_cols($cdr,$dup_cdr_id,
$dup_cash_balance_col_model_key,
$dup_time_balance_col_model_key,
$dup_relation_col_model_key);
$dup_relation_col_model_key,
$dup_tag_col_model_key);
copy_cdr_col_data($acc_tag_col_model_key,$dup_tag_col_model_key,$cdr,$cdr->{id},$dup_cdr_id,
{ direction => 'destination', provider => 'customer', tag => 'furnished_charging_info' });
@ -2127,6 +2129,7 @@ sub write_cdr_cols {
my $cash_balance_col_model_key = shift;
my $time_balance_col_model_key = shift;
my $relation_col_model_key = shift;
my $tag_col_model_key = shift;
foreach my $dir (('source', 'destination')) {
foreach my $provider (('carrier','reseller','customer')) {
@ -2148,6 +2151,10 @@ sub write_cdr_cols {
{ direction => $dir, provider => $provider, relation => 'contract_balance_id' },
$cdr->{$dir.'_'.$provider."_contract_balance_id"}) if $write_contract_balance_id;
write_cdr_col_data($tag_col_model_key,$cdr,$cdr_id,
{ direction => $dir, provider => $provider, tag => 'extra_rate' },
$cdr->{$dir.'_'.$provider."_extra_rate"});
}
}
@ -2170,6 +2177,7 @@ sub get_call_cost {
my $r_free_time = shift;
my $r_rating_duration = shift;
my $r_onpeak = shift;
my $r_extra_rate = shift;
my $r_balances = shift;
my $src_user;
@ -2210,8 +2218,8 @@ sub get_call_cost {
DEBUG sub { "billing fee is ".(Dumper $r_profile_info) };
my @offpeak = ();
get_offpeak($profile_id, $subscriber_contract_id, $cdr->{start_time},
$cdr->{duration}, \@offpeak) or
get_offpeak($profile_id, $subscriber_contract_id, $cdr->{_start_time},
$cdr->{start_time} - $cdr->{_start_time} + $cdr->{duration}, \@offpeak) or
FATAL "Error getting offpeak info\n";
DEBUG sub { "offpeak info: " . Dumper \@offpeak; };
@ -2222,9 +2230,16 @@ sub get_call_cost {
my $rate = 0;
my $offset = 0;
my $onpeak = 0;
#my $extra_second = undef;
#my $extra_rate = undef;
my $init = $cdr->{is_fragmented} // 0;
my $extra_second;
my $extra_rate;
if (is_offpeak($cdr->{_start_time}, 0, \@offpeak)) {
$extra_second = $r_profile_info->{off_extra_second};
$extra_rate = $r_profile_info->{off_extra_rate} // 0.0;
} else {
$extra_second = $r_profile_info->{on_extra_second};
$extra_rate = $r_profile_info->{on_extra_rate} // 0.0;
}
my $duration = (defined $cdr->{rating_duration} and $cdr->{rating_duration} < $cdr->{duration}) ? $cdr->{rating_duration} : $cdr->{duration};
my $prev_bal_id = undef;
my @cash_balance_rates = ();
@ -2259,10 +2274,6 @@ sub get_call_cost {
$r_profile_info->{on_init_interval} : $r_profile_info->{off_init_interval};
$rate = $onpeak == 1 ?
$r_profile_info->{on_init_rate} : $r_profile_info->{off_init_rate};
#$extra_second = $onpeak == 1 ?
# $r_profile_info->{on_extra_second} : $r_profile_info->{off_extra_second};
#$extra_rate = $onpeak == 1 ?
# $r_profile_info->{on_extra_second} : $r_profile_info->{off_extra_second};
DEBUG "add init rate $rate per sec to costs";
} else {
$interval = $onpeak == 1 ?
@ -2341,6 +2352,18 @@ sub get_call_cost {
DEBUG "calculate cost for remaining interval chunk $interval";
}
if (defined $extra_second) {
my $extra_second_time = int($cdr->{_start_time}) + $extra_second;
if ($extra_second_time >= $current_call_time
and $extra_second_time < ($current_call_time + $interval)
and int($cdr->{start_time}) <= $extra_second_time) {
DEBUG "add extra second ($extra_second) cost $extra_rate to rate $rate";
$rate += $extra_rate;
$$r_extra_rate = $extra_rate;
undef $extra_second;
}
}
if (($rate > 0 || $prepaid) and $rate <= $bal->{cash_balance}) {
DEBUG "we still have cash balance $$bal{cash_balance} left, subtract rate $rate from that";
$bal->{cash_balance} -= $rate;
@ -2748,6 +2771,7 @@ sub get_customer_call_cost {
my $r_rating_duration = shift;
my $onpeak;
my $real_cost = 0;
my $extra_rate;
my $dir;
if($direction eq "out") {
@ -2795,7 +2819,7 @@ sub get_customer_call_cost {
get_call_cost($cdr, $type, $direction,$contract_id,$subscriber_contract_id,
$billing_info{profile_id}, $readonly || ($outgoing_prepaid && defined $prepaid_cost_entry), $prepaid,
\%profile_info, \%package_info, $r_cost, \$real_cost, $r_free_time,
$r_rating_duration, \$onpeak, \@balances)
$r_rating_duration, \$onpeak, \$extra_rate, \@balances)
or FATAL "Error getting ".$dir."customer call cost\n";
DEBUG "got call cost $$r_cost and free time $$r_free_time";
@ -2811,6 +2835,7 @@ sub get_customer_call_cost {
$cdr->{$dir."customer_billing_fee_id"} = $profile_info{fee_id};
$cdr->{$dir."customer_billing_zone_id"} = $profile_info{zone_id};
$cdr->{frag_customer_onpeak} = $onpeak if $split_peak_parts;
$cdr->{$dir."customer_extra_rate"} = $extra_rate;
if ($outgoing_prepaid) { #prepaid out
# overwrite the calculated costs with the ones from our table
@ -2873,6 +2898,7 @@ sub get_provider_call_cost {
my $r_rating_duration = shift;
my $onpeak;
my $real_cost = 0;
my $extra_rate;
my $dir;
if($direction eq "out") {
@ -2903,7 +2929,7 @@ sub get_provider_call_cost {
get_call_cost($cdr, $type, $direction,$contract_id,$subscriber_contract_id,
$provider_info->{billing}->{profile_id}, $readonly || $prepaid, $prepaid, # no underruns for providers with prepaid profile
\%profile_info, $provider_info->{package}, $r_cost, \$real_cost, $r_free_time,
$r_rating_duration, \$onpeak, $provider_info->{balances})
$r_rating_duration, \$onpeak, \$extra_rate, $provider_info->{balances})
or FATAL "Error getting ".$dir."provider call cost\n";
my $snapshot_bal = get_snapshot_contract_balance($provider_info->{balances});
@ -2914,6 +2940,7 @@ sub get_provider_call_cost {
$cdr->{$dir.$provider_type."billing_fee_id"} = $profile_info{fee_id};
$cdr->{$dir.$provider_type."billing_zone_id"} = $profile_info{zone_id};
$cdr->{'frag_'.$provider_type.'onpeak'} = $onpeak if $split_peak_parts;
$cdr->{$dir.$provider_type."extra_rate"} = $extra_rate;
unless($prepaid == 1) {
$cdr->{$dir.$provider_type."cash_balance_before"} = $snapshot_bal->{cash_balance_old};

@ -38,9 +38,11 @@ our @EXPORT_OK = qw(
check_cdr_time_balance_data
check_cdr_cash_balance_data
check_cdr_relation_data
check_cdr_tag_data
get_cdr_time_balance_data
get_cdr_cash_balance_data
get_cdr_relation_data
get_cdr_tag_data
$rateomat_timeout
);
@ -736,6 +738,18 @@ sub get_cdr_relation_data {
return $result->[0]->{val};
}
sub get_cdr_tag_data {
my $label = shift;
my $cdr_id = shift;
my $direction = shift;
my $provider = shift;
my $tag = shift;
$label .= 'cdr id '.$cdr_id.' '.$direction.'_'.$provider.'_'.$tag.' ';
my $result = _get_cdr_tag_data($cdr_id,$direction,$provider,$tag);
is(scalar @$result,1,$label.'number of records '.(scalar @$result).' = 1');
return $result->[0]->{val};
}
sub get_cdr_cash_balance_data {
my $label = shift;
@ -782,6 +796,27 @@ sub check_cdr_relation_data {
}
sub check_cdr_tag_data {
my $label = shift;
my $cdr_id = shift;
my $direction = shift;
my $provider = shift;
my $tag = shift;
my $expected = shift;
$label .= 'cdr id '.$cdr_id.' '.$direction.'_'.$provider.'_'.$tag.' ';
my $result = _get_cdr_tag_data($cdr_id,$direction,$provider,$tag);
if (defined $expected) {
if ((scalar @$result) == 1) {
return is($result->[0]->{val},$expected,$label.$result->[0]->{val}.' = '.$expected);
} else {
return is(scalar @$result,1,$label.'number of records '.(scalar @$result).' = 1');
}
} else {
return is(scalar @$result,0,$label.'number of records '.(scalar @$result).' = 0');
}
}
sub check_cdr_cash_balance_data {
my $label = shift;
my $cdr_id = shift;
@ -848,6 +883,27 @@ sub _get_cdr_relation_data {
return $result;
}
sub _get_cdr_tag_data {
my $id = shift;
my $direction = shift;
my $provider = shift;
my $tag = shift;
my $result = undef;
eval {
my $dbh = _connect_accounting_db();
$result = _get_cdr_data($dbh,$id,'accounting.cdr_tag_data',[
{ type => $direction, table => 'accounting.cdr_direction', data_col => 'direction_id' },
{ type => $provider, table => 'accounting.cdr_provider', data_col => 'provider_id' },
{ type => $tag, table => 'accounting.cdr_tag', data_col => 'tag_id' },
]);
_disconnect_db($dbh);
};
if ($@) {
diag($@);
}
return $result;
}
sub _get_cdr_cash_balance_data {
my $id = shift;
my $direction = shift;
@ -920,6 +976,7 @@ sub _delete_all_cdr_data {
_delete_cdr_data($dbh,$ids,'accounting.cdr_relation_data');
_delete_cdr_data($dbh,$ids,'accounting.cdr_cash_balance_data');
_delete_cdr_data($dbh,$ids,'accounting.cdr_time_balance_data');
_delete_cdr_data($dbh,$ids,'accounting.cdr_tag_data');
}
sub _delete_cdr_data { # as long as no triggers are present

@ -60,6 +60,8 @@ local $ENV{RATEOMAT_BATCH_SIZE} = 1;
# };
#};
my $extra_rate = 100;
{
my $call_minutes = 10; #divisible by 2
@ -80,7 +82,7 @@ local $ENV{RATEOMAT_BATCH_SIZE} = 1;
my @caller_costs = (
$provider->{subscriber_fees}->[0]->{fees}->[0]->{offpeak_init_rate} * 60, #07:59:50 .. 08:00:50
$provider->{subscriber_fees}->[0]->{fees}->[0]->{onpeak_follow_rate} * 10,
$provider->{subscriber_fees}->[0]->{fees}->[0]->{onpeak_follow_rate} * 10 + $extra_rate,
(map { ($provider->{subscriber_fees}->[0]->{fees}->[0]->{offpeak_follow_rate} * 60,
$provider->{subscriber_fees}->[0]->{fees}->[0]->{onpeak_follow_rate} * 60); } (1..(int(($call_minutes - 1) / 2 + 0.99) - 1))), #4x on
@ -119,6 +121,7 @@ local $ENV{RATEOMAT_BATCH_SIZE} = 1;
Utils::Rateomat::check_cdr('cdr is fragmented: ',$cdr->{id},{ is_fragmented => '1' });
Utils::Rateomat::check_cdr('cdr is reseller onpeak: ',$cdr->{id},{ frag_reseller_onpeak => "$onpeak" });
Utils::Rateomat::check_cdr('cdr is customer onpeak: ',$cdr->{id},{ frag_customer_onpeak => "$onpeak" });
Utils::Rateomat::check_cdr_tag_data("extra rate: ",$cdr->{id},'source','customer','extra_rate',($i == 2 ? 100 : undef));
Utils::Api::is_float_approx($cdr->{source_customer_cost},$caller_costs[$i-1],'caller costs: ');
Utils::Api::check_interval_history("caller ",$caller->{customer}->{id},[{
debit => '~'.List::Util::sum(@caller_costs[0..$i-1])/100.0,
@ -199,6 +202,11 @@ sub create_provider {
offpeak_init_interval => 60,
offpeak_follow_rate => sprintf("%.10f",1/60),
offpeak_follow_interval => 1, #60,
onpeak_extra_second => 900,
onpeak_extra_rate => 200,
offpeak_extra_second => 60,
offpeak_extra_rate => $extra_rate,
},
],
@peaktimes,

@ -0,0 +1,160 @@
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 postpaid calls of callers using fees with "extra_second" rate.
###
### this tests verify charging the extra_second.
local $ENV{RATEOMAT_WRITE_CDR_RELATION_DATA} = 1;
local $ENV{RATEOMAT_PREPAID_UPDATE_BALANCE} = 1;
my $init_secs = 60;
my $follow_secs = 30;
my $extra_rate = 100;
foreach my $extra_second (undef, 0, 60, $init_secs + $follow_secs - 1,$init_secs + $follow_secs) {
my $provider_a = create_provider('testa<n>.com',$extra_second);
my $provider_b = create_provider('testb<n>.com');
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<n>', sn => '<t>' });
my $callee_fee = $provider_b->{subscriber_fees}->[0];
my $callee = Utils::Api::setup_subscriber($provider_b,$callee_fee->{profile},$balance,{ cc => 888, ac => '2<n>', sn => '<t>' });
my $duration = $init_secs + $follow_secs;
my $is_extra_second = (defined $extra_second and $extra_second < $duration);
my $_extra_rate = ($is_extra_second ? $extra_rate : 0);
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} + $_extra_rate;
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,$duration);
} ($caller) ]) };
if (ok((scalar @cdr_ids) > 0 && Utils::Rateomat::run_rateomat_threads(),'rate-o-mat executed')) {
my $caller_cdr_map = {};
my $label = 'extra_second ' . $extra_second . 'extra_rate ' . $extra_second . ': ';
ok(Utils::Rateomat::check_cdrs($label,
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');
#if ($is_extra_second) {
foreach (@cdr_ids) {
Utils::Rateomat::check_cdr_tag_data($label,$_,'source','carrier','extra_rate',undef);
Utils::Rateomat::check_cdr_tag_data($label,$_,'source','reseller','extra_rate',undef);
Utils::Rateomat::check_cdr_tag_data($label,$_,'source','customer','extra_rate',($is_extra_second ? $_extra_rate : undef));
Utils::Rateomat::check_cdr_tag_data($label,$_,'destination','carrier','extra_rate',undef);
Utils::Rateomat::check_cdr_tag_data($label,$_,'destination','reseller','extra_rate',undef);
Utils::Rateomat::check_cdr_tag_data($label,$_,'destination','customer','extra_rate',undef);
}
#}
}
}
done_testing();
exit;
sub create_provider {
my ($domain,$extra_second) = @_;
return Utils::Api::setup_provider($domain,
[ #subscriber rates:
{ prepaid => 0,
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,
onpeak_extra_second => $extra_second,
onpeak_extra_rate => $extra_rate,
offpeak_extra_second => $extra_second,
offpeak_extra_rate => $extra_rate,
},
{ #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,
}]},
);
}
Loading…
Cancel
Save