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'} &>
147 <SCRIPT TYPE="text/javascript">
149 function show_history () {
150 //alert('showing history!');
152 var balance_forward_row = document.getElementById('balance_forward_row');
154 balance_forward_row.style.display = 'none';
155 for ( var i = 0; i < <% $old_history %>; i++ ) {
156 var oldRow = document.getElementById('old_history'+i);
157 oldRow.style.display = '';
163 <%def .balance_forward_row>
164 % my( $b, $date ) = @_;
165 % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/;
167 <TR ID="balance_forward_row">
168 <TD CLASS="grid" BGCOLOR="#dddddd">
169 <% time2str($date_format, $date) %>
172 <TD CLASS="grid" BGCOLOR="#dddddd">
173 <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
174 (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
177 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
178 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
179 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
180 <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
181 <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
186 my $conf = new FS::Conf;
187 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
188 my $money_char = $conf->config('money_char') || '$';
192 my( $cust_main ) = @_;
193 my $custnum = $cust_main->custnum;
195 my $curuser = $FS::CurrentUser::CurrentUser;
197 my @payby = grep /\w/, $conf->config('payby');
198 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
199 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
201 my %payby = map { $_=>1 } @payby;
204 'Queued' => 'O', #Open
206 'Complete' => 'R', #Resolved
216 ( map { $_ => scalar($conf->config($_)) }
217 qw( card_refund-days date_format )
219 ( map { $_ => $conf->exists($_) }
220 qw( deletepayments deleterefunds pkg-balances
221 cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
224 'money_char ' => $money_char,
227 ( map { $_ => $curuser->access_right($_) }
229 'View invoices', 'Void invoices', 'Unvoid invoices',
230 'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
231 'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
232 'Delete payment', 'Unapply payment',
233 'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
235 'Billing event reports', 'View customer billing events',
239 #customer information
240 'total_owed' => $cust_main->total_owed,
241 'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
244 $opt{'date_format'} ||= '%m/%d/%Y';
247 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
249 'date' => $legacy_cust_bill->_date,
251 'num' => $legacy_cust_bill->legacyid,
252 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
253 'charge_nobal' => $legacy_cust_bill->charged,
258 my $num_cust_bill = 0;
259 foreach my $cust_bill ($cust_main->cust_bill) {
261 'date' => $cust_bill->_date,
263 'num' => $cust_bill->invnum,
264 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
265 'charge' => $cust_bill->charged,
271 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
273 'date' => $cust_bill_void->_date,
275 'num' => $cust_bill_void->invnum,
276 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
277 'void_charge' => $cust_bill_void->charged,
282 foreach my $cust_statement ($cust_main->cust_statement) {
284 'date' => $cust_statement->_date,
286 'num' => $cust_statement->statementnum,
287 'desc' => include('payment_history/statement.html', $cust_statement, %opt ),
288 #'charge' => $cust_bill->charged,
292 #payments (some false laziness w/credits)
293 foreach my $cust_pay ($cust_main->cust_pay) {
295 'date' => $cust_pay->_date,
297 'num' => $cust_pay->paynum,
298 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
299 'payment' => $cust_pay->paid,
300 #'target' => $target, #XXX
305 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
307 'date' => $cust_pay_pending->_date,
309 'num' => $cust_pay_pending->paypendingnum,
310 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
311 'void_payment' => $cust_pay_pending->paid,
317 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
319 'date' => $cust_pay_void->_date,
321 'num' => $cust_pay_void->paynum,
322 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
323 'void_payment' => $cust_pay_void->paid,
329 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
331 'date' => $cust_credit_void->_date,
333 'num' => $cust_credit_void->paynum,
334 'desc' => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
335 'void_credit' => $cust_credit_void->amount,
340 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
342 'date' => $cust_pay_pending->_date,
344 'num' => $cust_pay_pending->paypendingnum,
345 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
346 'void_payment' => $cust_pay_pending->paid, #??
347 #'target' => $target, #XXX
350 #declined batch payments
351 foreach my $cust_pay_batch (
352 $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
354 my $pay_batch = $cust_pay_batch->pay_batch;
356 'date' => $pay_batch->upload,
358 'num' => $cust_pay_batch->paybatchnum,
359 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
360 'void_payment' => $cust_pay_batch->amount,
364 #credits (some false laziness w/payments)
365 foreach my $cust_credit ($cust_main->cust_credit) {
367 'date' => $cust_credit->_date,
369 'num' => $cust_credit->crednum,
370 'desc' => include('payment_history/credit.html', $cust_credit, %opt ),
371 'credit' => $cust_credit->amount,
377 foreach my $cust_refund ($cust_main->cust_refund) {
379 'date' => $cust_refund->_date,
381 'num' => $cust_refund->refundnum,
382 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
383 'refund' => $cust_refund->refund,
388 # sort in forward order first, and calculate running balances
389 my $years = $conf->config('payment_history-years') || 2;
390 my $older_than = time - $years * 31556926; #60*60*24*365.2422
393 @history = sort { $a->{date} <=> $b->{date}
394 or $a->{order} <=> $b->{order}
395 or $a->{num} <=> $b->{num}
401 foreach my $item (@history) {
402 $balance += $item->{'charge'} if exists $item->{'charge'};
403 $balance -= $item->{'payment'} if exists $item->{'payment'};
404 $balance -= $item->{'credit'} if exists $item->{'credit'};
405 $balance += $item->{'refund'} if exists $item->{'refund'};
406 $balance = sprintf("%.2f", $balance);
407 $balance =~ s/^\-0\.00$/0.00/;
408 $item->{'balance'} = $balance;
410 if ( $item->{'date'} < $older_than ) {
412 } elsif ( $history[$i-1]->{'hide'} ) {
413 # this is the end of the hidden section
414 $history[$i-1]->{'balance_forward'} = 1;
418 if ( @history and $history[-1]->{'hide'} ) {
419 # then everything is hidden
420 $history[-1]->{'balance_forward'} = 1;
423 # then sort in user-pref order
424 if ( $curuser->option('history_order') eq 'newest' ) {
425 @history = sort { $b->{date} <=> $a->{date}
426 or $b->{order} <=> $a->{order} #or still forward here?
427 or $b->{num} <=> $a->{num}
430 } # else it's already oldest-first, and there are no other options yet
432 sub translate_payby {
433 my ($payby,$payinfo) = (shift,shift);
435 FS::payby->payby2shortname,
436 BILL => $payinfo ? emt('Check #') : '',
437 CHEK => emt('Electronic check '),
438 PREP => emt('Prepaid card '),
439 CARD => emt('Credit card #'),
440 COMP => emt('Complimentary by '),
441 #CASH => emt('Cash'),
442 #WEST => emt('Western Union'),
443 #MCRD => emt('Manual credit card'),
445 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
449 sub translate_payby_refund {
450 my ($payby,$payinfo) = (shift,shift);
452 FS::payby->payby2shortname,
453 BILL => $payinfo ? emt('Check #') : emt('Check'),
454 CHEK => emt('Electronic check '),
455 CARD => emt('Credit card #'),
456 COMP => emt('Complimentary by '),
458 $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
462 sub translate_payinfo {
464 my $payby = $object->payby;
465 my $payinfo = $object->payinfo;
467 if ( $payby eq 'CARD' ) {
468 $payinfo = $object->paymask;
469 } elsif ( $payby eq 'CHEK' ) {
470 #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
471 my( $account, $aba ) = split('@', $object->paymask );
472 if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
473 my($branch, $routing) = ($1, $2);
474 $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
475 $routing, $branch, $account);
477 $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
484 sub areyousure_link {
485 my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
486 ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';