summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/AccessRight.pm2
-rw-r--r--FS/FS/cust_main.pm40
-rw-r--r--FS/FS/cust_pay_pending.pm83
-rw-r--r--httemplate/edit/cust_pay_pending.html154
-rw-r--r--httemplate/edit/process/cust_pay_pending.html68
-rw-r--r--httemplate/elements/menu.html2
-rwxr-xr-xhttemplate/search/cust_pay_pending.html47
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html78
-rw-r--r--httemplate/view/cust_main/payment_history.html16
9 files changed, 465 insertions, 25 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index fe10572ca..93660e236 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -152,6 +152,8 @@ tie my %rights, 'Tie::IxHash',
'Resend invoices', #NEWNEW
'View customer tax exemptions', #yow
'View customer batched payments', #NEW
+ 'View customer pending payments', #NEW
+ 'Edit customer pending payments', #NEW
'View customer billing events', #NEW
],
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 1d234a765..7d68536a4 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -3691,7 +3691,7 @@ sub realtime_bop {
'country' => ( exists($options{'country'})
? $options{'country'}
: $self->country ),
- 'referer' => 'http://cleanwhisker.420.am/',
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
'email' => $email,
'phone' => $self->daytime || $self->night,
%content, #after
@@ -3847,6 +3847,7 @@ sub realtime_bop {
$cust_pay_pending->status('done');
$cust_pay_pending->statustext('captured');
+ $cust_pay_pending->paynum($cust_pay->paynum);
my $cpp_done_err = $cust_pay_pending->replace;
if ( $cpp_done_err ) {
@@ -4196,7 +4197,7 @@ sub realtime_refund_bop {
'password' => $password,
'order_number' => $order_number,
'amount' => $amount,
- 'referer' => 'http://cleanwhisker.420.am/',
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
);
$content{authorization} = $auth
if length($auth); #echeck/ACH transactions have an order # but no auth
@@ -5349,6 +5350,41 @@ sub cust_pay_batch {
qsearch( 'cust_pay_batch', { 'custnum' => $self->custnum } )
}
+=item cust_pay_pending
+
+Returns all pending payments (see L<FS::cust_pay_pending>) for this customer
+(without status "done").
+
+=cut
+
+sub cust_pay_pending {
+ my $self = shift;
+ return $self->num_cust_pay_pending unless wantarray;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_pending', {
+ 'custnum' => $self->custnum,
+ 'status' => { op=>'!=', value=>'done' },
+ },
+ );
+}
+
+=item num_cust_pay_pending
+
+Returns the number of pending payments (see L<FS::cust_pay_pending>) for this
+customer (without status "done"). Also called automatically when the
+cust_pay_pending method is used in a scalar context.
+
+=cut
+
+sub num_cust_pay_pending {
+ my $self = shift;
+ my $sql = " SELECT COUNT(*) FROM cust_pay_pending ".
+ " WHERE custnum = ? AND status != 'done' ";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute($self->custnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
=item cust_refund
Returns all the refunds (see L<FS::cust_refund>) for this customer.
diff --git a/FS/FS/cust_pay_pending.pm b/FS/FS/cust_pay_pending.pm
index 7469720f4..bbabd247e 100644
--- a/FS/FS/cust_pay_pending.pm
+++ b/FS/FS/cust_pay_pending.pm
@@ -3,12 +3,12 @@ package FS::cust_pay_pending;
use strict;
use vars qw( @ISA @encrypted_fields );
use FS::Record qw( qsearch qsearchs dbh ); #dbh for _upgrade_data
-use FS::payby;
-use FS::payinfo_Mixin;
+use FS::payinfo_transaction_Mixin;
+use FS::cust_main_Mixin;
use FS::cust_main;
use FS::cust_pay;
-@ISA = qw(FS::Record FS::payinfo_Mixin);
+@ISA = qw( FS::payinfo_transaction_Mixin FS::cust_main_Mixin FS::Record );
@encrypted_fields = ('payinfo');
@@ -215,6 +215,83 @@ sub check {
$self->SUPER::check;
}
+#these two are kind-of false laziness w/cust_main::realtime_bop
+#(currently only used when resolving pending payments manually)
+
+=item insert_cust_pay
+
+Sets the status of this pending pament to "done" (with statustext
+"captured (manual)"), and inserts a payment record (see L<FS::cust_pay>).
+
+Currently only used when resolving pending payments manually.
+
+=cut
+
+sub insert_cust_pay {
+ my $self = shift;
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $self->custnum,
+ 'paid' => $self->paid,
+ '_date' => $self->_date, #better than passing '' for now
+ 'payby' => $self->payby,
+ 'payinfo' => $self->payinfo,
+ 'paybatch' => $self->paybatch,
+ 'paydate' => $self->paydate,
+ } );
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction
+
+ my $error = $cust_pay->insert;#($options{'manual'} ? ( 'manual' => 1 ) : () );
+
+ if ( $error ) {
+ # gah.
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->status('done');
+ $self->statustext('captured (manual)');
+ $self->paynum($cust_pay->paynum);
+ my $cpp_done_err = $self->replace;
+
+ if ( $cpp_done_err ) {
+
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $cpp_done_err;
+
+ } else {
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return ''; #no error
+
+ }
+
+}
+
+=item decline
+
+Sets the status of this pending pament to "done" (with statustext
+"declined (manual)").
+
+Currently only used when resolving pending payments manually.
+
+=cut
+
+sub decline {
+ my $self = shift;
+
+ #could send decline email too? doesn't seem useful in manual resolution
+
+ $self->status('done');
+ $self->statustext("declined (manual)");
+ $self->replace;
+}
+
# _upgrade_data
#
# Used by FS::Upgrade to migrate to a new database.
diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html
new file mode 100644
index 000000000..0916a1c0c
--- /dev/null
+++ b/httemplate/edit/cust_pay_pending.html
@@ -0,0 +1,154 @@
+<% include('/elements/header-popup.html', $title ) %>
+
+% if ( $action eq 'delete' ) {
+
+ <CENTER><FONT SIZE="+1"><B>Are you sure you want to delete this pending payment?</B></FONT></CENTER>
+
+% } elsif ( $action eq 'complete' ) {
+
+ <CENTER><FONT SIZE="+1"><B>No response was received from <% $cust_pay_pending->processor || 'the payment gateway' %> for this transaction. Check <% $cust_pay_pending->processor || 'the payment gateway' %>'s reporting and determine if this transaction completed successfully.</B></FONT></CENTER>
+
+% }
+
+<BR>
+
+%#false laziness w/view/cust_pay.html
+<% include('/elements/small_custview.html',
+ $cust_pay_pending->custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #no balance
+ )
+%>
+<BR>
+
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD ALIGN="right">Pending payment#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->paypendingnum %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% time2str"%a&nbsp;%b&nbsp;%o,&nbsp;%Y&nbsp;%r", $cust_pay_pending->_date %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_pay_pending->paid %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Payment method</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->payby_name %> #<% $cust_pay_pending->paymask %></B></TD>
+</TR>
+
+% #if ( $cust_pay_pending->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_pay_pending->paybatch ) {
+
+ <TR>
+ <TD ALIGN="right">Processor</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->processor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Authorization#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->authorization %></B></TD>
+ </TR>
+
+% if ( $cust_pay_pending->order_number ) {
+ <TR>
+ <TD ALIGN="right">Order#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->order_number %></B></TD>
+ </TR>
+% }
+
+% #}
+
+</TABLE>
+
+<BR>
+
+<FORM NAME = "pendingform"
+ METHOD = "POST"
+ ACTION = "process/cust_pay_pending.html"
+>
+
+<INPUT TYPE="hidden" NAME="paypendingnum" VALUE="<% $paypendingnum %>">
+
+<% itable() %>
+
+% if ( $action eq 'delete' ) {
+
+ <INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>">
+
+ <TR>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, delete payment</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, cancel deletion</BUTTON>
+ </TD>
+ </TR>
+
+% } elsif ( $action eq 'complete' ) {
+
+ <INPUT TYPE="hidden" NAME="action" VALUE="">
+
+ <TR>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'delete'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was not received</BUTTON>
+ </TD>
+ </TR>
+
+ <TR><TD COLSPAN=5></TD></TR>
+
+ <TR>
+ <TD COLSPAN=5 ALIGN="center">
+ <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->Cancel payment completion; transaction status not yet known</BUTTON>
+ </TD>
+ </TR>
+
+% }
+
+</TABLE>
+
+</FORM>
+</BODY>
+</HTML>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Edit customer pending payments');
+
+$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action';
+my $action = $1;
+my $title = ucfirst($action). ' pending payment';
+
+$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum';
+my $paypendingnum = $1;
+my $cust_pay_pending =
+ qsearchs({
+ 'select' => 'cust_pay_pending.*',
+ 'table' => 'cust_pay_pending',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'paypendingnum' => $paypendingnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ })
+ or die 'unknown paypendingnum';
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/edit/process/cust_pay_pending.html b/httemplate/edit/process/cust_pay_pending.html
new file mode 100644
index 000000000..1bad6cffe
--- /dev/null
+++ b/httemplate/edit/process/cust_pay_pending.html
@@ -0,0 +1,68 @@
+<% include('/elements/header-popup.html', $title ) %>
+% if ( $error ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT>
+% } else {
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+% }
+</BODY>
+</HTML>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Edit customer pending payments');
+
+$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action';
+my $action = $1;
+
+$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum';
+my $paypendingnum = $1;
+my $cust_pay_pending =
+ qsearchs({
+ 'select' => 'cust_pay_pending.*',
+ 'table' => 'cust_pay_pending',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'paypendingnum' => $paypendingnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ })
+ or die 'unknown paypendingnum';
+
+my $error;
+my $title;
+if ( $action eq 'delete' ) {
+
+ $error = $cust_pay_pending->delete;
+ if ( $error ) {
+ $title = 'Error deleting pending payment';
+ } else {
+ $title = 'Pending payment deletion sucessful';
+ }
+
+} elsif ( $action eq 'insert_cust_pay' ) {
+
+ $error = $cust_pay_pending->insert_cust_pay;
+ if ( $error ) {
+ $title = 'Error completing pending payment';
+ } else {
+ $title = 'Pending payment completed';
+ }
+
+} elsif ( $action eq 'decline' ) {
+
+ $error = $cust_pay_pending->decline;
+ if ( $error ) {
+ $title = 'Error declining pending payment';
+ } else {
+ $title = 'Pending payment completed (decline)';
+ }
+
+} else {
+
+ die "unknown action $action";
+
+}
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index ae5ff5880..627f9c857 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -191,6 +191,8 @@ tie my %report_financial, 'Tie::IxHash',
'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
'Payment Report' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ],
;
+$report_financial{'Pending Payment Report'} = [ $fsurl.'search/cust_pay_pending.html?magic=_date;statusNOT=done', 'Pending real-time payments' ]
+ if $curuser->access_right('View customer pending payments');
$report_financial{'Payment Batch Report'} = [ $fsurl.'search/pay_batch.html', 'Payment batches (by status and/or date range)' ]
if $conf->exists('batch-enable') || $conf->config('batch-enable_payby');
$report_financial{'A/R Aging'} = [ $fsurl.'search/report_receivables.html', 'Accounts Receivable Aging report' ];
diff --git a/httemplate/search/cust_pay_pending.html b/httemplate/search/cust_pay_pending.html
new file mode 100755
index 000000000..f0a3a0150
--- /dev/null
+++ b/httemplate/search/cust_pay_pending.html
@@ -0,0 +1,47 @@
+<% include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay_pending',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'pending payment',
+ 'name_verb' => 'pending',
+ 'disable_link' => 1,
+ 'disable_by' => 1, #add otaker to cust_pay_pending?
+ 'html_init' => include('/elements/init_overlib.html'),
+ 'addl_header' => [ 'Time', 'Payment Status', ],
+ 'addl_fields' => [ sub { time2str('%r', shift->_date ) },
+ $status_sub,
+ ],
+ )
+%>
+<%init>
+
+my %statusaction = (
+ 'new' => 'delete',
+ 'pending' => 'complete',
+ #'authorized' => '',
+ #'captured' => '',
+ #'declined' => '',
+ #wouldn't need to take action on a done state#'done'
+);
+
+my $edit_pending =
+ $FS::CurrentUser::CurrentUser->access_right('Edit customer pending payments');
+
+my $status_sub = sub {
+ my $pending = shift;
+ my $return = $pending->status;
+ my $action = $statusaction{$pending->status};
+ return $return unless $action && $edit_pending;
+ my $link = include('/elements/popup_link.html',
+ 'action' => $p. 'edit/cust_pay_pending.html'.
+ '?paypendingnum='. $pending->paypendingnum.
+ ";action=$action",
+ 'label' => $action,
+ 'color' => '#ff0000',
+ 'width' => 655,
+ 'height' => ( $action eq 'delete' ? 480 : 575 ),
+ 'actionlabel' => ucfirst($action). ' pending payment',
+ );
+ $return. qq! <FONT SIZE="-1">($link)</FONT>!;
+};
+
+</%init>
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
index 9457ae2eb..9e2eceb3a 100755
--- a/httemplate/search/elements/cust_pay_or_refund.html
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -16,6 +16,18 @@ Examples:
'name_verb' => 'refunded',
)
+ include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay_pending',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'pending payment',
+ 'name_verb' => 'pending',
+ 'disable_link' => 1,
+ 'disable_by' => 1,
+ 'html_init' => '',
+ 'addl_header' => [],
+ 'addl_fields' => [],
+ )
+
</%doc>
<% include( 'search.html',
'title' => $title,
@@ -26,27 +38,25 @@ Examples:
'header' => [ "\u$name_singular",
'Amount',
'Date',
- 'By',
+ @header,
FS::UI::Web::cust_header(),
],
'fields' => [
'payby_payinfo_pretty',
sub { sprintf('$%.2f', shift->$amount_field() ) },
sub { time2str('%b %d %Y', shift->_date ) },
- sub { my $o = shift->otaker;
- $o = 'auto billing' if $o eq 'fs_daily';
- $o = 'customer self-service' if $o eq 'fs_selfservice';
- $o;
- },
+ @fields,
\&FS::UI::Web::cust_fields,
],
#'align' => 'lrrrll',
- 'align' => 'rrrc'.FS::UI::Web::cust_aligns(),
+ 'align' => 'rrr'.
+ join('', map 'c', @fields ).
+ FS::UI::Web::cust_aligns(),
'links' => [
$link,
$link,
$link,
- '',
+ ( map '', @fields ),
( map { $_ ne 'Cust. Status' ? $cust_link : '' }
FS::UI::Web::cust_header()
),
@@ -55,14 +65,14 @@ Examples:
'',
'',
'',
- '',
+ ( map '', @fields ),
FS::UI::Web::cust_colors(),
],
'style' => [
'',
'',
'',
- '',
+ ( map '', @fields ),
FS::UI::Web::cust_styles(),
],
)
@@ -71,14 +81,35 @@ Examples:
my %opt = @_;
+my $curuser = $FS::CurrentUser::CurrentUser;
+
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+ unless $curuser->access_right('Financial reports');
my $thing = $opt{'thing'};
my $amount_field = $opt{'amount_field'};
my $name_singular = $opt{'name_singular'};
my $title = "\u$name_singular Search Results";
+
+my @header = ();
+my @fields = ();
+unless ( $opt{'disable_by'} ) {
+ push @header, 'By';
+ push @fields, sub {
+ sub { my $o = shift->otaker;
+ $o = 'auto billing' if $o eq 'fs_daily';
+ $o = 'customer self-service' if $o eq 'fs_selfservice';
+ $o;
+ },
+ };
+}
+
+push @header, @{ $opt{'addl_header'} }
+ if $opt{'addl_header'};
+push @fields, @{ $opt{'addl_fields'} }
+ if $opt{'addl_fields'};
+
my( $count_query, $sql_query );
if ( $cgi->param('magic') ) {
@@ -93,7 +124,11 @@ if ( $cgi->param('magic') ) {
die "unknown agentnum $1" unless $agent;
$title = $agent->agent. " $title";
}
-
+
+ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @search, "custnum = $1";
+ }
+
if ( $cgi->param('payby') ) {
$cgi->param('payby') =~
/^(CARD|CHEK|BILL|PREP|CASH|WEST|MCRD)(-(VisaMC|Amex|Discover|Maestro))?$/
@@ -175,6 +210,11 @@ if ( $cgi->param('magic') ) {
push @search, "cust_$thing.payinfo = '$1'";
}
+ #for cust_pay_pending... statusNOT=done
+ if ( $cgi->param('statusNOT') =~ /^(\w+)$/ ) {
+ push @search, "status != '$1'";
+ }
+
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
push @search, "_date >= $beginning ",
"_date <= $ending";
@@ -197,7 +237,7 @@ if ( $cgi->param('magic') ) {
}
#here is the agent virtualization
- push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+ push @search, $curuser->agentnums_sql;
my $search = ' WHERE '. join(' AND ', @search);
@@ -229,21 +269,25 @@ if ( $cgi->param('magic') ) {
$count_query = "SELECT COUNT(*), SUM($amount_field) FROM cust_$thing".
" WHERE payinfo = '$payinfo' AND payby = '$payby'".
- " AND ". $FS::CurrentUser::CurrentUser->agentnums_sql;
+ " AND ". $curuser->agentnums_sql;
$sql_query = {
'table' => "cust_$thing",
'hashref' => { 'payinfo' => $payinfo,
'payby' => $payby },
- 'extra_sql' => $FS::CurrentUser::CurrentUser->agentnums_sql.
+ 'extra_sql' => $curuser->agentnums_sql.
" ORDER BY _date",
};
}
my $link = '';
-if ( $FS::CurrentUser::CurrentUser->access_right('View invoices') #XXX for now
- || $FS::CurrentUser::CurrentUser->access_right('View customer payments') ){
+if ( ( $curuser->access_right('View invoices') #XXX for now
+ || $curuser->access_right('View customer payments')
+ )
+ && ! $opt{'disable_link'}
+ )
+{
$link = [ "${p}view/cust_$thing.html?${thing}num=", $thing.'num' ]
}
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index 2ab5339db..335ce2485 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -138,15 +138,25 @@
% if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
% && $curuser->access_right('View customer batched payments')
-% ) {
- View batched payments:
+% )
+% {
+ View batched payments:
% foreach my $status (qw( Queued In-transit Complete All )) {
<A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% $status %></A>
<% $status ne 'All' ? '|' : '' %>
% }
- <BR>
+ <BR>
% }
+%# pending payment links
+
+% if ( $curuser->access_right('View customer pending payments')
+% && scalar($cust_main->cust_pay_pending)
+% )
+% {
+ <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>">View pending payments</A><BR>
+% }
+
%# and now the table
<% include("/elements/table-grid.html") %>