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