5 %# batched payment links
7 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
8 % && $curuser->access_right('View customer batched payments')
11 <% mt('View batched payments:') |h %>
12 % foreach my $status (qw( Queued In-transit Complete All )) {
13 <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% mt($status) |h %></A>
14 <% $status ne 'All' ? '|' : '' %>
26 <& /elements/table-grid.html &>
27 % my $bgcolor1 = '#eeeeee';
28 % my $bgcolor2 = '#ffffff';
33 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
34 <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
35 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
36 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
37 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
38 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
39 <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
43 %#display payment history
52 %foreach my $item ( @history ) {
54 % $lastdate = $item->{'date'};
57 % if ( $item->{'hide'} ) {
58 % $display = ' STYLE="display:none" ';
61 % if ( $bgcolor eq $bgcolor1 ) {
62 % $bgcolor = $bgcolor2;
64 % $bgcolor = $bgcolor1;
67 % my $charge = exists($item->{'charge'})
68 % ? sprintf("$money_char\%.2f", $item->{'charge'})
69 % : exists($item->{'charge_nobal'})
70 % ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
71 % : exists($item->{'void_charge'})
72 % ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
75 % my $payment = exists($item->{'payment'})
76 % ? sprintf("- $money_char\%.2f", $item->{'payment'})
79 % $payment ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
80 % $item->{'void_payment'}
82 % if exists($item->{'void_payment'});
84 % my $credit = exists($item->{'credit'})
85 % ? sprintf("- $money_char\%.2f", $item->{'credit'})
88 % $credit ||= sprintf( "<DEL>- $money_char\%.2f</DEL>",
89 % $item->{'void_credit'}
91 % if exists($item->{'void_credit'});
93 % my $refund = exists($item->{'refund'})
94 % ? sprintf("$money_char\%.2f", $item->{'refund'})
97 % my $target = exists($item->{'target'}) ? $item->{'target'} : '';
99 % my $showbalance = $money_char . $item->{'balance'};
100 % $showbalance =~ s/^\$\-/- \$/;
102 <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
103 <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
104 % unless ( !$target || $target{$target}++ ) {
106 <A NAME="<% $target %>">
109 <% time2str($date_format, $item->{'date'}) %>
110 % if ( $target && $target{$target} == 1 ) {
117 <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
118 <% $item->{'desc'} %>
120 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
123 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
126 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
129 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
132 <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
137 % if ( $item->{'balance_forward'} ) {
138 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
142 % if ( $old_history ) {
143 <& .hide_history_row, $old_history++ &>
151 <SCRIPT SRC="<% $fsurl %>elements/page_pref.js"></SCRIPT>
152 <SCRIPT TYPE="text/javascript">
154 function show_history(show) { // but don't update pref
155 var balance_forward_row = document.getElementById('balance_forward_row');
157 balance_forward_row.style.display = show ? 'none' : '';
158 for ( var i = 0; i < <% $old_history %>; i++ ) {
159 var oldRow = document.getElementById('old_history'+i);
160 oldRow.style.display = show ? '' : 'none';
164 function update_show_history (show) {
168 // update user pref (blind post, don't care about the output here)
169 set_page_pref('expand_old_history', '<% $custnum %>', show);
172 $().ready(function() {
173 show_history(<% get_page_pref('expand_old_history', $custnum) %>);
177 <%def .balance_forward_row>
178 % my( $b, $date ) = @_;
179 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
181 <TR ID="balance_forward_row">
182 <TD CLASS="grid" BGCOLOR="#dddddd">
183 <% time2str($date_format, $date) %>
186 <TD CLASS="grid" BGCOLOR="#dddddd">
187 <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
188 (<A HREF="javascript:void(0);" onClick="update_show_history(true);"><% mt('show prior history') |h %></A>)
191 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
192 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
193 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
194 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
195 <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
199 <%def .hide_history_row>
201 <TR ID="old_history<% $num %>" STYLE="display: none">
202 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
203 <TD CLASS="grid" BGCOLOR="#dddddd">
204 <I>(<A HREF="#" onclick="update_show_history(false)"><% mt('hide prior history') |h %></A>)</I>
206 <TD CLASS="grid" BGCOLOR="#dddddd" COLSPAN=5></TD>
211 my $conf = new FS::Conf;
212 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
213 my $money_char = $conf->config('money_char') || '$';
217 my( $cust_main ) = @_;
218 my $custnum = $cust_main->custnum;
220 my $curuser = $FS::CurrentUser::CurrentUser;
223 'Queued' => 'O', #Open
225 'Complete' => 'R', #Resolved
235 ( map { $_ => scalar($conf->config($_)) }
236 qw( card_refund-days date_format )
238 ( map { $_ => $conf->exists($_) }
239 qw( deletepayments deleterefunds pkg-balances
240 cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
243 'money_char ' => $money_char,
246 ( map { $_ => $curuser->access_right($_) }
248 'View invoices', 'Void invoices', 'Unvoid invoices', 'Resend invoices',
249 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
250 'Post refund', 'Post check refund', 'Post cash refund ', 'Refund payment',
251 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
253 'Apply credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
255 'Billing event reports', 'View customer billing events',
259 #customer information
260 'total_owed' => $cust_main->total_owed,
261 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
262 'has_email_address' => scalar($cust_main->invoicing_list_emailonly),
265 $opt{'date_format'} ||= '%m/%d/%Y';
268 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
270 'date' => $legacy_cust_bill->_date,
272 'num' => $legacy_cust_bill->legacyid,
273 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
274 'charge_nobal' => $legacy_cust_bill->charged,
279 my $num_cust_bill = 0;
280 foreach my $cust_bill ($cust_main->cust_bill) {
282 'date' => $cust_bill->_date,
284 'num' => $cust_bill->invnum,
285 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
286 'charge' => $cust_bill->charged,
292 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
294 'date' => $cust_bill_void->_date,
296 'num' => $cust_bill_void->invnum,
297 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
298 'void_charge' => $cust_bill_void->charged,
303 foreach my $cust_statement ($cust_main->cust_statement) {
305 'date' => $cust_statement->_date,
307 'num' => $cust_statement->statementnum,
308 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
309 #'charge' => $cust_bill->charged,
313 #payments (some false laziness w/credits)
314 foreach my $cust_pay ($cust_main->cust_pay) {
316 'date' => $cust_pay->_date,
318 'num' => $cust_pay->paynum,
319 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
320 'payment' => $cust_pay->paid,
321 #'target' => $target, #XXX
326 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
328 'date' => $cust_pay_pending->_date,
330 'num' => $cust_pay_pending->paypendingnum,
331 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
332 'void_payment' => $cust_pay_pending->paid,
338 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
340 'date' => $cust_pay_void->_date,
342 'num' => $cust_pay_void->paynum,
343 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
344 'void_payment' => $cust_pay_void->paid,
350 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
352 'date' => $cust_credit_void->_date,
354 'num' => $cust_credit_void->paynum,
355 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
356 'void_credit' => $cust_credit_void->amount,
361 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
363 'date' => $cust_pay_pending->_date,
365 'num' => $cust_pay_pending->paypendingnum,
366 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
367 'void_payment' => $cust_pay_pending->paid, #??
368 #'target' => $target, #XXX
371 #declined batch payments
372 foreach my $cust_pay_batch (
373 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
375 my $pay_batch = $cust_pay_batch->pay_batch;
377 'date' => $pay_batch->upload,
379 'num' => $cust_pay_batch->paybatchnum,
380 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
381 'void_payment' => $cust_pay_batch->amount,
385 #credits (some false laziness w/payments)
386 foreach my $cust_credit ($cust_main->cust_credit) {
388 'date' => $cust_credit->_date,
390 'num' => $cust_credit->crednum,
391 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
392 'credit' => $cust_credit->amount,
398 foreach my $cust_refund ($cust_main->cust_refund) {
400 'date' => $cust_refund->_date,
402 'num' => $cust_refund->refundnum,
403 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
404 'refund' => $cust_refund->refund,
409 # sort in forward order first, and calculate running balances
410 my $years = $conf->config('payment_history-years') || 2;
411 my $older_than = time - $years * 31556926; #60*60*24*365.2422
414 @history = sort { $a->{date} <=> $b->{date}
415 or $a->{order} <=> $b->{order}
416 or $a->{num} <=> $b->{num}
422 foreach my $item (@history) {
423 $balance += $item->{'charge'} if exists $item->{'charge'};
424 $balance -= $item->{'payment'} if exists $item->{'payment'};
425 $balance -= $item->{'credit'} if exists $item->{'credit'};
426 $balance += $item->{'refund'} if exists $item->{'refund'};
427 $balance = sprintf("%.2f", $balance);
428 $balance =~ s/^\-0\.00$/0.00/;
429 $item->{'balance'} = $balance;
431 if ( $item->{'date'} < $older_than ) {
433 } elsif ( $history[$i-1]->{'hide'} ) {
434 # this is the end of the hidden section
435 $history[$i-1]->{'balance_forward'} = 1;
439 if ( @history and $history[-1]->{'hide'} ) {
440 # then everything is hidden
441 $history[-1]->{'balance_forward'} = 1;
444 # then sort in user-pref order
445 if ( $curuser->option('history_order') eq 'newest' ) {
446 @history = sort { $b->{date} <=> $a->{date}
447 or $b->{order} <=> $a->{order} #or still forward here?
448 or $b->{num} <=> $a->{num}
451 } # else it's already oldest-first, and there are no other options yet
453 sub translate_payby {
454 my ($payby,$payinfo) = (shift,shift);
456 FS::payby->payby2shortname,
457 BILL => $payinfo ? emt('Check #') : '',
458 CHEK => emt('Electronic check '),
459 PREP => emt('Prepaid card '),
460 CARD => emt('Credit card #'),
461 COMP => emt('Complimentary by '),
462 #CASH => emt('Cash'),
463 #WEST => emt('Western Union'),
464 #MCRD => emt('Manual credit card'),
466 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
470 sub translate_payby_refund {
471 my ($payby,$payinfo) = (shift,shift);
473 FS::payby->payby2shortname,
474 BILL => $payinfo ? emt('Check #') : emt('Check'),
475 CHEK => emt('Electronic check '),
476 CARD => emt('Credit card #'),
477 COMP => emt('Complimentary by '),
479 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
483 sub translate_payinfo {
485 my $payby = $object->payby;
486 my $payinfo = $object->payinfo;
488 if ( $payby eq 'CARD' ) {
489 $payinfo = $object->paymask;
490 } elsif ( $payby eq 'CHEK' ) {
491 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
492 my( $account, $aba ) = split('@', $object->paymask );
493 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
494 my($branch, $routing) = ($1, $2);
495 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
496 $routing, $branch, $account);
498 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
505 sub areyousure_link {
506 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
507 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'" STYLE="white-space: nowrap;">'.$label.'</A>)';