adding prepaid self-service hooks, RT#4100
authorivan <ivan>
Fri, 24 Oct 2008 21:31:38 +0000 (21:31 +0000)
committerivan <ivan>
Fri, 24 Oct 2008 21:31:38 +0000 (21:31 +0000)
FS/FS/ClientAPI/PrepaidPhone.pm [new file with mode: 0644]
FS/FS/part_pkg/voip_cdr.pm
FS/FS/part_pkg/voip_sqlradacct.pm
FS/FS/rate.pm

diff --git a/FS/FS/ClientAPI/PrepaidPhone.pm b/FS/FS/ClientAPI/PrepaidPhone.pm
new file mode 100644 (file)
index 0000000..2d6297a
--- /dev/null
@@ -0,0 +1,196 @@
+package FS::ClientAPI::PrepaidPhone;
+
+use strict;
+#use vars qw($DEBUG $me);
+use FS::Record qw(qsearchs);
+use FS::rate;
+use FS::svc_phone;
+
+#$DEBUG = 0;
+#$me = '[FS::ClientAPI::PrepaidPhone]';
+
+#TODO:
+# - shared-secret auth? (set a conf value)
+
+=item call_time HASHREF
+
+HASHREF contains the following parameters:
+
+=over 4
+
+=item src
+
+Source number (with countrycode)
+
+=item dst
+
+Destination number (with countrycode)
+
+=back
+
+Always returns a hashref.  If there is an error, the hashref contains a single
+"error" key with the error message as a value.  Otherwise, returns a hashref
+with the following keys:
+
+=over 4
+
+=item custnum
+
+Empty if no customer is found associated with the number, customer number
+otherwise.
+
+=item seconds
+
+Number of seconds remaining for a call to destination number
+
+=back
+
+=cut
+
+sub call_time {
+  my $packet = shift;
+
+  my $src = $packet->{'src'};
+  my $dst = $packet->{'dst'};
+
+  my $number;
+  #my $conf = new FS::Conf;
+  #if ( #XXX toll-free?  collect?
+  #  $phonenum = $dst;
+  #} else { #use the src to find the customer
+    $number = $src;
+  #}
+
+  my( $countrycode, $phonenum );
+  if ( $number #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*)$/
+       || $number =~ /^(\d{3})(\d*)$/
+     )
+  {
+    $countrycode = $1;
+    $phonenum = $2;
+  } else { 
+    return { 'error' => "unparsable number: $number" };
+  }
+
+  my $svc_phone = qsearchs('svc_phone', { 'countrycode' => $countrycode,
+                                          'phonenum'    => $phonenum,
+                                        }
+                          );
+
+  unless ( $svc_phone ) {
+    return { 'custnum' => '',
+             'seconds' => 0,
+             #'balance' => 0,
+           };
+  };
+
+  my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+  my $part_pkg = $cust_pkg->part_pkg;
+  my $cust_main = $cust_pkg->cust_main;
+
+  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';
+
+  my $rate = qsearchs('rate', { 'ratenum' => $part_pkg->option('ratenum') } );
+
+  #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,
+                                       });
+
+  #XXX granularity?  included minutes?  another day...
+
+  $return{'seconds'} = int(60 * $cust_main->balance / $rate_detail->min_charge);
+
+  return \%return;
+}
+
+=item call_time_nanpa 
+
+Like I<call_time>, except countrycode 1 is not required, and all other
+countrycodes must be prefixed with 011.
+
+=cut
+
+# - everything is assumed to be countrycode 1 unless it starts with 011(ccode)
+sub call_time_nanpa {
+  my $packet = shift;
+
+  foreach (qw( src dst )) {
+    if ( $packet->{$_} =~ /^011(\d+)/ ) {
+      $packet->{$_} = $1;
+    } elsif ( $packet->{$_} !~ /^1/ ) {
+      $packet->{$_} = '1'.$packet->{$_};
+    }
+  }
+
+  call_time($packet);
+
+}
+
+=item phonenum_balance HASHREF
+
+HASHREF contains the following parameters:
+
+=over 4
+
+=item countrycode
+
+Optional countrycode.  Defaults to 1.
+
+=item phonenum
+
+Phone number.
+
+=back
+
+Always returns a hashref.  If there is an error, the hashref contains a single
+"error" key with the error message as a value.  Otherwise, returns a hashref
+with the following keys:
+
+=over 4
+
+=item custnum
+
+Empty if no customer is found associated with the number, customer number
+otherwise.
+
+=item balance
+
+Customer balance.
+
+=back
+
+=cut
+
+sub phonenum_balance {
+  my $packet = shift;
+
+  my $svc_phone = qsearchs('svc_phone', {
+    'countrycode' => ( $packet->{'countrycode'} || 1 ),
+    'phonenum'    => $packet->{'phonenum'},
+  });
+
+  unless ( $svc_phone ) {
+    return { 'custnum' => '',
+             'balance' => 0,
+           };
+  };
+
+  my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+
+  return {
+    'custnum' => $cust_pkg->custnum,
+    'balance' => $cust_pkg->cust_main->balance,
+  };
+
+}
+
+1;
index 0ceaac1..1b565f2 100644 (file)
@@ -8,8 +8,9 @@ use FS::Conf;
 use FS::Record qw(qsearchs qsearch);
 use FS::part_pkg::flat;
 use FS::cdr;
