[freeside-commits] branch FREESIDE_4_BRANCH updated. 42b35cdaeb0643630eda0513556a8e668fbcb8e2

Christopher Burger burgerc at freeside.biz
Tue Jun 12 08:10:01 PDT 2018


The branch, FREESIDE_4_BRANCH has been updated
       via  42b35cdaeb0643630eda0513556a8e668fbcb8e2 (commit)
       via  8196faffdbc0abc43ce346119a76430c8a242b56 (commit)
      from  744bf508495023922d88a53fcd07467e3e01c2f1 (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 42b35cdaeb0643630eda0513556a8e668fbcb8e2
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:
            httemplate/misc/payment.cgi

diff --git a/FS/FS/cust_main/Billing_Batch.pm b/FS/FS/cust_main/Billing_Batch.pm
index 38d100ef6..35e2714b5 100644
--- a/FS/FS/cust_main/Billing_Batch.pm
+++ b/FS/FS/cust_main/Billing_Batch.pm
@@ -114,7 +114,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{$_});
   }
@@ -142,7 +142,7 @@ sub batch_card {
     'payname'  => $options{payname}  || $cust_payby->payname,
     'paytype'  => $options{paytype}  || $cust_payby->paytype,
     '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..27c4b1937 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -34,7 +34,7 @@
 %  }
 
   <BR>Payment
-  <% ntable("#cccccc", 2) %>
+  <TABLE class="fsinnerbox">
 
     <TR>
       <TD ALIGN="right">Amount</TD><TD BGCOLOR="#ffffff">$<% $cust_pay->paid %></TD>
@@ -85,7 +85,8 @@
 
 
 <BR>Refund
-<% ntable("#cccccc", 2) %>
+
+<TABLE class="fsinnerbox">
 
   <TR>
     <TD ALIGN="right">Date</TD>
@@ -102,9 +103,23 @@
       <TD ALIGN="right">Check #</TD>
       <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
     </TR>
+    </TABLE>
 % }
 %  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 +138,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 0a3d55036..77da8d5d2 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($_)) }
