summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/AccessRight.pm3
-rw-r--r--FS/FS/Schema.pm5
-rw-r--r--FS/FS/access_right.pm3
-rw-r--r--FS/FS/cust_credit.pm13
-rw-r--r--FS/FS/cust_credit_void.pm81
-rw-r--r--FS/FS/reason_type.pm4
-rw-r--r--httemplate/elements/menu.html1
-rwxr-xr-xhttemplate/elements/tr-select-reason.html6
-rwxr-xr-xhttemplate/misc/unvoid-cust_credit_void.cgi21
-rwxr-xr-xhttemplate/misc/void-cust_credit.html74
-rwxr-xr-xhttemplate/search/cust_credit_void.html141
-rw-r--r--httemplate/search/report_cust_credit_void.html50
-rw-r--r--httemplate/view/cust_main/payment_history.html2
-rw-r--r--httemplate/view/cust_main/payment_history/credit.html14
-rw-r--r--httemplate/view/cust_main/payment_history/voided_credit.html12
15 files changed, 419 insertions, 11 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 121f83c..f395dea 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -221,6 +221,8 @@ tie my %rights, 'Tie::IxHash',
{ rightname=>'Backdate credit', desc=>'Enable credits to be posted for days other than today.' },
'Credit line items', #NEWNEWNEW
'Apply credit', #NEWNEW
+ 'Void credit', #NEWER than things marked NEWNEWNEW
+ 'Unvoid credit', #NEWER than things marked NEWNEWNEW
{ rightname=>'Unapply credit', desc=>'Enable "unapplication" of unclosed credits.' }, #aka unapplycredits
{ rightname=>'Delete credit', desc=>'Enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments.' }, #aka. deletecredits Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.
'View refunds',
@@ -233,6 +235,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 refund reason', #NEW
],
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 54a4680..5333b1a 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1423,6 +1423,7 @@ sub tables_hashref {
#void fields
'void_date', @date_type, '', '',
'void_reason', 'varchar', 'NULL', $char_d, '', '',
+ 'void_reasonnum', 'int', 'NULL', '', '', '',
'void_usernum', 'int', 'NULL', '', '', '',
],
'primary_key' => 'crednum',
@@ -1458,6 +1459,10 @@ sub tables_hashref {
table => 'cust_pkg',
references => [ 'pkgnum' ],
},
+ { columns => [ 'void_reasonnum' ],
+ table => 'reason',
+ references => [ 'reasonnum' ],
+ },
{ columns => [ 'void_usernum' ],
table => 'access_user',
references => [ 'usernum' ],
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index d5e3b8b..1ea6e49 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -251,6 +251,9 @@ 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',
);
# foreach my $old_acl ( keys %onetime ) {
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index deebe27..76fdecb 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -382,13 +382,18 @@ adds a record of the voided credit to the cust_credit_void table.
=cut
-# yes, false laziness with cust_pay and cust_bill
-# but frankly I don't have time to fix it now
-
sub void {
my $self = shift;
my $reason = shift;
+ unless (ref($reason) || !$reason) {
+ $reason = FS::reason->new_or_existing(
+ 'class' => 'X',
+ 'type' => 'Void credit',
+ 'reason' => $reason
+ );
+ }
+
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -403,7 +408,7 @@ sub void {
my $cust_credit_void = new FS::cust_credit_void ( {
map { $_ => $self->get($_) } $self->fields
} );
- $cust_credit_void->set('void_reason', $reason);
+ $cust_credit_void->set('void_reasonnum', $reason->reasonnum);
my $error = $cust_credit_void->insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_credit_void.pm b/FS/FS/cust_credit_void.pm
index f76f794..9c92068 100644
--- a/FS/FS/cust_credit_void.pm
+++ b/FS/FS/cust_credit_void.pm
@@ -1,11 +1,12 @@
package FS::cust_credit_void;
-use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::Record );
+use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::reason_Mixin FS::Record );
use strict;
use FS::Record qw(qsearchs); # qsearch qsearchs);
use FS::CurrentUser;
use FS::access_user;
use FS::cust_credit;
+use FS::UID qw( dbh );
=head1 NAME
@@ -85,6 +86,7 @@ sub check {
|| $self->ut_numbern('void_date')
|| $self->ut_textn('void_reason')
|| $self->ut_foreign_keyn('void_usernum', 'access_user', 'usernum')
+ || $self->ut_foreign_keyn('void_reasonnum', 'reason', 'reasonnum')
;
return $error if $error;
@@ -96,6 +98,49 @@ sub check {
$self->SUPER::check;
}
+=item unvoid
+
+"Un-void"s this credit: Deletes the voided credit from the database and adds
+back (but does not re-apply) a normal credit.
+
+=cut
+
+sub unvoid {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_credit = new FS::cust_credit ( {
+ map { $_ => $self->get($_) } grep { $_ !~ /void/ } $self->fields
+ } );
+ my $error = $cust_credit->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $error ||= $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
=item cust_main
Returns the parent customer object (see L<FS::cust_main>).
@@ -111,6 +156,40 @@ sub void_access_user {
qsearchs('access_user', { 'usernum' => $self->void_usernum } );
}
+=item void_access_user_name
+
+Returns the voiding employee name.
+
+=cut
+
+sub void_access_user_name {
+ my $self = shift;
+ my $user = $self->void_access_user;
+ return unless $user;
+ return $user->name;
+}
+
+=item void_reason
+
+Returns the text of the associated void credit reason (see L<FS::reason>) for this voided credit.
+
+The reason for the original credit remains accessible through the reason method.
+
+=cut
+
+sub void_reason {
+ my ($self, $value, %options) = @_;
+ my $reason_text;
+ if ( $self->void_reasonnum ) {
+ my $reason = FS::reason->by_key($self->void_reasonnum);
+ $reason_text = $reason->reason;
+ } else { # in case one of these somehow still exists
+ $reason_text = $self->get('void_reason');
+ }
+
+ return $reason_text;
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm
index 00ac9a8..17a7167 100644
--- a/FS/FS/reason_type.pm
+++ b/FS/FS/reason_type.pm
@@ -11,6 +11,7 @@ our %class_name = (
'R' => 'credit',
'S' => 'suspend',
'F' => 'refund',
+ 'X' => 'void credit',
);
our %class_purpose = (
@@ -18,6 +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',
);
=head1 NAME
@@ -48,7 +50,7 @@ inherits from FS::Record. The following fields are currently supported:
=item typenum - primary key
-=item class - currently 'C', 'R', or 'S' for cancel, credit, or suspend
+=item class - currently 'C', 'R', 'S', 'F' or 'X' for cancel, credit, suspend, refund or void credit
=item type - name of the type of reason
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 7d5d4f3..fe59ec5 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -340,6 +340,7 @@ tie my %report_credits, 'Tie::IxHash',
'Credit package source detail' => [ $fsurl.'search/report_cust_credit_source_bill_pkg.html', 'Line-item detail for triggered package credits' ],
'Credit application detail' => [ $fsurl.'search/report_cust_credit_bill_pkg.html', 'Line item application detail' ],
'Unapplied Credits' => [ $fsurl.'search/report_cust_credit.html?unapplied=1', 'Unapplied credit report (by type and/or date range)' ],
+ 'Voided Credits' => [ $fsurl.'search/report_cust_credit_void.html', 'Voided credit report (by employee and/or date range)' ],
;
tie my %report_refunds, 'Tie::IxHash',
diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
index 14bb6e8..3565975 100755
--- a/httemplate/elements/tr-select-reason.html
+++ b/httemplate/elements/tr-select-reason.html
@@ -6,8 +6,8 @@ Example:
#required
'field' => 'reasonnum',
- 'reason_class' => 'C', # currently 'C', 'R', 'F', or 'S'
- # for cancel, credit, refund, or suspend
+ 'reason_class' => 'C', # currently 'C', 'R', 'F', 'S' or 'X'
+ # for cancel, credit, refund, suspend or void credit
#recommended
'cgi' => $cgi, #easiest way for things to be properly "sticky" on errors
@@ -173,6 +173,8 @@ if ($class eq 'C') {
$add_access_right = 'Add on-the-fly credit reason';
} 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';
} else {
die "illegal class: $class";
}
diff --git a/httemplate/misc/unvoid-cust_credit_void.cgi b/httemplate/misc/unvoid-cust_credit_void.cgi
new file mode 100755
index 0000000..5f8d9d5
--- /dev/null
+++ b/httemplate/misc/unvoid-cust_credit_void.cgi
@@ -0,0 +1,21 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect($p. "view/cust_main.cgi?custnum=". $custnum .";show=payment_history") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unvoid credit');
+
+#untaint crednum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal crednum";
+my $crednum = $1;
+
+my $cust_credit_void = qsearchs('cust_credit_void', { 'crednum' => $crednum } );
+my $custnum = $cust_credit_void->custnum;
+
+my $error = $cust_credit_void->unvoid;
+
+</%init>
diff --git a/httemplate/misc/void-cust_credit.html b/httemplate/misc/void-cust_credit.html
new file mode 100755
index 0000000..1e71f00
--- /dev/null
+++ b/httemplate/misc/void-cust_credit.html
@@ -0,0 +1,74 @@
+%if ( $success ) {
+<& /elements/header-popup.html, mt("Credit voided") &>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+%} else {
+<& /elements/header-popup.html, mt('Void credit') &>
+
+<& /elements/error.html &>
+
+<P ALIGN="center"><B><% mt('Void this credit?') |h %></B>
+
+<FORM action="<% ${p} %>misc/void-cust_credit.html">
+<INPUT TYPE="hidden" NAME="crednum" VALUE="<% $crednum %>">
+
+<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_credit" VALUE="<% mt('Void credit') |h %>">
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<INPUT TYPE="BUTTON" VALUE="<% mt("Don't void credit") |h %>" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Void credit');
+
+#untaint crednum
+my $crednum = $cgi->param('crednum');
+if ($crednum) {
+ $crednum =~ /^(\d+)$/ || die "Illegal crednum";
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)/ || die "Illegal crednum";
+ $crednum = $1;
+}
+
+my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum}) || die "Credit not found";
+
+my $success = 0;
+if ($cgi->param('confirm_void_credit')) {
+
+ #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_credit->void($reason) unless $error;
+ }
+
+ if ($error) {
+ $cgi->param('error',$error);
+ } else {
+ $success = 1;
+ }
+}
+
+</%init>
diff --git a/httemplate/search/cust_credit_void.html b/httemplate/search/cust_credit_void.html
new file mode 100755
index 0000000..18731d1
--- /dev/null
+++ b/httemplate/search/cust_credit_void.html
@@ -0,0 +1,141 @@
+<& elements/search.html,
+ 'title' => $title,
+ 'name' => emt('credits'),
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'sort_fields' => \@sort_fields,
+ 'align' => $align,
+ 'links' => \@links,
+ 'color' => \@color,
+ 'style' => \@style,
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
+my $title = emt('Voided Credit Search Results');
+
+my $clink = sub {
+ my $cust_bill = shift;
+ $cust_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+my %void_access_users;
+
+my (@header, @fields, @sort_fields, $align, @links, @color, @style);
+$align = '';
+
+#amount
+push @header, emt('Amount');
+push @fields, sub { $money_char .sprintf('%.2f', shift->amount) };
+push @sort_fields, 'amount';
+$align .= 'r';
+push @links, '';
+push @color, '';
+push @style, '';
+
+push @header, emt('Void Date'),
+ emt('Void By'),
+ emt('Void Reason'),
+ emt('Date'),
+ emt('By'),
+ emt('Reason'),
+ FS::UI::Web::cust_header(),
+ ;
+push @fields, sub { time2str('%b %d %Y', shift->void_date ) },
+ 'void_access_user_name',
+ 'void_reason',
+ sub { time2str('%b %d %Y', shift->_date ) },
+ 'otaker',
+ 'reason',
+ \&FS::UI::Web::cust_fields,
+ ;
+push @sort_fields, 'void_date',
+ 'void_usernum', #not ideal, but at least groups them together
+ 'void_reasonnum, void_reason', #ditto
+ '_date',
+ 'usernum', #ditto
+ 'reasonnum, reason', #ditto
+ FS::UI::Web::cust_sort_fields();
+$align .= 'rllrll'.FS::UI::Web::cust_aligns();
+push @links, '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ;
+push @color, '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ;
+push @style, '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ;
+
+my @search = ();
+my $addl_from = '';
+
+# note that cgi field is usernum, but we're actually searching void_usernum
+# because true laziness with tr-select-user in report_cust_credit_void.html
+if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @search, "cust_credit_void.void_usernum = $1";
+}
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1";
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "void_date >= $beginning ",
+ "void_date <= $ending";
+
+push @search, FS::UI::Web::parse_lt_gt($cgi, 'amount' );
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql(table=>'cust_main');
+
+my @select = (
+ 'cust_credit_void.*',
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+);
+
+my $where = 'WHERE '. join(' AND ', @search);
+
+my $count_query = 'SELECT COUNT(*), SUM(amount) ';
+$count_query .= 'FROM cust_credit_void'.
+ $addl_from. FS::UI::Web::join_cust_main('cust_credit_void').
+ $where;
+
+my $sql_query = {
+ 'table' => 'cust_credit_void',
+ 'select' => join(', ',@select),
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'addl_from' => $addl_from. FS::UI::Web::join_cust_main('cust_credit_void')
+};
+
+</%init>
diff --git a/httemplate/search/report_cust_credit_void.html b/httemplate/search/report_cust_credit_void.html
new file mode 100644
index 0000000..e967080
--- /dev/null
+++ b/httemplate/search/report_cust_credit_void.html
@@ -0,0 +1,50 @@
+<& /elements/header.html, mt($title) &>
+
+<FORM ACTION="cust_credit_void.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+
+ <& /elements/tr-select-user.html,
+ 'label' => emt('Credit voids by employee: '),
+ 'access_user' => \%access_user,
+ &>
+
+ <& /elements/tr-select-agent.html,
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => emt('for agent: '),
+ 'disable_empty' => 0,
+ &>
+
+ <& /elements/tr-input-beginning_ending.html &>
+
+ <& /elements/tr-input-lessthan_greaterthan.html,
+ 'label' => emt('Amount'),
+ 'field' => 'amount',
+ &>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+
+</FORM>
+
+<& /elements/footer.html &>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $sth = dbh->prepare("SELECT DISTINCT usernum FROM cust_credit_void")
+ or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my @usernum = map $_->[0], @{$sth->fetchall_arrayref};
+my %access_user =
+ map { $_ => qsearchs('access_user',{'usernum'=>$_})->username }
+ @usernum;
+
+my $title = 'Voided credit report';
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index 0aacb0a..04e427f 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -425,7 +425,7 @@ my %opt = (
'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
'Delete payment', 'Unapply payment',
- 'Apply credit', 'Delete credit', 'Unapply credit',
+ 'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
'Delete refund',
'Billing event reports', 'View customer billing events',
)
diff --git a/httemplate/view/cust_main/payment_history/credit.html b/httemplate/view/cust_main/payment_history/credit.html
index 941180e..3eed833 100644
--- a/httemplate/view/cust_main/payment_history/credit.html
+++ b/httemplate/view/cust_main/payment_history/credit.html
@@ -1,4 +1,4 @@
-<% $credit. ' '. $reason. $desc. $change_pkg. $apply. $delete. $unapply %>
+<% $credit. ' '. $reason. $desc. $change_pkg. $apply. $delete. $unapply. $void %>
<%init>
my( $cust_credit, %opt ) = @_;
@@ -126,6 +126,18 @@ if ( $apply && $opt{'pkg-balances'} && $cust_credit->pkgnum ) {
')';
}
+my $void = '';
+$void = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => emt('void'),
+ 'action' => "${p}misc/void-cust_credit.html?".
+ $cust_credit->crednum,
+ 'actionlabel' => emt('Void credit'),
+ ).
+ ')'
+ if $cust_credit->closed !~ /^Y/i
+ && $opt{'Void credit'};
+
my $delete = '';
$delete = areyousure_link("${p}misc/delete-cust_credit.cgi?".$cust_credit->crednum,
emt('Are you sure you want to delete this credit?'),
diff --git a/httemplate/view/cust_main/payment_history/voided_credit.html b/httemplate/view/cust_main/payment_history/voided_credit.html
index 9ff4c1b..3515d85 100644
--- a/httemplate/view/cust_main/payment_history/voided_credit.html
+++ b/httemplate/view/cust_main/payment_history/voided_credit.html
@@ -8,16 +8,26 @@
% }
<% $void_reason |h %>
</I>
+<% $unvoid %>
<%init>
my( $cust_credit_void, %opt ) = @_;
my $date_format = $opt{'date_format'} || '%m/%d/%Y';
-#my $unvoid = ''; # not yet available
+my $unvoid = '';
+$unvoid = areyousure_link("${p}misc/unvoid-cust_credit_void.cgi?".$cust_credit_void->crednum,
+ emt('Are you sure you want to unvoid this credit?'),
+ '',
+ emt('unvoid')
+ )
+ if $cust_credit_void->closed !~ /^Y/i
+ && $opt{'Unvoid credit'};
+
my $reason = $cust_credit_void->reason;
$reason = " ($reason)" if $reason;
my $void_reason = $cust_credit_void->void_reason;
$void_reason = " ($void_reason)" if $void_reason;
</%init>
+