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('Resend invoices')
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 % && $curuser->access_right('View legacy typeset statements')
164 <A HREF="<% $p %>view/cust_main_statement-pdf.cgi?<% $custnum %>"><%
165 mt('Download typeset statement PDF') |h %></A>
168 <A HREF="<% $p %>search/report_cust_bill.html?custnum=<% $custnum %>"><% mt('Invoice reports') |h %></A>
172 %# XXX payments, credits, refund reports
174 %# tax exemption link
176 % my $view_exemptions = $curuser->access_right('View customer tax exemptions');
177 % my $add_adjustment = ( $conf->exists('enable_tax_adjustments')
178 % && $curuser->access_right('Add customer tax adjustment')
180 % if ( $view_exemptions || $add_adjustment ) {
182 % if ( $view_exemptions ) {
183 <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>"><% mt('View tax exemptions') |h %></A>
184 <% $add_adjustment ? '|' : '' %>
187 % if ( $add_adjustment ) {
188 <& /elements/popup_link.html, {
189 'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum,
190 'label' => emt('Add tax adjustment'),
191 'actionlabel' => emt('Add tax adjustment'),
196 <A HREF="<% $p %>search/cust_tax_adjustment.html?custnum=<% $custnum %>"><% mt('View tax adjustments') |h %></A>
202 %# batched payment links
204 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
205 % && $curuser->access_right('View customer batched payments')
208 <% mt('View batched payments:') |h %>
209 % foreach my $status (qw( Queued In-transit Complete All )) {
210 <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% mt($status) |h %></A>
211 <% $status ne 'All' ? '|' : '' %>
216 %# pending payment links
218 % if ( $curuser->access_right('View customer pending payments')
219 % && scalar($cust_main->cust_pay_pending)
222 <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>"><% mt('View pending payments') |h %></A><BR>
232 <& /elements/table-grid.html &>
233 % my $bgcolor1 = '#eeeeee';
234 % my $bgcolor2 = '#ffffff';
238 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
239 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
240 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
241 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
242 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
243 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
244 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
247 %#display payment history
253 %my $old_history = 0;
256 %foreach my $item ( @history ) {
258 % $lastdate = $item->{'date'};
261 % if ( $item->{'hide'} ) {
262 % $display = ' STYLE="display:none" ';
265 % if ( $bgcolor eq $bgcolor1 ) {
266 % $bgcolor = $bgcolor2;
268 % $bgcolor = $bgcolor1;
271 % my $charge = exists($item->{'charge'})
272 % ? sprintf("$money_char\%.2f", $item->{'charge'})
273 % : exists($item->{'charge_nobal'})
274 % ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
275 % : exists($item->{'void_charge'})
276 % ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
279 % my $payment = exists($item->{'payment'})
280 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
283 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
284 % $item->{'void_payment'}
286 % if exists($item->{'void_payment'});
288 % my $credit = exists($item->{'credit'})
289 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
292 % $credit ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
293 % $item->{'void_credit'}
295 % if exists($item->{'void_credit'});
297 % my $refund = exists($item->{'refund'})
298 % ? sprintf("$money_char\%.2f", $item->{'refund'})
301 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
303 % my $showbalance = $money_char . $item->{'balance'};
304 % $showbalance =~ s/^\$\-/- \$/;
306 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
307 <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
308 % unless ( !$target || $target{$target}++ ) {
310 <A NAME="<% $target %>">
313 <% time2str($date_format, $item->{'date'}) %>
314 % if ( $target && $target{$target} == 1 ) {
321 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
322 <% $item->{'desc'} %>
324 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
327 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
330 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
333 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
336 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
341 % if ( $item->{'balance_forward'} ) {
342 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
351 <SCRIPT TYPE="text/javascript">
353 function show_history () {
354 //alert('showing history!');
356 var balance_forward_row = document.getElementById('balance_forward_row');
358 balance_forward_row.style.display = 'none';
359 for ( var i = 0; i < <% $old_history %>; i++ ) {
360 var oldRow = document.getElementById('old_history'+i);
361 oldRow.style.display = '';
367 <%def .balance_forward_row>
368 % my( $b, $date ) = @_;
369 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
371 <TR ID="balance_forward_row">
372 <TD CLASS="grid" BGCOLOR="#dddddd">
373 <% time2str($date_format, $date) %>
376 <TD CLASS="grid" BGCOLOR="#dddddd">
377 <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
378 (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
381 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
382 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
383 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
384 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
385 <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
390 my $conf = new FS::Conf;
391 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
392 my $money_char = $conf->config('money_char') || '$';
396 my( $cust_main ) = @_;
397 my $custnum = $cust_main->custnum;
399 my $curuser = $FS::CurrentUser::CurrentUser;
401 my @payby = grep /\w/, $conf->config('payby');
402 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
403 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
405 my %payby = map { $_=>1 } @payby;
408 'Queued' => 'O', #Open
410 'Complete' => 'R', #Resolved
420 ( map { $_ => scalar($conf->config($_)) }
421 qw( card_refund-days date_format )
423 ( map { $_ => $conf->exists($_) }
424 qw( deleteinvoices deletepayments deleterefunds pkg-balances
425 cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
428 'money_char ' => $money_char,
431 ( map { $_ => $curuser->access_right($_) }
433 'View invoices', 'Void invoices', 'Unvoid invoices', 'Delete invoices',
434 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
435 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
436 'Delete payment', 'Unapply payment',
437 'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
439 'Billing event reports', 'View customer billing events',
443 #customer information
444 'total_owed' => $cust_main->total_owed,
445 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
448 $opt{'date_format'} ||= '%m/%d/%Y';
451 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
453 'date' => $legacy_cust_bill->_date,
455 'num' => $legacy_cust_bill->legacyid,
456 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
457 'charge_nobal' => $legacy_cust_bill->charged,
462 my $num_cust_bill = 0;
463 foreach my $cust_bill ($cust_main->cust_bill) {
465 'date' => $cust_bill->_date,
467 'num' => $cust_bill->invnum,
468 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
469 'charge' => $cust_bill->charged,
475 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
477 'date' => $cust_bill_void->_date,
479 'num' => $cust_bill_void->invnum,
480 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
481 'void_charge' => $cust_bill_void->charged,
486 foreach my $cust_statement ($cust_main->cust_statement) {
488 'date' => $cust_statement->_date,
490 'num' => $cust_statement->statementnum,
491 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
492 #'charge' => $cust_bill->charged,
496 #payments (some false laziness w/credits)
497 foreach my $cust_pay ($cust_main->cust_pay) {
499 'date' => $cust_pay->_date,
501 'num' => $cust_pay->paynum,
502 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
503 'payment' => $cust_pay->paid,
504 #'target' => $target, #XXX
509 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
511 'date' => $cust_pay_pending->_date,
513 'num' => $cust_pay_pending->paypendingnum,
514 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
515 'void_payment' => $cust_pay_pending->paid,
521 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
523 'date' => $cust_pay_void->_date,
525 'num' => $cust_pay_void->paynum,
526 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
527 'void_payment' => $cust_pay_void->paid,
533 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
535 'date' => $cust_credit_void->_date,
537 'num' => $cust_credit_void->paynum,
538 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
539 'void_credit' => $cust_credit_void->amount,
544 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
546 'date' => $cust_pay_pending->_date,
548 'num' => $cust_pay_pending->paypendingnum,
549 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
550 'void_payment' => $cust_pay_pending->paid, #??
551 #'target' => $target, #XXX
554 #declined batch payments
555 foreach my $cust_pay_batch (
556 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
558 my $pay_batch = $cust_pay_batch->pay_batch;
560 'date' => $pay_batch->upload,
562 'num' => $cust_pay_batch->paybatchnum,
563 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
564 'void_payment' => $cust_pay_batch->amount,
568 #credits (some false laziness w/payments)
569 foreach my $cust_credit ($cust_main->cust_credit) {
571 'date' => $cust_credit->_date,
573 'num' => $cust_credit->crednum,
574 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
575 'credit' => $cust_credit->amount,
581 foreach my $cust_refund ($cust_main->cust_refund) {
583 'date' => $cust_refund->_date,
585 'num' => $cust_refund->refundnum,
586 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
587 'refund' => $cust_refund->refund,
592 # sort in forward order first, and calculate running balances
593 my $years = $conf->config('payment_history-years') || 2;
594 my $older_than = time - $years * 31556926; #60*60*24*365.2422
597 @history = sort { $a->{date} <=> $b->{date}
598 or $a->{order} <=> $b->{order}
599 or $a->{num} <=> $b->{num}
605 foreach my $item (@history) {
606 $balance += $item->{'charge'} if exists $item->{'charge'};
607 $balance -= $item->{'payment'} if exists $item->{'payment'};
608 $balance -= $item->{'credit'} if exists $item->{'credit'};
609 $balance += $item->{'refund'} if exists $item->{'refund'};
610 $balance = sprintf("%.2f", $balance);
611 $balance =~ s/^\-0\.00$/0.00/;
612 $item->{'balance'} = $balance;
614 if ( $item->{'date'} < $older_than ) {
616 } elsif ( $history[$i-1]->{'hide'} ) {
617 # this is the end of the hidden section
618 $history[$i-1]->{'balance_forward'} = 1;
622 if ( @history and $history[-1]->{'hide'} ) {
623 # then everything is hidden
624 $history[-1]->{'balance_forward'} = 1;
627 # then sort in user-pref order
628 if ( $curuser->option('history_order') eq 'newest' ) {
629 @history = sort { $b->{date} <=> $a->{date}
630 or $b->{order} <=> $a->{order} #or still forward here?
631 or $b->{num} <=> $a->{num}
634 } # else it's already oldest-first, and there are no other options yet
636 sub translate_payby {
637 my ($payby,$payinfo) = (shift,shift);
639 FS::payby->payby2shortname,
640 BILL => $payinfo ? emt('Check #') : '',
641 CHEK => emt('Electronic check '),
642 PREP => emt('Prepaid card '),
643 CARD => emt('Credit card #'),
644 COMP => emt('Complimentary by '),
645 #CASH => emt('Cash'),
646 #WEST => emt('Western Union'),
647 #MCRD => emt('Manual credit card'),
649 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
653 sub translate_payby_refund {
654 my ($payby,$payinfo) = (shift,shift);
656 FS::payby->payby2shortname,
657 BILL => $payinfo ? emt('Check #') : emt('Check'),
658 CHEK => emt('Electronic check '),
659 CARD => emt('Credit card #'),
660 COMP => emt('Complimentary by '),
662 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
666 sub translate_payinfo {
668 my $payby = $object->payby;
669 my $payinfo = $object->payinfo;
671 if ( $payby eq 'CARD' ) {
672 $payinfo = $object->paymask;
673 } elsif ( $payby eq 'CHEK' ) {
674 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
675 my( $account, $aba ) = split('@', $object->paymask );
676 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
677 my($branch, $routing) = ($1, $2);
678 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
679 $routing, $branch, $account);
681 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
688 sub areyousure_link {
689 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
690 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';