RT# 83450 - fixed rateplan export
[freeside.git] / FS / FS / cust_location.pm
index a9660d8..21bf92f 100644 (file)
@@ -265,8 +265,15 @@ sub insert {
     return $error;
   }
 
     return $error;
   }
 
-  #false laziness with cust_main, will go away eventually
-  if ( !$import and $conf->config('tax_district_method') ) {
+  # If using tax_district_method, for rows in state of Washington,
+  # without a tax district already specified, queue a job to find
+  # the tax district
+  if (
+       !$import
+    && !$self->district
+    && lc $self->state eq 'wa'
+    && $conf->config('tax_district_method')
+  ) {
 
     my $queue = new FS::queue {
       'job' => 'FS::geocode_Mixin::process_district_update'
 
     my $queue = new FS::queue {
       'job' => 'FS::geocode_Mixin::process_district_update'
@@ -436,6 +443,26 @@ sub check {
     && $conf->exists('prospect_main-alt_address_format')
     && ! $self->location_kind;
 
     && $conf->exists('prospect_main-alt_address_format')
     && ! $self->location_kind;
 
+  # Do not allow bad tax district values in cust_location when
+  # using Washington State district sales tax calculation - would result
+  # in incorrect or missing sales tax on invoices.
+  my $tax_district_method = FS::Conf->new->config('tax_district_method');
+  if (
+    $tax_district_method
+    && $tax_district_method eq 'wa_sales'
+    && $self->district
+  ) {
+    my $cust_main_county = qsearchs(
+      cust_main_county => { district => $self->district }
+    );
+    unless ( ref $cust_main_county ) {
+      return sprintf (
+        'WA State tax district %s does not exist in tax table',
+        $self->district
+      );
+    }
+  }
+
   unless ( $import or qsearch('cust_main_county', {
     'country' => $self->country,
     'state'   => '',
   unless ( $import or qsearch('cust_main_county', {
     'country' => $self->country,
     'state'   => '',
@@ -518,17 +545,51 @@ sub disable_if_unused {
 
 }
 
 
 }
 
-=item move_to
+=item move_pkgs
+
+Returns array of cust_pkg objects that would have their location
+updated by L</move_to> (all packages that have this location as 
+their service address, and aren't canceled, and aren't supplemental 
+to another package, and aren't one-time charges that have already been charged.)
+
+=cut
+
+sub move_pkgs {
+  my $self = shift;
+  my @pkgs = ();
+  # find all packages that have the old location as their service address,
+  # and aren't canceled,
+  # and aren't supplemental to another package
+  # and aren't one-time charges that have already been charged
+  foreach my $cust_pkg (
+    qsearch('cust_pkg', { 
+      'locationnum' => $self->locationnum,
+      'cancel'      => '',
+      'main_pkgnum' => '',
+    })
+  ) {
+    next if $cust_pkg->part_pkg->freq eq '0'
+            and ($cust_pkg->setup || 0) > 0;
+    push @pkgs, $cust_pkg;
+  }
+  return @pkgs;
+}
+
+=item move_to NEW [ move_pkgs => \@move_pkgs ]
 
 Takes a new L<FS::cust_location> object.  Moves all packages that use the 
 existing location to the new one, then sets the "disabled" flag on the old
 location.  Returns nothing on success, an error message on error.
 
 
 Takes a new L<FS::cust_location> object.  Moves all packages that use the 
 existing location to the new one, then sets the "disabled" flag on the old
 location.  Returns nothing on success, an error message on error.
 
+Use option I<move_pkgs> to override the list of packages to update
+(see L</move_pkgs>.)
+
 =cut
 
 sub move_to {
   my $old = shift;
   my $new = shift;
 =cut
 
 sub move_to {
   my $old = shift;
   my $new = shift;
+  my %opt = @_;
   
   warn "move_to:\nFROM:".Dumper($old)."\nTO:".Dumper($new) if $DEBUG;
 
   
   warn "move_to:\nFROM:".Dumper($old)."\nTO:".Dumper($new) if $DEBUG;
 
@@ -560,19 +621,32 @@ sub move_to {
     return '';
   }
 
     return '';
   }
 
-  # find all packages that have the old location as their service address,
-  # and aren't canceled,
-  # and aren't supplemental to another package.
-  my @pkgs = qsearch('cust_pkg', { 
-      'locationnum' => $old->locationnum,
-      'cancel'      => '',
-      'main_pkgnum' => '',
-    });
-  foreach my $cust_pkg (@pkgs) {
-    # don't move one-time charges that have already been charged
-    next if $cust_pkg->part_pkg->freq eq '0'
-            and ($cust_pkg->setup || 0) > 0;
+  my @pkgs;
+  if ($opt{'move_pkgs'}) {
+    @pkgs = @{$opt{'move_pkgs'}};
+    my $pkgerr;
+    foreach my $pkg (@pkgs) {
+      my $pkgnum = $pkg->pkgnum;
+      $pkgerr = "cust_pkg $pkgnum has already been charged"
+        if $pkg->part_pkg->freq eq '0'
+          and ($pkg->setup || 0) > 0;
+      $pkgerr = "cust_pkg $pkgnum is supplemental"
+        if $pkg->main_pkgnum;
+      $pkgerr = "cust_pkg $pkgnum already cancelled"
+        if $pkg->cancel;
+      $pkgerr = "cust_pkg $pkgnum does not use this location"
+        unless $pkg->locationnum eq $old->locationnum;
+      last if $pkgerr;
+    }
+    if ($pkgerr) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Cannot update package location: $pkgerr";
+    }
+  } else {
+    @pkgs = $old->move_pkgs;
+  }
 
 
+  foreach my $cust_pkg (@pkgs) {
     $error = $cust_pkg->change(
       'locationnum' => $new->locationnum,
       'keep_dates'  => 1
     $error = $cust_pkg->change(
       'locationnum' => $new->locationnum,
       'keep_dates'  => 1
@@ -937,13 +1011,17 @@ sub _upgrade_data {
     next if $field eq 'disabled';
     foreach my $location (qsearch({
       table => 'cust_location',
     next if $field eq 'disabled';
     foreach my $location (qsearch({
       table => 'cust_location',
-      extra_sql => " WHERE $field LIKE ' %' OR $field LIKE '% '"
+      extra_sql => " WHERE disabled IS NULL AND ($field LIKE ' %' OR $field LIKE '% ')"
     })) {
       my $error = $location->replace;
       die "$error (fixing whitespace in $field, locationnum ".$location->locationnum.')'
         if $error;
 
     })) {
       my $error = $location->replace;
       die "$error (fixing whitespace in $field, locationnum ".$location->locationnum.')'
         if $error;
 
-      if ( $use_districts ) {
+      if (
+        $use_districts
+        && !$location->district
+        && lc $location->state eq 'wa'
+      ) {
         my $queue = new FS::queue {
           'job' => 'FS::geocode_Mixin::process_district_update'
         };
         my $queue = new FS::queue {
           'job' => 'FS::geocode_Mixin::process_district_update'
         };