'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, '', '',
=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
$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') ) {
=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.
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.
#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;
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.
|| $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')
<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>
--- /dev/null
+<%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>
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
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';
'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"}
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";
$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 {
die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
}
}
- if ( $cust_pay->unapplied > 0 ) {
+ if ( $unapplied > 0 ) {
$desc .= ' '.
'<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‑apply: '
+ . ($cust_pay->no_auto_apply ? 'no' : 'yes')
+ . ' | '
+ . 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'),