-#use FS::rate;
-#use FS::rate_prefix;
+use FS::rate;
+use FS::rate_prefix;
+use FS::rate_detail;
 
 @ISA = qw(FS::part_pkg::flat);
 
@@ -125,6 +126,10 @@ tie my %temporalities, 'Tie::IxHash',
                           'type' => 'checkbox',
                         },
 
+    'bill_every_call' => { 'name' => 'Generate an invoice immediately for every call.  Useful for prepaid.',
+                           'type' => 'checkbox',
+                         },
+
     #XXX also have option for an external db
 #    'cdr_location' => { 'name' => 'CDR database location'
 #                        'type' => 'select',
@@ -161,6 +166,7 @@ tie my %temporalities, 'Tie::IxHash',
                        use_duration
                        411_rewrite
                        output_format summarize_usage usage_section
+                       bill_every_call
                      )
                   ],
   'weight' => 40,
@@ -321,32 +327,15 @@ sub calc_recur {
           warn "rating call $to_or_from +$countrycode $number\n" if $DEBUG;
           $pretty_destnum = "+$countrycode $number";
 
-          #find a rate prefix, first look at most specific (4 digits) then 3, etc.,
-          # finally trying the country code only
-          my $rate_prefix = '';
-          for my $len ( reverse(1..6) ) {
-            $rate_prefix = qsearchs('rate_prefix', {
-              'countrycode' => $countrycode,
-              #'npa'         => { op=> 'LIKE', value=> substr($number, 0, $len) }
-              'npa'         => substr($number, 0, $len),
-            } ) and last;
-          }
-          $rate_prefix ||= qsearchs('rate_prefix', {
-            'countrycode' => $countrycode,
-            'npa'         => '',
-          });
-
-          #
-          die "Can't find rate for call $to_or_from +$countrycode $number\n"
-            unless $rate_prefix;
-
-          $regionnum = $rate_prefix->regionnum;
-          $rate_detail = qsearchs('rate_detail', {
-            'ratenum'        => $ratenum,
-            'dest_regionnum' => $regionnum,
-          } );
-
-          $rate_region = $rate_prefix->rate_region;
+          my $rate = qsearchs('rate', { 'ratenum' => $ratenum })
+            or die "ratenum $ratenum not found!";
+
+          $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
+                                              'phonenum'    => $number,
+                                            });
+
+          $rate_region = $rate_detail->dest_region;
+          $regionnum = $rate_region->regionnum;
 
           warn "  found rate for regionnum $regionnum ".
                "and rate detail $rate_detail\n"
index 49ef515..b4f0cf9 100644 (file)
@@ -13,6 +13,7 @@ use FS::rate_prefix;
 $DEBUG = 1;
 
 %info = (
+  'disabled' => 1, #they're sucked into our CDR table now instead
   'name' => 'VoIP rating by plan of CDR records in an SQL RADIUS radacct table',
   'shortname' => 'VoIP/telco CDR rating (external RADIUS)',
   'fields' => {
index c50ca04..257278a 100644 (file)
@@ -269,16 +269,52 @@ sub check {
   $self->SUPER::check;
 }
 
-=item dest_detail REGIONNUM | RATE_REGION_OBJECTD
+=item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
 
 Returns the rate detail (see L<FS::rate_detail>) for this rate to the
-specificed destination.
+specificed destination.  Destination can be specified as an FS::rate_detail
+object or regionnum (see L<FS::rate_detail>), or as a hashref with two keys:
+I<countrycode> and I<phonenum>.
 
 =cut
 
 sub dest_detail {
   my $self = shift;
-  my $regionnum = ref($_[0]) ? shift->regionnum : shift;
+
+  my $regionnum;
+  if ( ref($_[0]) eq 'HASH' ) {
+
+    my $countrycode = $_->{'countrycode'};
+    my $phonenum    = $_->{'phonenum'};
+
+    #find a rate prefix, first look at most specific (4 digits) then 3, etc.,
+    # finally trying the country code only
+    my $rate_prefix = '';
+    for my $len ( reverse(1..6) ) {
+      $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'         => '',
+    });
+
+    #
+    #die "Can't find rate for call $to_or_from +$countrycode $number\n"
+    die "Can't find rate for +$countrycode $phonenum\n"
+      unless $rate_prefix;
+
+    $regionnum = $rate_prefix->regionnum;
+
+    #$rate_region = $rate_prefix->rate_region;
+
+  } else {
+    $regionnum = ref($_[0]) ? shift->regionnum : shift;
+  }
+
   qsearchs( 'rate_detail', { 'ratenum'        => $self->ratenum,
                              'dest_regionnum' => $regionnum,     } );
 }