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