use vars qw( @ISA $conf $Debug $import );
use Safe;
use Carp;
-use Time::Local;
+BEGIN {
+ eval "use Time::Local;";
+ die "Time::Local version 1.05 required with Perl versions before 5.6"
+ if $] < 5.006 && !defined($Time::Local::VERSION);
+ eval "use Time::Local qw(timelocal timelocal_nocheck);";
+}
use Date::Format;
#use Date::Manip;
use Business::CreditCard;
}
}
- #false laziness with sub replace
- my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ $error = $self->queue_fuzzyfiles_update;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return "queueing job (transaction rolled back): $error";
- }
-
- if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) {
- $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "queueing job (transaction rolled back): $error";
- }
+ return "updating fuzzy search cache: $error";
}
- #eslaf
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ &&
grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) {
- # card info has changed, want to retry realtime_card invoice events
- #false laziness w/collect
- foreach my $cust_bill_event (
- grep {
- #$_->part_bill_event->plan eq 'realtime-card'
- $_->part_bill_event->eventcode =~
- /^\$cust_bill\->realtime_(card|ach|lec)\(\);$/
- && $_->status eq 'done'
- && $_->statustext
- }
- map { $_->cust_bill_event }
- grep { $_->cust_bill_event }
- $self->open_cust_bill
-
- ) {
- my $error = $cust_bill_event->retry;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error scheduling invoice events for retry: $error";
- }
+ # card/check/lec info has changed, want to retry realtime_ invoice events
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
- #eslaf
+ }
+ $error = $self->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
}
- #false laziness with sub insert
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item queue_fuzzyfiles_update
+
+Used by insert & replace to update the fuzzy search cache
+
+=cut
+
+sub queue_fuzzyfiles_update {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ my $error = $queue->insert($self->getfield('last'), $self->company);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) {
$queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ $error = $queue->insert($self->getfield('ship_last'), $self->ship_company);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
}
}
- #eslaf
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
# bad idea to disable, causes billing to fail because of no tax rates later
# unless ( $import ) {
- unless ( qsearchs('cust_main_county', {
+ unless ( qsearch('cust_main_county', {
'country' => $self->country,
'state' => '',
} ) ) {
return "Unknown state/county/country: ".
$self->state. "/". $self->county. "/". $self->country
- unless qsearchs('cust_main_county',{
+ unless qsearch('cust_main_county',{
'state' => $self->state,
'county' => $self->county,
'country' => $self->country,
}
}
- $self->payby =~ /^(CARD|CHEK|BILL|COMP|PREPAY)$/
+ $self->payby =~ /^(CARD|CHEK|LECB|BILL|COMP|PREPAY)$/
or return "Illegal payby: ". $self->payby;
$self->payby($1);
} elsif ( $self->payby eq 'CHEK' ) {
my $payinfo = $self->payinfo;
- $payinfo =~ s/[\D\@]//g;
+ $payinfo =~ s/[^\d\@]//g;
$payinfo =~ /^(\d+)\@(\d{9})$/ or return 'invalid echeck account@aba';
$payinfo = "$1\@$2";
$self->payinfo($payinfo);
": $setup_prog";
};
$setup_prog = $1;
+ $setup_prog = '0' if $setup_prog =~ /^\s*$/;
#my $cpt = new Safe;
##$cpt->permit(); #what is necessary?
my $sdate;
if ( $part_pkg->getfield('freq') > 0 &&
! $cust_pkg->getfield('susp') &&
- ( $cust_pkg->getfield('bill') || 0 ) < $time
+ ( $cust_pkg->getfield('bill') || 0 ) <= $time
) {
my $recur_prog = $part_pkg->getfield('recur');
$recur_prog =~ /^(.*)$/ or do {
": $recur_prog";
};
$recur_prog = $1;
+ $recur_prog = '0' if $recur_prog =~ /^\s*$/;
# shared with $recur_prog
$sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
# only for figuring next bill date, nothing else, so, reset $sdate again
# here
$sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
+ $cust_pkg->last_bill($sdate)
+ if $cust_pkg->dbdef_table->column('last_bill');
$mon += $part_pkg->freq;
until ( $mon < 12 ) { $mon -= 12; $year++; }
$cust_pkg->setfield('bill',
- timelocal($sec,$min,$hour,$mday,$mon,$year));
+ timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year));
$cust_pkg_mod_flag = 1;
}
|| $self->payby eq 'COMP'
|| $taxable_charged == 0 ) {
- my $cust_main_county =
- qsearchs('cust_main_county',{
+ my $cust_main_county = qsearchs('cust_main_county',{
'state' => $self->state,
'county' => $self->county,
'country' => $self->country,
'taxclass' => $part_pkg->taxclass,
- } )
- or qsearchs('cust_main_county',{
+ } );
+ $cust_main_county ||= qsearchs('cust_main_county',{
'state' => $self->state,
'county' => $self->county,
'country' => $self->country,
'taxclass' => '',
- } )
- or do {
- $dbh->rollback if $oldAutoCommit;
- return
- "fatal: can't find tax rate for state/county/country/taxclass ".
- join('/', ( map $self->$_(), qw(state county country) ),
- $part_pkg->taxclass ). "\n";
- };
+ } );
+ unless ( $cust_main_county ) {
+ $dbh->rollback if $oldAutoCommit;
+ return
+ "fatal: can't find tax rate for state/county/country/taxclass ".
+ join('/', ( map $self->$_(), qw(state county country) ),
+ $part_pkg->taxclass ). "\n";
+ }
if ( $cust_main_county->exempt_amount ) {
my ($mon,$year) = (localtime($sdate) )[4,5];
late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse>
for conversion functions.
-retry_card - Retry cards even when not scheduled by invoice events.
+retry - Retry card/echeck/LEC transactions even when not scheduled by invoice
+events.
+
+retry_card - Deprecated alias for 'retry'
batch_card - This option is deprecated. See the invoice events web interface
to control whether cards are batched or run against a realtime gateway.
return '';
}
- if ( exists($options{'retry_card'}) && $options{'retry_card'} ) {
- #false laziness w/replace
- foreach my $cust_bill_event (
- grep {
- #$_->part_bill_event->plan eq 'realtime-card'
- $_->part_bill_event->eventcode eq '$cust_bill->realtime_card();'
- && $_->status eq 'done'
- && $_->statustext
- }
- map { $_->cust_bill_event }
- grep { $_->cust_bill_event }
- $self->open_cust_bill
- ) {
- my $error = $cust_bill_event->retry;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error scheduling invoice events for retry: $error";
- }
+ if ( exists($options{'retry_card'}) ) {
+ carp 'retry_card option passed to collect is deprecated; use retry';
+ $options{'retry'} ||= $options{'retry_card'};
+ }
+ if ( exists($options{'retry'}) && $options{'retry'} ) {
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
- #eslaf
}
foreach my $cust_bill ( $self->cust_bill ) {
my $cust_bill_event = new FS::cust_bill_event {
'invnum' => $cust_bill->invnum,
'eventpart' => $part_bill_event->eventpart,
- '_date' => $invoice_time,
+ #'_date' => $invoice_time,
+ '_date' => time,
'status' => $status,
'statustext' => $statustext,
};
}
+=item retry_realtime
+
+Schedules realtime credit card / electronic check / LEC billing events for
+for retry. Useful if card information has changed or manual retry is desired.
+The 'collect' method must be called to actually retry the transaction.
+
+Implementation details: For each of this customer's open invoices, changes
+the status of the first "done" (with statustext error) realtime processing
+event to "failed".
+
+=cut
+
+sub retry_realtime {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_bill (
+ grep { $_->cust_bill_event }
+ $self->open_cust_bill
+ ) {
+ my @cust_bill_event =
+ sort { $a->part_bill_event->seconds <=> $b->part_bill_event->seconds }
+ grep {
+ #$_->part_bill_event->plan eq 'realtime-card'
+ $_->part_bill_event->eventcode =~
+ /\$cust_bill\->realtime_(card|ach|lec)/
+ && $_->status eq 'done'
+ && $_->statustext
+ }
+ $cust_bill->cust_bill_event;
+ next unless @cust_bill_event;
+ my $error = $cust_bill_event[0]->retry;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error scheduling invoice event for retry: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
=item total_owed
Returns the total owed for this customer on all invoices
1;
-