[freeside-commits] branch FREESIDE_3_BRANCH updated. a439c0c5998c428e7bfd533353911ae48b4bee7b

Christopher Burger burgerc at freeside.biz
Mon Jul 16 12:22:09 PDT 2018


The branch, FREESIDE_3_BRANCH has been updated
       via  a439c0c5998c428e7bfd533353911ae48b4bee7b (commit)
       via  a90d069b1f1d814a91bf7cb3eb0d7ac9fdec7e08 (commit)
       via  b4647550d33068067925f7f3fe8d6fe4f02a67e4 (commit)
       via  639e88d37ab33deeea4db7c8d1f15e8967f1826f (commit)
      from  f41ba7540d45187dac6dd147bab6bb26b8a4956b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit a439c0c5998c428e7bfd533353911ae48b4bee7b
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Mon Jul 16 14:40:39 2018 -0400

    RT# 74435 - added check, to make sure batch format can handle refunds
    
    Conflicts:
            httemplate/misc/download-batch.cgi

diff --git a/FS/FS/pay_batch/RBC.pm b/FS/FS/pay_batch/RBC.pm
index 05ee4e501..c4388d156 100644
--- a/FS/FS/pay_batch/RBC.pm
+++ b/FS/FS/pay_batch/RBC.pm
@@ -230,5 +230,10 @@ $name = 'RBC';
   },
 );
 
+## this format can handle credit transactions
+sub can_handle_credits {
+  1;
+}
+
 1;
 
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index 55f06642b..c79c39a80 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -85,7 +85,6 @@
 
 
 <BR>Refund
-
 <% ntable("#cccccc", 2) %>
 
   <TR>
@@ -176,9 +175,6 @@ if ( $cgi->param('paynum') =~ /^(\d+)$/ ) {
 }
 die "no custnum or paynum specified!" unless $custnum;
 
-my $cust_main = qsearchs( 'cust_main', { 'custnum'=>$custnum } );
-die "unknown custnum $custnum" unless $cust_main;
-
 my $_date = time;
 
 my $p1 = popurl(1);
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index 49aedf3fe..44605bf42 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -21,8 +21,6 @@ die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Refund payment')
       || $FS::CurrentUser::CurrentUser->access_right('Post refund');
 
-my $conf = new FS::Conf;
-
 $cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
 my $custnum = $1;
 my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
