summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2015-07-13 18:33:52 -0500
committerJonathan Prykop <jonathan@freeside.biz>2015-07-16 00:05:16 -0500
commite80770898cc86365b335845dd1a02b4d82bd7e40 (patch)
tree74883bf65d4a26c936b027859e699da4d7f5dd09
parentf28d38924a13370bdc308a37de74b9bdc0aaf195 (diff)
RT#31594: Unapplied payment issues
-rw-r--r--FS/FS/Schema.pm1
-rw-r--r--FS/FS/cust_bill.pm5
-rw-r--r--FS/FS/cust_main/Billing.pm4
-rw-r--r--FS/FS/cust_pay.pm5
-rwxr-xr-xhttemplate/edit/cust_pay.cgi10
-rw-r--r--httemplate/edit/process/cust_pay-no_auto_apply.cgi48
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi1
-rw-r--r--httemplate/misc/batch-cust_pay.html10
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi1
-rw-r--r--httemplate/view/cust_main/payment_history/payment.html103
10 files changed, 128 insertions, 60 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 40c61bbaa..cf5c4c1ca 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1739,6 +1739,7 @@ sub tables_hashref {
'payunique', 'varchar', 'NULL', $char_d, '', '', #separate paybatch "unique" functions from current usage
'closed', 'char', 'NULL', 1, '', '',
'pkgnum', 'int', 'NULL', '', '', '', #desired pkgnum for pkg-balances
+ 'no_auto_apply', 'char', 'NULL', 1, '', '',
# cash/check deposit info fields
'bank', 'varchar', 'NULL', $char_d, '', '',
'depositor', 'varchar', 'NULL', $char_d, '', '',
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index a195e5de8..69fdc91ee 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -913,6 +913,7 @@ sub hide {
=item apply_payments_and_credits [ OPTION => VALUE ... ]
Applies unapplied payments and credits to this invoice.
+Payments with the no_auto_apply flag set will not be applied.
A hash of optional arguments may be passed. Currently "manual" is supported.
If true, a payment receipt is sent instead of a statement when
@@ -939,7 +940,9 @@ sub apply_payments_and_credits {
$self->select_for_update; #mutex
- my @payments = grep { $_->unapplied > 0 } $self->cust_main->cust_pay;
+ my @payments = grep { $_->unapplied > 0 }
+ grep { !$_->no_auto_apply }
+ $self->cust_main->cust_pay;
my @credits = grep { $_->credited > 0 } $self->cust_main->cust_credit;
if ( $conf->exists('pkg-balances') ) {
diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm
index 644467587..908f48697 100644
--- a/FS/FS/cust_main/Billing.pm
+++ b/FS/FS/cust_main/Billing.pm
@@ -2363,6 +2363,7 @@ sub due_cust_event {
=item apply_payments_and_credits [ OPTION => VALUE ... ]
Applies unapplied payments and credits.
+Payments with the no_auto_apply flag set will not be applied.
In most cases, this new method should be used in place of sequential
apply_payments and apply_credits methods.
@@ -2505,6 +2506,7 @@ sub apply_credits {
Applies (see L<FS::cust_bill_pay>) unapplied payments (see L<FS::cust_pay>)
to outstanding invoice balances in chronological order.
+Payments with the no_auto_apply flag set will not be applied.
#and returns the value of any remaining unapplied payments.
@@ -2534,7 +2536,7 @@ sub apply_payments {
#return 0 unless
- my @payments = $self->unapplied_cust_pay;
+ my @payments = grep { !$_->no_auto_apply } $self->unapplied_cust_pay;
my @invoices = $self->open_cust_bill;
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index d2de5f80a..7952e6418 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -117,6 +117,10 @@ books closed flag, empty or `Y'
Desired pkgnum when using experimental package balances.
+=item no_auto_apply
+
+Flag to only allow manual application of payment, empty or 'Y'
+
=item bank
The bank where the payment was deposited.
@@ -558,6 +562,7 @@ sub check {
|| $self->ut_textn('paybatch')
|| $self->ut_textn('payunique')
|| $self->ut_enum('closed', [ '', 'Y' ])
+ || $self->ut_flag('no_auto_apply')
|| $self->ut_foreign_keyn('pkgnum', 'cust_pkg', 'pkgnum')
|| $self->ut_textn('bank')
|| $self->ut_alphan('depositor')
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
index 888335fbb..5d74365e7 100755
--- a/httemplate/edit/cust_pay.cgi
+++ b/httemplate/edit/cust_pay.cgi
@@ -87,15 +87,17 @@
<TD ALIGN="right"><% mt('Auto-apply to invoices') |h %></TD>
<TD COLSPAN=2>
<SELECT NAME="apply">
- <OPTION VALUE="yes" SELECTED><% mt('yes') |h %>
- <OPTION><% mt('no') |h %></SELECT>
- </TD>
+ <OPTION VALUE="yes" SELECTED><% mt('yes') |h %></OPTION>
+ <OPTION VALUE=""><% mt('not now') |h %></OPTION>
+ <OPTION VALUE="never"><% mt('never') |h %></OPTION>
+ </SELECT>
+ </TD>
% } elsif ( $link eq 'invnum' ) {
<TD ALIGN="right"><% mt('Apply to') |h %></TD>
<TD COLSPAN=2 BGCOLOR="#ffffff">Invoice #<B><% $linknum %></B> only</TD>
- <INPUT TYPE="hidden" NAME="apply" VALUE="no">
+ <INPUT TYPE="hidden" NAME="apply" VALUE="">
% }
</TR>
diff --git a/httemplate/edit/process/cust_pay-no_auto_apply.cgi b/httemplate/edit/process/cust_pay-no_auto_apply.cgi
new file mode 100644
index 000000000..ccbd2d7b5
--- /dev/null
+++ b/httemplate/edit/process/cust_pay-no_auto_apply.cgi
@@ -0,0 +1,48 @@
+<%doc>
+Quick process for toggling no_auto_apply field in cust_pay.
+
+Requires paynum and no_auto_apply ('Y' or '') in cgi.
+
+Requires 'Apply payment' acl.
+</%doc>
+
+% if ($error) {
+
+<P STYLE="color: #FF0000"><% emt($error) %></P>
+
+% } else {
+
+<P STYLE="font-weight: bold;"><% emt($message) %></P>
+<P><% emt('Please wait while the page reloads.') %></P>
+<SCRIPT TYPE="text/javascript">
+window.top.location.reload();
+</SCRIPT>
+
+% }
+
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply payment');
+
+my $paynum = $cgi->param('paynum');
+my $noauto = $cgi->param('no_auto_apply');
+
+my $error = '';
+my $message = '';
+my $cust_pay = qsearchs('cust_pay',{ paynum => $paynum });
+if ($cust_pay) {
+ if (($noauto eq 'Y') || (defined($noauto) && (length($noauto) == 0))) {
+ $cust_pay->no_auto_apply($noauto);
+ $error = $cust_pay->replace;
+ $message = $noauto ?
+ q(Payment will not be automatically applied to open invoices, must be applied manually) :
+ q(Payment will be automatically applied to open invoices the next time this customer's payments are processed);
+ } else {
+ $error = 'no_auto_apply not specified';
+ }
+} else {
+ $error .= 'Payment could not be found in database';
+}
+
+
+</%init>
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
index a002fa181..56d3f2ff1 100755
--- a/httemplate/edit/process/cust_pay.cgi
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -50,6 +50,7 @@ else {
my $new = new FS::cust_pay ( {
$field => $linknum,
_date => $_date,
+ no_auto_apply => ($cgi->param('apply') eq 'never') ? 'Y' : '',
map {
$_, scalar($cgi->param($_));
} qw( paid payby payinfo paybatch
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index 3b0ebc112..9f2540cc7 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -461,6 +461,16 @@ push @footer, '';
push @footer_align, '';
push @onchange, 'toggle_application_row';
+push @header, 'No Auto Allocate';
+push @fields, 'no_auto_apply';
+push @types, 'checkbox';
+push @align, 'c';
+push @sizes, '0';
+push @colors, '';
+push @footer, '';
+push @footer_align, '';
+push @onchange, '';
+
#push @header, 'Error';
push @header, '';
push @fields, 'error';
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
index bb4b9733c..ff7886239 100644
--- a/httemplate/misc/process/batch-cust_pay.cgi
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -40,6 +40,7 @@ foreach my $row ( map /^custnum(\d+)$/, keys %$param ) {
'payinfo' => $param->{"payinfo$row"},
'discount_term' => $param->{"discount_term$row"},
'paybatch' => $paybatch,
+ 'no_auto_apply' => exists($param->{"no_auto_apply$row"}) ? 'Y' : '',
}
if $param->{"custnum$row"}
|| $param->{"paid$row"}
diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html
index 4ec9271ef..bf88a6607 100644
--- a/httemplate/view/cust_main/payment_history/payment.html
+++ b/httemplate/view/cust_main/payment_history/payment.html
@@ -9,6 +9,7 @@ my $date_format = $opt{'date_format'} || '%m/%d/%Y';
my @cust_bill_pay = $cust_pay->cust_bill_pay;
my @cust_pay_refund = $cust_pay->cust_pay_refund;
+my $unapplied = $cust_pay->unapplied;
my ($payby,$payinfo) = translate_payinfo($cust_pay);
my $target = "$payby$payinfo";
@@ -50,39 +51,14 @@ if ( scalar(@cust_bill_pay) == 0
$payment = emt("Unapplied Payment by [_1]",$otaker);
$payment =~ s/$otaker/<i>$otaker<\/i>/ if $italicize_otaker;
$payment = '<B><FONT COLOR="#FF0000">'.$payment.'</FONT></B>';
- if ( $opt{'Apply payment'} ) {
- if ( $opt{total_owed} > 0 ) {
- $apply = ' ('.
- include( '/elements/popup_link.html',
- 'label' => emt('apply'),
- 'action' => "${p}edit/cust_bill_pay.cgi?".
- $cust_pay->paynum,
- 'actionlabel' => emt('Apply payment'),
- %cust_bill_pay_width,
- %cust_bill_pay_height,
- ).
- ')';
- }
- if ( $opt{total_unapplied_refunds} > 0 ) {
- $apply.= ' ('.
- include( '/elements/popup_link.html',
- 'label' => emt('apply to refund'),
- 'action' => "${p}edit/cust_pay_refund.cgi?".
- $cust_pay->paynum,
- 'actionlabel' => emt('Apply payment to refund'),
- 'width' => 392,
- ).
- ')';
- }
- }
} elsif ( scalar(@cust_bill_pay) == 1
&& scalar(@cust_pay_refund) == 0
- && $cust_pay->unapplied == 0 ) {
+ && $unapplied == 0 ) {
#applied to one invoice, the usual situation
$desc .= ' '. $cust_bill_pay[0]->applied_to_invoice;
} elsif ( scalar(@cust_bill_pay) == 0
&& scalar(@cust_pay_refund) == 1
- && $cust_pay->unapplied == 0 ) {
+ && $unapplied == 0 ) {
#applied to one refund
$desc .= emt(" refunded on [_1]", time2str($date_format, $cust_pay_refund[0]->_date) );
} else {
@@ -101,40 +77,59 @@ if ( scalar(@cust_bill_pay) == 0
die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
}
}
- if ( $cust_pay->unapplied > 0 ) {
+ if ( $unapplied > 0 ) {
$desc .= '&nbsp;&nbsp;'.
'<B><FONT COLOR="#FF0000">'.
- emt("[_1][_2] unapplied", $opt{money_char}, $cust_pay->unapplied).
+ emt("[_1][_2] unapplied", $opt{money_char}, $unapplied).
'</FONT></B>';
- if ( $opt{'Apply payment'} ) {
- if ( $opt{total_owed} > 0 ) {
- $apply = ' ('.
- include( '/elements/popup_link.html',
- 'label' => emt('apply'),
- 'action' => "${p}edit/cust_bill_pay.cgi?".
- $cust_pay->paynum,
- 'actionlabel' => emt('Apply payment'),
- %cust_bill_pay_width,
- %cust_bill_pay_height,
- ).
- ')';
- }
- if ( $opt{total_unapplied_refunds} > 0 ) {
- $apply.= ' ('.
- include( '/elements/popup_link.html',
- 'label' => emt('apply to refund'),
- 'action' => "${p}edit/cust_pay_refund.cgi?".
- $cust_pay->paynum,
- 'actionlabel' => emt('Apply payment to refund'),
- 'width' => 392,
- ).
- ')';
- }
- }
$desc .= '<BR>';
}
}
+if ($unapplied > 0) {
+ if ( $opt{'Apply payment'} ) {
+ if ( $opt{total_owed} > 0 ) {
+ $apply = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => emt('apply'),
+ 'action' => "${p}edit/cust_bill_pay.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => emt('Apply payment'),
+ %cust_bill_pay_width,
+ %cust_bill_pay_height,
+ ).
+ ')';
+ }
+ if ( $opt{total_unapplied_refunds} > 0 ) {
+ $apply.= ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => emt('apply to refund'),
+ 'action' => "${p}edit/cust_pay_refund.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => emt('Apply payment to refund'),
+ 'width' => 392,
+ ).
+ ')';
+ }
+ $apply .= ' (auto&#8209;apply:&nbsp;'
+ . ($cust_pay->no_auto_apply ? 'no' : 'yes')
+ . '&nbsp;|&nbsp;'
+ . include( '/elements/popup_link.html',
+ 'label' => emt($cust_pay->no_auto_apply ? 'yes' : 'no'),
+ 'action' => "${p}edit/process/cust_pay-no_auto_apply.cgi?paynum="
+ . $cust_pay->paynum
+ . '&no_auto_apply='
+ . ($cust_pay->no_auto_apply ? '' : 'Y'),
+ 'actionlabel' => emt('Toggle Auto-Apply'),
+ 'width' => 392,
+ 'height' => 200,
+ )
+ . ')';
+ } else { # end if $opt('Apply payment')
+ $apply .= ' (no auto-apply)' if $cust_pay->no_auto_apply;
+ }
+} # end if $unapplied > 0
+
my $view =
' ('. include('/elements/popup_link.html',
'label' => emt('view receipt'),