batch payment revocation, RT#10545
authormark <mark>
Tue, 15 Feb 2011 23:52:44 +0000 (23:52 +0000)
committermark <mark>
Tue, 15 Feb 2011 23:52:44 +0000 (23:52 +0000)
FS/FS/cust_pay_batch.pm
FS/FS/pay_batch.pm
httemplate/misc/process/pay_batch-approve.cgi
httemplate/search/cust_pay_batch.cgi
httemplate/search/elements/cust_pay_batch_top.html [new file with mode: 0644]
httemplate/search/pay_batch.cgi

index 9fa1459..171ec9f 100644 (file)
@@ -290,6 +290,7 @@ sub approve {
       'payinfo'   => $new->payinfo || $old->payinfo,
       'paid'      => $new->paid,
       '_date'     => $new->_date,
+      'usernum'   => $new->usernum,
     } );
   $error = $cust_pay->insert;
   if ( $error ) {
@@ -304,16 +305,37 @@ sub approve {
 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 $conf = new FS::Conf;
+
   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;
+  if ( $old->status ) {
+    # Handle the case where payments are rejected after the batch has been 
+    # approved.  Only if manual approval is enabled.
+    if ( $conf->exists('batch-manual_approval') 
+        and lc($old->status) eq 'approved' ) {
+      # Void the payment
+      my $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('Returned payment');
+    }
+    else {
+      # normal case: refuse to do anything
+      return "paybatchnum $paybatchnum already resolved ('".$old->status."')";
+    }
+  } # !$old->status
   $new->status('Declined');
   my $error = $new->replace($old);
   if ( $error ) {
index 5ccad5b..850335e 100644 (file)
@@ -231,17 +231,12 @@ sub import_results {
 
   my $reself = $self->select_for_update;
 
-  unless ( $reself->status eq 'I' ) {
+  if ( $reself->status ne 'I' 
+      and !$conf->exists('batch-manual_approval') ) {
     $dbh->rollback if $oldAutoCommit;
     return "batchnum ". $self->batchnum. "no longer in transit";
   }
 
-  my $error = $self->set_status('R');
-  if ( $error ) {
-    $dbh->rollback if $oldAutoCommit;
-    return $error;
-  }
-
   my $total = 0;
   my $line;
 
@@ -396,7 +391,13 @@ sub import_results {
       $dbh->rollback;
       die $@;
     }
-    $self->set_status('I') if !$close;
+    if ( $close ) {
+      my $error = $self->set_status('R');
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+    }
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -538,6 +539,7 @@ sub manual_approve {
   my $date = time;
   my %opt = @_;
   my $paybatch = $opt{'paybatch'} || $self->batchnum;
+  my $usernum = $opt{'usernum'} || die "manual approval requires a usernum";
   my $conf = FS::Conf->new;
   return 'manual batch approval disabled' 
     if ( ! $conf->exists('batch-manual_approval') );
@@ -562,8 +564,9 @@ sub manual_approve {
   ) {
     my $new_cust_pay_batch = new FS::cust_pay_batch { 
       $cust_pay_batch->hash,
-      'paid'  => $cust_pay_batch->amount,
-      '_date' => $date,
+      'paid'    => $cust_pay_batch->amount,
+      '_date'   => $date,
+      'usernum' => $usernum,
     };
     my $error = $new_cust_pay_batch->approve($paybatch);
     if ( $error ) {
@@ -572,9 +575,7 @@ sub manual_approve {
     }
     $payments++;
   }
-  return 'no unresolved payments in batch' if $payments == 0;
   $self->set_status('R');
-  
   $dbh->commit;
   return;
 }
index f857e23..ff5f12b 100644 (file)
@@ -7,10 +7,11 @@ die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Process batches');
 
 my $batchnum = $cgi->param('batchnum');
-# make a record in the paybatch of who did this
-my $paybatch = 'manual-'.$FS::CurrentUser::CurrentUser->username.
-   '-' . time2str('%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+my $paybatch = $batchnum;
+my $usernum = $FS::CurrentUser::CurrentUser->usernum;
 my $pay_batch = qsearchs('pay_batch', { 'batchnum' => $batchnum })
   or die "batchnum '$batchnum' not found";
-my $error = $pay_batch->manual_approve('paybatch' => $paybatch);
+my $error = $pay_batch->manual_approve(
+  'paybatch' => $paybatch, 'usernum' => $usernum
+);
 </%init>
index 4a22487..8022d46 100755 (executable)
@@ -3,7 +3,10 @@
               'name'        => 'batch details',
              'query'       => $sql_query,
              'count_query' => $count_query,
-              'html_init'   => $pay_batch ? $html_init : '',
+              'html_init'   => $pay_batch ? 
+                                  include('elements/cust_pay_batch_top.html',
+                                          'pay_batch' => $pay_batch
+                                  ) : '',
              'header'      => [ '#',
                                 'Inv #',
                                 'Customer',
@@ -66,7 +69,6 @@ die "access denied"
            && (    $conf->exists('batch-enable')
                 || $conf->config('batch-enable_payby')
               )
-           #&& $FS::CurrentUser::CurrentUser->access_right('View customer batched payments')
          );
 
 my( $count_query, $sql_query );
@@ -124,113 +126,9 @@ $sql_query = "SELECT paybatchnum,invnum,custnum,cpb.last,cpb.first," .
              'LEFT JOIN pay_batch USING ( batchnum ) ' .
              "$search ORDER BY $orderby";
 
-my $html_init = '<TABLE>';
+my $html_init = '';
 if ( $pay_batch ) {
-  my $fixed = $conf->config('batch-fixed_format-'. $pay_batch->payby);
-  if (
-       $pay_batch->status eq 'O' 
-       || ( $pay_batch->status eq 'I'
-            && $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')
-          ) 
-       || ( $pay_batch->status eq 'R'
-            && $FS::CurrentUser::CurrentUser->access_right('Redownload resolved batches')
-          ) 
-  ) {
-    $html_init .= qq!<TR><FORM ACTION="$p/misc/download-batch.cgi" METHOD="POST">!;
-    if ( $fixed ) {
-      $html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
-    } else {
-      $html_init .= qq!Download batch in format !.
-                    qq!<SELECT NAME="format">!.
-                    qq!<OPTION VALUE="">Default batch mode</OPTION>!.
-                    qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV file for TD Canada Trust Merchant PC Batch</OPTION>!.
-                    qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
-                    qq!<OPTION VALUE="PAP">80 byte file for TD Canada Trust PAP Batch</OPTION>!.
-                    qq!<OPTION VALUE="BoM">Bank of Montreal ECA batch</OPTION>!.
-                    qq!<OPTION VALUE="ach-spiritone">Spiritone ACH batch</OPTION>!.
-                    qq!<OPTION VALUE="paymentech">Chase Paymentech XML</OPTION>!.
-                    qq!<OPTION VALUE="RBC">Royal Bank of Canada PDS</OPTION>!.
-                    qq!<OPTION VALUE="td_eft1464">TD Commercial Banking EFT 1464 byte</OPTION>!.
-
-                    qq!</SELECT>!;
-    }
-    $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum"><INPUT TYPE="submit" VALUE="Download"></FORM><BR><BR></TR>!;
-  }
-
-  if (
-       $pay_batch->status eq 'I' 
-       || ( $pay_batch->status eq 'R'
-            && $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')
-          ) 
-  ) {
-    $html_init .= '<TR>'.
-                  include('/elements/form-file_upload.html',
-                            'name'      => 'FileUpload',
-                            'action'    => "$p/misc/upload-batch.cgi",
-                            'num_files' => 1,
-                            'fields'    => [ 'batchnum', 'format' ],
-                            'message'   => 'Batch results uploaded.',
-                            ) .
-                  'Upload results<BR></TR><TR>'.
-                  include('/elements/file-upload.html',
-                            'field'   => 'file',
-                            'label'   => 'Filename',
-                            'no_table'=> 1
-                         ).
-                  '<BR></TR>'
-                  ;
-    if ( $fixed and $fixed eq 'td_eft1464' ) {
-      # special case, this one has two upload formats
-      $html_init .= qq!<TR>Format !.
-                    qq!<SELECT NAME="format">!.
-                    qq!<OPTION VALUE="td_eftack264">TD EFT Acknowledgement</OPTION>!.
-                    qq!<OPTION VALUE="td_eftret80">TD EFT Returned Items</OPTION>!.
-                    qq!</SELECT><BR></TR>!;
-    }
-    elsif ( $fixed ) {
-      $html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
-    } else {
-      # should pull this from %import_info
-      $html_init .= qq!<TR>Format !.
-                    qq!<SELECT NAME="format">!.
-                    qq!<OPTION VALUE="">Default batch mode</OPTION>!.
-                    qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV results from TD Canada Trust Merchant PC Batch</OPTION>!.
-                    qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
-                    qq!<OPTION VALUE="PAP">264 byte results for TD Canada Trust PAP Batch</OPTION>!.
-                    qq!<OPTION VALUE="BoM">Bank of Montreal ECA results</OPTION>!.
-                    qq!<OPTION VALUE="ach-spiritone">Spiritone ACH batch</OPTION>!.
-                    qq!<OPTION VALUE="paymentech">Chase Paymentech XML</OPTION>!.
-                    qq!<OPTION VALUE="RBC">Royal Bank of Canada PDS</OPTION>!.
-                    qq!<OPTION VALUE="td_eftack264">TD EFT Acknowledgement</OPTION>!.
-                    qq!<OPTION VALUE="td_eftret80">TD EFT Returned Items</OPTION>!.
-                    qq!</SELECT><BR></TR>!;
-    }
-    $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum">!;
-    $html_init .= '<TR> <INPUT TYPE="submit" VALUE="Upload"></FORM><BR> </TR>';
-    if ( $conf->exists('batch-manual_approval') 
-          and $conf->config('batch-fixed_format-CHEK') eq 'td_eft1464'
-          and $pay_batch->status eq 'I'
-          and $pay_batch->payby eq 'CHEK' ) {
-      $html_init .= qq!<TR><INPUT TYPE="button" VALUE="Manually approve" onclick="
-if ( confirm('Approve all remaining payments in this batch?') )
-  window.location.href='${p}misc/process/pay_batch-approve.cgi?batchnum=$batchnum';"></TR>!
-    }
-  }
-  $html_init .= '</TABLE>';
+  $html_init = include('elements/cust_pay_batch_top.html', 
+                    'pay_batch' => $pay_batch);
 }
-
-if ($pay_batch) {
-  my $sth = dbh->prepare($count_query) or die dbh->errstr. "doing $count_query";
-  $sth->execute or die "Error executing \"$count_query\": ". $sth->errstr;
-  my $cards = $sth->fetchrow_arrayref->[0];
-
-  my $st = "SELECT SUM(amount) from cust_pay_batch WHERE batchnum=". $batchnum;
-  $sth = dbh->prepare($st) or die dbh->errstr. "doing $st";
-  $sth->execute or die "Error executing \"$st\": ". $sth->errstr;
-  my $total = $sth->fetchrow_arrayref->[0];
-
-  $html_init .= "$cards credit card payments batched<BR>\$" .
-                sprintf("%.2f", $total) ." total in batch<BR>";
-}
-
 </%init>
diff --git a/httemplate/search/elements/cust_pay_batch_top.html b/httemplate/search/elements/cust_pay_batch_top.html
new file mode 100644 (file)
index 0000000..96ed428
--- /dev/null
@@ -0,0 +1,127 @@
+% # Download batch
+% if ( $status eq 'O'
+%   or ( $status eq 'I' and $curuser->access_right('Reprocess batches') )
+%   or ( $status eq 'R' and $curuser->access_right('Redownload resolved batches') )
+%   ) {
+<TABLE>
+<TR><FORM ACTION="<%$p%>misc/download-batch.cgi" METHOD="POST">
+<INPUT TYPE="hidden" NAME="batchnum" VALUE="<%$batchnum%>">
+%   if ( $fixed ) {
+<INPUT TYPE="hidden" NAME="format" VALUE="<%$fixed%>">
+%   }
+%   else {
+Download batch in format <SELECT NAME="format">
+%     foreach ( keys %download_formats ) {
+<OPTION VALUE="<%$_%>"><% $download_formats{$_} %></OPTION>
+%     }
+</SELECT>
+%   }
+<INPUT TYPE="submit" VALUE="Download"></FORM><BR><BR></TR>
+% } # end of download
+
+% # Upload batch
+% if ( $pay_batch->status eq 'I'
+%   or ( $pay_batch->status eq 'R'
+%     and $curuser->access_right('Reprocess batches')
+%     and $conf->exists('batch-manual_approval')
+%   ) 
+% ) {
+<TR>
+<% include('/elements/form-file_upload.html',
+              'name'      => 'FileUpload',
+              'action'    => "${p}misc/upload-batch.cgi",
+              'num_files' => 1,
+              'fields'    => [ 'batchnum', 'format' ],
+              'message'   => 'Batch results uploaded.',
+) %>
+Upload results<BR></TR>
+<TR>
+<% include('/elements/file-upload.html',
+              'field'     => 'file',
+              'label'     => 'Filename',
+              'no_table'  => 1,
+) %>
+<INPUT TYPE="hidden" NAME="batchnum" VALUE="<% $batchnum %>">
+<BR></TR>
+%   if ( $fixed ) {
+%     if ( $fixed eq 'td_eft1464' ) { # special case
+<TR>Format <SELECT NAME="format">
+<OPTION VALUE="td_eftack264">TD EFT Acknowledgement</OPTION>
+<OPTION VALUE="td_eftret80">TD EFT Returned Items</OPTION>
+</SELECT></TR>
+%     }
+%     else {
+<INPUT TYPE="hidden" NAME="format" VALUE="<% $fixed %>">
+%     }
+%   }
+%   else {
+<TR>Format <SELECT NAME="format">
+%     foreach ( keys(%upload_formats) ) {
+<OPTION VALUE="<%$_%>"><% $upload_formats{$_} %></OPTION>
+%     }
+%   } # if $fixed
+<TR><INPUT TYPE="submit" VALUE="Upload"></TR>
+</FORM><BR>
+% } # end upload
+
+% # manual approval
+% if ( $fixed eq 'td_eft1464'
+%   and $status eq 'I'
+%   and $payby eq 'CHEK'
+%   and $conf->exists('batch-manual_approval')
+%   ) {
+<TR><INPUT TYPE="button" VALUE="Manually approve" onclick="
+if ( confirm('Approve all remaining payments in this batch?') ) 
+  window.location.href='<%$p%>misc/process/pay_batch-approve.cgi?batchnum=<%$batchnum%>';
+"></TR>
+% } # end manual approval
+</TABLE>
+
+% # summary info
+Batch is <% $statustext{$status} %><BR>
+<%$count%> payments batched<BR>
+<%$money_char%><%$total%> total in batch<BR>
+
+<%init>
+my %opt = @_;
+my $pay_batch = $opt{'pay_batch'} or return;
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+my $payby = $pay_batch->payby;
+my $status = $pay_batch->status;
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $batchnum = $pay_batch->batchnum;
+my $fixed = $conf->config("batch-fixed_format-$payby");
+
+tie my %download_formats, 'Tie::IxHash', (
+'' => 'Default batch mode',
+'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',
+# insert new batch formats here
+);
+
+tie my %upload_formats, 'Tie::IxHash', (
+  %download_formats,
+# minor tweaks
+  'td_eftack' => 'TD EFT Acknowledgement',
+  'td_eftret' => 'TD EFT Returned Items',
+);
+delete $upload_formats{'td_eft1464'};
+$upload_formats{'PAP'} = '264 byte results for TD Canada Trust PAP Batch',
+
+my %statustext = ( 'O' => 'open', 'I' => 'in transit', 'R' => 'resolved' );
+
+my $count_query = "SELECT COUNT(*) FROM cust_pay_batch WHERE batchnum=$batchnum";
+my $count = FS::Record->scalar_sql($count_query);
+my $sum_query = "SELECT SUM(amount) FROM cust_pay_batch WHERE batchnum=$batchnum";
+my $total = sprintf("%.2f", FS::Record->scalar_sql($sum_query));
+</%init>
index 6d571b4..34297a5 100755 (executable)
@@ -80,6 +80,7 @@
                                      sub { shift->status eq 'O' ? "b" : '' },
                                      sub { shift->status eq 'I' ? "b" : '' },
                                    ],
+                 'html_init'     => $html_init,
       )
 
 %>
@@ -128,4 +129,9 @@ my $extra_sql = scalar(@where) ? 'WHERE ' . join(' AND ', @where) : '';
 
 my $link = [ "${p}search/cust_pay_batch.cgi?dcln=1;batchnum=", 'batchnum' ];
 
+my $resolved = $cgi->param('resolved') || 0;
+$cgi->param('resolved' => !$resolved);
+my $html_init = '<A HREF="' . $cgi->self_url . '"><I>'.
+    ($resolved ? 'Hide' : 'Show') . ' resolved batches</I></A><BR>';
+
 </%init>