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