diff --git a/httemplate/misc/payment.cgi b/httemplate/elements/cust_payby_new.html
similarity index 59%
copy from httemplate/misc/payment.cgi
copy to httemplate/elements/cust_payby_new.html
index a6c6bac37..7ed049686 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/elements/cust_payby_new.html
@@ -1,139 +1,3 @@
-<& /elements/header-cust_main.html, view=>'payment_history', cust_main=>$cust_main &>
-
-<h2><% emt("Process [_1] payment",$type{$payby}) %></h2>
-
-<FORM NAME="OneTrueForm" ACTION="process/payment.cgi" METHOD="POST" onSubmit="document.OneTrueForm.process.disabled=true">
-<INPUT TYPE="hidden" NAME="custnum"   VALUE="<% $custnum %>">
-<INPUT TYPE="hidden" NAME="payby"     VALUE="<% $payby %>">
-<INPUT TYPE="hidden" NAME="payunique" VALUE="<% $payunique %>">
-<INPUT TYPE="hidden" NAME="balance"   VALUE="<% $balance %>">
-
-<& /elements/init_overlib.html &>
-
-<TABLE class="fsinnerbox">
-
-  <& /elements/tr-amount_fee.html,
-       'amount'             => $amount,
-       'process-pkgpart'    => 
-          scalar($conf->config('manual_process-pkgpart', $cust_main->agentnum)),
-       'process-display'    => scalar($conf->config('manual_process-display')),
-       'process-skip_first' => $conf->exists('manual_process-skip_first'),
-       'num_payments'       => scalar($cust_main->cust_pay), 
-       'surcharge_percentage' =>
-         ( $payby eq 'CARD'
-             ? scalar($conf->config('credit-card-surcharge-percentage', $cust_main->agentnum))
-             : 0
-         ),
-       'surcharge_flatfee' =>
-         ( $payby eq 'CARD'
-             ? scalar($conf->config('credit-card-surcharge-flatfee', $cust_main->agentnum))
-             : 0
-         ),
-  &>
-
-% if ( $conf->exists('part_pkg-term_discounts') ) {
-    <& /elements/tr-select-discount_term.html,
-         'custnum'   => $custnum,
-         'amount_id' => 'amount',
-    &>
-% }
-
-% my $disallow_no_auto_apply = 0;
-% if ( $conf->exists("batch-enable")
-%      || grep $payby eq $_, $conf->config('batch-enable_payby')
-%    ) {
-%
-%     if ( grep $payby eq $_, $conf->config('realtime-disable_payby') ) {
-%       $disallow_no_auto_apply = 1;
-
-          <INPUT TYPE="hidden" NAME="batch" VALUE="1">
-
-%     } else {
-
-          <TR>
-            <TH ALIGN="right">   <% mt('Add to current batch') |h %></TH>
-            <TD>
-              <INPUT TYPE="checkbox" NAME="batch" VALUE="1" ID="batch_checkbox" ONCHANGE="change_batch_checkbox()">
-            </TD>
-          </TR>
-
-%     }
-% }
-
-% unless ($disallow_no_auto_apply) {
-%   # false laziness with edit/cust_pay.cgi
-
-<TR ID="apply_box_row">
-  <TH ALIGN="right"><% mt('Auto-apply to invoices') |h %></TH>
-  <TD>
-    <SELECT NAME="apply" ID="apply_box">
-      <OPTION VALUE="yes" SELECTED><% mt('yes') |h %></OPTION> 
-      <OPTION VALUE=""><% mt('not now') |h %></OPTION>
-      <OPTION VALUE="never"><% mt('never') |h %></OPTION>
-    </SELECT>
-  </TD>
-</TR>
-
-% # this can go away if no_auto_apply handling gets added to batch payment processing
-<SCRIPT>
-function change_batch_checkbox () {
-  if (document.getElementById('batch_checkbox').checked) {
-    document.getElementById('apply_box').disabled = true;
-    document.getElementById('apply_box_row').style.display = 'none';
-  } else {
-    document.getElementById('apply_box').disabled = false;
-    document.getElementById('apply_box_row').style.display = '';
-  }
-}
-</SCRIPT>
-
-% }
-
-<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>
-
-% #can't quite handle CARD/CHEK on the same page yet, but very close
-% #does it make sense from a UI/usability perspective?
-%
-% 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)',
-&>
-
-</TABLE>
-<BR>
-<DIV ID="cust_payby"
-  <% $custpaybynum ? 'STYLE="display:none"'
-                   : ''
-  %>
->
-<TABLE class="fsinnerbox">
-
 % my $auto = 0;
 % if ( $payby eq 'CARD' ) {
 %
@@ -307,14 +171,6 @@ function change_batch_checkbox () {
   </TD>
 </TR>
 
-</TABLE>
-</DIV>
-
-<BR>
-<INPUT TYPE="submit" NAME="process" VALUE="<% mt('Process payment') |h %>">
-</FORM>
-
-<& /elements/footer-cust_main.html &>
 <%once>
 
 my %weight = (
@@ -328,10 +184,12 @@ my %weight = (
 );
 
 </%once>
+
 <%init>
 
-die "access denied"
-  unless $FS::CurrentUser::CurrentUser->access_right('Process payment');
+my %opt = @_;
+
+my @cust_payby = @{$opt{cust_payby}};
 
 my %type = ( 'CARD' => 'credit card',
              'CHEK' => 'electronic check (ACH)',
@@ -361,15 +219,4 @@ my %states = map { $_->state => 1 }
                } );
 my @states = sort { $a cmp $b } keys %states;
 
-my $amount = '';
-if ( $balance > 0 ) {
-  # when configured to do so, amount will only auto-fill with balance
-  # if balance represents a single invoice
-  $amount = $balance
-    unless $conf->exists('manual_process-single_invoice_amount')
-      && ($cust_main->open_cust_bill != 1);
-}
-
-my $payunique = "webui-payment-". time. "-$$-". rand() * 2**32;
-
-</%init>
+</%init>
\ No newline at end of file
diff --git a/httemplate/elements/tr-select-cust_payby.html b/httemplate/elements/tr-select-cust_payby.html
index e2b2e09d1..e5ace4d39 100644
--- a/httemplate/elements/tr-select-cust_payby.html
+++ b/httemplate/elements/tr-select-cust_payby.html
@@ -1,4 +1,4 @@
-% if ( scalar(@{ $opt{'cust_payby'} }) == 0 ) { 
+% if ( scalar(@{ $opt{'cust_payby'} }) == 0 ) {
 
   <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'custpaybynum' %>" VALUE="">
 
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index a6c6bac37..b170da27b 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -134,178 +134,10 @@ function change_batch_checkbox () {
 >
 <TABLE class="fsinnerbox">
 
-% my $auto = 0;
-% if ( $payby eq 'CARD' ) {
-%
-%   my( $payinfo, $paycvv, $month, $year ) = ( '', '', '', '' );
-%   my $payname = $cust_main->first. ' '. $cust_main->getfield('last');
-%   my $location = $cust_main->bill_location;
-
-    <TR>
-      <TH ALIGN="right"><% mt('Card number') |h %></TH>
-      <TD COLSPAN=7>
-        <TABLE>
-          <TR>
-            <TD>
-              <INPUT TYPE="text" NAME="payinfo" SIZE=20 MAXLENGTH=19 VALUE="<%$payinfo%>"> </TD>
-            <TH><% mt('Exp.') |h %></TH>
-            <TD>
-              <SELECT NAME="month">
-% for ( ( map "0$_", 1 .. 9 ), 10 .. 12 ) { 
-
-                  <OPTION<% $_ == $month ? ' SELECTED' : '' %>><% $_ %>
-% } 
-
-              </SELECT>
-            </TD>
-            <TD> / </TD>
-            <TD>
-              <SELECT NAME="year">
-% my @a = localtime; for ( $a[5]+1900 .. $a[5]+1915 ) { 
-
-                  <OPTION<% $_ == $year ? ' SELECTED' : '' %>><% $_ %>
-% } 
-
-              </SELECT>
-            </TD>
-          </TR>
-        </TABLE>
-      </TD>
-    </TR>
-    <TR>
-      <TH ALIGN="right"><% mt('CVV2') |h %></TH>
-      <TD><INPUT TYPE="text" NAME="paycvv" VALUE="<% $paycvv %>" SIZE=4 MAXLENGTH=4>
-          (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;"><% mt('help') |h %></A>)
-      </TD>
-    </TR>
-    <TR>
-      <TH ALIGN="right"><% mt('Exact name on card') |h %></TH>
-      <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%$payname%>"></TD>
-    </TR>
-
-    <& /elements/location.html,
-                  'object'         => $location,
-                  'no_asterisks'   => 1,
-                  'address1_label' => emt('Card billing address'),
-    &>
-
-% } elsif ( $payby eq 'CHEK' ) {
-%
-%   my( $account, $aba, $branch, $payname, $ss, $paytype, $paystate,
-%       $stateid, $stateid_state )
-%     = ( '', '', '', '', '', '', '', '', '' );
-%
-%  #false laziness w/{edit,view}/cust_main/billing.html
-%  my $routing_label = $conf->config('echeck-country') eq 'US'
-%                        ? 'ABA/Routing number'
-%                        : 'Routing number';
-%  my $routing_size      = $conf->config('echeck-country') eq 'CA' ? 4 : 10;
-%  my $routing_maxlength = $conf->config('echeck-country') eq 'CA' ? 3 : 9;
-
-    <INPUT TYPE="hidden" NAME="month" VALUE="12">
-    <INPUT TYPE="hidden" NAME="year" VALUE="2037">
-    <TR>
-      <TD ALIGN="right"><% mt('Account number') |h %></TD>
-      <TD><INPUT TYPE="text" SIZE=10 NAME="payinfo1" VALUE="<%$account%>"></TD>
-      <TD ALIGN="right"><% mt('Type') |h %></TD>
-      <TD><SELECT NAME="paytype"><% join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } FS::cust_payby->paytypes) %></SELECT></TD>
-    </TR>
-    <TR>
-      <TD ALIGN="right"><% mt($routing_label) |h %></TD>
-      <TD>
-        <INPUT TYPE="text" SIZE="<% $routing_size %>" MAXLENGTH="<% $routing_maxlength %>" NAME="payinfo2" VALUE="<%$aba%>">
-        (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;"><% mt('help') |h %></A>)
-      </TD>
-    </TR>
-%   if ( $conf->config('echeck-country') eq 'CA' ) {
-      <TR>
-        <TD ALIGN="right"><% mt('Branch number') |h %></TD>
-        <TD>
-          <INPUT TYPE="text" NAME="payinfo3" VALUE="<%$branch%>" SIZE=6 MAXLENGTH=5>
-        </TD>
-      </TR>
-%   }
-    <TR>
-      <TD ALIGN="right"><% mt('Bank name') |h %></TD>
-      <TD><INPUT TYPE="text" NAME="payname" VALUE="<%$payname%>"></TD>
-    </TR>
-
-%   if ( $conf->exists('show_bankstate') ) {
-      <TR>
-        <TD ALIGN="right"><% mt('Bank state') |h %></TD>
-        <TD><& /elements/select-state.html,
-                         'disable_empty' => 0,
-                         'empty_label'   => emt('(choose)'),
-                         'state'         => $paystate,
-                         'country'       => $cust_main->country,
-                         'prefix'        => 'pay',
-            &>
-        </TD>
-      </TR>
-%   } else {
-      <INPUT TYPE="hidden" NAME="paystate" VALUE="<% $paystate %>">
-%   }
-
-%   if ( $conf->exists('show_ss') ) {
-      <TR>
-        <TD ALIGN="right">
-          <% mt('Account holder') |h %><BR>
-          <% mt('Social security or tax ID #') |h %> 
-        </TD>
-        <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $ss %>"></TD>
-      </TR>
-%   } else {
-      <INPUT TYPE="hidden" NAME="ss" VALUE="<% $ss %>"></TD>
-%   }
-
-%   if ( $conf->exists('show_stateid') ) {
-      <TR>
-        <TD ALIGN="right">
-          <% mt('Account holder') |h %><BR>
-          <% mt("Driver's license or state ID #") |h %> 
-        </TD>
-        <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $stateid %>"></TD>
-        <TD ALIGN="right"><% mt('State') |h %></TD>
-        <TD><& /elements/select-state.html,
-                         'disable_empty' => 0,
-                         'empty_label'   => emt('(choose)'),
-                         'state'         => $stateid_state,
-                         'country'       => $cust_main->country,
-                         'prefix'        => 'stateid_',
-            &>
-        </TD>
-      </TR>
-%   } else {
-      <INPUT TYPE="hidden" NAME="stateid" VALUE="<% $stateid %>">
-      <INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $stateid_state %>">
-%   }
-
-% } #end CARD/CHEK-specific section
-
-
-<TR>
-  <TD COLSPAN=8>
-    <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
-    <% mt('Remember this information') |h %>
-  </TD>
-</TR>
-
-<TR>
-  <TD COLSPAN=8>
-    <INPUT TYPE="checkbox"<% $auto ? ' CHECKED' : '' %> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
-    <% mt("Charge future payments to this [_1] automatically",$type{$payby}) |h %> 
-% if ( @cust_payby ) {
-    <% mt('as') |h %>
-    <SELECT NAME="weight">
-%     for ( 1 .. 1+scalar(grep { $_->payby =~ /^(CARD|CHEK)$/ } @cust_payby) ) {
-        <OPTION VALUE="<%$_%>"><% mt( $weight{$_} ) |h %>
-%     }
-    </SELECT>
-% } else {
-    <INPUT TYPE="hidden" NAME="weight" VALUE="1">
-% }
-  </TD>
-</TR>
+<& /elements/cust_payby_new.html,
+     'cust_payby' => \@cust_payby,
+     'curr_value' => $custpaybynum,
+&>
 
 </TABLE>
 </DIV>
@@ -354,13 +186,6 @@ my $payinfo = '';
 
 my $conf = new FS::Conf;
 
-#false laziness w/selfservice make_payment.html shortcut for one-country
-my %states = map { $_->state => 1 }
-               qsearch('cust_main_county', {
-                 'country' => $conf->config('countrydefault') || 'US'
-               } );
-my @states = sort { $a cmp $b } keys %states;
-
 my $amount = '';
 if ( $balance > 0 ) {
   # when configured to do so, amount will only auto-fill with balance
diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi
index 717d57c85..5620b5b4b 100644
--- a/httemplate/misc/process/payment.cgi
+++ b/httemplate/misc/process/payment.cgi
@@ -90,6 +90,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 {
 
@@ -208,6 +209,10 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
 
 my $error = '';
 my $paynum = '';
+my $paydate;
+if ($cust_payby->paydate) { $paydate = "$year-$month-01"; }
+else { $paydate = "2037-12-01"; }
+
 if ( $cgi->param('batch') ) {
 
   $error = 'Prepayment discounts not supported with batched payments' 
@@ -217,7 +222,7 @@ if ( $cgi->param('batch') ) {
                                      'payby'    => $payby,
                                      'amount'   => $amount,
                                      'payinfo'  => $payinfo,
-                                     'paydate'  => "$year-$month-01",
+                                     'paydate'  => $paydate,
                                      'payname'  => $payname,
                                      map { $_ => scalar($cgi->param($_)) } 
                                        @{$payby2fields{$payby}}

commit 8196faffdbc0abc43ce346119a76430c8a242b56
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.

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 1ee70939e..22fa395f2 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2715,6 +2715,7 @@ sub tables_hashref {
         'status',        'varchar', 'NULL', $char_d, '', '', 
         'failure_status','varchar', 'NULL',      16, '', '',
         'error_message', 'varchar', 'NULL', $char_d, '', '',
+        'paycode',       'varchar', 'NULL', $char_d, '', '',
       ],
       'primary_key'  => 'paybatchnum',
       'unique'       => [],
diff --git a/FS/FS/cust_main/Billing_Batch.pm b/FS/FS/cust_main/Billing_Batch.pm
index 48854d3ed..38d100ef6 100644
--- a/FS/FS/cust_main/Billing_Batch.pm
+++ b/FS/FS/cust_main/Billing_Batch.pm
@@ -114,7 +114,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{$_});
   }
@@ -142,6 +142,7 @@ sub batch_card {
     'payname'  => $options{payname}  || $cust_payby->payname,
     'paytype'  => $options{paytype}  || $cust_payby->paytype,
     '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 18a61706e..dea89cde1 100644
--- a/FS/FS/pay_batch/RBC.pm
+++ b/FS/FS/pay_batch/RBC.pm
@@ -178,8 +178,13 @@ $name = 'RBC';
     my $custname = $cust_pay_batch->cust_main->batch_payment_payname($cust_pay_batch);
 
     $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 5a4b3271f..0a3d55036 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' => scalar($cgi->param('reasonnum')),
                                                   %options );
+  }
 } else {
   my %hash = map {
     $_, scalar($cgi->param($_))
diff --git a/httemplate/view/cust_main/menu.html b/httemplate/view/cust_main/menu.html
index dcba1fb00..f3aca21e8 100644
--- a/httemplate/view/cust_main/menu.html
+++ b/httemplate/view/cust_main/menu.html
@@ -459,6 +459,13 @@ my @menu = (
         #  acl         => ['Post refund' ],
         ##  condition   => sub { $payby{MCHK} },
         #},
+        {
+           label       => 'Batch Electronic check refund',
+           popup       => "edit/cust_refund.cgi?popup=1;payby=CHEK;custnum=$custnum",
+           actionlabel => 'Enter electronic check refund',
+           width       => 440,
+           acl         => ['Post refund' ],
+        },
 
       ],
 

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

Summary of changes:
 FS/FS/Schema.pm                                    |   1 +
 FS/FS/cust_main/Billing_Batch.pm                   |   3 +-
 FS/FS/pay_batch/RBC.pm                             |   7 +-
 httemplate/edit/cust_refund.cgi                    |  63 ++++++-
 httemplate/edit/process/cust_refund.cgi            | 185 ++++++++++++++++++++-
 .../payment.cgi => elements/cust_payby_new.html}   | 163 +-----------------
 httemplate/elements/tr-select-cust_payby.html      |   2 +-
 httemplate/misc/payment.cgi                        | 183 +-------------------
 httemplate/misc/process/payment.cgi                |   7 +-
 httemplate/view/cust_main/menu.html                |   7 +
 10 files changed, 275 insertions(+), 346 deletions(-)
 copy httemplate/{misc/payment.cgi => elements/cust_payby_new.html} (59%)




More information about the freeside-commits mailing list