3 my $conf = new FS::Conf;
4 my $custnum = $cust_main->custnum;
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>
19 foreach my $cust_bill ($cust_main->cust_bill) {
20 my $pre = ( $cust_bill->owed > 0 )
21 ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open '
23 my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : '';
24 my $invnum = $cust_bill->invnum;
26 'date' => $cust_bill->_date,
27 'desc' => qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!. $pre.
28 "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'.
30 'charge' => $cust_bill->charged,
34 #payments (some false laziness w/credits)
35 foreach my $cust_pay ($cust_main->cust_pay) {
37 my $payby = $cust_pay->payby;
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";
45 $payinfo = $cust_pay->payinfo;
47 my @cust_bill_pay = $cust_pay->cust_bill_pay;
48 my @cust_pay_refund = $cust_pay->cust_pay_refund;
50 my $target = "$payby$payinfo";
51 $payby =~ s/^BILL$/Check #/ if $payinfo;
52 $payby =~ s/^CHEK$/Electronic check /;
53 $payby =~ s/^PREP$/Prepaid card /;
55 $payby =~ s/^(CARD|COMP)$/$1 /;
56 my $info = $payby ? " ($payby$payinfo)" : '';
58 my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
59 if ( scalar(@cust_bill_pay) == 0
60 && scalar(@cust_pay_refund) == 0 ) {
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);
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 .= ' '.
84 ' applied to Invoice #'. $app->invnum.
86 #' on '. time2str("%D", $cust_bill_pay->_date).
87 } elsif ( $app->isa('FS::cust_pay_refund') ) {
88 $desc .= ' '.
90 ' refunded on'. time2str("%D", $app->_date).
93 die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
96 if ( $cust_pay->unapplied > 0 ) {
97 $desc .= ' '.
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>)'.
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
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"!.
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') )
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)'
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"!.
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"!.
156 'date' => $cust_pay->_date,
157 'desc' => $pre. "Payment$post$info$desc".
158 "$apply$refund$void$delete$unapply",
159 'payment' => $cust_pay->paid,
165 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
167 my $payby = $cust_pay_void->payby;
168 my $payinfo = $payby eq 'CARD'
169 ? $cust_pay_void->payinfo_masked
170 : $cust_pay_void->payinfo;
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)" : '';
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,
188 #credits (some false laziness w/payments)
189 foreach my $cust_credit ($cust_main->cust_credit) {
191 my @cust_credit_bill = $cust_credit->cust_credit_bill;
192 my @cust_credit_refund = $cust_credit->cust_credit_refund;
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);
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 .= ' '.
220 ' applied to Invoice #'. $app->invnum.
222 #' on '. time2str("%D", $app->_date).
223 } elsif ( $app->isa('FS::cust_credit_refund') ) {
224 $desc .= ' '.
226 ' refunded on'. time2str("%D", $app->_date).
229 die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
232 if ( $cust_credit->credited > 0 ) {
233 $desc .= ' <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>)'.
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?')">!.
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?')">!.
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,
270 foreach my $cust_refund ($cust_main->cust_refund) {
272 my $payby = $cust_refund->payby;
273 my $payinfo = $payby eq 'CARD'
274 ? $cust_refund->payinfo_masked
275 : $cust_refund->payinfo;
277 $payby =~ s/^BILL$/Check #/ if $payinfo;
278 $payby =~ s/^CHEK$/Electronic check /;
279 $payby =~ s/^(CARD|COMP)$/$1 /;
282 'date' => $cust_refund->_date,
283 'desc' => "Refund ($payby$payinfo) by ". $cust_refund->otaker,
284 'refund' => $cust_refund->refund,
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>
303 #display payment history
307 foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
309 my $charge = exists($item->{'charge'})
310 ? sprintf('$%.2f', $item->{'charge'})
312 my $payment = exists($item->{'payment'})
313 ? sprintf('- $%.2f', $item->{'payment'})
315 $payment ||= sprintf('<DEL>- $%.2f</DEL>', $item->{'void_payment'})
316 if exists($item->{'void_payment'});
317 my $credit = exists($item->{'credit'})
318 ? sprintf('- $%.2f', $item->{'credit'})
320 my $refund = exists($item->{'refund'})
321 ? sprintf('$%.2f', $item->{'refund'})
324 my $target = exists($item->{'target'}) ? $item->{'target'} : '';
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/^\$\-/- \$/;
338 <% unless ( !$target || $target{$target}++ ) { %>
339 <A NAME="<%= $target %>">
341 <%= time2str("%D",$item->{'date'}) %>
342 <% if ( $target && $target{$target} == 1 ) { %>
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>