fix cancellation error "No schema for table table found", seems to be fallout from...
[freeside.git] / FS / FS / cust_main.pm
index de39874..7104882 100644 (file)
@@ -53,6 +53,7 @@ use FS::cust_tax_location;
 use FS::part_pkg_taxrate;
 use FS::agent;
 use FS::cust_main_invoice;
+use FS::cust_tag;
 use FS::cust_credit_bill;
 use FS::cust_bill_pay;
 use FS::prepay_credit;
@@ -471,6 +472,30 @@ sub insert {
     $self->invoicing_list( $invoicing_list );
   }
 
+  warn "  setting customer tags\n"
+    if $DEBUG > 1;
+
+  foreach my $tagnum ( @{ $self->tagnum || [] } ) {
+    my $cust_tag = new FS::cust_tag { 'tagnum'  => $tagnum,
+                                      'custnum' => $self->custnum };
+    my $error = $cust_tag->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  if ( $invoicing_list ) {
+    $error = $self->check_invoicing_list( $invoicing_list );
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      #return "checking invoicing_list (transaction rolled back): $error";
+      return $error;
+    }
+    $self->invoicing_list( $invoicing_list );
+  }
+
+
   warn "  setting cust_main_exemption\n"
     if $DEBUG > 1;
 
@@ -1315,23 +1340,13 @@ sub delete {
     }
   }
 
-  foreach my $cust_main_invoice ( #(email invoice destinations, not invoices)
-    qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } )
-  ) {
-    my $error = $cust_main_invoice->delete;
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-  }
-
-  foreach my $cust_main_exemption (
-    qsearch( 'cust_main_exemption', { 'custnum' => $self->custnum } )
-  ) {
-    my $error = $cust_main_exemption->delete;
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
+  foreach my $table (qw( cust_main_invoice cust_main_exemption cust_tag )) {
+    foreach my $record ( qsearch( $table, { 'custnum' => $self->custnum } ) ) {
+      my $error = $record->delete;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
     }
   }
 
@@ -1420,6 +1435,28 @@ sub replace {
     $self->invoicing_list( $invoicing_list );
   }
 
+  if ( $self->exists('tagnum') ) { #so we don't delete these on edit by accident
+
+    #this could be more efficient than deleting and re-inserting, if it matters
+    foreach my $cust_tag (qsearch('cust_tag', {'custnum'=>$self->custnum} )) {
+      my $error = $cust_tag->delete;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+    }
+    foreach my $tagnum ( @{ $self->tagnum || [] } ) {
+      my $cust_tag = new FS::cust_tag { 'tagnum'  => $tagnum,
+                                        'custnum' => $self->custnum };
+      my $error = $cust_tag->insert;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+    }
+
+  }
+
   my %options = @param;
 
   my $tax_exemption = delete $options{'tax_exemption'};
@@ -2104,6 +2141,9 @@ sub sort_packages {
     return 1  if !$a_num_cust_svc &&  $b_num_cust_svc;
     my @a_cust_svc = $a->cust_svc;
     my @b_cust_svc = $b->cust_svc;
+    return 0  if !scalar(@a_cust_svc) && !scalar(@b_cust_svc);
+    return -1 if  scalar(@a_cust_svc) && !scalar(@b_cust_svc);
+    return 1  if !scalar(@a_cust_svc) &&  scalar(@b_cust_svc);
     $a_cust_svc[0]->svc_x->label cmp $b_cust_svc[0]->svc_x->label;
   }
 
@@ -2382,6 +2422,41 @@ sub agent {
   qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
 }
 
+=item agent_name
+
+Returns the agent name (see L<FS::agent>) for this customer.
+
+=cut
+
+sub agent_name {
+  my $self = shift;
+  $self->agent->agent;
+}
+
+=item cust_tag
+
+Returns any tags associated with this customer, as FS::cust_tag objects,
+or an empty list if there are no tags.
+
+=cut
+
+sub cust_tag {
+  my $self = shift;
+  qsearch('cust_tag', { 'custnum' => $self->custnum } );
+}
+
+=item part_tag
+
+Returns any tags associated with this customer, as FS::part_tag objects,
+or an empty list if there are no tags.
+
+=cut
+
+sub part_tag {
+  my $self = shift;
+  map $_->part_tag, $self->cust_tag; 
+}
+
 =item bill_and_collect 
 
 Cancels and suspends any packages due, generates bills, applies payments and
@@ -3492,19 +3567,17 @@ sub collect {
     }
   }
 
-  my $error = $self->do_cust_event(
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  #never want to roll back an event just because it returned an error
+  local $FS::UID::AutoCommit = 1; #$oldAutoCommit;
+
+  $self->do_cust_event(
     'debug'      => ( $options{'debug'} || 0 ),
     'time'       => $invoice_time,
     'check_freq' => $options{'check_freq'},
     'stage'      => 'collect',
   );
-  if ( $error ) {
-    $dbh->rollback if $oldAutoCommit;
-    return $error;
-  }
-
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-  '';
 
 }
 
