RT# 82988 - Fixed so only formats that can handle electronic refunds can download...
authorChristopher Burger <burgerc@freeside.biz>
Sun, 10 Mar 2019 23:12:20 +0000 (19:12 -0400)
committerChristopher Burger <burgerc@freeside.biz>
Sun, 10 Mar 2019 23:12:20 +0000 (19:12 -0400)
13 files changed:
FS/FS/Cron/pay_batch.pm
FS/FS/Schema.pm
FS/FS/cust_main/Billing_Batch.pm
FS/FS/pay_batch.pm
FS/FS/pay_batch/RBC.pm
FS/bin/freeside-eftca-upload
FS/bin/freeside-paymentech-upload
FS/bin/freeside-rbc-upload
httemplate/edit/process/cust_refund.cgi
httemplate/search/elements/cust_pay_batch_top.html
httemplate/search/pay_batch.cgi
httemplate/view/cust_main/menu.html
httemplate/view/cust_main/payment_history/payment.html

index 1e110f2..18532ac 100644 (file)
@@ -67,7 +67,7 @@ sub pay_batch_submit {
     my ($gateway, $payby, $agentnum) = @$config;
     if ( $gateway->batch_processor->can('default_transport') ) {
 
-      my $search = { status => 'O', payby => $payby };
+      my $search = { status => 'O', payby => $payby, type => 'DEBIT' };
       $search->{agentnum} = $agentnum if $agentnum;
 
       foreach my $pay_batch ( qsearch('pay_batch', $search) ) {
index fc22d6c..8cc8416 100644 (file)
@@ -2711,6 +2711,7 @@ sub tables_hashref {
         'upload',         @date_type,     '', '', 
         'title',   'varchar', 'NULL',255, '', '',
         'processor_id',   'varchar', 'NULL',255, '', '',
+        'type',      'char',  '',  6, 'DEBIT', '', # DEBIT/CREDIT
       ],
       'primary_key'  => 'batchnum',
       'unique'       => [],
index 0e713e9..c1bb35f 100644 (file)
@@ -83,6 +83,10 @@ sub batch_card {
                               );
   }
 
+  my $paycode= $options{paycode} || '';
+  my $batch_type = "DEBIT";
+  $batch_type = "CREDIT" if $paycode eq 'C';
+
   my $oldAutoCommit = $FS::UID::AutoCommit;
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
@@ -95,6 +99,7 @@ sub batch_card {
   my %pay_batch = (
     'status' => 'O',
     'payby'  => FS::payby->payby2payment($payby),
+    'type'   => $batch_type,
   );
   $pay_batch{agentnum} = $self->agentnum if $conf->exists('batch-spoolagent');
 
index 4aeb331..e8ae6ec 100644 (file)
@@ -56,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.
 
@@ -1154,7 +1158,136 @@ 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";
+
+    my @batch_refunds = qsearch({
+      'table'   => 'cust_pay_batch',
+      'select'  => 'cust_pay_batch.*',
+      'extra_sql' => $extrasql,
+    });
+
+    warn "found ".scalar @batch_refunds." batch refunds.\n";
+    warn "Searching for their cust refunds...\n" if (scalar @batch_refunds > 0);
+    my ($delete_cust_refund_error, $delete_cust_pay_batch_error);
+
+    ## find the cust_pay_refund for all those
+    foreach (@batch_refunds) {
+      my $extra_batch_refund_sql = " WHERE custnum = '".$_->{Hash}->{custnum}."' AND refund = '".$_->{Hash}->{amount}."' ORDER BY _date DESC LIMIT 1";
+      my $cust_refund = qsearchs({
+        'table'  => 'cust_refund',
+        'extra_sql' => $extra_batch_refund_sql,
+      });
+
+      warn "found cust refund number ".$cust_refund->{Hash}->{refundnum}.", now to delete it.\n" if $cust_refund;
+
+      ## delete the cust_pay_refund
+      $delete_cust_refund_error = $cust_refund->delete if $cust_refund;
+      warn "could not delete cust refund $delete_cust_refund_error\n" if $delete_cust_refund_error;
+
+      ## delete the refund from the batch.
+      unless ($delete_cust_refund_error) {
+        $delete_cust_pay_batch_error = $_->unbatch_and_delete;
+        warn "could not delete cust refund $delete_cust_pay_batch_error\n" if $delete_cust_pay_batch_error;
+      }
+
+      if ($delete_cust_refund_error || $delete_cust_pay_batch_error) { die "Could no delete cust_pay_batch refund\n"; }
+      else { warn "cust refund ".$cust_refund->{Hash}->{refundnum}." deleted\n"; }
+    }
+
+    FS::upgrade_journal->set_done('removed_refunds_nodownload_format');
+  }
+
   # Set up configuration for gateways that have a Business::BatchPayment
   # module.
   
index 22521e0..7c165a3 100644 (file)
@@ -154,7 +154,8 @@ $name = 'RBC';
     my $pay_batch = shift;
     my $mode = $testmode ? 'TEST' : 'PROD';
     my $filenum = $testmode ? 'TEST' : sprintf("%04u", $pay_batch->batchnum);
-    '$$AAPASTD0152['.$mode.'[NL$$'."\n".
+    my $qualifier = $pay_batch->type eq 'CREDIT' ? 'D' : 'A';
+    '$$AAP'.$qualifier.'STD0152['.$mode.'[NL$$'."\n".
     '000001'.
     'A'.
     'HDR'.
@@ -219,13 +220,15 @@ $name = 'RBC';
   },
   footer => sub {
     my ($pay_batch, $batchcount, $batchtotal) = @_;
+
+    my $batch_info = '0' x 20 . sprintf("%06u", $batchcount) . sprintf("%014.0f", $batchtotal*100);
+    $batch_info = sprintf("%06u", $batchcount) . sprintf("%014.0f", $batchtotal*100) . '0' x 20 if ($pay_batch->type eq 'CREDIT');
+
     sprintf("%06u", $i + 1).
     'Z'.
     'TRL'.
     sprintf("%10s", $client_num).
-    '0' x 20 .
-    sprintf("%06u", $batchcount).
-    sprintf("%014.0f", $batchtotal*100).
+    $batch_info.
     '00' .
     '000000' . # total number of customer information records
     ' ' x 84
index 503c7a3..18656c9 100755 (executable)
@@ -32,8 +32,12 @@ my @batches;
 
 if($opt_a) {
   local $@;
+
+  my %criteria= ( 'status' => 'O', 'payby' => 'CHEK' );
+  $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('eft_canada');
+
   eval {
-    @batches = qsearch('pay_batch', { 'status' => 'O', 'payby' => 'CHEK' })
+    @batches = qsearch('pay_batch', \%criteria)
   };
   log_error_and_die ("Fatal database error: $@")
     if $@;
index d6ca0cd..8ec8a5d 100755 (executable)
@@ -41,6 +41,7 @@ my @batches;
 if($opt_a) {
   my %criteria = (status => 'O');
   $criteria{'payby'} = uc($opt_p) if $opt_p;
+  $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('paymentech');
   @batches = qsearch('pay_batch', \%criteria);
   log_and_die("No open batches found".($opt_p ? " of type '$opt_p'" : '').".\n")
     if !@batches;
index 3fff32a..8f67a6e 100755 (executable)
@@ -33,6 +33,7 @@ my @batches;
 if($opt_a) {
   my %criteria = (status => 'O');
   $criteria{'payby'} = uc($opt_p) if $opt_p;
+  $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('RBC');
   @batches = qsearch('pay_batch', \%criteria);
   die "No open batches found".($opt_p ? " of type '$opt_p'" : '').".\n" 
     if !@batches;
index 9175eb1..25f6e00 100755 (executable)
@@ -39,6 +39,8 @@ $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum";
 my ($reasonnum, $error) = $m->comp('/misc/process/elements/reason');
 $cgi->param('reasonnum', $reasonnum) unless $error;
 
+$error = "No batch download format configured that allows electronic refunds" unless (FS::pay_batch->can_handle_electronic_refunds && !$error);
+
 if ( $error ) {
   # do nothing
 } elsif ( $payby =~ /^(CARD|CHEK)$/ ) { 
index 626d7c3..eee81dd 100644 (file)
@@ -135,23 +135,7 @@ my $batchnum = $pay_batch->batchnum;
 
 my $fixed = $conf->config("batch-fixed_format-$payby");
 
-tie my %download_formats, 'Tie::IxHash', (
-  '' => '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
-);
+tie my %download_formats, 'Tie::IxHash', FS::pay_batch::batch_download_formats;
 
 tie my %upload_formats, 'Tie::IxHash', (
   %download_formats,
@@ -160,7 +144,13 @@ tie my %upload_formats, 'Tie::IxHash', (
   'td_eftret' => 'TD EFT Returned Items',
 );
 delete $upload_formats{'td_eft1464'};
-$upload_formats{'PAP'} = '264 byte results for TD Canada Trust PAP Batch',
+$upload_formats{'PAP'} = '264 byte results for TD Canada Trust PAP Batch';
+
+if ($pay_batch->type eq "CREDIT") {
+  foreach my $key (keys %download_formats) {
+    delete $download_formats{$key} unless FS::pay_batch->can_handle_electronic_refunds($key);
+  }
+}
 
 my %statustext = ( 'O' => 'open', 'I' => 'in transit', 'R' => 'resolved' );
 
index 40df5aa..8fe4351 100755 (executable)
                                     ],
                 'align'         => 'rcllrrrrc',
                 'fields'        => [ 'batchnum',
-                                     sub { 
-                                       FS::payby->shortname(shift->payby);
-                                     },
+                                     sub {
+                my $self = shift;
+                my $type = $self->type eq 'CREDIT' ? 'CREDIT' : '';
+                $type ." " . FS::payby->shortname($self->payby);
+                                                 },
                                       sub {
                                        my $self = shift;
                                        my $_date = $self->download;
index cc9f1fc..63d5c9a 100644 (file)
@@ -467,6 +467,9 @@ my @menu = (
            actionlabel => 'Enter electronic check refund',
            width       => 440,
            acl         => ['Post refund' ],
+           condition   => sub {
+             FS::pay_batch->can_handle_electronic_refunds
+           },
         },
 
       ],
index 6402383..eeddc47 100644 (file)
@@ -178,6 +178,7 @@ if (    $cust_pay->closed !~ /^Y/i
             qq! TITLE="! . $refundtitle
             . '">' . emt('refund') . '</A>)';
 }
+$refund = '' if ($cust_pay->batchnum && !FS::pay_batch->can_handle_electronic_refunds);
 
 my $void = '';
 my $voidmsg = $cust_pay->payby =~ /^(CARD|CHEK)$/