RT# 83450 - fixed rateplan export
[freeside.git] / FS / FS / cust_main_county.pm
index 924f9bf..9582334 100644 (file)
@@ -4,6 +4,7 @@ use base qw( FS::Record );
 use strict;
 use vars qw( @EXPORT_OK $conf
              @cust_main_county %cust_main_county $countyflag ); # $cityflag );
+use Carp qw( croak );
 use Exporter;
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::cust_bill_pkg;
@@ -12,6 +13,7 @@ use FS::cust_pkg;
 use FS::part_pkg;
 use FS::cust_tax_exempt;
 use FS::cust_tax_exempt_pkg;
+use FS::Log;
 use FS::upgrade_journal;
 
 @EXPORT_OK = qw( regionselector );
@@ -560,6 +562,40 @@ sub taxline {
   return $tax_item;
 }
 
+=head1 find_wa_tax_dupes
+
+Return a list of cust_main_county Record objects that are detected
+as duplicate washington state sales tax rows (source=wa_state)
+within their respective tax classes
+
+=cut
+
+sub find_wa_tax_dupes {
+  my %cust_main_county;
+  my @dupes;
+
+  for my $row ( qsearch( cust_main_county => { source => 'wa_sales' } ) ) {
+    my $taxclass = $row->taxclass || 'none';
+    $cust_main_county{$taxclass} ||= {};
+
+    my $district = $row->district || 'none';
+    $cust_main_county{$taxclass}->{$district} ||= [];
+
+    push @{ $cust_main_county{$taxclass}->{$district} }, $row;
+  }
+
+  for my $taxclass ( keys %cust_main_county ) {
+    for my $district ( keys %{ $cust_main_county{$taxclass} } ) {
+      my $tax_rows = $cust_main_county{$taxclass}->{$district};
+      if ( scalar @$tax_rows > 1 ) {
+        push @dupes, @$tax_rows;
+      }
+    }
+  }
+
+  @dupes;
+}
+
 =back
 
 =head1 SUBROUTINES
