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,
435 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
436 'charge_nobal' => $legacy_cust_bill->charged,
441 my $num_cust_bill = 0;
442 foreach my $cust_bill ($cust_main->cust_bill) {
444 'date' => $cust_bill->_date,
445 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
446 'charge' => $cust_bill->charged,
452 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
454 'date' => $cust_bill_void->_date,
455 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
456 'void_charge' => $cust_bill_void->charged,
461 foreach my $cust_statement ($cust_main->cust_statement) {
463 'date' => $cust_statement->_date,
464 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
465 #'charge' => $cust_bill->charged,
469 #payments (some false laziness w/credits)
470 foreach my $cust_pay ($cust_main->cust_pay) {
472 'date' => $cust_pay->_date,
473 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
474 'payment' => $cust_pay->paid,
475 #'target' => $target, #XXX
480 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
482 'date' => $cust_pay_pending->_date,
483 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
484 'void_payment' => $cust_pay_pending->paid,
490 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
492 'date' => $cust_pay_void->_date,
493 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
494 'void_payment' => $cust_pay_void->paid,
500 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
502 'date' => $cust_credit_void->_date,
503 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
504 'void_credit' => $cust_credit_void->amount,
509 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
511 'date' => $cust_pay_pending->_date,
512 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
513 'void_payment' => $cust_pay_pending->paid, #??
514 #'target' => $target, #XXX
517 #declined batch payments
518 foreach my $cust_pay_batch (
519 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
521 my $pay_batch = $cust_pay_batch->pay_batch;
523 'date' => $pay_batch->upload,
524 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
525 'void_payment' => $cust_pay_batch->amount,
529 #credits (some false laziness w/payments)
530 foreach my $cust_credit ($cust_main->cust_credit) {
532 'date' => $cust_credit->_date,
533 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
534 'credit' => $cust_credit->amount,
540 foreach my $cust_refund ($cust_main->cust_refund) {
542 'date' => $cust_refund->_date,
543 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
544 'refund' => $cust_refund->refund,
549 # sort in forward order first, and calculate running balances
550 my $years = $conf->config('payment_history-years') || 2;
551 my $older_than = time - $years * 31556926; #60*60*24*365.2422
554 @history = sort { $a->{date} <=> $b->{date} } @history;
557 foreach my $item (@history) {
558 $balance += $item->{'charge'} if exists $item->{'charge'};
559 $balance -= $item->{'payment'} if exists $item->{'payment'};
560 $balance -= $item->{'credit'} if exists $item->{'credit'};
561 $balance += $item->{'refund'} if exists $item->{'refund'};
562 $balance = sprintf("%.2f", $balance);
563 $balance =~ s/^\-0\.00$/0.00/;
564 $item->{'balance'} = $balance;
566 if ( $item->{'date'} < $older_than ) {
568 } elsif ( $history[$i-1]->{'hide'} ) {
569 # this is the end of the hidden section
570 $history[$i-1]->{'balance_forward'} = 1;
574 if ( @history and $history[-1]->{'hide'} ) {
575 # then everything is hidden
576 $history[-1]->{'balance_forward'} = 1;
579 # then sort in user-pref order
580 if ( $curuser->option('history_order') eq 'newest' ) {
581 @history = sort { $b->{date} <=> $a->{date} } @history;
582 } # else it's already oldest-first, and there are no other options yet
584 sub translate_payby {
585 my ($payby,$payinfo) = (shift,shift);
587 FS::payby->payby2shortname,
588 BILL => $payinfo ? emt('Check #') : '',
589 CHEK => emt('Electronic check '),
590 PREP => emt('Prepaid card '),
591 CARD => emt('Credit card #'),
592 COMP => emt('Complimentary by '),
593 #CASH => emt('Cash'),
594 #WEST => emt('Western Union'),
595 #MCRD => emt('Manual credit card'),
597 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
601 sub translate_payby_refund {
602 my ($payby,$payinfo) = (shift,shift);
604 FS::payby->payby2shortname,
605 BILL => $payinfo ? emt('Check #') : emt('Check'),
606 CHEK => emt('Electronic check '),
607 CARD => emt('Credit card #'),
608 COMP => emt('Complimentary by '),
610 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
614 sub translate_payinfo {
616 my $payby = $object->payby;
617 my $payinfo = $object->payinfo;
619 if ( $payby eq 'CARD' ) {
620 $payinfo = $object->paymask;
621 } elsif ( $payby eq 'CHEK' ) {
622 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
623 my( $account, $aba ) = split('@', $object->paymask );
624 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
625 my($branch, $routing) = ($1, $2);
626 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
627 $routing, $branch, $account);
629 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
636 sub areyousure_link {
637 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
638 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';