From 8d40a4a4e914bd2003dd6443c2056d57d0aaaeff Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 28 Jun 2010 08:13:12 +0000 Subject: [PATCH] matching CDRs to svc_pbx records by title, RT#8084 --- FS/FS/Conf.pm | 16 +++++++++++ FS/FS/cdr.pm | 15 ++++++++-- FS/FS/part_pkg/voip_cdr.pm | 49 ++++++++++++++++++++++++++++++-- FS/FS/svc_pbx.pm | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 6 deletions(-) diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 99c4876b2..11658a8c1 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -3494,6 +3494,22 @@ and customer address. Include units.', }, { + 'key' => 'cdr-charged_party-field', + 'section' => '', + 'description' => 'Set the charged_party field of CDRs to this field.', + 'type' => 'select-sub', + 'options_sub' => sub { my $fields = FS::cdr->table_info->{'fields'}; + map { $_ => $fields->{$_}||$_ } + grep { $_ !~ /^(acctid|charged_party)$/ } + FS::Schema::dbdef->table('cdr')->columns; + }, + 'option_sub' => sub { my $f = shift; + FS::cdr->table_info->{'fields'}{$f} || $f; + }, + }, + + #probably deprecate in favor of cdr-charged_party-field above + { 'key' => 'cdr-charged_party-accountcode', 'section' => '', 'description' => 'Set the charged_party field of CDRs to the accountcode.', diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index 4de7aa35b..34243fae9 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -324,15 +324,19 @@ sub check { $self->SUPER::check; } -=item is_tollfree +=item is_tollfree [ COLUMN ] - Returns true when the cdr represents a toll free number and false otherwise. +Returns true when the cdr represents a toll free number and false otherwise. + +By default, inspects the dst field, but an optional column name can be passed +to inspect other field. =cut sub is_tollfree { my $self = shift; - ( $self->dst =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0; + my $field = scalar(@_) ? shift : 'dst'; + ( $self->$field() =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0; } =item set_charged_party @@ -361,6 +365,11 @@ sub set_charged_party { if $conf->exists('cdr-charged_party-accountcode-trim_leading_0s'); $self->charged_party( $charged_party ); + } elsif ( $conf->exists('cdr-charged_party-field') ) { + + my $field = $conf->config('cdr-charged_party-field'); + $self->charged_party( $self->$field() ); + } else { if ( $self->is_tollfree ) { diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm index 77b9af553..02ece07c2 100644 --- a/FS/FS/part_pkg/voip_cdr.pm +++ b/FS/FS/part_pkg/voip_cdr.pm @@ -17,6 +17,11 @@ use FS::part_pkg::recur_Common; $DEBUG = 0; +tie my %cdr_svc_method, 'Tie::IxHash', + 'svc_phone.phonenum' => 'Phone numbers (svc_phone.phonenum)', + 'svc_pbx.title' => 'PBX name (svc_pbx.title)', +; + tie my %rating_method, 'Tie::IxHash', 'prefix' => 'Rate calls by using destination prefix to look up a region and rate according to the internal prefix and rate tables', # 'upstream' => 'Rate calls based on upstream data: If the call type is "1", map the upstream rate ID directly to an internal rate (rate_detail), otherwise, pass the upstream price through directly.', @@ -71,6 +76,11 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); 'select_options' => \%FS::part_pkg::recur_Common::recur_method, }, + 'cdr_svc_method' => { 'name' => 'CDR service matching method', + 'type' => 'radio', + 'options' => \%cdr_svc_method, + }, + 'rating_method' => { 'name' => 'Rating method', 'type' => 'radio', 'options' => \%rating_method, @@ -143,6 +153,13 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); 'skip_dstchannel_prefix' => { 'name' => 'Do not charge for CDRs where the dstchannel starts with:', }, + 'skip_src_length_more' => { 'name' => 'Do not charge for CDRs where the source is more than this many digits:', + }, + + 'noskip_src_length_accountcode_tollfree' => { 'name' => 'Do charge for CDRs where source is equal or greater than the specified digits and accountcode is toll free', + 'type' => 'checkbox', + }, + 'skip_dst_length_less' => { 'name' => 'Do not charge for CDRs where the destination is less than this many digits:', }, @@ -210,6 +227,7 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); 'fieldorder' => [qw( setup_fee recur_fee recur_temporality unused_credit recur_method cutoff_day + cdr_svc_method rating_method ratenum min_charge sec_granularity ignore_unrateable default_prefix @@ -219,6 +237,7 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); use_amaflags use_disposition use_disposition_taqua use_carrierid use_cdrtypenum skip_dcontext skip_dstchannel_prefix + skip_src_length_more noskip_src_length_accountcode_tollfree skip_dst_length_less skip_lastapp use_duration 411_rewrite @@ -278,6 +297,7 @@ sub calc_usage { # my $downstream_cdr = ''; + my $cdr_svc_method = $self->option('cdr_svc_method')||'svc_phone.phonenum'; my $rating_method = $self->option('rating_method') || 'prefix'; my $intl = $self->option('international_prefix') || '011'; my $domestic_prefix = $self->option('domestic_prefix'); @@ -305,13 +325,15 @@ sub calc_usage { die $@ if $@; my $csv = new Text::CSV_XS; + my($svc_table, $svc_field) = split('.', $cdr_svc_method); + foreach my $cust_svc ( - grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc + grep { $_->part_svc->svcdb eq $svc_table } $cust_pkg->cust_svc ) { - my $svc_phone = $cust_svc->svc_x; + my $svc_x = $cust_svc->svc_x; foreach my $cdr ( - $svc_phone->get_cdrs( + $svc_x->get_cdrs( 'disable_src' => $self->option('disable_src'), 'default_prefix' => $self->option('default_prefix'), 'status' => '', @@ -690,6 +712,7 @@ sub check_chargable { use_cdrtypenum skip_dcontext skip_dstchannel_prefix + skip_src_length_more noskip_src_length_accountcode_tollfree skip_dst_length_less skip_lastapp ); @@ -732,6 +755,26 @@ sub check_chargable { return "lastapp is $opt{'skip_lastapp'}" if length($opt{'skip_lastapp'}) && $cdr->lastapp eq $opt{'skip_lastapp'}; + my $src_length = $opt{'skip_src_length_more'}; + if ( $src_length ) { + + if ( $opt{'noskip_src_length_accountcode_tollfree'} ) { + + if ( $cdr->is_tollfree('accountcode') ) { + return "source less than or equal to $src_length digits" + if length($cdr->src) <= $src_length; + } else { + return "source more than $src_length digits" + if length($cdr->src) > $src_length; + } + + } else { + return "source more than $src_length digits" + if length($cdr->src) > $src_length; + } + + } + #all right then, rate it ''; } diff --git a/FS/FS/svc_pbx.pm b/FS/FS/svc_pbx.pm index 21aaa4e0b..7c4385cde 100644 --- a/FS/FS/svc_pbx.pm +++ b/FS/FS/svc_pbx.pm @@ -267,6 +267,75 @@ sub _check_duplicate { } } +=item get_cdrs + +Returns a set of Call Detail Records (see L) associated with this +service. By default, "associated with" means that the "charged_party" field of +the CDR matches the "title" field of the service. + +=over 2 + +Accepts the following options: + +=item for_update => 1: SELECT the CDRs "FOR UPDATE". + +=item status => "" (or "done"): Return only CDRs with that processing status. + +=item inbound => 1: No-op for svc_pbx CDR processing. + +=item default_prefix => "XXX": Also accept the phone number of the service prepended +with the chosen prefix. + +=item disable_src => 1: No-op for svc_pbx CDR processing. + +=back + +=cut + +sub get_cdrs { + my($self, %options) = @_; + my %hash = (); + my @where = (); + + my @fields = ( 'charged_party' ); + $hash{'freesidestatus'} = $options{'status'} + if exists($options{'status'}); + + my $for_update = $options{'for_update'} ? 'FOR UPDATE' : ''; + + my $title = $self->title; + + my $prefix = $options{'default_prefix'}; + + my @orwhere = map " $_ = '$title' ", @fields; + push @orwhere, map " $_ = '$prefix$title' ", @fields + if length($prefix); + if ( $prefix =~ /^\+(\d+)$/ ) { + push @orwhere, map " $_ = '$1$title' ", @fields + } + + push @where, ' ( '. join(' OR ', @orwhere ). ' ) '; + + if ( $options{'begin'} ) { + push @where, 'startdate >= '. $options{'begin'}; + } + if ( $options{'end'} ) { + push @where, 'startdate < '. $options{'end'}; + } + + my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where ); + + my @cdrs = + qsearch( { + 'table' => 'cdr', + 'hashref' => \%hash, + 'extra_sql' => $extra_sql, + 'order_by' => "ORDER BY startdate $for_update", + } ); + + @cdrs; +} + =back =head1 BUGS -- 2.11.0