summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2006-08-14 12:13:40 +0000
committerivan <ivan>2006-08-14 12:13:40 +0000
commitb19897e1db4c110d7d7e8b52800cda5ab58ce9e0 (patch)
tree13cd7bebbe0319c6e96c79feb4ba4c182e71323f
parent97168edae6af4a4d98c4f790b0c064b73efbb9fd (diff)
sprinkle some magic ajax fairy dust on referring customer SELEKTAH. rewind! make smart search smarter, re-layout the top search bars and add an invoice one
-rw-r--r--FS/FS/cust_main.pm329
-rwxr-xr-xhttemplate/edit/cust_main.cgi10
-rw-r--r--httemplate/elements/header.html94
-rw-r--r--httemplate/elements/search-cust_main.html163
-rw-r--r--rt/html/Elements/FreesideInvoiceSearch20
-rw-r--r--rt/html/Elements/FreesideNewCust2
-rw-r--r--rt/html/Elements/FreesideSearch7
-rw-r--r--rt/html/Elements/FreesideSvcSearch7
-rw-r--r--rt/html/Elements/PageLayout2
-rw-r--r--rt/html/Elements/SimpleSearch7
-rw-r--r--rt/html/Elements/Tabs6
-rw-r--r--rt/html/NoAuth/webrt.css39
12 files changed, 531 insertions, 155 deletions
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 3f67c62..7962f6d 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -3824,8 +3824,8 @@ sub uncancel_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, only I<last> or I<company> may be specified (the
-appropriate ship_ field is also searched if applicable).
+records. Currently, I<first>, I<last> and/or I<company> may be specified (the
+appropriate ship_ field is also searched).
Additional options are the same as FS::Record::qsearch
@@ -3839,19 +3839,25 @@ sub fuzzy_search {
check_and_rebuild_fuzzyfiles();
foreach my $field ( keys %$fuzzy ) {
- my $sub = \&{"all_$field"};
my %match = ();
- $match{$_}=1 foreach ( amatch($fuzzy->{$field}, ['i'], @{ &$sub() } ) );
+ $match{$_}=1 foreach ( amatch( $fuzzy->{$field},
+ ['i'],
+ @{ $self->all_X($field) }
+ )
+ );
+ my @fcust = ();
foreach ( keys %match ) {
- push @cust_main, qsearch('cust_main', { %$hash, $field=>$_}, @opt);
- push @cust_main, qsearch('cust_main', { %$hash, "ship_$field"=>$_}, @opt)
- if defined dbdef->table('cust_main')->column('ship_last');
+ push @fcust, qsearch('cust_main', { %$hash, $field=>$_}, @opt);
+ push @fcust, qsearch('cust_main', { %$hash, "ship_$field"=>$_}, @opt);
}
+ my %fsaw = ();
+ push @cust_main, grep { ! $fsaw{$_->custnum}++ } @fcust;
}
+ # we want the components of $fuzzy ANDed, not ORed, but still don't want dupes
my %saw = ();
- @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
+ @cust_main = grep { ++$saw{$_->custnum} == scalar(keys %$fuzzy) } @cust_main;
@cust_main;
@@ -3866,8 +3872,9 @@ sub fuzzy_search {
=item smart_search OPTION => VALUE ...
Accepts the following options: I<search>, the string to search for. The string
-will be searched for as a customer number, last name or company name, first
-searching for an exact match then fuzzy and substring matches.
+will be searched for as a customer number, phone number, name or company name,
+first searching for an exact match then fuzzy and substring matches (in some
+cases - see the source code for the exact heuristics used).
Any additional options treated as an additional qualifier on the search
(i.e. I<agentnum>).
@@ -3878,13 +3885,53 @@ Returns a (possibly empty) array of FS::cust_main objects.
sub smart_search {
my %options = @_;
- my $search = delete $options{'search'};
#here is the agent virtualization
my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
my @cust_main = ();
- if ( $search =~ /^\s*(\d+)\s*$/ ) { # customer # search
+
+ my $search = delete $options{'search'};
+ ( my $alphanum_search = $search ) =~ s/\W//g;
+
+ if ( $alphanum_search =~ /^1?(\d{3})(\d{3})(\d{4})(\d*)$/ ) { #phone# search
+
+ #false laziness w/Record::ut_phone
+ my $phonen = "$1-$2-$3";
+ $phonen .= " x$4" if $4;
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %options },
+ 'extra_sql' => ( scalar(keys %options) ? ' AND ' : ' WHERE ' ).
+ ' ( '.
+ join(' OR ', map "$_ = '$phonen'",
+ qw( daytime night fax
+ ship_daytime ship_night ship_fax )
+ ).
+ ' ) '.
+ " AND $agentnums_sql", #agent virtualization
+ } );
+
+ unless ( @cust_main || $phonen =~ /x\d+$/ ) { #no exact match
+ #try looking for matches with extensions unless one was specified
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %options },
+ 'extra_sql' => ( scalar(keys %options) ? ' AND ' : ' WHERE ' ).
+ ' ( '.
+ join(' OR ', map "$_ LIKE '$phonen\%'",
+ qw( daytime night
+ ship_daytime ship_night )
+ ).
+ ' ) '.
+ " AND $agentnums_sql", #agent virtualization
+ } );
+
+ }
+
+ } elsif ( $search =~ /^\s*(\d+)\s*$/ ) { # customer # search
push @cust_main, qsearch( {
'table' => 'cust_main',
@@ -3892,22 +3939,86 @@ sub smart_search {
'extra_sql' => " AND $agentnums_sql", #agent virtualization
} );
- } elsif ( $search =~ /^\s*(\S.*\S)\s*$/ ) { #value search
+ } 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
+
+ foreach my $prefix ( '', 'ship_' ) {
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { $prefix.'first' => $first,
+ $prefix.'last' => $last,
+ $prefix.'company' => $company,
+ %options,
+ },
+ 'extra_sql' => " AND $agentnums_sql",
+ } );
+ }
+
+ } elsif ( $search =~ /^\s*(\S.*\S)\s*$/ ) { # value search
+ # try (ship_){last,company}
my $value = lc($1);
- # remove "(Last, First)" in "Company (Last, First"), otherwise the
- # full strings the browser remembers won't work
- $value =~ s/\([\w \,\.\-\']*\)$//; #false laziness w/Record::ut_name
+ # # remove "(Last, First)" in "Company (Last, First)", otherwise the
+ # # full strings the browser remembers won't work
+ # $value =~ s/\([\w \,\.\-\']*\)$//; #false laziness w/Record::ut_name
+
+ use Lingua::EN::NameParse;
+ my $NameParse = new Lingua::EN::NameParse(
+ auto_clean => 1,
+ allow_reversed => 1,
+ );
+
+ my($last, $first) = ( '', '' );
+ #maybe disable this too and just rely on NameParse?
+ if ( $value =~ /^(.+),\s*([^,]+)$/ ) { # Last, First
+ ($last, $first) = ( $1, $2 );
+
+ #} elsif ( $value =~ /^(.+)\s+(.+)$/ ) {
+ } elsif ( ! $NameParse->parse($value) ) {
+
+ my %name = $NameParse->components;
+ $first = $name{'given_name_1'};
+ $last = $name{'surname_1'};
+
+ }
+
+ if ( $first && $last ) {
+
+ my($q_last, $q_first) = ( dbh->quote($last), dbh->quote($first) );
+
+ #exact
+ my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
+ $sql .= "
+ ( ( LOWER(last) = $q_last AND LOWER(first) = $q_first )
+ OR ( LOWER(ship_last) = $q_last AND LOWER(ship_first) = $q_first )
+ )";
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => \%options,
+ 'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+ } );
+
+ # or it just be something that was typed in... (try that in a sec)
+
+ }
+
my $q_value = dbh->quote($value);
#exact
my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
- $sql .= " ( LOWER(last) = $q_value OR LOWER(company) = $q_value";
- $sql .= " OR LOWER(ship_last) = $q_value OR LOWER(ship_company) = $q_value"
- if defined dbdef->table('cust_main')->column('ship_last');
- $sql .= ' )';
+ $sql .= " ( LOWER(last) = $q_value
+ OR LOWER(company) = $q_value
+ OR LOWER(ship_last) = $q_value
+ OR LOWER(ship_company) = $q_value
+ )";
push @cust_main, qsearch( {
'table' => 'cust_main',
@@ -3920,56 +4031,62 @@ sub smart_search {
#still some false laziness w/ search/cust_main.cgi
#substring
- push @cust_main, qsearch( {
- 'table' => 'cust_main',
- 'hashref' => { 'last' => { 'op' => 'ILIKE',
- 'value' => "%$value%" },
- %options,
- },
- 'extra_sql' => " AND $agentnums_sql", #agent virtualizaiton
- } );
- push @cust_main, qsearch( {
- 'table' => 'cust_main',
- 'hashref' => { 'ship_last' => { 'op' => 'ILIKE',
- 'value' => "%$value%" },
- %options,
- },
- 'extra_sql' => " AND $agentnums_sql", #agent virtualization
- } )
- if defined dbdef->table('cust_main')->column('ship_last');
- push @cust_main, qsearch( {
- 'table' => 'cust_main',
- 'hashref' => { 'company' => { 'op' => 'ILIKE',
- 'value' => "%$value%" },
- %options,
- },
- 'extra_sql' => " AND $agentnums_sql", #agent virtualization
- } );
- push @cust_main, qsearch( {
- 'table' => 'cust_main',
- 'hashref' => { 'ship_company' => { 'op' => 'ILIKE',
- 'value' => "%$value%" },
- %options,
- },
- 'extra_sql' => " AND $agentnums_sql", #agent virtualization
- } )
- if defined dbdef->table('cust_main')->column('ship_last');
+ my @hashrefs = (
+ { 'company' => { op=>'ILIKE', value=>"%$value%" }, },
+ { 'ship_company' => { op=>'ILIKE', value=>"%$value%" }, },
+ );
+
+ if ( $first && $last ) {
+
+ push @hashrefs,
+ { 'first' => { op=>'ILIKE', value=>"%$first%" },
+ 'last' => { op=>'ILIKE', value=>"%$last%" },
+ },
+ { 'ship_first' => { op=>'ILIKE', value=>"%$first%" },
+ 'ship_last' => { op=>'ILIKE', value=>"%$last%" },
+ },
+ ;
+
+ } else {
+
+ push @hashrefs,
+ { 'last' => { op=>'ILIKE', value=>"%$value%" }, },
+ { 'ship_last' => { op=>'ILIKE', value=>"%$value%" }, },
+ ;
+ }
+
+ foreach my $hashref ( @hashrefs ) {
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %$hashref,
+ %options,
+ },
+ 'extra_sql' => " AND $agentnums_sql", #agent virtualizaiton
+ } );
+
+ }
#fuzzy
- push @cust_main, FS::cust_main->fuzzy_search(
- { 'last' => $value }, #fuzzy hashref
- \%options, #hashref
- '', #select
- " AND $agentnums_sql", #extra_sql #agent virtualization
- );
- push @cust_main, FS::cust_main->fuzzy_search(
- { 'company' => $value }, #fuzzy hashref
+ my @fuzopts = (
\%options, #hashref
'', #select
" AND $agentnums_sql", #extra_sql #agent virtualization
);
+ if ( $first && $last ) {
+ push @cust_main, FS::cust_main->fuzzy_search(
+ { 'last' => $last, #fuzzy hashref
+ 'first' => $first }, #
+ @fuzopts
+ );
+ }
+ foreach my $field ( 'last', 'company' ) {
+ push @cust_main,
+ FS::cust_main->fuzzy_search( { $field => $value }, @fuzopts );
+ }
+
}
#eliminate duplicates
@@ -3986,10 +4103,12 @@ sub smart_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;
- -e "$dir/cust_main.last" && -e "$dir/cust_main.company"
- or &rebuild_fuzzyfiles;
+ rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields
}
=item rebuild_fuzzyfiles
@@ -4003,71 +4122,39 @@ sub rebuild_fuzzyfiles {
my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
mkdir $dir, 0700 unless -d $dir;
- #last
-
- open(LASTLOCK,">>$dir/cust_main.last")
- or die "can't open $dir/cust_main.last: $!";
- flock(LASTLOCK,LOCK_EX)
- or die "can't lock $dir/cust_main.last: $!";
-
- my @all_last = map $_->getfield('last'), qsearch('cust_main', {});
- push @all_last,
- grep $_, map $_->getfield('ship_last'), qsearch('cust_main',{})
- if defined dbdef->table('cust_main')->column('ship_last');
-
- open (LASTCACHE,">$dir/cust_main.last.tmp")
- or die "can't open $dir/cust_main.last.tmp: $!";
- print LASTCACHE join("\n", @all_last), "\n";
- close LASTCACHE or die "can't close $dir/cust_main.last.tmp: $!";
-
- rename "$dir/cust_main.last.tmp", "$dir/cust_main.last";
- close LASTLOCK;
-
- #company
-
- open(COMPANYLOCK,">>$dir/cust_main.company")
- or die "can't open $dir/cust_main.company: $!";
- flock(COMPANYLOCK,LOCK_EX)
- or die "can't lock $dir/cust_main.company: $!";
-
- my @all_company = grep $_ ne '', map $_->company, qsearch('cust_main',{});
- push @all_company,
- grep $_ ne '', map $_->ship_company, qsearch('cust_main', {})
- if defined dbdef->table('cust_main')->column('ship_last');
+ foreach my $fuzzy ( @fuzzyfields ) {
- open (COMPANYCACHE,">$dir/cust_main.company.tmp")
- or die "can't open $dir/cust_main.company.tmp: $!";
- print COMPANYCACHE join("\n", @all_company), "\n";
- close COMPANYCACHE or die "can't close $dir/cust_main.company.tmp: $!";
-
- rename "$dir/cust_main.company.tmp", "$dir/cust_main.company";
- close COMPANYLOCK;
-
-}
-
-=item all_last
-
-=cut
+ open(LOCK,">>$dir/cust_main.$fuzzy")
+ or die "can't open $dir/cust_main.$fuzzy: $!";
+ flock(LOCK,LOCK_EX)
+ or die "can't lock $dir/cust_main.$fuzzy: $!";
+
+ my @all = map $_->getfield($fuzzy), qsearch('cust_main', {});
+ push @all,
+ grep $_, map $_->getfield("ship_$fuzzy"), qsearch('cust_main',{});
+
+ open (CACHE,">$dir/cust_main.$fuzzy.tmp")
+ or die "can't open $dir/cust_main.$fuzzy.tmp: $!";
+ print CACHE join("\n", @all), "\n";
+ close CACHE or die "can't close $dir/cust_main.$fuzzy.tmp: $!";
+
+ rename "$dir/cust_main.$fuzzy.tmp", "$dir/cust_main.$fuzzy";
+ close LOCK;
+ }
-sub all_last {
- my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
- open(LASTCACHE,"<$dir/cust_main.last")
- or die "can't open $dir/cust_main.last: $!";
- my @array = map { chomp; $_; } <LASTCACHE>;
- close LASTCACHE;
- \@array;
}
-=item all_company
+=item all_X
=cut
-sub all_company {
+sub all_X {
+ my( $self, $field ) = @_;
my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
- open(COMPANYCACHE,"<$dir/cust_main.company")
- or die "can't open $dir/cust_main.last: $!";
- my @array = map { chomp; $_; } <COMPANYCACHE>;
- close COMPANYCACHE;
+ open(CACHE,"<$dir/cust_main.$field")
+ or die "can't open $dir/cust_main.$field: $!";
+ my @array = map { chomp; $_; } <CACHE>;
+ close CACHE;
\@array;
}
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 45cb69f..c3d1804 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -64,7 +64,9 @@ if ( $cgi->param('error') ) {
@invoicing_list = ();
}
$cgi->delete_all();
+
my $action = $custnum ? 'Edit' : 'Add';
+$action .= ": ". $cust_main->name if $custnum;
my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
@@ -139,7 +141,13 @@ if ( $cust_main->referral_custnum
<TR>
<TD ALIGN="right">Referring customer</TD>
- <TD><INPUT TYPE="text" NAME="referral_custnum" VALUE=""></TD>
+ <TD>
+ <!-- <INPUT TYPE="text" NAME="referral_custnum" VALUE=""> -->
+ <%= include('/elements/search-cust_main.html',
+ 'field_name' => 'referral_custnum',
+ )
+ %>
+ </TD>
</TR>
<% } else { %>
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index 3aa81be..ea8c418 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -3,6 +3,7 @@
my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
my $conf = new FS::Conf;
+
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
@@ -18,17 +19,22 @@
<SCRIPT TYPE="text/javascript">
function clearhint_search_cust (what) {
- if ( what.value = '(cust #, name or company)' )
+ if ( what.value == '(cust #, name, company or phone)' )
+ what.value = '';
+ }
+
+ function clearhint_search_invoice (what) {
+ if ( what.value == '(inv #)' )
what.value = '';
}
function clearhint_search_svc (what) {
- if ( what.value = '(user, user@domain or domain)' )
+ if ( what.value == '(user, user@domain or domain)' )
what.value = '';
}
function clearhint_search_ticket (what) {
- if ( what.value = '(ticket # or subject string)' )
+ if ( what.value == '(ticket # or subject string)' )
what.value = '';
}
</SCRIPT>
@@ -39,13 +45,11 @@
<BODY BACKGROUND="<%=$fsurl%>images/background-cheat.png" <%= $etc %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0">
<table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0; padding-right:4">
<tr>
- <td rowspan=2 BGCOLOR="#ffffff">
- <IMG BORDER=0 ALT="freeside" SRC="<%=$fsurl%>images/small-logo.png">
- </td>
+ <td rowspan=2 BGCOLOR="#ffffff"><IMG BORDER=0 ALT="freeside" SRC="<%=$fsurl%>images/small-logo.png"></td>
<td align=left rowspan=2 BGCOLOR="#ffffff"> <!-- valign="top" -->
<font size=6><%= $conf->config('company_name') || 'ExampleCo' %></font>
</td>
- <td align=right valign=top BGCOLOR="#ffffff">Logged in as <b><%= getotaker %>&nbsp</b><br><FONT SIZE="-2"><a href="<%=$fsurl%>pref/XXXwritethis">Preferences</a>&nbsp;<BR><BR></FONT>
+ <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><%= getotaker %>&nbsp</b><br></FONT><FONT SIZE="-2"><a href="<%=$fsurl%>pref/XXXwritethis">Preferences</a>&nbsp;<BR></FONT>
</td>
</tr>
<tr>
@@ -77,34 +81,88 @@
</tr>
</table>
- <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=4>
+<style type="text/css">
+input.fsblackbutton {
+ background-color:#333333;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff333333',EndColorStr='#ff666666')
+}
+
+input.fsblackbuttonselected {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+</style>
+
+ <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=0>
<TR>
- <TD COLSPAN=4 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD>
+ <TD COLSPAN=5 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%=$fsurl%>images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD>
</TR>
+
<TR>
- <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right" WIDTH="15%">
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
<FORM ACTION="<%=$fsurl%>edit/cust_main.cgi" METHOD="GET" STYLE="margin:0">
- <INPUT TYPE="submit" VALUE="New customer">
+ <INPUT TYPE="submit" VALUE="New customer" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="vertical-align:bottom">
</FORM>
</TD>
+
<TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
<FORM ACTION="<%=$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0">
- <INPUT NAME="search_cust" TYPE="text" VALUE="(cust #, name or company)" SIZE="22" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="text-align:right">
- <INPUT TYPE="submit" VALUE="Search customers">
+ <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>
+ <A HREF="<%=$fsurl%>search/cust_main.html" STYLE="color: #000000; 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>
</TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <% if ( $FS::CurrentUser::CurrentUser->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">
+ <% if ( $FS::CurrentUser::CurrentUser->access_right('List invoices') ) { %>
+ <A HREF="<%=$fsurl%>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
+ <% } %>
+ <BR>
+ <INPUT TYPE="submit" VALUE="Search invoices" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </FORM>
+ <% } %>
+ </TD>
+
<TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
<FORM ACTION="<%=$fsurl%>search/svc_Smart.html" METHOD="GET" STYLE="margin:0">
- <INPUT NAME="search_svc" TYPE="text" VALUE="(user, user@domain or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="text-align:right">
- <INPUT TYPE="submit" VALUE="Search services">
+ <INPUT NAME="search_svc" TYPE="text" VALUE="(user, user@domain or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+ <A HREF="<%=$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>
</TD>
- <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right" STYLE="padding-right:4px">
<FORM ACTION="<%=$fsurl%>rt/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="text-align:right">
- <INPUT TYPE="submit" VALUE="Search tickets">
+ <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>
+ <A HREF="<%=$fsurl%>rt/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>
</TD>
+
</TR>
</TABLE>
<TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4>
diff --git a/httemplate/elements/search-cust_main.html b/httemplate/elements/search-cust_main.html
new file mode 100644
index 0000000..ca91b40
--- /dev/null
+++ b/httemplate/elements/search-cust_main.html
@@ -0,0 +1,163 @@
+<%
+ my( %opt ) = @_;
+ $opt{'field_name'} ||= 'custnum';
+
+ my $cust_main = '';
+ if ( $opt{'value'} ) {
+ $cust_main = qsearchs(
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $opt{'value'} },
+ 'extra_sql' => " AND ". $FS::CurrentUser::CurrentUser->agentnums_sql,
+ );
+ }
+%>
+
+<INPUT TYPE="hidden" NAME="<%= $opt{'field_name'} %>" VALUE="<%= $opt{'value'} %>">
+
+<!-- some false laziness w/ misc/batch-cust_pay.html, though not as bad as i'd thought at first... -->
+
+<INPUT TYPE="text" NAME="<%= $opt{'field_name'} %>_search" ID="<%= $opt{'field_name'} %>_search" SIZE="32" VALUE="<%= $cust_main ? $cust_main->name : '(cust #, name or company)' %>" onFocus="clearhint_<%= $opt{'field_name'} %>_search(this);" onClick="clearhint_<%= $opt{'field_name'} %>_search(this);" onChange="smart_<%= $opt{'field_name'} %>_search(this);">
+
+<SELECT NAME="<%= $opt{'field_name'} %>_select" ID="<%= $opt{'field_name'} %>_select" STYLE="color:#ff0000; display:none" onChange="select_<%= $opt{'field_name'} %>(this);">
+</SELECT>
+
+<%= include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [ 'smart_search' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function clearhint_<%= $opt{'field_name'} %>_search (what) {
+
+ what.style.color = '#000000';
+
+ if ( what.value == '(cust #, name or company)' )
+ what.value = '';
+
+ if ( what.value.indexOf('Customer not found: ') == 0 )
+ what.value = what.value.substr(20);
+
+ }
+
+ function smart_<%= $opt{'field_name'} %>_search(what) {
+
+ var customer = what.value;
+
+ if ( customer == 'searching...' || customer == ''
+ || customer.indexOf('Customer not found: ') == 0 )
+ return;
+
+ if ( what.getAttribute('magic') == 'nosearch' ) {
+ what.setAttribute('magic', '');
+ return;
+ }
+
+ //what.value = 'searching...'
+ what.disabled = true;
+ what.style.color= '#000000';
+ what.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('<%= $opt{'field_name'} %>_select');
+
+ //alert("search for customer " + customer);
+
+ function <%= $opt{'field_name'} %>_search_update(customers) {
+
+ //alert('customers returned: ' + customers);
+
+ var customerArray = eval('(' + customers + ')');
+
+ what.disabled = false;
+ what.style.backgroundColor = '#ffffff';
+
+ if ( customerArray.length == 0 ) {
+
+ what.form.<%= $opt{'field_name'} %>.value = '';
+
+ what.value = 'Customer not found: ' + what.value;
+ what.style.color = '#ff0000';
+
+ what.style.display = '';
+ customer_select.style.display = 'none';
+
+ } else if ( customerArray.length == 1 ) {
+
+ //alert('one customer found: ' + customerArray[0]);
+
+ what.form.<%= $opt{'field_name'} %>.value = customerArray[0][0];
+ what.value = customerArray[0][1];
+
+ what.style.display = '';
+ customer_select.style.display = 'none';
+
+ } else {
+
+ //alert('multiple customers found, have to create select dropdown');
+
+ //blank the current list
+ for ( var i = customer_select.length; i >= 0; i-- )
+ customer_select.options[i] = null;
+
+ opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
+
+ //add the multiple customers
+ for ( var s = 0; s < customerArray.length; s++ )
+ opt(customer_select, customerArray[s][0], customerArray[s][1], '#000000');
+
+ opt(customer_select, 'cancel', '(Edit search string)', '#000000');
+
+ what.style.display = 'none';
+ customer_select.style.display = '';
+
+ }
+
+ }
+
+ smart_search( customer, <%= $opt{'field_name'} %>_search_update );
+
+
+ }
+
+ function select_<%= $opt{'field_name'} %> (what) {
+
+ var custnum = what.options[what.selectedIndex].value;
+ var customer = what.options[what.selectedIndex].text;
+
+ var customer_obj = document.getElementById('<%= $opt{'field_name'} %>_search');
+
+ if ( custnum == '' ) {
+ //what.style.color = '#ff0000';
+
+ } else if ( custnum == 'cancel' ) {
+
+ customer_obj.style.color = '#000000';
+
+ what.style.display = 'none';
+ customer_obj.style.display = '';
+ customer_obj.focus();
+
+ } else {
+
+ what.form.<%= $opt{'field_name'} %>.value = custnum;
+
+ customer_obj.value = customer;
+ customer_obj.style.color = '#000000';
+
+ what.style.display = 'none';
+ customer_obj.style.display = '';
+
+ }
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+</SCRIPT>
+
diff --git a/rt/html/Elements/FreesideInvoiceSearch b/rt/html/Elements/FreesideInvoiceSearch
new file mode 100644
index 0000000..3842b2f
--- /dev/null
+++ b/rt/html/Elements/FreesideInvoiceSearch
@@ -0,0 +1,20 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('View invoices') ) {
+
+ <form action="<% $RT::URI::freeside::URL %>/search/cust_bill.html" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_invoice (what) {
+ if ( what.value == '(inv #)' )
+ what.value = '';
+ }
+ </SCRIPT>
+ <input name="invnum" accesskey="0" VALUE="(inv #)" SIZE="4" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="text-align:right; margin-bottom:1px; font-family: Arial, Verdana, Helvetica, sans-serif;">
+
+% if ( $FS::CurrentUser::CurrentUser->access_right('List invoices') ) {
+ <A HREF="<% $RT::URI::freeside::URL %>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+% }
+ <BR>
+
+ <input type="submit" value="<&|/l&>Search invoices</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </form>
+
+% }
diff --git a/rt/html/Elements/FreesideNewCust b/rt/html/Elements/FreesideNewCust
index af8f9f1..c752437 100644
--- a/rt/html/Elements/FreesideNewCust
+++ b/rt/html/Elements/FreesideNewCust
@@ -1,3 +1,3 @@
<form action="<% $RT::URI::freeside::URL %>/edit/cust_main.cgi" STYLE="margin:0">
-<INPUT TYPE="submit" VALUE="<&|/l&>New customer</&>">&nbsp;
+<INPUT TYPE="submit" VALUE="<&|/l&>New customer</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;">&nbsp;
</FORM>
diff --git a/rt/html/Elements/FreesideSearch b/rt/html/Elements/FreesideSearch
index f0efb60..99b8da0 100644
--- a/rt/html/Elements/FreesideSearch
+++ b/rt/html/Elements/FreesideSearch
@@ -1,10 +1,11 @@
<form action="<% $RT::URI::freeside::URL %>/search/cust_main.cgi" STYLE="margin:0">
<SCRIPT TYPE="text/javascript">
function clearhint_search_cust (what) {
- if ( what.value = '(cust #, name or company)' )
+ if ( what.value == '(cust #, name, company or phone)' )
what.value = '';
}
</SCRIPT>
-<input name="search_cust" accesskey="0" VALUE="(cust #, name or company)" SIZE="23" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="text-align:right">
-<input type="submit" value="<&|/l&>Search customers</&>">&nbsp;
+<input name="search_cust" accesskey="0" VALUE="(cust #, name, company or phone)" SIZE="28" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::URI::freeside::URL %>/search/cust_main.html" STYLE="color: #000000; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search customers</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
</form>
diff --git a/rt/html/Elements/FreesideSvcSearch b/rt/html/Elements/FreesideSvcSearch
index 47c430f..e9ad564 100644
--- a/rt/html/Elements/FreesideSvcSearch
+++ b/rt/html/Elements/FreesideSvcSearch
@@ -1,10 +1,11 @@
<form action="<% $RT::URI::freeside::URL %>/search/svc_Smart.html" STYLE="margin:0">
<SCRIPT TYPE="text/javascript">
function clearhint_search_svc (what) {
- if ( what.value = '(user, user@domain or domain)' )
+ if ( what.value == '(user, user@domain or domain)' )
what.value = '';
}
</SCRIPT>
-<input name="search_svc" accesskey="0" VALUE="(user, user@domain or domain)" SIZE="27" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="text-align:right">
-<input type="submit" value="<&|/l&>Search services</&>">&nbsp;
+<input name="search_svc" accesskey="0" VALUE="(user, user@domain or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+ <A HREF="<% $RT::URI::freeside::URL %>search/svc_Smarter.html" STYLE="color: #000000; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search services</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
</form>
diff --git a/rt/html/Elements/PageLayout b/rt/html/Elements/PageLayout
index 52353fa..f13ee0d 100644
--- a/rt/html/Elements/PageLayout
+++ b/rt/html/Elements/PageLayout
@@ -52,7 +52,7 @@
%# </th>
<span class="topactions">
% my $notfirst = 0; foreach my $action (sort keys %{$topactions}) {
- <td class="blackright" ALIGN="right" <% $notfirst++ ? '' : 'WIDTH="15%"' %>>
+ <td class="blackright" ALIGN="right" VALIGN="center">
<%$topactions->{"$action"}->{'html'} |n %>
</td>
% }
diff --git a/rt/html/Elements/SimpleSearch b/rt/html/Elements/SimpleSearch
index 55d65fc..e9fc5c6 100644
--- a/rt/html/Elements/SimpleSearch
+++ b/rt/html/Elements/SimpleSearch
@@ -46,10 +46,11 @@
<form action="<% $RT::WebPath %>/index.html" STYLE="margin:0">
<SCRIPT TYPE="text/javascript">
function clearhint_search_ticket (what) {
- if ( what.value = '(ticket # or subject string)' )
+ if ( what.value == '(ticket # or subject string)' )
what.value = '';
}
</SCRIPT>
-<input name="q" accesskey="0" VALUE="(ticket # or subject string)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="text-align:right">
-<input type="submit" value="<&|/l&>Search tickets</&>">&nbsp;
+<input name="q" accesskey="0" VALUE="(ticket # or subject string)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::WebPath %>/Search/Build.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>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>
diff --git a/rt/html/Elements/Tabs b/rt/html/Elements/Tabs
index dcb652e..721f920 100644
--- a/rt/html/Elements/Tabs
+++ b/rt/html/Elements/Tabs
@@ -63,9 +63,11 @@ my $basetopactions = {
},
B => { html => $m->scomp('/Elements/FreesideSearch')
},
- C => { html => $m->scomp('/Elements/FreesideSvcSearch')
+ C => { html => $m->scomp('/Elements/FreesideInvoiceSearch')
},
- D => { html => $m->scomp('/Elements/SimpleSearch')
+ D => { html => $m->scomp('/Elements/FreesideSvcSearch')
+ },
+ E => { html => $m->scomp('/Elements/SimpleSearch')
}
};
my $basetabs = {
diff --git a/rt/html/NoAuth/webrt.css b/rt/html/NoAuth/webrt.css
index 04c959a..5c241f9 100644
--- a/rt/html/NoAuth/webrt.css
+++ b/rt/html/NoAuth/webrt.css
@@ -373,9 +373,44 @@ li.currenttopnav-5-major {
background-color: #000000;
color: #ffffff;
background-position: left top;
- vertical-align: top;
+ vertical-align: center;
text-align: right;
+ font-size:16px;
+ padding-right:4px
}
+
+input.fsblackbutton {
+ background-color:#333333;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff333333',EndColorStr='#ff666666')
+}
+
+input.fsblackbuttonselected {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+
.mediumgray {
background-color: #cccccc;
background-position: left top;
@@ -442,7 +477,7 @@ div.downloadattachment {
td { font-family: Arial, Verdana, Helvetica, sans-serif;
- font-size: 11px;
+ font-size: 12px;
background-position: left top;
}
.black { background-color: #000000;