no need for these indices
[freeside.git] / FS / FS / cust_pkg.pm
index f01c5c3..586abcb 100644 (file)
@@ -10,7 +10,7 @@ use List::Util qw(max);
 use Tie::IxHash;
 use Time::Local qw( timelocal timelocal_nocheck );
 use MIME::Entity;
-use FS::UID qw( getotaker dbh );
+use FS::UID qw( getotaker dbh driver_name );
 use FS::Misc qw( send_email );
 use FS::Record qw( qsearch qsearchs fields );
 use FS::CurrentUser;
@@ -889,7 +889,9 @@ sub cancel_if_expired {
 locationnum, (other fields?).  Attempts to re-provision cancelled services
 using history information (errors at this stage are not fatal).
 
-cust_pkg: pass a scalar reference, will be filled in with
+cust_pkg: pass a scalar reference, will be filled in with the new cust_pkg object
+
+svc_fatal: service provisioning errors are fatal
 
 svc_errors: pass an array reference, will be filled in with any provisioning errors
 
@@ -972,12 +974,25 @@ sub uncancel {
     }
 
     my $svc_error = $svc_x->insert;
-    if ( $svc_error ) { #&& $options{svc_fatal} ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-    push @svc_errors, $svc_error if $svc_error;
-  }
+    if ( $svc_error ) {
+      if ( $options{svc_fatal} ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $svc_error;
+      } else {
+        push @svc_errors, $svc_error;
+        # is this necessary? svc_Common::insert already deletes the 
+        # cust_svc if inserting svc_x fails.
+        my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_x->svcnum });
+        if ( $cust_svc ) {
+          my $cs_error = $cust_svc->delete;
+          if ( $cs_error ) {
+            $dbh->rollback if $oldAutoCommit;
+            return $cs_error;
+          }
+        }
+      } # svc_fatal
+    } # svc_error
+  } #foreach $h_cust_svc
 
   #these are pretty rare, but should handle them
   # - dsl_device (mac addresses)
@@ -1165,8 +1180,13 @@ sub suspend {
     $hash{'resume'} = $resume_date;
   }
 
+  $options{options} ||= {};
+
   my $new = new FS::cust_pkg ( \%hash );
-  $error = $new->replace( $self, options => { $self->options } );
+  $error = $new->replace( $self, options => { $self->options,
+                                              %{ $options{options} },
+                                            }
+                        );
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
@@ -1236,7 +1256,8 @@ sub suspend {
 
 Unsuspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
 package, then unsuspends the package itself (clears the susp field and the
-adjourn field if it is in the past).
+adjourn field if it is in the past).  If the suspend reason includes an 
+unsuspension package, that package will be ordered.
 
 Available options are:
 
@@ -1340,6 +1361,8 @@ sub unsuspend {
 
   }
 
+  my $reason = $self->last_cust_pkg_reason('susp')->reason;
+
   my %hash = $self->hash;
   my $inactive = time - $hash{'susp'};
 
@@ -1366,6 +1389,33 @@ sub unsuspend {
     return $error;
   }
 
