manual echeck payment type, RT#26995
[freeside.git] / httemplate / view / cust_main / payment_history.html
1 <TABLE>
2   <TR>
3     <TD ALIGN="left">
4
5 %# payment links
6
7 % my $s = 0;
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),
16                'height'      => 392,
17   &>
18 % } 
19
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),
28                'height'      => 392,
29   &>
30 % } 
31
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>
35 % } 
36
37 <% $s ? '<BR>' : '' %>
38 % $s=0;
39
40 % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
41 %        && $curuser->access_right(['Process payment', 'Process credit card payment'])
42 %        && ! $cust_main->is_encrypted($cust_main->payinfo)
43 %      ) {
44   <% $s++ ? ' | ' : '' %>
45   <A HREF="<% $p %>misc/payment.cgi?payby=CARD;custnum=<% $custnum %>"><% mt('Process credit card payment') |h %></A>
46 % } 
47
48 % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
49 %        && $curuser->access_right(['Process payment', 'Process Echeck payment'])
50 %        && ! $cust_main->is_encrypted($cust_main->payinfo)
51 %      ) {
52   <% $s++ ? ' | ' : '' %>
53   <A HREF="<% $p %>misc/payment.cgi?payby=CHEK;custnum=<% $custnum %>"><% mt('Process electronic check (ACH) payment') |h %></A>
54 % } 
55
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>
59 % } 
60
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>
64 % } 
65
66 <% $s ? '<BR>' : '' %>
67
68 %# credit links
69
70 % $s=0;
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),
79   &>
80 % }
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'),
89                'width'       => 968, #763,
90                'height'      => 575,
91   &>
92 % } 
93 <% $s ? '<BR>' : '' %>
94
95 %# refund links
96
97 % $s = 0;
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'),
105                'width'       => 440,
106   &>
107 % } 
108
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'),
116                'width'       => 392,
117   &>
118 % } 
119
120 %# someday, perhaps.  very few gateways let you do unlinked refunds at all.
121 %# Authorize.net makes you sign a special form
122 %#
123 %#    % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
124 %#    %        && $curuser->access_right('Process refund')
125 %#    %        && ! $cust_main->is_encrypted($cust_main->payinfo)
126 %#    %      ) {
127 %#      <% $s++ ? ' | ' : '' %>
128 %#      <A HREF="<% $p %>misc/refund.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card refund</A>
129 %#    % } 
130 %#    
131 %#    % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
132 %#    %        && $curuser->access_right('Process refund')
133 %#    %        && ! $cust_main->is_encrypted($cust_main->payinfo)
134 %#    %      ) {
135 %#      <% $s++ ? ' | ' : '' %>
136 %#      <A HREF="<% $p %>misc/refund.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) refund</A>
137 %#    % } 
138
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>
142 % } 
143
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>
147 % } 
148
149     </TD>
150     <TD ALIGN="right" VALIGN="top">
151
152 %# invoice reports, combined statement
153 % if ( $curuser->access_right('List invoices') ) { 
154 %   if ( $num_cust_bill > 0 ) {
155   <A HREF="<% $p %>view/cust_main_statement-pdf.cgi?<% $custnum %>"><%
156   mt('Download typeset statement PDF') |h %></A>
157   <BR>
158 %   }
159   <A HREF="<% $p %>search/report_cust_bill.html?custnum=<% $custnum %>"><% mt('Invoice reports') |h %></A>
160 % } 
161 <BR>
162
163 %# XXX payments, credits, refund reports
164
165 %# tax exemption link
166
167 % my $view_exemptions = $curuser->access_right('View customer tax exemptions');
168 % my $add_adjustment = ( $conf->exists('enable_tax_adjustments')
169 %                       && $curuser->access_right('Add customer tax adjustment')
170 %                      );
171 % if ( $view_exemptions || $add_adjustment ) {
172
173 %   if ( $view_exemptions ) {
174       <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>"><% mt('View tax exemptions') |h %></A>
175       <% $add_adjustment ? '|' : '' %>
176 %   } 
177
178 %   if ( $add_adjustment ) {
179       <& /elements/popup_link.html, {
180            'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum,
181            'label'  => emt('Add tax adjustment'),
182            'actionlabel' => emt('Add tax adjustment'),
183            'height' => 200,
184          }
185       &>
186       |
187       <A HREF="<% $p %>search/cust_tax_adjustment.html?custnum=<% $custnum %>"><% mt('View tax adjustments') |h %></A>
188 %   } 
189
190   <BR>
191 % }
192
193 %# batched payment links
194
195 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
196 %      && $curuser->access_right('View customer batched payments')
197 %    )
198 % { 
199     <% mt('View batched payments:') |h %> 
200 %   foreach my $status (qw( Queued In-transit Complete All )) {
201       <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% mt($status) |h %></A> 
202       <% $status ne 'All' ? '|' : '' %>
203 %   }
204     <BR>
205 % } 
206
207 %# pending payment links
208
209 % if ( $curuser->access_right('View customer pending payments')
210 %      && scalar($cust_main->cust_pay_pending)
211 %    )
212 % {
213     <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>"><% mt('View pending payments') |h %></A><BR>
214 % }
215
216     </TD>
217   </TR>
218   <TR>
219     <TD COLSPAN=2>
220
221 %# and now the table
222
223 <& /elements/table-grid.html &>
224 % my $bgcolor1 = '#eeeeee';
225 %   my $bgcolor2 = '#ffffff';
226 %   my $bgcolor = '';
227
228 <TR>
229   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
230   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
231   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
232   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
233   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
234   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
235   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
236 </TR>
237
238 %#display payment history
239
240 %my %target = ();
241 %
242 %my $hidden = 0;
243 %my $seen = 0;
244 %my $old_history = 0;
245 %my $lastdate = 0;
246 %
247 %foreach my $item ( @history ) {
248 %
249 %  $lastdate = $item->{'date'};
250 %
251 %  my $display = '';
252 %  if ( $item->{'hide'} ) {
253 %    $display = ' STYLE="display:none" ';
254 %  }
255 %
256 %  if ( $bgcolor eq $bgcolor1 ) {
257 %    $bgcolor = $bgcolor2;
258 %  } else {
259 %    $bgcolor = $bgcolor1;
260 %  }
261 %
262 %  my $charge  = exists($item->{'charge'})
263 %                  ? sprintf("$money_char\%.2f", $item->{'charge'})
264 %                  : exists($item->{'charge_nobal'})
265 %                    ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
266 %                    : exists($item->{'void_charge'})
267 %                      ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
268 %                      : '';
269 %
270 %  my $payment = exists($item->{'payment'})
271 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'payment'})
272 %                  : '';
273 %
274 %  $payment ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
275 %                        $item->{'void_payment'}
276 %                      )
277 %    if exists($item->{'void_payment'});
278 %
279 %  my $credit  = exists($item->{'credit'})
280 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'credit'})
281 %                  : '';
282 %
283 %  $credit ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
284 %                       $item->{'void_credit'}
285 %                     )
286 %    if exists($item->{'void_credit'});
287 %
288 %  my $refund  = exists($item->{'refund'})
289 %                  ? sprintf("$money_char\%.2f", $item->{'refund'})
290 %                  : '';
291 %
292 %  my $target = exists($item->{'target'}) ? $item->{'target'} : '';
293 %
294 %  my $showbalance = $money_char . $item->{'balance'};
295 %  $showbalance =~ s/^\$\-/-&nbsp;\$/;
296
297   <TR <% $display ? $display.' ID="old_history'.$old_history++.'"'  : ''%>>
298     <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
299 % unless ( !$target || $target{$target}++ ) { 
300
301         <A NAME="<% $target %>">
302 % } 
303
304       <% time2str($date_format, $item->{'date'}) %>
305 % if ( $target && $target{$target} == 1 ) { 
306
307         </A>
308 % } 
309
310       </FONT>
311     </TD>
312     <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
313       <% $item->{'desc'} %>
314     </TD>
315     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
316       <% $charge  %>
317     </TD>
318     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
319       <% $payment %>
320     </TD>
321     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
322       <% $credit  %>
323     </TD>
324     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
325       <% $refund  %>
326     </TD>
327     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
328       <% $showbalance %>
329     </TD>
330   </TR>
331
332 % if ( $item->{'balance_forward'} ) {
333 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
334 % } 
335 %} # foreach $item
336
337 </TABLE>
338     </TD>
339   </TR>
340 </TABLE>
341
342 <SCRIPT TYPE="text/javascript">
343
344 function show_history () {
345   //alert('showing history!');
346
347   var balance_forward_row = document.getElementById('balance_forward_row');
348
349   balance_forward_row.style.display = 'none';
350   for ( var i = 0; i < <% $old_history %>; i++ ) {
351     var oldRow = document.getElementById('old_history'+i);
352     oldRow.style.display = '';
353   }
354
355 }
356
357 </SCRIPT>
358 <%def .balance_forward_row>
359 %  my( $b, $date ) = @_;
360 %  ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/-&nbsp;\$/;
361
362    <TR ID="balance_forward_row">
363      <TD CLASS="grid" BGCOLOR="#dddddd">
364        <% time2str($date_format, $date) %>
365      </TD>
366
367      <TD CLASS="grid" BGCOLOR="#dddddd">
368        <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
369        (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
370      </TD>
371
372      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
373      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
374      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
375      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
376      <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
377
378    </TR>
379 </%def>
380 <%shared>
381 my $conf = new FS::Conf;
382 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
383 my $money_char = $conf->config('money_char') || '$';
384 </%shared>
385 <%init>
386
387 my( $cust_main ) = @_;
388 my $custnum = $cust_main->custnum;
389
390 my $curuser = $FS::CurrentUser::CurrentUser;
391
392 my @payby = grep /\w/, $conf->config('payby');
393 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
394 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
395   unless @payby;
396 my %payby = map { $_=>1 } @payby;
397
398 my %status = (
399   'Queued'     => 'O', #Open
400   'In-transit' => 'I',
401   'Complete'   => 'R', #Resolved
402   'All'        => '',
403 );
404
405 #get payment history
406 my @history = ();
407
408 my %opt = (
409
410   #config
411   ( map { $_ => scalar($conf->config($_)) }
412         qw( card_refund-days date_format )
413   ),
414   ( map { $_ => $conf->exists($_) } 
415         qw( deleteinvoices deletepayments deleterefunds pkg-balances
416             cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
417           )
418   ),
419   'money_char             ' => $money_char,
420
421   #rights
422   ( map { $_ => $curuser->access_right($_) }
423       (
424         'View invoices', 'Void invoices', 'Unvoid invoices', 'Delete invoices',
425         'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
426         'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
427         'Delete payment', 'Unapply payment',
428         'Apply credit', 'Delete credit', 'Unapply credit',
429         'Delete refund',
430         'Billing event reports', 'View customer billing events',
431       )
432   ),
433
434   #customer information
435   'total_owed'              => $cust_main->total_owed,
436   'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
437 );
438
439 $opt{'date_format'} ||= '%m/%d/%Y';
440
441 #legacy invoices
442 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
443   push @history, {
444     'date'   => $legacy_cust_bill->_date,
445     'order'  => 1,
446     'num'    => $legacy_cust_bill->legacyid,
447     'desc'   => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
448     'charge_nobal' => $legacy_cust_bill->charged,
449   };
450 }
451
452 #invoices
453 my $num_cust_bill = 0;
454 foreach my $cust_bill ($cust_main->cust_bill) {
455   push @history, {
456     'date'   => $cust_bill->_date,
457     'order'  => 1,
458     'num'    => $cust_bill->invnum,
459     'desc'   => include('payment_history/invoice.html', $cust_bill, %opt ),
460     'charge' => $cust_bill->charged,
461   };
462   $num_cust_bill++;
463 }
464
465 #voided invoices
466 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
467   push @history, {
468     'date'        => $cust_bill_void->_date,
469     'order'       => 0,
470     'num'         => $cust_bill_void->invnum,
471     'desc'        => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
472     'void_charge' => $cust_bill_void->charged,
473   };
474 }
475
476 #statements
477 foreach my $cust_statement ($cust_main->cust_statement) {
478   push @history, {
479     'date'   => $cust_statement->_date,
480     'order'  => 2,
481     'num'    => $cust_statement->statementnum,
482     'desc'   => include('payment_history/statement.html', $cust_statement, %opt ),
483     #'charge' => $cust_bill->charged,
484   };
485 }
486
487 #payments (some false laziness w/credits)
488 foreach my $cust_pay ($cust_main->cust_pay) {
489   push @history, {
490     'date'    => $cust_pay->_date,
491     'order'   => 6,
492     'num'     => $cust_pay->paynum,
493     'desc'    => include('payment_history/payment.html', $cust_pay, %opt ),
494     'payment' => $cust_pay->paid,
495     #'target'  => $target, #XXX
496   };
497 }
498
499 #pending payments 
500 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
501   push @history, {
502     'date'    => $cust_pay_pending->_date,
503     'order'   => 4,
504     'num'     => $cust_pay_pending->paypendingnum,
505     'desc'    => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
506     'void_payment' => $cust_pay_pending->paid, 
507   };
508 }
509
510
511 #voided payments
512 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
513   push @history, {
514     'date'   => $cust_pay_void->_date,
515     'order'  => 3,
516     'num'    => $cust_pay_void->paynum,
517     'desc'   => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
518     'void_payment' => $cust_pay_void->paid,
519   };
520
521 }
522
523 #voided credits 
524 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
525   push @history, {
526     'date'        => $cust_credit_void->_date,
527     'order'       => 7,
528     'num'         => $cust_credit_void->paynum,
529     'desc'        => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
530     'void_credit' => $cust_credit_void->amount,
531   };
532 }
533
534 #declined payments
535 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
536   push @history, {
537     'date'    => $cust_pay_pending->_date,
538     'order'   => 5,
539     'num'     => $cust_pay_pending->paypendingnum,
540     'desc'    => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
541     'void_payment' => $cust_pay_pending->paid, #??
542     #'target'  => $target, #XXX
543   };
544 }
545 #declined batch payments
546 foreach my $cust_pay_batch (
547   $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
548 ) {
549   my $pay_batch = $cust_pay_batch->pay_batch;
550   push @history, {
551     'date'    => $pay_batch->upload,
552     'order'   => 5,
553     'num'     => $cust_pay_batch->paybatchnum,
554     'desc'    => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
555     'void_payment' => $cust_pay_batch->amount,
556   };
557 }
558
559 #credits (some false laziness w/payments)
560 foreach my $cust_credit ($cust_main->cust_credit) {
561   push @history, {
562     'date'   => $cust_credit->_date,
563     'order'  => 8,
564     'num'    => $cust_credit->crednum,
565     'desc'   => include('payment_history/credit.html', $cust_credit, %opt ),
566     'credit' => $cust_credit->amount,
567   };
568
569 }
570
571 #refunds
572 foreach my $cust_refund ($cust_main->cust_refund) {
573   push @history, {
574     'date'   => $cust_refund->_date,
575     'order'  => 9,
576     'num'    => $cust_refund->refundnum,
577     'desc'   => include('payment_history/refund.html', $cust_refund, %opt),
578     'refund' => $cust_refund->refund,
579   };
580
581 }
582
583 # sort in forward order first, and calculate running balances
584 my $years =  $conf->config('payment_history-years') || 2;
585 my $older_than = time - $years * 31556926; #60*60*24*365.2422
586 my $balance = 0;
587
588 @history = sort {    $a->{date}  <=> $b->{date}
589                   or $a->{order} <=> $b->{order}
590                   or $a->{num}   <=> $b->{num}
591                 }
592              @history;
593
594 my $i = 0;
595 my $balance_forward;
596 foreach my $item (@history) {
597   $balance += $item->{'charge'}  if exists $item->{'charge'};
598   $balance -= $item->{'payment'} if exists $item->{'payment'};
599   $balance -= $item->{'credit'}  if exists $item->{'credit'};
600   $balance += $item->{'refund'}  if exists $item->{'refund'};
601   $balance = sprintf("%.2f", $balance);
602   $balance =~ s/^\-0\.00$/0.00/;
603   $item->{'balance'} = $balance;
604
605   if ( $item->{'date'} < $older_than ) {
606     $item->{'hide'} = 1;
607   } elsif ( $history[$i-1]->{'hide'} ) {
608     # this is the end of the hidden section
609     $history[$i-1]->{'balance_forward'} = 1;
610   }
611   $i++;
612 }
613 if ( @history and $history[-1]->{'hide'} ) {
614   # then everything is hidden
615   $history[-1]->{'balance_forward'} = 1;
616 }
617
618 # then sort in user-pref order
619 if ( $curuser->option('history_order') eq 'newest' ) {
620   @history = sort {    $b->{date}  <=> $a->{date}
621                     or $b->{order} <=> $a->{order} #or still forward here?
622                     or $b->{num}   <=> $a->{num}
623                   }
624                @history;
625 } # else it's already oldest-first, and there are no other options yet
626
627 sub translate_payby {
628     my ($payby,$payinfo) = (shift,shift);
629     my %payby = (
630         FS::payby->payby2shortname,
631         BILL    => $payinfo ? emt('Check #') : '',
632         CHEK    => emt('Electronic check '),
633         PREP    => emt('Prepaid card '),
634         CARD    => emt('Credit card #'),
635         COMP    => emt('Complimentary by '),
636         #CASH    => emt('Cash'),
637         #WEST    => emt('Western Union'),
638         #MCRD    => emt('Manual credit card'),
639     );
640     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
641     $payby;
642 };
643
644 sub translate_payby_refund {
645     my ($payby,$payinfo) = (shift,shift);
646     my %payby = (
647         FS::payby->payby2shortname,
648         BILL    => $payinfo ? emt('Check #') : emt('Check'),
649         CHEK    => emt('Electronic check '),
650         CARD    => emt('Credit card #'),
651         COMP    => emt('Complimentary by '),
652     );
653     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
654     $payby;
655 };
656
657 sub translate_payinfo {
658     my $object = shift;
659     my $payby = $object->payby;
660     my $payinfo = $object->payinfo;
661
662     if ( $payby eq 'CARD' ) {
663         $payinfo = $object->paymask;
664     } elsif ( $payby eq 'CHEK' ) {
665         #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
666         my( $account, $aba ) = split('@', $object->paymask );
667         if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
668           my($branch, $routing) = ($1, $2);
669           $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
670                          $routing, $branch, $account);
671         } else {
672           $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
673         }
674     }
675
676     ($payby,$payinfo);
677 }
678
679 sub areyousure_link {
680     my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
681     ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';
682 }
683
684 </%init>