2 % my( $cust_main ) = @_;
3 % my $custnum = $cust_main->custnum;
5 % my $conf = new FS::Conf;
7 % my $curuser = $FS::CurrentUser::CurrentUser;
9 % my @payby = grep /\w/, $conf->config('payby');
10 % #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
11 % @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
13 % my %payby = map { $_=>1 } @payby;
20 <BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR>
21 % if ( $payby{'BILL'} && $curuser->access_right('Post payment') ) {
24 <% $s++ ? ' | ' : '' %>
25 <A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<% $p %>edit/cust_pay.cgi?popup=1;payby=BILL;custnum=<% $custnum %>', 392, 336, 'cust_pay_popup' ), CAPTION, 'Enter check payment', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">Enter check payment</A>
27 % if ( $payby{'CASH'} && $curuser->access_right('Post payment') ) {
30 <% $s++ ? ' | ' : '' %>
31 <A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<% $p %>edit/cust_pay.cgi?popup=1;payby=CASH;custnum=<% $custnum %>', 392, 336, 'cust_pay_popup' ), CAPTION, 'Enter cash payment', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">Enter cash payment</A>
33 % if ( $payby{'WEST'} && $curuser->access_right('Post payment') ) {
36 <% $s++ ? ' | ' : '' %>
37 <A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;custnum=<% $custnum %>">Enter Western Union payment</A>
39 % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
40 % && $curuser->access_right('Process payment')
45 <% $s++ ? ' | ' : '' %>
46 <A HREF="<% $p %>misc/payment.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card payment</A>
48 % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
49 % && $curuser->access_right('Process payment')
54 <% $s++ ? ' | ' : '' %>
55 <A HREF="<% $p %>misc/payment.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) payment</A>
57 % if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) {
60 <% $s++ ? ' | ' : '' %>
61 <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;custnum=<% $custnum %>">Post manual (offline) credit card payment</A>
66 % if ( $curuser->access_right('Post credit') ) {
69 <A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<% $p %>edit/cust_credit.cgi?<% $custnum %>', 392, 336, 'cust_credit_popup' ), CAPTION, 'Enter credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">Enter credit</A>
78 %foreach my $cust_bill ($cust_main->cust_bill) {
79 % my $pre = ( $cust_bill->owed > 0 )
80 % ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open '
82 % my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : '';
83 % my $invnum = $cust_bill->invnum;
84 % my $link = $curuser->access_right('View invoices')
85 % ? qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!
88 % 'date' => $cust_bill->_date,
89 % 'desc' => $link. $pre.
90 % "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'.
91 % $post. ( $link ? '</A>' : '' ),
92 % 'charge' => $cust_bill->charged,
96 %#payments (some false laziness w/credits)
97 %foreach my $cust_pay ($cust_main->cust_pay) {
99 % my $payby = $cust_pay->payby;
102 % if ( $payby eq 'CARD' ) {
103 % $payinfo = $cust_pay->payinfo_masked;
104 % } elsif ( $payby eq 'CHEK' && $cust_pay->payinfo =~ /^(\d+)\@(\d+)$/ ) {
105 % $payinfo = "ABA $2, Acct# $1";
107 % $payinfo = $cust_pay->payinfo;
109 % my @cust_bill_pay = $cust_pay->cust_bill_pay;
110 % my @cust_pay_refund = $cust_pay->cust_pay_refund;
112 % my $target = "$payby$payinfo";
113 % $payby =~ s/^BILL$/Check #/ if $payinfo;
114 % $payby =~ s/^CHEK$/Electronic check /;
115 % $payby =~ s/^PREP$/Prepaid card /;
116 % $payby =~ s/^CARD$/Credit card #/;
117 % $payby =~ s/^COMP$/Complimentary by /;
118 % $payby =~ s/^CASH$/Cash/;
119 % $payby =~ s/^WEST$/Western Union/;
120 % $payby =~ s/^MCRD$/Manual credit card/;
121 % $payby =~ s/^BILL$//;
122 % my $info = $payby ? " ($payby$payinfo)" : '';
124 % my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
125 % if ( scalar(@cust_bill_pay) == 0
126 % && scalar(@cust_pay_refund) == 0 ) {
127 % #completely unapplied
128 % $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
129 % $post = '</FONT></B>';
130 % $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
132 % qq!', 392, 336, 'cust_bill_pay_popup' ), CAPTION, 'Apply payment', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">apply</A>)!;
134 % } elsif ( scalar(@cust_bill_pay) == 1
135 % && scalar(@cust_pay_refund) == 0
136 % && $cust_pay->unapplied == 0 ) {
137 % #applied to one invoice, the usual situation
138 % $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum;
139 % } elsif ( scalar(@cust_bill_pay) == 0
140 % && scalar(@cust_pay_refund) == 1
141 % && $cust_pay->unapplied == 0 ) {
142 % #applied to one refund
143 % $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
147 % foreach my $app ( sort { $a->_date <=> $b->_date }
148 % ( @cust_bill_pay, @cust_pay_refund ) ) {
149 % if ( $app->isa('FS::cust_bill_pay') ) {
150 % $desc .= ' '.
152 % ' applied to Invoice #'. $app->invnum.
154 % #' on '. time2str("%D", $cust_bill_pay->_date).
155 % } elsif ( $app->isa('FS::cust_pay_refund') ) {
156 % $desc .= ' '.
158 % ' refunded on '. time2str("%D", $app->_date).
161 % die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
164 % if ( $cust_pay->unapplied > 0 ) {
165 % $desc .= ' '.
166 % '<B><FONT COLOR="#FF0000">$'.
167 % $cust_pay->unapplied. ' unapplied</FONT></B>'.
168 % qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
170 % qq!', 392, 336, 'cust_bill_pay_popup' ), CAPTION, 'Apply payment', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">apply</A>)!.
176 % my $refund_days = $conf->config('card_refund-days') || 120;
177 % if ( $cust_pay->closed !~ /^Y/i
178 % && $cust_pay->payby =~ /^(CARD|CHEK)$/
179 % && time-$cust_pay->_date < $refund_days*86400
180 % && $cust_pay->unrefunded > 0
181 % && $curuser->access_right('Refund payment')
183 % $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
184 % qq!paynum=!. $cust_pay->paynum. '"'.
185 % qq! TITLE="Send a refund for this payment to the payment gateway"!.
190 % if ( $cust_pay->closed !~ /^Y/i
191 % && ( ( $cust_pay->payby eq 'CARD'
192 % && $curuser->access_right('Credit card void')
194 % || ( $cust_pay->payby eq 'CHEK'
195 % && $curuser->access_right('Echeck void')
197 % || ( $cust_pay->payby !~ /^(CARD|CHEK)$/
198 % && $curuser->access_right('Regular void')
203 % $void = qq! (<A HREF="javascript:areyousure('!.
204 % qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum.
205 % qq!', 'Are you sure you want to void this payment?')"!.
206 % qq! TITLE="Void this payment from the database!.
207 % ( $cust_pay->payby =~ /^(CARD|CHEK)$/
208 % ? ' (do not send anything to the payment gateway)'
215 % if ( $cust_pay->closed !~ /^Y/i
216 % && $conf->exists('deletepayments')
217 % && $curuser->access_right('Delete payment')
220 % $delete = qq! (<A HREF="javascript:areyousure('!.
221 % qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum.
222 % qq!', 'Are you sure you want to delete this payment?')"!.
223 % qq! TITLE="Delete this payment from the database completely - not recommended"!.
228 % if ( $cust_pay->closed !~ /^Y/i
229 % && scalar(@cust_bill_pay)
230 % && $curuser->access_right('Unapply payment')
233 % $unapply = qq! (<A HREF="javascript:areyousure('!.
234 % qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum.
235 % qq!', 'Are you sure you want to unapply this payment?')"!.
236 % qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!.
241 % 'date' => $cust_pay->_date,
242 % 'desc' => $pre. "Payment$post$info$desc".
243 % "$apply$refund$void$delete$unapply",
244 % 'payment' => $cust_pay->paid,
245 % 'target' => $target,
250 %foreach my $cust_pay_void ($cust_main->cust_pay_void) {
252 % my $payby = $cust_pay_void->payby;
253 % my $payinfo = $payby eq 'CARD'
254 % ? $cust_pay_void->payinfo_masked
255 % : $cust_pay_void->payinfo;
257 % $payby =~ s/^BILL$/Check #/ if $payinfo;
258 % $payby =~ s/^CHEK$/Electronic check /;
259 % $payby =~ s/^BILL$//;
260 % $payby =~ s/^(CARD|COMP)$/$1 /;
261 % my $info = $payby ? " ($payby$payinfo)" : '';
264 % if ( $cust_pay_void->closed !~ /^Y/i
265 % && $curuser->access_right('Unvoid')
268 % $unvoid = qq! (<A HREF="javascript:areyousure('!.
269 % qq!${p}misc/unvoid-cust_pay_void.cgi?!. $cust_pay_void->paynum.
270 % qq!', 'Are you sure you want to unvoid this payment?')"!.
271 % qq! TITLE="Unvoid this payment from the database!.
272 % ( $cust_pay_void->payby =~ /^(CARD|CHEK)$/
273 % ? ' (do not send anything to the payment gateway)'
280 % 'date' => $cust_pay_void->_date,
281 % 'desc' => "<DEL>Payment $info</DEL> <I>voided ".
282 % time2str("%D", $cust_pay_void->void_date).
283 % " by ". $cust_pay_void->otaker. '</i>'. $unvoid,
284 % 'void_payment' => $cust_pay_void->paid,
289 %#credits (some false laziness w/payments)
290 %foreach my $cust_credit ($cust_main->cust_credit) {
292 % my @cust_credit_bill = $cust_credit->cust_credit_bill;
293 % my @cust_credit_refund = $cust_credit->cust_credit_refund;
295 % my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
296 % if ( scalar(@cust_credit_bill) == 0
297 % && scalar(@cust_credit_refund) == 0 ) {
298 % #completely unapplied
299 % $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
300 % $post = '</FONT></B>';
301 % $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
302 % $cust_credit->crednum.
303 % qq!', 392, 336, 'cust_credit_bill_popup' ), CAPTION, 'Apply credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">apply</A>)!;
304 % } elsif ( scalar(@cust_credit_bill) == 1
305 % && scalar(@cust_credit_refund) == 0
306 % && $cust_credit->credited == 0 ) {
307 % #applied to one invoice, the usual situation
308 % $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum;
309 % } elsif ( scalar(@cust_credit_bill) == 0
310 % && scalar(@cust_credit_refund) == 1
311 % && $cust_credit->credited == 0 ) {
312 % #applied to one refund
313 % $desc = ' refunded on '. time2str("%D", $cust_credit_refund[0]->_date);
317 % foreach my $app ( sort { $a->_date <=> $b->_date }
318 % ( @cust_credit_bill, @cust_credit_refund ) ) {
319 % if ( $app->isa('FS::cust_credit_bill') ) {
320 % $desc .= ' '.
322 % ' applied to Invoice #'. $app->invnum.
324 % #' on '. time2str("%D", $app->_date).
325 % } elsif ( $app->isa('FS::cust_credit_refund') ) {
326 % $desc .= ' '.
328 % ' refunded on '. time2str("%D", $app->_date).
331 % die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
334 % if ( $cust_credit->credited > 0 ) {
335 % $desc .= ' <B><FONT COLOR="#FF0000">$'.
336 % $cust_credit->credited. ' unapplied</FONT></B>'.
337 % qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
338 % $cust_credit->crednum.
339 % qq!', 392, 336, 'cust_credit_bill_popup' ), CAPTION, 'Apply credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK ); return false;">apply</A>)!.
345 % if ( $cust_credit->closed !~ /^Y/i
347 % #s'pose deleting a credit isn't bad like deleting a payment
348 % # and this needs to be generally available until we have credit voiding..
349 % #&& $conf->exists('deletecredits')
351 % && $curuser->access_right('Delete credit')
354 % $delete = qq! (<A HREF="javascript:areyousure('!.
355 % qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum.
356 % qq!', 'Are you sure you want to delete this credit?')">!.
361 % if ( $cust_credit->closed !~ /^Y/i
362 % && scalar(@cust_credit_bill)
363 % && $curuser->access_right('Unapply credit')
366 % $unapply = qq! (<A HREF="javascript:areyousure('!.
367 % qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum.
368 % qq!', 'Are you sure you want to unapply this credit?')">!.
373 % 'date' => $cust_credit->_date,
374 % 'desc' => $pre. "Credit$post by ". $cust_credit->otaker.
375 % ( $cust_credit->reason
376 % ? ' ('. $cust_credit->reason. ')'
379 % "$desc$apply$delete$unapply",
380 % 'credit' => $cust_credit->amount,
386 %foreach my $cust_refund ($cust_main->cust_refund) {
388 % my $payby = $cust_refund->payby;
389 % my $payinfo = $payby eq 'CARD'
390 % ? $cust_refund->payinfo_masked
391 % : $cust_refund->payinfo;
393 % $payby =~ s/^BILL$/Check #/ if $payinfo;
394 % $payby =~ s/^CHEK$/Electronic check /;
395 % $payby =~ s/^(CARD|COMP)$/$1 /;
398 % 'date' => $cust_refund->_date,
399 % 'desc' => "Refund ($payby$payinfo) by ". $cust_refund->otaker,
400 % 'refund' => $cust_refund->refund,
408 <% include("/elements/table-grid.html") %>
409 % my $bgcolor1 = '#eeeeee';
410 % my $bgcolor2 = '#ffffff';
416 <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH>
417 <TH CLASS="grid" BGCOLOR="#cccccc">Description</TH>
418 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Charge</FONT></TH>
419 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Payment</FONT></TH>
420 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
421 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Refund</FONT></TH>
422 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Balance</FONT></TH>
425 %#display payment history
429 %my $money_char = $conf->config('money_char') || '$';
431 %my $years = $conf->config('payment_history-years') || 2;
432 %my $older_than = time - $years * 31556736; #60*60*24*365.24
435 %my $old_history = 0;
437 %foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
440 % if ( $item->{'date'} < $older_than ) {
441 % $display = ' STYLE="display:none" ';
447 % if ( $hidden && ! $seen++ ) {
448 % ( my $balance_forward = $money_char. $balance ) =~ s/^\$\-/- \$/;
452 <TR ID="balance_forward_row">
453 <TD CLASS="grid" BGCOLOR="#dddddd">
454 <% time2str("%D",$item->{'date'}) %>
457 <TD CLASS="grid" BGCOLOR="#dddddd">
458 <I>Starting balance on <% time2str("%D",$item->{'date'}) %></I>
459 (<A HREF="javascript:void(0);" onClick="show_history();">show prior history</A>)
462 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
463 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
464 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
465 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
466 <TD CLASS="grid" BGCOLOR="#dddddd"><I><% $balance_forward %></I></TD>
474 % if ( $bgcolor eq $bgcolor1 ) {
475 % $bgcolor = $bgcolor2;
477 % $bgcolor = $bgcolor1;
480 % my $charge = exists($item->{'charge'})
481 % ? sprintf("$money_char\%.2f", $item->{'charge'})
484 % my $payment = exists($item->{'payment'})
485 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
488 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
489 % $item->{'void_payment'}
491 % if exists($item->{'void_payment'});
493 % my $credit = exists($item->{'credit'})
494 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
497 % my $refund = exists($item->{'refund'})
498 % ? sprintf("$money_char\%.2f", $item->{'refund'})
501 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
503 % $balance += $item->{'charge'} if exists $item->{'charge'};
504 % $balance -= $item->{'payment'} if exists $item->{'payment'};
505 % $balance -= $item->{'credit'} if exists $item->{'credit'};
506 % $balance += $item->{'refund'} if exists $item->{'refund'};
507 % $balance = sprintf("%.2f", $balance);
508 % $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
509 % ( my $showbalance = $money_char. $balance ) =~ s/^\$\-/- \$/;
514 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
515 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
516 % unless ( !$target || $target{$target}++ ) {
518 <A NAME="<% $target %>">
521 <% time2str("%D",$item->{'date'}) %>
522 % if ( $target && $target{$target} == 1 ) {
529 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
530 <% $item->{'desc'} %>
532 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
535 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
538 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
541 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
544 <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
553 <SCRIPT TYPE="text/javascript">
555 function show_history () {
556 //alert('showing history!');
558 var balance_forward_row = document.getElementById('balance_forward_row');
560 balance_forward_row.style.display = 'none';
561 for ( var i = 0; i < <% $old_history %>; i++ ) {
562 var oldRow = document.getElementById('old_history'+i);
563 oldRow.style.display = '';