diff options
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/cust_main.pm | 25 | ||||
-rw-r--r-- | FS/FS/cust_pkg.pm | 15 | ||||
-rw-r--r-- | FS/FS/cust_pkg_reason.pm | 48 |
3 files changed, 85 insertions, 3 deletions
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 1b8e0330a..e4fc3db34 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -75,6 +75,7 @@ use FS::cust_attachment; use FS::contact; use FS::Locales; use FS::upgrade_journal; +use FS::reason; # 1 is mostly method/subroutine entry and options # 2 traces progress of some operations @@ -2396,7 +2397,11 @@ FS::cust_pkg::cancel() methods. =item quiet - can be set true to supress email cancellation notices. -=item reason - can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason. +=item reason - can be set to a cancellation reason (see L<FS:reason>), either a +reasonnum of an existing reason, or passing a hashref will create a new reason. +The hashref should have the following keys: +typenum - Reason type (see L<FS::reason_type>) +reason - Text of the new reason. =item cust_pkg_reason - can be an arrayref of L<FS::cust_pkg_reason> objects for the individual packages, parallel to the C<cust_pkg> argument. The @@ -2486,12 +2491,26 @@ sub cancel_pkgs { if ($opt{'cust_pkg_reason'}) { @cprs = @{ delete $opt{'cust_pkg_reason'} }; } + my $null_reason; foreach (@pkgs) { my %lopt = %opt; if (@cprs) { my $cpr = shift @cprs; - $lopt{'reason'} = $cpr->reasonnum; - $lopt{'reason_otaker'} = $cpr->otaker; + if ( $cpr ) { + $lopt{'reason'} = $cpr->reasonnum; + $lopt{'reason_otaker'} = $cpr->otaker; + } else { + warn "no reason found when canceling package ".$_->pkgnum."\n"; + # we're not actually required to pass a reason to cust_pkg::cancel, + # but if we're getting to this point, something has gone awry. + $null_reason ||= FS::reason->new_or_existing( + reason => 'unknown reason', + type => 'Cancel Reason', + class => 'C', + ); + $lopt{'reason'} = $null_reason->reasonnum; + $lopt{'reason_otaker'} = $FS::CurrentUser::CurrentUser->username; + } } my $error = $_->cancel(%lopt); push @errors, 'pkgnum '.$_->pkgnum.': '.$error if $error; diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 2275c5980..b5777027b 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -2490,6 +2490,21 @@ sub change { return "transferring package notes: $error"; } } + + # transfer scheduled expire/adjourn reasons + foreach my $action ('expire', 'adjourn') { + if ( $cust_pkg->get($action) ) { + my $reason = $self->last_cust_pkg_reason($action); + if ( $reason ) { + $reason->set('pkgnum', $cust_pkg->pkgnum); + $error = $reason->replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "transferring $action reason: $error"; + } + } + } + } my @new_supp_pkgs; diff --git a/FS/FS/cust_pkg_reason.pm b/FS/FS/cust_pkg_reason.pm index c29a2f928..98b6b812d 100644 --- a/FS/FS/cust_pkg_reason.pm +++ b/FS/FS/cust_pkg_reason.pm @@ -216,6 +216,54 @@ sub _upgrade_data { # class method FS::upgrade_journal->set_done('cust_pkg_reason__missing_reason'); } + # Fix misplaced expire/suspend reasons due to package change (RT#71623). + # These will look like: + # - there is an expire reason linked to pkg1 + # - pkg1 has been canceled before the reason's date + # - pkg2 was changed from pkg1, has an expire date equal to the reason's + # date, and has no expire reason (check this later) + + my $error; + foreach my $action ('expire', 'adjourn') { + # Iterate this, because a package could be scheduled to expire, then + # changed several times, and we need to walk the reason forward to the + # last one. + while(1) { + my @reasons = qsearch( + { + select => 'cust_pkg_reason.*', + table => 'cust_pkg_reason', + addl_from => ' JOIN cust_pkg pkg1 USING (pkgnum) + JOIN cust_pkg pkg2 ON (pkg1.pkgnum = pkg2.change_pkgnum)', + hashref => { 'action' => uc(substr($action, 0, 1)) }, + extra_sql => " AND pkg1.cancel IS NOT NULL + AND cust_pkg_reason.date > pkg1.cancel + AND pkg2.$action = cust_pkg_reason.date" + }); + last if !@reasons; + warn "Checking ".scalar(@reasons)." possible misplaced $action reasons.\n"; + foreach my $cust_pkg_reason (@reasons) { + my $new_pkg = qsearchs('cust_pkg', { change_pkgnum => $cust_pkg_reason->pkgnum }); + my $new_reason = $new_pkg->last_cust_pkg_reason($action); + if ($new_reason and $new_reason->_date == $new_pkg->get($action)) { + # the expiration reason has been recreated on the new package, so + # just delete the old one + warn "Cleaning $action reason from canceled pkg#" . + $cust_pkg_reason->pkgnum . "\n"; + $error = $cust_pkg_reason->delete; + } else { + # then the old reason needs to be transferred + warn "Moving $action reason from canceled pkg#" . + $cust_pkg_reason->pkgnum . + " to new pkg#" . $new_pkg->pkgnum ."\n"; + $cust_pkg_reason->set('pkgnum' => $new_pkg->pkgnum); + $error = $cust_pkg_reason->replace; + } + die $error if $error; + } + } + } + #still can't fill in an action? don't abort the upgrade local($ignore_empty_action) = 1; |