avoid infinite looping on bad CDRs in voip_inbound, from #16723
[freeside.git] / FS / FS / part_pkg / voip_inbound.pm
index 425d86b..9054f7b 100644 (file)
@@ -1,16 +1,16 @@
 package FS::part_pkg::voip_inbound;
+use base qw( FS::part_pkg::recur_Common );
 
 use strict;
-use vars qw(@ISA $DEBUG %info);
+use vars qw($DEBUG %info);
 use Date::Format;
 use Tie::IxHash;
+use Text::CSV_XS;
 use FS::Conf;
 use FS::Record qw(qsearchs qsearch);
-use FS::part_pkg::recur_Common;
 use FS::cdr;
-use FS::part_pkg::recur_Common;
-
-@ISA = qw(FS::part_pkg::recur_Common);
+use FS::rate_detail;
+use FS::detail_format;
 
 $DEBUG = 0;
 
@@ -56,10 +56,6 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
                           'default' => '+1',
                         },
 
-    'disable_tollfree' => { 'name' => 'Disable automatic toll-free processing',
-                            'type' => 'checkbox',
-                          },
-
     'use_amaflags' => { 'name' => 'Only charge for CDRs where the amaflags field is set to "2" ("BILL"/"BILLING").',
                         'type' => 'checkbox',
                       },
@@ -148,7 +144,6 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
                        FS::part_pkg::prorate_Mixin::fieldorder,
                    qw( min_charge min_included sec_granularity
                        default_prefix
-                       disable_tollfree
                        use_amaflags
                        use_carrierid
                        use_cdrtypenum ignore_cdrtypenum
@@ -160,7 +155,7 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
                        bill_every_call
                      )
                   ],
