X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=007d6e48e40e88616361d6e9f94d5a7ad2f33d7e;hb=e0093aa9840234ddea14f43d7db5e35ccae1eb5d;hp=21f66b92ed8a6564203e142011f5cbd936ed99c9;hpb=5a52da30588e8811338845ce2edaf0631acad479;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 21f66b92e..007d6e48e 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -6,15 +6,15 @@ use base qw( FS::cust_main::Billing FS::cust_main::Billing_Realtime FS::otaker_Mixin FS::payinfo_Mixin FS::cust_main_Mixin FS::Record ); -use vars qw( @EXPORT_OK $DEBUG $me $conf +use vars qw( $DEBUG $me $conf @encrypted_fields - $import $ignore_expired_card + $import + $ignore_expired_card $ignore_illegal_zip $ignore_banned_card $skip_fuzzyfiles @fuzzyfields @paytypes ); use vars qw( $realtime_bop_decline_quiet ); #ugh use Carp; -use Exporter; use Scalar::Util qw( blessed ); use List::Util qw( min ); use Time::Local qw(timelocal); @@ -26,7 +26,6 @@ use Digest::MD5 qw(md5_base64); use Date::Format; #use Date::Manip; use File::Temp qw( tempfile ); -use String::Approx qw(amatch); use Business::CreditCard 0.28; use Locale::Country; use FS::UID qw( getotaker dbh driver_name ); @@ -67,8 +66,6 @@ use FS::agent_payment_gateway; use FS::banned_pay; use FS::TicketSystem; -@EXPORT_OK = qw( smart_search ); - $realtime_bop_decline_quiet = 0; #move to Billing_Realtime # 1 is mostly method/subroutine entry and options @@ -79,6 +76,8 @@ $me = '[FS::cust_main]'; $import = 0; $ignore_expired_card = 0; +$ignore_illegal_zip = 0; +$ignore_banned_card = 0; $skip_fuzzyfiles = 0; @fuzzyfields = ( 'first', 'last', 'company', 'address1' ); @@ -1804,10 +1803,14 @@ sub check { $self->ut_phonen('daytime', $self->country) || $self->ut_phonen('night', $self->country) || $self->ut_phonen('fax', $self->country) - || $self->ut_zip('zip', $self->country) ; return $error if $error; + unless ( $ignore_illegal_zip ) { + $error = $self->ut_zip('zip', $self->country); + return $error if $error; + } + if ( $conf->exists('cust_main-require_phone') && ! length($self->daytime) && ! length($self->night) ) { @@ -1860,10 +1863,13 @@ sub check { $self->ut_phonen('ship_daytime', $self->ship_country) || $self->ut_phonen('ship_night', $self->ship_country) || $self->ut_phonen('ship_fax', $self->ship_country) - || $self->ut_zip('ship_zip', $self->ship_country) ; return $error if $error; + unless ( $ignore_illegal_zip ) { + $error = $self->ut_zip('ship_zip', $self->ship_country); + return $error if $error; + } return "Unit # is required." if $self->ship_address2 =~ /^\s*$/ && $conf->exists('cust_main-require_address2'); @@ -1918,12 +1924,14 @@ sub check { if $self->payinfo !~ /^99\d{14}$/ #token && cardtype($self->payinfo) eq "Unknown"; - my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); - if ( $ban ) { - return 'Banned credit card: banned on '. - time2str('%a %h %o at %r', $ban->_date). - ' by '. $ban->otaker. - ' (ban# '. $ban->bannum. ')'; + unless ( $ignore_banned_card ) { + my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); + if ( $ban ) { + return 'Banned credit card: banned on '. + time2str('%a %h %o at %r', $ban->_date). + ' by '. $ban->otaker. + ' (ban# '. $ban->bannum. ')'; + } } if (length($self->paycvv) && !$self->is_encrypted($self->paycvv)) { @@ -4714,6 +4722,22 @@ sub service_coordinates { scalar(@svc_X) ? ( $svc_X[0]->latitude, $svc_X[0]->longitude ) : () } +=item masked FIELD + +Returns a masked version of the named field + +=cut + +sub masked { +my ($self,$field) = @_; + +# Show last four + +'x'x(length($self->getfield($field))-4). + substr($self->getfield($field), (length($self->getfield($field))-4)); + +} + =back =head1 CLASS METHODS @@ -5002,356 +5026,11 @@ sub _money_table_where { } -=item search HASHREF - -(Class method) - -Returns a qsearch hash expression to search for parameters specified in -HASHREF. Valid parameters are - -=over 4 - -=item agentnum - -=item status - -=item cancelled_pkgs - -bool - -=item signupdate - -listref of start date, end date - -=item payby - -listref - -=item paydate_year - -=item paydate_month - -=item current_balance - -listref (list returned by FS::UI::Web::parse_lt_gt($cgi, 'current_balance')) - -=item cust_fields - -=item flattened_pkgs - -bool - -=back - -=cut - +#for dyanmic FS::$table->search in httemplate/misc/email_customers.html +use FS::cust_main::Search; sub search { - my ($class, $params) = @_; - - my $dbh = dbh; - - my @where = (); - my $orderby; - - ## - # parse agent - ## - - if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) { - push @where, - "cust_main.agentnum = $1"; - } - - ## - # do the same for user - ## - - if ( $params->{'usernum'} =~ /^(\d+)$/ and $1 ) { - push @where, - "cust_main.usernum = $1"; - } - - ## - # parse status - ## - - #prospect ordered active inactive suspended cancelled - if ( grep { $params->{'status'} eq $_ } FS::cust_main->statuses() ) { - my $method = $params->{'status'}. '_sql'; - #push @where, $class->$method(); - push @where, FS::cust_main->$method(); - } - - ## - # parse cancelled package checkbox - ## - - my $pkgwhere = ""; - - $pkgwhere .= "AND (cancel = 0 or cancel is null)" - unless $params->{'cancelled_pkgs'}; - - ## - # parse without census tract checkbox - ## - - push @where, "(censustract = '' or censustract is null)" - if $params->{'no_censustract'}; - - ## - # dates - ## - - foreach my $field (qw( signupdate )) { - - next unless exists($params->{$field}); - - my($beginning, $ending, $hour) = @{$params->{$field}}; - - push @where, - "cust_main.$field IS NOT NULL", - "cust_main.$field >= $beginning", - "cust_main.$field <= $ending"; - - # XXX: do this for mysql and/or pull it out of here - if(defined $hour) { - if ($dbh->{Driver}->{Name} eq 'Pg') { - push @where, "extract(hour from to_timestamp(cust_main.$field)) = $hour"; - } - else { - warn "search by time of day not supported on ".$dbh->{Driver}->{Name}." databases"; - } - } - - $orderby ||= "ORDER BY cust_main.$field"; - - } - - ### - # classnum - ### - - if ( $params->{'classnum'} ) { - - my @classnum = ref( $params->{'classnum'} ) - ? @{ $params->{'classnum'} } - : ( $params->{'classnum'} ); - - @classnum = grep /^(\d*)$/, @classnum; - - if ( @classnum ) { - push @where, '( '. join(' OR ', map { - $_ ? "cust_main.classnum = $_" - : "cust_main.classnum IS NULL" - } - @classnum - ). - ' )'; - } - - } - - ### - # payby - ### - - if ( $params->{'payby'} ) { - - my @payby = ref( $params->{'payby'} ) - ? @{ $params->{'payby'} } - : ( $params->{'payby'} ); - - @payby = grep /^([A-Z]{4})$/, @payby; - - push @where, '( '. join(' OR ', map "cust_main.payby = '$_'", @payby). ' )' - if @payby; - - } - - ### - # paydate_year / paydate_month - ### - - if ( $params->{'paydate_year'} =~ /^(\d{4})$/ ) { - my $year = $1; - $params->{'paydate_month'} =~ /^(\d\d?)$/ - or die "paydate_year without paydate_month?"; - my $month = $1; - - push @where, - 'paydate IS NOT NULL', - "paydate != ''", - "CAST(paydate AS timestamp) < CAST('$year-$month-01' AS timestamp )" -; - } - - ### - # invoice terms - ### - - if ( $params->{'invoice_terms'} =~ /^([\w ]+)$/ ) { - my $terms = $1; - if ( $1 eq 'NULL' ) { - push @where, - "( cust_main.invoice_terms IS NULL OR cust_main.invoice_terms = '' )"; - } else { - push @where, - "cust_main.invoice_terms IS NOT NULL", - "cust_main.invoice_terms = '$1'"; - } - } - - ## - # amounts - ## - - if ( $params->{'current_balance'} ) { - - #my $balance_sql = $class->balance_sql(); - my $balance_sql = FS::cust_main->balance_sql(); - - my @current_balance = - ref( $params->{'current_balance'} ) - ? @{ $params->{'current_balance'} } - : ( $params->{'current_balance'} ); - - push @where, map { s/current_balance/$balance_sql/; $_ } - @current_balance; - - } - - ## - # custbatch - ## - - if ( $params->{'custbatch'} =~ /^([\w\/\-\:\.]+)$/ and $1 ) { - push @where, - "cust_main.custbatch = '$1'"; - } - - ## - # setup queries, subs, etc. for the search - ## - - $orderby ||= 'ORDER BY custnum'; - - # here is the agent virtualization - push @where, $FS::CurrentUser::CurrentUser->agentnums_sql; - - my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : ''; - - my $addl_from = 'LEFT JOIN cust_pkg USING ( custnum ) '; - - my $count_query = "SELECT COUNT(*) FROM cust_main $extra_sql"; - - my $select = join(', ', - 'cust_main.custnum', - FS::UI::Web::cust_sql_fields($params->{'cust_fields'}), - ); - - my(@extra_headers) = (); - my(@extra_fields) = (); - - if ($params->{'flattened_pkgs'}) { - - if ($dbh->{Driver}->{Name} eq 'Pg') { - - $select .= ", array_to_string(array(select pkg from cust_pkg left join part_pkg using ( pkgpart ) where cust_main.custnum = cust_pkg.custnum $pkgwhere),'|') as magic"; - - }elsif ($dbh->{Driver}->{Name} =~ /^mysql/i) { - $select .= ", GROUP_CONCAT(pkg SEPARATOR '|') as magic"; - $addl_from .= " LEFT JOIN part_pkg using ( pkgpart )"; - }else{ - warn "warning: unknown database type ". $dbh->{Driver}->{Name}. - "omitting packing information from report."; - } - - my $header_query = "SELECT COUNT(cust_pkg.custnum = cust_main.custnum) AS count FROM cust_main $addl_from $extra_sql $pkgwhere group by cust_main.custnum order by count desc limit 1"; - - my $sth = dbh->prepare($header_query) or die dbh->errstr; - $sth->execute() or die $sth->errstr; - my $headerrow = $sth->fetchrow_arrayref; - my $headercount = $headerrow ? $headerrow->[0] : 0; - while($headercount) { - unshift @extra_headers, "Package ". $headercount; - unshift @extra_fields, eval q!sub {my $c = shift; - my @a = split '\|', $c->magic; - my $p = $a[!.--$headercount. q!]; - $p; - };!; - } - - } - - my $sql_query = { - 'table' => 'cust_main', - 'select' => $select, - 'hashref' => {}, - 'extra_sql' => $extra_sql, - 'order_by' => $orderby, - 'count_query' => $count_query, - 'extra_headers' => \@extra_headers, - 'extra_fields' => \@extra_fields, - }; - -} - -=item fuzzy_search FUZZY_HASHREF [ HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ ] - -Performs a fuzzy (approximate) search and returns the matching FS::cust_main -records. Currently, I, I, I and/or I may be -specified (the appropriate ship_ field is also searched). - -Additional options are the same as FS::Record::qsearch - -=cut - -sub fuzzy_search { - my( $self, $fuzzy, $hash, @opt) = @_; - #$self - $hash ||= {}; - my @cust_main = (); - - check_and_rebuild_fuzzyfiles(); - foreach my $field ( keys %$fuzzy ) { - - my $all = $self->all_X($field); - next unless scalar(@$all); - - my %match = (); - $match{$_}=1 foreach ( amatch( $fuzzy->{$field}, ['i'], @$all ) ); - - my @fcust = (); - foreach ( keys %match ) { - 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} == scalar(keys %$fuzzy) } @cust_main; - - @cust_main; - -} - -=item masked FIELD - -Returns a masked version of the named field - -=cut - -sub masked { -my ($self,$field) = @_; - -# Show last four - -'x'x(length($self->getfield($field))-4). - substr($self->getfield($field), (length($self->getfield($field))-4)); - + my $class = shift; + FS::cust_main::Search->search(@_); } =back @@ -5360,427 +5039,6 @@ my ($self,$field) = @_; =over 4 -=item smart_search OPTION => VALUE ... - -Accepts the following options: I, the string to search for. The string -will be searched for as a customer number, phone number, name or company name, -as an exact, or, in some cases, a substring or fuzzy match (see the source code -for the exact heuristics used); I, causes smart_search to -skip fuzzy matching when an exact match is found. - -Any additional options are treated as an additional qualifier on the search -(i.e. I). - -Returns a (possibly empty) array of FS::cust_main objects. - -=cut - -sub smart_search { - my %options = @_; - - #here is the agent virtualization - my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql; - - my @cust_main = (); - - my $skip_fuzzy = delete $options{'no_fuzzy_on_exact'}; - 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 - } ); - - } - - # custnum search (also try agent_custid), with some tweaking options if your - # legacy cust "numbers" have letters - } - - if ( $search =~ /^\s*(\d+)\s*$/ - || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+' - && $search =~ /^\s*(\w\w?\d+)\s*$/ - ) - || ( $conf->exists('address1-search' ) - && $search =~ /^\s*(\d+\-?\w*)\s*$/ #i.e. 1234A or 9432-D - ) - ) - { - - my $num = $1; - - if ( $num =~ /^(\d+)$/ && $num <= 2147483647 ) { #need a bigint custnum? wow - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { 'custnum' => $num, %options }, - 'extra_sql' => " AND $agentnums_sql", #agent virtualization - } ); - } - - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { 'agent_custid' => $num, %options }, - 'extra_sql' => " AND $agentnums_sql", #agent virtualization - } ); - - if ( $conf->exists('address1-search') ) { - my $len = length($num); - $num = lc($num); - foreach my $prefix ( '', 'ship_' ) { - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { %options, }, - 'extra_sql' => - ( keys(%options) ? ' AND ' : ' WHERE ' ). - " LOWER(SUBSTRING(${prefix}address1 FROM 1 FOR $len)) = '$num' ". - " AND $agentnums_sql", - } ); - } - } - - } elsif ( $search =~ /^\s*(\S.*\S)\s+\((.+), ([^,]+)\)\s*$/ ) { - - my($company, $last, $first) = ( $1, $2, $3 ); - - # "Company (Last, First)" - #this is probably something a browser remembered, - #so just do an exact search (but case-insensitive, so USPS standardization - #doesn't throw a wrench in the works) - - foreach my $prefix ( '', 'ship_' ) { - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { %options }, - 'extra_sql' => - ( keys(%options) ? ' AND ' : ' WHERE ' ). - join(' AND ', - " LOWER(${prefix}first) = ". dbh->quote(lc($first)), - " LOWER(${prefix}last) = ". dbh->quote(lc($last)), - " LOWER(${prefix}company) = ". dbh->quote(lc($company)), - $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 - - 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 - OR LOWER(ship_last) = $q_value - OR LOWER(ship_company) = $q_value - "; - $sql .= " OR LOWER(address1) = $q_value - OR LOWER(ship_address1) = $q_value - " - if $conf->exists('address1-search'); - $sql .= " )"; - - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => \%options, - 'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization - } ); - - #no exact match, trying substring/fuzzy - #always do substring & fuzzy (unless they're explicity config'ed off) - #getting complaints searches are not returning enough - unless ( @cust_main && $skip_fuzzy || $conf->exists('disable-fuzzy') ) { - - #still some false laziness w/search (was search/cust_main.cgi) - - #substring - - 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%" }, }, - ; - } - - if ( $conf->exists('address1-search') ) { - push @hashrefs, - { 'address1' => { op=>'ILIKE', value=>"%$value%" }, }, - { 'ship_address1' => { op=>'ILIKE', value=>"%$value%" }, }, - ; - } - - foreach my $hashref ( @hashrefs ) { - - push @cust_main, qsearch( { - 'table' => 'cust_main', - 'hashref' => { %$hashref, - %options, - }, - 'extra_sql' => " AND $agentnums_sql", #agent virtualizaiton - } ); - - } - - #fuzzy - 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 ); - } - if ( $conf->exists('address1-search') ) { - push @cust_main, - FS::cust_main->fuzzy_search( { 'address1' => $value }, @fuzopts ); - } - - } - - } - - #eliminate duplicates - my %saw = (); - @cust_main = grep { !$saw{$_->custnum}++ } @cust_main; - - @cust_main; - -} - -=item email_search - -Accepts the following options: I, the email address to search for. The -email address will be searched for as an email invoice destination and as an -svc_acct account. - -#Any additional options are treated as an additional qualifier on the search -#(i.e. I). - -Returns a (possibly empty) array of FS::cust_main objects (but usually just -none or one). - -=cut - -sub email_search { - my %options = @_; - - local($DEBUG) = 1; - - my $email = delete $options{'email'}; - - #we're only being used by RT at the moment... no agent virtualization yet - #my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql; - - my @cust_main = (); - - if ( $email =~ /([^@]+)\@([^@]+)/ ) { - - my ( $user, $domain ) = ( $1, $2 ); - - warn "$me smart_search: searching for $user in domain $domain" - if $DEBUG; - - push @cust_main, - map $_->cust_main, - qsearch( { - 'table' => 'cust_main_invoice', - 'hashref' => { 'dest' => $email }, - } - ); - - push @cust_main, - map $_->cust_main, - grep $_, - map $_->cust_svc->cust_pkg, - qsearch( { - 'table' => 'svc_acct', - 'hashref' => { 'username' => $user, }, - 'extra_sql' => - 'AND ( SELECT domain FROM svc_domain - WHERE svc_acct.domsvc = svc_domain.svcnum - ) = '. dbh->quote($domain), - } - ); - } - - my %saw = (); - @cust_main = grep { !$saw{$_->custnum}++ } @cust_main; - - warn "$me smart_search: found ". scalar(@cust_main). " unique customers" - if $DEBUG; - - @cust_main; - -} - -=item check_and_rebuild_fuzzyfiles - -=cut - -sub check_and_rebuild_fuzzyfiles { - my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; - rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields -} - -=item rebuild_fuzzyfiles - -=cut - -sub rebuild_fuzzyfiles { - - use Fcntl qw(:flock); - - my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; - mkdir $dir, 0700 unless -d $dir; - - foreach my $fuzzy ( @fuzzyfields ) { - - 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: $!"; - - open (CACHE,">$dir/cust_main.$fuzzy.tmp") - or die "can't open $dir/cust_main.$fuzzy.tmp: $!"; - - foreach my $field ( $fuzzy, "ship_$fuzzy" ) { - my $sth = dbh->prepare("SELECT $field FROM cust_main". - " WHERE $field != '' AND $field IS NOT NULL"); - $sth->execute or die $sth->errstr; - - while ( my $row = $sth->fetchrow_arrayref ) { - print CACHE $row->[0]. "\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; - } - -} - -=item all_X - -=cut - -sub all_X { - my( $self, $field ) = @_; - my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; - open(CACHE,"<$dir/cust_main.$field") - or die "can't open $dir/cust_main.$field: $!"; - my @array = map { chomp; $_; } ; - close CACHE; - \@array; -} - =item append_fuzzyfiles FIRSTNAME LASTNAME COMPANY ADDRESS1 =cut @@ -6291,7 +5549,9 @@ sub _upgrade_data { #class method $sth->execute or die $sth->errstr; local($ignore_expired_card) = 1; - local($skip_fuzzyfiles) = 1; + local($ignore_illegal_zip) = 1; + local($ignore_illegal_zip) = 1; + local($ignore_banned_card) = 1; $class->_upgrade_otaker(%opts); }