credits, and applies collection events to run cards, send bills and notices,
etc.
-By default, warns on errors and continues with the next operation (but see the
+Any errors prevent subsequent operations from continuing and die (but see the
"fatal" flag below).
Options are passed as name-value pairs. Currently available options are:
if ( $error ) {
$error = "Error expiring custnum ". $self->custnum. ": $error";
if ( $options{fatal} && $options{fatal} eq 'return' ) { return $error; }
- elsif ( $options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
$log->debug('suspending adjourned packages', %logopt);
if ( $error ) {
$error = "Error adjourning custnum ". $self->custnum. ": $error";
if ( $options{fatal} && $options{fatal} eq 'return' ) { return $error; }
- elsif ( $options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
$log->debug('unsuspending resumed packages', %logopt);
if ( $error ) {
$error = "Error resuming custnum ".$self->custnum. ": $error";
if ( $options{fatal} && $options{fatal} eq 'return' ) { return $error; }
- elsif ( $options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
$job->update_statustext('20,billing packages') if $job;
if ( $error ) {
$error = "Error billing custnum ". $self->custnum. ": $error";
if ( $options{fatal} && $options{fatal} eq 'return' ) { return $error; }
- elsif ( $options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
$job->update_statustext('50,applying payments and credits') if $job;
if ( $error ) {
$error = "Error applying custnum ". $self->custnum. ": $error";
if ( $options{fatal} && $options{fatal} eq 'return' ) { return $error; }
- elsif ( $options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
unless ( $conf->exists('cancelled_cust-noevents')
if ( $error ) {
$error = "Error collecting custnum ". $self->custnum. ": $error";
if ($options{fatal} && $options{fatal} eq 'return') { return $error; }
- elsif ($options{fatal} ) { die $error; }
- else { warn $error; }
+ else { die $error; }
}
}
my @errors = ();
- my @really_cancel_pkgs;
- my @cancel_reasons;
+ my @really_cancel_pkgs = ();
+ my @cancel_reasons = ();
CUST_PKG: foreach my $cust_pkg ( @cancel_pkgs ) {
my $cpr = $cust_pkg->last_cust_pkg_reason('expire');
- my $error;
if ( $cust_pkg->change_to_pkgnum ) {
$cust_pkg->change_to_pkgnum.'; not expiring';
next CUST_PKG;
}
- $error = $cust_pkg->change( 'cust_pkg' => $new_pkg,
- 'unprotect_svcs' => 1 );
- $error = '' if ref $error eq 'FS::cust_pkg';
+ my $error = $cust_pkg->change( 'cust_pkg' => $new_pkg,
+ 'unprotect_svcs' => 1,
+ );
+ push @errors, $error if $error && ref($error) ne 'FS::cust_pkg';
} else { # just cancel it
foreach my $part_pkg ( @part_pkg ) {
- $cust_pkg->set($_, $hash{$_}) foreach qw ( setup last_bill bill );
+ my $this_cust_pkg = $cust_pkg;
+ # for add-on packages, copy the object to avoid leaking changes back to
+ # the caller if pkg_list is in use; see RT#73607
+ if ( $part_pkg->get('pkgpart') != $real_pkgpart ) {
+ $this_cust_pkg = FS::cust_pkg->new({ %hash });
+ }
my $pass = '';
- if ( $cust_pkg->separate_bill ) {
+ if ( $this_cust_pkg->separate_bill ) {
# if no_auto is also set, that's fine. we just need to not have
# invoices that are both auto and no_auto, and since the package
# gets an invoice all to itself, it will only be one or the other.
- $pass = $cust_pkg->pkgnum;
+ $pass = $this_cust_pkg->pkgnum;
if (!exists $cust_bill_pkg{$pass}) { # it may not exist yet
push @passes, $pass;
$total_setup{$pass} = do { my $z = 0; \$z };
$taxlisthash{$pass} = {};
$cust_bill_pkg{$pass} = [];
}
- } elsif ( ($cust_pkg->no_auto || $part_pkg->no_auto) ) {
+ } elsif ( ($this_cust_pkg->no_auto || $part_pkg->no_auto) ) {
$pass = 'no_auto';
}
- my $next_bill = $cust_pkg->getfield('bill') || 0;
+ my $next_bill = $this_cust_pkg->getfield('bill') || 0;
my $error;
# let this run once if this is the last bill upon cancellation
while ( $next_bill <= $cmp_time or $options{cancel} ) {
$error =
$self->_make_lines( 'part_pkg' => $part_pkg,
- 'cust_pkg' => $cust_pkg,
+ 'cust_pkg' => $this_cust_pkg,
'precommit_hooks' => \@precommit_hooks,
'line_items' => $cust_bill_pkg{$pass},
'setup' => $total_setup{$pass},
last if $error;
# or if we're not incrementing the bill date.
- last if ($cust_pkg->getfield('bill') || 0) == $next_bill;
+ last if ($this_cust_pkg->getfield('bill') || 0) == $next_bill;
# or if we're letting it run only once
last if $options{cancel};
- $next_bill = $cust_pkg->getfield('bill') || 0;
+ $next_bill = $this_cust_pkg->getfield('bill') || 0;
#stop if -o was passed to freeside-daily
last if $options{'one_recur'};
}
}
+ $lineitems++
+ if $cust_pkg->waive_setup && $part_pkg->can('prorate_setup') && $part_pkg->prorate_setup($cust_pkg, $time);
+
$cust_pkg->setfield('setup', $time)
unless $cust_pkg->setup;
#do need it, but it won't get written to the db
# We fetch taxes even if the customer is completely exempt,
# because we need to record that fact.
- my @loc_keys = qw( district city county state country );
- my %taxhash = map { $_ => $location->$_ } @loc_keys;
+ my %taxhash = map { $_ => $location->get($_) }
+ qw( district county state country );
+ # city names in cust_main_county are uppercase
+ $taxhash{'city'} = uc($location->get('city'));
$taxhash{'taxclass'} = $part_item->taxclass;