%#display payment history %my %target = (); % %my $hidden = 0; %my $seen = 0; %my $old_history = 0; %my $lastdate = 0; % %foreach my $item ( @history ) { % % $lastdate = $item->{'date'}; % % my $display = ''; % if ( $item->{'hide'} ) { % $display = ' STYLE="display:none" '; % } % % if ( $bgcolor eq $bgcolor1 ) { % $bgcolor = $bgcolor2; % } else { % $bgcolor = $bgcolor1; % } % % my $charge = exists($item->{'charge'}) % ? sprintf("$money_char\%.2f", $item->{'charge'}) % : exists($item->{'charge_nobal'}) % ? sprintf("$money_char\%.2f", $item->{'charge_nobal'}) % : exists($item->{'void_charge'}) % ? sprintf("$money_char\%.2f", $item->{'void_charge'}) % : ''; % % my $payment = exists($item->{'payment'}) % ? sprintf("- $money_char\%.2f", $item->{'payment'}) % : ''; % % $payment ||= sprintf( "- $money_char\%.2f", % $item->{'void_payment'} % ) % if exists($item->{'void_payment'}); % % my $credit = exists($item->{'credit'}) % ? sprintf("- $money_char\%.2f", $item->{'credit'}) % : ''; % % my $refund = exists($item->{'refund'}) % ? sprintf("$money_char\%.2f", $item->{'refund'}) % : ''; % % my $target = exists($item->{'target'}) ? $item->{'target'} : ''; % % my $showbalance = $money_char . $item->{'balance'}; % $showbalance =~ s/^\$\-/- \$/; > % if ( $item->{'balance_forward'} ) { <& .balance_forward_row, $item->{'balance'}, $item->{'date'} &> % } %} # foreach $item
%# payment links % my $s = 0; % if ( $payby{'BILL'} && $curuser->access_right(['Post payment', 'Post check payment' ]) ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Enter check payment'), 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=BILL", 'cust_main' => $cust_main, 'actionlabel' => emt('Enter check payment'), 'width' => 392, 'height' => 392, &> % } % if ( $payby{'CASH'} && $curuser->access_right(['Post payment', 'Post cash payment']) ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Enter cash payment'), 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=CASH", 'cust_main' => $cust_main, 'actionlabel' => emt('Enter cash payment'), 'width' => 392, 'height' => 392, &> % } % if ( $payby{'WEST'} && $curuser->access_right('Post payment') ) { <% $s++ ? ' | ' : '' %> <% mt('Enter Western Union payment') |h %> % } <% $s ? '
' : '' %> % $s=0; % if ( ( $payby{'CARD'} || $payby{'DCRD'} ) % && $curuser->access_right(['Process payment', 'Process credit card payment']) % && ! $cust_main->is_encrypted($cust_main->payinfo) % ) { <% $s++ ? ' | ' : '' %> <% mt('Process credit card payment') |h %> % } % if ( ( $payby{'CHEK'} || $payby{'DCHK'} ) % && $curuser->access_right(['Process payment', 'Process Echeck payment']) % && ! $cust_main->is_encrypted($cust_main->payinfo) % ) { <% $s++ ? ' | ' : '' %> <% mt('Process electronic check (ACH) payment') |h %> % } % if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) { <% $s++ ? ' | ' : '' %> <% mt('Post manual (offline/POS) credit card payment') |h %> % } <% $s ? '
' : '' %> %# credit links % $s=0; % if ( $curuser->access_right('Post credit') ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Enter credit'), 'action' => "${p}edit/cust_credit.cgi", 'cust_main' => $cust_main, 'actionlabel' => emt('Enter credit'), 'width' => 616, #make room for reasons #540 default &> % } % if ( $curuser->access_right('Credit line items') ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Credit line items'), #'action' => "${p}search/cust_bill_pkg.cgi?nottax=1;type=select", 'action' => "${p}edit/credit-cust_bill_pkg.html", 'cust_main' => $cust_main, 'actionlabel' => emt('Credit line items'), 'width' => 968, #763, 'height' => 575, &> % } <% $s ? '
' : '' %> %# refund links % $s = 0; % if ( $payby{'BILL'} && $curuser->access_right(['Post refund', 'Post check refund']) ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Enter check refund'), 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=BILL", 'cust_main' => $cust_main, 'actionlabel' => emt('Enter check refund'), 'width' => 392, &> % } % if ( $payby{'CASH'} && $curuser->access_right(['Post refund', 'Post cash refund']) ) { <% $s++ ? ' | ' : '' %> <& /elements/popup_link-cust_main.html, 'label' => emt('Enter cash refund'), 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=CASH", 'cust_main' => $cust_main, 'actionlabel' => emt('Enter cash refund'), 'width' => 392, &> % } %# someday, perhaps. very few gateways let you do unlinked refunds at all. %# Authorize.net makes you sign a special form %# %# % if ( ( $payby{'CARD'} || $payby{'DCRD'} ) %# % && $curuser->access_right('Process refund') %# % && ! $cust_main->is_encrypted($cust_main->payinfo) %# % ) { %# <% $s++ ? ' | ' : '' %> %# Process credit card refund %# % } %# %# % if ( ( $payby{'CHEK'} || $payby{'DCHK'} ) %# % && $curuser->access_right('Process refund') %# % && ! $cust_main->is_encrypted($cust_main->payinfo) %# % ) { %# <% $s++ ? ' | ' : '' %> %# Process electronic check (ACH) refund %# % } % if ( $payby{'MCRD'} && $curuser->access_right('Post refund') ) { <% $s++ ? ' | ' : '' %> <% mt('Post manual (offline/POS) credit card refund') |h %> % }
%# invoice reports, combined statement % if ( $curuser->access_right('List invoices') ) { % if ( $num_cust_bill > 0 ) { <% mt('Download typeset statement PDF') |h %>
% } <% mt('Invoice reports') |h %> % }
%# XXX payments, credits, refund reports %# tax exemption link % my $view_exemptions = $curuser->access_right('View customer tax exemptions'); % my $add_adjustment = ( $conf->exists('enable_tax_adjustments') % && $curuser->access_right('Add customer tax adjustment') % ); % if ( $view_exemptions || $add_adjustment ) { % if ( $view_exemptions ) { <% mt('View tax exemptions') |h %> <% $add_adjustment ? '|' : '' %> % } % if ( $add_adjustment ) { <& /elements/popup_link.html, { 'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum, 'label' => emt('Add tax adjustment'), 'actionlabel' => emt('Add tax adjustment'), 'height' => 200, } &> | <% mt('View tax adjustments') |h %> % }
% } %# batched payment links % if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') ) % && $curuser->access_right('View customer batched payments') % ) % { <% mt('View batched payments:') |h %> % foreach my $status (qw( Queued In-transit Complete All )) { <% mt($status) |h %> <% $status ne 'All' ? '|' : '' %> % }
% } %# pending payment links % if ( $curuser->access_right('View customer pending payments') % && scalar($cust_main->cust_pay_pending) % ) % { <% mt('View pending payments') |h %>
% }
%# and now the table <& /elements/table-grid.html &> % my $bgcolor1 = '#eeeeee'; % my $bgcolor2 = '#ffffff'; % my $bgcolor = '';
<% mt('Date') |h %> <% mt('Description') |h %> <% mt('Invoice') |h %> <% mt('Payment') |h %> <% mt('In-house Credit') |h %> <% mt('Refund') |h %> <% mt('Balance') |h %>
% unless ( !$target || $target{$target}++ ) { % } <% time2str($date_format, $item->{'date'}) %> % if ( $target && $target{$target} == 1 ) { % } <% $item->{'desc'} %> <% $charge %> <% $payment %> <% $credit %> <% $refund %> <% $showbalance %>
<%def .balance_forward_row> % my( $b, $date ) = @_; % ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/; <% time2str($date_format, $date) %> <% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %> (<% mt('show prior history') |h %>) <% $balance_forward %> <%shared> my $conf = new FS::Conf; my $date_format = $conf->config('date_format') || '%m/%d/%Y'; my $money_char = $conf->config('money_char') || '$'; <%init> my( $cust_main ) = @_; my $custnum = $cust_main->custnum; my $curuser = $FS::CurrentUser::CurrentUser; my @payby = grep /\w/, $conf->config('payby'); #@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP )) @payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP )) unless @payby; my %payby = map { $_=>1 } @payby; my %status = ( 'Queued' => 'O', #Open 'In-transit' => 'I', 'Complete' => 'R', #Resolved 'All' => '', ); #get payment history my @history = (); my %opt = ( ( map { $_ => scalar($conf->config($_)) } qw( card_refund-days date_format ) ), ( map { $_ => $conf->exists($_) } qw( deleteinvoices deletepayments deleterefunds pkg-balances ) ) ); $opt{'date_format'} ||= '%m/%d/%Y'; #legacy invoices foreach my $legacy_cust_bill ($cust_main->legacy_cust_bill) { push @history, { 'date' => $legacy_cust_bill->_date, 'desc' => include('payment_history/legacy_invoice.html', $legacy_cust_bill, %opt ), 'charge_nobal' => $legacy_cust_bill->charged, }; } #invoices my $num_cust_bill = 0; foreach my $cust_bill ($cust_main->cust_bill) { push @history, { 'date' => $cust_bill->_date, 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ), 'charge' => $cust_bill->charged, }; $num_cust_bill++; } #voided invoices foreach my $cust_bill_void ($cust_main->cust_bill_void) { push @history, { 'date' => $cust_bill_void->_date, 'desc' => include('payment_history/voided_invoice.html', $cust_bill_void, %opt ), 'void_charge' => $cust_bill_void->charged, }; } #statements foreach my $cust_statement ($cust_main->cust_statement) { push @history, { 'date' => $cust_statement->_date, 'desc' => include('payment_history/statement.html', $cust_statement, %opt ), #'charge' => $cust_bill->charged, }; } #payments (some false laziness w/credits) foreach my $cust_pay ($cust_main->cust_pay) { push @history, { 'date' => $cust_pay->_date, 'desc' => include('payment_history/payment.html', $cust_pay, %opt ), 'payment' => $cust_pay->paid, #'target' => $target, #XXX }; } #pending payments foreach my $cust_pay_pending ($cust_main->cust_pay_pending) { push @history, { 'date' => $cust_pay_pending->_date, 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ), 'void_payment' => $cust_pay_pending->paid, }; } #voided payments foreach my $cust_pay_void ($cust_main->cust_pay_void) { push @history, { 'date' => $cust_pay_void->_date, 'desc' => include('payment_history/voided_payment.html', $cust_pay_void, %opt ), 'void_payment' => $cust_pay_void->paid, }; } #declined payments foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) { push @history, { 'date' => $cust_pay_pending->_date, 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ), 'void_payment' => $cust_pay_pending->paid, #?? #'target' => $target, #XXX }; } #declined batch payments foreach my $cust_pay_batch ( $cust_main->cust_pay_batch(hashref => {status => 'Declined'}) ) { my $pay_batch = $cust_pay_batch->pay_batch; push @history, { 'date' => $pay_batch->upload, 'desc' => include('payment_history/attempted_batch_payment.html', $cust_pay_batch, %opt), 'void_payment' => $cust_pay_batch->amount, }; } #credits (some false laziness w/payments) foreach my $cust_credit ($cust_main->cust_credit) { push @history, { 'date' => $cust_credit->_date, 'desc' => include('payment_history/credit.html', $cust_credit, %opt ), 'credit' => $cust_credit->amount, }; } #refunds foreach my $cust_refund ($cust_main->cust_refund) { push @history, { 'date' => $cust_refund->_date, 'desc' => include('payment_history/refund.html', $cust_refund, %opt), 'refund' => $cust_refund->refund, }; } # sort in forward order first, and calculate running balances my $years = $conf->config('payment_history-years') || 2; my $older_than = time - $years * 31556926; #60*60*24*365.2422 my $balance = 0; @history = sort { $a->{date} <=> $b->{date} } @history; my $i = 0; my $balance_forward; foreach my $item (@history) { $balance += $item->{'charge'} if exists $item->{'charge'}; $balance -= $item->{'payment'} if exists $item->{'payment'}; $balance -= $item->{'credit'} if exists $item->{'credit'}; $balance += $item->{'refund'} if exists $item->{'refund'}; $balance = sprintf("%.2f", $balance); $balance =~ s/^\-0\.00$/0.00/; $item->{'balance'} = $balance; if ( $item->{'date'} < $older_than ) { $item->{'hide'} = 1; } elsif ( $history[$i-1]->{'hide'} ) { # this is the end of the hidden section $history[$i-1]->{'balance_forward'} = 1; } $i++; } if ( @history and $history[-1]->{'hide'} ) { # then everything is hidden $history[-1]->{'balance_forward'} = 1; } # then sort in user-pref order if ( $curuser->option('history_order') eq 'newest' ) { @history = sort { $b->{date} <=> $a->{date} } @history; } # else it's already oldest-first, and there are no other options yet sub translate_payby { my ($payby,$payinfo) = (shift,shift); my %payby = ( FS::payby->payby2shortname, BILL => $payinfo ? emt('Check #') : '', CHEK => emt('Electronic check '), PREP => emt('Prepaid card '), CARD => emt('Credit card #'), COMP => emt('Complimentary by '), #CASH => emt('Cash'), #WEST => emt('Western Union'), #MCRD => emt('Manual credit card'), ); $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; $payby; }; sub translate_payby_refund { my ($payby,$payinfo) = (shift,shift); my %payby = ( FS::payby->payby2shortname, BILL => $payinfo ? emt('Check #') : emt('Check'), CHEK => emt('Electronic check '), CARD => emt('Credit card #'), COMP => emt('Complimentary by '), ); $payby = (exists $payby{$payby}) ? $payby{$payby} : $payby; $payby; }; sub translate_payinfo { my $object = shift; my $payby = $object->payby; my $payinfo = $object->payinfo; my $conf = new FS::Conf; if ( $payby eq 'CARD' ) { $payinfo = $object->paymask; } elsif ( $payby eq 'CHEK' ) { my( $account, $aba ) = split('@', $object->paymask ); $payinfo = emt("ABA [_1], Acct #[_2]",$aba,$account); } ($payby,$payinfo); } sub areyousure_link { my ($url,$msg,$title,$label) = (shift,shift,shift,shift); ' ('.$label.')'; }