-  'weight' => 40,
+  'weight' => 42,
 );
 
 sub price_info {
@@ -183,12 +178,13 @@ sub calc_recur {
 
 }
 
-sub calc_cancel {
-  my $self = shift;
-  my($cust_pkg, $sdate, $details, $param ) = @_;
-
-  $self->calc_usage(@_);
-}
+# use the default
+#sub calc_cancel {
+#  my $self = shift;
+#  my($cust_pkg, $sdate, $details, $param ) = @_;
+#
+#  $self->calc_usage(@_);
+#}
 
 #false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again
 
@@ -205,25 +201,25 @@ sub calc_usage {
 
   my $spool_cdr = $cust_pkg->cust_main->spool_cdr;
 
-  my $included_min = ($self->option('min_included') 
-                        && $self->option('min_included') > 0) 
-                                        ? $self->option('min_included') : 0;
 
   my $charges = 0;
 
 #  my $downstream_cdr = '';
 
-  my $disable_tollfree  = $self->option('disable_tollfree');
-  my $ignore_unrateable = $self->option('ignore_unrateable', 'Hush!');
-  my $use_duration      = $self->option('use_duration');
+  my $included_min  = $self->option('min_included', 1) || 0;
+  my $use_duration  = $self->option('use_duration');
+  my $output_format = $self->option('output_format', 1) || 'default';
+
+  my $formatter = 
+    FS::detail_format->new($output_format, buffer => $details, inbound => 1);
 
-  my $output_format     = $self->option('output_format', 'Hush!') || 'default';
+  my $granularity   = length($self->option('sec_granularity'))
+                        ? $self->option('sec_granularity')
+                        : 60;
 
   #for check_chargable, so we don't keep looking up options inside the loop
   my %opt_cache = ();
 
-  eval "use Text::CSV_XS;";
-  die $@ if $@;
   my $csv = new Text::CSV_XS;
 
   foreach my $cust_svc (
@@ -231,20 +227,29 @@ sub calc_usage {
   ) {
     my $svc_phone = $cust_svc->svc_x;
 
-    foreach my $cdr ( $svc_phone->get_cdrs(
-      'for_update'     => 1,
-      'status'         => '', # unprocessed only
-      'default_prefix' => $self->option('default_prefix'),
+    my $cdr_search = $svc_phone->psearch_cdrs(
       'inbound'        => 1,
-      )
-    ) {
+      'default_prefix' => $self->option('default_prefix'),
+      'status'         => '', # unprocessed only
+      'for_update'     => 1,
+    );
+    $cdr_search->limit(1000);
+    $cdr_search->increment(0);
+    while ( my $cdr = $cdr_search->fetch ) {
+
+      my $reason = $self->check_chargable( $cdr,
+                                           'option_cache' => \%opt_cache,
+                                         );
+      if ( $reason ) {
+        warn "not charging for CDR ($reason)\n" if $DEBUG;
+        $cdr_search->adjust(1);
+        next;
+      }
+
       if ( $DEBUG > 1 ) {
         warn "rating inbound CDR $cdr\n".
              join('', map { "  $_ => ". $cdr->{$_}. "\n" } keys %$cdr );
       }
-      my $granularity = length($self->option('sec_granularity'))
-                          ? $self->option('sec_granularity')
-                          : 60;
 
       my $seconds = $use_duration ? $cdr->duration : $cdr->billsec;
 
@@ -282,31 +287,43 @@ sub calc_usage {
                                 'granularity' => $granularity,
                               )
         );
-        push @$details,
-          { format      => 'C',
-            detail      => $call_details[0],
-            amount      => $charge,
-            classnum    => $cdr->calltypenum, #classnum
-            phonenum    => $self->phonenum,
-            accountcode => $cdr->accountcode,
-            startdate   => $cdr->startdate,
-            duration    => $seconds,
-            # regionname?? => '', #regionname, not set for inbound calls
-          };
-     }
-
-     my $error = $cdr->set_status_and_rated_price( 'done',
-                                                  $charge,
-                                                  $cust_svc->svcnum,
-                                                  'inbound' => 1 );
-     die $error if $error;
+#        push @$details,
+#          { format      => 'C',
+#            detail      => $call_details[0],
+#            amount      => $charge,
+#            classnum    => $cdr->calltypenum, #classnum
+#            #phonenum    => $self->phonenum,
+#            accountcode => $cdr->accountcode,
+#            startdate   => $cdr->startdate,
+#            duration    => $seconds,
+#            # regionname?? => '', #regionname, not set for inbound calls
+#          };
+      }
+
+      # eventually use FS::cdr::rate for this
+      my $error = $cdr->set_status_and_rated_price(
+        'done',
+        $charge,
+        $cust_svc->svcnum,
+        'rated_seconds'     => $use_duration ? $cdr->duration : $cdr->billsec,
+        'rated_granularity' => $granularity, 
+        'rated_classnum'    => $cdr->calltypenum,
+        'inbound'        => 1,
+      );
+      die $error if $error;
+      $formatter->append($cdr);
+
+      $cdr_search->adjust(1) if $cdr->freesidestatus eq '';
 
     } #$cdr
   } # $cust_svc
-  unshift @$details, { format => 'C',
-                       detail => FS::cdr::invoice_header($output_format),
-                     }
-    if @$details;
+#  unshift @$details, { format => 'C',
+#                       detail => FS::cdr::invoice_header($output_format),
+#                     }
+#    if @$details;
+  
+  $formatter->finish;
+  unshift @$details, $formatter->header if @$details;
 
   $charges;
 }
@@ -348,8 +365,7 @@ sub check_chargable {
 
   return "carrierid != $opt{'use_carrierid'}"
     if length($opt{'use_carrierid'})
-    && $cdr->carrierid ne $opt{'use_carrierid'} #ne otherwise 0 matches ''
-    && ! $flags{'da_rewrote'};
+    && $cdr->carrierid ne $opt{'use_carrierid'}; #ne otherwise 0 matches ''
 
   return "cdrtypenum != $opt{'use_cdrtypenum'}"
     if length($opt{'use_cdrtypenum'})