--- /dev/null
+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;
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);
'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',
use_duration
411_rewrite
output_format summarize_usage usage_section
+ bill_every_call
)
],
'weight' => 40,
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"
$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, } );
}