record and show batch payment status info, #21117
[freeside.git] / FS / FS / cust_pay_batch.pm
index f5e6a4b..4138436 100644 (file)
@@ -9,7 +9,7 @@ use FS::payinfo_Mixin;
 use FS::cust_main;
 use FS::cust_bill;
 
-@ISA = qw( FS::payinfo_Mixin FS::Record );
+@ISA = qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
 
 # 1 is mostly method/subroutine entry and options
 # 2 traces progress of some operations
@@ -80,7 +80,9 @@ following fields are currently supported:
 
 =item country 
 
-=item status
+=item status - 'Approved' or 'Declined'
+
+=item error_message - the error returned by the gateway if any
 
 =back
 
@@ -204,6 +206,35 @@ sub cust_main {
   qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
 }
 
+=item expmmyy
+
+Returns the credit card expiration date in MMYY format.  If this is a 
+CHEK payment, returns an empty string.
+
+=cut
+
+sub expmmyy {
+  my $self = shift;
+  if ( $self->payby eq 'CARD' ) {
+    $self->get('exp') =~ /^(\d{4})-(\d{2})-(\d{2})$/;
+    return sprintf('%02u%02u', $2, ($1 % 100));
+  }
+  else {
+    return '';
+  }
+}
+
+=item pay_batch
+
+Returns the payment batch this payment belongs to (L<FS::pay_batch).
+
+=cut
+
+sub pay_batch {
+  my $self = shift;
+  FS::pay_batch->by_key($self->batchnum);
+}
+
 #you know what, screw this in the new world of events.  we should be able to
 #get the event defs to retry (remove once.pm condition, add every.pm) without
 #mucking about with statuses of previous cust_event records.  right?
@@ -260,22 +291,26 @@ sub retriable {
   '';
 }
 
-=item approve PAYBATCH
+=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 $paybatch = shift;
+  my %opt = @_;
   my $paybatchnum = $new->paybatchnum;
   my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
     or return "paybatchnum $paybatchnum not found";
+  # leave these restrictions in place until TD EFT is converted over
+  # to B::BP
   return "paybatchnum $paybatchnum already resolved ('".$old->status."')" 
     if $old->status;
   $new->status('Approved');
@@ -286,12 +321,17 @@ sub approve {
   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,
       'usernum'   => $new->usernum,
+      'batchnum'  => $new->batchnum,
+      '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";
@@ -343,6 +383,7 @@ sub decline {
     }
   } # !$old->status
   $new->status('Declined');
+  $new->error_message($reason);
   my $error = $new->replace($old);
   if ( $error ) {
     return "error updating status of paybatchnum $paybatchnum: $error\n";
@@ -364,6 +405,62 @@ sub decline {
   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;
+
+  eval "use Business::BatchPayment;";
+  die "couldn't load Business::BatchPayment: $@" if $@;
+
+  my $cust_main = $self->cust_main;
+  my $location = $cust_main->bill_location;
+  my $pay_batch = $self->pay_batch;
+
+  my %payment;
+  $payment{payment_type} = FS::payby->payby2bop( $pay_batch->payby );
+  if ( $payment{payment_type} eq 'CC' ) {
+    $payment{card_number} = $self->payinfo,
+    $payment{expiration}  = $self->expmmyy,
+  } elsif ( $payment{payment_type} eq 'ECHECK' ) {
+    $self->payinfo =~ /(\d+)@(\d+)/; # or else what?
+    $payment{account_number} = $1;
+    $payment{routing_code} = $2;
+    $payment{account_type} = $cust_main->paytype;
+    # XXX what if this isn't their regular payment method?
+  } else {
+    die "unsupported BatchPayment method: ".$pay_batch->payby;
+  }
+
+  Business::BatchPayment->create(Item =>
+    # required
+    action      => 'payment',
+    tid         => $self->paybatchnum,
+    amount      => $self->amount,
+
+    # customer info
+    customer_id => $self->custnum,
+    first_name  => $cust_main->first,
+    last_name   => $cust_main->last,
+    company     => $cust_main->company,
+    address     => $location->address1,
+    ( map { $_ => $location->$_ } qw(address2 city state country zip) ),
+    
+    invoice_number  => $self->invnum,
+    %payment,
+  );
+}
+
 =back
 
 =head1 BUGS