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