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