481251478d969eab5f9a2e84fc57dd30dc03d0db
[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', 'Resend invoices',
230         'Apply payment', 'Refund credit card payment', 'Refund Echeck payment',
231         'Post refund', 'Post check refund', 'Post cash refund ', 'Refund payment',
232         'Credit card void', 'Echeck void', 'Void payments', 'Unvoid payments',
233         'Unapply payment',
234         'Apply credit', 'Unapply credit', 'Void credit', 'Unvoid credit',
235         'Delete refund',
236         'Billing event reports', 'View customer billing events',
237       )
238   ),
239
240   #customer information
241   'total_owed'              => $cust_main->total_owed,
242   'total_unapplied_refunds' => $cust_main->total_unapplied_refunds,
243   'has_email_address'       => scalar($cust_main->invoicing_list_emailonly),
244 );
245
246 $opt{'date_format'} ||= '%m/%d/%Y';
247
248 #legacy invoices
249 foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) {
250   push @history, {
251     'date'   => $legacy_cust_bill->_date,
252     'order'  => 1,
253     'num'    => $legacy_cust_bill->legacyid,
254     'desc'   => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ),
255     'charge_nobal' => $legacy_cust_bill->charged,
256   };
257 }
258
259 #invoices
260 my $num_cust_bill = 0;
261 foreach my $cust_bill ($cust_main->cust_bill) {
262   push @history, {
263     'date'   => $cust_bill->_date,
264     'order'  => 1,
265     'num'    => $cust_bill->invnum,
266     'desc'   => include('payment_history/invoice.html', $cust_bill, %opt ),
267     'charge' => $cust_bill->charged,
268   };
269   $num_cust_bill++;
270 }
271
272 #voided invoices
273 foreach my $cust_bill_void ($cust_main->cust_bill_void) {
274   push @history, {
275     'date'        => $cust_bill_void->_date,
276     'order'       => 0,
277     'num'         => $cust_bill_void->invnum,
278     'desc'        => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ),
279     'void_charge' => $cust_bill_void->charged,
280   };
281 }
282
283 #statements
284 foreach my $cust_statement ($cust_main->cust_statement) {
285   push @history, {
286     'date'   => $cust_statement->_date,
287     'order'  => 2,
288     'num'    => $cust_statement->statementnum,
289     'desc'   => include('payment_history/statement.html', $cust_statement, %opt ),
290     #'charge' => $cust_bill->charged,
291   };
292 }
293
294 #payments (some false laziness w/credits)
295 foreach my $cust_pay ($cust_main->cust_pay) {
296   push @history, {
297     'date'    => $cust_pay->_date,
298     'order'   => 6,
299     'num'     => $cust_pay->paynum,
300     'desc'    => include('payment_history/payment.html', $cust_pay, %opt ),
301     'payment' => $cust_pay->paid,
302     #'target'  => $target, #XXX
303   };
304 }
305
306 #pending payments 
307 foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
308   push @history, {
309     'date'    => $cust_pay_pending->_date,
310     'order'   => 4,
311     'num'     => $cust_pay_pending->paypendingnum,
312     'desc'    => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
313     'void_payment' => $cust_pay_pending->paid, 
314   };
315 }
316
317
318 #voided payments
319 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
320   push @history, {
321     'date'   => $cust_pay_void->_date,
322     'order'  => 3,
323     'num'    => $cust_pay_void->paynum,
324     'desc'   => include('payment_history/voided_payment.html', $cust_pay_void, %opt ),
325     'void_payment' => $cust_pay_void->paid,
326   };
327
328 }
329
330 #voided credits 
331 foreach my $cust_credit_void ($cust_main->cust_credit_void) {
332   push @history, {
333     'date'        => $cust_credit_void->_date,
334     'order'       => 7,
335     'num'         => $cust_credit_void->paynum,
336     'desc'        => include('payment_history/voided_credit.html', $cust_credit_void, %opt ),
337     'void_credit' => $cust_credit_void->amount,
338   };
339 }
340
341 #declined payments
342 foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
343   push @history, {
344     'date'    => $cust_pay_pending->_date,
345     'order'   => 5,
346     'num'     => $cust_pay_pending->paypendingnum,
347     'desc'    => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
348     'void_payment' => $cust_pay_pending->paid, #??
349     #'target'  => $target, #XXX
350   };
351 }
352 #declined batch payments
353 foreach my $cust_pay_batch (
354   $cust_main->cust_pay_batch(hashref => {status => 'Declined'})
355 ) {
356   my $pay_batch = $cust_pay_batch->pay_batch;
357   push @history, {
358     'date'    => $pay_batch->upload,
359     'order'   => 5,
360     'num'     => $cust_pay_batch->paybatchnum,
361     'desc'    => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt),
362     'void_payment' => $cust_pay_batch->amount,
363   };
364 }
365
366 #credits (some false laziness w/payments)
367 foreach my $cust_credit ($cust_main->cust_credit) {
368   push @history, {
369     'date'   => $cust_credit->_date,
370     'order'  => 8,
371     'num'    => $cust_credit->crednum,
372     'desc'   => include('payment_history/credit.html', $cust_credit, %opt ),
373     'credit' => $cust_credit->amount,
374   };
375
376 }
377
378 #refunds
379 foreach my $cust_refund ($cust_main->cust_refund) {
380   push @history, {
381     'date'   => $cust_refund->_date,
382     'order'  => 9,
383     'num'    => $cust_refund->refundnum,
384     'desc'   => include('payment_history/refund.html', $cust_refund, %opt),
385     'refund' => $cust_refund->refund,
386   };
387
388 }
389
390 # sort in forward order first, and calculate running balances
391 my $years =  $conf->config('payment_history-years') || 2;
392 my $older_than = time - $years * 31556926; #60*60*24*365.2422
393 my $balance = 0;
394
395 @history = sort {    $a->{date}  <=> $b->{date}
396                   or $a->{order} <=> $b->{order}
397                   or $a->{num}   <=> $b->{num}
398                 }
399              @history;
400
401 my $i = 0;
402 my $balance_forward;
403 foreach my $item (@history) {
404   $balance += $item->{'charge'}  if exists $item->{'charge'};
405   $balance -= $item->{'payment'} if exists $item->{'payment'};
406   $balance -= $item->{'credit'}  if exists $item->{'credit'};
407   $balance += $item->{'refund'}  if exists $item->{'refund'};
408   $balance = sprintf("%.2f", $balance);
409   $balance =~ s/^\-0\.00$/0.00/;
410   $item->{'balance'} = $balance;
411
412   if ( $item->{'date'} < $older_than ) {
413     $item->{'hide'} = 1;
414   } elsif ( $history[$i-1]->{'hide'} ) {
415     # this is the end of the hidden section
416     $history[$i-1]->{'balance_forward'} = 1;
417   }
418   $i++;
419 }
420 if ( @history and $history[-1]->{'hide'} ) {
421   # then everything is hidden
422   $history[-1]->{'balance_forward'} = 1;
423 }
424
425 # then sort in user-pref order
426 if ( $curuser->option('history_order') eq 'newest' ) {
427   @history = sort {    $b->{date}  <=> $a->{date}
428                     or $b->{order} <=> $a->{order} #or still forward here?
429                     or $b->{num}   <=> $a->{num}
430                   }
431                @history;
432 } # else it's already oldest-first, and there are no other options yet
433
434 sub translate_payby {
435     my ($payby,$payinfo) = (shift,shift);
436     my %payby = (
437         FS::payby->payby2shortname,
438         BILL    => $payinfo ? emt('Check #') : '',
439         CHEK    => emt('Electronic check '),
440         PREP    => emt('Prepaid card '),
441         CARD    => emt('Credit card #'),
442         COMP    => emt('Complimentary by '),
443         #CASH    => emt('Cash'),
444         #WEST    => emt('Western Union'),
445         #MCRD    => emt('Manual credit card'),
446     );
447     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
448     $payby;
449 };
450
451 sub translate_payby_refund {
452     my ($payby,$payinfo) = (shift,shift);
453     my %payby = (
454         FS::payby->payby2shortname,
455         BILL    => $payinfo ? emt('Check #') : emt('Check'),
456         CHEK    => emt('Electronic check '),
457         CARD    => emt('Credit card #'),
458         COMP    => emt('Complimentary by '),
459     );
460     $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; 
461     $payby;
462 };
463
464 sub translate_payinfo {
465     my $object = shift;
466     my $payby = $object->payby;
467     my $payinfo = $object->payinfo;
468
469     if ( $payby eq 'CARD' ) {
470         $payinfo = $object->paymask;
471     } elsif ( $payby eq 'CHEK' ) {
472         #false laziness w/payinfo_Mixin::payby_payinfo_pretty, should use that
473         my( $account, $aba ) = split('@', $object->paymask );
474         if ( $aba =~ /^(\d{5})\.(\d{3})$/ ) { #blame canada
475           my($branch, $routing) = ($1, $2);
476           $payinfo = emt("Routing [_1], Branch [_2], Acct [_3]",
477                          $routing, $branch, $account);
478         } else {
479           $payinfo = emt("Routing [_1], Acct [_2]", $aba, $account);
480         }
481     }
482
483     ($payby,$payinfo);
484 }
485
486 sub areyousure_link {
487     my ($url,$msg,$title,$label) = (shift,shift,shift,shift);
488     ' (<A HREF="javascript:areyousure(\''.$url.'\',\''.$msg.'\')" TITLE="'.$title.'">'.$label.'</A>)';
489 }
490
491 </%init>