X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=FS%2FFS%2FMisc%2FGeo.pm;h=6bc71fc84551eeac346e008091eea9b557bd97a2;hb=468c9e660eb0edb2033f0f8dbb4458f20280082c;hp=5d6f33cb728cf4c155c8aa32da9695f47daee650;hpb=2b2aa5664742a134da11862a7cedb37d25524423;p=freeside.git diff --git a/FS/FS/Misc/Geo.pm b/FS/FS/Misc/Geo.pm index 5d6f33cb7..6bc71fc84 100644 --- a/FS/FS/Misc/Geo.pm +++ b/FS/FS/Misc/Geo.pm @@ -2,7 +2,7 @@ package FS::Misc::Geo; use strict; use base qw( Exporter ); -use vars qw( $DEBUG @EXPORT_OK ); +use vars qw( $DEBUG @EXPORT_OK $conf ); use LWP::UserAgent; use HTTP::Request; use HTTP::Request::Common qw( GET POST ); @@ -10,15 +10,19 @@ use HTML::TokeParser; use URI::Escape 3.31; use Data::Dumper; +FS::UID->install_callback( sub { + $conf = new FS::Conf; +} ); + $DEBUG = 0; -@EXPORT_OK = qw( get_censustract get_district ); +@EXPORT_OK = qw( get_district ); =head1 NAME FS::Misc::Geo - routines to fetch geographic information -=head1 FUNCTIONS +=head1 CLASS METHODS =over 4 @@ -30,7 +34,8 @@ codes) or an error message. =cut -sub get_censustract { +sub get_censustract_ffiec { + my $class = shift; my $location = shift; my $year = shift; @@ -45,7 +50,7 @@ sub get_censustract { my $res = $ua->request( GET( $url ) ); warn $res->as_string - if $DEBUG > 1; + if $DEBUG > 2; unless ($res->code eq '200') { @@ -87,12 +92,12 @@ sub get_censustract { btnSearch => 'Search', ); warn join("\n", @ffiec_args ) - if $DEBUG; + if $DEBUG > 1; push @{ $ua->requests_redirectable }, 'POST'; $res = $ua->request( POST( $url, \@ffiec_args ) ); warn $res->as_string - if $DEBUG > 1; + if $DEBUG > 2; unless ($res->code eq '200') { @@ -102,7 +107,7 @@ sub get_censustract { my @id = qw( MSACode StateCode CountyCode TractCode ); $content = $res->content; - warn $res->content if $DEBUG > 1; + warn $res->content if $DEBUG > 2; $p = new HTML::TokeParser \$content; my $prefix = 'UcGeoResult11_lb'; my $compare = @@ -127,7 +132,7 @@ sub get_censustract { } #unless ($res->code eq '200') - return "FFIEC Geocoding error: $error" if $error; + die "FFIEC Geocoding error: $error\n" if $error; $return->{'statecode'} . $return->{'countycode'} . $return->{'tractcode'}; } @@ -201,12 +206,12 @@ sub wa_sales { my $query_string = join($delim, @args ); $url .= "?$query_string"; - warn "\nrequest: $url\n\n" if $DEBUG; + warn "\nrequest: $url\n\n" if $DEBUG > 1; my $res = $ua->request( GET( "$url?$query_string" ) ); warn $res->as_string - if $DEBUG > 1; + if $DEBUG > 2; if ($res->code ne '200') { $error = $res->message; @@ -253,7 +258,7 @@ sub wa_sales { # just to make sure if ( $return->{'district'} =~ /^\d+$/ and $return->{'tax'} =~ /^.\d+$/ ) { $return->{'tax'} *= 100; #percentage - warn Dumper($return) if $DEBUG; + warn Dumper($return) if $DEBUG > 1; return $return; } else { @@ -267,6 +272,131 @@ sub wa_sales { die "WA tax district lookup error: $error"; } +sub standardize_usps { + my $class = shift; + + eval "use Business::US::USPS::WebTools::AddressStandardization"; + die $@ if $@; + + my $location = shift; + if ( $location->{country} ne 'US' ) { + # soft failure + warn "standardize_usps not for use in country ".$location->{country}."\n"; + $location->{addr_clean} = ''; + return $location; + } + my $userid = $conf->config('usps_webtools-userid'); + my $password = $conf->config('usps_webtools-password'); + my $verifier = Business::US::USPS::WebTools::AddressStandardization->new( { + UserID => $userid, + Password => $password, + Testing => 0, + } ) or die "error starting USPS WebTools\n"; + + my($zip5, $zip4) = split('-',$location->{'zip'}); + + my %usps_args = ( + FirmName => $location->{company}, + Address2 => $location->{address1}, + Address1 => $location->{address2}, + City => $location->{city}, + State => $location->{state}, + Zip5 => $zip5, + Zip4 => $zip4, + ); + warn join('', map "$_: $usps_args{$_}\n", keys %usps_args ) + if $DEBUG > 1; + + my $hash = $verifier->verify_address( %usps_args ); + + warn $verifier->response + if $DEBUG > 1; + + die "USPS WebTools error: ".$verifier->{error}{description} ."\n" + if $verifier->is_error; + + my $zip = $hash->{Zip5}; + $zip .= '-' . $hash->{Zip4} if $hash->{Zip4} =~ /\d/; + + { company => $hash->{FirmName}, + address1 => $hash->{Address2}, + address2 => $hash->{Address1}, + city => $hash->{City}, + state => $hash->{State}, + zip => $zip, + country => 'US', + addr_clean=> 'Y' } +} + +my %ezlocate_error = ( # USA_Geo_002 documentation + 10 => 'State not found', + 11 => 'City not found', + 12 => 'Invalid street address', + 14 => 'Street name not found', + 15 => 'Address range does not exist', + 16 => 'Ambiguous address', + 17 => 'Intersection not found', #unused? +); + +sub standardize_ezlocate { + my $self = shift; + my $location = shift; + my $class; + #if ( $location->{country} eq 'US' ) { + # $class = 'USA_Geo_004Tool'; + #} + #elsif ( $location->{country} eq 'CA' ) { + # $class = 'CAN_Geo_001Tool'; + #} + #else { # shouldn't be a fatal error, just pass through unverified address + # warn "standardize_teleatlas: address lookup in '".$location->{country}. + # "' not available\n"; + # return $location; + #} + #my $path = $conf->config('teleatlas-path') || ''; + #local @INC = (@INC, $path); + #eval "use $class;"; + #if ( $@ ) { + # die "Loading $class failed:\n$@". + # "\nMake sure the TeleAtlas Perl SDK is installed correctly.\n"; + #} + + $class = 'Geo::EZLocate'; # use our own library + eval "use $class"; + die $@ if $@; + + my $userid = $conf->config('ezlocate-userid') + or die "no ezlocate-userid configured\n"; + my $password = $conf->config('ezlocate-password') + or die "no ezlocate-password configured\n"; + + my $tool = $class->new($userid, $password); + my $match = $tool->findAddress( + $location->{address1}, + $location->{city}, + $location->{state}, + $location->{zip}, #12345-6789 format is allowed + ); + warn "ezlocate returned match:\n".Dumper($match) if $DEBUG > 1; + # error handling - B codes indicate success + die $ezlocate_error{$match->{MAT_STAT}}."\n" + unless $match->{MAT_STAT} =~ /^B\d$/; + + { + address1 => $match->{STD_ADDR}, + address2 => $location->{address2}, + city => $match->{STD_CITY}, + state => $match->{STD_ST}, + country => $location->{country}, + zip => $match->{STD_ZIP}.'-'.$match->{STD_P4}, + latitude => $match->{MAT_LAT}, + longitude => $match->{MAT_LON}, + censustract => $match->{FIPS_ST}.$match->{FIPS_CTY}. + sprintf('%04.2f',$match->{CEN_TRCT}), + addr_clean => 'Y', + }; +} + =back =cut