CDR rewriting and included-calls feature, #16271
authorMark Wells <mark@freeside.biz>
Fri, 24 Feb 2012 16:45:46 +0000 (08:45 -0800)
committerMark Wells <mark@freeside.biz>
Fri, 24 Feb 2012 17:30:12 +0000 (09:30 -0800)
FS/FS/Conf.pm
FS/FS/detail_format.pm
FS/FS/detail_format/sum_count.pm
FS/FS/detail_format/sum_duration.pm
FS/FS/detail_format/sum_duration_prefix.pm
FS/FS/part_pkg/voip_cdr.pm
FS/bin/freeside-cdrrewrited

index 4393dcf..38a6894 100644 (file)
@@ -4258,6 +4258,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cdr-asterisk_australia_rewrite',
+    'section'     => 'telephony',
+    'description' => 'For Asterisk CDRs, assign CDR type numbers based on Australian conventions.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'cust_pkg-show_autosuspend',
     'section'     => 'UI',
     'description' => 'Show package auto-suspend dates.  Use with caution for now; can slow down customer view for large insallations.',
index 144aaa7..af97f36 100644 (file)
@@ -171,8 +171,11 @@ sub single_detail {
   die "$me error combining ".$self->csv->error_input."\n"
     if !$status;
 
+  my $rated_price = $cdr->rated_price;
+  $rated_price = 0 if $cdr->freesidestatus eq 'no-charge';
+
   FS::cust_bill_pkg_detail->new( {
-      'amount'      => $cdr->rated_price,
+      'amount'      => $rated_price,
       'classnum'    => $cdr->rated_classnum,
       'duration'    => $cdr->rated_seconds,
       'regionname'  => $cdr->rated_regionname,
@@ -250,6 +253,7 @@ sub price {
   my $cdr = shift;
   my $object = $self->{inbound} ? $cdr->cdr_termination(1) : $cdr;
   my $price = $object->rated_price if $object;
+  $price = '0.00' if $object->freesidestatus eq 'no-charge';
   length($price) ? $self->money_char . $price : '';
 }
 
index 5cf87c7..c40fcb8 100644 (file)
@@ -31,7 +31,8 @@ sub append {
     my $subtotal = ($svcnums->{$svcnum} ||=
       { count => 0, duration => 0, amount => 0 });
     $subtotal->{count}++;
-    $subtotal->{amount} += $object->rated_price;
+    $subtotal->{amount} += $object->rated_price
+      if $object->freesidestatus ne 'no-charge';
   }
 }
 
index 4af7f0a..1b967b4 100644 (file)
@@ -32,7 +32,8 @@ sub append {
       { count => 0, duration => 0, amount => 0 });
     $subtotal->{count}++;
     $subtotal->{duration} += $object->rated_seconds;
-    $subtotal->{amount} += $object->rated_price;
+    $subtotal->{amount} += $object->rated_price
+      if $object->freesidestatus ne 'no-charge';
   }
 }
 
index 19b6648..d70ad0e 100644 (file)
@@ -47,7 +47,8 @@ sub append {
     #         "' in CDR #".$cdr->acctid."\n";
     $subtotal->{count}++;
     $subtotal->{duration} += $object->rated_seconds;
-    $subtotal->{amount} += $object->rated_price;
+    $subtotal->{amount} += $object->rated_price
+      if $object->freesidestatus ne 'no-charge';
   }
 }
 
index 0072401..3c456dc 100644 (file)
@@ -100,6 +100,8 @@ tie my %unrateable_opts, 'Tie::IxHash',
                      'empty_label'   => '',
                    },
 
+    'calls_included' => { 'name' => 'Number of calls included at no usage charge', },
+
     'min_included' => { 'name' => 'Minutes included when using the "single price per minute" rating method or when using the "prefix" rating method ("region group" billing)',
                     },
 