@@ -62,12 +60,7 @@ if ( $error ) {
   my $refund = "$1$2";
   $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!";
   my $paynum = $1;
-  #my $paydate;
   my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
-  #unless ($paynum) {
-  #  if ($cust_payby->paydate) { $paydate = "$year-$month-01"; }
-  #  else { $paydate = "2037-12-01"; }
-  #}
 
   if ( $cgi->param('batch') ) {
     $paydate = "2037-12-01" unless $paydate;
diff --git a/httemplate/misc/download-batch.cgi b/httemplate/misc/download-batch.cgi
index f3a31eb3b..7b56f2aa1 100644
--- a/httemplate/misc/download-batch.cgi
+++ b/httemplate/misc/download-batch.cgi
@@ -20,7 +20,16 @@ elsif ( $cgi->param('format') =~ /^([\w\- ]+)$/ ) {
   $opt{'format'} = $1;
 }
 
-my $pay_batch = qsearchs('pay_batch', { batchnum => $batchnum } );
+my $credit_transactions = "EXISTS (SELECT 1 FROM cust_pay_batch WHERE batchnum = $batchnum AND paycode = 'C') AS arecredits";
+my $pay_batch = qsearchs({ 'select'    => "*, $credit_transactions",
+                           'table'     => 'pay_batch',
+                           'hashref'   => { batchnum => $batchnum },
+                         });
 die "Batch not found: '$batchnum'" if !$pay_batch;
 
+if ($pay_batch->{Hash}->{arecredits}) {
+  my $export_format = "FS::pay_batch::".$opt{'format'};
+    die "This format can not handle refunds." unless $export_format->can('can_handle_credits');
+}
+
 </%init>

commit a90d069b1f1d814a91bf7cb3eb0d7ac9fdec7e08
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Mon Jul 9 08:27:53 2018 -0400

    RT# 74435 - Fixed error with refund link on payment history page not allowing batch refund when batch payment was made.
    
    Conflicts:
            FS/FS/cust_main/Billing_Realtime.pm
            httemplate/view/cust_main/menu.html

diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index ae41c70c4..c503b45e6 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -1479,7 +1479,7 @@ sub realtime_refund_bop {
       $self->agent->payment_gateway( 'method'  => $options{method},
                                      #'payinfo' => $payinfo,
                                    );
-    my( $processor, $login, $password, $namespace ) =
+    ( $processor, $login, $password, $namespace ) =
       map { my $method = "gateway_$_"; $payment_gateway->$method }
         qw( module username password namespace );
 
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index 473648f42..55f06642b 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -104,63 +104,25 @@
       <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
     </TR>
 % }
-%  elsif ($payby eq 'CHEK') {
-%
-<SCRIPT TYPE="text/javascript">
-  function cust_payby_changed (what) {
-    var custpaybynum = what.options[what.selectedIndex].value
-    if ( custpaybynum == '' || custpaybynum == '0' ) {
-       //what.form.payinfo.disabled = false;
-       $('#cust_payby').slideDown();
-    } else {
-       //what.form.payinfo.value = '';
-       //what.form.payinfo.disabled = true;
-       $('#cust_payby').slideUp();
-    }
-  }
-</SCRIPT>
-% my @cust_payby = ();
-% if ( $payby eq 'CARD' ) {
-%   @cust_payby = $cust_main->cust_payby('CARD','DCRD');
-% } elsif ( $payby eq 'CHEK' ) {
-%   @cust_payby = $cust_main->cust_payby('CHEK','DCHK');
-% } else {
-%   die "unknown payby $payby";
-% }
-%
-% my $custpaybynum = length(scalar($cgi->param('custpaybynum')))
-%                      ? scalar($cgi->param('custpaybynum'))
-%                      : scalar(@cust_payby) && $cust_payby[0]->custpaybynum;
-<& /elements/tr-select-cust_payby.html,
-     'cust_payby' => \@cust_payby,
-     'curr_value' => $custpaybynum,
-     'onchange'   => 'cust_payby_changed(this)',
-&>
-    <INPUT TYPE="hidden" NAME="batch" VALUE="1">
-    </TABLE>
-<P>
-<DIV ID="cust_payby"
-  <% $custpaybynum ? 'STYLE="display:none"'
-                   : ''
-  %>
->
-<TABLE class="fsinnerbox">
-
-<& /elements/cust_payby_new.html,
-     'cust_payby' => \@cust_payby,
-     'curr_value' => $custpaybynum,
-&>
-
-</TABLE>
-</DIV>
+% elsif ($payby eq 'CHEK' || $payby eq 'CARD') {
+
+%  if ( $conf->exists("batch-enable")
+%      || grep $payby eq $_, $conf->config('batch-enable_payby')
+%  ) {
+%     if ( grep $payby eq $_, $conf->config('realtime-disable_payby') ) {
+          <INPUT TYPE="hidden" NAME="batch" VALUE="1">
+%     } else {
+        <TR>
+          <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="batch" VALUE="1" ID="batch" <% ($batchnum || $batch) ? 'checked' : '' %> ></TD>
+          <TD ALIGN="left">   <% mt('Add to current batch') |h %></TD>
+        </TR>
+%     }
+%  }
 
-%  } else {
+% } else {
     <INPUT TYPE="hidden" NAME="payinfo" VALUE="">
-    </TABLE>
 % }
 
-<P>
-<TABLE class="fsinnerbox">
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'F',
@@ -193,16 +155,18 @@ my $payby   = $cgi->param('payby');
 my $payinfo = $cgi->param('payinfo');
 my $reason  = $cgi->param('reason');
 my $link    = $cgi->param('popup') ? 'popup' : '';
+my $batch   = $cgi->param('batch');
 
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->refund_access_right($payby);
 
-my( $paynum, $cust_pay ) = ( '', '' );
+my( $paynum, $cust_pay, $batchnum ) = ( '', '', '' );
 if ( $cgi->param('paynum') =~ /^(\d+)$/ ) {
   $paynum = $1;
   $cust_pay = qsearchs('cust_pay', { paynum=>$paynum } )
     or die "unknown payment # $paynum";
   $refund ||= $cust_pay->unrefunded;
+  $batchnum = $cust_pay->batchnum;
   if ( $custnum ) {
     die "payment # $paynum is not for specified customer # $custnum"
       unless $custnum == $cust_pay->custnum;
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index 8dbe4fc63..49aedf3fe 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -53,137 +53,6 @@ if ( $error ) {
              'CHEK' => 'electronic check (ACH)',
              );
 
-my( $cust_payby, $payinfo, $paycvv, $month, $year, $payname );
-my $paymask = '';
-if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
-
-  ##
-  # use stored cust_payby info
-  ##
-
-  $cust_payby = qsearchs('cust_payby', { custnum      => $custnum,
-                                            custpaybynum => $custpaybynum, } )
-    or die "unknown custpaybynum $custpaybynum";
-
-  # not needed for realtime_bop, but still needed for batch_card
-  $payinfo = $cust_payby->payinfo;
-  $paymask = $cust_payby->paymask;
-  $paycvv = $cust_payby->paycvv; # pass it if we got it, running a transaction will clear it
-  ( $month, $year ) = $cust_payby->paydate_mon_year;
-  $payname = $cust_payby->payname;
-  $cgi->param(-name=>"paytype", -value=>$cust_payby->paytype) unless $cgi->param("paytype");
-
-} else {
-
-  ##
-  # use new info
-  ##
-
-  $cgi->param('year') =~ /^(\d+)$/
-    or errorpage("illegal year ". $cgi->param('year'));
-  $year = $1;
-
-  $cgi->param('month') =~ /^(\d+)$/
-    or errorpage("illegal month ". $cgi->param('month'));
-  $month = $1;
-
-  $cgi->param('payname') =~ /^([\w \,\.\-\']+)$/
-    or errorpage(gettext('illegal_name'). " payname: ". $cgi->param('payname'));
-  $payname = $1;
-
-  if ( $payby eq 'CHEK' ) {
-
-    $cgi->param('payinfo1') =~ /^(\d+)$/
-      or errorpage("Illegal account number ". $cgi->param('payinfo1'));
-    my $payinfo1 = $1;
-    $cgi->param('payinfo2') =~ /^(\d+)$/
-      or errorpage("Illegal ABA/routing number ". $cgi->param('payinfo2'));
-    my $payinfo2 = $1;
-    if ( $conf->config('echeck-country') eq 'CA' ) {
-      $cgi->param('payinfo3') =~ /^(\d{5})$/
-        or errorpage("Illegal branch number ". $cgi->param('payinfo2'));
-      $payinfo2 = "$1.$payinfo2";
-    }
-    $payinfo = $payinfo1 . '@'. $payinfo2;
-
-  } elsif ( $payby eq 'CARD' ) {
-
-    $payinfo = $cgi->param('payinfo');
-
-    $payinfo =~ s/\D//g;
-    $payinfo =~ /^(\d{13,19}|\d{8,9})$/
-      or errorpage(gettext('invalid_card'));
-    $payinfo = $1;
-    validate($payinfo)
-      or errorpage(gettext('invalid_card'));
-
-    unless ( $cust_main->tokenized($payinfo) ) { #token
-
-      my $cardtype = cardtype($payinfo);
-
-      errorpage(gettext('unknown_card_type'))
-        if $cardtype eq "Unknown";
-
-      my %bop_card_types = map { $_=>1 } values %{ card_types() };
-      errorpage("$cardtype not accepted") unless $bop_card_types{$cardtype};
-
-    }
-
-    if ( length($cgi->param('paycvv') ) ) {
-      if ( cardtype($payinfo) eq 'American Express card' ) {
-        $cgi->param('paycvv') =~ /^(\d{4})$/
-          or errorpage("CVV2 (CID) for American Express cards is four digits.");
-        $paycvv = $1;
-      } else {
-        $cgi->param('paycvv') =~ /^(\d{3})$/
-          or errorpage("CVV2 (CVC2/CID) is three digits.");
-        $paycvv = $1;
-      }
-    } elsif ( $conf->exists('backoffice-require_cvv') ){
-      errorpage("CVV2 is required");
-    }
-
-  } else {
-    die "unknown payby $payby";
-  }
-
-  # save first, for proper tokenization
-  if ( $cgi->param('save') ) {
-
-    my %saveopt;
-    if ( $payby eq 'CARD' ) {
-      my $bill_location = FS::cust_location->new;
-      $bill_location->set( $_ => scalar($cgi->param($_)) )
-        foreach @{$payby2fields{$payby}};
-      $saveopt{'bill_location'} = $bill_location;
-      $saveopt{'paycvv'} = $paycvv; # save_cust_payby contains conf logic for when to use this
-      $saveopt{'paydate'} = "$year-$month-01";
-    } else {
-      # ss/stateid/stateid_state won't be saved, but should be harmless to pass
-      %saveopt = map { $_ => scalar($cgi->param($_)) } @{$payby2fields{$payby}};
-    }
-
-    my $error = $cust_main->save_cust_payby(
-      'saved_cust_payby' => \$cust_payby,
-      'payment_payby' => $payby,
-      'auto'          => scalar($cgi->param('auto')),
-      'weight'        => scalar($cgi->param('weight')),
-      'payinfo'       => $payinfo,
-      'payname'       => $payname,
-      %saveopt
-    );
-
-    errorpage("error saving info, payment not processed: $error")
-      if $error;
-
-  } elsif ( $payby eq 'CARD' ) { # not saving
-
-    $paymask = FS::payinfo_Mixin->mask_payinfo('CARD',$payinfo); # for untokenized but tokenizable payinfo
-
-  }
-
-}
-
 ##
 # now run the refund
 ##
@@ -193,46 +62,45 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
   my $refund = "$1$2";
   $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!";
   my $paynum = $1;
-  my $paydate;
-  if ($cust_payby->paydate) { $paydate = "$year-$month-01"; }
-  else { $paydate = "2037-12-01"; }
+  #my $paydate;
+  my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
+  #unless ($paynum) {
+  #  if ($cust_payby->paydate) { $paydate = "$year-$month-01"; }
+  #  else { $paydate = "2037-12-01"; }
+  #}
 
   if ( $cgi->param('batch') ) {
-
+    $paydate = "2037-12-01" unless $paydate;
     $error ||= $cust_main->batch_card(
                                      'payby'    => $payby,
                                      'amount'   => $refund,
-                                     'payinfo'  => $payinfo,
-                                     'paydate'  => $paydate,
-                                     'payname'  => $payname,
+                                     #'payinfo'  => $payinfo,
+                                     #'paydate'  => $paydate,
+                                     #'payname'  => $payname,
                                      'paycode'  => 'C',
                                      map { $_ => scalar($cgi->param($_)) }
                                        @{$payby2fields{$payby}}
                                    );
     errorpage($error) if $error;
 
-#### post refund #####
     my %hash = map {
       $_, scalar($cgi->param($_))
     } fields('cust_refund');
-    $paynum = $cgi->param('paynum');
-    $paynum =~ /^(\d*)$/ or die "Illegal paynum!";
-    if ($paynum) {
-      my $cust_pay = qsearchs('cust_pay',{ 'paynum' => $paynum });
-      die "Could not find paynum $paynum" unless $cust_pay;
-      $error = $cust_pay->refund(\%hash);
-    } else {
-      my $new = new FS::cust_refund ( \%hash );
-      $error = $new->insert;
-    }
-    # if not a batch refund run realtime.
+
+    my $new = new FS::cust_refund ( { 'paynum' => $paynum,
+                                      %hash,
+                                  } );
+    $error = $new->insert;
+
+  # if not a batch refund run realtime.
   } else {
+    $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/;
     $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund,
                                                   'paynum' => $paynum,
                                                   'reasonnum' => $reasonnum,
                                                   %options );
   }
-} else {
+} else { # run cash refund.
   my %hash = map {
     $_, scalar($cgi->param($_))
   } fields('cust_refund');

commit b4647550d33068067925f7f3fe8d6fe4f02a67e4
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Tue Jun 12 09:51:58 2018 -0400

    RT# 74435 - fixed errors in posting a echeck refund when no account is listed.
    
    Conflicts:
            FS/FS/cust_main/Billing_Batch.pm
            httemplate/elements/tr-select-cust_payby.html
            httemplate/misc/payment.cgi
            httemplate/misc/process/payment.cgi

diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index d4214d1c8..c5cee8d94 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2790,7 +2790,7 @@ sub batch_card {
   } );
 
   foreach (qw( address1 address2 city state zip country latitude longitude
-               payby payinfo paydate payname paycode ))
+               payby payinfo paydate payname paycode paytype ))
   {
     $options{$_} = '' unless exists($options{$_});
   }
@@ -2817,7 +2817,7 @@ sub batch_card {
     'exp'      => $options{paydate}  || $self->paydate,
     'payname'  => $options{payname}  || $self->payname,
     'amount'   => $amount,                         # consolidating
-    'paycode'  => $options{paycode}  || $cust_payby->paycode,
+    'paycode'  => $options{paycode}  || '',
   } );
   
   $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum)
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index e1975ed70..473648f42 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -85,6 +85,7 @@
 
 
 <BR>Refund
