address1 search, RT#5060
authorivan <ivan>
Tue, 27 Oct 2009 18:11:55 +0000 (18:11 +0000)
committerivan <ivan>
Tue, 27 Oct 2009 18:11:55 +0000 (18:11 +0000)
FS/FS/Conf.pm
FS/FS/cust_main.pm
httemplate/elements/header.html

index 6ad3fcb..bd7250f 100644 (file)
@@ -1860,6 +1860,13 @@ worry that config_items is freeside-specific and icky.
   },
 
   {
+    'key'         => 'address1-search',
+    'section'     => 'UI',
+    'description' => 'Enable the ability to search the address1 field from customer search.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'address2-search',
     'section'     => 'UI',
     'description' => 'Enable a "Unit" search box which searches the second address field.  Useful for multi-tenant applications.  See also: cust_main-require_address2',
index 2559cd3..ee9064d 100644 (file)
@@ -1,8 +1,12 @@
 package FS::cust_main;
 
 use strict;
-use vars qw( @ISA @EXPORT_OK $DEBUG $me $conf @encrypted_fields
-             $import $skip_fuzzyfiles $ignore_expired_card @paytypes);
+use vars qw( @ISA @EXPORT_OK $DEBUG $me $conf
+             @encrypted_fields
+             $import $ignore_expired_card
+             $skip_fuzzyfiles @fuzzyfields
+             @paytypes
+           );
 use vars qw( $realtime_bop_decline_quiet ); #ugh
 use Safe;
 use Carp;
@@ -70,9 +74,11 @@ $DEBUG = 0;
 $me = '[FS::cust_main]';
 
 $import = 0;
-$skip_fuzzyfiles = 0;
 $ignore_expired_card = 0;
 
+$skip_fuzzyfiles = 0;
+@fuzzyfields = ( 'first', 'last', 'company', 'address1' );
+
 @encrypted_fields = ('payinfo', 'paycvv');
 sub nohistory_fields { ('paycvv'); }
 
@@ -1203,9 +1209,7 @@ sub queue_fuzzyfiles_update {
   my $dbh = dbh;
 
   my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
-  my $error = $queue->insert( map $self->getfield($_),
-                                  qw(first last company)
-                            );
+  my $error = $queue->insert( map $self->getfield($_), @fuzzyfields );
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return "queueing job (transaction rolled back): $error";
@@ -1213,9 +1217,7 @@ sub queue_fuzzyfiles_update {
 
   if ( $self->ship_last ) {
     $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
-    $error = $queue->insert( map $self->getfield("ship_$_"),
-                                 qw(first last company)
-                           );
+    $error = $queue->insert( map $self->getfield("ship_$_"), @fuzzyfields );
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "queueing job (transaction rolled back): $error";
@@ -3292,9 +3294,9 @@ sub remove_cvv {
 sub _bop_recurring_billing {
   my( $self, %opt ) = @_;
 
-  my $method = $conf->config('credit_card-recurring_billing_flag');
+  my $method = scalar($conf->config('credit_card-recurring_billing_flag'));
 
-  if ( $method eq 'transaction_is_recur' ) {
+  if ( defined($method) && $method eq 'transaction_is_recur' ) {
 
     return 1 if $opt{'trans_is_recur'};
 
@@ -5352,8 +5354,8 @@ sub process_email_search_sql {
 =item fuzzy_search FUZZY_HASHREF [ HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ ]
 
 Performs a fuzzy (approximate) search and returns the matching FS::cust_main
-records.  Currently, I<first>, I<last> and/or I<company> may be specified (the
-appropriate ship_ field is also searched).
+records.  Currently, I<first>, I<last>, I<company> and/or I<address1> may be
+specified (the appropriate ship_ field is also searched).
 
 Additional options are the same as FS::Record::qsearch
 
@@ -5480,31 +5482,53 @@ sub smart_search {
   # custnum search (also try agent_custid), with some tweaking options if your
   # legacy cust "numbers" have letters
   } elsif ( $search =~ /^\s*(\d+)\s*$/
-            || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+'
-                 && $search =~ /^\s*(\w\w?\d+)\s*$/
-               )
+              || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+'
+                   && $search =~ /^\s*(\w\w?\d+)\s*$/
+                 )
+              || ( $conf->exists('address1-search' )
+                   && $search =~ /^\s*(\d+\-?\w*)\s*$/ #i.e. 1234A or 9432-D
+                 )
           )
   {
 
-    push @cust_main, qsearch( {
-      'table'     => 'cust_main',
-      'hashref'   => { 'custnum' => $1, %options },
-      'extra_sql' => " AND $agentnums_sql", #agent virtualization
-    } );
+    my $num = $1;
+
+    if ( $num =~ /^(\d+)$/ && $num <= 2147483647 ) { #need a bigint custnum? wow
+      push @cust_main, qsearch( {
+        'table'     => 'cust_main',
+        'hashref'   => { 'custnum' => $num, %options },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualization
+      } );
+    }
 
     push @cust_main, qsearch( {
       'table'     => 'cust_main',
-      'hashref'   => { 'agent_custid' => $1, %options },
+      'hashref'   => { 'agent_custid' => $num, %options },
       'extra_sql' => " AND $agentnums_sql", #agent virtualization
     } );
 
+    if ( $conf->exists('address1-search') ) {
+      my $len = length($num);
+      $num = lc($num);
+      foreach my $prefix ( '', 'ship_' ) {
+        push @cust_main, qsearch( {
+          'table'     => 'cust_main',
+          'hashref'   => { %options, },
+          'extra_sql' => 
+            ( keys(%options) ? ' AND ' : ' WHERE ' ).
+            " LOWER(SUBSTRING(${prefix}address1 FROM 1 FOR $len)) = '$num' ".
+            " AND $agentnums_sql",
+        } );
+      }
+    }
+
   } elsif ( $search =~ /^\s*(\S.*\S)\s+\((.+), ([^,]+)\)\s*$/ ) {
 
     my($company, $last, $first) = ( $1, $2, $3 );
 
     # "Company (Last, First)"
     #this is probably something a browser remembered,
-    #so just do an exact search
+    #so just do an exact (but case-insensitive) search
 
     foreach my $prefix ( '', 'ship_' ) {
       push @cust_main, qsearch( {
@@ -5573,11 +5597,16 @@ sub smart_search {
 
     #exact
     my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
-    $sql .= " (    LOWER(last)         = $q_value
-                OR LOWER(company)      = $q_value
-                OR LOWER(ship_last)    = $q_value
-                OR LOWER(ship_company) = $q_value
-              )";
+    $sql .= " (    LOWER(last)          = $q_value
+                OR LOWER(company)       = $q_value
+                OR LOWER(ship_last)     = $q_value
+                OR LOWER(ship_company)  = $q_value
+            ";
+    $sql .= "   OR LOWER(address1)      = $q_value
+                OR LOWER(ship_address1) = $q_value
+            "
+      if $conf->exists('address1-search');
+    $sql .= " )";
 
     push @cust_main, qsearch( {
       'table'     => 'cust_main',
@@ -5617,6 +5646,13 @@ sub smart_search {
         ;
       }
 
+      if ( $conf->exists('address1-search') ) {
+        push @hashrefs,
+          { 'address1'      => { op=>'ILIKE', value=>"%$value%" }, },
+          { 'ship_address1' => { op=>'ILIKE', value=>"%$value%" }, },
+        ;
+      }
+
       foreach my $hashref ( @hashrefs ) {
 
         push @cust_main, qsearch( {
@@ -5647,6 +5683,10 @@ sub smart_search {
         push @cust_main,
           FS::cust_main->fuzzy_search( { $field => $value }, @fuzopts );
       }
+      if ( $conf->exists('address1-search') ) {
+        push @cust_main,
+          FS::cust_main->fuzzy_search( { 'address1' => $value }, @fuzopts );
+      }
 
     }
 
@@ -5730,9 +5770,6 @@ sub email_search {
 
 =cut
 
-use vars qw(@fuzzyfields);
-@fuzzyfields = ( 'last', 'first', 'company' );
-
 sub check_and_rebuild_fuzzyfiles {
   my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
   rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields
@@ -5792,7 +5829,7 @@ sub all_X {
   \@array;
 }
 
-=item append_fuzzyfiles LASTNAME COMPANY
+=item append_fuzzyfiles FIRSTNAME LASTNAME COMPANY ADDRESS1
 
 =cut
 
@@ -5805,7 +5842,7 @@ sub append_fuzzyfiles {
 
   my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
 
-  foreach my $field (qw( first last company )) {
+  foreach my $field (@fuzzyfields) {
     my $value = shift;
 
     if ( $value ) {
index 3843a0f..c26f48b 100644 (file)
 
     <SCRIPT TYPE="text/javascript">
       function clearhint_search_cust (what) {
-        if ( what.value == '(cust #, name, company or phone)' )
+        if ( what.value == '<% $cust_label %>' )
           what.value = '';
       }
 
       function clearhint_search_address2 (what) {
-        if ( what.value == '(Unit #)' )
+        if ( what.value == '<% $address2_label %>' )
           what.value = '';
       }
 
       function clearhint_search_invoice (what) {
-        if ( what.value == '(inv #)' )
+        if ( what.value == '<% $inv_label %>' )
           what.value = '';
       }
 
       function clearhint_search_svc (what) {
-        if ( what.value == '(user, email, ip, mac, or domain)' )
+        if ( what.value == '<% $svc_label %>' )
           what.value = '';
       }
 
       function clearhint_search_ticket (what) {
-        if ( what.value == '(ticket # or subject string)' )
+        if ( what.value == '<% $ticketing_label %>' )
           what.value = '';
       }
     </SCRIPT>
@@ -167,7 +167,7 @@ input.fsblackbuttonselected {
         <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
 % if ( $curuser->access_right('List customers') ) {
           <FORM ACTION="<%$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0">
-            <INPUT NAME="search_cust" TYPE="text" VALUE="(cust #, name, company or phone)" SIZE="28" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+            <INPUT NAME="search_cust" TYPE="text" VALUE="<% $cust_label %>" STYLE="width:<%$cust_width%>px" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
             <A HREF="<%$fsurl%>search/report_cust_main.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
             <INPUT TYPE="submit" VALUE="Search customers" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
           </FORM>
@@ -178,7 +178,7 @@ input.fsblackbuttonselected {
 % if ( $conf->exists('address2-search') ) { 
             <FORM ACTION="<%$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0;display:inline">
               <INPUT TYPE="hidden" NAME="address2_on" VALUE="1">
-              <INPUT NAME="address2_text" TYPE="text" VALUE="(Unit #)" SIZE="4" onFocus="clearhint_search_address2(this);" onClick="clearhint_search_address2(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
+              <INPUT NAME="address2_text" TYPE="text" VALUE="<% $address2_label %>" STYLE="width:67px" onFocus="clearhint_search_address2(this);" onClick="clearhint_search_address2(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
               <BR>
               <INPUT TYPE="submit" VALUE="Search units" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
             </FORM>
@@ -187,12 +187,11 @@ input.fsblackbuttonselected {
 
         <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
 % if ( $curuser->access_right('View invoices') ) { 
-
             <FORM ACTION="<%$fsurl%>search/cust_bill.html" METHOD="GET" STYLE="margin:0;display:inline">
-              <INPUT NAME="invnum" TYPE="text" VALUE="(inv #)" SIZE="4" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
+              <INPUT NAME="invnum" TYPE="text" VALUE="<% $inv_label %>" STYLE="width:64px" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
 %   if ( $curuser->access_right('List invoices') ) { 
 
-                <A HREF="<%$fsurl%>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
+                <A HREF="<%$fsurl%>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%">Adv</A>
 %   } 
 
               <BR>
@@ -204,7 +203,7 @@ input.fsblackbuttonselected {
         <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
 % if ( $curuser->access_right('View customer services') ) {
           <FORM ACTION="<%$fsurl%>search/cust_svc.html" METHOD="GET" STYLE="margin:0">
-            <INPUT NAME="search_svc" TYPE="text" VALUE="(user, email, ip, mac, or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+            <INPUT NAME="search_svc" TYPE="text" VALUE="<% $svc_label %>" STYLE="width:224px" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
             <A NOTYET="<%$fsurl%>search/svc_Smarter.html" STYLE="color: #000000; font-size: 70%">Advanced</A>
             <INPUT TYPE="submit" VALUE="Search services" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
           </FORM>
@@ -214,7 +213,7 @@ input.fsblackbuttonselected {
         <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right" STYLE="padding-right:4px">
 % if ( $conf->config("ticket_system") ) { 
           <FORM ACTION="<% FS::TicketSystem->baseurl %>index.html" METHOD="GET" STYLE="margin:0">
-            <INPUT NAME="q" TYPE="text" VALUE="(ticket # or subject string)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+            <INPUT NAME="q" TYPE="text" VALUE="<% $ticketing_label %>" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="width=155px;vertical-align:bottom;text-align:right"><BR>
             <A HREF="<% FS::TicketSystem->baseurl %>Search/Build.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
             <INPUT TYPE="submit" VALUE="Search tickets" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
           </FORM>
@@ -276,4 +275,17 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 my $menu_position = $FS::CurrentUser::CurrentUser->option('menu_position')
                     || 'left';
 
+my $cust_width = 288; #251 #ok for IE, slightly bigger for windows firefox
+my $cust_label = '(cust #, name, company';
+if ( $conf->exists('address1-search') ) {
+  $cust_label .= ', address';
+  $cust_width += 64;
+}
+$cust_label .= ' or contact phone)';
+
+my $address2_label = '(Unit #)';
+my $inv_label = '(inv #)';
+my $svc_label = '(user, email, ip, mac, or domain)';
+my $ticketing_label = '(ticket # or subject string)';
+
 </%init>