[freeside-commits] branch master updated. 3a742395d79d0d6712e5111320c4780fd172b870

Mark Wells mark at 420.am
Tue Nov 3 12:28:29 PST 2015


The branch, master has been updated
       via  3a742395d79d0d6712e5111320c4780fd172b870 (commit)
       via  b5d5f7680d0736ff0150b337cd29026135fb2e34 (commit)
       via  1ef811033e1ea791388c7c8d15a3a5a720b1f37f (commit)
       via  9170a3f9609625af49cd9e20003948713389c634 (commit)
       via  c01c0065f9de3428af812638f6e3bb9e02fb725f (commit)
       via  6d34c5060a4e5e9338ebc0d04459861a5c45e812 (commit)
       via  676a117afd9d1091dcf425cc894593beda54e78c (commit)
       via  55c7e3cc18a45620f48ae62d3bc044a830bd8c95 (commit)
      from  a87e91ee19d14a6fc3da62f4b44a9628d32bb6a3 (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 3a742395d79d0d6712e5111320c4780fd172b870
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Nov 3 12:10:52 2015 -0800

    cleanup for access rights

diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 95cf29a..a96a6cb 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -232,7 +232,7 @@ tie my %rights, 'Tie::IxHash',
     'Refund Echeck payment',
     'Delete refund', #NEW
     'Add on-the-fly credit reason', #NEW
-    'Add on-the-fly void credit reason',
+    'Add on-the-fly void reason',
     'Add on-the-fly refund reason', #NEW
   ],
   
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index 40475ec..0da718c 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -251,9 +251,7 @@ sub _upgrade_data { # class method
     'List customers' => 'List contacts',
     'Backdate payment' => 'Backdate credit',
     'Generate quotation' => 'Disable quotation',
-    'Void credit' => 'Void credit',
-    'Unvoid credit' => 'Unvoid credit',
-    'Add on-the-fly void credit reason' => 'Add on-the-fly void credit reason',
+    'Add on-the-fly void credit reason' => 'Add on-the-fly void reason',
     '_ALL' => 'Employee preference telephony integration',
   );
 
diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm
index 50df495..4042972 100644
--- a/FS/FS/reason_type.pm
+++ b/FS/FS/reason_type.pm
@@ -19,7 +19,7 @@ our %class_purpose = (
   'R' => 'explain why a customer was credited',
   'S' => 'explain why a customer package was suspended',
   'F' => 'explain why a customer was refunded',
-  'X' => 'explain why a credit was voided',
+  'X' => 'explain why a transaction was voided',
 );
 
 =head1 NAME

commit b5d5f7680d0736ff0150b337cd29026135fb2e34
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Thu Oct 8 15:45:09 2015 -0600

    Changes for being consistent in the payment history GUI on void credit /
    payment / invoice links
    
    renamed httemplate/misc/void-cust_credit.html to .cgi
    renamed httemplate/misc/void-cust_bill.html to .cgi

diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
index 1258746..37a34ba 100755
--- a/httemplate/elements/tr-select-reason.html
+++ b/httemplate/elements/tr-select-reason.html
@@ -199,7 +199,7 @@ if ($class eq 'C') {
 } elsif ($class eq 'F') {
   $add_access_right = 'Add on-the-fly refund reason';
 } elsif ($class eq 'X') {
-  $add_access_right = 'Add on-the-fly void credit reason';
+  $add_access_right = 'Add on-the-fly void reason';
 } else {
   die "illegal class: $class";
 }
diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html
index 7773b0b..c0f432b 100755
--- a/httemplate/misc/process/void-cust_bill.html
+++ b/httemplate/misc/process/void-cust_bill.html
@@ -1,6 +1,6 @@
 %if ( $error ) {
 %  $cgi->param('error', $error);
-<% $cgi->redirect(popurl(2). "void-cust_bill.html?". $cgi->query_string ) %>
+<% $cgi->redirect(popurl(2). "void-cust_bill.cgi?". $cgi->query_string ) %>
 %} else {
 <& /elements/header-popup.html, 'Invoice voided' &>
 <SCRIPT TYPE="text/javascript">
diff --git a/httemplate/misc/void-cust_bill.cgi b/httemplate/misc/void-cust_bill.cgi
new file mode 100755
index 0000000..213cf95
--- /dev/null
+++ b/httemplate/misc/void-cust_bill.cgi
@@ -0,0 +1,46 @@
+<& /elements/header-popup.html, mt('Void invoice') &>
+
+<% include('/elements/error.html') %>
+
+<% emt('Are you sure you want to void this invoice?') %>
+<BR><BR>
+
+<% emt("Invoice #[_1] ([_2])",$cust_bill->display_invnum, $money_char. $cust_bill->owed) %>
+<BR><BR>
+
+<FORM METHOD="POST" ACTION="process/void-cust_bill.html">
+<INPUT TYPE="hidden" NAME="invnum" VALUE="<% $invnum %>">
+
+<% ntable("#cccccc", 2) %>
+<& /elements/tr-select-reason.html,
+             'field'          => 'reasonnum',
+             'reason_class'   => 'X',
+             'cgi'            => $cgi
+&>
+
+</TABLE>
+
+<BR>
+<CENTER>
+<BUTTON TYPE="submit">Yes, void invoice</BUTTON>   \
+<BUTTON TYPE="button" onClick="parent.cClick();">No, do not void invoice</BUTTON>
+</CENTER>
+
+</FORM>
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Void invoices');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+#untaint invnum
+$cgi->param('invnum') =~ /^(\d+)$/ || die "Illegal invnum";
+my $invnum = $1;
+
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+
+</%init>
diff --git a/httemplate/misc/void-cust_credit.html b/httemplate/misc/void-cust_credit.cgi
similarity index 97%
rename from httemplate/misc/void-cust_credit.html
rename to httemplate/misc/void-cust_credit.cgi
index 1e71f00..f352e60 100755
--- a/httemplate/misc/void-cust_credit.html
+++ b/httemplate/misc/void-cust_credit.cgi
@@ -12,7 +12,7 @@
 
 <P ALIGN="center"><B><% mt('Void this credit?') |h %></B>
 
-<FORM action="<% ${p} %>misc/void-cust_credit.html">
+<FORM action="<% ${p} %>misc/void-cust_credit.cgi">
 <INPUT TYPE="hidden" NAME="crednum" VALUE="<% $crednum %>">
 
 <TABLE BGCOLOR="#cccccc" BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto">
diff --git a/httemplate/misc/void-cust_pay.cgi b/httemplate/misc/void-cust_pay.cgi
index 376fb56..784bb9a 100755
--- a/httemplate/misc/void-cust_pay.cgi
+++ b/httemplate/misc/void-cust_pay.cgi
@@ -1,16 +1,52 @@
-%if ( $error ) {
-%  errorpage($error);
+%if ( $success ) {
+<& /elements/header-popup.html, mt("Payment voided") &>
+  <SCRIPT TYPE="text/javascript">
+    window.top.location.reload();
+  </SCRIPT>
+  </BODY>
+</HTML>
 %} else {
-<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum. ";show=payment_history") %>
+<& /elements/header-popup.html, mt('Void payment')  &>
+
+<& /elements/error.html &>
+
+<P ALIGN="center"><B><% mt('Void this payment?') |h %></B>
+
+<FORM action="<% ${p} %>misc/void-cust_pay.cgi">
+<INPUT TYPE="hidden" NAME="paynum" VALUE="<% $paynum %>">
+
+<TABLE BGCOLOR="#cccccc" BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto">
+<& /elements/tr-select-reason.html,
+             'field'          => 'reasonnum',
+             'reason_class'   => 'X',
+             'cgi'            => $cgi
+&>
+</TABLE>
+
+<BR>
+<P ALIGN="CENTER">
+<INPUT TYPE="submit" NAME="confirm_void_payment" VALUE="<% mt('Void payment') |h %>"> 
+        
+<INPUT TYPE="BUTTON" VALUE="<% mt("Don't void payment") |h %>" onClick="parent.cClick();"> 
+
+</FORM>
+</BODY>
+</HTML>
+
 %}
 <%init>
 
 #untaint paynum
-my($query) = $cgi->keywords;
-$query =~ /^(\d+)$/ || die "Illegal paynum";
-my $paynum = $1;
+my $paynum = $cgi->param('paynum');
+if ($paynum) {
+  $paynum =~ /^(\d+)$/ || die "Illegal paynum";
+} else {
+  my($query) = $cgi->keywords;
+  $query =~ /^(\d+)/ || die "Illegal paynum";
+  $paynum = $1;
+}
 
-my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum});
+my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum}) || die "Payment not found";
 
 my $right = 'Void payments';
 $right = 'Credit card void' if $cust_pay->payby eq 'CARD';
