Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / httemplate / view / cust_main / payment_history.html
1 <TABLE>
2   <TR>
3     <TD ALIGN="left">
4
5 %# batched payment links
6
7 % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
8 %      && $curuser->access_right('View customer batched payments')
9 %    )
10 % { 
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' ? '|' : '' %>
15 %   }
16     <BR>
17 % } 
18
19     </TD>
20   </TR>
21   <TR>
22     <TD COLSPAN=2>
23
24 %# and now the table
25
26 <& /elements/table-grid.html &>
27 % my $bgcolor1 = '#eeeeee';
28 %   my $bgcolor2 = '#ffffff';
29 %   my $bgcolor = '';
30
31 <THEAD>
32 <TR>
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>
40 </TR>
41 </THEAD>
42
43 %#display payment history
44
45 %my %target = ();
46 %
47 %my $hidden = 0;
48 %my $seen = 0;
49 %my $old_history = 0;
50 %my $lastdate = 0;
51 %
52 %foreach my $item ( @history ) {
53 %
54 %  $lastdate = $item->{'date'};
55 %
56 %  my $display = '';
57 %  if ( $item->{'hide'} ) {
58 %    $display = ' STYLE="display:none" ';
59 %  }
60 %
61 %  if ( $bgcolor eq $bgcolor1 ) {
62 %    $bgcolor = $bgcolor2;
63 %  } else {
64 %    $bgcolor = $bgcolor1;
65 %  }
66 %
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'})
73 %                      : '';
74 %
75 %  my $payment = exists($item->{'payment'})
76 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'payment'})
77 %                  : '';
78 %
79 %  $payment ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
80 %                        $item->{'void_payment'}
81 %                      )
82 %    if exists($item->{'void_payment'});
83 %
84 %  my $credit  = exists($item->{'credit'})
85 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'credit'})
86 %                  : '';
87 %
88 %  $credit ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
89 %                       $item->{'void_credit'}
90 %                     )
91 %    if exists($item->{'void_credit'});
92 %
93 %  my $refund  = exists($item->{'refund'})
94 %                  ? sprintf("$money_char\%.2f", $item->{'refund'})
95 %                  : '';
96 %
97 %  my $target = exists($item->{'target'}) ? $item->{'target'} : '';
98 %
99 %  my $showbalance = $money_char . $item->{'balance'};
100 %  $showbalance =~ s/^\$\-/-&nbsp;\$/;
101
102   <TR <% $display ? $display.' ID="old_history'.$old_history++.'"'  : ''%>>
103     <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
104 % unless ( !$target || $target{$target}++ ) { 
105
106         <A NAME="<% $target %>">
107 % } 
108
109       <% time2str($date_format, $item->{'date'}) %>
110 % if ( $target && $target{$target} == 1 ) { 
111
112         </A>
113 % } 
114
115       </FONT>
116     </TD>
117     <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
118       <% $item->{'desc'} %>
119     </TD>
120     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
121       <% $charge  %>
122     </TD>
123     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
124       <% $payment %>
125     </TD>
126     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
127       <% $credit  %>
128     </TD>
129     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
130       <% $refund  %>
131     </TD>
132     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
133       <% $showbalance %>
134     </TD>
135   </TR>
136
137 % if ( $item->{'balance_forward'} ) {
138 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
139 % } 
140 %} # foreach $item
141
142 </TABLE>
143     </TD>
144   </TR>
145 </TABLE>
146
147 <SCRIPT TYPE="text/javascript">
148
149 function show_history () {
150   //alert('showing history!');
151
152   var balance_forward_row = document.getElementById('balance_forward_row');
153
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 = '';
158   }
159
160 }
161
162 </SCRIPT>
163 <%def .balance_forward_row>
164 %  my( $b, $date ) = @_;
165 %  ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/-&nbsp;\$/;
166
167    <TR ID="balance_forward_row">
168      <TD CLASS="grid" BGCOLOR="#dddddd">
169        <% time2str($date_format, $date) %>
170      </TD>
171
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>)
175      </TD>
176
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>
182
183    </TR>
184 </%def>
185 <%shared>
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') || '$';
189 </%shared>
190 <%init>
191
192 my( $cust_main ) = @_;
193 my $custnum = $cust_main->custnum;
194
195 my $curuser = $FS::CurrentUser::CurrentUser;
196
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 ))
200   unless @payby;
201 my %payby = map { $_=>1 } @payby;
202
203 my %status = (
204   'Queued'     => 'O', #Open
205   'In-transit' => 'I',
206   'Complete'   => 'R', #Resolved
207   'All'        => '',
208 );
209
210 #get payment history
211 my @history = ();
212
213 my %opt = (
214
215   #config
216   ( map { $_ => scalar($conf->config($_)) }
217         qw( card_refund-days date_format )
218   ),
219   ( map { $_ => $conf->exists($_) } 
220         qw( deletepayments deleterefunds pkg-balances
221             cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
222           )
223   ),
224   'money_char             ' => $money_char,
225
226   #rights
227   ( map { $_ => $curuser->access_right($_) }
228       (
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',
234         'Delete refund',
235         'Billing event reports', 'View customer billing events',
236       )
237   ),
238
239   #customer information
240   'total_owed'              => $cust_main->total_owed,
241   'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
242 );
243
244 $opt{'date_format'} ||= '%m/%d/%Y';
245
246 #legacy invoices
247 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
248   push @history, {
249     'date'   => $legacy_cust_bill->_date,
250     'order'  => 1,
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,
254   };
255 }
256
257 #invoices
258 my $num_cust_bill = 0;
259 foreach my $cust_bill ($cust_main->cust_bill) {
260   push @history, {
261     'date'   => $cust_bill->_date,
262     'order'  => 1,
263     'num'    => $cust_bill->invnum,
264     'desc'   => include('payment_history/invoice.html', $cust_bill, %opt ),
265     'charge' => $cust_bill->charged,
266   };
267   $num_cust_bill++;
268 }
269
270 #voided invoices
271 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
272   push @history, {
273     'date'        => $cust_bill_void->_date,
274     'order'       => 0,
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,
278   };
279 }
280
281 #statements
282 foreach my $cust_statement ($cust_main->cust_statement) {
283   push @history, {
284     'date'   => $cust_statement->_date,
285     'order'  => 2,
286     'num'    => $cust_statement->statementnum,
287     'desc'   => include('payment_history/statement.html', $cust_statement, %opt ),
288     #'charge' => $cust_bill->charged,
289   };
290 }
291
292 #payments (some false laziness w/credits)
293 foreach my $cust_pay ($cust_main->cust_pay) {
294   push @history, {
295     'date'    => $cust_pay->_date,
296     'order'   => 6,
297     'num'     => $cust_pay->paynum,
298     'desc'    => include('payment_history/payment.html', $cust_pay, %opt ),
299     'payment' => $cust_pay->paid,
300     #'target'  => $target, #XXX
301   };
302 }
303
304 #pending payments 
305 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
306   push @history, {
307     'date'    => $cust_pay_pending->_date,
308     'order'   => 4,
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, 
312   };
313 }
314
315
316 #voided payments
317 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
318   push @history, {
319     'date'   => $cust_pay_void->_date,
320     'order'  => 3,
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,
324   };
325
326 }
327
328 #voided credits 
329 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
330   push @history, {
331     'date'        => $cust_credit_void->_date,
332     'order'       => 7,
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,
336   };
337 }
338
339 #declined payments
340 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
341   push @history, {
342     'date'    => $cust_pay_pending->_date,
343     'order'   => 5,
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
348   };
349 }
350 #declined batch payments
351 foreach my $cust_pay_batch (
352   $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
353 ) {
354   my $pay_batch = $cust_pay_batch->pay_batch;
355   push @history, {
356     'date'    => $pay_batch->upload,
357     'order'   => 5,
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,
361   };
362 }
363
364 #credits (some false laziness w/payments)
365 foreach my $cust_credit ($cust_main->cust_credit) {
366   push @history, {
367     'date'   => $cust_credit->_date,
368     'order'  => 8,
369     'num'    => $cust_credit->crednum,
370     'desc'   => include('payment_history/credit.html', $cust_credit, %opt ),
371     'credit' => $cust_credit->amount,
372   };
373
374 }
375
376 #refunds
377 foreach my $cust_refund ($cust_main->cust_refund) {
378   push @history, {
379     'date'   => $cust_refund->_date,
380     'order'  => 9,
381     'num'    => $cust_refund->refundnum,
382     'desc'   => include('payment_history/refund.html', $cust_refund, %opt),
383     'refund' => $cust_refund->refund,
384   };
385
386 }
387
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
391 my $balance = 0;
392
393 @history = sort {    $a->{date}  <=> $b->{date}
394                   or $a->{order} <=> $b->{order}
395                   or $a->{num}   <=> $b->{num}
396                 }
397              @history;
398
399 my $i = 0;
400 my $balance_forward;
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;
409
410   if ( $item->{'date'} < $older_than ) {
411     $item->{'hide'} = 1;
412   } elsif ( $history[$i-1]->{'hide'} ) {
413     # this is the end of the hidden section
414     $history[$i-1]->{'balance_forward'} = 1;
415   }
416   $i++;
417 }
418 if ( @history and $history[-1]->{'hide'} ) {
419   # then everything is hidden
420   $history[-1]->{'balance_forward'} = 1;
421 }
422
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}
428                   }
429                @history;
430 } # else it's already oldest-first, and there are no other options yet
431
432 sub translate_payby {
433     my ($payby,$payinfo) = (shift,shift);
434     my %payby = (
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'),
444     );
445     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
446     $payby;
447 };
448
449 sub translate_payby_refund {
450     my ($payby,$payinfo) = (shift,shift);
451     my %payby = (
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 '),
457     );
458     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
459     $payby;
460 };
461
462 sub translate_payinfo {
463     my $object = shift;
464     my $payby = $object->payby;
465     my $payinfo = $object->payinfo;
466
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);
476         } else {
477           $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
478         }
479     }
480
481     ($payby,$payinfo);
482 }
483
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>)';
487 }
488
489 </%init>