8 % if ( $payby{'BILL'} && $curuser->access_right(['Post payment', 'Post check payment' ]) ) {
9 <% $s++ ? ' | ' : '' %>
10 <& /elements/popup_link-cust_main.html,
11 'label' => emt('Enter check payment'),
12 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=BILL",
13 'cust_main' => $cust_main,
14 'actionlabel' => emt('Enter check payment'),
15 'width' => ( $opt{'pkg-balances'} ? 763 : 392),
20 % if ( $payby{'CASH'} && $curuser->access_right(['Post payment', 'Post cash payment']) ) {
21 <% $s++ ? ' | ' : '' %>
22 <& /elements/popup_link-cust_main.html,
23 'label' => emt('Enter cash payment'),
24 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=CASH",
25 'cust_main' => $cust_main,
26 'actionlabel' => emt('Enter cash payment'),
27 'width' => ( $opt{'pkg-balances'} ? 763 : 392),
32 % if ( $payby{'WEST'} && $curuser->access_right('Post payment') ) {
33 <% $s++ ? ' | ' : '' %>
34 <A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;custnum=<% $custnum %>"><% mt('Enter Western Union payment') |h %></A>
37 <% $s ? '<BR>' : '' %>
40 % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
41 % && $curuser->access_right(['Process payment', 'Process credit card payment'])
42 % && ! $cust_main->is_encrypted($cust_main->payinfo)
44 <% $s++ ? ' | ' : '' %>
45 <A HREF="<% $p %>misc/payment.cgi?payby=CARD;custnum=<% $custnum %>"><% mt('Process credit card payment') |h %></A>
48 % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
49 % && $curuser->access_right(['Process payment', 'Process Echeck payment'])
50 % && ! $cust_main->is_encrypted($cust_main->payinfo)
52 <% $s++ ? ' | ' : '' %>
53 <A HREF="<% $p %>misc/payment.cgi?payby=CHEK;custnum=<% $custnum %>"><% mt('Process electronic check (ACH) payment') |h %></A>
56 % if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) {
57 <% $s++ ? ' | ' : '' %>
58 <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;custnum=<% $custnum %>"><% mt('Post manual (offline/POS) credit card payment') |h %></A>
61 <% $s ? '<BR>' : '' %>
66 % if ( $curuser->access_right('Post credit') ) {
67 <% $s++ ? ' | ' : '' %>
68 <& /elements/popup_link-cust_main.html,
69 'label' => emt('Enter credit'),
70 'action' => "${p}edit/cust_credit.cgi",
71 'cust_main' => $cust_main,
72 'actionlabel' => emt('Enter credit'),
73 'width' => ( $opt{'pkg-balances'} ? 763 : 616),
76 % if ( $curuser->access_right('Credit line items') ) {
77 <% $s++ ? ' | ' : '' %>
78 <& /elements/popup_link-cust_main.html,
79 'label' => emt('Credit line items'),
80 #'action' => "${p}search/cust_bill_pkg.cgi?nottax=1;type=select",
81 'action' => "${p}edit/credit-cust_bill_pkg.html",
82 'cust_main' => $cust_main,
83 'actionlabel' => emt('Credit line items'),
88 <% $s ? '<BR>' : '' %>
93 % if ( $payby{'BILL'} && $curuser->access_right(['Post refund', 'Post check refund']) ) {
94 <% $s++ ? ' | ' : '' %>
95 <& /elements/popup_link-cust_main.html,
96 'label' => emt('Enter check refund'),
97 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=BILL",
98 'cust_main' => $cust_main,
99 'actionlabel' => emt('Enter check refund'),
104 % if ( $payby{'CASH'} && $curuser->access_right(['Post refund', 'Post cash refund']) ) {
105 <% $s++ ? ' | ' : '' %>
106 <& /elements/popup_link-cust_main.html,
107 'label' => emt('Enter cash refund'),
108 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=CASH",
109 'cust_main' => $cust_main,
110 'actionlabel' => emt('Enter cash refund'),
115 %# someday, perhaps. very few gateways let you do unlinked refunds at all.
116 %# Authorize.net makes you sign a special form
118 %# % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
119 %# % && $curuser->access_right('Process refund')
120 %# % && ! $cust_main->is_encrypted($cust_main->payinfo)
122 %# <% $s++ ? ' | ' : '' %>
123 %# <A HREF="<% $p %>misc/refund.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card refund</A>
126 %# % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
127 %# % && $curuser->access_right('Process refund')
128 %# % && ! $cust_main->is_encrypted($cust_main->payinfo)
130 %# <% $s++ ? ' | ' : '' %>
131 %# <A HREF="<% $p %>misc/refund.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) refund</A>
134 % if ( $payby{'MCRD'} && $curuser->access_right('Post refund') ) {
135 <% $s++ ? ' | ' : '' %>
136 <A HREF="<% $p %>edit/cust_refund.cgi?payby=MCRD;custnum=<% $custnum %>"><% mt('Post manual (offline/POS) credit card refund') |h %></A>
140 <TD ALIGN="right" VALIGN="top">
142 %# invoice reports, combined statement
143 % if ( $curuser->access_right('List invoices') ) {
144 % if ( $num_cust_bill > 0 ) {
145 <A HREF="<% $p %>view/cust_main_statement-pdf.cgi?<% $custnum %>"><%
146 mt('Download typeset statement PDF') |h %></A>
149 <A HREF="<% $p %>search/report_cust_bill.html?custnum=<% $custnum %>"><% mt('Invoice reports') |h %></A>
153 %# XXX payments, credits, refund reports
155 %# tax exemption link
157 % my $view_exemptions = $curuser->access_right('View customer tax exemptions');
158 % my $add_adjustment = ( $conf->exists('enable_tax_adjustments')
159 % && $curuser->access_right('Add customer tax adjustment')
161 % if ( $view_exemptions || $add_adjustment ) {
163 % if ( $view_exemptions ) {
164 <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>"><% mt('View tax exemptions') |h %></A>
165 <% $add_adjustment ? '|' : '' %>
168 % if ( $add_adjustment ) {
169 <& /elements/popup_link.html, {
170 'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum,
171 'label' => emt('Add tax adjustment'),
172 'actionlabel' => emt('Add tax adjustment'),
177 <A HREF="<% $p %>search/cust_tax_adjustment.html?custnum=<% $custnum %>"><% mt('View tax adjustments') |h %></A>
183 %# batched payment links
185 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
186 % && $curuser->access_right('View customer batched payments')
189 <% mt('View batched payments:') |h %>
190 % foreach my $status (qw( Queued In-transit Complete All )) {
191 <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% mt($status) |h %></A>
192 <% $status ne 'All' ? '|' : '' %>
197 %# pending payment links
199 % if ( $curuser->access_right('View customer pending payments')
200 % && scalar($cust_main->cust_pay_pending)
203 <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>"><% mt('View pending payments') |h %></A><BR>
213 <& /elements/table-grid.html &>
214 % my $bgcolor1 = '#eeeeee';
215 % my $bgcolor2 = '#ffffff';
219 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
220 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
221 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
222 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
223 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
224 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
225 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
228 %#display payment history
234 %my $old_history = 0;
237 %foreach my $item ( @history ) {
239 % $lastdate = $item->{'date'};
242 % if ( $item->{'hide'} ) {
243 % $display = ' STYLE="display:none" ';
246 % if ( $bgcolor eq $bgcolor1 ) {
247 % $bgcolor = $bgcolor2;
249 % $bgcolor = $bgcolor1;
252 % my $charge = exists($item->{'charge'})
253 % ? sprintf("$money_char\%.2f", $item->{'charge'})
254 % : exists($item->{'charge_nobal'})
255 % ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
256 % : exists($item->{'void_charge'})
257 % ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
260 % my $payment = exists($item->{'payment'})
261 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
264 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
265 % $item->{'void_payment'}
267 % if exists($item->{'void_payment'});
269 % my $credit = exists($item->{'credit'})
270 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
273 % $credit ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
274 % $item->{'void_credit'}
276 % if exists($item->{'void_credit'});
278 % my $refund = exists($item->{'refund'})
279 % ? sprintf("$money_char\%.2f", $item->{'refund'})
282 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
284 % my $showbalance = $money_char . $item->{'balance'};
285 % $showbalance =~ s/^\$\-/- \$/;
287 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
288 <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
289 % unless ( !$target || $target{$target}++ ) {
291 <A NAME="<% $target %>">
294 <% time2str($date_format, $item->{'date'}) %>
295 % if ( $target && $target{$target} == 1 ) {
302 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
303 <% $item->{'desc'} %>
305 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
308 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
311 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
314 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
317 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
322 % if ( $item->{'balance_forward'} ) {
323 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
332 <SCRIPT TYPE="text/javascript">
334 function show_history () {
335 //alert('showing history!');
337 var balance_forward_row = document.getElementById('balance_forward_row');
339 balance_forward_row.style.display = 'none';
340 for ( var i = 0; i < <% $old_history %>; i++ ) {
341 var oldRow = document.getElementById('old_history'+i);
342 oldRow.style.display = '';
348 <%def .balance_forward_row>
349 % my( $b, $date ) = @_;
350 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
352 <TR ID="balance_forward_row">
353 <TD CLASS="grid" BGCOLOR="#dddddd">
354 <% time2str($date_format, $date) %>
357 <TD CLASS="grid" BGCOLOR="#dddddd">
358 <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
359 (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
362 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
363 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
364 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
365 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
366 <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
371 my $conf = new FS::Conf;
372 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
373 my $money_char = $conf->config('money_char') || '$';
377 my( $cust_main ) = @_;
378 my $custnum = $cust_main->custnum;
380 my $curuser = $FS::CurrentUser::CurrentUser;
382 my @payby = grep /\w/, $conf->config('payby');
383 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
384 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
386 my %payby = map { $_=>1 } @payby;
389 'Queued' => 'O', #Open
391 'Complete' => 'R', #Resolved
401 ( map { $_ => scalar($conf->config($_)) }
402 qw( card_refund-days date_format )
404 ( map { $_ => $conf->exists($_) }
405 qw( deleteinvoices deletepayments deleterefunds pkg-balances
406 cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
409 'money_char ' => $money_char,
412 ( map { $_ => $curuser->access_right($_) }
414 'View invoices', 'Void invoices', 'Unvoid invoices', 'Delete invoices',
415 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
416 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
417 'Delete payment', 'Unapply payment',
418 'Apply credit', 'Delete credit', 'Unapply credit',
420 'Billing event reports', 'View customer billing events',
424 #customer information
425 'total_owed' => $cust_main->total_owed,
426 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
429 $opt{'date_format'} ||= '%m/%d/%Y';
432 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
434 'date' => $legacy_cust_bill->_date,
436 'num' => $legacy_cust_bill->legacyid,
437 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
438 'charge_nobal' => $legacy_cust_bill->charged,
443 my $num_cust_bill = 0;
444 foreach my $cust_bill ($cust_main->cust_bill) {
446 'date' => $cust_bill->_date,
448 'num' => $cust_bill->invnum,
449 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
450 'charge' => $cust_bill->charged,
456 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
458 'date' => $cust_bill_void->_date,
460 'num' => $cust_bill_void->invnum,
461 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
462 'void_charge' => $cust_bill_void->charged,
467 foreach my $cust_statement ($cust_main->cust_statement) {
469 'date' => $cust_statement->_date,
471 'num' => $cust_statement->statementnum,
472 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
473 #'charge' => $cust_bill->charged,
477 #payments (some false laziness w/credits)
478 foreach my $cust_pay ($cust_main->cust_pay) {
480 'date' => $cust_pay->_date,
482 'num' => $cust_pay->paynum,
483 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
484 'payment' => $cust_pay->paid,
485 #'target' => $target, #XXX
490 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
492 'date' => $cust_pay_pending->_date,
494 'num' => $cust_pay_pending->paypendingnum,
495 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
496 'void_payment' => $cust_pay_pending->paid,
502 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
504 'date' => $cust_pay_void->_date,
506 'num' => $cust_pay_void->paynum,
507 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
508 'void_payment' => $cust_pay_void->paid,
514 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
516 'date' => $cust_credit_void->_date,
518 'num' => $cust_credit_void->paynum,
519 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
520 'void_credit' => $cust_credit_void->amount,
525 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
527 'date' => $cust_pay_pending->_date,
529 'num' => $cust_pay_pending->paypendingnum,
530 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
531 'void_payment' => $cust_pay_pending->paid, #??
532 #'target' => $target, #XXX
535 #declined batch payments
536 foreach my $cust_pay_batch (
537 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
539 my $pay_batch = $cust_pay_batch->pay_batch;
541 'date' => $pay_batch->upload,
543 'num' => $cust_pay_batch->paybatchnum,
544 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
545 'void_payment' => $cust_pay_batch->amount,
549 #credits (some false laziness w/payments)
550 foreach my $cust_credit ($cust_main->cust_credit) {
552 'date' => $cust_credit->_date,
554 'num' => $cust_credit->crednum,
555 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
556 'credit' => $cust_credit->amount,
562 foreach my $cust_refund ($cust_main->cust_refund) {
564 'date' => $cust_refund->_date,
566 'num' => $cust_refund->refundnum,
567 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
568 'refund' => $cust_refund->refund,
573 # sort in forward order first, and calculate running balances
574 my $years = $conf->config('payment_history-years') || 2;
575 my $older_than = time - $years * 31556926; #60*60*24*365.2422
578 @history = sort { $a->{date} <=> $b->{date}
579 or $a->{order} <=> $b->{order}
580 or $a->{num} <=> $b->{num}
586 foreach my $item (@history) {
587 $balance += $item->{'charge'} if exists $item->{'charge'};
588 $balance -= $item->{'payment'} if exists $item->{'payment'};
589 $balance -= $item->{'credit'} if exists $item->{'credit'};
590 $balance += $item->{'refund'} if exists $item->{'refund'};
591 $balance = sprintf("%.2f", $balance);
592 $balance =~ s/^\-0\.00$/0.00/;
593 $item->{'balance'} = $balance;
595 if ( $item->{'date'} < $older_than ) {
597 } elsif ( $history[$i-1]->{'hide'} ) {
598 # this is the end of the hidden section
599 $history[$i-1]->{'balance_forward'} = 1;
603 if ( @history and $history[-1]->{'hide'} ) {
604 # then everything is hidden
605 $history[-1]->{'balance_forward'} = 1;
608 # then sort in user-pref order
609 if ( $curuser->option('history_order') eq 'newest' ) {
610 @history = sort { $b->{date} <=> $a->{date}
611 or $b->{order} <=> $a->{order} #or still forward here?
612 or $b->{num} <=> $a->{num}
615 } # else it's already oldest-first, and there are no other options yet
617 sub translate_payby {
618 my ($payby,$payinfo) = (shift,shift);
620 FS::payby->payby2shortname,
621 BILL => $payinfo ? emt('Check #') : '',
622 CHEK => emt('Electronic check '),
623 PREP => emt('Prepaid card '),
624 CARD => emt('Credit card #'),
625 COMP => emt('Complimentary by '),
626 #CASH => emt('Cash'),
627 #WEST => emt('Western Union'),
628 #MCRD => emt('Manual credit card'),
630 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
634 sub translate_payby_refund {
635 my ($payby,$payinfo) = (shift,shift);
637 FS::payby->payby2shortname,
638 BILL => $payinfo ? emt('Check #') : emt('Check'),
639 CHEK => emt('Electronic check '),
640 CARD => emt('Credit card #'),
641 COMP => emt('Complimentary by '),
643 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
647 sub translate_payinfo {
649 my $payby = $object->payby;
650 my $payinfo = $object->payinfo;
652 if ( $payby eq 'CARD' ) {
653 $payinfo = $object->paymask;
654 } elsif ( $payby eq 'CHEK' ) {
655 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
656 my( $account, $aba ) = split('@', $object->paymask );
657 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
658 my($branch, $routing) = ($1, $2);
659 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
660 $routing, $branch, $account);
662 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
669 sub areyousure_link {
670 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
671 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';