171c29cb75c108f5d71275730b6c57039baa92f0
[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 <TR>
32   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
33   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Description') |h %></TH>
34   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Invoice') |h %></FONT></TH>
35   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Payment') |h %></FONT></TH>
36   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('In-house Credit') |h %></FONT></TH>
37   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Refund') |h %></FONT></TH>
38   <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% mt('Balance') |h %></FONT></TH>
39 </TR>
40
41 %#display payment history
42
43 %my %target = ();
44 %
45 %my $hidden = 0;
46 %my $seen = 0;
47 %my $old_history = 0;
48 %my $lastdate = 0;
49 %
50 %foreach my $item ( @history ) {
51 %
52 %  $lastdate = $item->{'date'};
53 %
54 %  my $display = '';
55 %  if ( $item->{'hide'} ) {
56 %    $display = ' STYLE="display:none" ';
57 %  }
58 %
59 %  if ( $bgcolor eq $bgcolor1 ) {
60 %    $bgcolor = $bgcolor2;
61 %  } else {
62 %    $bgcolor = $bgcolor1;
63 %  }
64 %
65 %  my $charge  = exists($item->{'charge'})
66 %                  ? sprintf("$money_char\%.2f", $item->{'charge'})
67 %                  : exists($item->{'charge_nobal'})
68 %                    ? sprintf("$money_char\%.2f", $item->{'charge_nobal'})
69 %                    : exists($item->{'void_charge'})
70 %                      ? sprintf("<DEL>$money_char\%.2f</DEL>", $item->{'void_charge'})
71 %                      : '';
72 %
73 %  my $payment = exists($item->{'payment'})
74 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'payment'})
75 %                  : '';
76 %
77 %  $payment ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
78 %                        $item->{'void_payment'}
79 %                      )
80 %    if exists($item->{'void_payment'});
81 %
82 %  my $credit  = exists($item->{'credit'})
83 %                  ? sprintf("-&nbsp;$money_char\%.2f", $item->{'credit'})
84 %                  : '';
85 %
86 %  $credit ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
87 %                       $item->{'void_credit'}
88 %                     )
89 %    if exists($item->{'void_credit'});
90 %
91 %  my $refund  = exists($item->{'refund'})
92 %                  ? sprintf("$money_char\%.2f", $item->{'refund'})
93 %                  : '';
94 %
95 %  my $target = exists($item->{'target'}) ? $item->{'target'} : '';
96 %
97 %  my $showbalance = $money_char . $item->{'balance'};
98 %  $showbalance =~ s/^\$\-/-&nbsp;\$/;
99
100   <TR <% $display ? $display.' ID="old_history'.$old_history++.'"'  : ''%>>
101     <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
102 % unless ( !$target || $target{$target}++ ) { 
103
104         <A NAME="<% $target %>">
105 % } 
106
107       <% time2str($date_format, $item->{'date'}) %>
108 % if ( $target && $target{$target} == 1 ) { 
109
110         </A>
111 % } 
112
113       </FONT>
114     </TD>
115     <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
116       <% $item->{'desc'} %>
117     </TD>
118     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
119       <% $charge  %>
120     </TD>
121     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
122       <% $payment %>
123     </TD>
124     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
125       <% $credit  %>
126     </TD>
127     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
128       <% $refund  %>
129     </TD>
130     <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
131       <% $showbalance %>
132     </TD>
133   </TR>
134
135 % if ( $item->{'balance_forward'} ) {
136 <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
137 % } 
138 %} # foreach $item
139
140 </TABLE>
141     </TD>
142   </TR>
143 </TABLE>
144
145 <SCRIPT TYPE="text/javascript">
146
147 function show_history () {
148   //alert('showing history!');
149
150   var balance_forward_row = document.getElementById('balance_forward_row');
151
152   balance_forward_row.style.display = 'none';
153   for ( var i = 0; i < <% $old_history %>; i++ ) {
154     var oldRow = document.getElementById('old_history'+i);
155     oldRow.style.display = '';
156   }
157
158 }
159
160 </SCRIPT>
161 <%def .balance_forward_row>
162 %  my( $b, $date ) = @_;
163 %  ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/-&nbsp;\$/;
164
165    <TR ID="balance_forward_row">
166      <TD CLASS="grid" BGCOLOR="#dddddd">
167        <% time2str($date_format, $date) %>
168      </TD>
169
170      <TD CLASS="grid" BGCOLOR="#dddddd">
171        <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
172        (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
173      </TD>
174
175      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
176      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
177      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
178      <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
179      <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
180
181    </TR>
182 </%def>
183 <%shared>
184 my $conf = new FS::Conf;
185 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
186 my $money_char = $conf->config('money_char') || '$';
187 </%shared>
188 <%init>
189
190 my( $cust_main ) = @_;
191 my $custnum = $cust_main->custnum;
192
193 my $curuser = $FS::CurrentUser::CurrentUser;
194
195 my @payby = grep /\w/, $conf->config('payby');
196 #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
197 @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
198   unless @payby;
199 my %payby = map { $_=>1 } @payby;
200
201 my %status = (
202   'Queued'     => 'O', #Open
203   'In-transit' => 'I',
204   'Complete'   => 'R', #Resolved
205   'All'        => '',
206 );
207
208 #get payment history
209 my @history = ();
210
211 my %opt = (
212
213   #config
214   ( map { $_ => scalar($conf->config($_)) }
215         qw( card_refund-days date_format )
216   ),
217   ( map { $_ => $conf->exists($_) } 
218         qw( deleteinvoices deletepayments deleterefunds pkg-balances
219             cust_credit_bill_pkg-manual cust_bill_pay_pkg-manual
220           )
221   ),
222   'money_char             ' => $money_char,
223
224   #rights
225   ( map { $_ => $curuser->access_right($_) }
226       (
227         'View invoices', 'Void invoices', 'Unvoid invoices', 'Delete invoices',
228         'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
229         'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
230         'Delete payment', 'Unapply payment',
231         'Apply credit', 'Delete credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
232         'Delete refund',
233         'Billing event reports', 'View customer billing events',
234       )
235   ),
236
237   #customer information
238   'total_owed'              => $cust_main->total_owed,
239   'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
240 );
241
242 $opt{'date_format'} ||= '%m/%d/%Y';
243
244 #legacy invoices
245 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
246   push @history, {
247     'date'   => $legacy_cust_bill->_date,
248     'order'  => 1,
249     'num'    => $legacy_cust_bill->legacyid,
250     'desc'   => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
251     'charge_nobal' => $legacy_cust_bill->charged,
252   };
253 }
254
255 #invoices
256 my $num_cust_bill = 0;
257 foreach my $cust_bill ($cust_main->cust_bill) {
258   push @history, {
259     'date'   => $cust_bill->_date,
260     'order'  => 1,
261     'num'    => $cust_bill->invnum,
262     'desc'   => include('payment_history/invoice.html', $cust_bill, %opt ),
263     'charge' => $cust_bill->charged,
264   };
265   $num_cust_bill++;
266 }
267
268 #voided invoices
269 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
270   push @history, {
271     'date'        => $cust_bill_void->_date,
272     'order'       => 0,
273     'num'         => $cust_bill_void->invnum,
274     'desc'        => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
275     'void_charge' => $cust_bill_void->charged,
276   };
277 }
278
279 #statements
280 foreach my $cust_statement ($cust_main->cust_statement) {
281   push @history, {
282     'date'   => $cust_statement->_date,
283     'order'  => 2,
284     'num'    => $cust_statement->statementnum,
285     'desc'   => include('payment_history/statement.html', $cust_statement, %opt ),
286     #'charge' => $cust_bill->charged,
287   };
288 }
289
290 #payments (some false laziness w/credits)
291 foreach my $cust_pay ($cust_main->cust_pay) {
292   push @history, {
293     'date'    => $cust_pay->_date,
294     'order'   => 6,
295     'num'     => $cust_pay->paynum,
296     'desc'    => include('payment_history/payment.html', $cust_pay, %opt ),
297     'payment' => $cust_pay->paid,
298     #'target'  => $target, #XXX
299   };
300 }
301
302 #pending payments 
303 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
304   push @history, {
305     'date'    => $cust_pay_pending->_date,
306     'order'   => 4,
307     'num'     => $cust_pay_pending->paypendingnum,
308     'desc'    => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
309     'void_payment' => $cust_pay_pending->paid, 
310   };
311 }
312
313
314 #voided payments
315 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
316   push @history, {
317     'date'   => $cust_pay_void->_date,
318     'order'  => 3,
319     'num'    => $cust_pay_void->paynum,
320     'desc'   => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
321     'void_payment' => $cust_pay_void->paid,
322   };
323
324 }
325
326 #voided credits 
327 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
328   push @history, {
329     'date'        => $cust_credit_void->_date,
330     'order'       => 7,
331     'num'         => $cust_credit_void->paynum,
332     'desc'        => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
333     'void_credit' => $cust_credit_void->amount,
334   };
335 }
336
337 #declined payments
338 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
339   push @history, {
340     'date'    => $cust_pay_pending->_date,
341     'order'   => 5,
342     'num'     => $cust_pay_pending->paypendingnum,
343     'desc'    => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
344     'void_payment' => $cust_pay_pending->paid, #??
345     #'target'  => $target, #XXX
346   };
347 }
348 #declined batch payments
349 foreach my $cust_pay_batch (
350   $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
351 ) {
352   my $pay_batch = $cust_pay_batch->pay_batch;
353   push @history, {
354     'date'    => $pay_batch->upload,
355     'order'   => 5,
356     'num'     => $cust_pay_batch->paybatchnum,
357     'desc'    => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
358     'void_payment' => $cust_pay_batch->amount,
359   };
360 }
361
362 #credits (some false laziness w/payments)
363 foreach my $cust_credit ($cust_main->cust_credit) {
364   push @history, {
365     'date'   => $cust_credit->_date,
366     'order'  => 8,
367     'num'    => $cust_credit->crednum,
368     'desc'   => include('payment_history/credit.html', $cust_credit, %opt ),
369     'credit' => $cust_credit->amount,
370   };
371
372 }
373
374 #refunds
375 foreach my $cust_refund ($cust_main->cust_refund) {
376   push @history, {
377     'date'   => $cust_refund->_date,
378     'order'  => 9,
379     'num'    => $cust_refund->refundnum,
380     'desc'   => include('payment_history/refund.html', $cust_refund, %opt),
381     'refund' => $cust_refund->refund,
382   };
383
384 }
385
386 # sort in forward order first, and calculate running balances
387 my $years =  $conf->config('payment_history-years') || 2;
388 my $older_than = time - $years * 31556926; #60*60*24*365.2422
389 my $balance = 0;
390
391 @history = sort {    $a->{date}  <=> $b->{date}
392                   or $a->{order} <=> $b->{order}
393                   or $a->{num}   <=> $b->{num}
394                 }
395              @history;
396
397 my $i = 0;
398 my $balance_forward;
399 foreach my $item (@history) {
400   $balance += $item->{'charge'}  if exists $item->{'charge'};
401   $balance -= $item->{'payment'} if exists $item->{'payment'};
402   $balance -= $item->{'credit'}  if exists $item->{'credit'};
403   $balance += $item->{'refund'}  if exists $item->{'refund'};
404   $balance = sprintf("%.2f", $balance);
405   $balance =~ s/^\-0\.00$/0.00/;
406   $item->{'balance'} = $balance;
407
408   if ( $item->{'date'} < $older_than ) {
409     $item->{'hide'} = 1;
410   } elsif ( $history[$i-1]->{'hide'} ) {
411     # this is the end of the hidden section
412     $history[$i-1]->{'balance_forward'} = 1;
413   }
414   $i++;
415 }
416 if ( @history and $history[-1]->{'hide'} ) {
417   # then everything is hidden
418   $history[-1]->{'balance_forward'} = 1;
419 }
420
421 # then sort in user-pref order
422 if ( $curuser->option('history_order') eq 'newest' ) {
423   @history = sort {    $b->{date}  <=> $a->{date}
424                     or $b->{order} <=> $a->{order} #or still forward here?
425                     or $b->{num}   <=> $a->{num}
426                   }
427                @history;
428 } # else it's already oldest-first, and there are no other options yet
429
430 sub translate_payby {
431     my ($payby,$payinfo) = (shift,shift);
432     my %payby = (
433         FS::payby->payby2shortname,
434         BILL    => $payinfo ? emt('Check #') : '',
435         CHEK    => emt('Electronic check '),
436         PREP    => emt('Prepaid card '),
437         CARD    => emt('Credit card #'),
438         COMP    => emt('Complimentary by '),
439         #CASH    => emt('Cash'),
440         #WEST    => emt('Western Union'),
441         #MCRD    => emt('Manual credit card'),
442     );
443     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
444     $payby;
445 };
446
447 sub translate_payby_refund {
448     my ($payby,$payinfo) = (shift,shift);
449     my %payby = (
450         FS::payby->payby2shortname,
451         BILL    => $payinfo ? emt('Check #') : emt('Check'),
452         CHEK    => emt('Electronic check '),
453         CARD    => emt('Credit card #'),
454         COMP    => emt('Complimentary by '),
455     );
456     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
457     $payby;
458 };
459
460 sub translate_payinfo {
461     my $object = shift;
462     my $payby = $object->payby;
463     my $payinfo = $object->payinfo;
464
465     if ( $payby eq 'CARD' ) {
466         $payinfo = $object->paymask;
467     } elsif ( $payby eq 'CHEK' ) {
468         #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
469         my( $account, $aba ) = split('@', $object->paymask );
470         if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
471           my($branch, $routing) = ($1, $2);
472           $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
473                          $routing, $branch, $account);
474         } else {
475           $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
476         }
477     }
478
479     ($payby,$payinfo);
480 }
481
482 sub areyousure_link {
483     my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
484     ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';
485 }
486
487 </%init>