tower tracking, RT#15538
[freeside.git] / FS / FS / cust_main / Search.pm
index 3813f89..db752ad 100644 (file)
@@ -18,7 +18,7 @@ use FS::svc_acct;
 $DEBUG = 0;
 $me = '[FS::cust_main::Search]';
 
-@fuzzyfields = @FS::cust_main::fuzzyfields;
+@fuzzyfields = ( 'first', 'last', 'company', 'address1' );
 
 install_callback FS::UID sub { 
   $conf = new FS::Conf;
@@ -112,8 +112,17 @@ sub smart_search {
   # custnum search (also try agent_custid), with some tweaking options if your
   # legacy cust "numbers" have letters
   } 
-
-  if ( $search =~ /^\s*(\d+)\s*$/
+  
+  
+  if ( $search =~ /@/ ) {
+      push @cust_main,
+         map $_->cust_main,
+             qsearch( {
+                        'table'     => 'cust_main_invoice',
+                        'hashref'   => { 'dest' => $search },
+                      }
+                    );
+  } elsif ( $search =~ /^\s*(\d+)\s*$/
          || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+'
               && $search =~ /^\s*(\w\w?\d+)\s*$/
             )
@@ -133,6 +142,19 @@ sub smart_search {
       } );
     }
 
+    #if this becomes agent-virt need to get a list of all prefixes the current
+    #user can see (via their agents)
+    my $prefix = $conf->config('cust_main-custnum-display_prefix');
+    if ( $prefix && $prefix eq substr($num, 0, length($prefix)) ) {
+      push @cust_main, qsearch( {
+        'table'     => 'cust_main',
+        'hashref'   => { 'custnum' => 0 + substr($num, length($prefix)),
+                         %options,
+                       },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualization
+      } );
+    }
+
     push @cust_main, qsearch( {
       'table'     => 'cust_main',
       'hashref'   => { 'agent_custid' => $num, %options },
@@ -203,7 +225,7 @@ sub smart_search {
     } elsif ( ! $NameParse->parse($value) ) {
 
       my %name = $NameParse->components;
-      $first = $name{'given_name_1'};
+      $first = $name{'given_name_1'} || $name{'initials_1'}; #wtf NameParse, Ed?
       $last  = $name{'surname_1'};
 
     }
@@ -422,6 +444,8 @@ HASHREF.  Valid parameters are
 
 =item status
 
+=item address
+
 =item cancelled_pkgs
 
 bool
@@ -460,6 +484,33 @@ sub search {
   my @where = ();
   my $orderby;
 
+  # initialize these to prevent warnings
+  $params = {
+    'custnum'       => '',
+    'agentnum'      => '',
+    'usernum'       => '',
+    'status'        => '',
+    'address'       => '',
+    'paydate_year'  => '',
+    'invoice_terms' => '',
+    'custbatch'     => '',
+    %$params
+  };
+
+  ##
+  # explicit custnum(s)
+  ##
+
+  if ( $params->{'custnum'} ) {
+    my @custnums = ref($params->{'custnum'}) ? 
+                      @{ $params->{'custnum'} } : 
+                      $params->{'custnum'};
+    push @where, 
+      'cust_main.custnum IN (' . 
+      join(',', map { $_ =~ /^(\d+)$/ ? $1 : () } @custnums ) .
+      ')' if scalar(@custnums) > 0;
+  }
+
   ##
   # parse agent
   ##
@@ -488,7 +539,19 @@ sub search {
     #push @where, $class->$method();
     push @where, FS::cust_main->$method();
   }
-  
+
+  ##
+  # address
+  ##
+  if ( $params->{'address'} =~ /\S/ ) {
+    my $address = dbh->quote('%'. lc($params->{'address'}). '%');
+    push @where, '('. join(' OR ',
+                             map "LOWER($_) LIKE $address",
+                               qw(address1 address2 ship_address1 ship_address2)
+                          ).
+                 ')';
+  }
+
   ##
   # parse cancelled package checkbox
   ##
@@ -506,6 +569,13 @@ sub search {
     if $params->{'no_censustract'};
 
   ##
+  # parse with hardcoded tax location checkbox
+  ##
+
+  push @where, "geocode is not null"
+    if $params->{'with_geocode'};
+
+  ##
   # dates
   ##
 
@@ -520,11 +590,13 @@ sub search {
       "cust_main.$field >= $beginning",
       "cust_main.$field <= $ending";
 
-    # XXX: do this for mysql and/or pull it out of here
     if(defined $hour) {
-      if ($dbh->{Driver}->{Name} eq 'Pg') {
+      if ($dbh->{Driver}->{Name} =~ /Pg/i) {
         push @where, "extract(hour from to_timestamp(cust_main.$field)) = $hour";
       }
+      elsif( $dbh->{Driver}->{Name} =~ /mysql/i) {
+        push @where, "hour(from_unixtime(cust_main.$field)) = $hour"
+      }
       else {
         warn "search by time of day not supported on ".$dbh->{Driver}->{Name}." databases";
       }
@@ -635,6 +707,21 @@ sub search {
     push @where,
       "cust_main.custbatch = '$1'";
   }
+  
+  if ( $params->{'tagnum'} ) {
+    my @tagnums = ref( $params->{'tagnum'} ) ? @{ $params->{'tagnum'} } : ( $params->{'tagnum'} );
+
+    @tagnums = grep /^(\d+)$/, @tagnums;
+
+    if ( @tagnums ) {
+       my $tags_where = "0 < (select count(1) from cust_tag where " 
+               . " cust_tag.custnum = cust_main.custnum and tagnum in ("
+               . join(',', @tagnums) . "))";
+
+       push @where, $tags_where;
+    }
+  }
+
 
   ##
   # setup queries, subs, etc. for the search
@@ -651,7 +738,7 @@ sub search {
 
   my $count_query = "SELECT COUNT(*) FROM cust_main $extra_sql";
 
-  my $select = join(', ', 
+  my @select = (
                  'cust_main.custnum',
                  FS::UI::Web::cust_sql_fields($params->{'cust_fields'}),
                );
@@ -663,10 +750,10 @@ sub search {
 
     if ($dbh->{Driver}->{Name} eq 'Pg') {
 
-      $select .= ", array_to_string(array(select pkg from cust_pkg left join part_pkg using ( pkgpart ) where cust_main.custnum = cust_pkg.custnum $pkgwhere),'|') as magic";
+      push @select, "array_to_string(array(select pkg from cust_pkg left join part_pkg using ( pkgpart ) where cust_main.custnum = cust_pkg.custnum $pkgwhere),'|') as magic";
 
     }elsif ($dbh->{Driver}->{Name} =~ /^mysql/i) {
-      $select .= ", GROUP_CONCAT(pkg SEPARATOR '|') as magic";
+      push @select, "GROUP_CONCAT(pkg SEPARATOR '|') as magic";
       $addl_from .= " LEFT JOIN part_pkg using ( pkgpart )";
     }else{
       warn "warning: unknown database type ". $dbh->{Driver}->{Name}. 
@@ -690,6 +777,21 @@ sub search {
 
   }
 
+  if ( $params->{'with_geocode'} ) {
+
+    unshift @extra_headers, 'Tax location override', 'Calculated tax location';
+    unshift @extra_fields, sub { my $c = shift; $c->get('geocode'); },
+                           sub { my $c = shift;
+                                 $c->set('geocode', '');
+                                 $c->geocode('cch'); #XXX only cch right now
+                               };
+    push @select, 'geocode';
+    push @select, 'zip' unless grep { $_ eq 'zip' } @select;
+    push @select, 'ship_zip' unless grep { $_ eq 'ship_zip' } @select;
+  }
+
+  my $select = join(', ', @select);
+
   my $sql_query = {
     'table'         => 'cust_main',
     'select'        => $select,
@@ -757,7 +859,7 @@ sub fuzzy_search {
 
 sub check_and_rebuild_fuzzyfiles {
   my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
-  rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields
+  rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields;
 }
 
 =item rebuild_fuzzyfiles
@@ -800,6 +902,41 @@ sub rebuild_fuzzyfiles {
 
 }
 
+=item append_fuzzyfiles FIRSTNAME LASTNAME COMPANY ADDRESS1
+
+=cut
+
+sub append_fuzzyfiles {
+  #my( $first, $last, $company ) = @_;
+
+  check_and_rebuild_fuzzyfiles();
+
+  use Fcntl qw(:flock);
+
+  my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+
+  foreach my $field (@fuzzyfields) {
+    my $value = shift;
+
+    if ( $value ) {
+
+      open(CACHE,">>$dir/cust_main.$field")
+        or die "can't open $dir/cust_main.$field: $!";
+      flock(CACHE,LOCK_EX)
+        or die "can't lock $dir/cust_main.$field: $!";
+
+      print CACHE "$value\n";
+
+      flock(CACHE,LOCK_UN)
+        or die "can't unlock $dir/cust_main.$field: $!";
+      close CACHE;
+    }
+
+  }
+
+  1;
+}
+
 =item all_X
 
 =cut