1 <BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR>
4 % if ( $payby{'BILL'} && $curuser->access_right('Post payment') ) {
5 <% $s++ ? ' | ' : '' %>
6 <% include('/elements/popup_link-cust_main.html',
7 'label' => 'Enter check payment',
8 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=BILL",
9 'cust_main' => $cust_main,
10 'actionlabel' => 'Enter check payment',
12 #default# 'height' => 336,
17 % if ( $payby{'CASH'} && $curuser->access_right('Post payment') ) {
18 <% $s++ ? ' | ' : '' %>
19 <% include('/elements/popup_link-cust_main.html',
20 'label' => 'Enter cash payment',
21 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=CASH",
22 'cust_main' => $cust_main,
23 'actionlabel' => 'Enter cash payment',
25 #default# 'height' => 336,
30 % if ( $payby{'WEST'} && $curuser->access_right('Post payment') ) {
31 <% $s++ ? ' | ' : '' %>
32 <A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;custnum=<% $custnum %>">Enter Western Union payment</A>
35 % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
36 % && $curuser->access_right('Process payment')
37 % && ! $cust_main->is_encrypted($cust_main->payinfo)
39 <% $s++ ? ' | ' : '' %>
40 <A HREF="<% $p %>misc/payment.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card payment</A>
43 % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
44 % && $curuser->access_right('Process payment')
45 % && ! $cust_main->is_encrypted($cust_main->payinfo)
47 <% $s++ ? ' | ' : '' %>
48 <A HREF="<% $p %>misc/payment.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) payment</A>
51 % if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) {
52 <% $s++ ? ' | ' : '' %>
53 <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;custnum=<% $custnum %>">Post manual (offline) credit card payment</A>
58 % if ( $curuser->access_right('Post credit') ) {
59 <% include('/elements/popup_link-cust_main.html',
60 'label' => 'Enter credit',
61 'action' => "${p}edit/cust_credit.cgi",
62 'cust_main' => $cust_main,
63 'actionlabel' => 'Enter credit',
65 #default# 'height' => 336,
71 % if ( $curuser->access_right('View customer tax exemptions') ) {
72 <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>">View tax exemptions</A>
76 % if ( $conf->exists('batch-enable')
77 % && $curuser->access_right('View customer batched payments')
79 View batched payments:
80 % foreach my $status (qw( Queued In-transit Complete All )) {
81 <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% $status %></A>
82 <% $status ne 'All' ? '|' : '' %>
91 %foreach my $cust_bill ($cust_main->cust_bill) {
92 % my $pre = ( $cust_bill->owed > 0 )
93 % ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open '
95 % my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : '';
96 % my $invnum = $cust_bill->invnum;
97 % my $link = $curuser->access_right('View invoices')
98 % ? qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!
101 % if ( $cust_bill->num_cust_event
102 % && ( $curuser->access_right('Billing event reports')
103 % || $curuser->access_right('View customer billing events')
107 % qq!<BR><FONT SIZE="-1"><A HREF="${p}search/cust_event.html?invnum=!.
108 % $cust_bill->invnum. '">( View invoice events )</A></FONT>';
111 % 'date' => $cust_bill->_date,
112 % 'desc' => $link. $pre.
113 % "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'.
114 % $post. ( $link ? '</A>' : '' ). $events,
115 % 'charge' => $cust_bill->charged,
119 %#payments (some false laziness w/credits)
120 %foreach my $cust_pay ($cust_main->cust_pay) {
122 % my $payby = $cust_pay->payby;
125 % if ( $payby eq 'CARD' ) {
126 % $payinfo = $cust_pay->paymask;
127 % } elsif ( $payby eq 'CHEK' ) {
128 % my( $account, $aba ) = split('@', $cust_pay->paymask );
129 % $payinfo = "ABA $aba, Acct #$account";
131 % $payinfo = $cust_pay->payinfo;
133 % my @cust_bill_pay = $cust_pay->cust_bill_pay;
134 % my @cust_pay_refund = $cust_pay->cust_pay_refund;
136 % my $target = "$payby$payinfo";
137 % $payby =~ s/^BILL$/Check #/ if $payinfo;
138 % $payby =~ s/^CHEK$/Electronic check /;
139 % $payby =~ s/^PREP$/Prepaid card /;
140 % $payby =~ s/^CARD$/Credit card #/;
141 % $payby =~ s/^COMP$/Complimentary by /;
142 % $payby =~ s/^CASH$/Cash/;
143 % $payby =~ s/^WEST$/Western Union/;
144 % $payby =~ s/^MCRD$/Manual credit card/;
145 % $payby =~ s/^BILL$//;
146 % my $info = $payby ? "($payby$payinfo)" : '';
148 % my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
149 % if ( scalar(@cust_bill_pay) == 0
150 % && scalar(@cust_pay_refund) == 0 ) {
151 % #completely unapplied
152 % $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
153 % $post = '</FONT></B>';
154 % if ( $curuser->access_right('Apply payment') ) {
155 % $apply = ' ('. include( '/elements/popup_link.html',
156 % 'label' => 'apply',
157 % 'action' => "${p}edit/cust_bill_pay.cgi?".
159 % 'actionlabel' => 'Apply payment',
161 % #default# 'height' => 336,
165 % } elsif ( scalar(@cust_bill_pay) == 1
166 % && scalar(@cust_pay_refund) == 0
167 % && $cust_pay->unapplied == 0 ) {
168 % #applied to one invoice, the usual situation
169 % $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum;
170 % } elsif ( scalar(@cust_bill_pay) == 0
171 % && scalar(@cust_pay_refund) == 1
172 % && $cust_pay->unapplied == 0 ) {
173 % #applied to one refund
174 % $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
178 % foreach my $app ( sort { $a->_date <=> $b->_date }
179 % ( @cust_bill_pay, @cust_pay_refund ) ) {
180 % if ( $app->isa('FS::cust_bill_pay') ) {
181 % $desc .= ' '.
183 % ' applied to Invoice #'. $app->invnum.
185 % #' on '. time2str("%D", $cust_bill_pay->_date).
186 % } elsif ( $app->isa('FS::cust_pay_refund') ) {
187 % $desc .= ' '.
189 % ' refunded on '. time2str("%D", $app->_date).
192 % die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
195 % if ( $cust_pay->unapplied > 0 ) {
196 % $desc .= ' '.
197 % '<B><FONT COLOR="#FF0000">$'.
198 % $cust_pay->unapplied. ' unapplied</FONT></B>';
199 % if ( $curuser->access_right('Apply payment') ) {
200 % $desc = ' ('. include( '/elements/popup_link.html',
201 % 'label' => 'apply',
202 % 'action' => "${p}edit/cust_bill_pay.cgi?".
204 % 'actionlabel' => 'Apply payment',
206 % #default# 'height' => 336,
215 % ' ('. include('/elements/popup_link.html',
216 % 'label' => 'view receipt',
217 % 'action' => "${p}view/cust_pay.html?link=popup;paynum=".
219 % 'actionlabel' => 'Payment Receipt',
224 % my $refund_days = $conf->config('card_refund-days') || 120;
225 % if ( $cust_pay->closed !~ /^Y/i
226 % && $cust_pay->payby =~ /^(CARD|CHEK)$/
227 % && time-$cust_pay->_date < $refund_days*86400
228 % && $cust_pay->unrefunded > 0
229 % && $curuser->access_right('Refund payment')
231 % $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
232 % qq!paynum=!. $cust_pay->paynum. '"'.
233 % qq! TITLE="Send a refund for this payment to the payment gateway"!.
238 % if ( $cust_pay->closed !~ /^Y/i
239 % && ( ( $cust_pay->payby eq 'CARD'
240 % && $curuser->access_right('Credit card void')
242 % || ( $cust_pay->payby eq 'CHEK'
243 % && $curuser->access_right('Echeck void')
245 % || ( $cust_pay->payby !~ /^(CARD|CHEK)$/
246 % && $curuser->access_right('Regular void')
251 % $void = qq! (<A HREF="javascript:areyousure('!.
252 % qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum.
253 % qq!', 'Are you sure you want to void this payment?')"!.
254 % qq! TITLE="Void this payment from the database!.
255 % ( $cust_pay->payby =~ /^(CARD|CHEK)$/
256 % ? ' (do not send anything to the payment gateway)'
263 % if ( $cust_pay->closed !~ /^Y/i
264 % && $conf->exists('deletepayments')
265 % && $curuser->access_right('Delete payment')
268 % $delete = qq! (<A HREF="javascript:areyousure('!.
269 % qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum.
270 % qq!', 'Are you sure you want to delete this payment?')"!.
271 % qq! TITLE="Delete this payment from the database completely - not recommended"!.
276 % if ( $cust_pay->closed !~ /^Y/i
277 % && scalar(@cust_bill_pay)
278 % && $curuser->access_right('Unapply payment')
281 % $unapply = qq! (<A HREF="javascript:areyousure('!.
282 % qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum.
283 % qq!', 'Are you sure you want to unapply this payment?')"!.
284 % qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!.
288 % my $otaker = $cust_pay->otaker;
289 % $otaker = '<i>auto billing</i>' if $otaker eq 'fs_daily';
290 % $otaker = '<i>customer self-service</i>' if $otaker eq 'fs_selfservice';
293 % 'date' => $cust_pay->_date,
294 % 'desc' => $pre. "Payment$post by $otaker $info$desc".
295 % "$view$apply$refund$void$delete$unapply",
296 % 'payment' => $cust_pay->paid,
297 % 'target' => $target,
302 %foreach my $cust_pay_void ($cust_main->cust_pay_void) {
304 % my $payby = $cust_pay_void->payby;
305 % my $payinfo = $payby eq 'CARD'
306 % ? $cust_pay_void->paymask
307 % : $cust_pay_void->payinfo;
309 % $payby =~ s/^BILL$/Check #/ if $payinfo;
310 % $payby =~ s/^CHEK$/Electronic check /;
311 % $payby =~ s/^BILL$//;
312 % $payby =~ s/^(CARD|COMP)$/$1 /;
313 % my $info = $payby ? " ($payby$payinfo)" : '';
316 % if ( $cust_pay_void->closed !~ /^Y/i
317 % && $curuser->access_right('Unvoid')
320 % $unvoid = qq! (<A HREF="javascript:areyousure('!.
321 % qq!${p}misc/unvoid-cust_pay_void.cgi?!. $cust_pay_void->paynum.
322 % qq!', 'Are you sure you want to unvoid this payment?')"!.
323 % qq! TITLE="Unvoid this payment from the database!.
324 % ( $cust_pay_void->payby =~ /^(CARD|CHEK)$/
325 % ? ' (do not send anything to the payment gateway)'
332 % 'date' => $cust_pay_void->_date,
333 % 'desc' => "<DEL>Payment $info</DEL> <I>voided ".
334 % time2str("%D", $cust_pay_void->void_date).
335 % " by ". $cust_pay_void->otaker. '</i>'. $unvoid,
336 % 'void_payment' => $cust_pay_void->paid,
341 %#credits (some false laziness w/payments)
342 %foreach my $cust_credit ($cust_main->cust_credit) {
344 % my @cust_credit_bill = $cust_credit->cust_credit_bill;
345 % my @cust_credit_refund = $cust_credit->cust_credit_refund;
347 % my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
348 % if ( scalar(@cust_credit_bill) == 0
349 % && scalar(@cust_credit_refund) == 0 ) {
350 % #completely unapplied
351 % $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
352 % $post = '</FONT></B>';
353 % if ( $curuser->access_right('Apply credit') ) {
354 % $apply = ' ('. include( '/elements/popup_link.html',
355 % 'label' => 'apply',
356 % 'action' => "${p}edit/cust_credit_bill.cgi?".
357 % $cust_credit->crednum,
358 % 'actionlabel' => 'Apply credit',
360 % #default# 'height' => 336,
364 % } elsif ( scalar(@cust_credit_bill) == 1
365 % && scalar(@cust_credit_refund) == 0
366 % && $cust_credit->credited == 0 ) {
367 % #applied to one invoice, the usual situation
368 % $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum;
369 % } elsif ( scalar(@cust_credit_bill) == 0
370 % && scalar(@cust_credit_refund) == 1
371 % && $cust_credit->credited == 0 ) {
372 % #applied to one refund
373 % $desc = ' refunded on '. time2str("%D", $cust_credit_refund[0]->_date);
377 % foreach my $app ( sort { $a->_date <=> $b->_date }
378 % ( @cust_credit_bill, @cust_credit_refund ) ) {
379 % if ( $app->isa('FS::cust_credit_bill') ) {
380 % $desc .= ' '.
382 % ' applied to Invoice #'. $app->invnum.
384 % #' on '. time2str("%D", $app->_date).
385 % } elsif ( $app->isa('FS::cust_credit_refund') ) {
386 % $desc .= ' '.
388 % ' refunded on '. time2str("%D", $app->_date).
391 % die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
394 % if ( $cust_credit->credited > 0 ) {
395 % $desc .= ' <B><FONT COLOR="#FF0000">$'.
396 % $cust_credit->credited. ' unapplied</FONT></B>';
397 % if ( $curuser->access_right('Apply credit') ) {
398 % $desc = ' ('. include( '/elements/popup_link.html',
399 % 'label' => 'apply',
400 % 'action' => "${p}edit/cust_credit_bill.cgi?".
401 % $cust_credit->crednum,
402 % 'actionlabel' => 'Apply credit',
404 % #default# 'height' => 336,
413 % if ( $cust_credit->closed !~ /^Y/i
415 % #s'pose deleting a credit isn't bad like deleting a payment
416 % # and this needs to be generally available until we have credit voiding..
417 % #&& $conf->exists('deletecredits')
419 % && $curuser->access_right('Delete credit')
422 % $delete = qq! (<A HREF="javascript:areyousure('!.
423 % qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum.
424 % qq!', 'Are you sure you want to delete this credit?')">!.
429 % if ( $cust_credit->closed !~ /^Y/i
430 % && scalar(@cust_credit_bill)
431 % && $curuser->access_right('Unapply credit')
434 % $unapply = qq! (<A HREF="javascript:areyousure('!.
435 % qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum.
436 % qq!', 'Are you sure you want to unapply this credit?')">!.
441 % 'date' => $cust_credit->_date,
442 % 'desc' => $pre. "Credit$post by ". $cust_credit->otaker.
443 % ( $cust_credit->reason
444 % ? ' ('. $cust_credit->reason. ')'
447 % "$desc$apply$delete$unapply",
448 % 'credit' => $cust_credit->amount,
454 %foreach my $cust_refund ($cust_main->cust_refund) {
456 % my $payby = $cust_refund->payby;
457 % my $payinfo = $payby eq 'CARD'
458 % ? $cust_refund->paymask
459 % : $cust_refund->payinfo;
461 % $payby =~ s/^BILL$/Check #/ if $payinfo;
462 % $payby =~ s/^CHEK$/Electronic check /;
463 % $payby =~ s/^(CARD|COMP)$/$1 /;
466 % if ( $cust_refund->closed !~ /^Y/i
467 % && $conf->exists('deleterefunds')
468 % && $curuser->access_right('Delete refund')
471 % $delete = qq! (<A HREF="javascript:areyousure('!.
472 % qq!${p}misc/delete-cust_refund.cgi?!. $cust_refund->refundnum.
473 % qq!', 'Are you sure you want to delete this refund?')"!.
474 % qq! TITLE="Delete this refund from the database completely - not recommended"!.
479 % 'date' => $cust_refund->_date,
480 % 'desc' => "Refund ($payby$payinfo) by ". $cust_refund->otaker. "<BR>".
482 % 'refund' => $cust_refund->refund,
490 <% include("/elements/table-grid.html") %>
491 % my $bgcolor1 = '#eeeeee';
492 % my $bgcolor2 = '#ffffff';
498 <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH>
499 <TH CLASS="grid" BGCOLOR="#cccccc">Description</TH>
500 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Charge</FONT></TH>
501 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Payment</FONT></TH>
502 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
503 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Refund</FONT></TH>
504 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Balance</FONT></TH>
507 %#display payment history
509 %sub balance_forward_row {
510 % my( $b, $date ) = @_;
511 % my $conf = new FS::Conf;
512 % my $money_char = $conf->config('money_char') || '$';
513 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
515 <TR ID="balance_forward_row">
516 <TD CLASS="grid" BGCOLOR="#dddddd">
517 <% time2str("%D",$date) %>
520 <TD CLASS="grid" BGCOLOR="#dddddd">
521 <I>Starting balance on <% time2str("%D",$date) %></I>
522 (<A HREF="javascript:void(0);" onClick="show_history();">show prior history</A>)
525 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
526 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
527 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
528 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
529 <TD CLASS="grid" BGCOLOR="#dddddd"><I><% $balance_forward %></I></TD>
536 %my $money_char = $conf->config('money_char') || '$';
538 %my $years = $conf->config('payment_history-years') || 2;
539 %my $older_than = time - $years * 31556736; #60*60*24*365.24
542 %my $old_history = 0;
545 %foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
547 % $lastdate = $item->{'date'};
550 % if ( $item->{'date'} < $older_than ) {
551 % $display = ' STYLE="display:none" ';
557 % if ( $hidden && ! $seen++ ) {
558 % balance_forward_row($balance, $item->{'date'});
563 % if ( $bgcolor eq $bgcolor1 ) {
564 % $bgcolor = $bgcolor2;
566 % $bgcolor = $bgcolor1;
569 % my $charge = exists($item->{'charge'})
570 % ? sprintf("$money_char\%.2f", $item->{'charge'})
573 % my $payment = exists($item->{'payment'})
574 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
577 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
578 % $item->{'void_payment'}
580 % if exists($item->{'void_payment'});
582 % my $credit = exists($item->{'credit'})
583 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
586 % my $refund = exists($item->{'refund'})
587 % ? sprintf("$money_char\%.2f", $item->{'refund'})
590 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
592 % $balance += $item->{'charge'} if exists $item->{'charge'};
593 % $balance -= $item->{'payment'} if exists $item->{'payment'};
594 % $balance -= $item->{'credit'} if exists $item->{'credit'};
595 % $balance += $item->{'refund'} if exists $item->{'refund'};
596 % $balance = sprintf("%.2f", $balance);
597 % $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
598 % ( my $showbalance = $money_char. $balance ) =~ s/^\$\-/- \$/;
603 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
604 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
605 % unless ( !$target || $target{$target}++ ) {
607 <A NAME="<% $target %>">
610 <% time2str("%D",$item->{'date'}) %>
611 % if ( $target && $target{$target} == 1 ) {
618 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
619 <% $item->{'desc'} %>
621 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
624 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
627 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
630 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
633 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
639 %if ( scalar(@history) && $hidden && ! $seen++ ) {
640 % balance_forward_row($balance, $lastdate);
645 <SCRIPT TYPE="text/javascript">
647 function show_history () {
648 //alert('showing history!');
650 var balance_forward_row = document.getElementById('balance_forward_row');
652 balance_forward_row.style.display = 'none';
653 for ( var i = 0; i < <% $old_history %>; i++ ) {
654 var oldRow = document.getElementById('old_history'+i);
655 oldRow.style.display = '';
664 my( $cust_main ) = @_;
665 my $custnum = $cust_main->custnum;
667 my $conf = new FS::Conf;
669 my $curuser = $FS::CurrentUser::CurrentUser;
671 my @payby = grep /\w/, $conf->config('payby');
672 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
673 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
675 my %payby = map { $_=>1 } @payby;
678 'Queued' => 'O', #Open
680 'Complete' => 'R', #Resolved