@@ -683,33 +719,74 @@ END
 }
 
 sub _merge_into {
-  # for internal use: takes another cust_main_county object, transfers
-  # all existing references to this record to that one, and deletes this
-  # one.
-  my $record = shift;
-  my $other = shift or die "record to merge into must be provided";
-  my $new_taxnum = $other->taxnum;
-  my $old_taxnum = $record->taxnum;
-  if ($other->tax != $record->tax or
-      $other->exempt_amount != $record->exempt_amount) {
-    # don't assume these are the same.
-    warn "Found duplicate taxes (#$new_taxnum and #$old_taxnum) but they have different rates and can't be merged.\n";
-  } else {
-    warn "Merging tax #$old_taxnum into #$new_taxnum\n";
-    foreach my $table (qw(
-      cust_bill_pkg_tax_location
-      cust_bill_pkg_tax_location_void
-      cust_tax_exempt_pkg
-      cust_tax_exempt_pkg_void
-    )) {
-      foreach my $row (qsearch($table, { 'taxnum' => $old_taxnum })) {
-        $row->set('taxnum' => $new_taxnum);
-        my $error = $row->replace;
-        die $error if $error;
+  # For internal use:
+  #
+  # When given two cust_main_county row objects, rewrite all database foreign
+  # key references referring to $row_to_merge->taxnum as references to
+  # $row_to_keep->taxnum, so $row_to_merge can be safely deleted from
+  # cust_main_county
+  #
+  # Usage (class method):
+  #    $row_to_merge->_merge_into( $row_to_keep )
+  #
+  # Usage (package function):
+  #    FS::cust_main_county::_merge_into( $row_to_merge, $row_to_keep )
+  #
+  # Optionally, allow merge when records don't match
+  #      (useful during tax table update routines)
+  #     $row_to_merge->_merge_info(
+  #       $row_to_keep,
+  #       { identical_record_check => 0 }
+  #     );
+
+  my $row_to_merge = shift;
+  my $row_to_keep  = shift
+    or croak 'record to merge into must be provided';
+
+  my $args = shift || { identical_record_check => 1 };
+  croak 'invalid arguments hashref' unless ref $args;
+
+  my $log = FS::Log->new('FS::cust_main_county');
+
+  my $keep_taxnum  = $row_to_keep->taxnum;
+  my $merge_taxnum = $row_to_merge->taxnum;
+
+  if (
+    $args->{identical_record_check}
+    && (
+      $row_to_keep->tax != $row_to_merge->tax
+      || $row_to_keep->exempt_amount != $row_to_merge->exempt_amount
+    )
+  ) {
+    my $msg = "Found duplicate taxes (#$keep_taxnum and #$merge_taxnum) "
+            . "but they have different rates and can't be merged.";
+    $log->warn( $msg );
+    warn "$msg\n";
+    return;
+  }
+
+  my $msg = "Merging tax #$merge_taxnum into #$keep_taxnum";
+  $log->warn( $msg );
+  warn "$msg\n";
+
+  foreach my $table (qw(
+    cust_bill_pkg_tax_location
+    cust_bill_pkg_tax_location_void
+    cust_tax_exempt_pkg
+    cust_tax_exempt_pkg_void
+  )) {
+    foreach my $row (qsearch($table, { 'taxnum' => $merge_taxnum })) {
+      $row->set('taxnum' => $keep_taxnum);
+      if ( my $error = $row->replace ) {
+        $log->error( $error );
+        die $error;
       }
     }
-    my $error = $record->delete;
-    die $error if $error;
+  }
+
+  if ( my $error = $row_to_merge->delete ) {
+    $log->error( $error );
+    die $error;
   }
 }
 
@@ -717,7 +794,7 @@ sub _upgrade_data {
   my $class = shift;
   # assume taxes in Washington with district numbers, and null name, or 
   # named 'sales tax', are looked up via the wa_sales method. mark them.
-  my $journal = 'cust_main_county__source_wa_sales';
+  my $journal = 'cust_main_county__source_wa_sales_201611';
   if (!FS::upgrade_journal->is_done($journal)) {
     my @taxes = qsearch({
         'table'     => 'cust_main_county',
@@ -737,26 +814,6 @@ sub _upgrade_data {
   }
   my @key_fields = (qw(city county state country district taxname taxclass));
 
-  # remove duplicates (except disabled records)
-  my @duplicate_sets = qsearch({
-    table => 'cust_main_county',
-    select => FS::Record::group_concat_sql('taxnum', ',') . ' AS taxnums, ' .
-              join(',', @key_fields),
-    extra_sql => ' WHERE tax > 0
-      GROUP BY city, county, state, country, district, taxname, taxclass
-      HAVING COUNT(*) > 1'
-  });
-  warn "Found ".scalar(@duplicate_sets)." set(s) of duplicate tax definitions\n"
-    if @duplicate_sets;
-  foreach my $set (@duplicate_sets) {
-    my @taxnums = split(',', $set->get('taxnums'));
-    my $first = FS::cust_main_county->by_key(shift @taxnums);
-    foreach my $taxnum (@taxnums) {
-      my $record = FS::cust_main_county->by_key($taxnum);
-      $record->_merge_into($first);
-    }
-  }
-
   # trim whitespace and convert to uppercase in the 'city' field.
   foreach my $record (qsearch({
     table => 'cust_main_county',
@@ -782,7 +839,7 @@ sub _upgrade_data {
   my $district_taxname = $conf->config('tax_district_taxname');
   $journal = 'cust_main_county__district_taxclass';
   if (!FS::upgrade_journal->is_done($journal)
-      and $conf->config('enable_taxclasses')) {
+      and $conf->exists('enable_taxclasses')) {
     eval "use FS::part_pkg_taxclass";
     my @taxes = qsearch({
         'table'     => 'cust_main_county',
@@ -856,6 +913,27 @@ sub _upgrade_data {
       }   
     }
   }
+
+  # remove duplicates (except disabled records)
+  my @duplicate_sets = qsearch({
+    table => 'cust_main_county',
+    select => FS::Record::group_concat_sql('taxnum', ',') . ' AS taxnums, ' .
+              join(',', @key_fields),
+    extra_sql => ' WHERE tax > 0
+      GROUP BY city, county, state, country, district, taxname, taxclass
+      HAVING COUNT(*) > 1'
+  });
+  warn "Found ".scalar(@duplicate_sets)." set(s) of duplicate tax definitions\n"
+    if @duplicate_sets;
+  foreach my $set (@duplicate_sets) {
+    my @taxnums = split(',', $set->get('taxnums'));
+    my $first = FS::cust_main_county->by_key(shift @taxnums);
+    foreach my $taxnum (@taxnums) {
+      my $record = FS::cust_main_county->by_key($taxnum);
+      $record->_merge_into($first);
+    }
+  }
+
  
   '';
 }