Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Tue, 26 Feb 2013 03:31:06 +0000 (19:31 -0800)
committerIvan Kohler <ivan@freeside.biz>
Tue, 26 Feb 2013 03:31:06 +0000 (19:31 -0800)
18 files changed:
FS/FS/ClientAPI/MyAccount.pm
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/cdr.pm
FS/FS/cdr/taqua62.pm
FS/FS/part_export/huawei_hlr.pm
FS/FS/part_pkg/voip_cdr.pm
FS/FS/pay_batch/BoM.pm
FS/FS/rate.pm
FS/FS/rate_region.pm
FS/FS/svc_phone.pm
FS/bin/freeside-cdrrewrited
fs_selfservice/FS-SelfService/cgi/selfservice.cgi
fs_selfservice/FS-SelfService/cgi/view_cdr_details.html
fs_selfservice/FS-SelfService/cgi/view_usage.html
httemplate/browse/rate_region.html
httemplate/edit/rate_region.cgi
httemplate/search/cdr.html

index faf3778..38139e1 100644 (file)
@@ -1723,7 +1723,34 @@ sub list_svcs {
               } else {
                 $hash{'name'} = $cust_main->name;
               }
+            } elsif ( $svcdb eq 'svc_phone' ) {
+              # could potentially show lots of things...
+              $hash{'outbound'} = 1;
+              $hash{'inbound'}  = 0;
+              if ( $part_pkg->plan eq 'voip_inbound' ) {
+                $hash{'outbound'} = 0;
+                $hash{'inbound'}  = 1;
+              } elsif ( $part_pkg->option('selfservice_inbound_format')
+                    or  $conf->config('selfservice-default_inbound_cdr_format')
+              ) {
+                $hash{'inbound'}  = 1;
+              }
+              foreach (qw(inbound outbound)) {
+                # hmm...we can't filter by status here, because there might
+                # not be cdr_terminations at all.  have to go by date.
+                # find all since the last bill date.
+                # XXX cdr types?  we are going to need them.
+                if ( $hash{$_} ) {
+                  my $sum_cdr = $svc_x->sum_cdrs(
+                    'inbound' => ( $_ eq 'inbound' ? 1 : 0 ),
+                    'begin'   => ($cust_pkg->last_bill || 0),
+                    'nonzero' => 1,
+                  );
+                  $hash{$_} = $sum_cdr->hashref;
+                }
+              }
             }
+
             # elsif ( $svcdb eq 'svc_phone' || $svcdb eq 'svc_port' ) {
             #  %hash = (
             #    %hash,
