stray closing /TABLE in the no-ticket case
[freeside.git] / FS / FS / ClientAPI / PrepaidPhone.pm
index 8f53491..2cea3c2 100644 (file)
@@ -1,13 +1,14 @@
 package FS::ClientAPI::PrepaidPhone;
 
 use strict;
-#use vars qw($DEBUG $me);
+use vars qw($DEBUG $me);
 use FS::Record qw(qsearchs);
+use FS::Conf;
 use FS::rate;
 use FS::svc_phone;
 
-#$DEBUG = 0;
-#$me = '[FS::ClientAPI::PrepaidPhone]';
+$DEBUG = 0;
+$me = '[FS::ClientAPI::PrepaidPhone]';
 
 #TODO:
 # - shared-secret auth? (set a conf value)
@@ -53,26 +54,29 @@ sub call_time {
   my $src = $packet->{'src'};
   my $dst = $packet->{'dst'};
 
-  my $number;
+  my $chargeto;
+  my $rateby;
   #my $conf = new FS::Conf;
   #if ( #XXX toll-free?  collect?
   #  $phonenum = $dst;
   #} else { #use the src to find the customer
-    $number = $src;
+    $chargeto = $src;
+    $rateby = $dst;
   #}
 
   my( $countrycode, $phonenum );
-  if ( $number #this is an interesting regex to parse out 1&2 digit countrycodes
+  if ( $chargeto #an interesting regex to parse out 1&2 digit countrycodes
          =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
-       || $number =~ /^(\d{3})(\d*)$/
+       || $chargeto =~ /^(\d{3})(\d*)$/
      )
   {
     $countrycode = $1;
     $phonenum = $2;
   } else { 
-    return { 'error' => "unparsable number: $number" };
+    return { 'error' => "unparsable billing number: $chargeto" };
   }
 
+
   my $svc_phone = qsearchs('svc_phone', { 'countrycode' => $countrycode,
                                           'phonenum'    => $phonenum,
                                         }
@@ -87,27 +91,82 @@ sub call_time {
   };
 
   my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
-  my $part_pkg = $cust_pkg->part_pkg;
   my $cust_main = $cust_pkg->cust_main;
 
+  my $part_pkg = $cust_pkg->part_pkg;
+  my @part_pkg = ( $part_pkg, map $_->dst_pkg, $part_pkg->bill_part_pkg_link );
+  #XXX uuh, behavior indeterminate if you have more than one voip_cdr+prefix
+  #add-on, i guess.
+  warn "$me ". scalar(@part_pkg). ': '.
+       join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
+    if $DEBUG;
+  @part_pkg =
+    grep { $_->plan eq 'voip_cdr' && $_->option('rating_method') eq 'prefix' }
+         @part_pkg;
+
   my %return = (
     'custnum' => $cust_pkg->custnum,
     #'balance' => $cust_pkg->cust_main->balance,
   );
 
-  return \%return unless $part_pkg->plan eq 'voip_cdr'
-                      && $part_pkg->option('rating_method') eq 'prefix';
+  warn "$me: ". scalar(@part_pkg). ': '.
+       join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
+    if $DEBUG;
+  return \%return unless @part_pkg;
+
+  warn "$me searching for rate ". $part_pkg[0]->option('ratenum')
+    if $DEBUG;
+
+  my $rate = qsearchs('rate', { 'ratenum'=>$part_pkg[0]->option('ratenum') } );
+
+  unless ( $rate ) {
+    my $error = 'ratenum '. $part_pkg[0]->option('ratenum'). ' not found';
+    warn "$me $error"
+      if $DEBUG;
+    return { 'error'=>$error };
+  }
 
-  my $rate = qsearchs('rate', { 'ratenum' => $part_pkg->option('ratenum') } );
+  warn "$me found rate ". $rate->ratenum
+    if $DEBUG;
 
   #rate the call and arrive at a max # of seconds for the customer's balance
-  my $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
-                                         'phonenum'    => $phonenum,
+
+  my( $rate_countrycode, $rate_phonenum );
+  if ( $rateby #this is an interesting regex to parse out 1&2 digit countrycodes
+         =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
+       || $rateby =~ /^(\d{3})(\d*)$/
+     )
+  {
+    $rate_countrycode = $1;
+    $rate_phonenum = $2;
+  } else { 
+    return { 'error' => "unparsable rating number: $rateby" };
+  }
+
+  my $rate_detail = $rate->dest_detail({ 'countrycode' => $rate_countrycode,
+                                         'phonenum'    => $rate_phonenum,
                                        });
+  unless ( $rate_detail ) {
+    return { 'error'=>"can't find rate for +$rate_countrycode $rate_phonenum"};
+  }
+
+  unless ( $rate_detail->min_charge > 0 ) {
+    #XXX no charge??  return lots of seconds, a default, 0 or what?
+    #return { 'error' => '0 rate for +$rate_countrycode $rate_phonenum; prepaid service not available" };
+    #customer wants no default for now# $return{'seconds'} = 1800; #half hour?!
+    return \%return;
+  }
+
+  my $balance = FS::ClientAPI::PrepaidPhone->prepaid_phone_balance( $cust_pkg );
 
   #XXX granularity?  included minutes?  another day...
+  if ( $balance >= 0 ) {
+    return { 'error'=>'No balance' };
+  } else {
+    $return{'seconds'} = int(60 * abs($balance) / $rate_detail->min_charge);
+  }
 
-  $return{'seconds'} = int(60 * $cust_main->balance / $rate_detail->min_charge);
+  warn "$me returning seconds: ". $return{'seconds'};
 
   return \%return;
  
@@ -174,12 +233,17 @@ Customer balance.
 sub phonenum_balance {
   my $packet = shift;
 
+  warn "$me phonenum_balance called with countrycode ".$packet->{'countrycode'}.
+       " and phonenum ". $packet->{'phonenum'}. "\n"
+    if $DEBUG;
+
   my $svc_phone = qsearchs('svc_phone', {
     'countrycode' => ( $packet->{'countrycode'} || 1 ),
     'phonenum'    => $packet->{'phonenum'},
   });
 
   unless ( $svc_phone ) {
+    warn "$me no phone number found\n" if $DEBUG;
     return { 'custnum' => '',
              'balance' => 0,
            };
@@ -187,11 +251,72 @@ sub phonenum_balance {
 
   my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
 
+  my $balance = FS::ClientAPI::PrepaidPhone->prepaid_phone_balance( $cust_pkg );
+
+  warn "$me returning $balance balance for pkgnum ".  $cust_pkg->pkgnum.
+                                        ", custnum ". $cust_pkg->custnum
+    if $DEBUG;
+
   return {
     'custnum' => $cust_pkg->custnum,
-    'balance' => $cust_pkg->cust_main->balance,
+    'balance' => $balance,
   };
 
 }
 
+sub prepaid_phone_balance {
+  my $class = shift; # i guess
+  my ($cust_pkg) = @_;
+
+  my $conf = new FS::Conf;
+
+  my $pkg_balances = $conf->config_bool('pkg-balances');
+  
+  my $balance = $pkg_balances ? $cust_pkg->balance
+                              : $cust_pkg->cust_main->balance;
+
+  if ( $conf->config_bool('cdr-prerate') ) {
+    my @cust_pkg = $pkg_balances ? ( $cust_pkg )
+                                 : ( $cust_pkg->cust_main->ncancelled_pkgs );
+    foreach my $cust_pkg ( @cust_pkg ) {
+
+      #we only support prerated CDRs with "VOIP/telco CDR rating (standard)"
+      # and "Phone numbers (svc_phone.phonenum)" CDR service matching for now
+      my $part_pkg = $cust_pkg->part_pkg;
+      next unless $part_pkg->plan eq 'voip_cdr'
+               && ($part_pkg->option('cdr_svc_method') || 'svc_phone.phonenum')
+                    eq 'svc_phone.phonenum'
+               && ! $part_pkg->option('bill_inactive_svcs');
+      #XXX skip when there's included minutes
+
+      #select prerated cdrs & subtract them from balance
+
+      # false laziness w/ part_pkg/voip_cdr.pm sorta
+
+      my %options = (
+          'disable_src'    => $part_pkg->option('disable_src'),
+          'default_prefix' => $part_pkg->option('default_prefix'),
+          'cdrtypenum'     => $part_pkg->option('use_cdrtypenum'),
+          'calltypenum'    => $part_pkg->option('use_calltypenum'),
+          'status'         => 'rated',
+          'by_svcnum'      => 1,
+      );  # $last_bill, $$sdate )
+
+      my @cust_svc = grep { $_->part_svc->svcdb eq 'svc_phone' }
+                       $cust_pkg->cust_svc;
+      foreach my $cust_svc ( @cust_svc ) {
+        
+        my $svc_x = $cust_svc->svc_x;
+        my $sum_cdr = $svc_x->sum_cdrs(%options);
+        $balance += $sum_cdr->rated_price;
+
+      }
+
+    }
+  }
+
+  $balance;
+
+}
+
 1;