RT# 83044 - fixed so open empty batches not created on upgrade
[freeside.git] / FS / FS / pay_batch.pm
index 35c79f5..c57c554 100644 (file)
@@ -9,11 +9,12 @@ use List::Util qw(sum);
 use Time::Local;
 use Text::CSV_XS;
 use Date::Parse qw(str2time);
-use Business::CreditCard qw(cardtype);
+use Business::CreditCard qw( 0.35 cardtype );
 use FS::Record qw( dbh qsearch qsearchs );
 use FS::Conf;
 use FS::cust_pay;
 use FS::Log;
+use Try::Tiny;
 
 =head1 NAME
 
@@ -55,6 +56,10 @@ from FS::Record.  The following fields are currently supported:
 
 =item title - unique batch identifier
 
+=item processor_id -
+
+=item type - batch type payents (DEBIT), or refunds (CREDIT)
+
 For incoming batches, the combination of 'title', 'payby', and 'agentnum'
 must be unique.
 
@@ -614,7 +619,8 @@ sub import_from_gateway {
       my $error;
 
       my $paybatch = $gateway->gatewaynum .  '-' .  $gateway->gateway_module .
-        ':' . $item->authorization .  ':' . $item->order_number;
+        ':' . ($item->authorization || '') .
+        ':' . ($item->order_number || '');
 
       if ( $batch->incoming ) {
         # This is a one-way batch.
@@ -1085,16 +1091,21 @@ sub export_to_gateway {
     return '';
   }
 
-  my $batch = Business::BatchPayment->create(Batch =>
-    batch_id  => $self->batchnum,
-    items     => \@items
-  );
-  $processor->submit($batch);
+  try {
+    my $batch = Business::BatchPayment->create(Batch =>
+      batch_id  => $self->batchnum,
+      items     => \@items
+    );
+    $processor->submit($batch);
 
-  if ($batch->processor_id) {
-    $self->set('processor_id',$batch->processor_id);
-    $self->replace;
-  }
+    if ($batch->processor_id) {
+      $self->set('processor_id',$batch->processor_id);
+      $self->replace;
+    }
+  } catch {
+    $dbh->rollback if $oldAutoCommit;
+    die $_;
+  };
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
@@ -1147,7 +1158,152 @@ sub manual_approve {
   return;
 }
 
+=item batch_download_formats
+
+returns a hash of batch download formats.
+
+my %download_formats = FS::pay_batch::batch_download_formats;
+
+=cut
+
+sub batch_download_formats {
+
+  my @formats = (
+    ''              =>
+        'Default batch mode',
+    'NACHA'         =>
+        '94 byte NACHA',
+    'csv-td_canada_trust-merchant_pc_batch' =>
+        'CSV file for TD Canada Trust Merchant PC Batch',
+    'csv-chase_canada-E-xactBatch' =>
+        'CSV file for Chase Canada E-xactBatch',
+    'PAP'           =>
+        '80 byte file for TD Canada Trust PAP Batch',
+    'BoM'           =>
+        'Bank of Montreal ECA batch',
+    'ach-spiritone' =>
+        'Spiritone ACH batch',
+    'paymentech'    =>
+        'XML file for Chase Paymentech',
+    'RBC'           =>
+        'Royal Bank of Canada PDS batch',
+    'td_eft1464'    =>
+        '1464 byte file for TD Commercial Banking EFT',
+    'eft_canada'    =>
+        'EFT Canada CSV batch',
+    'CIBC'          =>
+        '80 byte file for Canadian Imperial Bank of Commerce',
+    # insert new batch formats here
+  );
+
+}
+
+=item batch_download_formats
+
+returns a hash of batch download formats.
+
+my %download_formats = FS::pay_batch::batch_download_formats;
+
+=cut
+
+sub can_handle_electronic_refunds {
+
+  my $self = shift;
+  my $format = shift;
+  my $conf = new FS::Conf;
+
+  tie my %download_formats, 'Tie::IxHash', batch_download_formats;
+
+  my %paybatch_mods = (
+    'NACHA'                                 => 'nacha',
+    'csv-td_canada_trust-merchant_pc_batch' => 'td_canada_trust',
+    'csv-chase_canada-E-xactBatch'          => 'chase-canada',
+    'PAP'                                   => 'PAP',
+    'BoM'                                   => 'BoM',
+    'ach-spiritone'                         => 'ach_spiritone',
+    'paymentech'                            => 'paymentech',
+    'RBC'                                   => 'RBC',
+    'td_eft1464'                            => 'td_eft1464',
+    'eft_canada'                            => 'eft_canada',
+    'CIBC'                                  => 'CIBC',
+  );
+
+  %download_formats = ( $format => $download_formats{$format}, ) if $format;
+
+  foreach my $key (keys %download_formats) {
+    my $mod = "FS::pay_batch::".$paybatch_mods{$key};
+    if ($mod->can('can_handle_credits')) {
+      return '1' if $conf->exists('batchconfig-'.$key);
+    }
+  }
+
+  return;
+
+}
+
+use FS::upgrade_journal;
 sub _upgrade_data {
+
+  # check if there are any pending batch refunds and no download format configured
+  # that allows electronic refunds.
+  unless ( FS::upgrade_journal->is_done('removed_refunds_nodownload_format') ) {
+
+    ## get a list of all refunds in batches.
+    my $extrasql = " LEFT JOIN pay_batch USING ( batchnum ) WHERE cust_pay_batch.paycode = 'C' AND pay_batch.download IS NULL AND pay_batch.type = 'DEBIT' ";
+
+    my @batch_refunds = qsearch({
+      'table'   => 'cust_pay_batch',
+      'select'  => 'cust_pay_batch.*',
+      'extra_sql' => $extrasql,
+    });
+
+    my $replace_error;
+
+    if (@batch_refunds) {
+      warn "found ".scalar @batch_refunds." batch refunds.\n";
+      warn "Searching for their cust refunds...\n" if (scalar @batch_refunds > 0);
+
+      my $oldAutoCommit = $FS::UID::AutoCommit;
+      local $FS::UID::AutoCommit = 0;
+      my $dbh = dbh;
+
+      ## move refund to credit batch.
+      foreach my $cust_pay_batch (@batch_refunds) {
+        my $payby = $cust_pay_batch->payby eq "CARD" ? "CARD" : "CHEK";
+
+        my %pay_batch = (
+          'status' => 'O',
+          'payby'  => $payby,
+          'type'   => 'CREDIT',
+        );
+
+        my $pay_batch = qsearchs( 'pay_batch', \%pay_batch );
+
+        unless ( $pay_batch ) {
+          $pay_batch = new FS::pay_batch \%pay_batch;
+          my $error = $pay_batch->insert;
+          if ( $error ) {
+            $dbh->rollback if $oldAutoCommit;
+            warn "error creating a $payby credit batch: $error\n";
+          }
+        }
+
+        $cust_pay_batch->batchnum($pay_batch->batchnum);
+        $replace_error = $cust_pay_batch->replace();
+        if ( $replace_error ) {
+          $dbh->rollback if $oldAutoCommit;
+          warn "Unable to move credit to a credit batch: $replace_error";
+        }
+        else {
+          warn "Moved cust pay credit ".$cust_pay_batch->paybatchnum." to ".$cust_pay_batch->payby." credit batch ".$cust_pay_batch->batchnum."\n";
+        }
+      }
+    } #end @batch_refunds
+    else { warn "No batch refunds found\n"; }
+
+    FS::upgrade_journal->set_done('removed_refunds_nodownload_format') unless $replace_error;
+  }
+
   # Set up configuration for gateways that have a Business::BatchPayment
   # module.