release held packages when automatically unsuspending, RT#83847
[freeside.git] / FS / FS / cust_main_Mixin.pm
index 94e6eaa..95a5342 100644 (file)
@@ -210,19 +210,9 @@ a customer.
 sub cust_status {
   my $self = shift;
   return $self->cust_unlinked_msg unless $self->cust_linked;
-
-  #FS::cust_main::status($self)
-  #false laziness w/actual cust_main::status
-  # (make sure FS::cust_main methods are called)
-  for my $status (qw( prospect active inactive suspended cancelled )) {
-    my $method = $status.'_sql';
-    my $sql = FS::cust_main->$method();;
-    my $numnum = ( $sql =~ s/cust_main\.custnum/?/g );
-    my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
-    $sth->execute( ($self->custnum) x $numnum )
-      or die "Error executing 'SELECT $sql': ". $sth->errstr;
-    return $status if $sth->fetchrow_arrayref->[0];
-  }
+  my $cust_main = $self->cust_main;
+  return $self->cust_unlinked_msg unless $cust_main;
+  return $cust_main->cust_status;
 }
 
 =item ucfirst_cust_status
@@ -258,6 +248,17 @@ sub cust_statuscolor {
     : '000000';
 }
 
+=item agent_name
+
+=cut
+
+sub agent_name {
+  my $self = shift;
+  $self->cust_linked
+    ? $self->cust_main->agent_name
+    : $self->cust_unlinked_msg;
+}
+
 =item prospect_sql
 
 =item active_sql
@@ -357,7 +358,7 @@ Queue job for status updates.  Required.
 
 =item search
 
-Hashref of params to the L<search()> method.  Required.
+Hashref of params to the L<FS::Record/search> method.  Required.
 
 =item msgnum
 
@@ -404,14 +405,21 @@ use Digest::SHA qw(sha1); # for duplicate checking
 sub email_search_result {
   my($class, $param) = @_;
 
+  my $conf = FS::Conf->new;
+  my $send_to_domain = $conf->config('email-to-voice_domain');
+
   my $msgnum = $param->{msgnum};
   my $from = delete $param->{from};
   my $subject = delete $param->{subject};
   my $html_body = delete $param->{html_body};
   my $text_body = delete $param->{text_body};
   my $to_contact_classnum = delete $param->{to_contact_classnum};
+  my $emailtovoice_name = delete $param->{emailtovoice_contact};
+
   my $error = '';
 
+  my $to = $emailtovoice_name . '@' . $send_to_domain unless !$emailtovoice_name;
+
   my $job = delete $param->{'job'}
     or die "email_search_result must run from the job queue.\n";
   
@@ -457,26 +465,28 @@ sub email_search_result {
       next; # unlinked object; nothing else we can do
     }
 
+    my %to = ( to => $to ) if $to;
+
     if ( $msg_template ) {
       # Now supports other context objects.
       %message = $msg_template->prepare(
         'cust_main' => $cust_main,
         'object'    => $obj,
         'to_contact_classnum' => $to_contact_classnum,
+        %to
       );
 
     } else {
       # 3.x: false laziness with msg_template.pm; on 4.x, all email notices
       # are generated from templates and this case goes away
       my @classes;
-      if ( $opt{'to_contact_classnum'} ) {
-        my $classnum = $opt{'to_contact_classnum'};
-        @classes = ref($classnum) ? @$classnum : split(',', $classnum);
+      if ( $to_contact_classnum ) {
+        @classes = ref($to_contact_classnum) ? @$to_contact_classnum : split(',', $to_contact_classnum);
       }
       if (!@classes) {
         @classes = ( 'invoice' );
       }
-      my @to = $cust_main->contact_list_email(@classes);
+      my @to = $to ? split(',', $to) : $cust_main->contact_list_email(@classes);
       next if !@to;
 
       %message = (
@@ -490,7 +500,7 @@ sub email_search_result {
     } #if $msg_template
 
     # For non-cust_main searches, we avoid duplicates based on message
-    # body text.  
+    # body text.
     my $unique = $cust_main->custnum;
     $unique .= sha1($message{'text_body'}) if $class ne 'FS::cust_main';
     if( $sent_to{$unique} ) {
@@ -567,6 +577,102 @@ sub process_email_search_result {
 
 }
 
+sub customer_agent_transfer_search_result {
+  my($class, $param) = @_;
+
+  my $newagentnum = $param->{agentnum};
+  my $error = '';
+  my @customers;
+
+  my $job = delete $param->{'job'}
+    or die "customer_agent_transfer_search_result must run from the job queue.\n";
+
+  my $list = $param->{'list'};
+
+  if ($param->{'search'}) {
+    my $sql_query = $class->search($param->{'search'});
+    $sql_query->{'select'} = $sql_query->{'table'} . '.*';
+    @customers = qsearch($sql_query);
+  }
+
+  @customers = @$list if !@customers && $list;
+  my $num_cust = scalar(@customers);
+
+  my( $num, $last, $min_sec ) = (0, time, 5); #progresbar
+
+  # Transactionize
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  foreach my $obj ( @customers ) {
+
+    #progressbar first, so that the count is right
+    $num++;
+    if ( time - $min_sec > $last ) {
+      my $error = $job->update_statustext(
+        int( 100 * $num / $num_cust )
+      );
+      die $error if $error;
+      $last = time;
+    }
+
+    my $cust_main = $obj->cust_main;
+    if ( !$cust_main ) {
+      next; # unlinked object nothing to do
+    }
+
+    $cust_main->agentnum($newagentnum);
+    $error = $cust_main->replace;
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "transfering to new agent: $error";
+    }
+
+  } # foreach $obj
+
+  $dbh->commit if $oldAutoCommit;
+  return '';
+}
+
+=item process_customer_agent_transfer_search_result
+
+Mass transfers customers to new agent.
+
+Is Transactionized so entire list transfers or none.
+
+excepts either a list of cust_main objects in the base64 encoded cgi param list
+or a list of search fields in the base64 encoded  cgi param search.
+
+=cut
+
+sub process_customer_agent_transfer_search_result {
+  my $job = shift;
+
+  my $param = thaw(decode_base64(shift));
+  warn Dumper($param) if $DEBUG;
+
+  $param->{'job'} = $job;
+
+  $param->{'search'} = thaw(decode_base64($param->{'search'}))
+    or die "process_customer_agent_transfer_search_result.\n" if $param->{'search'};
+
+  $param->{'list'} = thaw(decode_base64($param->{'list'}))
+    or die "process_customer_agent_transfer_search_result.\n" if $param->{'list'};;
+
+  my $table = $param->{'table'}
+    or die "process_customer_agent_transfer_search_result.\n";
+
+  eval "use FS::$table;";
+  die "error loading FS::$table: $@\n" if $@;
+
+  my $error = "FS::$table"->customer_agent_transfer_search_result( $param );
+
+  die $error if $error;
+
+}
+
 =item conf
 
 Returns a configuration handle (L<FS::Conf>) set to the customer's locale, 
@@ -705,7 +811,11 @@ sub unsuspend_balance {
   }
   my $balance = $cust_main->balance || 0;
   if ($balance <= $maxbalance) {
-    my @errors = $cust_main->unsuspend;
+    my @errors = $cust_main->unsuspend(
+                  'reason_type' => $conf->config('unsuspend_reason_type')
+                );
+
+    push @errors, $cust_main->release_hold if $conf->exists('unsuspend-unhold');
     # side-fx with nested transactions?  upstack rolls back?
     warn "WARNING:Errors unsuspending customer ". $cust_main->custnum. ": ".
          join(' / ', @errors)