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 % if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) {
62 <% $s++ ? ' | ' : '' %>
63 <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCHK;custnum=<% $custnum %>"><% mt('Post manual (offline) electronic check payment') |h %></A>
66 <% $s ? '<BR>' : '' %>
71 % if ( $curuser->access_right('Post credit') ) {
72 <% $s++ ? ' | ' : '' %>
73 <& /elements/popup_link-cust_main.html,
74 'label' => emt('Enter credit'),
75 'action' => "${p}edit/cust_credit.cgi",
76 'cust_main' => $cust_main,
77 'actionlabel' => emt('Enter credit'),
78 'width' => ( $opt{'pkg-balances'} ? 763 : 616),
81 % if ( $curuser->access_right('Credit line items') ) {
82 <% $s++ ? ' | ' : '' %>
83 <& /elements/popup_link-cust_main.html,
84 'label' => emt('Credit line items'),
85 #'action' => "${p}search/cust_bill_pkg.cgi?nottax=1;type=select",
86 'action' => "${p}edit/credit-cust_bill_pkg.html",
87 'cust_main' => $cust_main,
88 'actionlabel' => emt('Credit line items'),
93 <% $s ? '<BR>' : '' %>
98 % if ( $payby{'BILL'} && $curuser->access_right(['Post refund', 'Post check refund']) ) {
99 <% $s++ ? ' | ' : '' %>
100 <& /elements/popup_link-cust_main.html,
101 'label' => emt('Enter check refund'),
102 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=BILL",
103 'cust_main' => $cust_main,
104 'actionlabel' => emt('Enter check refund'),
109 % if ( $payby{'CASH'} && $curuser->access_right(['Post refund', 'Post cash refund']) ) {
110 <% $s++ ? ' | ' : '' %>
111 <& /elements/popup_link-cust_main.html,
112 'label' => emt('Enter cash refund'),
113 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=CASH",
114 'cust_main' => $cust_main,
115 'actionlabel' => emt('Enter cash refund'),
120 %# someday, perhaps. very few gateways let you do unlinked refunds at all.
121 %# Authorize.net makes you sign a special form
123 %# % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
124 %# % && $curuser->access_right('Process refund')
125 %# % && ! $cust_main->is_encrypted($cust_main->payinfo)
127 %# <% $s++ ? ' | ' : '' %>
128 %# <A HREF="<% $p %>misc/refund.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card refund</A>
131 %# % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
132 %# % && $curuser->access_right('Process refund')
133 %# % && ! $cust_main->is_encrypted($cust_main->payinfo)
135 %# <% $s++ ? ' | ' : '' %>
136 %# <A HREF="<% $p %>misc/refund.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) refund</A>
139 % if ( $payby{'MCRD'} && $curuser->access_right('Post refund') ) {
140 <% $s++ ? ' | ' : '' %>
141 <A HREF="<% $p %>edit/cust_refund.cgi?payby=MCRD;custnum=<% $custnum %>"><% mt('Post manual (offline/POS) credit card refund') |h %></A>
144 % if ( $payby{'MCHK'} && $curuser->access_right('Post refund') ) {
145 <% $s++ ? ' | ' : '' %>
146 <A HREF="<% $p %>edit/cust_refund.cgi?payby=MCRD;custnum=<% $custnum %>"><% mt('Post manual (offline) electronic check refund') |h %></A>
150 <TD ALIGN="right" VALIGN="top">
152 %# invoice reports, combined statement
153 % if ( $curuser->access_right('List invoices') ) {
154 % if ( $curuser->access_right('Bulk send customer notices')
155 % && $cust_main->invoicing_list_emailonly ) {
157 <A HREF="<% $p %>misc/email-customer-statement.html?table=cust_main;agent_virt_agentnum=<% $cust_main->agentnum %>;custnum=<% $custnum %>"><% mt('Email statement to this customer') |h %></A>
161 % if ( $num_cust_bill > 0 ) {
162 <A HREF="<% $p %>view/cust_main_statement-pdf.cgi?<% $custnum %>"><%
163 mt('Download typeset statement PDF') |h %></A>
166 <A HREF="<% $p %>search/report_cust_bill.html?custnum=<% $custnum %>"><% mt('Invoice reports') |h %></A>
170 %# XXX payments, credits, refund reports
172 %# tax exemption link
174 % my $view_exemptions = $curuser->access_right('View customer tax exemptions');
175 % my $add_adjustment = ( $conf->exists('enable_tax_adjustments')
176 % && $curuser->access_right('Add customer tax adjustment')
178 % if ( $view_exemptions || $add_adjustment ) {
180 % if ( $view_exemptions ) {
181 <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>"><% mt('View tax exemptions') |h %></A>
182 <% $add_adjustment ? '|' : '' %>
185 % if ( $add_adjustment ) {
186 <& /elements/popup_link.html, {
187 'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum,
188 'label' => emt('Add tax adjustment'),
189 'actionlabel' => emt('Add tax adjustment'),
194 <A HREF="<% $p %>search/cust_tax_adjustment.html?custnum=<% $custnum %>"><% mt('View tax adjustments') |h %></A>
200 %# batched payment links
202 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
203 % && $curuser->access_right('View customer batched payments')
206 <% mt('View batched payments:') |h %>
207 % foreach my $status (qw( Queued In-transit Complete All )) {
208 <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% mt($status) |h %></A>
209 <% $status ne 'All' ? '|' : '' %>
214 %# pending payment links
216 % if ( $curuser->access_right('View customer pending payments')
217 % && scalar($cust_main->cust_pay_pending)
220 <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>"><% mt('View pending payments') |h %></A><BR>
230 <& /elements/table-grid.html &>
231 % my $bgcolor1 = '#eeeeee';
232 % my $bgcolor2 = '#ffffff';
236 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
237 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
238 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
239 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
240 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
241 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
242 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
245 %#display payment history
251 %my $old_history = 0;
254 %foreach my $item ( @history ) {
256 % $lastdate = $item->{'date'};
259 % if ( $item->{'hide'} ) {
260 % $display = ' STYLE="display:none" ';
263 % if ( $bgcolor eq $bgcolor1 ) {
264 % $bgcolor = $bgcolor2;
266 % $bgcolor = $bgcolor1;
269 % my $charge = exists($item->{'charge'})
270 % ? sprintf("$money_char\%.2f", $item->{'charge'})
271 % : exists($item->{'charge_nobal'})
272 % ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
273 % : exists($item->{'void_charge'})
274 % ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
277 % my $payment = exists($item->{'payment'})
278 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
281 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
282 % $item->{'void_payment'}
284 % if exists($item->{'void_payment'});
286 % my $credit = exists($item->{'credit'})
287 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
290 % $credit ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
291 % $item->{'void_credit'}
293 % if exists($item->{'void_credit'});
295 % my $refund = exists($item->{'refund'})
296 % ? sprintf("$money_char\%.2f", $item->{'refund'})
299 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
301 % my $showbalance = $money_char . $item->{'balance'};
302 % $showbalance =~ s/^\$\-/- \$/;
304 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
305 <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
306 % unless ( !$target || $target{$target}++ ) {
308 <A NAME="<% $target %>">
311 <% time2str($date_format, $item->{'date'}) %>
312 % if ( $target && $target{$target} == 1 ) {
319 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
320 <% $item->{'desc'} %>
322 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
325 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
328 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
331 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
334 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
339 % if ( $item->{'balance_forward'} ) {
340 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
349 <SCRIPT TYPE="text/javascript">
351 function show_history () {
352 //alert('showing history!');
354 var balance_forward_row = document.getElementById('balance_forward_row');
356 balance_forward_row.style.display = 'none';
357 for ( var i = 0; i < <% $old_history %>; i++ ) {
358 var oldRow = document.getElementById('old_history'+i);
359 oldRow.style.display = '';
365 <%def .balance_forward_row>
366 % my( $b, $date ) = @_;
367 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
369 <TR ID="balance_forward_row">
370 <TD CLASS="grid" BGCOLOR="#dddddd">
371 <% time2str($date_format, $date) %>
374 <TD CLASS="grid" BGCOLOR="#dddddd">
375 <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
376 (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
379 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
380 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
381 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
382 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
383 <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
388 my $conf = new FS::Conf;
389 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
390 my $money_char = $conf->config('money_char') || '$';
394 my( $cust_main ) = @_;
395 my $custnum = $cust_main->custnum;
397 my $curuser = $FS::CurrentUser::CurrentUser;
399 my @payby = grep /\w/, $conf->config('payby');
400 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
401 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
403 my %payby = map { $_=>1 } @payby;
406 'Queued' => 'O', #Open
408 'Complete' => 'R', #Resolved
418 ( map { $_ => scalar($conf->config($_)) }
419 qw( card_refund-days date_format )
421 ( map { $_ => $conf->exists($_) }
422 qw( deleteinvoices deletepayments deleterefunds pkg-balances
423 cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
426 'money_char ' => $money_char,
429 ( map { $_ => $curuser->access_right($_) }
431 'View invoices', 'Void invoices', 'Unvoid invoices', 'Delete invoices',
432 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
433 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
434 'Delete payment', 'Unapply payment',
435 'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
437 'Billing event reports', 'View customer billing events',
441 #customer information
442 'total_owed' => $cust_main->total_owed,
443 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
446 $opt{'date_format'} ||= '%m/%d/%Y';
449 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
451 'date' => $legacy_cust_bill->_date,
453 'num' => $legacy_cust_bill->legacyid,
454 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
455 'charge_nobal' => $legacy_cust_bill->charged,
460 my $num_cust_bill = 0;
461 foreach my $cust_bill ($cust_main->cust_bill) {
463 'date' => $cust_bill->_date,
465 'num' => $cust_bill->invnum,
466 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
467 'charge' => $cust_bill->charged,
473 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
475 'date' => $cust_bill_void->_date,
477 'num' => $cust_bill_void->invnum,
478 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
479 'void_charge' => $cust_bill_void->charged,
484 foreach my $cust_statement ($cust_main->cust_statement) {
486 'date' => $cust_statement->_date,
488 'num' => $cust_statement->statementnum,
489 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
490 #'charge' => $cust_bill->charged,
494 #payments (some false laziness w/credits)
495 foreach my $cust_pay ($cust_main->cust_pay) {
497 'date' => $cust_pay->_date,
499 'num' => $cust_pay->paynum,
500 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
501 'payment' => $cust_pay->paid,
502 #'target' => $target, #XXX
507 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
509 'date' => $cust_pay_pending->_date,
511 'num' => $cust_pay_pending->paypendingnum,
512 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
513 'void_payment' => $cust_pay_pending->paid,
519 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
521 'date' => $cust_pay_void->_date,
523 'num' => $cust_pay_void->paynum,
524 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
525 'void_payment' => $cust_pay_void->paid,
531 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
533 'date' => $cust_credit_void->_date,
535 'num' => $cust_credit_void->paynum,
536 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
537 'void_credit' => $cust_credit_void->amount,
542 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
544 'date' => $cust_pay_pending->_date,
546 'num' => $cust_pay_pending->paypendingnum,
547 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
548 'void_payment' => $cust_pay_pending->paid, #??
549 #'target' => $target, #XXX
552 #declined batch payments
553 foreach my $cust_pay_batch (
554 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
556 my $pay_batch = $cust_pay_batch->pay_batch;
558 'date' => $pay_batch->upload,
560 'num' => $cust_pay_batch->paybatchnum,
561 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
562 'void_payment' => $cust_pay_batch->amount,
566 #credits (some false laziness w/payments)
567 foreach my $cust_credit ($cust_main->cust_credit) {
569 'date' => $cust_credit->_date,
571 'num' => $cust_credit->crednum,
572 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
573 'credit' => $cust_credit->amount,
579 foreach my $cust_refund ($cust_main->cust_refund) {
581 'date' => $cust_refund->_date,
583 'num' => $cust_refund->refundnum,
584 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
585 'refund' => $cust_refund->refund,
590 # sort in forward order first, and calculate running balances
591 my $years = $conf->config('payment_history-years') || 2;
592 my $older_than = time - $years * 31556926; #60*60*24*365.2422
595 @history = sort { $a->{date} <=> $b->{date}
596 or $a->{order} <=> $b->{order}
597 or $a->{num} <=> $b->{num}
603 foreach my $item (@history) {
604 $balance += $item->{'charge'} if exists $item->{'charge'};
605 $balance -= $item->{'payment'} if exists $item->{'payment'};
606 $balance -= $item->{'credit'} if exists $item->{'credit'};
607 $balance += $item->{'refund'} if exists $item->{'refund'};
608 $balance = sprintf("%.2f", $balance);
609 $balance =~ s/^\-0\.00$/0.00/;
610 $item->{'balance'} = $balance;
612 if ( $item->{'date'} < $older_than ) {
614 } elsif ( $history[$i-1]->{'hide'} ) {
615 # this is the end of the hidden section
616 $history[$i-1]->{'balance_forward'} = 1;
620 if ( @history and $history[-1]->{'hide'} ) {
621 # then everything is hidden
622 $history[-1]->{'balance_forward'} = 1;
625 # then sort in user-pref order
626 if ( $curuser->option('history_order') eq 'newest' ) {
627 @history = sort { $b->{date} <=> $a->{date}
628 or $b->{order} <=> $a->{order} #or still forward here?
629 or $b->{num} <=> $a->{num}
632 } # else it's already oldest-first, and there are no other options yet
634 sub translate_payby {
635 my ($payby,$payinfo) = (shift,shift);
637 FS::payby->payby2shortname,
638 BILL => $payinfo ? emt('Check #') : '',
639 CHEK => emt('Electronic check '),
640 PREP => emt('Prepaid card '),
641 CARD => emt('Credit card #'),
642 COMP => emt('Complimentary by '),
643 #CASH => emt('Cash'),
644 #WEST => emt('Western Union'),
645 #MCRD => emt('Manual credit card'),
647 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
651 sub translate_payby_refund {
652 my ($payby,$payinfo) = (shift,shift);
654 FS::payby->payby2shortname,
655 BILL => $payinfo ? emt('Check #') : emt('Check'),
656 CHEK => emt('Electronic check '),
657 CARD => emt('Credit card #'),
658 COMP => emt('Complimentary by '),
660 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
664 sub translate_payinfo {
666 my $payby = $object->payby;
667 my $payinfo = $object->payinfo;
669 if ( $payby eq 'CARD' ) {
670 $payinfo = $object->paymask;
671 } elsif ( $payby eq 'CHEK' ) {
672 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
673 my( $account, $aba ) = split('@', $object->paymask );
674 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
675 my($branch, $routing) = ($1, $2);
676 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
677 $routing, $branch, $account);
679 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
686 sub areyousure_link {
687 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
688 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';