@@ -19,8 +55,24 @@ $right = 'Echeck void'      if $cust_pay->payby eq 'CHEK';
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right($right);
 
-my $custnum = $cust_pay->custnum;
+my $success = 0;
+if ($cgi->param('confirm_void_payment')) {
+
+  #untaint reasonnum / create new reason
+  my ($reasonnum, $error) = $m->comp('process/elements/reason');
+  if (!$reasonnum) {
+    $error = 'Reason required';
+  } else {
+    my $reason = qsearchs('reason', { 'reasonnum' => $reasonnum })
+      || die "Reason num $reasonnum not found in database";
+	$error = $cust_pay->void($reason) unless $error;
+  }
 
-my $error = $cust_pay->void;
+  if ($error) {
+    $cgi->param('error',$error);
+  } else {
+    $success = 1;
+  }
+}
 
 </%init>
diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi
index 8884dde..6d13974 100755
--- a/httemplate/view/cust_bill.cgi
+++ b/httemplate/view/cust_bill.cgi
@@ -9,13 +9,30 @@ function areyousure(href, message) {
 }
 </SCRIPT>
 
-% if ( !$cust_bill->closed && $curuser->access_right('Void invoices') ) {
+% if ( !$cust_bill->closed ) { # otherwise allow no changes
+%   my $can_delete = $conf->exists('deleteinvoices')
+%                    && $curuser->access_right('Delete invoices');
+%   my $can_void = $curuser->access_right('Void invoices');
+%   if ( $can_void ) {
     <& /elements/popup_link.html,
       'label'       => emt('Void this invoice'),
       'actionlabel' => emt('Void this invoice'),
-      'action'      => $p.'misc/void-cust_bill.html?invnum='.$invnum,
+      'action'      => $p.'misc/void-cust_bill.cgi?invnum='.$invnum,
     &>
-    <BR><BR>
+%   }
+%   if ( $can_void and $can_delete ) {
+   | 
+%   }
+%   if ( $can_delete ) {
+    <A href="" onclick="areyousure(\
+      '<%$p%>misc/delete-cust_bill.html?<% $invnum %>',\
+      <% mt('Are you sure you want to delete this invoice?') |js_string %>)"\
+    TITLE = "<% mt('Delete this invoice from the database completely') |h %>">\
+    <% emt('Delete this invoice') |h %></A>
+%   }
+%   if ( $can_void or $can_delete ) {
+  <BR><BR>
+%   }
 % }
 
 % if ( $cust_bill->owed > 0
diff --git a/httemplate/view/cust_main/payment_history/credit.html b/httemplate/view/cust_main/payment_history/credit.html
index db2e5e5..81be1cd 100644
--- a/httemplate/view/cust_main/payment_history/credit.html
+++ b/httemplate/view/cust_main/payment_history/credit.html
@@ -130,7 +130,7 @@ my $void = '';
 $void = ' ('.
                include( '/elements/popup_link.html',
                     'label'    => emt('void'),
-                    'action'   => "${p}misc/void-cust_credit.html?".
+                    'action'   => "${p}misc/void-cust_credit.cgi?".
                                   $cust_credit->crednum,
                     'actionlabel' => emt('Void credit'),
                 ).
diff --git a/httemplate/view/cust_main/payment_history/invoice.html b/httemplate/view/cust_main/payment_history/invoice.html
index 613936e..ca59c15 100644
--- a/httemplate/view/cust_main/payment_history/invoice.html
+++ b/httemplate/view/cust_main/payment_history/invoice.html
@@ -27,7 +27,7 @@ if ( $cust_bill->closed !~ /^Y/i && $opt{'Void invoices'} ) {
   $void =
     ' ('. include('/elements/popup_link.html',
                     'label'     => emt('void'),
-                    'action'    => "${p}misc/void-cust_bill.html?;invnum=".
+                    'action'    => "${p}misc/void-cust_bill.cgi?;invnum=".
                                     $cust_bill->invnum,
                     'actionlabel' => emt('Void Invoice'),
                  ).
diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html
index d72e34b..7701314 100644
--- a/httemplate/view/cust_main/payment_history/payment.html
+++ b/httemplate/view/cust_main/payment_history/payment.html
@@ -169,8 +169,9 @@ if (    $cust_pay->closed !~ /^Y/i
      && scalar(@refund_right)
 ) {
   my $refundtitle = ($cust_pay->payby =~ /^(CARD|CHEK)$/)
-            ? emt('Send a refund for this payment to the payment gateway')
-            : emt('Record a refund for this payment');
+           ? emt('Send a refund for this payment to the payment gateway')
+           : emt('Record a refund for this payment');
+
   $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
             qq!paynum=!. $cust_pay->paynum. '"'.
             qq! TITLE="! . $refundtitle
@@ -178,14 +179,17 @@ if (    $cust_pay->closed !~ /^Y/i
 }
 
 my $void = '';
-my $voidmsg = $cust_pay->payby =~ /^(CARD|CHEK)$/
+# note: "TOKN" is not yet supported in stock freeside
+my $voidmsg = $cust_pay->payby =~ /^(CARD|CHEK|TOKN)$/
               ? ' (' . emt('do not send anything to the payment gateway').')'
               : '';
-$void = areyousure_link("${p}misc/void-cust_pay.cgi?".$cust_pay->paynum,
-                        emt('Are you sure you want to void this payment?'),
-                        emt('Void this payment from the database') . $voidmsg,
-                        emt('void')
-                       )
+$void = ' ('.
+               include( '/elements/popup_link.html',
+                    'label'    => emt('void'),
+                    'action'   => "${p}misc/void-cust_pay.cgi?".$cust_pay->paynum,
+                    'actionlabel' => emt('Void payment'),
+                ).
+          ')'
   if $cust_pay->closed !~ /^Y/i
   && (    ( $cust_pay->payby eq 'CARD'          && $opt{'Credit card void'} )
        || ( $cust_pay->payby eq 'CHEK'          && $opt{'Echeck void'}      )

commit 1ef811033e1ea791388c7c8d15a3a5a720b1f37f
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Thu Oct 8 12:03:08 2015 -0600

    Tables added to the list with data upgrade because of reason and void_reason
    legacy fields:
      - cust_credit_void
      - cust_bill_void
      - cust_bill_pkg_void
    
    Made some changes to reason_Mixin to be able to upgrade reason an void_reason
    fields

diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index 263230b..bffda56 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -350,6 +350,11 @@ sub upgrade_data {
     #customer credits
     'cust_credit' => [],
 
+    # reason / void_reason migration to reasonnum / void_reasonnum
+    'cust_credit_void' => [],
+    'cust_bill_void' => [],
+    'cust_bill_pkg_void' => [],
+
     #duplicate history records
     'h_cust_svc'  => [],
 
diff --git a/FS/FS/cust_bill_pkg_void.pm b/FS/FS/cust_bill_pkg_void.pm
index 4b9cffd..991dd37 100644
--- a/FS/FS/cust_bill_pkg_void.pm
+++ b/FS/FS/cust_bill_pkg_void.pm
@@ -2,6 +2,7 @@ package FS::cust_bill_pkg_void;
 use base qw( FS::TemplateItem_Mixin FS::reason_Mixin FS::Record );
 
 use strict;
+use vars qw( $me $DEBUG );
 use FS::Record qw( qsearch qsearchs dbh fields );
 use FS::cust_bill_void;
 use FS::cust_bill_pkg_detail;
@@ -13,6 +14,9 @@ use FS::cust_bill_pkg_tax_location;
 use FS::cust_bill_pkg_tax_rate_location;
 use FS::cust_tax_exempt_pkg;
 
+$me = '[ FS::cust_bill_pkg_void ]';
+$DEBUG = 0;
+
 =head1 NAME
 
 FS::cust_bill_pkg_void - Object methods for cust_bill_pkg_void records
@@ -279,6 +283,18 @@ sub cust_bill_pkg_fee {
   qsearch( 'cust_bill_pkg_fee_void', { 'billpkgnum' => $self->billpkgnum } );
 }
 
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+sub _upgrade_data {  # class method
+  my ($class, %opts) = @_;
+
+  warn "$me upgrading $class\n" if $DEBUG;
+
+  $class->_upgrade_reasonnum(%opts);
+}
+
 =back
 
 =head1 BUGS
diff --git a/FS/FS/cust_bill_void.pm b/FS/FS/cust_bill_void.pm
index 04c69d4..50f69c9 100644
--- a/FS/FS/cust_bill_void.pm
+++ b/FS/FS/cust_bill_void.pm
@@ -3,12 +3,16 @@ use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin
              FS::reason_Mixin FS::Record );
 
 use strict;
+use vars qw( $me $DEBUG );
 use FS::Record qw( qsearch qsearchs dbh fields );
 use FS::cust_statement;
 use FS::access_user;
 use FS::cust_bill_pkg_void;
 use FS::cust_bill;
 
+$me = '[ FS::cust_bill_void ]';
+$DEBUG = 0;
+
 =head1 NAME
 
 FS::cust_bill_void - Object methods for cust_bill_void records
@@ -349,6 +353,17 @@ sub search_sql_where {
 
 sub enable_previous { 0 }
 
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+sub _upgrade_data {  # class method
+  my ($class, %opts) = @_;
+
+  warn "$me upgrading $class\n" if $DEBUG;
+
+  $class->_upgrade_reasonnum(%opts);
+}
+
 =back
 
 =head1 BUGS
diff --git a/FS/FS/cust_credit_void.pm b/FS/FS/cust_credit_void.pm
index 9c92068..60beaa6 100644
--- a/FS/FS/cust_credit_void.pm
+++ b/FS/FS/cust_credit_void.pm
@@ -2,12 +2,16 @@ package FS::cust_credit_void;
 use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::reason_Mixin FS::Record );
 
 use strict;
+use vars qw( $me $DEBUG );
 use FS::Record qw(qsearchs); # qsearch qsearchs);
 use FS::CurrentUser;
 use FS::access_user;
 use FS::cust_credit;
 use FS::UID qw( dbh );
 
+$me = '[ FS::cust_credit_void ]';
+$DEBUG = 0;
+
 =head1 NAME
 
 FS::cust_credit_void - Object methods for cust_credit_void objects
@@ -190,6 +194,17 @@ sub void_reason {
   return $reason_text;
 }
 
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+sub _upgrade_data {    # class method
+    my ( $class, %opts ) = @_;
+
+    warn "$me upgrading $class\n" if $DEBUG;
+
+    $class->_upgrade_reasonnum(%opts);
+}
+
 =back
 
 =head1 BUGS
diff --git a/FS/FS/reason_Mixin.pm b/FS/FS/reason_Mixin.pm
index a4c2d3f..af9aa50 100644
--- a/FS/FS/reason_Mixin.pm
+++ b/FS/FS/reason_Mixin.pm
@@ -39,52 +39,64 @@ sub reason {
 # FS::reason->new_or_existing
  
 # Used by FS::Upgrade to migrate reason text fields to reasonnum.
-sub _upgrade_reasonnum {  # class method
-  my $class = shift;
-  my $table = $class->table;
-
-  if (   defined dbdef->table($table)->column('reason')
-      && defined dbdef->table($table)->column('reasonnum') )
-  {
-
-    warn "$me Checking for unmigrated reasons\n" if $DEBUG;
+sub _upgrade_reasonnum {    # class method
+    my $class = shift;
+    my $table = $class->table;
 
-    my @legacy_reason_records = qsearch(
+    for my $fieldname (qw(reason void_reason)) {
+        if (   defined dbdef->table($table)->column($fieldname)
+            && defined dbdef->table($table)->column( $fieldname . 'num' ) )
         {
-            'table'     => $table,
-            'hashref'   => {},
-            'extra_sql' => 'WHERE reason IS NOT NULL',
+
+            warn "$me Checking for unmigrated reasons\n" if $DEBUG;
+
+            my @legacy_reason_records = qsearch(
+                {
+                    'table'     => $table,
+                    'hashref'   => {},
+                    'extra_sql' => 'WHERE ' . $fieldname . ' IS NOT NULL',
+                }
+            );
+
+            if (
+                scalar(
+                    grep { $_->getfield($fieldname) =~ /\S/ }
+                      @legacy_reason_records
+                )
+              )
+            {
+                warn "$me Found unmigrated reasons\n" if $DEBUG;
+
+                my $reason_type =
+                  _upgrade_get_legacy_reason_type( $class, $table );
+                my $noreason = _upgrade_get_no_reason( $class, $reason_type );
+
+                foreach my $record_to_upgrade (@legacy_reason_records) {
+                    my $reason = $record_to_upgrade->getfield($fieldname);
+                    warn "Contemplating reason $reason\n" if $DEBUG > 1;
+                    if ( $reason =~ /\S/ ) {
+                        my $reason =
+                          _upgrade_get_reason( $class, $reason, $reason_type );
+                        $record_to_upgrade->set( $fieldname . 'num',
+                            $reason->reasonnum );
+                    }
+                    else {
+                        $record_to_upgrade->set( $fieldname . 'num',
+                            $noreason->reasonnum );
+                    }
+
+                    $record_to_upgrade->setfield( $fieldname, '' );
+                    my $error = $record_to_upgrade->replace;
+
+                    my $primary_key = $record_to_upgrade->primary_key;
+                    warn "*** WARNING: error replacing $fieldname in $class "
+                      . $record_to_upgrade->get($primary_key)
+                      . ": $error ***\n"
+                      if $error;
+                }
+            }
         }
-    );
-
-    if (scalar(grep { $_->getfield('reason') =~ /\S/ } @legacy_reason_records)) {
-      warn "$me Found unmigrated reasons\n" if $DEBUG;
-
-      my $reason_type = _upgrade_get_legacy_reason_type($class, $table);
-      my $noreason = _upgrade_get_no_reason($class, $reason_type);
-
-      foreach my $record_to_upgrade (@legacy_reason_records) {
-          my $reason = $record_to_upgrade->getfield('reason');
-          warn "Contemplating reason $reason\n" if $DEBUG > 1;
-          if ( $reason =~ /\S/ ) {
-              my $reason = _upgrade_get_reason( $class, $reason, $reason_type );
-              $record_to_upgrade->reasonnum( $reason->reasonnum );
-          }
-          else {
-              $record_to_upgrade->reasonnum( $noreason->reasonnum );
-          }
-
-          $record_to_upgrade->setfield( 'reason', '' );
-          my $error = $record_to_upgrade->replace;
-
-          my $primary_key = $record_to_upgrade->primary_key;
-          warn "*** WARNING: error replacing reason in $class "
-            . $record_to_upgrade->get($primary_key)
-            . ": $error ***\n"
-            if $error;
-       }
     }
-  }
 }
 
 # _upgrade_get_legacy_reason_type is class method supposed to be used only

commit 9170a3f9609625af49cd9e20003948713389c634
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Thu Oct 8 09:02:22 2015 -0600

    Changes in FS::reason_Mixin for converting legacy reasons to FS::reason. There
    was a bug where if the $value was passed the reason_Mixin::reason will just
    ignore it and this was deleting legacy reasons instead of converting them.

diff --git a/FS/FS/reason_Mixin.pm b/FS/FS/reason_Mixin.pm
index a397541..a4c2d3f 100644
--- a/FS/FS/reason_Mixin.pm
+++ b/FS/FS/reason_Mixin.pm
@@ -6,18 +6,21 @@ use FS::Record qw( qsearch qsearchs dbdef );
 use FS::access_user;
 use FS::UID qw( dbh );
 use FS::reason;
+use FS::reason_type;
 
 our $DEBUG = 0;
 our $me = '[FS::reason_Mixin]';
 
 =item reason
 
-Returns the text of the associated reason (see L<FS::reason>) for this credit.
+Returns the text of the associated reason (see L<FS::reason>) for this credit /
+voided payment / voided invoice.
 
 =cut
 
 sub reason {
-  my ($self, $value, %options) = @_;
+  my $self = shift;
+
   my $reason_text;
   if ( $self->reasonnum ) {
     my $reason = FS::reason->by_key($self->reasonnum);
@@ -40,57 +43,101 @@ sub _upgrade_reasonnum {  # class method
   my $class = shift;
   my $table = $class->table;
 
-  if (defined dbdef->table($table)->column('reason')) {
+  if (   defined dbdef->table($table)->column('reason')
+      && defined dbdef->table($table)->column('reasonnum') )
+  {
 
     warn "$me Checking for unmigrated reasons\n" if $DEBUG;
 
-    my @cust_refunds = qsearch({ 'table'     => $table,
-                                 'hashref'   => {},
-                                 'extra_sql' => 'WHERE reason IS NOT NULL',
-                              });
-
-    if (scalar(grep { $_->getfield('reason') =~ /\S/ } @cust_refunds)) {
-      warn "$me Found unmigrated reasons\n" if $DEBUG;
-      my $hashref = { 'class' => 'F', 'type' => 'Legacy' };
-      my $reason_type = qsearchs( 'reason_type', $hashref );
-      unless ($reason_type) {
-        $reason_type  = new FS::reason_type( $hashref );
-        my $error   = $reason_type->insert();
-        die "$class had error inserting FS::reason_type into database: $error\n"
-          if $error;
-      }
-
-      $hashref = { 'reason_type' => $reason_type->typenum,
-                   'reason' => '(none)'
-                 };
-      my $noreason = qsearchs( 'reason', $hashref );
-      unless ($noreason) {
-        $hashref->{'disabled'} = 'Y';
-        $noreason = new FS::reason( $hashref );
-        my $error  = $noreason->insert();
-        die "can't insert legacy reason '(none)' into database: $error\n"
-          if $error;
-      }
-
-      foreach my $cust_refund ( @cust_refunds ) {
-        my $reason = $cust_refund->getfield('reason');
-        warn "Contemplating reason $reason\n" if $DEBUG > 1;
-        if ($reason =~ /\S/) {
-          $cust_refund->reason($reason, 'reason_type' => $reason_type->typenum)
-            or die "can't insert legacy reason $reason into database\n";
-        }else{
-          $cust_refund->reasonnum($noreason->reasonnum);
+    my @legacy_reason_records = qsearch(
+        {
+            'table'     => $table,
+            'hashref'   => {},
+            'extra_sql' => 'WHERE reason IS NOT NULL',
         }
+    );
 
-        $cust_refund->setfield('reason', '');
-        my $error = $cust_refund->replace;
+    if (scalar(grep { $_->getfield('reason') =~ /\S/ } @legacy_reason_records)) {
+      warn "$me Found unmigrated reasons\n" if $DEBUG;
 
-        warn "*** WARNING: error replacing reason in $class ".
-             $cust_refund->refundnum. ": $error ***\n"
-          if $error;
-      }
+      my $reason_type = _upgrade_get_legacy_reason_type($class, $table);
+      my $noreason = _upgrade_get_no_reason($class, $reason_type);
+
+      foreach my $record_to_upgrade (@legacy_reason_records) {
+          my $reason = $record_to_upgrade->getfield('reason');
+          warn "Contemplating reason $reason\n" if $DEBUG > 1;
+          if ( $reason =~ /\S/ ) {
+              my $reason = _upgrade_get_reason( $class, $reason, $reason_type );
+              $record_to_upgrade->reasonnum( $reason->reasonnum );
+          }
+          else {
+              $record_to_upgrade->reasonnum( $noreason->reasonnum );
+          }
+
+          $record_to_upgrade->setfield( 'reason', '' );
+          my $error = $record_to_upgrade->replace;
+
+          my $primary_key = $record_to_upgrade->primary_key;
+          warn "*** WARNING: error replacing reason in $class "
+            . $record_to_upgrade->get($primary_key)
+            . ": $error ***\n"
+            if $error;
+       }
     }
   }
 }
 
+# _upgrade_get_legacy_reason_type is class method supposed to be used only
+# within the reason_Mixin class which will either find or create a reason_type
+sub _upgrade_get_legacy_reason_type {
+ 
+    my $class = shift;
+    my $table = shift;
+
+    my $reason_class =
+      ( $table =~ /void/ ) ? 'X' : 'F';    # see FS::reason_type (%class_name)
+    my $reason_type_params = { 'class' => $reason_class, 'type' => 'Legacy' };
+    my $reason_type = qsearchs( 'reason_type', $reason_type_params );
+    unless ($reason_type) {
+        $reason_type = new FS::reason_type($reason_type_params);
+        my $error = $reason_type->insert();
+        die "$class had error inserting FS::reason_type into database: $error\n"
+           if $error;
+    }
+    return $reason_type;
+}
+
+# _upgrade_get_no_reason is class method supposed to be used only within the
+# reason_Mixin class which will either find or create a default (no reason)
+# reason
+sub _upgrade_get_no_reason {
+
+    my $class       = shift;
+    my $reason_type = shift;
+    return _upgrade_get_reason( $class, '(none)', $reason_type );
+}
+
+# _upgrade_get_reason is class method supposed to be used only within the
+# reason_Mixin class which will either find or create a reason
+sub _upgrade_get_reason {
+
+    my $class       = shift;
+    my $reason_text = shift;
+    my $reason_type = shift;
+
+    my $reason_params = {
+        'reason_type' => $reason_type->typenum,
+        'reason'      => $reason_text
+    };
+    my $reason = qsearchs( 'reason', $reason_params );
+    unless ($reason) {
+        $reason_params->{'disabled'} = 'Y';
+        $reason = new FS::reason($reason_params);
+        my $error = $reason->insert();
+        die "can't insert legacy reason '$reason_text' into database: $error\n"
+           if $error;
+     }
+    return $reason;
+}
+
 1;

commit c01c0065f9de3428af812638f6e3bb9e02fb725f
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Wed Sep 30 14:37:18 2015 -0600

    When void / unvoid / apply / unapply is called stay on the same page
    (payment_history)
    
    Keep consistent void credits with other void methods (invoices, payments)
    regarding the method interface
    
        cust_credit->void
        cust_credit->void('reason string')
        cust_credit->void(FS::reason)

diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 2f2338e..0329535 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -377,7 +377,7 @@ sub void {
   my $cust_credit_void = new FS::cust_credit_void ( {
       map { $_ => $self->get($_) } $self->fields
     } );
-  $cust_credit_void->set('void_reasonnum', $reason->reasonnum);
+  $cust_credit_void->set('void_reasonnum', $reason->reasonnum) if $reason;
   my $error = $cust_credit_void->insert;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
diff --git a/httemplate/misc/unapply-cust_credit.cgi b/httemplate/misc/unapply-cust_credit.cgi
index ed739ac..aa1a3a9 100755
--- a/httemplate/misc/unapply-cust_credit.cgi
+++ b/httemplate/misc/unapply-cust_credit.cgi
@@ -1,4 +1,4 @@
-<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum. ";show=payment_history") %>
 <%init>
 
 die "access denied"
diff --git a/httemplate/misc/unapply-cust_pay.cgi b/httemplate/misc/unapply-cust_pay.cgi
index b0343d0..34c1ecf 100755
--- a/httemplate/misc/unapply-cust_pay.cgi
+++ b/httemplate/misc/unapply-cust_pay.cgi
@@ -1,4 +1,4 @@
-<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum. ";show=payment_history") %>
 <%init>
 
 die "access denied"
diff --git a/httemplate/misc/unvoid-cust_pay_void.cgi b/httemplate/misc/unvoid-cust_pay_void.cgi
index 4726ee5..84b7879 100755
--- a/httemplate/misc/unvoid-cust_pay_void.cgi
+++ b/httemplate/misc/unvoid-cust_pay_void.cgi
@@ -1,7 +1,7 @@
 %if ( $error ) {
 %  errorpage($error);
 %} else {
-<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum. ";show=payment_history") %>
 %}
 <%init>
 
diff --git a/httemplate/misc/void-cust_pay.cgi b/httemplate/misc/void-cust_pay.cgi
index 31b7a62..376fb56 100755
--- a/httemplate/misc/void-cust_pay.cgi
+++ b/httemplate/misc/void-cust_pay.cgi
@@ -1,7 +1,7 @@
 %if ( $error ) {
 %  errorpage($error);
 %} else {
-<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum. ";show=payment_history") %>
 %}
 <%init>
 

commit 6d34c5060a4e5e9338ebc0d04459861a5c45e812
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Wed Sep 30 14:35:07 2015 -0600

    Changes to add classified reasons for invoice void
    
     - Added reasonnum and a corresponding foreign key in cust_bill_void and
       cust_bill_pkg_void tables (Schema.pm)
     - Used the FS::reason_Mixin::reason as the override of the
       cust_bill_void->reason to handle legacy and classified reasons
     - Changes in voided_invoice.html template to handle classified reasons for
       void invoices

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index c9ab5bc..6b5d658 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -735,8 +735,9 @@ sub tables_hashref {
 
         #void fields
         'void_date', @date_type, '', '', 
-        'reason',    'varchar',   'NULL', $char_d, '', '', 
-        'void_usernum',   'int', 'NULL', '', '', '',
+        'reason',     'varchar', 'NULL', $char_d, '', '', 
+        'reasonnum',      'int', 'NULL',      '', '', '',
+        'void_usernum',   'int', 'NULL',      '', '', '',
       ],
       'primary_key'  => 'invnum',
       'unique'       => [ [ 'custnum', 'agent_invid' ] ], #agentnum?  huh
@@ -750,6 +751,9 @@ sub tables_hashref {
                           { columns    => [ 'statementnum' ],
                             table      => 'cust_statement', #_void? both?
                           },
+                          { columns    => [ 'reasonnum' ],
+                            table      => 'reason',
+                          },
                           { columns    => [ 'void_usernum' ],
                             table      => 'access_user',
                             references => [ 'usernum' ],
@@ -1197,8 +1201,9 @@ sub tables_hashref {
         'feepart',              'int', 'NULL',      '', '', '',
         #void fields
         'void_date', @date_type, '', '', 
-        'reason',    'varchar',   'NULL', $char_d, '', '', 
-        'void_usernum',   'int', 'NULL', '', '', '',
+        'reason',     'varchar', 'NULL', $char_d, '', '', 
+        'reasonnum',      'int', 'NULL',      '', '', '',
+        'void_usernum',   'int', 'NULL',      '', '', '',
       ],
       'primary_key'  => 'billpkgnum',
       'unique'       => [],
@@ -1209,6 +1214,9 @@ sub tables_hashref {
                           { columns    => [ 'invnum' ],
                             table      => 'cust_bill_void',
                           },
+                          { columns    => [ 'reasonnum' ],
+                            table      => 'reason',
+                          },
                           #pkgnum 0 and -1 are used for special things
                           #{ columns    => [ 'pkgnum' ],
                           #  table      => 'cust_pkg',
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 6546bfa..138d0fa 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -37,6 +37,8 @@ use FS::cust_bill_pay_pkg;
 use FS::cust_credit_bill_pkg;
 use FS::discount_plan;
 use FS::cust_bill_void;
+use FS::reason;
+use FS::reason_type;
 use FS::L10N;
 
 $DEBUG = 0;
@@ -212,7 +214,7 @@ sub insert {
 
 }
 
-=item void
+=item void [ REASON ]
 
 Voids this invoice: deletes the invoice and adds a record of the voided invoice
 to the FS::cust_bill_void table (and related tables starting from
@@ -224,6 +226,14 @@ sub void {
   my $self = shift;
   my $reason = scalar(@_) ? shift : '';
 
+  unless (ref($reason) || !$reason) {
+    $reason = FS::reason->new_or_existing(
+      'class'  => 'X',
+      'type'   => 'Void invoice',
+      'reason' => $reason
+    );
+  }
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE';
@@ -238,7 +248,7 @@ sub void {
   my $cust_bill_void = new FS::cust_bill_void ( {
     map { $_ => $self->get($_) } $self->fields
   } );
-  $cust_bill_void->reason($reason);
+  $cust_bill_void->reasonnum($reason->reasonnum) if $reason;
   my $error = $cust_bill_void->insert;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 5861ee4..aea776a 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -26,6 +26,8 @@ use FS::cust_bill_pkg_tax_location_void;
 use FS::cust_bill_pkg_tax_rate_location_void;
 use FS::cust_tax_exempt_pkg_void;
 use FS::cust_bill_pkg_fee_void;
+use FS::reason;
+use FS::reason_type;
 
 use FS::Cursor;
 
@@ -322,7 +324,7 @@ sub insert {
 
 }
 
-=item void
+=item void [ REASON ]
 
 Voids this line item: deletes the line item and adds a record of the voided
 line item to the FS::cust_bill_pkg_void table (and related tables).
@@ -333,6 +335,14 @@ sub void {
   my $self = shift;
   my $reason = scalar(@_) ? shift : '';
 
+  unless (ref($reason) || !$reason) {
+    $reason = FS::reason->new_or_existing(
+      'class'  => 'X',
+      'type'   => 'Void invoice',
+      'reason' => $reason
+    );
+  }
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE';
@@ -347,7 +357,7 @@ sub void {
   my $cust_bill_pkg_void = new FS::cust_bill_pkg_void ( {
     map { $_ => $self->get($_) } $self->fields
   } );
-  $cust_bill_pkg_void->reason($reason);
+  $cust_bill_pkg_void->reasonnum($reason->reasonnum) if $reason;
   my $error = $cust_bill_pkg_void->insert;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_bill_pkg_void.pm b/FS/FS/cust_bill_pkg_void.pm
index 080452e..4b9cffd 100644
--- a/FS/FS/cust_bill_pkg_void.pm
+++ b/FS/FS/cust_bill_pkg_void.pm
@@ -1,5 +1,5 @@
 package FS::cust_bill_pkg_void;
-use base qw( FS::TemplateItem_Mixin FS::Record );
+use base qw( FS::TemplateItem_Mixin FS::reason_Mixin FS::Record );
 
 use strict;
 use FS::Record qw( qsearch qsearchs dbh fields );
@@ -104,6 +104,13 @@ unitrecur
 
 hidden
 
+=item reason 
+
+freeform string (deprecated)
+
+=item reasonnum 
+
+reason for voiding the payment (see L<FS::reson>)
 
 =back
 
@@ -134,6 +141,10 @@ sub discount_table          { 'cust_bill_pkg_discount_void'; }
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
 
+=item reason
+
+Returns the text of the associated void reason (see L<FS::reason>) for this.
+
 =item unvoid 
 
 "Un-void"s this line item: Deletes the voided line item from the database and
@@ -242,6 +253,8 @@ sub check {
     || $self->ut_moneyn('unitrecur')
     || $self->ut_enum('hidden', [ '', 'Y' ])
     || $self->ut_numbern('feepart')
+    || $self->ut_textn('reason')
+    || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum')
   ;
   return $error if $error;
 
diff --git a/FS/FS/cust_bill_void.pm b/FS/FS/cust_bill_void.pm
index f3dba90..04c69d4 100644
--- a/FS/FS/cust_bill_void.pm
+++ b/FS/FS/cust_bill_void.pm
@@ -1,5 +1,6 @@
 package FS::cust_bill_void;
-use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin FS::Record );
+use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin
+             FS::reason_Mixin FS::Record );
 
 use strict;
 use FS::Record qw( qsearch qsearchs dbh fields );
@@ -82,9 +83,13 @@ promised_date
 
 void_date
 
-=item reason
+=item reason 
+
+freeform string (deprecated)
+
+=item reasonnum 
 
-reason
+reason for voiding the payment (see L<FS::reson>)
 
 =item void_usernum
 
@@ -216,6 +221,7 @@ sub check {
     || $self->ut_numbern('void_date')
     || $self->ut_textn('reason')
     || $self->ut_numbern('void_usernum')
+    || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum')
   ;
   return $error if $error;
 
@@ -259,6 +265,10 @@ sub void_access_user {
 
 =item cust_bill_pkg
 
+=item reason
+
+Returns the text of the associated void reason (see L<FS::reason>) for this.
+
 =cut
 
 sub cust_bill_pkg { #actually cust_bill_pkg_void objects
diff --git a/httemplate/view/cust_main/payment_history/voided_invoice.html b/httemplate/view/cust_main/payment_history/voided_invoice.html
index ea61f84..ff4d12f 100644
--- a/httemplate/view/cust_main/payment_history/voided_invoice.html
+++ b/httemplate/view/cust_main/payment_history/voided_invoice.html
@@ -6,7 +6,7 @@
 % }
 % my $reason = $cust_bill_void->reason;
 % if ($reason) {
-     (<% $reason %>)
+     (<% $reason |h %>)
 % }
 <% mt("on [_1]", time2str($date_format, $cust_bill_void->void_date) ) |h %> 
 </I>

commit 676a117afd9d1091dcf425cc894593beda54e78c
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Tue Sep 29 13:58:39 2015 -0600

    Changed the purpose of 'void credit' reasons type 'X' to void
    credit/invoice/payment reasons

diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm
index 17a7167..50df495 100644
--- a/FS/FS/reason_type.pm
+++ b/FS/FS/reason_type.pm
@@ -11,7 +11,7 @@ our %class_name = (
   'R' => 'credit',
   'S' => 'suspend',
   'F' => 'refund',
-  'X' => 'void credit',
+  'X' => 'void', # credit/invoice/payment
 );
 
 our %class_purpose = (  
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index dcc02c2..81c1212 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -721,6 +721,10 @@ if ( $curuser->access_right('Configuration') ) {
   $config_billing{'separator5'} = ''; #its a separator!
   $config_billing{'Refund reasons'}  = [ $fsurl.'browse/reason.html?class=F', 'Refund reasons explain why a refund was issued.' ];
   $config_billing{'Refund reason types'}  = [ $fsurl.'browse/reason_type.html?class=F', 'Refund reason types define groups of reasons.' ];
+
+  $config_billing{'separator6'} = ''; #its a separator!
+  $config_billing{'Void reasons'}  = [ $fsurl.'browse/reason.html?class=X', 'Void reasons explain why a void was issued.' ];
+  $config_billing{'Void reason types'}  = [ $fsurl.'browse/reason_type.html?class=X', 'Void reason types define groups of reasons.' ];
 }
 
 #XXX also to be unified

commit 55c7e3cc18a45620f48ae62d3bc044a830bd8c95
Author: Irina Todeva <itodeva at hostgator.com>
Date:   Mon Sep 28 13:48:20 2015 -0600

    Changes to add classified reasons for payment void
    
     - Added reasonnum in cust_pay_void and a foreign key in Schema.pm
     - Added an override of the cust_pay_void_reason to handle legacy and
       classified reasons
     - Added usage of FS::reason_Mixin::_upgrade_reasonnum in
       cust_pay->_upgrade_data
     - Changes in voided_payment.html template to handle classified reasons for
       void payemnts

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 447302b..c9ab5bc 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2523,6 +2523,7 @@ sub tables_hashref {
         #void fields
         'void_date',  @date_type,                  '', '', 
         'reason',      'varchar', 'NULL', $char_d, '', '', 
+        'reasonnum',       'int', 'NULL',      '', '', '', 
         'void_usernum',    'int', 'NULL',      '', '', '',
       ],
       'primary_key'  => 'paynum',
@@ -2544,6 +2545,9 @@ sub tables_hashref {
                           { columns    => [ 'gatewaynum' ],
                             table      => 'payment_gateway',
                           },
+                          { columns    => [ 'reasonnum' ],
+                            table      => 'reason',
+                          },
                           { columns    => [ 'void_usernum' ],
                             table      => 'access_user',
                             references => [ 'usernum' ],
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index d9ae0d3..4d06862 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -2,9 +2,9 @@ package FS::cust_pay;
 
 use strict;
 use base qw( FS::otaker_Mixin FS::payinfo_transaction_Mixin FS::cust_main_Mixin
-             FS::Record );
+             FS::reason_Mixin FS::Record);
 use vars qw( $DEBUG $me $conf @encrypted_fields
-             $unsuspendauto $ignore_noapply 
+             $unsuspendauto $ignore_noapply
            );
 use Date::Format;
 use Business::CreditCard;
@@ -24,6 +24,8 @@ use FS::cust_pkg;
 use FS::cust_pay_void;
 use FS::upgrade_journal;
 use FS::Cursor;
+use FS::reason;
+use FS::reason_type;
 
 $DEBUG = 0;
 
@@ -438,6 +440,15 @@ adds a record of the voided payment to the FS::cust_pay_void table.
 
 sub void {
   my $self = shift;
+  my $reason = shift;
+
+  unless (ref($reason) || !$reason) {
+    $reason = FS::reason->new_or_existing(
+      'class'  => 'X',
+      'type'   => 'Void payment',
+      'reason' => $reason
+    );
+  }
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -453,7 +464,7 @@ sub void {
   my $cust_pay_void = new FS::cust_pay_void ( {
     map { $_ => $self->get($_) } $self->fields
   } );
-  $cust_pay_void->reason(shift) if scalar(@_);
+  $cust_pay_void->reasonnum($reason->reasonnum) if $reason;
   my $error = $cust_pay_void->insert;
 
   my $cust_pay_pending =
@@ -1023,6 +1034,8 @@ sub _upgrade_data {  #class method
 
   warn "$me upgrading $class\n" if $DEBUG;
 
+  $class->_upgrade_reasonnum(%opt);
+
   local $FS::payinfo_Mixin::ignore_masked_payinfo = 1;
 
   ##
diff --git a/FS/FS/cust_pay_void.pm b/FS/FS/cust_pay_void.pm
index b2f777b..8fd5396 100644
--- a/FS/FS/cust_pay_void.pm
+++ b/FS/FS/cust_pay_void.pm
@@ -1,6 +1,6 @@
 package FS::cust_pay_void; 
 use base qw( FS::otaker_Mixin FS::payinfo_transaction_Mixin FS::cust_main_Mixin
-             FS::Record );
+             FS::reason_Mixin FS::Record );
 
 use strict;
 use vars qw( @encrypted_fields $otaker_upgrade_kludge );
@@ -88,7 +88,9 @@ Desired pkgnum when using experimental package balances.
 
 =item void_date
 
-=item reason
+=item reason - a freeform string (deprecated)
+
+=item reasonnum - Reason for voiding the payment (see L<FS::reson>)
 
 =back
 
@@ -189,6 +191,7 @@ sub check {
     || $self->ut_numbern('void_date')
     || $self->ut_textn('reason')
     || $self->payinfo_check
+    || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum')
   ;
   return $error if $error;
 
@@ -221,10 +224,18 @@ sub void_access_user {
   qsearchs('access_user', { 'usernum' => $self->void_usernum } );
 }
 
+=item reason
+
+Returns the text of the associated void reason (see L<FS::reason>) for this.
+
+=cut
+
 # Used by FS::Upgrade to migrate to a new database.
 sub _upgrade_data {  # class method
   my ($class, %opts) = @_;
 
+  $class->_upgrade_reasonnum(%opts);
+
   my $sql = "SELECT usernum FROM access_user WHERE username = ( SELECT history_user FROM h_cust_pay_void WHERE paynum = ? AND history_action = 'insert' ORDER BY history_date LIMIT 1 ) ";
   my $sth = dbh->prepare($sql) or die dbh->errstr;
 
diff --git a/httemplate/view/cust_main/payment_history/voided_payment.html b/httemplate/view/cust_main/payment_history/voided_payment.html
index 5c43c91..e295f9b 100644
--- a/httemplate/view/cust_main/payment_history/voided_payment.html
+++ b/httemplate/view/cust_main/payment_history/voided_payment.html
@@ -6,7 +6,7 @@
 % }
 % my $reason = $cust_pay_void->reason;
 % if ($reason) {
-     (<% $reason %>)
+     (<% $reason |h %>)
 % }
 <% mt("on [_1]", time2str($date_format, $cust_pay_void->void_date) ) |h %> 
 </I>

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

Summary of changes:
 FS/FS/AccessRight.pm                               |    2 +-
 FS/FS/Schema.pm                                    |   20 ++-
 FS/FS/Upgrade.pm                                   |    5 +
 FS/FS/access_right.pm                              |    4 +-
 FS/FS/cust_bill.pm                                 |   14 +-
 FS/FS/cust_bill_pkg.pm                             |   14 +-
 FS/FS/cust_bill_pkg_void.pm                        |   31 +++-
 FS/FS/cust_bill_void.pm                            |   31 +++-
 FS/FS/cust_credit.pm                               |    2 +-
 FS/FS/cust_credit_void.pm                          |   15 ++
 FS/FS/cust_pay.pm                                  |   19 ++-
 FS/FS/cust_pay_void.pm                             |   15 +-
 FS/FS/reason_Mixin.pm                              |  167 +++++++++++++-------
 FS/FS/reason_type.pm                               |    4 +-
 httemplate/elements/menu.html                      |    4 +
 httemplate/elements/tr-select-reason.html          |    2 +-
 httemplate/misc/process/void-cust_bill.html        |    2 +-
 httemplate/misc/unapply-cust_credit.cgi            |    2 +-
 httemplate/misc/unapply-cust_pay.cgi               |    2 +-
 httemplate/misc/unvoid-cust_pay_void.cgi           |    2 +-
 .../{void-cust_bill.html => void-cust_bill.cgi}    |    9 +-
 ...{void-cust_credit.html => void-cust_credit.cgi} |    2 +-
 httemplate/misc/void-cust_pay.cgi                  |   70 ++++++--
 httemplate/view/cust_bill.cgi                      |   23 ++-
 .../view/cust_main/payment_history/credit.html     |    2 +-
 .../view/cust_main/payment_history/invoice.html    |    2 +-
 .../view/cust_main/payment_history/payment.html    |   20 ++-
 .../cust_main/payment_history/voided_invoice.html  |    2 +-
 .../cust_main/payment_history/voided_payment.html  |    2 +-
 29 files changed, 377 insertions(+), 112 deletions(-)
 copy httemplate/misc/{void-cust_bill.html => void-cust_bill.cgi} (85%)
 mode change 100644 => 100755
 rename httemplate/misc/{void-cust_credit.html => void-cust_credit.cgi} (97%)




More information about the freeside-commits mailing list