diff options
author | ivan <ivan> | 2006-08-14 12:13:40 +0000 |
---|---|---|
committer | ivan <ivan> | 2006-08-14 12:13:40 +0000 |
commit | b19897e1db4c110d7d7e8b52800cda5ab58ce9e0 (patch) | |
tree | 13cd7bebbe0319c6e96c79feb4ba4c182e71323f | |
parent | 97168edae6af4a4d98c4f790b0c064b73efbb9fd (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.pm | 329 | ||||
-rwxr-xr-x | httemplate/edit/cust_main.cgi | 10 | ||||
-rw-r--r-- | httemplate/elements/header.html | 94 | ||||
-rw-r--r-- | httemplate/elements/search-cust_main.html | 163 | ||||
-rw-r--r-- | rt/html/Elements/FreesideInvoiceSearch | 20 | ||||
-rw-r--r-- | rt/html/Elements/FreesideNewCust | 2 | ||||
-rw-r--r-- | rt/html/Elements/FreesideSearch | 7 | ||||
-rw-r--r-- | rt/html/Elements/FreesideSvcSearch | 7 | ||||
-rw-r--r-- | rt/html/Elements/PageLayout | 2 | ||||
-rw-r--r-- | rt/html/Elements/SimpleSearch | 7 | ||||
-rw-r--r-- | rt/html/Elements/Tabs | 6 | ||||
-rw-r--r-- | rt/html/NoAuth/webrt.css | 39 |
12 files changed, 531 insertions, 155 deletions
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 3f67c62f0..7962f6dfa 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 45cb69fc2..c3d1804bc 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> !; @@ -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 3aa81be3b..ea8c418c3 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 %> </b><br><FONT SIZE="-2"><a href="<%=$fsurl%>pref/XXXwritethis">Preferences</a> <BR><BR></FONT> + <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><%= getotaker %> </b><br></FONT><FONT SIZE="-2"><a href="<%=$fsurl%>pref/XXXwritethis">Preferences</a> <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 000000000..ca91b4027 --- /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 000000000..3842b2ff9 --- /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 af8f9f1a7..c752437da 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</&>"> +<INPUT TYPE="submit" VALUE="<&|/l&>New customer</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;"> </FORM> diff --git a/rt/html/Elements/FreesideSearch b/rt/html/Elements/FreesideSearch index f0efb60d4..99b8da072 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</&>"> +<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 47c430f1a..e9ad56426 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</&>"> +<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 52353fa46..f13ee0dda 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 55d65fc89..e9fc5c6ed 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</&>"> +<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 dcb652e12..721f920d5 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 04c959a05..5c241f93f 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; |