working on the road:
[freeside.git] / httemplate / search / cust_main.cgi
1 <%
2
3 my $conf = new FS::Conf;
4 my $maxrecords = $conf->config('maxsearchrecordsperpage');
5
6 #my $cache;
7
8 #my $monsterjoin = <<END;
9 #cust_main left outer join (
10 #  ( cust_pkg left outer join part_pkg using(pkgpart)
11 #  ) left outer join (
12 #    (
13 #      (
14 #        ( cust_svc left outer join part_svc using (svcpart)
15 #        ) left outer join svc_acct using (svcnum)
16 #      ) left outer join svc_domain using(svcnum)
17 #    ) left outer join svc_forward using(svcnum)
18 #  ) using (pkgnum)
19 #) using (custnum)
20 #END
21
22 #my $monsterjoin = <<END;
23 #cust_main left outer join (
24 #  ( cust_pkg left outer join part_pkg using(pkgpart)
25 #  ) left outer join (
26 #    (
27 #      (
28 #        ( cust_svc left outer join part_svc using (svcpart)
29 #        ) left outer join (
30 #          svc_acct left outer join (
31 #            select svcnum, domain, catchall from svc_domain
32 #            ) as svc_acct_domsvc (
33 #              svc_acct_svcnum, svc_acct_domain, svc_acct_catchall
34 #          ) on svc_acct.domsvc = svc_acct_domsvc.svc_acct_svcnum
35 #        ) using (svcnum)
36 #      ) left outer join svc_domain using(svcnum)
37 #    ) left outer join svc_forward using(svcnum)
38 #  ) using (pkgnum)
39 #) using (custnum)
40 #END
41
42 my $limit = '';
43 $limit .= "LIMIT $maxrecords" if $maxrecords;
44
45 my $offset = $cgi->param('offset') || 0;
46 $limit .= " OFFSET $offset" if $offset;
47
48 my $total = 0;
49
50 my(@cust_main, $sortby, $orderby);
51 if ( $cgi->param('browse')
52      || $cgi->param('otaker_on')
53 ) {
54
55   my %search = ();
56   if ( $cgi->param('browse') ) {
57     my $query = $cgi->param('browse');
58     if ( $query eq 'custnum' ) {
59       $sortby=\*custnum_sort;
60       $orderby = "ORDER BY custnum";
61     } elsif ( $query eq 'last' ) {
62       $sortby=\*last_sort;
63       $orderby = "ORDER BY LOWER(last || ' ' || first)";
64     } elsif ( $query eq 'company' ) {
65       $sortby=\*company_sort;
66       $orderby = "ORDER BY LOWER(company || ' ' || last || ' ' || first )";
67     } else {
68       die "unknown browse field $query";
69     }
70   } else {
71     $sortby = \*last_sort; #??
72     $orderby = "ORDER BY LOWER(last || ' ' || first)"; #??
73     if ( $cgi->param('otaker_on') ) {
74       $cgi->param('otaker') =~ /^(\w{1,32})$/ or eidiot "Illegal otaker\n";
75       $search{otaker} = $1;
76     } else {
77       die "unknown query...";
78     }
79   }
80
81   my $ncancelled = '';
82
83   if ( driver_name eq 'mysql' ) {
84
85        my $sql = "CREATE TEMPORARY TABLE temp1_$$ TYPE=MYISAM
86                     SELECT cust_pkg.custnum,COUNT(*) as count
87                       FROM cust_pkg,cust_main
88                         WHERE cust_pkg.custnum = cust_main.custnum
89                               AND ( cust_pkg.cancel IS NULL
90                                     OR cust_pkg.cancel = 0 )
91                         GROUP BY cust_pkg.custnum";
92        my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
93        $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
94        $sql = "CREATE TEMPORARY TABLE temp2_$$ TYPE=MYISAM
95                  SELECT cust_pkg.custnum,COUNT(*) as count
96                    FROM cust_pkg,cust_main
97                      WHERE cust_pkg.custnum = cust_main.custnum
98                      GROUP BY cust_pkg.custnum";
99        $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
100        $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
101   }
102
103   if (  $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
104        || ( $conf->exists('hidecancelledcustomers')
105              && ! $cgi->param('showcancelledcustomers') )
106      ) {
107     #grep { $_->ncancelled_pkgs || ! $_->all_pkgs }
108     if ( driver_name eq 'mysql' ) {
109        $ncancelled = "
110           temp1_$$.custnum = cust_main.custnum
111                AND temp2_$$.custnum = cust_main.custnum
112                AND (temp1_$$.count > 0
113                        OR temp2_$$.count = 0 )
114        ";
115     } else {
116        $ncancelled = "
117           0 < ( SELECT COUNT(*) FROM cust_pkg
118                        WHERE cust_pkg.custnum = cust_main.custnum
119                          AND ( cust_pkg.cancel IS NULL
120                                OR cust_pkg.cancel = 0
121                              )
122                    )
123             OR 0 = ( SELECT COUNT(*) FROM cust_pkg
124                        WHERE cust_pkg.custnum = cust_main.custnum
125                    )
126        ";
127     }
128
129   }
130
131   #EWWWWWW
132   my $qual = join(' AND ',
133             map { "$_ = ". dbh->quote($search{$_}) } keys %search );
134
135   if ( $ncancelled ) {
136     $qual .= ' AND ' if $qual;
137     $qual .= $ncancelled;
138   }
139     
140   $qual = " WHERE $qual" if $qual;
141   my $statement;
142   if ( driver_name eq 'mysql' ) {
143     $statement = "SELECT COUNT(*) FROM cust_main";
144     $statement .= ", temp1_$$, temp2_$$ $qual" if $qual;
145   } else {
146     $statement = "SELECT COUNT(*) FROM cust_main $qual";
147   }
148   my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
149   $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
150
151   $total = $sth->fetchrow_arrayref->[0];
152
153   if ( $ncancelled ) {
154     if ( %search ) {
155       $ncancelled = " AND $ncancelled";
156     } else {
157       $ncancelled = " WHERE $ncancelled";
158     }
159   }
160
161   my @just_cust_main;
162   if ( driver_name eq 'mysql' ) {
163     @just_cust_main = qsearch('cust_main', \%search, 'cust_main.*',
164                               ",temp1_$$,temp2_$$ $ncancelled $orderby $limit");
165   } else {
166     @just_cust_main = qsearch('cust_main', \%search, '',   
167                               "$ncancelled $orderby $limit" );
168   }
169   if ( driver_name eq 'mysql' ) {
170     my $sql = "DROP TABLE temp1_$$,temp2_$$;";
171     my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
172     $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
173   }
174   @cust_main = @just_cust_main;
175
176 #  foreach my $cust_main ( @just_cust_main ) {
177 #
178 #    my @one_cust_main;
179 #    $FS::Record::DEBUG=1;
180 #    ( $cache, @one_cust_main ) = jsearch(
181 #      "$monsterjoin",
182 #      { 'custnum' => $cust_main->custnum },
183 #      '',
184 #      '',
185 #      'cust_main',
186 #      'custnum',
187 #    );
188 #    push @cust_main, @one_cust_main;
189 #  }
190
191 } else {
192   @cust_main=();
193   $sortby = \*last_sort;
194
195   push @cust_main, @{&custnumsearch}
196     if $cgi->param('custnum_on') && $cgi->param('custnum_text');
197   push @cust_main, @{&cardsearch}
198     if $cgi->param('card_on') && $cgi->param('card');
199   push @cust_main, @{&lastsearch}
200     if $cgi->param('last_on') && $cgi->param('last_text');
201   push @cust_main, @{&companysearch}
202     if $cgi->param('company_on') && $cgi->param('company_text');
203   push @cust_main, @{&address2search}
204     if $cgi->param('address2_on') && $cgi->param('address2_text');
205   push @cust_main, @{&phonesearch}
206     if $cgi->param('phone_on') && $cgi->param('phone_text');
207   push @cust_main, @{&referralsearch}
208     if $cgi->param('referral_custnum');
209
210   if ( $cgi->param('company_on') && $cgi->param('company_text') ) {
211     $sortby = \*company_sort;
212     push @cust_main, @{&companysearch};
213   }
214
215   @cust_main = grep { $_->ncancelled_pkgs || ! $_->all_pkgs } @cust_main
216     if $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
217        || ( $conf->exists('hidecancelledcustomers')
218              && ! $cgi->param('showcancelledcustomers') );
219
220   my %saw = ();
221   @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
222 }
223
224 my %all_pkgs;
225 if ( $conf->exists('hidecancelledpackages' ) ) {
226   %all_pkgs = map { $_->custnum => [ $_->ncancelled_pkgs ] } @cust_main;
227 } else {
228   %all_pkgs = map { $_->custnum => [ $_->all_pkgs ] } @cust_main;
229 }
230 #%all_pkgs = ();
231
232 if ( scalar(@cust_main) == 1 && ! $cgi->param('referral_custnum') ) {
233   if ( $cgi->param('quickpay') eq 'yes' ) {
234     print $cgi->redirect(popurl(2). "edit/cust_pay.cgi?quickpay=yes;custnum=". $cust_main[0]->custnum);
235   } else {
236     print $cgi->redirect(popurl(2). "view/cust_main.cgi?". $cust_main[0]->custnum);
237   }
238   #exit;
239 } elsif ( scalar(@cust_main) == 0 ) {
240 %>
241 <!-- mason kludge -->
242 <%
243   eidiot "No matching customers found!\n";
244 } else { 
245 %>
246 <!-- mason kludge -->
247 <%
248
249   $total ||= scalar(@cust_main);
250   print header("Customer Search Results",menubar(
251     'Main Menu', popurl(2)
252   )), "$total matching customers found ";
253
254   #begin pager
255   my $pager = '';
256   if ( $total != scalar(@cust_main) && $maxrecords ) {
257     unless ( $offset == 0 ) {
258       $cgi->param('offset', $offset - $maxrecords);
259       $pager .= '<A HREF="'. $cgi->self_url.
260                 '"><B><FONT SIZE="+1">Previous</FONT></B></A> ';
261     }
262     my $poff;
263     my $page;
264     for ( $poff = 0; $poff < $total; $poff += $maxrecords ) {
265       $page++;
266       if ( $offset == $poff ) {
267         $pager .= qq!<FONT SIZE="+2">$page</FONT> !;
268       } else {
269         $cgi->param('offset', $poff);
270         $pager .= qq!<A HREF="!. $cgi->self_url. qq!">$page</A> !;
271       }
272     }
273     unless ( $offset + $maxrecords > $total ) {
274       $cgi->param('offset', $offset + $maxrecords);
275       $pager .= '<A HREF="'. $cgi->self_url.
276                 '"><B><FONT SIZE="+1">Next</FONT></B></A> ';
277     }
278   }
279   #end pager
280   
281   if ( $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
282        || ( $conf->exists('hidecancelledcustomers')
283             && ! $cgi->param('showcancelledcustomers')
284           )
285      ) {
286     $cgi->param('showcancelledcustomers', 1);
287     $cgi->param('offset', 0);
288     print qq!( <a href="!. $cgi->self_url. qq!">show cancelled customers</a> )!;
289   } else {
290     $cgi->param('showcancelledcustomers', 0);
291     $cgi->param('offset', 0);
292     print qq!( <a href="!. $cgi->self_url. qq!">hide cancelled customers</a> )!;
293   }
294   if ( $cgi->param('referral_custnum') ) {
295     $cgi->param('referral_custnum') =~ /^(\d+)$/
296       or eidiot "Illegal referral_custnum\n";
297     my $referral_custnum = $1;
298     my $cust_main = qsearchs('cust_main', { custnum => $referral_custnum } );
299     print '<FORM METHOD=POST>'.
300           qq!<INPUT TYPE="hidden" NAME="referral_custnum" VALUE="$referral_custnum">!.
301           'referrals of <A HREF="'. popurl(2).
302           "view/cust_main.cgi?$referral_custnum\">$referral_custnum: ".
303           ( $cust_main->company
304             || $cust_main->last. ', '. $cust_main->first ).
305           '</A>';
306     print "\n",<<END;
307       <SCRIPT>
308       function changed(what) {
309         what.form.submit();
310       }
311       </SCRIPT>
312 END
313     print ' <SELECT NAME="referral_depth" SIZE="1" onChange="changed(this)">';
314     my $max = 8; #config file
315     $cgi->param('referral_depth') =~ /^(\d*)$/ 
316       or eidiot "Illegal referral_depth";
317     my $referral_depth = $1;
318
319     foreach my $depth ( 1 .. $max ) {
320       print '<OPTION',
321             ' SELECTED'x($depth == $referral_depth),
322             ">$depth";
323     }
324     print "</SELECT> levels deep".
325           '<NOSCRIPT> <INPUT TYPE="submit" VALUE="change"></NOSCRIPT>'.
326           '</FORM>';
327   }
328
329   print "<BR><BR>". $pager. &table(). <<END;
330       <TR>
331         <TH></TH>
332         <TH>(bill) name</TH>
333         <TH>company</TH>
334 END
335
336 if ( defined dbdef->table('cust_main')->column('ship_last') ) {
337   print <<END;
338       <TH>(service) name</TH>
339       <TH>company</TH>
340 END
341 }
342
343 print <<END;
344         <TH>Packages</TH>
345         <TH COLSPAN=2>Services</TH>
346       </TR>
347 END
348
349   my(%saw,$cust_main);
350   my $p = popurl(2);
351   foreach $cust_main (
352     sort $sortby grep(!$saw{$_->custnum}++, @cust_main)
353   ) {
354     my($custnum,$last,$first,$company)=(
355       $cust_main->custnum,
356       $cust_main->getfield('last'),
357       $cust_main->getfield('first'),
358       $cust_main->company,
359     );
360
361     my(@lol_cust_svc);
362     my($rowspan)=0;#scalar( @{$all_pkgs{$custnum}} );
363     foreach ( @{$all_pkgs{$custnum}} ) {
364       #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } );
365       my @cust_svc = $_->cust_svc;
366       push @lol_cust_svc, \@cust_svc;
367       $rowspan += scalar(@cust_svc) || 1;
368     }
369
370     #my($rowspan) = scalar(@{$all_pkgs{$custnum}});
371     my $view;
372     if ( defined $cgi->param('quickpay') && $cgi->param('quickpay') eq 'yes' ) {
373       $view = $p. 'edit/cust_pay.cgi?quickpay=yes;custnum='. $custnum;
374     } else {
375       $view = $p. 'view/cust_main.cgi?'. $custnum;
376     }
377     my $pcompany = $company
378       ? qq!<A HREF="$view"><FONT SIZE=-1>$company</FONT></A>!
379       : '<FONT SIZE=-1>&nbsp;</FONT>';
380     print <<END;
381     <TR>
382       <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$custnum</FONT></A></TD>
383       <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$last, $first</FONT></A></TD>
384       <TD ROWSPAN=$rowspan>$pcompany</TD>
385 END
386     if ( defined dbdef->table('cust_main')->column('ship_last') ) {
387       my($ship_last,$ship_first,$ship_company)=(
388         $cust_main->ship_last || $cust_main->getfield('last'),
389         $cust_main->ship_last ? $cust_main->ship_first : $cust_main->first,
390         $cust_main->ship_last ? $cust_main->ship_company : $cust_main->company,
391       );
392       my $pship_company = $ship_company
393         ? qq!<A HREF="$view"><FONT SIZE=-1>$ship_company</FONT></A>!
394         : '<FONT SIZE=-1>&nbsp;</FONT>';
395       print <<END;
396       <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$ship_last, $ship_first</FONT></A></TD>
397       <TD ROWSPAN=$rowspan>$pship_company</A></TD>
398 END
399     }
400
401     my($n1)='';
402     foreach ( @{$all_pkgs{$custnum}} ) {
403       my $pkgnum = $_->pkgnum;
404 #      my $part_pkg = qsearchs( 'part_pkg', { pkgpart => $_->pkgpart } );
405       my $part_pkg = $_->part_pkg;
406
407       my $pkg = $part_pkg->pkg;
408       my $comment = $part_pkg->comment;
409       my $pkgview = $p. 'view/cust_pkg.cgi?'. $pkgnum;
410       my @cust_svc = @{shift @lol_cust_svc};
411       #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } );
412       my $rowspan = scalar(@cust_svc) || 1;
413
414       print $n1, qq!<TD ROWSPAN=$rowspan><A HREF="$pkgview"><FONT SIZE=-1>$pkg - $comment</FONT></A></TD>!;
415       my($n2)='';
416       foreach my $cust_svc ( @cust_svc ) {
417          my($label, $value, $svcdb) = $cust_svc->label;
418          my($svcnum) = $cust_svc->svcnum;
419          my($sview) = $p.'view';
420          print $n2,qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$label</FONT></A></TD>!,
421                qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$value</FONT></A></TD>!;
422          $n2="</TR><TR>";
423       }
424       #print qq!</TR><TR>\n!;
425       $n1="</TR><TR>";
426     }
427     print "</TR>";
428   }
429  
430   print "</TABLE>$pager</BODY></HTML>";
431
432 }
433
434 #undef $cache; #does this help?
435
436 #
437
438 sub last_sort {
439   lc($a->getfield('last')) cmp lc($b->getfield('last'))
440   || lc($a->first) cmp lc($b->first);
441 }
442
443 sub company_sort {
444   return -1 if $a->company && ! $b->company;
445   return 1 if ! $a->company && $b->company;
446   lc($a->company) cmp lc($b->company)
447   || lc($a->getfield('last')) cmp lc($b->getfield('last'))
448   || lc($a->first) cmp lc($b->first);;
449 }
450
451 sub custnum_sort {
452   $a->getfield('custnum') <=> $b->getfield('custnum');
453 }
454
455 sub custnumsearch {
456
457   my $custnum = $cgi->param('custnum_text');
458   $custnum =~ s/\D//g;
459   $custnum =~ /^(\d{1,23})$/ or eidiot "Illegal customer number\n";
460   $custnum = $1;
461   
462   [ qsearchs('cust_main', { 'custnum' => $custnum } ) ];
463 }
464
465 sub cardsearch {
466
467   my($card)=$cgi->param('card');
468   $card =~ s/\D//g;
469   $card =~ /^(\d{13,16})$/ or eidiot "Illegal card number\n";
470   my($payinfo)=$1;
471
472   [ qsearch('cust_main',{'payinfo'=>$payinfo, 'payby'=>'CARD'}) ];
473 }
474
475 sub referralsearch {
476   $cgi->param('referral_custnum') =~ /^(\d+)$/
477     or eidiot "Illegal referral_custnum";
478   my $cust_main = qsearchs('cust_main', { 'custnum' => $1 } )
479     or eidiot "Customer $1 not found";
480   my $depth;
481   if ( $cgi->param('referral_depth') ) {
482     $cgi->param('referral_depth') =~ /^(\d+)$/
483       or eidiot "Illegal referral_depth";
484     $depth = $1;
485   } else {
486     $depth = 1;
487   }
488   [ $cust_main->referral_cust_main($depth) ];
489 }
490
491 sub lastsearch {
492   my(%last_type);
493   my @cust_main;
494   foreach ( $cgi->param('last_type') ) {
495     $last_type{$_}++;
496   }
497
498   $cgi->param('last_text') =~ /^([\w \,\.\-\']*)$/
499     or eidiot "Illegal last name";
500   my($last)=$1;
501
502   if ( $last_type{'Exact'} || $last_type{'Fuzzy'} ) {
503     push @cust_main, qsearch( 'cust_main',
504                               { 'last' => { 'op'    => 'ILIKE',
505                                             'value' => $last    } } );
506
507     push @cust_main, qsearch( 'cust_main',
508                               { 'ship_last' => { 'op'    => 'ILIKE',
509                                                  'value' => $last    } } )
510       if defined dbdef->table('cust_main')->column('ship_last');
511   }
512
513   if ( $last_type{'Substring'} || $last_type{'All'} ) {
514
515     push @cust_main, qsearch( 'cust_main',
516                               { 'last' => { 'op'    => 'ILIKE',
517                                             'value' => "%$last%" } } );
518
519     push @cust_main, qsearch( 'cust_main',
520                               { 'ship_last' => { 'op'    => 'ILIKE',
521                                                  'value' => "%$last%" } } )
522       if defined dbdef->table('cust_main')->column('ship_last');
523
524   }
525
526   if ( $last_type{'Fuzzy'} || $last_type{'All'} ) {
527
528     &FS::cust_main::check_and_rebuild_fuzzyfiles;
529     my $all_last = &FS::cust_main::all_last;
530
531     my %last;
532     if ( $last_type{'Fuzzy'} || $last_type{'All'} ) { 
533       foreach ( amatch($last, [ qw(i) ], @$all_last) ) {
534         $last{$_}++; 
535       }
536     }
537
538     #if ($last_type{'Sound-alike'}) {
539     #}
540
541     foreach ( keys %last ) {
542       push @cust_main, qsearch('cust_main',{'last'=>$_});
543       push @cust_main, qsearch('cust_main',{'ship_last'=>$_})
544         if defined dbdef->table('cust_main')->column('ship_last');
545     }
546
547   }
548
549   \@cust_main;
550 }
551
552 sub companysearch {
553
554   my(%company_type);
555   my @cust_main;
556   foreach ( $cgi->param('company_type') ) {
557     $company_type{$_}++ 
558   };
559
560   $cgi->param('company_text') =~
561     /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
562       or eidiot "Illegal company";
563   my $company = $1;
564
565   if ( $company_type{'Exact'} || $company_type{'Fuzzy'} ) {
566     push @cust_main, qsearch( 'cust_main',
567                               { 'company' => { 'op'    => 'ILIKE',
568                                                'value' => $company } } );
569
570     push @cust_main, qsearch( 'cust_main',
571                               { 'ship_company' => { 'op'    => 'ILIKE',
572                                                     'value' => $company } } )
573       if defined dbdef->table('cust_main')->column('ship_last');
574   }
575
576   if ( $company_type{'Substring'} || $company_type{'All'} ) {
577
578     push @cust_main, qsearch( 'cust_main',
579                               { 'company' => { 'op'    => 'ILIKE',
580                                                'value' => "%$company%" } } );
581
582     push @cust_main, qsearch( 'cust_main',
583                               { 'ship_company' => { 'op'    => 'ILIKE',
584                                                     'value' => "%$company%" } })
585       if defined dbdef->table('cust_main')->column('ship_last');
586
587   }
588
589   if ( $company_type{'Fuzzy'} || $company_type{'All'} ) {
590
591     &FS::cust_main::check_and_rebuild_fuzzyfiles;
592     my $all_company = &FS::cust_main::all_company;
593
594     my %company;
595     if ( $company_type{'Fuzzy'} || $company_type{'All'} ) { 
596       foreach ( amatch($company, [ qw(i) ], @$all_company ) ) {
597         $company{$_}++;
598       }
599     }
600
601     #if ($company_type{'Sound-alike'}) {
602     #}
603
604     foreach ( keys %company ) {
605       push @cust_main, qsearch('cust_main',{'company'=>$_});
606       push @cust_main, qsearch('cust_main',{'ship_company'=>$_})
607         if defined dbdef->table('cust_main')->column('ship_last');
608     }
609
610   }
611
612   \@cust_main;
613 }
614
615 sub address2search {
616   my @cust_main;
617
618   $cgi->param('address2_text') =~
619     /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
620       or eidiot "Illegal address2";
621   my $address2 = $1;
622
623   push @cust_main, qsearch( 'cust_main',
624                             { 'address2' => { 'op'    => 'ILIKE',
625                                               'value' => $address2 } } );
626   push @cust_main, qsearch( 'cust_main',
627                             { 'address2' => { 'op'    => 'ILIKE',
628                                               'value' => $address2 } } )
629     if defined dbdef->table('cust_main')->column('ship_last');
630
631   \@cust_main;
632 }
633
634 sub phonesearch {
635   my @cust_main;
636
637   my $phone = $cgi->param('phone_text');
638
639   #(no longer really) false laziness with Record::ut_phonen
640   #only works with US/CA numbers...
641   $phone =~ s/\D//g;
642   if ( $phone =~ /^(\d{3})(\d{3})(\d{4})(\d*)$/ ) {
643     $phone = "$1-$2-$3";
644     $phone .= " x$4" if $4;
645   } elsif ( $phone =~ /^(\d{3})(\d{4})$/ ) {
646     $phone = "$1-$2";
647   } elsif ( $phone =~ /^(\d{3,4})$/ ) {
648     $phone = $1;
649   } else {
650     eidiot gettext('illegal_phone'). ": $phone";
651   }
652
653   my @fields = qw(daytime night fax);
654   push @fields, qw(ship_daytime ship_night ship_fax)
655     if defined dbdef->table('cust_main')->column('ship_last');
656
657   for my $field ( @fields ) {
658     push @cust_main, qsearch ( 'cust_main', 
659                                { $field => { 'op'    => 'LIKE',
660                                              'value' => "%$phone%" } } );
661   }
662
663   \@cust_main;
664 }
665
666 %>