RT#17599: display cancelled services from history
authorJonathan Prykop <jonathan@freeside.biz>
Tue, 21 Jun 2016 06:11:43 +0000 (01:11 -0500)
committerJonathan Prykop <jonathan@freeside.biz>
Tue, 21 Jun 2016 06:11:43 +0000 (01:11 -0500)
FS/FS/cust_pkg.pm
httemplate/misc/cancel_pkg.html
httemplate/misc/process/cancel_pkg.html
httemplate/view/cust_main/packages/services.html

index d15eb89..99ca2b6 100644 (file)
@@ -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</uncancel>.  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</uncancel>, 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
index 81c5c48..c802897 100755 (executable)
   } &>
 
   <& /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) {
+<TR><TD COLSPAN="2">&nbsp;</TD></TR>
+<TR><TH ALIGN="right"><% emt("Re-provision the following services") %></TH><TD></TD>
+%     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' ) {
index 6972a49..eb3b2ef 100755 (executable)
@@ -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) {
index 39055f4..352f5ea 100644 (file)
@@ -105,9 +105,22 @@ function clearhint_search_cust_svc(obj, str) {
         </TD>
       </TR>
 
-%   } 
+%   }
+
+% } #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'};
+      <TR>
+        <TD ALIGN="right" VALIGN="top"><% $svc->{'svc'} |h %></TD>
+        <TD STYLE="padding-bottom:0px; font-style: italic"><% $svc->{'label'} |h %></TD>
+      </TR>
+%   }
+% }
 
-% } 
 
     </TABLE>
   </TD>