+
 <% ntable("#cccccc", 2) %>
 
   <TR>
@@ -105,6 +106,19 @@
 % }
 %  elsif ($payby eq 'CHEK') {
 %
+<SCRIPT TYPE="text/javascript">
+  function cust_payby_changed (what) {
+    var custpaybynum = what.options[what.selectedIndex].value
+    if ( custpaybynum == '' || custpaybynum == '0' ) {
+       //what.form.payinfo.disabled = false;
+       $('#cust_payby').slideDown();
+    } else {
+       //what.form.payinfo.value = '';
+       //what.form.payinfo.disabled = true;
+       $('#cust_payby').slideUp();
+    }
+  }
+</SCRIPT>
 % my @cust_payby = ();
 % if ( $payby eq 'CARD' ) {
 %   @cust_payby = $cust_main->cust_payby('CARD','DCRD');
@@ -123,10 +137,30 @@
      'onchange'   => 'cust_payby_changed(this)',
 &>
     <INPUT TYPE="hidden" NAME="batch" VALUE="1">
+    </TABLE>
+<P>
+<DIV ID="cust_payby"
+  <% $custpaybynum ? 'STYLE="display:none"'
+                   : ''
+  %>
+>
+<TABLE class="fsinnerbox">
+
+<& /elements/cust_payby_new.html,
+     'cust_payby' => \@cust_payby,
+     'curr_value' => $custpaybynum,
+&>
+
+</TABLE>
+</DIV>
+
 %  } else {
     <INPUT TYPE="hidden" NAME="payinfo" VALUE="">
+    </TABLE>
 % }
 
+<P>
+<TABLE class="fsinnerbox">
 <& /elements/tr-select-reason.html,
               'field'          => 'reasonnum',
               'reason_class'   => 'F',
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index b1b5c80bd..8dbe4fc63 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -71,6 +71,7 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
   $paycvv = $cust_payby->paycvv; # pass it if we got it, running a transaction will clear it
   ( $month, $year ) = $cust_payby->paydate_mon_year;
   $payname = $cust_payby->payname;
+  $cgi->param(-name=>"paytype", -value=>$cust_payby->paytype) unless $cgi->param("paytype");
 
 } else {
 
@@ -192,8 +193,9 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
   my $refund = "$1$2";
   $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!";
   my $paynum = $1;
-  my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
-  $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/;
+  my $paydate;
+  if ($cust_payby->paydate) { $paydate = "$year-$month-01"; }
+  else { $paydate = "2037-12-01"; }
 
   if ( $cgi->param('batch') ) {
 
@@ -201,7 +203,7 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
                                      'payby'    => $payby,
                                      'amount'   => $refund,
                                      'payinfo'  => $payinfo,
-                                     'paydate'  => "$year-$month-01",
+                                     'paydate'  => $paydate,
                                      'payname'  => $payname,
                                      'paycode'  => 'C',
                                      map { $_ => scalar($cgi->param($_)) }

commit 639e88d37ab33deeea4db7c8d1f15e8967f1826f
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Thu May 18 11:58:26 2017 -0400

    RT# 74435 - Adding option to allow refunds using electronic check batch with RBC format.
    
    Conflicts:
            FS/FS/Schema.pm
            FS/FS/cust_main/Billing_Batch.pm
            httemplate/view/cust_main/menu.html

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 1567b0030..699143239 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1909,6 +1909,7 @@ sub tables_hashref {
         'amount',   @money_type, '', '', 
         'status',   'varchar', 'NULL', $char_d, '', '', 
         'error_message',   'varchar', 'NULL', $char_d, '', '',
+        'paycode',       'varchar', 'NULL', $char_d, '', '',
       ],
       'primary_key' => 'paybatchnum',
       'unique' => [],
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 7d913b955..d4214d1c8 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2790,7 +2790,7 @@ sub batch_card {
   } );
 
   foreach (qw( address1 address2 city state zip country latitude longitude
-               payby payinfo paydate payname ))
+               payby payinfo paydate payname paycode ))
   {
     $options{$_} = '' unless exists($options{$_});
   }