@@ -3599,6 +3672,11 @@ sub do_cust_event {
     return $due_cust_event;
   }
 
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  #never want to roll back an event just because it or a different one
+  # returned an error
+  local $FS::UID::AutoCommit = 1; #$oldAutoCommit;
+
   foreach my $cust_event ( @$due_cust_event ) {
 
     #XXX lock event
@@ -3607,11 +3685,7 @@ sub do_cust_event {
     unless ( $cust_event->test_conditions( 'time' => $time ) ) {
       #don't leave stray "new/locked" records around
       my $error = $cust_event->delete;
-      if ( $error ) {
-        #gah, even with transactions
-        $dbh->commit if $oldAutoCommit; #well.
-        return $error;
-      }
+      return $error if $error;
       next;
     }
 
@@ -3620,20 +3694,16 @@ sub do_cust_event {
       warn "  running cust_event ". $cust_event->eventnum. "\n"
         if $DEBUG > 1;
 
-      
       #if ( my $error = $cust_event->do_event(%options) ) { #XXX %options?
       if ( my $error = $cust_event->do_event() ) {
         #XXX wtf is this?  figure out a proper dealio with return value
         #from do_event
-         # gah, even with transactions.
-         $dbh->commit if $oldAutoCommit; #well.
-         return $error;
-       }
+        return $error;
+      }
     }
 
   }
 
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 
 }
@@ -4547,28 +4617,42 @@ sub realtime_bop {
          && ! grep { $transaction->error_message =~ /$_/ }
                    $conf->config('emaildecline-exclude')
     ) {
-      my @templ = $conf->config('declinetemplate');
-      my $template = new Text::Template (
-        TYPE   => 'ARRAY',
-        SOURCE => [ map "$_\n", @templ ],
-      ) or return "($perror) can't create template: $Text::Template::ERROR";
-      $template->compile()
-        or return "($perror) can't compile template: $Text::Template::ERROR";
 
-      my $templ_hash = {
-        'company_name'    =>
-          scalar( $conf->config('company_name', $self->agentnum ) ),
-        'company_address' =>
-          join("\n", $conf->config('company_address', $self->agentnum ) ),
-        'error'           => $transaction->error_message,
-      };
+      # Send a decline alert to the customer.
+      my $msgnum = $conf->config('decline_msgnum', $self->agentnum);
+      my $error = '';
+      if ( $msgnum ) {
+        # include the raw error message in the transaction state
+        $cust_pay_pending->setfield('error', $transaction->error_message);
+        my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
+        $error = $msg_template->send( 'cust_main' => $self,
+                                      'object'    => $cust_pay_pending );
+      }
+      else { #!$msgnum
+
+        my @templ = $conf->config('declinetemplate');
+        my $template = new Text::Template (
+          TYPE   => 'ARRAY',
+          SOURCE => [ map "$_\n", @templ ],
+        ) or return "($perror) can't create template: $Text::Template::ERROR";
+        $template->compile()
+          or return "($perror) can't compile template: $Text::Template::ERROR";
+
+        my $templ_hash = {
+          'company_name'    =>
+            scalar( $conf->config('company_name', $self->agentnum ) ),
+          'company_address' =>
+            join("\n", $conf->config('company_address', $self->agentnum ) ),
+          'error'           => $transaction->error_message,
+        };
 
-      my $error = send_email(
-        'from'    => $conf->config('invoice_from', $self->agentnum ),
-        'to'      => [ grep { $_ ne 'POST' } $self->invoicing_list ],
-        'subject' => 'Your payment could not be processed',
-        'body'    => [ $template->fill_in(HASH => $templ_hash) ],
-      );
+        my $error = send_email(
+          'from'    => $conf->config('invoice_from', $self->agentnum ),
+          'to'      => [ grep { $_ ne 'POST' } $self->invoicing_list ],
+          'subject' => 'Your payment could not be processed',
+          'body'    => [ $template->fill_in(HASH => $templ_hash) ],
+        );
+      }
 
       $perror .= " (also received error sending decline notification: $error)"
         if $error;
@@ -6915,7 +6999,7 @@ sub balance_date_range {
   my $self = shift;
   my $sql = 'SELECT SUM('. $self->balance_date_sql(@_).
             ') FROM cust_main WHERE custnum='. $self->custnum;
-  sprintf( "%.2f", $self->scalar_sql($sql) );
+  sprintf( '%.2f', $self->scalar_sql($sql) );
 }
 
 =item balance_pkgnum PKGNUM
@@ -8195,6 +8279,7 @@ Returns an SQL expression identifying un-cancelled cust_main records.
 
 sub uncancelled_sql { uncancel_sql(@_); }
 sub uncancel_sql { "
+
   ( 0 < ( $select_count_pkgs
                    AND ( cust_pkg.cancel IS NULL
                          OR cust_pkg.cancel = 0
@@ -9326,6 +9411,9 @@ sub batch_charge {
 
 =item notify CUSTOMER_OBJECT TEMPLATE_NAME OPTIONS
 
+Deprecated.  Use event notification and message templates 
+(L<FS::msg_template>) instead.
+
 Sends a templated email notification to the customer (see L<Text::Template>).
 
 OPTIONS is a hash and may include