+  my $unsusp_pkg;
+
+  if ( $reason->unsuspend_pkgpart ) {
+    my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart)
+      or $error = "Unsuspend package definition ".$reason->unsuspend_pkgpart.
+                  " not found.";
+    my $start_date = $self->cust_main->next_bill_date 
+      if $reason->unsuspend_hold;
+
+    if ( $part_pkg ) {
+      $unsusp_pkg = FS::cust_pkg->new({
+          'custnum'     => $self->custnum,
+          'pkgpart'     => $reason->unsuspend_pkgpart,
+          'start_date'  => $start_date,
+          'locationnum' => $self->locationnum,
+          # discount? probably not...
+      });
+      
+      $error ||= $self->cust_main->order_pkg( 'cust_pkg' => $unsusp_pkg );
+    }
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
   if ( $conf->config('unsuspend_email_admin') ) {
  
     my $error = send_email(
@@ -1379,6 +1429,11 @@ sub unsuspend {
         'Customer: #'. $self->custnum. ' '. $self->cust_main->name. "\n",
         'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n",
         ( map { "Service : $_\n" } @labels ),
+        ($unsusp_pkg ?
+          "An unsuspension fee was charged: ".
+            $unsusp_pkg->part_pkg->pkg_comment."\n"
+          : ''
+        ),
       ],
     );
 
@@ -1989,7 +2044,7 @@ sub cust_svc {
   }
   if ( $opt{'svcdb'} ) {
     $search{addl_from} = ' LEFT JOIN part_svc USING ( svcpart ) ';
-    $search{hashref}->{svcdb} = $opt{'svcdb'};
+    $search{extra_sql} = ' AND svcdb = '. dbh->quote( $opt{'svcdb'} );
   }
 
   cluck "cust_pkg->cust_svc called" if $DEBUG > 2;
@@ -2581,7 +2636,7 @@ sub seconds_since_sqlradacct {
     grep {
       my $part_svc = $_->part_svc;
       $part_svc->svcdb eq 'svc_acct'
-        && scalar($part_svc->part_export('sqlradius'));
+        && scalar($part_svc->part_export_usage);
     } $self->cust_svc
   ) {
     $seconds += $cust_svc->seconds_since_sqlradacct($start, $end);
@@ -2613,7 +2668,7 @@ sub attribute_since_sqlradacct {
     grep {
       my $part_svc = $_->part_svc;
       $part_svc->svcdb eq 'svc_acct'
-        && scalar($part_svc->part_export('sqlradius'));
+        && scalar($part_svc->part_export_usage);
     } $self->cust_svc
   ) {
     $sum += $cust_svc->attribute_since_sqlradacct($start, $end, $attrib);
@@ -3471,20 +3526,34 @@ sub search {
                   'LEFT JOIN part_pkg  USING ( pkgpart  ) '.
                   'LEFT JOIN pkg_class ON ( part_pkg.classnum = pkg_class.classnum ) ';
 
-  my $count_query = "SELECT COUNT(*) FROM cust_pkg $addl_from $extra_sql";
+  my $select;
+  my $count_query;
+  if ( $params->{'select_zip5'} ) {
+    my $zip = 'COALESCE(cust_location.zip, cust_main.ship_zip, cust_main.zip)';
+
+    $select = "DISTINCT substr($zip,1,5) as zip";
+    $orderby = "ORDER BY substr($zip,1,5)";
+    $addl_from .= 'LEFT JOIN cust_location ON ( locationnum )';
+    $count_query = "SELECT COUNT( DISTINCT substr($zip,1,5) )";
+  } else {
+    $select = join(', ',
+                         'cust_pkg.*',
+                         ( map "part_pkg.$_", qw( pkg freq ) ),
+                         'pkg_class.classname',
+                         'cust_main.custnum AS cust_main_custnum',
+                         FS::UI::Web::cust_sql_fields(
+                           $params->{'cust_fields'}
+                         ),
+                  );
+    $count_query = 'SELECT COUNT(*)';
+  }
+
+  $count_query .= " FROM cust_pkg $addl_from $extra_sql";
 
   my $sql_query = {
     'table'       => 'cust_pkg',
     'hashref'     => {},
-    'select'      => join(', ',
-                                'cust_pkg.*',
-                                ( map "part_pkg.$_", qw( pkg freq ) ),
-                                'pkg_class.classname',
-                                'cust_main.custnum AS cust_main_custnum',
-                                FS::UI::Web::cust_sql_fields(
-                                  $params->{'cust_fields'}
-                                ),
-                     ),
+    'select'      => $select,
     'extra_sql'   => $extra_sql,
     'order_by'    => $orderby,
     'addl_from'   => $addl_from,
@@ -3608,10 +3677,12 @@ sub _location_sql_where {
   my $or_empty_county   = " OR ( ? = '' AND $table.${prefix}county   IS NULL )";
   my $or_empty_state    = " OR ( ? = '' AND $table.${prefix}state    IS NULL )";
 
+  my $text = (driver_name =~ /^mysql/i) ? 'char' : 'text';
+
 #        ( $table.${prefix}city    = ? $or_empty_city   $ornull )
   "
-        ( $table.${prefix}district = ? OR ? = '' OR CAST(? AS text) IS NULL )
-    AND ( $table.${prefix}city     = ? OR ? = '' OR CAST(? AS text) IS NULL )
+        ( $table.district = ? OR ? = '' OR CAST(? AS $text) IS NULL )
+    AND ( $table.${prefix}city     = ? OR ? = '' OR CAST(? AS $text) IS NULL )
     AND ( $table.${prefix}county   = ? $or_empty_county $ornull )
     AND ( $table.${prefix}state    = ? $or_empty_state  $ornull )
     AND   $table.${prefix}country  = ?