@@ -2817,6 +2817,7 @@ sub batch_card {
     'exp'      => $options{paydate}  || $self->paydate,
     'payname'  => $options{payname}  || $self->payname,
     'amount'   => $amount,                         # consolidating
+    'paycode'  => $options{paycode}  || $cust_payby->paycode,
   } );
   
   $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum)
diff --git a/FS/FS/pay_batch/RBC.pm b/FS/FS/pay_batch/RBC.pm
index 142c50b79..05ee4e501 100644
--- a/FS/FS/pay_batch/RBC.pm
+++ b/FS/FS/pay_batch/RBC.pm
@@ -180,8 +180,13 @@ $name = 'RBC';
       if (($cust_pay_batch->cust_main->paytype eq "Business checking" || $cust_pay_batch->cust_main->paytype eq "Business savings") && $cust_pay_batch->cust_main->company);
 
     $i++;
+
+    ## set to D for debit by default, then override to what cust_pay_batch has as payments may not have paycode.
+    my $debitorcredit = 'D';
+    $debitorcredit = $cust_pay_batch->paycode unless !$cust_pay_batch->paycode;
+
     sprintf("%06u", $i).
-    'D'.
+    $debitorcredit.
     sprintf("%3s",$trans_code).
     sprintf("%10s",$client_num).
     ' '.
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index 32da4543e..e1975ed70 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -102,7 +102,28 @@
       <TD ALIGN="right">Check #</TD>
       <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
     </TR>
