- my $param = shift;
- my $fh = $param->{'filehandle'};
- my $format = $param->{'format'};
- my $paybatch = $param->{'paybatch'};
-
- my @fields;
- my $condition;
- my $hook;
-
- if ( $format eq 'csv-td_canada_trust-merchant_pc_batch' ) {
-
- @fields = (
- 'paybatchnum', # Reference#: Invoice number of the transaction
- 'paid', # Amount: Amount of the transaction. Dollars and cents
- # with no decimal entered.
- '', # Card Type: 0 - MCrd, 1 - Visa, 2 - AMEX, 3 - Discover,
- # 4 - Insignia, 5 - Diners/EnRoute, 6 - JCB
- '_date', # Transaction Date: Date the Transaction was processed
- 'time', # Transaction Time: Time the transaction was processed
- 'payinfo', # Card Number: Card number for the transaction
- '', # Expiry Date: Expiry date of the card
- '', # Auth#: Authorization number entered for force post
- # transaction
- 'type', # Transaction Type: 0 - purchase, 40 - refund,
- # 20 - force post
- 'result', # Processing Result: 3 - Approval,
- # 4 - Declined/Amount over limit,
- # 5 - Invalid/Expired/stolen card,
- # 6 - Comm Error
- '', # Terminal ID: Terminal ID used to process the transaction
- );
-
- $condition = sub {
- my $hash = shift;
- $hash->{'result'} == 3 && $hash->{'type'} == 0;
- };
-
- $hook = sub {
- my $hash = shift;
- $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
- $hash->{'_date'} = timelocal( substr($hash->{'time'}, 4, 2),
- substr($hash->{'time'}, 2, 2),
- substr($hash->{'time'}, 0, 2),
- substr($hash->{'_date'}, 6, 2),
- substr($hash->{'_date'}, 4, 2)-1,
- substr($hash->{'_date'}, 0, 4)-1900, );
- };
+#Marks the corresponding event (see L<FS::cust_bill_event>) for this batched
+#credit card payment as retriable. Useful if the corresponding financial
+#institution account was declined for temporary reasons and/or a manual
+#retry is desired.
+#
+#Implementation details: For the named customer's invoice, changes the
+#statustext of the 'done' (without statustext) event to 'retriable.'
+#
+#=cut
+
+sub retriable {
+
+ confess "deprecated method cust_pay_batch->retriable called; try removing ".
+ "the once condition and adding an every condition?";
+
+}
+
+=item approve OPTIONS
+
+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.
+
+OPTIONS may contain "gatewaynum", "processor", "auth", and "order_number".
+
+=cut
+
+sub approve {
+ # to break up the Big Wall of Code that is import_results
+ my $new = shift;
+ my %opt = @_;
+ my $paybatchnum = $new->paybatchnum;
+ my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
+ or return "cannot approve, paybatchnum $paybatchnum not found";
+ # leave these restrictions in place until TD EFT is converted over
+ # to B::BP
+ return "cannot approve paybatchnum $paybatchnum, already resolved ('".$old->status."')"
+ if $old->status;
+ $new->status('Approved');
+ my $error = $new->replace($old);
+ if ( $error ) {
+ return "error approving paybatchnum $paybatchnum: $error\n";
+ }
+
+ return if $new->paycode eq "C";
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $new->custnum,
+ 'payby' => $new->payby,
+ 'payinfo' => $new->payinfo || $old->payinfo,
+ 'paymask' => $new->mask_payinfo,
+ 'paid' => $new->paid,
+ '_date' => $new->_date,
+ 'usernum' => $new->usernum,
+ 'batchnum' => $new->batchnum,
+ 'invnum' => $old->invnum,
+ 'gatewaynum' => $opt{'gatewaynum'},
+ 'processor' => $opt{'processor'},
+ 'auth' => $opt{'auth'},
+ 'order_number' => $opt{'order_number'}
+ } );
+
+ $error = $cust_pay->insert;
+ if ( $error ) {
+ return "error inserting payment for paybatchnum $paybatchnum: $error\n";
+ }
+ $cust_pay->cust_main->apply_payments;
+ return;
+}
+
+=item decline [ REASON [ STATUS ] ]
+
+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.
+
+REASON is a string description of the decline reason, defaulting to
+'Returned payment', and will go into the "error_message" field.
+
+STATUS is a normalized failure status defined by L<Business::BatchPayment>,
+and will go into the "failure_status" field.
+
+=cut
+
+sub decline {
+ my $new = shift;
+ my $reason = shift || 'Returned payment';
+ my $failure_status = shift || '';
+ #my $conf = new FS::Conf;
+
+ my $paybatchnum = $new->paybatchnum;
+ my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
+ or return "cannot decline, paybatchnum $paybatchnum not found";
+ if ( $old->status ) {
+ # Handle the case where payments are rejected after the batch has been
+ # approved. FS::pay_batch::import_results won't allow results to be
+ # imported to a closed batch unless batch-manual_approval is enabled,
+ # so we don't check it here.
+# if ( $conf->exists('batch-manual_approval') and
+ if ( lc($old->status) eq 'approved' ) {
+ # Void the payment
+ my $cust_pay = qsearchs('cust_pay', {
+ custnum => $new->custnum,
+ batchnum => $new->batchnum
+ });
+ # these should all be migrated over, but if it's not found, look for
+ # batchnum in the 'paybatch' field also
+ $cust_pay ||= qsearchs('cust_pay', {
+ custnum => $new->custnum,
+ paybatch => $new->batchnum
+ });
+ if ( !$cust_pay ) {
+ # should never happen...
+ return "failed to revoke paybatchnum $paybatchnum, payment not found";
+ }
+ $cust_pay->void($reason);
+ }
+ else {
+ # normal case: refuse to do anything
+ return "cannot decline paybatchnum $paybatchnum, already resolved ('".$old->status."')";
+ }
+ } # !$old->status
+ $new->status('Declined');
+ $new->error_message($reason);
+ $new->failure_status($failure_status);
+ my $error = $new->replace($old);
+ if ( $error ) {
+ return "error declining 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;
+}
+
+=item request_item [ OPTIONS ]
+
+Returns a L<Business::BatchPayment::Item> object for this batch payment
+entry. This can be submitted to a processor.
+
+OPTIONS can be a list of key/values to append to the attributes. The most
+useful case of this is "process_date" to set a processing date based on the
+date the batch is being submitted.
+
+=cut
+
+sub request_item {
+ local $@;
+ my $self = shift;