my $dbh = dbh;
my $cust_main = qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+ unless ( $cust_main ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown custnum ". $self->custnum;
+ }
my $old_balance = $cust_main->balance;
if (!$self->reasonnum) {
if ($recur) {
$recur -= $cust_bill_pkg->credited('', '', setuprecur => 'recur') || 0;
}
+ # Skip line items that have been completely credited.
+ next if ($setup + $recur) == 0;
my $setup_ratio = $setup / ($setup + $recur);
- # Calculate the fraction of tax to credit: it's the fraction of this charge
- # (either setup or recur) that's being credited.
+ # Calculate the fraction of tax to credit: it's the fraction of this
+ # charge (either setup or recur) that's being credited.
my $charged = ($setuprecur eq 'setup') ? $setup : $recur;
next if $charged == 0; # shouldn't happen, but still...
'setuprecurs' => \@setuprecurs,
'amounts' => \@amounts,
'apply' => 1, #0 leaves the credit unapplied
+ 'set_source' => 1, #creates credit source records for the line items
#the credit
map { $_ => scalar($cgi->param($_)) }
$arg{amount} = sprintf('%.2f', $tax_adjust{subtotal} + $tax_adjust{taxtotal});
}
+ my $set_source = $arg{set_source};
+
# create the credit
my $cust_credit = new FS::cust_credit ( {
map { $_ => $arg{$_} }
my %cust_bill_pkg = ();
my %cust_credit_bill_pkg = ();
my %unapplied_payments = (); #invoice numbers, and then billpaynums
+ my %currency;
+
+ # little private function to unapply payments from a cust_bill_pkg until
+ # there's a specified amount of unpaid balance on it.
+ # it's a separate sub because we do it for both tax and nontax items. it's
+ # private because it needs access to some local data structures.
+ my $unapply_sub = sub {
+ my ($cust_bill_pkg, $setuprecur, $need_to_unapply) = @_;
+
+ my $invnum = $cust_bill_pkg->invnum;
+
+ $need_to_unapply -= $cust_bill_pkg->owed($setuprecur);
+ return if $need_to_unapply < 0.005;
+
+ my $error;
+ # then unapply payments one at a time (partially if need be) until the
+ # unpaid balance = the credit amount.
+ foreach my $cust_bill_pay_pkg (
+ $cust_bill_pkg->cust_bill_pay_pkg($setuprecur)
+ ) {
+ my $this_amount = $cust_bill_pay_pkg->amount;
+ if ( $this_amount > $need_to_unapply ) {
+ # unapply the needed amount
+ $cust_bill_pay_pkg->set('amount',
+ sprintf('%.2f', $this_amount - $need_to_unapply));
+ $error = $cust_bill_pay_pkg->replace;
+ $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum} += $need_to_unapply;
+ last; # and we're done
+
+ } else {
+ # unapply it all
+ $error = $cust_bill_pay_pkg->delete;
+ $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum} += $this_amount;
+
+ $need_to_unapply -= $this_amount;
+ }
+
+ } # foreach $cust_bill_pay_pkg
+
+ # return an error if we somehow still have leftover $need_to_unapply?
+
+ return $error;
+ };
+
foreach my $billpkgnum ( @{$arg{billpkgnums}} ) {
my $setuprecur = shift @{$arg{setuprecurs}};
'sdate' => $cust_bill_pkg->sdate,
'edate' => $cust_bill_pkg->edate,
};
- # unapply payments (but not other credits) from this line item
- foreach my $cust_bill_pay_pkg (
- $cust_bill_pkg->cust_bill_pay_pkg($setuprecur)
- ) {
- $error = $cust_bill_pay_pkg->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "Error unapplying payment: $error";
- }
- $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum}
- += $cust_bill_pay_pkg->amount;
+
+ # unapply payments if necessary
+ $error = &{$unapply_sub}($cust_bill_pkg, $setuprecur, $amount);
+
+ if ( $set_source ) {
+ $currency{$invnum} ||= $cust_bill_pkg->cust_bill->currency;
+ my $source = FS::cust_credit_source_bill_pkg->new({
+ 'crednum' => $cust_credit->crednum,
+ 'billpkgnum' => $billpkgnum,
+ 'amount' => $amount,
+ 'currency' => $currency{invnum},
+ });
+ $error ||= $source->insert;
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error unapplying payment: $error";
}
}
'setuprecur' => 'setup',
$tax_link->primary_key, $tax_credit->{num}
};
- # unapply any payments from the tax
- foreach my $cust_bill_pay_pkg (
- $cust_bill_pkg->cust_bill_pay_pkg('setup')
- ) {
- $error = $cust_bill_pay_pkg->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "Error unapplying payment: $error";
- }
- $unapplied_payments{$invnum}{$cust_bill_pay_pkg->billpaynum}
- += $cust_bill_pay_pkg->amount;
+
+ $error = &{$unapply_sub}($cust_bill_pkg, 'setup', $amount);
+
+ # I guess it's correct to do this for taxes also?
+ if ( $set_source ) {
+ $currency{$invnum} ||= $cust_bill_pkg->cust_bill->currency;
+ my $source = FS::cust_credit_source_bill_pkg->new({
+ 'crednum' => $cust_credit->crednum,
+ 'billpkgnum' => $billpkgnum,
+ 'amount' => $amount,
+ 'currency' => $currency{invnum},
+ });
+ $error ||= $source->insert;
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error unapplying payment: $error";
}
}