@@ -1996,7 +2023,7 @@ sub _list_cdr_usage {
   # we have to return the results all at once...
   my($svc_phone, $begin, $end, %opt) = @_;
   map [ $_->downstream_csv(%opt, 'keeparray' => 1) ],
-    $svc_phone->get_cdrs( 'begin'=>$begin, 'end'=>$end, );
+    $svc_phone->get_cdrs( 'begin'=>$begin, 'end'=>$end, %opt );
 }
 
 sub list_cdr_usage {
@@ -2026,18 +2053,21 @@ sub _usage_details {
   my %callback_opt;
   my $header = [];
   if ( $svcdb eq 'svc_phone' ) {
-    my $format   = $cust_pkg->part_pkg->option('output_format') || '';
-    $format = '' if $format =~ /^sum_/;
-    # sensible default if there is no format or it's a summary format
-    if ( $cust_pkg->part_pkg->plan eq 'voip_inbound' ) {
-      $format ||= 'source_default';
+    my $conf = FS::Conf->new;
+    my $format = '';
+    if ( $p->{inbound} ) {
+      $format = $cust_pkg->part_pkg->option('selfservice_inbound_format') 
+                || $conf->config('selfservice-default_inbound_cdr_format')
+                || 'source_default';
       $callback_opt{inbound} = 1;
+    } else {
+      $format = $cust_pkg->part_pkg->option('selfservice_format')
+                || $conf->config('selfservice-default_cdr_format')
+                || 'default';
     }
-    else {
-      $format ||= 'default';
-    }
-    
+
     $callback_opt{format} = $format;
+    $callback_opt{use_clid} = 1;
     $header = [ split(',', FS::cdr::invoice_header($format) ) ];
   }
 
index 809333c..2931f35 100644 (file)
@@ -717,6 +717,18 @@ my %batch_gateway_options = (
   },
 );
 
+my @cdr_formats = (
+  '' => '',
+  'default' => 'Default',
+  'source_default' => 'Default with source',
+  'accountcode_default' => 'Default plus accountcode',
+  'description_default' => 'Default with description field as destination',
+  'basic' => 'Basic',
+  'simple' => 'Simple',
+  'simple2' => 'Simple with source',
+  'accountcode_simple' => 'Simple with accountcode',
+);
+
 # takes the reason class (C, R, S) as an argument
 sub reason_type_options {
   my $reason_class = shift;
@@ -4760,6 +4772,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cdr-taqua-callerid_rewrite',
+    'section'     => 'telephony',
+    'description' => 'For the Taqua CDR format, pull Caller ID blocking information from secondary CDRs.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'cdr-asterisk_australia_rewrite',
     'section'     => 'telephony',
     'description' => 'For Asterisk CDRs, assign CDR type numbers based on Australian conventions.',
@@ -5351,6 +5370,22 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice-default_cdr_format',
+    'section'     => 'self-service',
+    'description' => 'Format for showing outbound CDRs in self-service.  The per-package option overrides this.',
+    'type'        => 'select',
+    'select_hash' => \@cdr_formats,
+  },
+
+  {
+    'key'         => 'selfservice-default_inbound_cdr_format',
+    'section'     => 'self-service',
+    'description' => 'Format for showing inbound CDRs in self-service.  The per-package option overrides this.  Leave blank to avoid showing these CDRs.',
+    'type'        => 'select',
+    'select_hash' => \@cdr_formats,
+  },
+
+  {
     'key'         => 'logout-timeout',
     'section'     => 'UI',
     'description' => 'If set, automatically log users out of the backoffice after this many minutes.',
index 6c7453d..774dcd2 100644 (file)
@@ -3056,6 +3056,7 @@ sub tables_hashref {
       'columns' => [
         'regionnum',   'serial',      '', '', '', '', 
         'regionname',  'varchar',     '', $char_d, '', '', 
+        'exact_match', 'char',    'NULL',  1, '', '',
       ],
       'primary_key' => 'regionnum',
       'unique'      => [],
index fedf28a..5e986ab 100644 (file)
@@ -1173,6 +1173,8 @@ sub export_formats {
     length($price) ? ($opt{money_char} . $price) : '';
   };
 
+  my $src_sub = sub { $_[0]->clid || $_[0]->src };
+
   %export_formats = (
     'simple' => [
       sub { time2str($date_format, shift->calldate_unix ) },   #DATE
@@ -1187,7 +1189,7 @@ sub export_formats {
       sub { time2str($date_format, shift->calldate_unix ) },   #DATE
       sub { time2str('%r', shift->calldate_unix ) },   #TIME
       #'userfield',                                     #USER
-      'src',                                           #called from
+      $src_sub,                                           #called from
       'dst',                                           #NUMBER_DIALED
       $duration_sub,                                   #DURATION
       #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
@@ -1196,7 +1198,7 @@ sub export_formats {
     'accountcode_simple' => [
       sub { time2str($date_format, shift->calldate_unix ) },   #DATE
       sub { time2str('%r', shift->calldate_unix ) },   #TIME
-      'src',                                           #called from
+      $src_sub,                                           #called from
       'accountcode',                                   #NUMBER_DIALED
       $duration_sub,                                   #DURATION
       $price_sub,
@@ -1204,14 +1206,14 @@ sub export_formats {
     'sum_duration' => [ 
       # for summary formats, the CDR is a fictitious object containing the 
       # total billsec and the phone number of the service
-      'src',
+      $src_sub,
       sub { my($cdr, %opt) = @_; $opt{ratename} },
       sub { my($cdr, %opt) = @_; $opt{count} },
       sub { my($cdr, %opt) = @_; int($opt{seconds}/60).'m' },
       $price_sub,
     ],
     'sum_count' => [
-      'src',
+      $src_sub,
       sub { my($cdr, %opt) = @_; $opt{ratename} },
       sub { my($cdr, %opt) = @_; $opt{count} },
       $price_sub,
@@ -1245,7 +1247,7 @@ sub export_formats {
       $price_sub,
     ],
   );
-  $export_formats{'source_default'} = [ 'src', @{ $export_formats{'default'} }, ];
+  $export_formats{'source_default'} = [ $src_sub, @{ $export_formats{'default'} }, ];
   $export_formats{'accountcode_default'} =
     [ @{ $export_formats{'default'} }[0,1],
       'accountcode',
@@ -1253,7 +1255,7 @@ sub export_formats {
     ];
   my @default = @{ $export_formats{'default'} };
   $export_formats{'description_default'} = 
-    [ 'src', @default[0..2], 
+    [ $src_sub, @default[0..2], 
       sub { my($cdr, %opt) = @_; $cdr->description },
       @default[4,5] ];
 
index 862018e..aa94630 100644 (file)
@@ -20,7 +20,9 @@ use FS::cdr qw(_cdr_date_parser_maker);
       my($cdr, $field, $conf, $hashref) = @_;
       $hashref->{skiprow} = 1
         unless ($field == 0 && $cdr->disposition == 100       )  #regular CDR
-            || ($field == 1 && $cdr->lastapp     eq 'acctcode'); #accountcode
+            || ($field == 1 && $cdr->lastapp     eq 'acctcode')  #accountcode
+            || ($field == 1 && $cdr->lastapp     eq 'CallerId')  #CID blocking
+            ;
       $cdr->cdrtypenum($field);
     },
 
index fb3b679..d231567 100644 (file)
@@ -18,6 +18,7 @@ tie my %options, 'Tie::IxHash',
   'pwd'       => { label=>'Operator password' },
   'tplid'     => { label=>'Template number' },
   'hlrsn'     => { label=>'HLR serial number' },
+  'timeout'   => { label=>'Timeout (seconds)', default => 120 },
   'debug'     => { label=>'Enable debugging', type=>'checkbox' },
 ;
 
@@ -36,13 +37,13 @@ sub _export_insert {
   my( $self, $svc_phone ) = (shift, shift);
   # svc_phone::check should ensure phonenum and sim_imsi are numeric
   my @command = (
-    'ADD TPLSUB',
     IMSI   => '"'.$svc_phone->sim_imsi.'"',
-    ISDN   => '"'.$svc_phone->phonenum.'"',
+    ISDN   => '"'.$svc_phone->countrycode.$svc_phone->phonenum.'"',
     TPLID  => $self->option('tplid'),
   );
   unshift @command, 'HLRSN', $self->option('hlrsn')
     if $self->option('hlrsn');
+  unshift @command, 'ADD TPLSUB';
   my $err_or_queue = $self->queue_command($svc_phone->svcnum, @command);
   ref($err_or_queue) ? '' : $err_or_queue;
 }
@@ -53,7 +54,7 @@ sub _export_replace  {
   if ( $new->sim_imsi ne $old->sim_imsi ) {
     my @command = (
       'MOD IMSI',
-      ISDN    => '"'.$old->phonenum.'"',
+      ISDN    => '"'.$old->countrycode.$old->phonenum.'"',
       IMSI    => '"'.$old->sim_imsi.'"',
       NEWIMSI => '"'.$new->sim_imsi.'"',
     );
@@ -61,11 +62,12 @@ sub _export_replace  {
     return $err_or_queue unless ref $err_or_queue;
     $depend_jobnum = $err_or_queue->jobnum;
   }
-  if ( $new->phonenum ne $old->phonenum ) {
+  if ( $new->countrycode ne $old->countrycode or 
+       $new->phonenum ne $old->phonenum ) {
     my @command = (
       'MOD ISDN',
-      ISDN    => '"'.$old->phonenum.'"',
-      NEWISDN => '"'.$new->phonenum.'"',
+      ISDN    => '"'.$old->countrycode.$old->phonenum.'"',
+      NEWISDN => '"'.$new->countrycode.$new->phonenum.'"',
     );
     my $err_or_queue = $self->queue_command($new->svcnum, @command);
     return $err_or_queue unless ref $err_or_queue;
@@ -94,7 +96,7 @@ sub _export_lock {
   my @command = (
     'MOD LCK',
     IMSI    => '"'.$svc_phone->sim_imsi.'"',
-    ISDN    => '"'.$svc_phone->phonenum.'"',
+    ISDN    => '"'.$svc_phone->countrycode.$svc_phone->phonenum.'"',
     IC      => $lockstate,
     OC      => $lockstate,
     GPRSLOCK=> $lockstate,
@@ -107,8 +109,8 @@ sub _export_delete {
   my( $self, $svc_phone ) = (shift, shift);
   my @command = (
     'RMV SUB',
-    IMSI    => '"'.$svc_phone->sim_imsi.'"',
-    ISDN    => '"'.$svc_phone->phonenum.'"',
+    #IMSI    => '"'.$svc_phone->sim_imsi.'"',
+    ISDN    => '"'.$svc_phone->countrycode.$svc_phone->phonenum.'"',
   );
   my $err_or_queue = $self->queue_command($svc_phone->svcnum, @command);
   ref($err_or_queue) ? '' : $err_or_queue;
@@ -179,15 +181,16 @@ sub command {
     $string .= shift(@param) . '=' . shift(@param);
     $string .= ',' if @param;
   }
-  $string .= "\n";
+  $string .= "\n;";
   my @result;
   eval { # timeout
     local $SIG{ALRM} = sub { die "timeout\n" };
-    alarm ($self->option('timeout') || 30);
+    alarm ($self->option('timeout') || 120);
     warn "Sending to server:\n$string\n\n" if $DEBUG;
     $socket->print($string);
     warn "Received:\n";
     my $line;
+    local $/ = "\r\n";
     do {
       $line = $socket->getline();
       warn $line if $DEBUG;
@@ -203,11 +206,10 @@ sub command {
     return { error => $@ };
   } else {
     #+++    HLR9820        <date> <time>\n
-    # skip empty lines
     my $header = shift(@result);
-    return { error => 'malformed response: '.$header }
-      unless $header =~ /^\+\+\+/;
-    $return{header} = $header;
+    $header =~ /(\+\+\+.*)/
+      or return { error => 'malformed response: '.$header };
+    $return{header} = $1;
     #SMU    #<serial number>\n
     $return{smu} = shift(@result);
     #%%<command string>%%\n 
index aae51e9..04098a8 100644 (file)
@@ -51,6 +51,11 @@ tie my %unrateable_opts, 'Tie::IxHash',
   2  => 'Flag for later review',
 ;
 
+tie my %detail_formats, 'Tie::IxHash',
+  '' => '',
+  FS::cdr::invoice_formats()
+;
+
 %info = (
   'name' => 'VoIP rating by plan of CDR records in an internal (or external) SQL table',
   'shortname' => 'VoIP/telco CDR rating (standard)',
@@ -211,12 +216,25 @@ tie my %unrateable_opts, 'Tie::IxHash',
                       },
 
     #false laziness w/cdr_termination.pm
-    'output_format' => { 'name' => 'CDR invoice display format',
+    'output_format' => { 'name' => 'CDR display format for invoices',
                          'type' => 'select',
-                         'select_options' => { FS::cdr::invoice_formats() },
+                         'select_options' => \%detail_formats,
                          'default'        => 'default', #XXX test
                        },
 
+    'selfservice_format' => 
+      { 'name' => 'CDR display format for selfservice',
+        'type' => 'select',
+        'select_options' => \%detail_formats,
+        'default' => 'default'
+      },
+    'selfservice_inbound_format' =>
+      { 'name' => 'Inbound CDR display format for selfservice',
+        'type' => 'select',
+        'select_options' => \%detail_formats,
+        'default' => ''
+      },
+
     'usage_section' => { 'name' => 'Section in which to place usage charges (whether separated or not): ',
                        },
 
@@ -297,7 +315,9 @@ tie my %unrateable_opts, 'Tie::IxHash',
                        skip_max_callers
                        use_duration
                        411_rewrite
-                       output_format usage_mandate summarize_usage usage_section
+                       output_format 
+                       selfservice_format selfservice_inbound_format
+                       usage_mandate summarize_usage usage_section
                        bill_every_call bill_inactive_svcs
                        count_available_phones suspend_bill 
                      )
index 719b504..a3708d4 100644 (file)
@@ -31,13 +31,13 @@ $name = 'BoM';
   },
   header => sub { 
     my $pay_batch = shift;
-    sprintf( "A%10s%04u%06u%05u%54s\n",  #80
+    sprintf( "A%10s%04u%06u%05u%53s\n",  #80
       $origid,
       $pay_batch->batchnum,
       jdate($pay_batch->download),
       $datacenter,
       "") .
-    sprintf( "XD%03u%06u%-15s%-30s%09u%-12s   \n", #80
+    sprintf( "XD%03u%06u%-15s%-30s%09u%-12s ", #80
       $typecode,
       jdate($pay_batch->download),
       $shortname,
@@ -48,7 +48,7 @@ $name = 'BoM';
   row => sub {
     my ($cust_pay_batch, $pay_batch) = @_;
     my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
-    sprintf( "D%010.0f%09u%-12s%-29s%-19s\n", #80
+    sprintf( "D%010.0f%09u%-12s%-29s%-18s ", #80
       $cust_pay_batch->amount * 100,
       $aba,
       $account,
@@ -58,8 +58,8 @@ $name = 'BoM';
   },
   footer => sub {
     my ($pay_batch, $batchcount, $batchtotal) = @_;
-    sprintf( "YD%08u%014.0f%56s\n", $batchcount, $batchtotal*100, ""). #80
-    sprintf( "Z%014u%04u%014u%05u%42s\n",  #80 now
+    sprintf( "YD%08u%014.0f%55s\n", $batchcount, $batchtotal*100, ""). #80
+    sprintf( "Z%014u%05u%014u%05u%40s",  #80 now
       $batchtotal*100, $batchcount, "0", "0", "");
   },
 );
index a2511cf..49ac938 100644 (file)
@@ -308,17 +308,28 @@ sub dest_detail {
     #find a rate prefix, first look at most specific, then fewer digits,
     # finally trying the country code only
     my $rate_prefix = '';
-    for my $len ( reverse(1..10) ) {
-      $rate_prefix = qsearchs('rate_prefix', {
+    $rate_prefix = qsearchs({
+        'table'     => 'rate_prefix',
+        'addl_from' => ' JOIN rate_region USING (regionnum)',
+        'hashref'   => {
+          'countrycode' => $countrycode,
+          'npa'         => $phonenum,
+        },
+        'extra_sql' => ' AND exact_match = \'Y\''
+    });
+    if (!$rate_prefix) {
+      for my $len ( reverse(1..10) ) {
+        $rate_prefix = qsearchs('rate_prefix', {
+          'countrycode' => $countrycode,
+          #'npa'         => { op=> 'LIKE', value=> substr($number, 0, $len) }
+          'npa'         => substr($phonenum, 0, $len),
+        } ) and last;
+      }
+      $rate_prefix ||= qsearchs('rate_prefix', {
         'countrycode' => $countrycode,
-        #'npa'         => { op=> 'LIKE', value=> substr($number, 0, $len) }
-        'npa'         => substr($phonenum, 0, $len),
-      } ) and last;
+        'npa'         => '',
+      });
     }
-    $rate_prefix ||= qsearchs('rate_prefix', {
-      'countrycode' => $countrycode,
-      'npa'         => '',
-    });
 
     return '' unless $rate_prefix;
 
index f4a0ab1..d42fdb4 100644 (file)
@@ -36,7 +36,10 @@ inherits from FS::Record.  The following fields are currently supported:
 
 =item regionnum - primary key
 
-=item regionname
+=item regionname - name of the region
+
+=item exact_match - 'Y' if "prefixes" in this region really represent 
+complete phone numbers.  Null if they represent prefixes (the usual case).
 
 =back
 
@@ -233,6 +236,7 @@ sub check {
   my $error =
        $self->ut_numbern('regionnum')
     || $self->ut_text('regionname')
+    || $self->ut_flag('exact_match')
   ;
   return $error if $error;
 
index bf610c6..f28002c 100644 (file)
@@ -688,6 +688,8 @@ with the chosen prefix.
 
 =item disable_src => 1: Only match on "charged_party", not "src".
 
+=item nonzero: Only return CDRs where duration > 0.
+
 =item by_svcnum: not supported for svc_phone
 
 =item billsec_sum: Instead of returning all of the CDRs, return a single
@@ -755,6 +757,9 @@ sub psearch_cdrs {
   if ( $options{'end'} ) {
     push @where, 'startdate < '.  $options{'end'};
   }
+  if ( $options{'nonzero'} ) {
+    push @where, 'duration > 0';
+  }
 
   my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where );
 
@@ -781,6 +786,30 @@ sub get_cdrs {
   qsearch ( $psearch->{query} )
 }
 
+=item sum_cdrs
+
+Takes the same options as psearch_cdrs, but returns a single row containing
+"count" (the number of CDRs) and the sums of the following fields: duration,
+billsec, rated_price, rated_seconds, rated_minutes.
+
+Note that if any calls are not rated, their rated_* fields will be null.
+If you want to use those fields, pass the 'status' option to limit to 
+calls that have been rated.  This is intentional; please don't "fix" it.
+
+=cut
+
+sub sum_cdrs {
+  my $self = shift;
+  my $psearch = $self->psearch_cdrs(@_);
+  $psearch->{query}->{'select'} = join(',',
+    'COUNT(*) AS count',
+    map { "SUM($_) AS $_" }
+      qw(duration billsec rated_price rated_seconds rated_minutes)
+  );
+  # hack
+  $psearch->{query}->{'extra_sql'} =~ s/ ORDER BY.*$//;
+  qsearchs ( $psearch->{query} );
+}
 
 =back
 
index f2c3926..16f931f 100644 (file)
@@ -30,9 +30,9 @@ die "not running; cdr-asterisk_forward_rewrite, cdr-charged_party_rewrite ".
 
 #--
 
-my %accountcode_unmatch = ();
-my $accountcode_retry = 4 * 60 * 60; # 4 hours
-my $accountcode_giveup = 4 * 24 * 60 * 60; # 4 days
+my %sessionnum_unmatch = ();
+my $sessionnum_retry = 4 * 60 * 60; # 4 hours
+my $sessionnum_giveup = 4 * 24 * 60 * 60; # 4 days
 
 my %cdr_type = map { lc($_->cdrtypename) => $_->cdrtypenum } 
   qsearch('cdr_type',{});
@@ -45,8 +45,8 @@ while (1) {
   # instead of just doing this search like normal CDRs
 
   #hmm :/
-  my @recent = grep { ($accountcode_unmatch{$_} + $accountcode_retry) > time }
-                 keys %accountcode_unmatch;
+  my @recent = grep { ($sessionnum_unmatch{$_} + $sessionnum_retry) > time }
+                 keys %sessionnum_unmatch;
   my $extra_sql = scalar(@recent)
                     ? ' AND acctid NOT IN ('. join(',', @recent). ') '
                     : '';
@@ -136,45 +136,62 @@ while (1) {
 
     }
 
-    if ( $conf->exists('cdr-taqua-accountcode_rewrite')
-         && $cdr->lastapp eq 'acctcode' && $cdr->cdrtypenum == 1
+    if (     $cdr->cdrtypenum == 1
+         and $cdr->lastapp
+         and (
+            $conf->exists('cdr-taqua-accountcode_rewrite') or
+            $conf->exists('cdr-taqua-callerid_rewrite') )
        )
     {
 
       #find the matching CDR
-      my $primary = qsearchs('cdr', {
-        'sessionnum'  => $cdr->sessionnum,
-        'src'         => $cdr->subscriber,
-        #'accountcode' => '',
-      });
+      my %search = ( 'sessionnum' => $cdr->sessionnum );
+      if ( $cdr->lastapp eq 'acctcode' ) {
+        $search{'src'} = $cdr->subscriber;
+      } elsif ( $cdr->lastapp eq 'CallerId' ) {
+        $search{'dst'} = $cdr->subscriber;
+      }
+      my $primary = qsearchs('cdr', \%search);
 
       unless ( $primary ) {
 
         my $cantfind = "can't find primary CDR with session ". $cdr->sessionnum.
                        ", src ". $cdr->subscriber;
-        if ( $cdr->calldate_unix + $accountcode_giveup < time ) {
+        if ( $cdr->calldate_unix + $sessionnum_giveup < time ) {
           warn "ERROR: $cantfind; giving up\n";
-          push @status, 'taqua-accountcode-NOTFOUND';
+          push @status, 'taqua-sessionnum-NOTFOUND';
           $cdr->status('done'); #so it doesn't try to rate
-          delete $accountcode_unmatch{$cdr->acctid}; #so it doesn't suck mem
+          delete $sessionnum_unmatch{$cdr->acctid}; #so it doesn't suck mem
         } else {
           warn "WARNING: $cantfind; will keep trying\n";
-          $accountcode_unmatch{$cdr->acctid} = time;
+          $sessionnum_unmatch{$cdr->acctid} = time;
           next;
         }
 
       } else {
 
-        $primary->accountcode( $cdr->lastdata );
+        if ( $cdr->lastapp eq 'acctcode' ) {
+          # lastdata contains the dialed account code
+          $primary->accountcode( $cdr->lastdata );
+          push @status, 'taqua-accountcode';
+        } elsif ( $cdr->lastapp eq 'CallerId' ) {
+          # lastdata contains "allowed" or "restricted"
+          # or case variants thereof
+          if ( lc($cdr->lastdata) eq 'restricted' ) {
+            $primary->clid( 'PRIVATE' );
+          }
+          push @status, 'taqua-callerid';
+        } else {
+          warn "unknown Taqua service name: ".$cdr->lastapp."\n";
+        }
         #$primary->freesiderewritestatus( 'taqua-accountcode-primary' );
-        my $error = $primary->replace;
+        my $error = $primary->replace if $primary->modified;
         if ( $error ) {
           warn "WARNING: error rewriting primary CDR (will retry): $error\n";
           next;
         }
         $skip{$primary->acctid} = 1;
 
-        push @status, 'taqua-accountcode';
         $cdr->status('done'); #so it doesn't try to rate
 
       }
@@ -214,7 +231,10 @@ 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');
+  || $conf->exists('cdr-taqua-accountcode_rewrite')
+  || $conf->exists('cdr-taqua-callerid_rewrite')
+  || 0
+  ;
 }
 
 sub usage { 
index de0ab1a..f7fe308 100755 (executable)
@@ -873,6 +873,7 @@ sub view_cdr_details {
     'svcnum'      => $cgi->param('svcnum'),
     'beginning'   => $cgi->param('beginning') || '',
     'ending'      => $cgi->param('ending') || '',
+    'inbound'     => $cgi->param('inbound') || 0,
   );
 }
 
index b0205ec..1342c08 100644 (file)
@@ -1,5 +1,6 @@
 <%= $url = "$selfurl?session=$session_id;action="; ''; %>
-<%= include('header', 'Call usage for '.
+<%= include('header', ($inbound ? 'Dialed calls ' : 'Received calls ') . 
+                       ' for '.
                        Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $beginning).
                        ' - '.
                        Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $ending)
index fd5426a..f707668 100644 (file)
 <%= scalar(@svc_acct) ? '</TABLE><BR><BR>' : '' %>
 
 <%= if ( @svc_phone ) {
+      %any = ();
+      for my $dir (qw(outbound inbound)) {
+        $any{$dir} = grep { $_->{$dir} } @svc_phone;
+      }
       $OUT.= '<FONT SIZE="4">Call usage</FONT><BR><BR>
               <TABLE BGCOLOR="#cccccc">
                 <TR>
-                  <TH ALIGN="left">Number</TH>'; #"Account" ?
-                                                 #what else?
+                  <TH ALIGN="left">Number</TH>';
+      if ( $any{outbound} ) {
+        $OUT .= '
+                  <TH>Dialed</TH>';
+      }
+      if ( $any{inbound} ) {
+        $OUT .= '
+                  <TH>Received</TH>';
+      }
       $OUT .= '</TR>';
     } else {
       $OUT .= '';
 <%= foreach my $svc_phone ( @svc_phone ) {
       my $link = "${url}view_cdr_details;".
         "svcnum=$svc_phone->{'svcnum'};beginning=0;ending=0";
-  $OUT .= '<TR><TD>';
-    $OUT .= qq!<A HREF="$link">!. $svc_phone->{'label'}. ': '. $svc_phone->{'value'}.'</A>';
-  $OUT .= '</TD></TR>';
+  $OUT .= '<TR><TD>'. $svc_phone->{'label'}. ': '. $svc_phone->{'value'};
+  $OUT .= '</TD>';
+  # usage summary w/ links
+  for my $dir (qw(outbound inbound)) {
+    if ( $dir eq 'inbound' ) {
+      $link .= ';inbound=1';
+    }
+    if ( $svc_phone->{$dir} ) {
+      $OUT .= '<TD ALIGN="right">'.qq!<A HREF="$link">! .
+        sprintf('%d calls (%.0f minutes)',
+          $svc_phone->{$dir}->{'count'},
+          $svc_phone->{$dir}->{'duration'} / 60
+        ) .
+        '</A></TD>';
+    } elsif ( $any{$dir} )  {
+      $OUT .= '<TD></TD>';
+    }
   }
+  $OUT .= '</TR>';
+}
+'';
 %>
 
 <%= scalar(@svc_phone) ? '</TABLE><BR><BR>' : '' %>
index b958894..b0ce467 100644 (file)
@@ -62,8 +62,14 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
+my $sub_prefixes = sub {
+  my $region = shift;
+  $region->prefixes .
+  ($region->exact_match ? ' <I>(exact match only)</I>' : '');
+};
+
 my @header     = ( '#',         'Region',  'Country code', 'Prefixes' );
-my @fields     = ( 'regionnum', 'regionname',   'ccode',   'prefixes' );
+my @fields     = ( 'regionnum', 'regionname',   'ccode',   $sub_prefixes );
 my @links      = ( ($link) x 4 );
 my @align      = ( 'right', 'left', 'right', 'left' );
 my @xls_format = ( ({ locked=>1, bg_color=>22 }) x 4 );
index 367bbaf..a1c1bcb 100644 (file)
     </TD>
   </TR>
 
+  <& /elements/tr-checkbox.html,
+    label       => 'Exact match',
+    field       => 'exact_match',
+    cell_style  => 'font-weight: bold',
+    value       => 'Y',
+    curr_value  => $rate_region->exact_match
+  &>
+
 </TABLE>
 
 <BR>
index 1b4604b..ca303d3 100644 (file)
@@ -10,7 +10,6 @@
                'links' => \@links,
                'html_form'   => qq!<FORM NAME="cdrForm" ACTION="$p/misc/cdr.cgi" METHOD="POST">!,
                'html_foot' => $html_foot,
-             )
 &>
 <%init>