remove ticket links to deleted services, #17067
[freeside.git] / FS / FS / cust_svc.pm
index 0a58d55..6bd8cb8 100644 (file)
@@ -98,6 +98,28 @@ Deletes this service from the database.  If there is an error, returns the
 error, otherwise returns false.  Note that this only removes the cust_svc
 record - you should probably use the B<cancel> method instead.
 
+=cut
+
+sub delete {
+  my $self = shift;
+  my $error = $self->SUPER::delete;
+  return $error if $error;
+
+  if ( FS::Conf->new->config('ticket_system') eq 'RT_Internal' ) {
+    FS::TicketSystem->init;
+    my $session = FS::TicketSystem->session;
+    my $links = RT::Links->new($session->{CurrentUser});
+    my $svcnum = $self->svcnum;
+    $links->Limit(FIELD => 'Target', 
+                  VALUE => 'freeside://freeside/cust_svc/'.$svcnum);
+    while ( my $l = $links->Next ) {
+      my ($val, $msg) = $l->Delete;
+      # can't do anything useful on error
+      warn "error unlinking ticket $svcnum: $msg\n" if !$val;
+    }
+  }
+}
+
 =item cancel
 
 Cancels the relevant service by calling the B<cancel> method of the associated
@@ -307,22 +329,13 @@ sub check {
   if ( $self->pkgnum ) {
     my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
     return "Unknown pkgnum" unless $cust_pkg;
-    my $pkg_svc = qsearchs( 'pkg_svc', {
-      'pkgpart' => $cust_pkg->pkgpart,
-      'svcpart' => $self->svcpart,
-    });
-    # or new FS::pkg_svc ( { 'pkgpart'  => $cust_pkg->pkgpart,
-    #                        'svcpart'  => $self->svcpart,
-    #                        'quantity' => 0                   } );
-    my $quantity = $pkg_svc ? $pkg_svc->quantity : 0;
-
-    my @cust_svc = qsearch('cust_svc', {
-      'pkgnum'  => $self->pkgnum,
-      'svcpart' => $self->svcpart,
-    });
-    return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
+    ($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.
            " services for pkgnum ". $self->pkgnum
-      if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
+      if $part_svc->get('num_avail') == 0 and !$ignore_quantity;
   }
 
   $self->SUPER::check;
@@ -382,6 +395,20 @@ sub date_inserted {
   $self->h_date('insert');
 }
 
+=item pkg_cancel_date
+
+Returns the date this service's package was canceled.  This normally only 
+exists for a service that's been preserved through cancellation with the 
+part_pkg.preserve flag.
+
+=cut
+
+sub pkg_cancel_date {
+  my $self = shift;
+  my $cust_pkg = $self->cust_pkg or return;
+  return $cust_pkg->getfield('cancel') || '';
+}
+
 =item label
 
 Returns a list consisting of:
@@ -486,18 +513,20 @@ where B<svcdb> is not "svc_acct".
 
 =cut
 
-#note: implementation here, POD in FS::svc_acct
-sub seconds_since {
-  my($self, $since) = @_;
-  my $dbh = dbh;
-  my $sth = $dbh->prepare(' SELECT SUM(logout-login) FROM session
-                              WHERE svcnum = ?
-                                AND login >= ?
-                                AND logout IS NOT NULL'
-  ) or die $dbh->errstr;
-  $sth->execute($self->svcnum, $since) or die $sth->errstr;
-  $sth->fetchrow_arrayref->[0];
-}
+#internal session db deprecated (or at least on hold)
+sub seconds_since { 'internal session db deprecated'; };
+##note: implementation here, POD in FS::svc_acct
+#sub seconds_since {
+#  my($self, $since) = @_;
+#  my $dbh = dbh;
+#  my $sth = $dbh->prepare(' SELECT SUM(logout-login) FROM session
+#                              WHERE svcnum = ?
+#                                AND login >= ?
+#                                AND logout IS NOT NULL'
+#  ) or die $dbh->errstr;
+#  $sth->execute($self->svcnum, $since) or die $sth->errstr;
+#  $sth->fetchrow_arrayref->[0];
+#}
 
 =item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END
 
@@ -749,8 +778,94 @@ sub get_session_history {
 
 }
 
+=item tickets
+
+Returns an array of hashes representing the tickets linked to this service.
+
+=cut
+
+sub tickets {
+  my $self = shift;
+
+  my $conf = FS::Conf->new;
+  my $num = $conf->config('cust_main-max_tickets') || 10;
+  my @tickets = ();
+
+  if ( $conf->config('ticket_system') ) {
+    unless ( $conf->config('ticket_system-custom_priority_field') ) {
+
+      @tickets = @{ FS::TicketSystem->service_tickets($self->svcnum, $num) };
+
+    } else {
+
+      foreach my $priority (
+        $conf->config('ticket_system-custom_priority_field-values'), ''
+      ) {
+        last if scalar(@tickets) >= $num;
+        push @tickets,
+        @{ FS::TicketSystem->service_tickets( $self->svcnum,
+            $num - scalar(@tickets),
+            $priority,
+          )
+        };
+      }
+    }
+  }
+  (@tickets);
+}
+
+
 =back
 
+=head1 SUBROUTINES
+
+=over 4
+
+=item smart_search OPTION => VALUE ...
+
+Accepts the option I<search>, the string to search for.  The string will 
+be searched for as a username, email address, IP address, MAC address, 
+phone number, and hardware serial number.  Unlike the I<smart_search> on 
+customers, this always requires an exact match.
+
+=cut
+
+# though perhaps it should be fuzzy in some cases?
+sub smart_search {
+  my %opt = @_;
+  # some false laziness w/ search/cust_svc.html
+  my $string = $opt{'search'};
+  $string =~ s/(^\s+|\s+$)//; #trim leading & trailing whitespace
+
+  my @extra_sql = ' ( '. join(' OR ',
+    map { my $table = $_;
+      my $search_sql = "FS::$table"->search_sql($string);
+      " ( svcdb = '$table'
+      AND 0 < ( SELECT COUNT(*) FROM $table
+      WHERE $table.svcnum = cust_svc.svcnum
+      AND $search_sql
+      )
+      ) ";
+    }
+    FS::part_svc->svc_tables
+  ). ' ) ';
+  push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+    'null_right' => 'View/link unlinked services'
+  );
+  my $extra_sql = ' WHERE '.join(' AND ', @extra_sql);
+  #for agentnum
+  my $addl_from = ' LEFT JOIN cust_pkg  USING ( pkgnum  )'.
+                  ' LEFT JOIN cust_main USING ( custnum )'.
+                  ' LEFT JOIN part_svc  USING ( svcpart )';
+
+  qsearch({
+      'table'     => 'cust_svc',
+      'addl_from' => $addl_from,
+      'hashref'   => {},
+      'extra_sql' => $extra_sql,
+  });
+}
+
 =head1 BUGS
 
 Behaviour of changing the svcpart of cust_svc records is undefined and should