summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Conf.pm14
-rw-r--r--FS/FS/cust_pay_batch.pm76
-rw-r--r--FS/FS/pay_batch.pm108
-rw-r--r--FS/FS/pay_batch/td_eft1464.pm13
4 files changed, 143 insertions, 68 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 5fb5a272f..132ee47d7 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2995,13 +2995,13 @@ and customer address. Include units.',
'type' => 'textarea',
},
-# {
-# 'key' => 'batch-manual_approval',
-# 'section' => 'billing',
-# 'description' => 'Allow manual batch closure, which will approve all payments that do not yet have a status. This is very dangerous.',
-# 'type' => 'checkbox',
-# },
-#
+ {
+ 'key' => 'batch-manual_approval',
+ 'section' => 'billing',
+ 'description' => 'Allow manual batch closure, which will approve all payments that do not yet have a status. This is not advised, but is needed for payment processors that provide a report of rejected rather than approved payments.',
+ 'type' => 'checkbox',
+ },
+
{
'key' => 'payment_history-years',
'section' => 'UI',
diff --git a/FS/FS/cust_pay_batch.pm b/FS/FS/cust_pay_batch.pm
index 9ef1e1cc1..9fa14598a 100644
--- a/FS/FS/cust_pay_batch.pm
+++ b/FS/FS/cust_pay_batch.pm
@@ -260,6 +260,82 @@ sub retriable {
'';
}
+=item approve PAYBATCH
+
+Approve this payment. This will replace the existing record with the
+same paybatchnum, set its status to 'Approved', and generate a payment
+record (L<FS::cust_pay>). This should only be called from the batch
+import process.
+
+=cut
+
+sub approve {
+ # to break up the Big Wall of Code that is import_results
+ my $new = shift;
+ my $paybatch = shift;
+ my $paybatchnum = $new->paybatchnum;
+ my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
+ or return "paybatchnum $paybatchnum not found";
+ return "paybatchnum $paybatchnum already resolved ('".$old->status."')"
+ if $old->status;
+ $new->status('Approved');
+ my $error = $new->replace($old);
+ if ( $error ) {
+ return "error updating status of paybatchnum $paybatchnum: $error\n";
+ }
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $new->custnum,
+ 'payby' => $new->payby,
+ 'paybatch' => $paybatch,
+ 'payinfo' => $new->payinfo || $old->payinfo,
+ 'paid' => $new->paid,
+ '_date' => $new->_date,
+ } );
+ $error = $cust_pay->insert;
+ if ( $error ) {
+ return "error inserting payment for paybatchnum $paybatchnum: $error\n";
+ }
+ $cust_pay->cust_main->apply_payments;
+ return;
+}
+
+=item decline
+
+Decline this payment. This will replace the existing record with the
+same paybatchnum, set its status to 'Declined', and run collection events
+as appropriate. This should only be called from the batch import process.
+
+
+=cut
+sub decline {
+ my $new = shift;
+ my $paybatchnum = $new->paybatchnum;
+ my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
+ or return "paybatchnum $paybatchnum not found";
+ return "paybatchnum $paybatchnum already resolved ('".$old->status."')"
+ if $old->status;
+ $new->status('Declined');
+ my $error = $new->replace($old);
+ if ( $error ) {
+ return "error updating status of paybatchnum $paybatchnum: $error\n";
+ }
+ my $due_cust_event = $new->cust_main->due_cust_event(
+ 'eventtable' => 'cust_pay_batch',
+ 'objects' => [ $new ],
+ );
+ if ( !ref($due_cust_event) ) {
+ return $due_cust_event;
+ }
+ # XXX breaks transaction integrity
+ foreach my $cust_event (@$due_cust_event) {
+ next unless $cust_event->test_conditions;
+ if ( my $error = $cust_event->do_event() ) {
+ return $error;
+ }
+ }
+ return;
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/pay_batch.pm b/FS/FS/pay_batch.pm
index d142411e5..5cd40cda0 100644
--- a/FS/FS/pay_batch.pm
+++ b/FS/FS/pay_batch.pm
@@ -356,20 +356,21 @@ sub import_results {
&{$hook}(\%hash, $cust_pay_batch->hashref);
+ my $error = '';
if ( &{$approved_condition}(\%hash) ) {
- $new_cust_pay_batch->status('Approved');
+ $error = $new_cust_pay_batch->approve($hash{'paybatch'} || $self->batchnum);
+ $total += $hash{'paid'};
} elsif ( &{$declined_condition}(\%hash) ) {
- $new_cust_pay_batch->status('Declined');
+ $error = $new_cust_pay_batch->decline;
}
- my $error = $new_cust_pay_batch->replace($cust_pay_batch);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return "error updating status of paybatchnum $hash{'paybatchnum'}: $error\n";
+ return $error;
}
# purge CVV when the batch is processed
@@ -379,64 +380,13 @@ sub import_results {
$conf->config('cvv-save') ) {
$new_cust_pay_batch->cust_main->remove_cvv;
}
- }
-
- if ( $new_cust_pay_batch->status =~ /Approved/i ) {
-
- my $cust_pay = new FS::cust_pay ( {
- 'custnum' => $custnum,
- 'payby' => $payby,
- 'paybatch' => $hash{'paybatch'} || $self->batchnum,
- 'payinfo' => ( $hash{'payinfo'} || $cust_pay_batch->payinfo ),
- map { $_ => $hash{$_} } (qw( paid _date )),
- } );
- $error = $cust_pay->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error adding payment paybatchnum $hash{'paybatchnum'}: $error\n";
- }
- $total += $hash{'paid'};
-
- $cust_pay->cust_main->apply_payments;
-
- } elsif ( $new_cust_pay_batch->status =~ /Declined/i ) {
-
- #false laziness w/cust_main::collect
-
- my $due_cust_event = $new_cust_pay_batch->cust_main->due_cust_event(
- #'check_freq' => '1d', #?
- 'eventtable' => 'cust_pay_batch',
- 'objects' => [ $new_cust_pay_batch ],
- );
- unless( ref($due_cust_event) ) {
- $dbh->rollback if $oldAutoCommit;
- return $due_cust_event;
- }
-
- foreach my $cust_event ( @$due_cust_event ) {
-
- #XXX lock event
-
- #re-eval event conditions (a previous event could have changed things)
- next unless $cust_event->test_conditions;
-
- if ( my $error = $cust_event->do_event() ) {
- # gah, even with transactions.
- #$dbh->commit if $oldAutoCommit; #well.
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
-
- }
}
- }
+ } # foreach (@all_values)
if ( defined($close_condition) ) {
# Allow the module to decide whether to close the batch.
- # This is used for TD EFT, which requires two imports before
- # closing.
# $close_condition can also die() to abort the whole import.
my $close = eval { $close_condition->($self) };
if ( $@ ) {
@@ -577,6 +527,52 @@ sub export_batch {
return $batch;
}
+sub manual_approve {
+ my $self = shift;
+ my $date = time;
+ my %opt = @_;
+ my $paybatch = $opt{'paybatch'} || $self->batchnum;
+ my $conf = FS::Conf->new;
+ return 'manual batch approval disabled'
+ if ( ! $conf->exists('batch-manual_approval') );
+ return 'batch already resolved' if $self->status eq 'R';
+ return 'batch not yet submitted' if $self->status eq 'O';
+
+ 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 $payments = 0;
+ foreach my $cust_pay_batch (
+ qsearch('cust_pay_batch', { batchnum => $self->batchnum,
+ status => '' })
+ ) {
+ my $new_cust_pay_batch = new FS::cust_pay_batch {
+ $cust_pay_batch->hash,
+ 'paid' => $cust_pay_batch->amount,
+ '_date' => $date,
+ };
+ my $error = $new_cust_pay_batch->approve($paybatch);
+ if ( $error ) {
+ $dbh->rollback;
+ return 'paybatchnum '.$cust_pay_batch->paybatchnum.": $error";
+ }
+ $payments++;
+ }
+ return 'no unresolved payments in batch' if $payments == 0;
+ $self->set_status('R');
+
+ $dbh->commit;
+ return;
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/pay_batch/td_eft1464.pm b/FS/FS/pay_batch/td_eft1464.pm
index c3294d161..7f58ab5f7 100644
--- a/FS/FS/pay_batch/td_eft1464.pm
+++ b/FS/FS/pay_batch/td_eft1464.pm
@@ -76,13 +76,13 @@ $name = 'td_eft1464';
my @cust_pay_batch = @{(shift)};
my $time = $pay_batch->download || time;
my $now = sprintf("%03u%03u",
- (localtime(time))[5],#year since 1900
+ (localtime(time))[5] % 100,#year since 1900
(localtime(time))[7]+1);#day of year
# Request settlement the next day
my $duedate = time+86400;
$opt{'due'} = sprintf("%03u%03u",
- (localtime($duedate))[5],
+ (localtime($duedate))[5] % 100,
(localtime($duedate))[7]+1);
$opt{'fcn'} =
@@ -94,7 +94,8 @@ $name = 'td_eft1464';
$opt{'fcn'},
$now,
$opt{'datacenter'},
- ' ' x 1429 #filler
+ ' ' x 1429, #filler
+ "\r"
);
},
row => sub {
@@ -116,7 +117,7 @@ $name = 'td_eft1464';
sprintf('%09u', $aba),
sprintf('%-12s', $account),
' ' x 22,
- ' ' x 3,
+ '0' x 3,
$opt{'shortname'},
sprintf('%-30s',
join(' ',
@@ -127,11 +128,12 @@ $name = 'td_eft1464';
sprintf('%-19s', $cust_pay_batch->paybatchnum), # originator reference num
$opt{'retbranch'},
$opt{'retacct'},
+ ' ' x 15,
' ' x 22,
' ' x 2,
'0' x 11,
);
- return $control . $payment . (' ' x 720);
+ return sprintf('%-1464s',$control . $payment) . "\r";
},
footer => sub {
my ($pay_batch, $batchcount, $batchtotal) = @_;
@@ -145,6 +147,7 @@ $name = 'td_eft1464';
'0' x 14, # total of credit txns
'0' x 8, # total of credit txns
' ' x 1396,
+ "\r"
)
},
);