@@ -267,6 +269,7 @@ tie my %unrateable_opts, 'Tie::IxHash',
                     qw(
                        cdr_svc_method
                        rating_method ratenum intrastate_ratenum 
+                       calls_included
                        min_charge min_included sec_granularity
                        ignore_unrateable
                        default_prefix
@@ -339,6 +342,7 @@ sub calc_usage {
   my $charges = 0;
 
   my $included_min = $self->option('min_included', 1) || 0; #single price rating
+  my $included_calls = $self->option('calls_included', 1) || 0;
 
   my $cdr_svc_method    = $self->option('cdr_svc_method',1)||'svc_phone.phonenum';
   my $rating_method     = $self->option('rating_method') || 'prefix';
@@ -410,11 +414,20 @@ sub calc_usage {
     foreach my $cdr (
       $svc_x->get_cdrs( %options ) 
     ) {
+      my $error;
       # at this point we officially Do Not Care about the rating method
-      $charges += $cdr->rated_price;
-      $formatter->append($cdr);
-      my $error = $cdr->set_status('done');
+      if ( $included_calls > 0 ) {
+        $included_calls--;
+        #$charges += 0, obviously
+        #but don't set the rated price to zero--there should be a record
+        $error = $cdr->set_status('no-charge');
+      }
+      else {
+        $charges += $cdr->rated_price;
+        $error = $cdr->set_status('done');
+      }
       die $error if $error;
+      $formatter->append($cdr);
     }
   }
 
index 846b0b9..f2c3926 100644 (file)
@@ -34,6 +34,9 @@ my %accountcode_unmatch = ();
 my $accountcode_retry = 4 * 60 * 60; # 4 hours
 my $accountcode_giveup = 4 * 24 * 60 * 60; # 4 days
 
+my %cdr_type = map { lc($_->cdrtypename) => $_->cdrtypenum } 
+  qsearch('cdr_type',{});
+
 while (1) {
 
   #hmm... don't want to do an expensive search with an ever-growing bunch
@@ -50,6 +53,8 @@ while (1) {
 
   my $found = 0;
   my %skip = ();
+  my %warning = ();
+
   foreach my $cdr ( 
     qsearch( {
       'table'     => 'cdr',
@@ -85,6 +90,45 @@ while (1) {
 
     }
 
+    # XXX weird special case stuff--can we modularize this somehow?
+    # reference RT#16271
+    if ( $conf->exists('cdr-asterisk_australia_rewrite') and
+         $cdr->disposition eq 'ANSWERED' ) {
+      my $dst = $cdr->dst;
+      my $type;
+      if ( $dst =~ /^0?(12|13|1800|1900|0055)/ ) {
+        # toll free or smart numbers, any length
+        $type = 'tollfree';
+        $cdr->charged_party($dst);
+      }
+      elsif ( $dst =~ /^(11|0011)/ ) {
+        # will be followed by country code
+        $type = 'international';
+        $dst =~ s/^$1/0011/; #standardize
+        $cdr->dst($dst);
+      }
+      elsif ( length($dst) == 10 and$dst =~ /^04/ ) {
+        $type = 'mobile';
+      }
+      elsif ( length($dst) == 10 and $dst =~ /^02|03|07|08/ ) {
+        $type = 'domestic';
+      }
+      elsif ( length($dst) == 8 ) {
+        # local call, no area code
+        $type = 'domestic';
+      }
+      else {
+        $type = 'other';
+      }
+      if ( $type and exists($cdr_type{$type}) ) {
+        $cdr->cdrtypenum($cdr_type{$type});
+        push @status, 'asterisk_australia';
+      }
+      else {
+        $warning{"no CDR type defined for $type calls"}++;
+      }
+    }
+
     if ( $conf->exists('cdr-charged_party_rewrite') && ! $cdr->charged_party ) {
 
       $cdr->set_charged_party;
@@ -153,6 +197,11 @@ while (1) {
 
   }
 
+  foreach (sort keys %warning) {
+    warn "WARNING: $_ (x $warning{$_})\n";
+  }
+  %warning = ();
+
   myexit() if sigterm() || sigint();
   #sleep 1 unless $found;
   sleep 5 unless $found;
@@ -163,6 +212,7 @@ while (1) {
 
 sub _shouldrun {
      $conf->exists('cdr-asterisk_forward_rewrite')
+  || $conf->exists('cdr-asterisk_australia_rewrite')
   || $conf->exists('cdr-charged_party_rewrite')
   || $conf->exists('cdr-taqua-accountcode_rewrite');
 }