1 package FS::ClientAPI::PrepaidPhone;
4 use vars qw($DEBUG $me);
5 use FS::Record qw(qsearchs);
11 $me = '[FS::ClientAPI::PrepaidPhone]';
14 # - shared-secret auth? (set a conf value)
16 =item call_time HASHREF
18 HASHREF contains the following parameters:
24 Source number (with countrycode)
28 Destination number (with countrycode)
32 Always returns a hashref. If there is an error, the hashref contains a single
33 "error" key with the error message as a value. Otherwise, returns a hashref
34 with the following keys:
40 Empty if no customer is found associated with the number, customer number
45 Number of seconds remaining for a call to destination number
54 my $src = $packet->{'src'};
55 my $dst = $packet->{'dst'};
59 #my $conf = new FS::Conf;
60 #if ( #XXX toll-free? collect?
62 #} else { #use the src to find the customer
67 my( $countrycode, $phonenum );
68 if ( $chargeto #an interesting regex to parse out 1&2 digit countrycodes
69 =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
70 || $chargeto =~ /^(\d{3})(\d*)$/
76 return { 'error' => "unparsable billing number: $chargeto" };
80 my $svc_phone = qsearchs('svc_phone', { 'countrycode' => $countrycode,
81 'phonenum' => $phonenum,
85 unless ( $svc_phone ) {
86 return { 'error' => "can't find customer for +$countrycode $phonenum" };
87 # return { 'custnum' => '',
93 my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
94 my $cust_main = $cust_pkg->cust_main;
96 my $part_pkg = $cust_pkg->part_pkg;
97 my @part_pkg = ( $part_pkg, map $_->dst_pkg, $part_pkg->bill_part_pkg_link );
98 #XXX uuh, behavior indeterminate if you have more than one voip_cdr+prefix
100 warn "$me ". scalar(@part_pkg). ': '.
101 join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
104 grep { $_->plan eq 'voip_cdr' && $_->option('rating_method') eq 'prefix' }
108 'custnum' => $cust_pkg->custnum,
109 #'balance' => $cust_pkg->cust_main->balance,
112 warn "$me: ". scalar(@part_pkg). ': '.
113 join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
115 return \%return unless @part_pkg;
117 warn "$me searching for rate ". $part_pkg[0]->option('ratenum')
120 my $rate = qsearchs('rate', { 'ratenum'=>$part_pkg[0]->option('ratenum') } );
123 my $error = 'ratenum '. $part_pkg[0]->option('ratenum'). ' not found';
126 return { 'error'=>$error };
129 warn "$me found rate ". $rate->ratenum
132 #rate the call and arrive at a max # of seconds for the customer's balance
134 my( $rate_countrycode, $rate_phonenum );
135 if ( $rateby #this is an interesting regex to parse out 1&2 digit countrycodes
136 =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
137 || $rateby =~ /^(\d{3})(\d*)$/
140 $rate_countrycode = $1;
143 return { 'error' => "unparsable rating number: $rateby" };
146 my $rate_detail = $rate->dest_detail({ 'countrycode' => $rate_countrycode,
147 'phonenum' => $rate_phonenum,
149 unless ( $rate_detail ) {
150 return { 'error'=>"can't find rate for +$rate_countrycode $rate_phonenum"};
153 unless ( $rate_detail->min_charge > 0 ) {
154 #XXX no charge?? return lots of seconds, a default, 0 or what?
155 #return { 'error' => '0 rate for +$rate_countrycode $rate_phonenum; prepaid service not available" };
156 #customer wants no default for now# $return{'seconds'} = 1800; #half hour?!
160 my $balance = FS::ClientAPI::PrepaidPhone->prepaid_phone_balance( $cust_pkg );
162 #XXX granularity? included minutes? another day...
163 if ( $balance >= 0 ) {
164 return { 'error'=>'No balance' };
166 $return{'seconds'} = int(60 * abs($balance) / $rate_detail->min_charge);
169 warn "$me returning seconds: ". $return{'seconds'};
175 =item call_time_nanpa
177 Like I<call_time>, except countrycode 1 is not required, and all other
178 countrycodes must be prefixed with 011.
182 # - everything is assumed to be countrycode 1 unless it starts with 011(ccode)
183 sub call_time_nanpa {
186 foreach (qw( src dst )) {
187 if ( $packet->{$_} =~ /^011(\d+)/ ) {
189 } elsif ( $packet->{$_} !~ /^1/ ) {
190 $packet->{$_} = '1'.$packet->{$_};
198 =item phonenum_balance HASHREF
200 HASHREF contains the following parameters:
206 Optional countrycode. Defaults to 1.
214 Always returns a hashref. If there is an error, the hashref contains a single
215 "error" key with the error message as a value. Otherwise, returns a hashref
216 with the following keys:
222 Empty if no customer is found associated with the number, customer number
233 sub phonenum_balance {
236 warn "$me phonenum_balance called with countrycode ".$packet->{'countrycode'}.
237 " and phonenum ". $packet->{'phonenum'}. "\n"
240 my $svc_phone = qsearchs('svc_phone', {
241 'countrycode' => ( $packet->{'countrycode'} || 1 ),
242 'phonenum' => $packet->{'phonenum'},
245 unless ( $svc_phone ) {
246 warn "$me no phone number found\n" if $DEBUG;
247 return { 'custnum' => '',
252 my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
254 my $balance = FS::ClientAPI::PrepaidPhone->prepaid_phone_balance( $cust_pkg );
256 warn "$me returning $balance balance for pkgnum ". $cust_pkg->pkgnum.
257 ", custnum ". $cust_pkg->custnum
261 'custnum' => $cust_pkg->custnum,
262 'balance' => $balance,
267 sub prepaid_phone_balance {
268 my $class = shift; # i guess
271 my $conf = new FS::Conf;
273 my $pkg_balances = $conf->config_bool('pkg-balances');
275 my $balance = $pkg_balances ? $cust_pkg->balance
276 : $cust_pkg->cust_main->balance;
278 if ( $conf->config_bool('cdr-prerate') ) {
279 my @cust_pkg = $pkg_balances ? ( $cust_pkg )
280 : ( $cust_pkg->cust_main->ncancelled_pkgs );
281 foreach my $cust_pkg ( @cust_pkg ) {
283 #we only support prerated CDRs with "VOIP/telco CDR rating (standard)"
284 # and "Phone numbers (svc_phone.phonenum)" CDR service matching for now
285 my $part_pkg = $cust_pkg->part_pkg;
286 next unless $part_pkg->plan eq 'voip_cdr'
287 && ($part_pkg->option('cdr_svc_method') || 'svc_phone.phonenum')
288 eq 'svc_phone.phonenum'
289 && ! $part_pkg->option('bill_inactive_svcs');
290 #XXX skip when there's included minutes
292 #select prerated cdrs & subtract them from balance
294 # false laziness w/ part_pkg/voip_cdr.pm sorta
297 'disable_src' => $part_pkg->option('disable_src'),
298 'default_prefix' => $part_pkg->option('default_prefix'),
299 'cdrtypenum' => $part_pkg->option('use_cdrtypenum'),
300 'calltypenum' => $part_pkg->option('use_calltypenum'),
303 ); # $last_bill, $$sdate )
305 my @cust_svc = grep { $_->part_svc->svcdb eq 'svc_phone' }
307 foreach my $cust_svc ( @cust_svc ) {
309 my $svc_x = $cust_svc->svc_x;
310 my $sum_cdr = $svc_x->sum_cdrs(%options);
311 $balance += $sum_cdr->rated_price;