add option to only charge for CDRs starting with a disposition prefix, RT#73195
[freeside.git] / FS / FS / geocode_Mixin.pm
index 57d8ca0..09b1131 100644 (file)
@@ -3,7 +3,7 @@ package FS::geocode_Mixin;
 use strict;
 use vars qw( $DEBUG $me );
 use Carp;
 use strict;
 use vars qw( $DEBUG $me );
 use Carp;
-use Locale::Country;
+use Locale::Country ();
 use Geo::Coder::Googlev3; #compile time for now, until others are supported
 use FS::Record qw( qsearchs qsearch );
 use FS::Conf;
 use Geo::Coder::Googlev3; #compile time for now, until others are supported
 use FS::Record qw( qsearchs qsearch );
 use FS::Conf;
@@ -126,25 +126,41 @@ sub location_label {
       $notfirst++;
     }
   }
       $notfirst++;
     }
   }
-  $line .= $separator. &$escape(code2country($self->country))
+  $line .= $separator. &$escape($self->country_full)
     if $self->country ne $cydefault;
 
   $line;
 }
 
     if $self->country ne $cydefault;
 
   $line;
 }
 
-=item set_coord [ PREFIX ]
+=item country_full
+
+Returns the full country name.
+
+=cut
+
+sub country_full {
+  my $self = shift;
+  $self->code2country($self->get('country'));
+}
+
+sub code2country {
+  my( $self, $country ) = @_;
+
+  #a hash?  not expecting an explosion of business from unrecognized countries..
+  return 'KKTC' if $country eq 'XC';
+                                           
+  Locale::Country::code2country($country);
+}
+
+=item set_coord
 
 Look up the coordinates of the location using (currently) the Google Maps
 API and set the 'latitude' and 'longitude' fields accordingly.
 
 
 Look up the coordinates of the location using (currently) the Google Maps
 API and set the 'latitude' and 'longitude' fields accordingly.
 
-PREFIX, if specified, will be prepended to all location field names,
-including latitude and longitude.
-
 =cut
 
 sub set_coord {
   my $self = shift;
 =cut
 
 sub set_coord {
   my $self = shift;
-  my $pre = scalar(@_) ? shift : '';
 
   #my $module = FS::Conf->new->config('geocode_module') || 'Geo::Coder::Googlev3';
 
 
   #my $module = FS::Conf->new->config('geocode_module') || 'Geo::Coder::Googlev3';
 
@@ -152,11 +168,11 @@ sub set_coord {
 
   my $location = eval {
     $geocoder->geocode( location =>
 
   my $location = eval {
     $geocoder->geocode( location =>
-      $self->get($pre.'address1'). ','.
-      ( $self->get($pre.'address2') ? $self->get($pre.'address2').',' : '' ).
-      $self->get($pre.'city'). ','.
-      $self->get($pre.'state'). ','.
-      code2country($self->get($pre.'country'))
+      $self->get('address1'). ','.
+      ( $self->get('address2') ? $self->get('address2').',' : '' ).
+      $self->get('city'). ','.
+      $self->get('state'). ','.
+      $self->country_full
     );
   };
   if ( $@ ) {
     );
   };
   if ( $@ ) {
@@ -166,9 +182,9 @@ sub set_coord {
 
   my $geo_loc = $location->{'geometry'}{'location'} or return;
   if ( $geo_loc->{'lat'} && $geo_loc->{'lng'} ) {
 
   my $geo_loc = $location->{'geometry'}{'location'} or return;
   if ( $geo_loc->{'lat'} && $geo_loc->{'lng'} ) {
-    $self->set($pre.'latitude',  $geo_loc->{'lat'} );
-    $self->set($pre.'longitude', $geo_loc->{'lng'} );
-    $self->set($pre.'coord_auto', 'Y');
+    $self->set('latitude',  $geo_loc->{'lat'} );
+    $self->set('longitude', $geo_loc->{'lng'} );
+    $self->set('coord_auto', 'Y');
   }
 
 }
   }
 
 }
@@ -232,10 +248,14 @@ Queueable function to update the tax district code using the selected method
 
 =cut
 
 
 =cut
 
+# this is run from the job queue so I'm not transactionizing it.
+
 sub process_district_update {
   my $class = shift;
   my $id = shift;
 
 sub process_district_update {
   my $class = shift;
   my $id = shift;
 
+  local $DEBUG = 1;
+
   eval "use FS::Misc::Geo qw(get_district); use FS::Conf; use $class;";
   die $@ if $@;
   die "$class has no location data" if !$class->can('location_hash');
   eval "use FS::Misc::Geo qw(get_district); use FS::Conf; use $class;";
   die $@ if $@;
   die "$class has no location data" if !$class->can('location_hash');
@@ -253,15 +273,55 @@ sub process_district_update {
     my $error = $self->replace;
     die $error if $error;
 
     my $error = $self->replace;
     die $error if $error;
 
-    my %hash = map { $_ => $tax_info->{$_} } 
+    my %hash = map { $_ => uc( $tax_info->{$_} ) } 
       qw( district city county state country );
       qw( district city county state country );
-    my $old = qsearchs('cust_main_county', \%hash);
-    if ( $old ) {
-      my $new = new FS::cust_main_county { $old->hash, %$tax_info };
-      warn "updating tax rate for district ".$tax_info->{'district'} if $DEBUG;
-      $error = $new->replace($old);
-    }
-    else {
+    $hash{'source'} = $method; # apply the update only to taxes we maintain
+
+    my @old = qsearch('cust_main_county', \%hash);
+    if ( @old ) {
+      # prune any duplicates rather than updating them
+      my %keep; # key => cust_main_county record
+      foreach my $cust_main_county (@old) {
+        my $key = join('.', $cust_main_county->city ,
+                            $cust_main_county->district ,
+                            $cust_main_county->taxclass
+                      );
+        if ( exists $keep{$key} ) {
+          my $disable_this = $cust_main_county;
+          # prefer records that have a tax name
+          if ( $cust_main_county->taxname and not $keep{$key}->taxname ) {
+            $disable_this = $keep{$key};
+            $keep{$key} = $cust_main_county;
+          }
+          # disable by setting the rate to zero, and setting source to null
+          # so it doesn't get auto-updated in the future. don't actually 
+          # delete it, that produces orphan records
+          warn "disabling tax rate #" .
+            $disable_this->taxnum .
+            " because it's a duplicate for $key\n"
+            if $DEBUG;
+          # by setting its rate to zero, and never updating
+          # it again
+          $disable_this->set('tax' => 0);
+          $disable_this->set('source' => '');
+          $error = $disable_this->replace;
+          die $error if $error;
+        }
+
+        $keep{$key} ||= $cust_main_county;
+
+      }
+      foreach my $key (keys %keep) {
+        my $cust_main_county = $keep{$key};
+        warn "updating tax rate #".$cust_main_county->taxnum.
+          " for $key" if $DEBUG;
+        # update the tax rate only
+        $cust_main_county->set('tax', $tax_info->{'tax'});
+        $error ||= $cust_main_county->replace;
+      }
+    } else {
+      # make a new tax record, and mark it so we can find it later
+      $tax_info->{'source'} = $method;
       my $new = new FS::cust_main_county $tax_info;
       warn "creating tax rate for district ".$tax_info->{'district'} if $DEBUG;
       $error = $new->insert;
       my $new = new FS::cust_main_county $tax_info;
       warn "creating tax rate for district ".$tax_info->{'district'} if $DEBUG;
       $error = $new->insert;