X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcdr.pm;h=3a6b01ba58e1a523dae16460705ba40be543c530;hb=54422869b5d895058a78454ddfc7c01789cb56f7;hp=31c7c23982996ca37fe7a316ca1f9d7138190ff7;hpb=c9c9f28394024d03f78c61b37811e4816aa73c9a;p=freeside.git diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index 31c7c2398..3a6b01ba5 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -10,6 +10,7 @@ use Tie::IxHash; use Date::Parse; use Date::Format; use Time::Local; +use List::Util qw( first min ); use FS::UID qw( dbh ); use FS::Conf; use FS::Record qw( qsearch qsearchs ); @@ -86,6 +87,10 @@ following fields are currently supported: =item lastdata - Last application data +=item src_ip_addr - Source IP address (dotted quad, zero-filled) + +=item dst_ip_addr - Destination IP address (same) + =item startdate - Start of call (UNIX-style integer timestamp) =item answerdate - Answer time of call (UNIX-style integer timestamp) @@ -147,7 +152,7 @@ following fields are currently supported: =item svcnum - Link to customer service (see L) -=item freesidestatus - NULL, processing-tiered, rated, done +=item freesidestatus - NULL, processing-tiered, rated, done, skipped, no-charge, failed =item freesiderewritestatus - NULL, done, skipped @@ -186,6 +191,8 @@ sub table_info { 'dstchannel' => 'Destination channel', #'lastapp' => '', #'lastdata' => '', + 'src_ip_addr' => 'Source IP', + 'dst_ip_addr' => 'Dest. IP', 'startdate' => 'Start date', 'answerdate' => 'Answer date', 'enddate' => 'End date', @@ -426,7 +433,9 @@ sub set_status { Sets the status and rated price. -Available options are: inbound, rated_seconds, rated_minutes, rated_classnum, rated_ratename +Available options are: inbound, rated_pretty_dst, rated_regionname, +rated_seconds, rated_minutes, rated_granularity, rated_ratedetailnum, +rated_classnum, rated_ratename. If there is an error, returns the error, otherwise returns false. @@ -536,7 +545,7 @@ sub rate_prefix { ); if ( $reason ) { warn "not charging for CDR ($reason)\n" if $DEBUG; - return $self->set_status_and_rated_price( 'rated', + return $self->set_status_and_rated_price( 'skipped', 0, $opt{'svcnum'}, ); @@ -689,8 +698,11 @@ sub rate_prefix { if ( !exists($interval_cache{$regionnum}) ) { my @intervals = ( sort { $a->stime <=> $b->stime } - map { my $r = $_->rate_time; $r ? $r->intervals : () } - $rate->rate_detail + map { $_->rate_time->intervals } + qsearch({ 'table' => 'rate_detail', + 'hashref' => { 'ratenum' => $rate->ratenum }, + 'extra_sql' => 'AND ratetimenum IS NOT NULL', + }) ); $interval_cache{$regionnum} = \@intervals; warn " cached ".scalar(@intervals)." interval(s)\n" @@ -833,10 +845,12 @@ sub rate_prefix { sub rate_upstream_simple { my( $self, %opt ) = @_; - $self->set_status_and_rated_price( 'rated', - sprintf('%.3f', $self->upstream_price), - $opt{'svcnum'}, - ); + $self->set_status_and_rated_price( + 'rated', + sprintf('%.3f', $self->upstream_price), + $opt{'svcnum'}, + 'rated_classnum' => $self->calltypenum, + ); } sub rate_single_price { @@ -874,10 +888,13 @@ sub rate_single_price { sprintf('%.4f', ( $part_pkg->option_cacheable('min_charge') * $charge_min ) + 0.0000000001 ); #so 1.00005 rounds to 1.0001 - $self->set_status_and_rated_price( 'rated', - $charge, - $opt{'svcnum'}, - ); + $self->set_status_and_rated_price( + 'rated', + $charge, + $opt{'svcnum'}, + 'rated_granularity' => $granularity, + 'rated_seconds' => $seconds, + ); } @@ -1000,6 +1017,10 @@ my %export_names = ( 'invoice_header' => "Date,Time,Called From,Destination,Duration,Price", #"Date,Time,Name,Called From,Destination,Duration,Price", }, + 'accountcode_simple' => { + 'name' => 'Simple with accountcode', + 'invoice_header' => "Date,Time,Called From,Account,Duration,Price", + }, 'basic' => { 'name' => 'Basic', 'invoice_header' => "Date/Time,Called Number,Min/Sec,Price", @@ -1021,13 +1042,17 @@ my %export_names = ( 'invoice_header' => 'Caller,Date,Time,Number,Destination,Duration,Price', }, 'sum_duration' => { - 'name' => 'Summary (one line per service, with duration)', + 'name' => 'Summary, one line per service', 'invoice_header' => 'Caller,Rate,Calls,Minutes,Price', }, 'sum_count' => { - 'name' => 'Summary (one line per service, with count)', + 'name' => 'Number of calls, one line per service', 'invoice_header' => 'Caller,Rate,Messages,Price', }, + 'sum_duration_prefix' => { + 'name' => 'Summary, one line per destination prefix', + 'invoice_header' => 'Caller,Rate,Calls,Minutes,Price', + }, ); my %export_formats = (); @@ -1093,6 +1118,14 @@ sub export_formats { #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE $price_sub, ], + 'accountcode_simple' => [ + sub { time2str($date_format, shift->calldate_unix ) }, #DATE + sub { time2str('%r', shift->calldate_unix ) }, #TIME + 'src', #called from + 'accountcode', #NUMBER_DIALED + $duration_sub, #DURATION + $price_sub, + ], 'sum_duration' => [ # for summary formats, the CDR is a fictitious object containing the # total billsec and the phone number of the service @@ -1216,6 +1249,8 @@ as keys (for use with part_pkg::voip_cdr) and "pretty" format names as values. =cut +# in the future, load this dynamically from detail_format classes + sub invoice_formats { map { ($_ => $export_names{$_}->{'name'}) } grep { $export_names{$_}->{'invoice_header'} } @@ -1242,6 +1277,7 @@ CDR reprocessing. sub clear_status { my $self = shift; + my %opt = @_; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -1257,6 +1293,7 @@ sub clear_status { if ( $cdr_prerate && $cdr_prerate_cdrtypenums{$self->cdrtypenum} && $self->rated_ratedetailnum #avoid putting old CDRs back in "rated" && $self->freesidestatus eq 'done' + && ! $opt{'rerate'} ) { #special case $self->freesidestatus('rated'); @@ -1551,6 +1588,31 @@ sub _upgrade_data { } +=item ip_addr_sql FIELD RANGE + +Returns an SQL condition to search for CDRs with an IP address +within RANGE. FIELD is either 'src_ip_addr' or 'dst_ip_addr'. RANGE +should be in the form "a.b.c.d-e.f.g.h' (dotted quads), where any of +the leftmost octets of the second address can be omitted if they're +the same as the first address. + +=cut + +sub ip_addr_sql { + my $class = shift; + my ($field, $range) = @_; + $range =~ /^[\d\.-]+$/ or die "bad ip address range '$range'"; + my @r = split('-', $range); + my @saddr = split('\.', $r[0] || ''); + my @eaddr = split('\.', $r[1] || ''); + unshift @eaddr, (undef) x (4 - scalar @eaddr); + for(0..3) { + $eaddr[$_] = $saddr[$_] if !defined $eaddr[$_]; + } + "$field >= '".sprintf('%03d.%03d.%03d.%03d', @saddr) . "' AND ". + "$field <= '".sprintf('%03d.%03d.%03d.%03d', @eaddr) . "'"; +} + =back =head1 BUGS