matching CDRs to svc_pbx records by title, RT#8084
authorivan <ivan>
Mon, 28 Jun 2010 08:13:12 +0000 (08:13 +0000)
committerivan <ivan>
Mon, 28 Jun 2010 08:13:12 +0000 (08:13 +0000)
FS/FS/Conf.pm
FS/FS/cdr.pm
FS/FS/part_pkg/voip_cdr.pm
FS/FS/svc_pbx.pm

index 99c4876..11658a8 100644 (file)
@@ -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.',
index 4de7aa3..34243fa 100644 (file)
@@ -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 ) {
index 77b9af5..02ece07 100644 (file)
@@ -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
   '';
 }
index 21aaa4e..7c4385c 100644 (file)
@@ -267,6 +267,75 @@ sub _check_duplicate {
   }
 }
 
+=item get_cdrs
+
+Returns a set of Call Detail Records (see L<FS::cdr>) 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