first part of ACL and re-skinning work and some other small stuff
[freeside.git] / httemplate / view / cust_main / payment_history.html
1 <%
2   my( $cust_main ) = @_;
3   my $custnum = $cust_main->custnum;
4
5   my $conf = new FS::Conf;
6
7   my @payby = grep /\w/, $conf->config('payby');
8   #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
9   @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
10     unless @payby;
11   my %payby = map { $_=>1 } @payby;
12
13   my $s = 0;
14
15 %>
16
17 <BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR>
18
19 <% if ( $payby{'BILL'} ) { %>
20
21   <%= $s++ ? ' | ' : '' %>
22   <A HREF="<%= $p %>edit/cust_pay.cgi?payby=BILL;custnum=<%= $custnum %>">Post check payment</A>
23
24 <% } %>
25
26 <% if ( $payby{'CASH'} ) { %>
27
28   <%= $s++ ? ' | ' : '' %>
29   <A HREF="<%= $p %>edit/cust_pay.cgi?payby=CASH;custnum=<%= $custnum %>">Post cash payment</A>
30
31 <% } %>
32
33 <% if ( $payby{'WEST'} ) { %>
34
35   <%= $s++ ? ' | ' : '' %>
36   <A HREF="<%= $p %>edit/cust_pay.cgi?payby=WEST;custnum=<%= $custnum %>">Post Western Union payment</A>
37
38 <% } %>
39
40 <% if ( $payby{'CARD'} || $payby{'DCRD'} ) { %>
41
42   <%= $s++ ? ' | ' : '' %>
43   <A HREF="<%= $p %>misc/payment.cgi?payby=CARD;custnum=<%= $custnum %>">Process credit card payment</A>
44
45 <% } %>
46
47 <% if ( $payby{'CHEK'} || $payby{'DCHK'} ) { %>
48
49   <%= $s++ ? ' | ' : '' %>
50   <A HREF="<%= $p %>misc/payment.cgi?payby=CHEK;custnum=<%= $custnum %>">Process electronic check (ACH) payment</A>
51
52 <% } %>
53
54 <% if ( $payby{'MCRD'} ) { %>
55
56   <%= $s++ ? ' | ' : '' %>
57   <A HREF="<%= $p %>edit/cust_pay.cgi?payby=MCRD;custnum=<%= $custnum %>">Post manual credit card payment</A>
58
59 <% } %>
60
61 <BR>
62
63 <A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<%= $p %>edit/cust_credit.cgi?<%= $custnum %>', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">Post credit</A>
64
65 <BR>
66
67 <%
68 #get payment history
69 my @history = ();
70
71 #invoices
72 foreach my $cust_bill ($cust_main->cust_bill) {
73   my $pre = ( $cust_bill->owed > 0 )
74               ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open '
75               : '';
76   my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : '';
77   my $invnum = $cust_bill->invnum;
78   push @history, {
79     'date'   => $cust_bill->_date,
80     'desc'   => qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!. $pre.
81                 "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'.
82                 $post. '</A>',
83     'charge' => $cust_bill->charged,
84   };
85 }
86
87 #payments (some false laziness w/credits)
88 foreach my $cust_pay ($cust_main->cust_pay) {
89
90   my $payby = $cust_pay->payby;
91
92   my $payinfo;
93   if ( $payby eq 'CARD' ) {
94     $payinfo = $cust_pay->payinfo_masked;
95   } elsif ( $payby eq 'CHEK' && $cust_pay->payinfo =~ /^(\d+)\@(\d+)$/ ) {
96     $payinfo = "ABA $2, Acct# $1";
97   } else {
98     $payinfo = $cust_pay->payinfo;
99   }
100   my @cust_bill_pay = $cust_pay->cust_bill_pay;
101   my @cust_pay_refund = $cust_pay->cust_pay_refund;
102
103   my $target = "$payby$payinfo";
104   $payby =~ s/^BILL$/Check #/ if $payinfo;
105   $payby =~ s/^CHEK$/Electronic check /;
106   $payby =~ s/^PREP$/Prepaid card /;
107   $payby =~ s/^CARD$/Credit card #/; 
108   $payby =~ s/^COMP$/Complimentary by /; 
109   $payby =~ s/^CASH$/Cash/;
110   $payby =~ s/^WEST$/Western Union/;
111   $payby =~ s/^MCRD$/Manual credit card/;
112   $payby =~ s/^BILL$//;
113   my $info = $payby ? " ($payby$payinfo)" : '';
114
115   my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
116   if (    scalar(@cust_bill_pay)   == 0
117        && scalar(@cust_pay_refund) == 0 ) {
118     #completely unapplied
119     $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
120     $post = '</FONT></B>';
121     $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
122              $cust_pay->paynum.
123              qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!;
124
125   } elsif (    scalar(@cust_bill_pay)   == 1
126             && scalar(@cust_pay_refund) == 0
127             && $cust_pay->unapplied == 0     ) {
128     #applied to one invoice, the usual situation
129     $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum;
130   } elsif (    scalar(@cust_bill_pay)   == 0
131             && scalar(@cust_pay_refund) == 1
132             && $cust_pay->unapplied == 0     ) {
133     #applied to one refund
134     $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
135   } else {
136     #complicated
137     $desc = '<BR>';
138     foreach my $app ( sort { $a->_date <=> $b->_date }
139                            ( @cust_bill_pay, @cust_pay_refund ) ) {
140       if ( $app->isa('FS::cust_bill_pay') ) {
141         $desc .= '&nbsp;&nbsp;'.
142                  '$'. $app->amount.
143                  ' applied to Invoice #'. $app->invnum.
144                  '<BR>';
145                  #' on '. time2str("%D", $cust_bill_pay->_date).
146       } elsif ( $app->isa('FS::cust_pay_refund') ) {
147         $desc .= '&nbsp;&nbsp;'.
148                  '$'. $app->amount.
149                  ' refunded on'. time2str("%D", $app->_date).
150                  '<BR>';
151       } else {
152         die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
153       }
154     }
155     if ( $cust_pay->unapplied > 0 ) {
156       $desc .= '&nbsp;&nbsp;'.
157                '<B><FONT COLOR="#FF0000">$'.
158                $cust_pay->unapplied. ' unapplied</FONT></B>'.
159                qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_bill_pay.cgi?!.
160                $cust_pay->paynum. 
161                qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!.
162                '<BR>';
163     }
164   }
165
166   my $refund = '';
167   my $refund_days = $conf->config('card_refund-days') || 120;
168   if (    $cust_pay->closed !~ /^Y/i
169        && $cust_pay->payby =~ /^(CARD|CHEK)$/
170        && time-$cust_pay->_date < $refund_days*86400
171        && $cust_pay->unrefunded > 0
172   ) {
173     $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
174               qq!paynum=!. $cust_pay->paynum. '"'.
175               qq! TITLE="Send a refund for this payment to the payment gateway"!.
176               qq!>refund</A>)!;
177   }
178
179   my $void = '';
180   if (    $cust_pay->closed !~ /^Y/i
181        && ( $cust_pay->payby ne 'CARD' || $conf->exists('cc-void')     )
182        && ( $cust_pay->payby ne 'CHEK' || $conf->exists('echeck-void') ) 
183      ) {
184     $void = qq! (<A HREF="javascript:areyousure('!.
185             qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum.
186             qq!', 'Are you sure you want to void this payment?')"!.
187             qq! TITLE="Void this payment from the database!.
188               ( $cust_pay->payby =~ /^(CARD|CHEK)$/
189                 ? ' (do not send anything to the payment gateway)'
190                 : '' 
191               ). '"'.
192             qq!>void</A>)!;
193   }
194
195   my $delete = '';
196   if ( $cust_pay->closed !~ /^Y/i && $conf->exists('deletepayments') ) {
197     $delete = qq! (<A HREF="javascript:areyousure('!.
198               qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum.
199               qq!', 'Are you sure you want to delete this payment?')"!.
200               qq! TITLE="Delete this payment from the database completely - not recommended"!.
201               qq!>delete</A>)!;
202   }
203
204   my $unapply = '';
205   if (    $cust_pay->closed !~ /^Y/i
206        && $conf->exists('unapplypayments')
207        && scalar(@cust_bill_pay)           ) {
208     $unapply = qq! (<A HREF="javascript:areyousure('!.
209                qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum.
210                qq!', 'Are you sure you want to unapply this payment?')"!.
211                qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!.
212                qq!>unapply</A>)!;
213   }
214
215   push @history, {
216     'date'    => $cust_pay->_date,
217     'desc'    => $pre. "Payment$post$info$desc".
218                  "$apply$refund$void$delete$unapply",
219     'payment' => $cust_pay->paid,
220     'target'  => $target,
221   };
222 }
223
224 #voided payments
225 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
226
227   my $payby = $cust_pay_void->payby;
228   my $payinfo = $payby eq 'CARD'
229                   ? $cust_pay_void->payinfo_masked
230                   : $cust_pay_void->payinfo;
231
232   $payby =~ s/^BILL$/Check #/ if $payinfo;
233   $payby =~ s/^CHEK$/Electronic check /;
234   $payby =~ s/^BILL$//;
235   $payby =~ s/^(CARD|COMP)$/$1 /;
236   my $info = $payby ? " ($payby$payinfo)" : '';
237
238   my $unvoid = '';
239   if ( $cust_pay_void->closed !~ /^Y/i && $conf->exists('unvoid') ) {
240     $unvoid = qq! (<A HREF="javascript:areyousure('!.
241               qq!${p}misc/unvoid-cust_pay_void.cgi?!. $cust_pay_void->paynum.
242               qq!', 'Are you sure you want to unvoid this payment?')"!.
243               qq! TITLE="Unvoid this payment from the database!.
244                 ( $cust_pay_void->payby =~ /^(CARD|CHEK)$/
245                   ? ' (do not send anything to the payment gateway)'
246                   : '' 
247                 ). '"'.
248               qq!>unvoid</A>)!;
249   }
250
251   push @history, {
252     'date'   => $cust_pay_void->_date,
253     'desc'   => "<DEL>Payment $info</DEL> <I>voided ".
254                 time2str("%D", $cust_pay_void->void_date).
255                 " by ". $cust_pay_void->otaker. '</i>'. $unvoid,
256     'void_payment' => $cust_pay_void->paid,
257   };
258
259 }
260
261 #credits (some false laziness w/payments)
262 foreach my $cust_credit ($cust_main->cust_credit) {
263
264   my @cust_credit_bill = $cust_credit->cust_credit_bill;
265   my @cust_credit_refund = $cust_credit->cust_credit_refund;
266
267   my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
268   if (    scalar(@cust_credit_bill)   == 0
269        && scalar(@cust_credit_refund) == 0 ) {
270     #completely unapplied
271     $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
272     $post = '</FONT></B>';
273     $apply = qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
274              $cust_credit->crednum.
275              qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!;
276   } elsif (    scalar(@cust_credit_bill)   == 1
277             && scalar(@cust_credit_refund) == 0
278             && $cust_credit->credited == 0      ) {
279     #applied to one invoice, the usual situation
280     $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum;
281   } elsif (    scalar(@cust_credit_bill)   == 0
282             && scalar(@cust_credit_refund) == 1
283             && $cust_credit->credited == 0      ) {
284     #applied to one refund
285     $desc = ' refunded on '.  time2str("%D", $cust_credit_refund[0]->_date);
286   } else {
287     #complicated
288     $desc = '<BR>';
289     foreach my $app ( sort { $a->_date <=> $b->_date }
290                            ( @cust_credit_bill, @cust_credit_refund ) ) {
291       if ( $app->isa('FS::cust_credit_bill') ) {
292         $desc .= '&nbsp;&nbsp;'.
293                  '$'. $app->amount.
294                  ' applied to Invoice #'. $app->invnum.
295                  '<BR>';
296                  #' on '. time2str("%D", $app->_date).
297       } elsif ( $app->isa('FS::cust_credit_refund') ) {
298         $desc .= '&nbsp;&nbsp;'.
299                  '$'. $app->amount.
300                  ' refunded on'. time2str("%D", $app->_date).
301                  '<BR>';
302       } else {
303         die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
304       }
305     }
306     if ( $cust_credit->credited > 0 ) {
307       $desc .= '&nbsp;&nbsp;<B><FONT COLOR="#FF0000">$'.
308                $cust_credit->credited. ' unapplied</FONT></B>'.
309                qq! (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('${p}edit/cust_credit_bill.cgi?!.
310                $cust_credit->crednum.
311                qq!', 392, 336, 'cust_credit_popup' ), CAPTION, 'Post credit', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK )">apply</A>)!.
312                '<BR>';
313     }
314   }
315 #
316   my $delete = '';
317   if ( $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') ) {
318     $delete = qq! (<A HREF="javascript:areyousure('!.
319               qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum.
320               qq!', 'Are you sure you want to delete this credit?')">!.
321               qq!delete</A>)!;
322   }
323   
324   my $unapply = '';
325   if (    $cust_credit->closed !~ /^Y/i
326        && $conf->exists('unapplycredits')
327        && scalar(@cust_credit_bill)       ) {
328     $unapply = qq! (<A HREF="javascript:areyousure('!.
329                qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum.
330                qq!', 'Are you sure you want to unapply this credit?')">!.
331                qq!unapply</A>)!;
332   }
333   
334   push @history, {
335     'date'   => $cust_credit->_date,
336     'desc'   => $pre. "Credit$post by ". $cust_credit->otaker.
337                 ( $cust_credit->reason
338                     ? ' ('. $cust_credit->reason. ')'
339                     : ''
340                 ).
341                 "$desc$apply$delete$unapply",
342     'credit' => $cust_credit->amount,
343   };
344
345 }
346
347 #refunds
348 foreach my $cust_refund ($cust_main->cust_refund) {
349
350   my $payby = $cust_refund->payby;
351   my $payinfo = $payby eq 'CARD'
352                   ? $cust_refund->payinfo_masked
353                   : $cust_refund->payinfo;
354
355   $payby =~ s/^BILL$/Check #/ if $payinfo;
356   $payby =~ s/^CHEK$/Electronic check /;
357   $payby =~ s/^(CARD|COMP)$/$1 /;
358
359   push @history, {
360     'date'   => $cust_refund->_date,
361     'desc'   => "Refund ($payby$payinfo) by ". $cust_refund->otaker,
362     'refund' => $cust_refund->refund,
363   };
364
365 }
366
367 %>
368
369 <%= include("/elements/table.html") %>
370 <TR>
371   <TH>Date</TH>
372   <TH>Description</TH>
373   <TH><FONT SIZE=-1>Charge</FONT></TH>
374   <TH><FONT SIZE=-1>Payment</FONT></TH>
375   <TH><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
376   <TH><FONT SIZE=-1>Refund</FONT></TH>
377   <TH><FONT SIZE=-1>Balance</FONT></TH>
378 </TR>
379
380 <%
381 #display payment history
382
383 my %target;
384 my $balance = 0;
385 foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
386
387   my $charge  = exists($item->{'charge'})
388                   ? sprintf('$%.2f', $item->{'charge'})
389                   : '';
390   my $payment = exists($item->{'payment'})
391                   ? sprintf('-&nbsp;$%.2f', $item->{'payment'})
392                   : '';
393   $payment ||= sprintf('<DEL>-&nbsp;$%.2f</DEL>', $item->{'void_payment'})
394     if exists($item->{'void_payment'});
395   my $credit  = exists($item->{'credit'})
396                   ? sprintf('-&nbsp;$%.2f', $item->{'credit'})
397                   : '';
398   my $refund  = exists($item->{'refund'})
399                   ? sprintf('$%.2f', $item->{'refund'})
400                   : '';
401
402   my $target = exists($item->{'target'}) ? $item->{'target'} : '';
403
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/; #yay ieee fp
410   ( my $showbalance = '$'. $balance ) =~ s/^\$\-/-&nbsp;\$/;
411
412 %>
413
414   <TR>
415     <TD>
416       <% unless ( !$target || $target{$target}++ ) { %>
417         <A NAME="<%= $target %>">
418       <% } %>
419       <%= time2str("%D",$item->{'date'}) %>
420       <% if ( $target && $target{$target} == 1 ) { %>
421         </A>
422       <% } %>
423       </FONT>
424     </TD>
425     <TD><%= $item->{'desc'} %></TD>
426     <TD ALIGN="right"><%= $charge  %></TD>
427     <TD ALIGN="right"><%= $payment %></TD>
428     <TD ALIGN="right"><%= $credit  %></TD>
429     <TD ALIGN="right"><%= $refund  %></TD>
430     <TD ALIGN="right"><%= $showbalance %></TD>
431   </TR>
432
433 <% } %>
434
435 </TABLE>
436