From 9aa198dfc90054de34cf1af8f3238d004416ebc9 Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Tue, 21 Jun 2016 01:11:43 -0500 Subject: [PATCH] RT#17599: display cancelled services from history --- FS/FS/cust_pkg.pm | 175 +++++++++++++++++------ httemplate/misc/cancel_pkg.html | 28 +++- httemplate/misc/process/cancel_pkg.html | 3 + httemplate/view/cust_main/packages/services.html | 17 ++- 4 files changed, 180 insertions(+), 43 deletions(-) diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index d15eb89ac..99ca2b663 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -1129,6 +1129,135 @@ sub cancel_if_expired { ''; } +=item uncancel_svc_x + +For cancelled cust_pkg, returns a list of new, uninserted FS::svc_X records +for services that would be inserted by L. Returned objects also +include the field '_uncancel_svcnum' that contains the original svcnum. +Set pkgnum before inserting. + +Accepts the following options: + +summarize_size - if true, returns empty list if number of potential services is +equal to or greater than this + +only_svcnum - arrayref of svcnum, only returns objects for these svcnum +(and only if they would otherwise be returned by this) + +=cut + +sub uncancel_svc_x { + my ($self, %opt) = @_; + + die 'uncancel_svc_x called on a non-cancelled cust_pkg' unless $self->get('cancel'); + + #find historical services within this timeframe before the package cancel + # (incompatible with "time" option to cust_pkg->cancel?) + my $fuzz = 2 * 60; #2 minutes? too much? (might catch separate unprovision) + # too little? (unprovisioing export delay?) + my($end, $start) = ( $self->get('cancel'), $self->get('cancel') - $fuzz ); + my @h_cust_svc = $self->h_cust_svc( $end, $start ); + + return () if $opt{'summarize_size'} and @h_cust_svc >= $opt{'summarize_size'}; + + my @svc_x; + foreach my $h_cust_svc (@h_cust_svc) { + next if $opt{'only_svcnum'} && !(grep { $_ == $h_cust_svc->svcnum } @{$opt{'only_svcnum'}}); + my $h_svc_x = $h_cust_svc->h_svc_x( $end, $start ); + #next unless $h_svc_x; #should this happen? + (my $table = $h_svc_x->table) =~ s/^h_//; + require "FS/$table.pm"; + my $class = "FS::$table"; + my $svc_x = $class->new( { + 'svcpart' => $h_cust_svc->svcpart, + '_uncancel_svcnum' => $h_cust_svc->svcnum, + map { $_ => $h_svc_x->get($_) } fields($table) + } ); + + # radius_usergroup + if ( $h_svc_x->isa('FS::h_svc_Radius_Mixin') ) { + $svc_x->usergroup( [ $h_svc_x->h_usergroup($end, $start) ] ); + } + + #these are pretty rare, but should handle them + # - dsl_device (mac addresses) + # - phone_device (mac addresses) + # - dsl_note (ikano notes) + # - domain_record (i.e. restore DNS information w/domains) + # - inventory_item(?) (inventory w/un-cancelling service?) + # - nas (svc_broaband nas stuff) + #this stuff is unused in the wild afaik + # - mailinglistmember + # - router.svcnum? + # - svc_domain.parent_svcnum? + # - acct_snarf (ancient mail fetching config) + # - cgp_rule (communigate) + # - cust_svc_option (used by our Tron stuff) + # - acct_rt_transaction (used by our time worked stuff) + + push @svc_x, $svc_x; + } + return @svc_x; +} + +=item uncancel_svc_summary + +Returns an array of hashrefs, one for each service that could be +successfully reprovisioned by L, with the following keys: + +svcpart + +svc + +uncancel_svcnum + +label + +reprovisionable - 1 if test reprovision succeeded, otherwise 0 + +Cannot be run from within a transaction. Performs inserts +to test the results, and then rolls back the transaction. +Does not perform exports, so does not catch if export would fail. + +Also accepts the following options: + +summarize_size - if true, returns empty list if number of potential services is +equal to or greater than this + +=cut + +sub uncancel_svc_summary { + my ($self, %opt) = @_; + + die 'uncancel_svc_summary called on a non-cancelled cust_pkg' unless $self->get('cancel'); + die 'uncancel_svc_summary called from within a transaction' unless $FS::UID::AutoCommit; + + local $FS::svc_Common::noexport_hack = 1; # very important not to run exports!!! + local $FS::UID::AutoCommit = 0; + + my @out; + foreach my $svc_x ($self->uncancel_svc_x(%opt)) { + $svc_x->pkgnum($self->pkgnum); # provisioning services on a canceled package, will be rolled back + my $part_svc = $svc_x->part_svc; + my $out = { + 'svcpart' => $part_svc->svcpart, + 'svc' => $part_svc->svc, + 'uncancel_svcnum' => $svc_x->get('_uncancel_svcnum'), + }; + if ($svc_x->insert) { # if error inserting + $out->{'label'} = "(cannot re-provision)"; + $out->{'reprovisionable'} = 0; + } else { + $out->{'label'} = $svc_x->label; + $out->{'reprovisionable'} = 1; + } + push @out, $out; + } + + dbh->rollback; + return @out; +} + =item uncancel "Un-cancels" this package: Orders a new package with the same custnum, pkgpart, @@ -1141,6 +1270,8 @@ svc_fatal: service provisioning errors are fatal svc_errors: pass an array reference, will be filled in with any provisioning errors +only_svcnum: arrayref, only attempt to re-provision these cancelled services + main_pkgnum: link the package as a supplemental package of this one. For internal use only. @@ -1197,32 +1328,12 @@ sub uncancel { # insert services ## - #find historical services within this timeframe before the package cancel - # (incompatible with "time" option to cust_pkg->cancel?) - my $fuzz = 2 * 60; #2 minutes? too much? (might catch separate unprovision) - # too little? (unprovisioing export delay?) - my($end, $start) = ( $self->get('cancel'), $self->get('cancel') - $fuzz ); - my @h_cust_svc = $self->h_cust_svc( $end, $start ); - my @svc_errors; - foreach my $h_cust_svc (@h_cust_svc) { - my $h_svc_x = $h_cust_svc->h_svc_x( $end, $start ); - #next unless $h_svc_x; #should this happen? - (my $table = $h_svc_x->table) =~ s/^h_//; - require "FS/$table.pm"; - my $class = "FS::$table"; - my $svc_x = $class->new( { - 'pkgnum' => $cust_pkg->pkgnum, - 'svcpart' => $h_cust_svc->svcpart, - map { $_ => $h_svc_x->get($_) } fields($table) - } ); - - # radius_usergroup - if ( $h_svc_x->isa('FS::h_svc_Radius_Mixin') ) { - $svc_x->usergroup( [ $h_svc_x->h_usergroup($end, $start) ] ); - } + foreach my $svc_x ($self->uncancel_svc_x('only_svcnum' => $options{'only_svcnum'})) { + $svc_x->pkgnum($cust_pkg->pkgnum); my $svc_error = $svc_x->insert; + if ( $svc_error ) { if ( $options{svc_fatal} ) { $dbh->rollback if $oldAutoCommit; @@ -1246,23 +1357,7 @@ sub uncancel { } } # svc_fatal } # svc_error - } #foreach $h_cust_svc - - #these are pretty rare, but should handle them - # - dsl_device (mac addresses) - # - phone_device (mac addresses) - # - dsl_note (ikano notes) - # - domain_record (i.e. restore DNS information w/domains) - # - inventory_item(?) (inventory w/un-cancelling service?) - # - nas (svc_broaband nas stuff) - #this stuff is unused in the wild afaik - # - mailinglistmember - # - router.svcnum? - # - svc_domain.parent_svcnum? - # - acct_snarf (ancient mail fetching config) - # - cgp_rule (communigate) - # - cust_svc_option (used by our Tron stuff) - # - acct_rt_transaction (used by our time worked stuff) + } #foreach uncancel_svc_x ## # also move over any services that didn't unprovision at cancellation diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html index 81c5c4861..c802897bb 100755 --- a/httemplate/misc/cancel_pkg.html +++ b/httemplate/misc/cancel_pkg.html @@ -45,12 +45,38 @@ } &> <& /elements/tr-checkbox.html, - 'label' => mt("Uncancel even if a service can't be re-provisioned"), + 'label' => mt("Uncancel even if service reprovisioning fails"), 'field' => 'svc_not_fatal', 'value' => 'Y', &> % $date_init = 1; +% my @uncancel_svcs = sort { $b->{'reprovisionable'} <=> $a->{'reprovisionable'} } +% sort { $a->{'svcpart'} <=> $b->{'svcpart'} } +% $cust_pkg->uncancel_svc_summary(); +% if (@uncancel_svcs) { +  +<% emt("Re-provision the following services") %> +% foreach my $uncancel_svc (@uncancel_svcs) { +% my $uncancel_curr_value = $uncancel_svc->{'uncancel_svcnum'}; +% my $uncancel_disabled = ''; +% if ($cgi->param('error')) { +% $uncancel_curr_value = '' unless grep { $_ == $uncancel_svc->{'_uncancel_svcnum'} } $cgi->param('only_svcnum'); +% } +% unless ($uncancel_svc->{'reprovisionable'}) { +% $uncancel_curr_value = ''; +% $uncancel_disabled = 1; +% } + <& /elements/tr-checkbox.html, + 'label' => $uncancel_svc->{'svc'} . ': ' . $uncancel_svc->{'label'}, + 'field' => 'only_svcnum', + 'value' => $uncancel_svc->{'uncancel_svcnum'}, + 'curr_value' => $uncancel_curr_value, + 'disabled' => $uncancel_disabled, + 'cell_style' => 'font-weight: normal;' + &> +% } +% } % } % unless ( $method eq 'resume' || $method eq 'uncancel' ) { diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html index 6972a49bf..eb3b2efe6 100755 --- a/httemplate/misc/process/cancel_pkg.html +++ b/httemplate/misc/process/cancel_pkg.html @@ -85,6 +85,8 @@ my $bill = my $svc_fatal = ( $cgi->param('svc_not_fatal') ne 'Y' ); +my $only_svcnum = ($method eq 'uncancel') ? [ $cgi->param('only_svcnum') ] : undef; + $error ||= $cust_pkg->$method( 'reason' => $reasonnum, 'date' => $date, 'resume_date' => $resume_date, @@ -92,6 +94,7 @@ $error ||= $cust_pkg->$method( 'reason' => $reasonnum, 'bill' => $bill, 'svc_fatal' => $svc_fatal, 'options' => $options, + 'only_svcnum' => $only_svcnum, ); if ($error) { diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html index 39055f49a..352f5ea9d 100644 --- a/httemplate/view/cust_main/packages/services.html +++ b/httemplate/view/cust_main/packages/services.html @@ -105,9 +105,22 @@ function clearhint_search_cust_svc(obj, str) { -% } +% } + +% } #foreach part_svc + +% if ($cust_pkg->get('cancel')) { +% foreach my $svc (sort { $a->{'svcpart'} <=> $b->{'svcpart'} } +% $cust_pkg->uncancel_svc_summary('summarize_size' => $opt{'cust_pkg-large_pkg_size'}) +% ) { +% next unless $svc->{'reprovisionable'}; + + <% $svc->{'svc'} |h %> + <% $svc->{'label'} |h %> + +% } +% } -% } -- 2.11.0