RT#39638: VoIP Usage cost reporting [bug fixes/cleanup]
[freeside.git] / FS / FS / cdr.pm
index cced0eb..5a1d8ea 100644 (file)
@@ -214,6 +214,7 @@ sub table_info {
         #'upstream_rateplanid'   => '',
         #'ratedetailnum'         => '',
         'rated_price'           => 'Rated price',
         #'upstream_rateplanid'   => '',
         #'ratedetailnum'         => '',
         'rated_price'           => 'Rated price',
+        'rated_cost'            => 'Rated cost',
         #'distance'              => '',
         #'islocal'               => '',
         #'calltypenum'           => '',
         #'distance'              => '',
         #'islocal'               => '',
         #'calltypenum'           => '',
@@ -368,8 +369,10 @@ to inspect other field.
 sub is_tollfree {
   my $self = shift;
   my $field = scalar(@_) ? shift : 'dst';
 sub is_tollfree {
   my $self = shift;
   my $field = scalar(@_) ? shift : 'dst';
-  my $country = $conf->config('tollfree-country');
+  my $country = $conf->config('tollfree-country') || '';
   if ( $country eq 'AU' ) { 
   if ( $country eq 'AU' ) { 
+    ( $self->$field() =~ /^(\+?61)?(1800|1300)/ ) ? 1 : 0;
+  } elsif ( $country eq 'NZ' ) { 
     ( $self->$field() =~ /^(\+?64)?(800|508)/ ) ? 1 : 0;
   } else { #NANPA (US/Canaada)
     ( $self->$field() =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0;
     ( $self->$field() =~ /^(\+?64)?(800|508)/ ) ? 1 : 0;
   } else { #NANPA (US/Canaada)
     ( $self->$field() =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0;
@@ -461,7 +464,9 @@ Sets the status and rated price.
 
 Available options are: inbound, rated_pretty_dst, rated_regionname,
 rated_seconds, rated_minutes, rated_granularity, rated_ratedetailnum,
 
 Available options are: inbound, rated_pretty_dst, rated_regionname,
 rated_seconds, rated_minutes, rated_granularity, rated_ratedetailnum,
-rated_classnum, rated_ratename.
+rated_classnum, rated_ratename.  If rated_ratedetailnum is provided,
+will also set a recalculated L</rate_cost> in the rated_cost field 
+after the other fields are set (does not work with inbound.)
 
 If there is an error, returns the error, otherwise returns false.
 
 
 If there is an error, returns the error, otherwise returns false.
 
@@ -499,6 +504,8 @@ sub set_status_and_rated_price {
         qw( pretty_dst regionname seconds minutes granularity
             ratedetailnum classnum ratename );
     $self->svcnum($svcnum) if $svcnum;
         qw( pretty_dst regionname seconds minutes granularity
             ratedetailnum classnum ratename );
     $self->svcnum($svcnum) if $svcnum;
+    $self->rated_cost($self->rate_cost) if $opt{'rated_ratedetailnum'};
+
     return $self->replace();
 
   }
     return $self->replace();
 
   }
@@ -797,8 +804,8 @@ sub rate_prefix {
 
   }
 
 
   }
 
+  my $regionnum = $rate_detail->dest_regionnum;
   my $rate_region = $rate_detail->dest_region;
   my $rate_region = $rate_detail->dest_region;
-  my $regionnum = $rate_region->regionnum;
   warn "  found rate for regionnum $regionnum ".
        "and rate detail $rate_detail\n"
     if $DEBUG;
   warn "  found rate for regionnum $regionnum ".
        "and rate detail $rate_detail\n"
     if $DEBUG;
@@ -840,6 +847,11 @@ sub rate_prefix {
   my $charge = 0;
   my $connection_charged = 0;
 
   my $charge = 0;
   my $connection_charged = 0;
 
+  # before doing anything else, if there's an upstream multiplier and 
+  # an upstream price, add that to the charge. (usually the rate detail 
+  # will then have a minute charge of zero, but not necessarily.)
+  $charge += ($self->upstream_price || 0) * $rate_detail->upstream_mult_charge;
+
   my $etime;
   while($seconds_left) {
     my $ratetimenum = $rate_detail->ratetimenum; # may be empty
   my $etime;
   while($seconds_left) {
     my $ratetimenum = $rate_detail->ratetimenum; # may be empty
@@ -987,7 +999,7 @@ sub rate_prefix {
     $price,
     $opt{'svcnum'},
     'rated_pretty_dst'    => $pretty_dst,
     $price,
     $opt{'svcnum'},
     'rated_pretty_dst'    => $pretty_dst,
-    'rated_regionname'    => $rate_region->regionname,
+    'rated_regionname'    => ($rate_region ? $rate_region->regionname : ''),
     'rated_seconds'       => $rated_seconds, #$seconds,
     'rated_granularity'   => $rate_detail->sec_granularity, #$granularity
     'rated_ratedetailnum' => $rate_detail->ratedetailnum,
     'rated_seconds'       => $rated_seconds, #$seconds,
     'rated_granularity'   => $rate_detail->sec_granularity, #$granularity
     'rated_ratedetailnum' => $rate_detail->ratedetailnum,
@@ -1071,10 +1083,15 @@ sub rate_cost {
   my $rate_detail =
     qsearchs('rate_detail', { 'ratedetailnum' => $self->rated_ratedetailnum } );
 
   my $rate_detail =
     qsearchs('rate_detail', { 'ratedetailnum' => $self->rated_ratedetailnum } );
 
-  return $rate_detail->min_cost if $self->rated_granularity == 0;
+  my $charge = 0;
+  $charge += ($self->upstream_price || 0) * ($rate_detail->upstream_mult_cost);
 
 
-  my $minutes = $self->rated_seconds / 60;
-  my $charge = $rate_detail->conn_cost + $minutes * $rate_detail->min_cost;
+  if ( $self->rated_granularity == 0 ) {
+    $charge += $rate_detail->min_cost;
+  } else {
+    my $minutes = $self->rated_seconds / 60;
+    $charge += $rate_detail->conn_cost + $minutes * $rate_detail->min_cost;
+  }
 
   sprintf('%.2f', $charge + .00001 );
 
 
   sprintf('%.2f', $charge + .00001 );
 
@@ -1446,7 +1463,7 @@ as keys (for use with part_pkg::voip_cdr) and "pretty" format names as values.
 sub invoice_formats {
   map { ($_ => $export_names{$_}->{'name'}) }
     grep { $export_names{$_}->{'invoice_header'} }
 sub invoice_formats {
   map { ($_ => $export_names{$_}->{'name'}) }
     grep { $export_names{$_}->{'invoice_header'} }
-    keys %export_names;
+    sort keys %export_names;
 }
 
 =item invoice_header FORMAT
 }
 
 =item invoice_header FORMAT
@@ -1615,7 +1632,7 @@ sub _cdr_date_parse {
     # optionally without seconds
     ($mon, $day, $year, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
     $sec = 0 if !defined($sec);
     # optionally without seconds
     ($mon, $day, $year, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
     $sec = 0 if !defined($sec);
-  } elsif ( $date  =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d+\.\d+)(\D|$)/ ) {
+   } elsif ( $date  =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\.\d+)$/ ) {
     # broadsoft: 20081223201938.314
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
   } elsif ( $date  =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\d+(\D|$)/ ) {
     # broadsoft: 20081223201938.314
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
   } elsif ( $date  =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\d+(\D|$)/ ) {
@@ -1625,7 +1642,7 @@ sub _cdr_date_parse {
     # WIP: 20100329121420
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
   } elsif ( $date =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/) {
     # WIP: 20100329121420
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
   } elsif ( $date =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/) {
-    # Telos
+    # Telos 2014-10-10T05:30:33Z
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
     $options{gmt} = 1;
   } else {
     ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
     $options{gmt} = 1;
   } else {