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