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 /;
54 $payby =~ s/^(CARD|COMP)$/$1 /;
55 my $info = $payby ? " ($payby$payinfo)" : '';
57 my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
58 if ( scalar(@cust_bill_pay) == 0
59 && scalar(@cust_pay_refund) == 0 ) {
61 $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
62 $post = '</FONT></B>';
63 $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!.
64 $cust_pay->paynum. '">apply</A>)';
65 } elsif ( scalar(@cust_bill_pay) == 1
66 && scalar(@cust_pay_refund) == 0
67 && $cust_pay->unapplied == 0 ) {
68 #applied to one invoice, the usual situation
69 $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum;
70 } elsif ( scalar(@cust_bill_pay) == 0
71 && scalar(@cust_pay_refund) == 1
72 && $cust_pay->unapplied == 0 ) {
73 #applied to one refund
74 $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
78 foreach my $app ( sort { $a->_date <=> $b->_date }
79 ( @cust_bill_pay, @cust_pay_refund ) ) {
80 if ( $app->isa('FS::cust_bill_pay') ) {
81 $desc .= ' '.
83 ' applied to Invoice #'. $app->invnum.
85 #' on '. time2str("%D", $cust_bill_pay->_date).
86 } elsif ( $app->isa('FS::cust_pay_refund') ) {
87 $desc .= ' '.
89 ' refunded on'. time2str("%D", $app->_date).
92 die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
95 if ( $cust_pay->unapplied > 0 ) {
96 $desc .= ' '.
97 '<B><FONT COLOR="#FF0000">$'.
98 $cust_pay->unapplied. ' unapplied</FONT></B>'.
99 qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!.
100 $cust_pay->paynum. '">apply</A>)'.
106 my $refund_days = $conf->config('card_refund-days') || 120;
107 if ( $cust_pay->closed !~ /^Y/i
108 && $cust_pay->payby =~ /^(CARD|CHEK)$/
109 && time-$cust_pay->_date < $refund_days*86400
110 && $cust_pay->unrefunded > 0
112 $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
113 qq!paynum=!. $cust_pay->paynum. '"'.
114 qq! TITLE="Send a refund for this payment to the payment gateway"!.
119 if ( $cust_pay->closed !~ /^Y/i
120 && $cust_pay->payby ne 'CARD'
121 && ( $cust_pay->payby ne 'CHEK' || $conf->exists('echeck-void') )
123 $void = qq! (<A HREF="javascript:areyousure('!.
124 qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum.
125 qq!', 'Are you sure you want to void this payment?')"!.
126 qq! TITLE="Void this payment from the database!.
127 ( $cust_pay->payby eq 'CHEK'
128 ? ' (do not send anything to the payment gateway)'
135 if ( $cust_pay->closed !~ /^Y/i && $conf->exists('deletepayments') ) {
136 $delete = qq! (<A HREF="javascript:areyousure('!.
137 qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum.
138 qq!', 'Are you sure you want to delete this payment?')"!.
139 qq! TITLE="Delete this payment from the database completely - not recommended"!.
144 if ( $cust_pay->closed !~ /^Y/i
145 && $conf->exists('unapplypayments')
146 && scalar(@cust_bill_pay) ) {
147 $unapply = qq! (<A HREF="javascript:areyousure('!.
148 qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum.
149 qq!', 'Are you sure you want to unapply this payment?')"!.
150 qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!.
155 'date' => $cust_pay->_date,
156 'desc' => $pre. "Payment$post$info$desc".
157 "$apply$refund$void$delete$unapply",
158 'payment' => $cust_pay->paid,
164 foreach my $cust_pay_void ($cust_main->cust_pay_void) {
166 my $payby = $cust_pay_void->payby;
167 my $payinfo = $payby eq 'CARD'
168 ? $cust_pay_void->payinfo_masked
169 : $cust_pay_void->payinfo;
171 $payby =~ s/^BILL$/Check #/ if $payinfo;
172 $payby =~ s/^CHEK$/Electronic check /;
173 $payby =~ s/^BILL$//;
174 $payby =~ s/^(CARD|COMP)$/$1 /;
175 my $info = $payby ? " ($payby$payinfo)" : '';
178 'date' => $cust_pay_void->_date,
179 'desc' => "<DEL>Payment $info</DEL> <I>voided ".
180 time2str("%D", $cust_pay_void->void_date).
181 " by ". $cust_pay_void->otaker. '</i>',
182 'void_payment' => $cust_pay_void->paid,
187 #credits (some false laziness w/payments)
188 foreach my $cust_credit ($cust_main->cust_credit) {
190 my @cust_credit_bill = $cust_credit->cust_credit_bill;
191 my @cust_credit_refund = $cust_credit->cust_credit_refund;
193 my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
194 if ( scalar(@cust_credit_bill) == 0
195 && scalar(@cust_credit_refund) == 0 ) {
196 #completely unapplied
197 $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
198 $post = '</FONT></B>';
199 $apply = qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!.
200 $cust_credit->crednum. '">apply</A>)';
201 } elsif ( scalar(@cust_credit_bill) == 1
202 && scalar(@cust_credit_refund) == 0
203 && $cust_credit->credited == 0 ) {
204 #applied to one invoice, the usual situation
205 $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum;
206 } elsif ( scalar(@cust_credit_bill) == 0
207 && scalar(@cust_credit_refund) == 1
208 && $cust_credit->credited == 0 ) {
209 #applied to one refund
210 $desc = ' refunded on '. time2str("%D", $cust_credit_refund[0]->_date);
214 foreach my $app ( sort { $a->_date <=> $b->_date }
215 ( @cust_credit_bill, @cust_credit_refund ) ) {
216 if ( $app->isa('FS::cust_credit_bill') ) {
217 $desc .= ' '.
219 ' applied to Invoice #'. $app->invnum.
221 #' on '. time2str("%D", $app->_date).
222 } elsif ( $app->isa('FS::cust_credit_refund') ) {
223 $desc .= ' '.
225 ' refunded on'. time2str("%D", $app->_date).
228 die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
231 if ( $cust_credit->credited > 0 ) {
232 $desc .= ' <B><FONT COLOR="#FF0000">$'.
233 $cust_credit->credited. ' unapplied</FONT></B>'.
234 qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!.
235 $cust_credit->crednum. '">apply</A>)'.
241 if ( $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') ) {
242 $delete = qq! (<A HREF="javascript:areyousure('!.
243 qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum.
244 qq!', 'Are you sure you want to delete this credit?')">!.
249 if ( $cust_credit->closed !~ /^Y/i
250 && $conf->exists('unapplycredits')
251 && scalar(@cust_credit_bill) ) {
252 $unapply = qq! (<A HREF="javascript:areyousure('!.
253 qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum.
254 qq!', 'Are you sure you want to unapply this credit?')">!.
259 'date' => $cust_credit->_date,
260 'desc' => $pre. "Credit$post by ". $cust_credit->otaker.
261 ' ('. $cust_credit->reason. ')'.
262 "$desc$apply$delete$unapply",
263 'credit' => $cust_credit->amount,
269 foreach my $cust_refund ($cust_main->cust_refund) {
271 my $payby = $cust_refund->payby;
272 my $payinfo = $payby eq 'CARD'
273 ? $cust_refund->payinfo_masked
274 : $cust_refund->payinfo;
276 $payby =~ s/^BILL$/Check #/ if $payinfo;
277 $payby =~ s/^CHEK$/Electronic check /;
278 $payby =~ s/^(CARD|COMP)$/$1 /;
281 'date' => $cust_refund->_date,
282 'desc' => "Refund ($payby$payinfo) by ". $cust_refund->otaker,
283 'refund' => $cust_refund->refund,
294 <TH><FONT SIZE=-1>Charge</FONT></TH>
295 <TH><FONT SIZE=-1>Payment</FONT></TH>
296 <TH><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
297 <TH><FONT SIZE=-1>Refund</FONT></TH>
298 <TH><FONT SIZE=-1>Balance</FONT></TH>
302 #display payment history
306 foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
308 my $charge = exists($item->{'charge'})
309 ? sprintf('$%.2f', $item->{'charge'})
311 my $payment = exists($item->{'payment'})
312 ? sprintf('- $%.2f', $item->{'payment'})
314 $payment ||= sprintf('<DEL>- $%.2f</DEL>', $item->{'void_payment'})
315 if exists($item->{'void_payment'});
316 my $credit = exists($item->{'credit'})
317 ? sprintf('- $%.2f', $item->{'credit'})
319 my $refund = exists($item->{'refund'})
320 ? sprintf('$%.2f', $item->{'refund'})
323 my $target = exists($item->{'target'}) ? $item->{'target'} : '';
325 $balance += $item->{'charge'} if exists $item->{'charge'};
326 $balance -= $item->{'payment'} if exists $item->{'payment'};
327 $balance -= $item->{'credit'} if exists $item->{'credit'};
328 $balance += $item->{'refund'} if exists $item->{'refund'};
329 $balance = sprintf("%.2f", $balance);
330 $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
331 ( my $showbalance = '$'. $balance ) =~ s/^\$\-/- \$/;
337 <% unless ( !$target || $target{$target}++ ) { %>
338 <A NAME="<%= $target %>">
340 <%= time2str("%D",$item->{'date'}) %>
341 <% if ( $target && $target{$target} == 1 ) { %>
346 <TD><%= $item->{'desc'} %></TD>
347 <TD ALIGN="right"><%= $charge %></TD>
348 <TD ALIGN="right"><%= $payment %></TD>
349 <TD ALIGN="right"><%= $credit %></TD>
350 <TD ALIGN="right"><%= $refund %></TD>
351 <TD ALIGN="right"><%= $showbalance %></TD>