+% }
+%  elsif ($payby eq 'CHEK') {
+%
+% my @cust_payby = ();
+% if ( $payby eq 'CARD' ) {
+%   @cust_payby = $cust_main->cust_payby('CARD','DCRD');
+% } elsif ( $payby eq 'CHEK' ) {
+%   @cust_payby = $cust_main->cust_payby('CHEK','DCHK');
 % } else {
+%   die "unknown payby $payby";
+% }
+%
+% my $custpaybynum = length(scalar($cgi->param('custpaybynum')))
+%                      ? scalar($cgi->param('custpaybynum'))
+%                      : scalar(@cust_payby) && $cust_payby[0]->custpaybynum;
+<& /elements/tr-select-cust_payby.html,
+     'cust_payby' => \@cust_payby,
+     'curr_value' => $custpaybynum,
+     'onchange'   => 'cust_payby_changed(this)',
+&>
+    <INPUT TYPE="hidden" NAME="batch" VALUE="1">
+%  } else {
     <INPUT TYPE="hidden" NAME="payinfo" VALUE="">
 % }
 
@@ -157,6 +178,9 @@ if ( $cgi->param('paynum') =~ /^(\d+)$/ ) {
 }
 die "no custnum or paynum specified!" unless $custnum;
 
+my $cust_main = qsearchs( 'cust_main', { 'custnum'=>$custnum } );
+die "unknown custnum $custnum" unless $cust_main;
+
 my $_date = time;
 
 my $p1 = popurl(1);
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index 764f2deb7..b1b5c80bd 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -21,6 +21,8 @@ die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Refund payment')
       || $FS::CurrentUser::CurrentUser->access_right('Post refund');
 
