use Carp;
use Exporter;
use Scalar::Util qw( blessed );
-use Time::Local qw(timelocal timelocal_nocheck);
+use Time::Local qw(timelocal);
use Data::Dumper;
use Tie::IxHash;
use Digest::MD5 qw(md5_base64);
if ( $@ );
if ( $increment_next_bill ) {
-
- #change this bit to use Date::Manip? CAREFUL with timezones (see
- # mailing list archive)
- my ($sec,$min,$hour,$mday,$mon,$year) =
- (localtime($sdate) )[0,1,2,3,4,5];
- #pro-rating magic - if $recur_prog fiddles $sdate, want to use that
+ my $next_bill = $part_pkg->add_freq($sdate);
+ return "unparsable frequency: ". $part_pkg->freq
+ if $next_bill == -1;
+
+ #pro-rating magic - if $recur_prog fiddled $sdate, want to use that
# only for figuring next bill date, nothing else, so, reset $sdate again
# here
$sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
#no need, its in $hash{last_bill}# my $last_bill = $cust_pkg->last_bill;
$cust_pkg->last_bill($sdate);
- if ( $part_pkg->freq =~ /^\d+$/ ) {
- $mon += $part_pkg->freq;
- until ( $mon < 12 ) { $mon -= 12; $year++; }
- } elsif ( $part_pkg->freq =~ /^(\d+)w$/ ) {
- my $weeks = $1;
- $mday += $weeks * 7;
- } elsif ( $part_pkg->freq =~ /^(\d+)d$/ ) {
- my $days = $1;
- $mday += $days;
- } elsif ( $part_pkg->freq =~ /^(\d+)h$/ ) {
- my $hours = $1;
- $hour += $hours;
- } else {
- return "unparsable frequency: ". $part_pkg->freq;
- }
- $cust_pkg->setfield('bill',
- timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year));
+ $cust_pkg->setfield('bill', $next_bill );
}
my @taxes = qsearch( 'cust_main_county', \%taxhash );
- unless ( @taxes ) {
- $taxhash{'taxclass'} = '';
- @taxes = qsearch( 'cust_main_county', \%taxhash );
- }
+ my %taxhash_elim = %taxhash;
- #one more try at a whole-country tax rate
- unless ( @taxes ) {
- $taxhash{$_} = '' foreach qw( state county );
- @taxes = qsearch( 'cust_main_county', \%taxhash );
+ my @elim = qw( taxclass county state );
+ while ( !scalar(@taxes) && scalar(@elim) ) {
+ $taxhash_elim{ shift(@elim) } = '';
+ @taxes = qsearch( 'cust_main_county', \%taxhash_elim );
}
if ( $conf->exists('tax-pkg_address') && $cust_pkg->locationnum ) {
})
if scalar(@taxclassnums);
- # maybe eliminate this entirely, along with all the 0% records
- unless ( @taxes ) {
- return
- "fatal: can't find tax rate for geocode/taxproduct/pkgpart ".
- join('/', $geocode,
- $part_pkg->taxproduct_description,
- $part_pkg->pkgpart
- );
- }
-
warn "Found taxes ".
join(',', map{ ref($_). " ". $_->get($_->primary_key) } @taxes). "\n"
if $DEBUG;
'country' => ( exists($options{'country'})
? $options{'country'}
: $self->country ),
- 'referer' => 'http://cleanwhisker.420.am/',
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
'email' => $email,
'phone' => $self->daytime || $self->night,
%content, #after
$cust_pay_pending->status('done');
$cust_pay_pending->statustext('captured');
+ $cust_pay_pending->paynum($cust_pay->paynum);
my $cpp_done_err = $cust_pay_pending->replace;
if ( $cpp_done_err ) {
'password' => $password,
'order_number' => $order_number,
'amount' => $amount,
- 'referer' => 'http://cleanwhisker.420.am/',
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
);
$content{authorization} = $auth
if length($auth); #echeck/ACH transactions have an order # but no auth
sub charge {
my $self = shift;
- my ( $amount, $quantity, $pkg, $comment, $taxclass, $additional, $classnum );
- my ( $taxproduct, $override );
+ my ( $amount, $quantity, $pkg, $comment, $classnum, $additional );
+ my ( $setuptax, $taxclass ); #internal taxes
+ my ( $taxproduct, $override ); #vendor (CCH) taxes
if ( ref( $_[0] ) ) {
$amount = $_[0]->{amount};
$quantity = exists($_[0]->{quantity}) ? $_[0]->{quantity} : 1;
$pkg = exists($_[0]->{pkg}) ? $_[0]->{pkg} : 'One-time charge';
$comment = exists($_[0]->{comment}) ? $_[0]->{comment}
: '$'. sprintf("%.2f",$amount);
+ $setuptax = exists($_[0]->{setuptax}) ? $_[0]->{setuptax} : '';
$taxclass = exists($_[0]->{taxclass}) ? $_[0]->{taxclass} : '';
$classnum = exists($_[0]->{classnum}) ? $_[0]->{classnum} : '';
$additional = $_[0]->{additional};
$quantity = 1;
$pkg = @_ ? shift : 'One-time charge';
$comment = @_ ? shift : '$'. sprintf("%.2f",$amount);
+ $setuptax = '';
$taxclass = @_ ? shift : '';
$additional = [];
}
'freq' => 0,
'disabled' => 'Y',
'classnum' => $classnum ? $classnum : '',
+ 'setuptax' => $setuptax,
'taxclass' => $taxclass,
'taxproductnum' => $taxproduct,
} );
qsearch( 'cust_pay_batch', { 'custnum' => $self->custnum } )
}
+=item cust_pay_pending
+
+Returns all pending payments (see L<FS::cust_pay_pending>) for this customer
+(without status "done").
+
+=cut
+
+sub cust_pay_pending {
+ my $self = shift;
+ return $self->num_cust_pay_pending unless wantarray;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_pending', {
+ 'custnum' => $self->custnum,
+ 'status' => { op=>'!=', value=>'done' },
+ },
+ );
+}
+
+=item num_cust_pay_pending
+
+Returns the number of pending payments (see L<FS::cust_pay_pending>) for this
+customer (without status "done"). Also called automatically when the
+cust_pay_pending method is used in a scalar context.
+
+=cut
+
+sub num_cust_pay_pending {
+ my $self = shift;
+ my $sql = " SELECT COUNT(*) FROM cust_pay_pending ".
+ " WHERE custnum = ? AND status != 'done' ";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute($self->custnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
=item cust_refund
Returns all the refunds (see L<FS::cust_refund>) for this customer.
" AND peo_agentnum.optionname = 'agentnum' ".
" AND ( agentnum IS NULL OR agentnum = $agentnum ) ".
" ORDER BY
- CASE WHEN peo_cust_bill_age.optionname != 'cust_bill_age'
+ CASE WHEN part_event_condition_option.optionname IS NULL
THEN -1
ELSE ". FS::part_event::Condition->age2seconds_sql('part_event_condition_option.optionvalue').
" END