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