+my $conf = new FS::Conf;
+
 $cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
 my $custnum = $1;
 my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
@@ -42,6 +44,149 @@ if ( $error ) {
 } elsif ( $payby =~ /^(CARD|CHEK)$/ ) { 
   my %options = ();
   my $bop = $FS::payby::payby2bop{$1};
+
+  my %payby2fields = (
+  'CARD' => [ qw( address1 address2 city county state zip country ) ],
+  'CHEK' => [ qw( ss paytype paystate stateid stateid_state ) ],
+  );
+  my %type = ( 'CARD' => 'credit card',
+             'CHEK' => 'electronic check (ACH)',
+             );
+
+my( $cust_payby, $payinfo, $paycvv, $month, $year, $payname );
+my $paymask = '';
+if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
+
+  ##
+  # use stored cust_payby info
+  ##
+
+  $cust_payby = qsearchs('cust_payby', { custnum      => $custnum,
+                                            custpaybynum => $custpaybynum, } )
+    or die "unknown custpaybynum $custpaybynum";
+
+  # not needed for realtime_bop, but still needed for batch_card
+  $payinfo = $cust_payby->payinfo;
+  $paymask = $cust_payby->paymask;
+  $paycvv = $cust_payby->paycvv; # pass it if we got it, running a transaction will clear it
+  ( $month, $year ) = $cust_payby->paydate_mon_year;
+  $payname = $cust_payby->payname;
+
+} else {
+
+  ##
+  # use new info
+  ##
+
+  $cgi->param('year') =~ /^(\d+)$/
+    or errorpage("illegal year ". $cgi->param('year'));
+  $year = $1;
+
+  $cgi->param('month') =~ /^(\d+)$/
+    or errorpage("illegal month ". $cgi->param('month'));
+  $month = $1;
+
+  $cgi->param('payname') =~ /^([\w \,\.\-\']+)$/
+    or errorpage(gettext('illegal_name'). " payname: ". $cgi->param('payname'));
+  $payname = $1;
+
+  if ( $payby eq 'CHEK' ) {
+
+    $cgi->param('payinfo1') =~ /^(\d+)$/
+      or errorpage("Illegal account number ". $cgi->param('payinfo1'));
+    my $payinfo1 = $1;
+    $cgi->param('payinfo2') =~ /^(\d+)$/
+      or errorpage("Illegal ABA/routing number ". $cgi->param('payinfo2'));
+    my $payinfo2 = $1;
+    if ( $conf->config('echeck-country') eq 'CA' ) {
+      $cgi->param('payinfo3') =~ /^(\d{5})$/
+        or errorpage("Illegal branch number ". $cgi->param('payinfo2'));
+      $payinfo2 = "$1.$payinfo2";
+    }
+    $payinfo = $payinfo1 . '@'. $payinfo2;
+
+  } elsif ( $payby eq 'CARD' ) {
+
+    $payinfo = $cgi->param('payinfo');
+
+    $payinfo =~ s/\D//g;
+    $payinfo =~ /^(\d{13,19}|\d{8,9})$/
+      or errorpage(gettext('invalid_card'));
+    $payinfo = $1;
+    validate($payinfo)
+      or errorpage(gettext('invalid_card'));
+
+    unless ( $cust_main->tokenized($payinfo) ) { #token
+
+      my $cardtype = cardtype($payinfo);
+
+      errorpage(gettext('unknown_card_type'))
+        if $cardtype eq "Unknown";
+
+      my %bop_card_types = map { $_=>1 } values %{ card_types() };
+      errorpage("$cardtype not accepted") unless $bop_card_types{$cardtype};
+
+    }
+
+    if ( length($cgi->param('paycvv') ) ) {
+      if ( cardtype($payinfo) eq 'American Express card' ) {
+        $cgi->param('paycvv') =~ /^(\d{4})$/
+          or errorpage("CVV2 (CID) for American Express cards is four digits.");
+        $paycvv = $1;
+      } else {
+        $cgi->param('paycvv') =~ /^(\d{3})$/
+          or errorpage("CVV2 (CVC2/CID) is three digits.");
+        $paycvv = $1;
+      }
+    } elsif ( $conf->exists('backoffice-require_cvv') ){
+      errorpage("CVV2 is required");
+    }
+
+  } else {
+    die "unknown payby $payby";
+  }
+
+  # save first, for proper tokenization
+  if ( $cgi->param('save') ) {
+
+    my %saveopt;
+    if ( $payby eq 'CARD' ) {
+      my $bill_location = FS::cust_location->new;
+      $bill_location->set( $_ => scalar($cgi->param($_)) )
+        foreach @{$payby2fields{$payby}};
+      $saveopt{'bill_location'} = $bill_location;
+      $saveopt{'paycvv'} = $paycvv; # save_cust_payby contains conf logic for when to use this
+      $saveopt{'paydate'} = "$year-$month-01";
+    } else {
+      # ss/stateid/stateid_state won't be saved, but should be harmless to pass
+      %saveopt = map { $_ => scalar($cgi->param($_)) } @{$payby2fields{$payby}};
+    }
+
+    my $error = $cust_main->save_cust_payby(
+      'saved_cust_payby' => \$cust_payby,
+      'payment_payby' => $payby,
+      'auto'          => scalar($cgi->param('auto')),
+      'weight'        => scalar($cgi->param('weight')),
+      'payinfo'       => $payinfo,
+      'payname'       => $payname,
+      %saveopt
+    );
+
+    errorpage("error saving info, payment not processed: $error")
+      if $error;
+
+  } elsif ( $payby eq 'CARD' ) { # not saving
+
+    $paymask = FS::payinfo_Mixin->mask_payinfo('CARD',$payinfo); # for untokenized but tokenizable payinfo
+
+  }
+
+}
+
+##
+# now run the refund
+##
+
   $cgi->param('refund') =~ /^(\d*)(\.\d{2})?$/
     or die "illegal refund amount ". $cgi->param('refund');
   my $refund = "$1$2";
@@ -49,10 +194,42 @@ if ( $error ) {
   my $paynum = $1;
   my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
   $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/;
-  $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund,
+
+  if ( $cgi->param('batch') ) {
+
+    $error ||= $cust_main->batch_card(
+                                     'payby'    => $payby,
+                                     'amount'   => $refund,
+                                     'payinfo'  => $payinfo,
+                                     'paydate'  => "$year-$month-01",
+                                     'payname'  => $payname,
+                                     'paycode'  => 'C',
+                                     map { $_ => scalar($cgi->param($_)) }
+                                       @{$payby2fields{$payby}}
+                                   );
+    errorpage($error) if $error;
+
+#### post refund #####
+    my %hash = map {
+      $_, scalar($cgi->param($_))
+    } fields('cust_refund');
+    $paynum = $cgi->param('paynum');
+    $paynum =~ /^(\d*)$/ or die "Illegal paynum!";
+    if ($paynum) {
+      my $cust_pay = qsearchs('cust_pay',{ 'paynum' => $paynum });
+      die "Could not find paynum $paynum" unless $cust_pay;
+      $error = $cust_pay->refund(\%hash);
+    } else {
+      my $new = new FS::cust_refund ( \%hash );
+      $error = $new->insert;
+    }
+    # if not a batch refund run realtime.
+  } else {
+    $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund,
                                                   'paynum' => $paynum,
                                                   'reasonnum' => $reasonnum,
                                                   %options );
+  }
 } else {
   my %hash = map {
     $_, scalar($cgi->param($_))

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Schema.pm                         |  1 +
 FS/FS/cust_main.pm                      |  3 ++-
 FS/FS/cust_main/Billing_Realtime.pm     |  2 +-
 FS/FS/pay_batch/RBC.pm                  | 12 ++++++++-
 httemplate/edit/cust_refund.cgi         | 20 +++++++++++++-
 httemplate/edit/process/cust_refund.cgi | 46 ++++++++++++++++++++++++++++++---
 httemplate/misc/download-batch.cgi      | 11 +++++++-
 7 files changed, 87 insertions(+), 8 deletions(-)




More information about the freeside-commits mailing list