fix perf edge case with multiple large packages, on svc insert, RT#26097
[freeside.git] / FS / FS / cust_svc.pm
index dacf41d..6dee274 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use vars qw( @ISA $DEBUG $me $ignore_quantity );
 use Carp;
 #use Scalar::Util qw( blessed );
+use List::Util qw( max );
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh str2time_sql );
 use FS::cust_pkg;
@@ -272,6 +273,17 @@ sub replace {
 #    }
 #  }
 
+  #trigger a pkg_change export on pkgnum changes
+  if ( $new->pkgnum != $old->pkgnum ) {
+    my $error = $new->svc_x->export('pkg_change', $new->cust_pkg,
+                                                  $old->cust_pkg,
+                                   );
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error if $error;
+    }
+  }
+
   #my $error = $new->SUPER::replace($old, @_);
   my $error = $new->SUPER::replace($old);
   if ( $error ) {
@@ -307,21 +319,44 @@ sub check {
   my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
   return "Unknown svcpart" unless $part_svc;
 
-  if ( $self->pkgnum ) {
-    my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
-    return "Unknown pkgnum" unless $cust_pkg;
-    ($part_svc) = grep { $_->svcpart == $self->svcpart } $cust_pkg->part_svc;
-    return "No svcpart ". $self->svcpart.
-           " services in pkgpart ". $cust_pkg->pkgpart
-      unless $part_svc;
-    return "Already ". $part_svc->get('num_cust_svc'). " ". $part_svc->svc.
+  if ( $self->pkgnum && ! $ignore_quantity ) {
+
+    #slightly inefficient since ->pkg_svc will also look it up, but fixing
+    # a much larger perf problem and have bigger fish to fry
+    my $cust_pkg = $self->cust_pkg;
+
+    my $pkg_svc = $self->pkg_svc
+      or return "No svcpart ". $self->svcpart.
+                " services in pkgpart ". $cust_pkg->pkgpart;
+
+    my $num_cust_svc = $cust_pkg->num_cust_svc( $self->svcpart );
+
+    #false laziness w/cust_pkg->part_svc
+    my $num_avail = max( 0, ($cust_pkg->quantity || 1) * $pkg_svc->quantity
+                            - $num_cust_svc
+                       );
+
+    return "Already $num_cust_svc ". $pkg_svc->part_svc->svc.
            " services for pkgnum ". $self->pkgnum
-      if $part_svc->get('num_avail') == 0 and !$ignore_quantity;
+      if $num_avail <= 0;
+
   }
 
   $self->SUPER::check;
 }
 
+=item display_svcnum 
+
+Returns the displayed service number for this service: agent_svcid if it has a
+value, svcnum otherwise
+
+=cut
+
+sub display_svcnum {
+  my $self = shift;
+  $self->agent_svcid || $self->svcnum;
+}
+
 =item part_svc
 
 Returns the definition for this service, as a FS::part_svc object (see
@@ -427,12 +462,9 @@ sub svc_label_long { shift->_svc_label('label_long', @_); }
 sub _svc_label {
   my( $self, $method, $svc_x ) = ( shift, shift, shift );
 
-  my $identifier = $svc_x->$method(@_);
-  $identifier = '['.$self->agent_svcid.']'. $identifier if $self->agent_svcid;
-
   (
     $self->part_svc->svc,
-    $identifier,
+    $svc_x->$method(@_),
     $self->part_svc->svcdb,
     $self->svcnum
   );