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', 'Resend invoices',
434 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
435 'Post refund', 'Post check refund', 'Post cash refund ', 'Refund payment',
436 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
437 'Delete payment', 'Unapply payment',
438 'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
440 'Billing event reports', 'View customer billing events',
444 #customer information
445 'total_owed' => $cust_main->total_owed,
446 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
447 'has_email_address' => scalar($cust_main->invoicing_list_emailonly),
450 $opt{'date_format'} ||= '%m/%d/%Y';
453 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
455 'date' => $legacy_cust_bill->_date,
457 'num' => $legacy_cust_bill->legacyid,
458 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
459 'charge_nobal' => $legacy_cust_bill->charged,
464 my $num_cust_bill = 0;
465 foreach my $cust_bill ($cust_main->cust_bill) {
467 'date' => $cust_bill->_date,
469 'num' => $cust_bill->invnum,
470 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
471 'charge' => $cust_bill->charged,
477 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
479 'date' => $cust_bill_void->_date,
481 'num' => $cust_bill_void->invnum,
482 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
483 'void_charge' => $cust_bill_void->charged,
488 foreach my $cust_statement ($cust_main->cust_statement) {
490 'date' => $cust_statement->_date,
492 'num' => $cust_statement->statementnum,
493 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
494 #'charge' => $cust_bill->charged,
498 #payments (some false laziness w/credits)
499 foreach my $cust_pay ($cust_main->cust_pay) {
501 'date' => $cust_pay->_date,
503 'num' => $cust_pay->paynum,
504 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
505 'payment' => $cust_pay->paid,
506 #'target' => $target, #XXX
511 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
513 'date' => $cust_pay_pending->_date,
515 'num' => $cust_pay_pending->paypendingnum,
516 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
517 'void_payment' => $cust_pay_pending->paid,
523 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
525 'date' => $cust_pay_void->_date,
527 'num' => $cust_pay_void->paynum,
528 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
529 'void_payment' => $cust_pay_void->paid,
535 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
537 'date' => $cust_credit_void->_date,
539 'num' => $cust_credit_void->paynum,
540 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
541 'void_credit' => $cust_credit_void->amount,
546 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
548 'date' => $cust_pay_pending->_date,
550 'num' => $cust_pay_pending->paypendingnum,
551 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
552 'void_payment' => $cust_pay_pending->paid, #??
553 #'target' => $target, #XXX
556 #declined batch payments
557 foreach my $cust_pay_batch (
558 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
560 my $pay_batch = $cust_pay_batch->pay_batch;
562 'date' => $pay_batch->upload,
564 'num' => $cust_pay_batch->paybatchnum,
565 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
566 'void_payment' => $cust_pay_batch->amount,
570 #credits (some false laziness w/payments)
571 foreach my $cust_credit ($cust_main->cust_credit) {
573 'date' => $cust_credit->_date,
575 'num' => $cust_credit->crednum,
576 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
577 'credit' => $cust_credit->amount,
583 foreach my $cust_refund ($cust_main->cust_refund) {
585 'date' => $cust_refund->_date,
587 'num' => $cust_refund->refundnum,
588 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
589 'refund' => $cust_refund->refund,
594 # sort in forward order first, and calculate running balances
595 my $years = $conf->config('payment_history-years') || 2;
596 my $older_than = time - $years * 31556926; #60*60*24*365.2422
599 @history = sort { $a->{date} <=> $b->{date}
600 or $a->{order} <=> $b->{order}
601 or $a->{num} <=> $b->{num}
607 foreach my $item (@history) {
608 $balance += $item->{'charge'} if exists $item->{'charge'};
609 $balance -= $item->{'payment'} if exists $item->{'payment'};
610 $balance -= $item->{'credit'} if exists $item->{'credit'};
611 $balance += $item->{'refund'} if exists $item->{'refund'};
612 $balance = sprintf("%.2f", $balance);
613 $balance =~ s/^\-0\.00$/0.00/;
614 $item->{'balance'} = $balance;
616 if ( $item->{'date'} < $older_than ) {
618 } elsif ( $history[$i-1]->{'hide'} ) {
619 # this is the end of the hidden section
620 $history[$i-1]->{'balance_forward'} = 1;
624 if ( @history and $history[-1]->{'hide'} ) {
625 # then everything is hidden
626 $history[-1]->{'balance_forward'} = 1;
629 # then sort in user-pref order
630 if ( $curuser->option('history_order') eq 'newest' ) {
631 @history = sort { $b->{date} <=> $a->{date}
632 or $b->{order} <=> $a->{order} #or still forward here?
633 or $b->{num} <=> $a->{num}
636 } # else it's already oldest-first, and there are no other options yet
638 sub translate_payby {
639 my ($payby,$payinfo) = (shift,shift);
641 FS::payby->payby2shortname,
642 BILL => $payinfo ? emt('Check #') : '',
643 CHEK => emt('Electronic check '),
644 PREP => emt('Prepaid card '),
645 CARD => emt('Credit card #'),
646 COMP => emt('Complimentary by '),
647 #CASH => emt('Cash'),
648 #WEST => emt('Western Union'),
649 #MCRD => emt('Manual credit card'),
651 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
655 sub translate_payby_refund {
656 my ($payby,$payinfo) = (shift,shift);
658 FS::payby->payby2shortname,
659 BILL => $payinfo ? emt('Check #') : emt('Check'),
660 CHEK => emt('Electronic check '),
661 CARD => emt('Credit card #'),
662 COMP => emt('Complimentary by '),
664 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
668 sub translate_payinfo {
670 my $payby = $object->payby;
671 my $payinfo = $object->payinfo;
673 if ( $payby eq 'CARD' ) {
674 $payinfo = $object->paymask;
675 } elsif ( $payby eq 'CHEK' ) {
676 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
677 my( $account, $aba ) = split('@', $object->paymask );
678 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
679 my($branch, $routing) = ($1, $2);
680 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
681 $routing, $branch, $account);
683 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
690 sub areyousure_link {
691 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
692 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';