MT#14457 re-implement onpeak/offpeak CDR fragmentation

Change-Id: Ib4284d468dd441f5d3bb7f97291eb8ae9bfef4df
changes/59/3659/10
Richard Fuchs 10 years ago
parent 2081c041d7
commit aabd020937

@ -21,6 +21,11 @@ my $log_ident = 'rate-o-mat';
my $log_facility = 'daemon';
my $log_opts = 'ndely,cons,pid,nowait';
# if split_peak_parts is set to true, rate-o-mat will create a separate
# CDR every time a peak time border is crossed for either the customer,
# the reseller or the carrier billing profile.
my $split_peak_parts = 0;
# if the LNP database is used not just for LNP, but also for on-net
# billing, special routing or similar things, this should be set to
# better guess the correct LNP provider ID when selecting ported numbers
@ -53,6 +58,8 @@ my $DupDB_Port = $ENV{RATEOMAT_DUPLICATE_DB_PORT} ? int $ENV{RATEOMAT_DUPLICATE_
my $DupDB_User = $ENV{RATEOMAT_DUPLICATE_DB_USER};
my $DupDB_Pass = $ENV{RATEOMAT_DUPLICATE_DB_PASS};
$split_peak_parts = $ENV{RATEOMAT_SPLIT_PEAK_PARTS} // $split_peak_parts;
########################################################################
sub main;
@ -74,6 +81,7 @@ my $sth_offpeak_weekdays;
my $sth_offpeak_special;
my $sth_unrated_cdrs;
my $sth_update_cdr;
my $sth_create_cdr_fragment;
my $sth_provider_info;
my $sth_reseller_info;
my $sth_get_cbalances;
@ -478,17 +486,36 @@ EOS
"destination_carrier_cost = ?, destination_reseller_cost = ?, destination_customer_cost = ?, ".
"destination_carrier_free_time = ?, destination_reseller_free_time = ?, destination_customer_free_time = ?, ".
"destination_carrier_billing_fee_id = ?, destination_reseller_billing_fee_id = ?, destination_customer_billing_fee_id = ?, ".
"destination_carrier_billing_zone_id = ?, destination_reseller_billing_zone_id = ?, destination_customer_billing_zone_id = ? ".
"destination_carrier_billing_zone_id = ?, destination_reseller_billing_zone_id = ?, destination_customer_billing_zone_id = ?, ".
"frag_carrier_onpeak = ?, frag_reseller_onpeak = ?, frag_customer_onpeak = ?, is_fragmented = ?, ".
"duration = ? ".
"WHERE id = ?"
) or FATAL "Error preparing update cdr statement: ".$acctdbh->errstr;
if ($split_peak_parts) {
my @exclude_fragment_fields = qw(start_time duration is_fragmented);
my %exclude_fragment_fields = map {$_,1} @exclude_fragment_fields;
my @fragment_fields = grep {!$exclude_fragment_fields{$_}} @cdr_fields;
$sth_create_cdr_fragment = $acctdbh->prepare(
"INSERT INTO accounting.cdr
(".
join(',', @fragment_fields, @exclude_fragment_fields).
")
SELECT ".
join(',', @fragment_fields).",
start_time + ?,duration - ?,1
FROM accounting.cdr
WHERE id = ?
") or FATAL "Error preparing create cdr fragment statement: ".$acctdbh->errstr;
}
$sth_provider_info = $billdbh->prepare(
"SELECT p.class, bm.billing_profile_id ".
"FROM billing.products p, billing.billing_mappings bm ".
"WHERE bm.contract_id = ? AND bm.product_id = p.id ".
"AND (bm.start_date IS NULL OR bm.start_date <= FROM_UNIXTIME(?)) ".
"AND (bm.end_date IS NULL OR bm.end_date >= FROM_UNIXTIME(?)) ".
"ORDER BY bm.start_date, bm.id DESC ".
"ORDER BY bm.start_date DESC, bm.id DESC ".
"LIMIT 1"
) or FATAL "Error preparing provider info statement: ".$billdbh->errstr;
@ -502,7 +529,7 @@ EOS
"AND r.contract_id = bm.contract_id ".
"AND (bm.start_date IS NULL OR bm.start_date <= FROM_UNIXTIME(?)) ".
"AND (bm.end_date IS NULL OR bm.end_date >= FROM_UNIXTIME(?)) ".
"ORDER BY bm.start_date, bm.id DESC ".
"ORDER BY bm.start_date DESC, bm.id DESC ".
"LIMIT 1"
) or FATAL "Error preparing reseller info statement: ".$billdbh->errstr;
@ -1478,6 +1505,8 @@ sub update_cdr
$cdr->{destination_carrier_free_time}, $cdr->{destination_reseller_free_time}, $cdr->{destination_customer_free_time},
$cdr->{destination_carrier_billing_fee_id}, $cdr->{destination_reseller_billing_fee_id}, $cdr->{destination_customer_billing_fee_id},
$cdr->{destination_carrier_billing_zone_id}, $cdr->{destination_reseller_billing_zone_id}, $cdr->{destination_customer_billing_zone_id},
$cdr->{frag_carrier_onpeak}, $cdr->{frag_reseller_onpeak}, $cdr->{frag_customer_onpeak},
$cdr->{is_fragmented} // 0, $cdr->{duration},
$cdr->{id})
or FATAL "Error executing update cdr statement: ".$sth->errstr;
@ -1545,8 +1574,6 @@ sub get_call_cost
my $r_onpeak = shift;
my $r_balances = shift;
$$r_rating_duration = 0; # ensure we start with zero length
my $src_user = $cdr->{source_cli};
my $src_user_domain = $cdr->{source_cli}.'@'.$cdr->{source_domain};
my $dst_user = $cdr->{destination_user_in};
@ -1574,6 +1601,8 @@ sub get_call_cost
}
}
$$r_rating_duration = 0; # ensure we start with zero length
DEBUG "billing fee is ".(Dumper $r_profile_info);
#print Dumper $r_profile_info;
@ -1597,8 +1626,8 @@ sub get_call_cost
my $rate = 0;
my $offset = 0;
my $onpeak = 0;
my $init = 0;
my $duration = $cdr->{duration};
my $init = $cdr->{is_fragmented} // 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 = ();
my $prev_cash_balance = undef;
@ -1645,17 +1674,20 @@ 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};
$$r_onpeak = $onpeak;
DEBUG "add init rate $rate per sec to costs";
}
else
{
last if $split_peak_parts and defined($$r_onpeak) and $$r_onpeak != $onpeak
and !defined $cdr->{rating_duration};
$interval = $onpeak == 1 ?
$r_profile_info->{on_follow_interval} : $r_profile_info->{off_follow_interval};
$rate = $onpeak == 1 ?
$r_profile_info->{on_follow_rate} : $r_profile_info->{off_follow_rate};
DEBUG "add follow rate $rate per sec to costs";
}
$$r_onpeak = $onpeak;
$rate *= $interval;
DEBUG "interval is $interval, so rate for this interval is $rate";
@ -1818,6 +1850,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;
DEBUG "got call cost $$r_cost and free time $$r_free_time";
# we don't do prepaid for termination fees for now, so treat it as post-paid
@ -1911,6 +1944,7 @@ sub get_provider_call_cost
$cdr->{destination_reseller_billing_fee_id} = $profile_info{fee_id};
$cdr->{destination_reseller_billing_zone_id} = $profile_info{zone_id};
}
$cdr->{frag_reseller_onpeak} = $onpeak if $split_peak_parts;
}
else
{
@ -1921,6 +1955,7 @@ sub get_provider_call_cost
$cdr->{destination_carrier_billing_fee_id} = $profile_info{fee_id};
$cdr->{destination_carrier_billing_zone_id} = $profile_info{zone_id};
}
$cdr->{frag_carrier_onpeak} = $onpeak if $split_peak_parts;
}
return 1;
@ -1945,7 +1980,7 @@ sub rate_cdr
my $destination_reseller_free_time = 0;
my $direction;
my $rating_duration;
my @rating_durations;
unless($cdr->{call_status} eq "ok")
{
@ -2014,7 +2049,7 @@ sub rate_cdr
DEBUG "destination provider has billing profile $destination_provider_info{profile_id}, get reseller termination cost";
get_provider_call_cost($cdr, $type, "in",
\%destination_provider_info, \$destination_reseller_cost, \$destination_reseller_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting destination reseller cost for local destination_provider_id ".
$cdr->{destination_provider_id}." for cdr ".$cdr->{id}."\n";
DEBUG "destination reseller termination cost is $destination_reseller_cost";
@ -2026,7 +2061,7 @@ sub rate_cdr
DEBUG "get customer termination cost for destination_user_id $$cdr{destination_user_id}";
get_customer_call_cost($cdr, $type, "in",
\$destination_customer_cost, \$destination_customer_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting destination customer cost for local destination_user_id ".
$cdr->{destination_user_id}." for cdr ".$cdr->{id}."\n";
DEBUG "destination customer termination cost is $destination_customer_cost";
@ -2040,7 +2075,7 @@ sub rate_cdr
DEBUG "fetching source_carrier_cost based on destination_provider_info ".(Dumper \%destination_provider_info);
get_provider_call_cost($cdr, $type, "out",
\%destination_provider_info, \$source_carrier_cost, \$source_carrier_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting source carrier cost for cdr ".$cdr->{id}."\n";
} else {
WARNING "missing destination profile, so we can't calculate source_carrier_cost for destination_provider_info ".(Dumper \%destination_provider_info);
@ -2051,7 +2086,7 @@ sub rate_cdr
if($source_provider_info{profile_id}) {
get_provider_call_cost($cdr, $type, "out",
\%source_provider_info, \$source_reseller_cost, \$source_reseller_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting source reseller cost for cdr ".$cdr->{id}."\n";
} else {
# up to 2.8, there is one hardcoded reseller id 1, which doesn't have a billing profile, so skip this step here.
@ -2061,7 +2096,7 @@ sub rate_cdr
# get customer cost
get_customer_call_cost($cdr, $type, "out",
\$source_customer_cost, \$source_customer_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting source customer cost for local source_user_id ".
$cdr->{source_user_id}." for cdr ".$cdr->{id}."\n";
} else {
@ -2080,7 +2115,7 @@ sub rate_cdr
DEBUG "fetching destination_carrier_cost based on source_provider_info ".(Dumper \%source_provider_info);
get_provider_call_cost($cdr, $type, "in",
\%source_provider_info, \$destination_carrier_cost, \$destination_carrier_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting destination carrier cost for local destination_provider_id ".
$cdr->{destination_provider_id}." for cdr ".$cdr->{id}."\n";
} else {
@ -2090,7 +2125,7 @@ sub rate_cdr
DEBUG "fetching destination_reseller_cost based on source_provider_info ".(Dumper \%destination_provider_info);
get_provider_call_cost($cdr, $type, "in",
\%destination_provider_info, \$destination_reseller_cost, \$destination_reseller_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting destination reseller cost for local destination_provider_id ".
$cdr->{destination_provider_id}." for cdr ".$cdr->{id}."\n";
} else {
@ -2100,7 +2135,7 @@ sub rate_cdr
}
get_customer_call_cost($cdr, $type, "in",
\$destination_customer_cost, \$destination_customer_free_time,
\$rating_duration)
\$rating_durations[@rating_durations])
or FATAL "Error getting destination customer cost for local destination_user_id ".
$cdr->{destination_user_id}." for cdr ".$cdr->{id}."\n";
} else {
@ -2108,6 +2143,29 @@ sub rate_cdr
}
}
if ($split_peak_parts) {
# We require the onpeak/offpeak thresholds to be the same for all rating fee profiles used by any
# one particular CDR, so that CDR fragmentations are uniform across customer/carrier/reseller/etc
# entries. Mismatching onpeak/offpeak thresholds are a fatal error (which also results in a
# transaction rollback).
my %rating_durations;
for my $rd (@rating_durations) {
defined($rd) and $rating_durations{$rd} = 1;
}
scalar(keys(%rating_durations)) > 1
and FATAL "Error getting consistent rating fragment for cdr ".$cdr->{id}.". Rating profiles don't match.";
my $rating_duration = (keys(%rating_durations))[0] // $cdr->{duration};
if ($rating_duration < $cdr->{duration}) {
my $sth = $sth_create_cdr_fragment;
$sth->execute($rating_duration, $rating_duration, $cdr->{id})
or FATAL "Error executing create cdr fragment statement: ".$sth->errstr;
$cdr->{is_fragmented} = 1;
$cdr->{duration} = $rating_duration;
}
}
$cdr->{source_carrier_cost} = $source_carrier_cost;
$cdr->{source_reseller_cost} = $source_reseller_cost;
$cdr->{source_customer_cost} = $source_customer_cost;
@ -2289,6 +2347,7 @@ sub main
$sth_offpeak_special->finish;
$sth_unrated_cdrs->finish;
$sth_update_cdr->finish;
$split_peak_parts and $sth_create_cdr_fragment->finish;
$sth_provider_info->finish;
$sth_reseller_info->finish;
$sth_get_cbalances->finish;

Loading…
Cancel
Save