cust_location.state,
cust_location.zip
',
- hashref => {
- state => 'WA',
- district => undef,
- },
addl_from => '
LEFT JOIN cust_main USING (custnum)
LEFT JOIN cust_pkg ON cust_location.locationnum = cust_pkg.locationnum
',
- extra_sql => sprintf(
- '
+ extra_sql => sprintf(q{
+ WHERE cust_location.state = 'WA'
+ AND (
+ cust_location.district IS NULL
+ or cust_location.district = ''
+ )
AND cust_pkg.pkgnum IS NOT NULL
AND (
cust_pkg.cancel > %s
OR cust_pkg.cancel IS NULL
)
- ', time()
+ }, time()
),
);
)
);
+ # The checks themselves will fully log details about the problem,
+ # so simple error message is sufficient here
+ log_error_and_die('abort tax table update, sanity checks failed')
+ unless wa_sales_update_tax_table_sanity_check();
+
$args->{temp_dir} ||= tempdir();
$args->{filename} ||= wa_sales_fetch_xlsx_file( $args );
for my $taxclass ( FS::part_pkg_taxclass->taxclass_names ) {
$taxclass ||= undef; # trap empty string when taxclasses are disabled
- my %cust_main_county =
- map { $_->district => $_ }
+ # Dupe detection/remediation:
+ #
+ # Previous code for washington state tax district was creating
+ # duplicate entries for tax districts. This could lead to customers
+ # being double-taxed
+ #
+ # The following code detects and eliminates duplicates that
+ # were created by wa_sales district code (source=wa_sales)
+ # before updating the tax table with the newly downloaded
+ # data
+
+ my %cust_main_county;
+ my %cust_main_county_dupe;
+
+ for my $row (
qsearch(
cust_main_county => {
- district => { op => '!=', value => undef },
- state => 'WA',
- country => 'US',
- source => 'wa_sales',
- taxclass => $taxclass,
+ source => 'wa_sales',
+ district => { op => '!=', value => undef },
+ tax_class => $taxclass,
}
- );
+ )
+ ) {
+ my $district = $row->district;
+
+ # Row belongs to a known dupe group of districts
+ if ( $cust_main_county_dupe{$district} ) {
+ push @{ $cust_main_county_dupe{$district} }, $row;
+ next;
+ }
+
+ # Row is the first seen dupe for the given district
+ if ( $cust_main_county{$district} ) {
+ $cust_main_county_dupe{$district} = [
+ delete $cust_main_county{$district},
+ $row
+ ];
+ next;
+ }
+
+ # Row is the first seen with this district
+ $cust_main_county{$district} = $row;
+ }
+
+ # Merge any dupes, place resulting non-dupe row in %cust_main_county
+ # Merge, even if one of the dupes has a $0 tax, or some other
+ # variation on tax row data. Data for this row will get corrected
+ # during the following tax import
+ for my $dupe_district_aref ( values %cust_main_county_dupe ) {
+ my $row_to_keep = shift @$dupe_district_aref;
+ while ( my $row_to_merge = shift @$dupe_district_aref ) {
+ $row_to_merge->_merge_into(
+ $row_to_keep,
+ { identical_record_check => 0 },
+ );
+ }
+ $cust_main_county{$row_to_keep->district} = $row_to_keep;
+ }
for my $district ( @{ $args->{tax_districts} } ) {
if ( my $row = $cust_main_county{ $district->{district} } ) {
# creating an entire separate set of tax rows with
# the new taxname, update the taxname on existing records
- if (
- $row->tax == ( $district->{tax_combined} * 100 )
- && $row->taxname eq $args->{taxname}
- && uc $row->county eq uc $district->{county}
- && uc $row->city eq uc $district->{city}
- ) {
- $same_count++;
- next;
+ {
+ # Supress warning on taxname comparison, when taxname is undef
+ no warnings 'uninitialized';
+
+ if (
+ $row->tax == ( $district->{tax_combined} * 100 )
+ && $row->taxname eq $args->{taxname}
+ && uc $row->county eq uc $district->{county}
+ && uc $row->city eq uc $district->{city}
+ ) {
+ $same_count++;
+ next;
+ }
}
$row->city( uc $district->{city} );
}
+=head2 wa_sales_update_tax_table_sanity_check
+
+There should be no duplicate tax table entries in the tax table,
+with the same district value, within a tax class, where source=wa_sales.
+
+If there are, custome taxes may have been user-entered in the
+freeside UI, and incorrectly labelled as source=wa_sales. Or, the
+dupe record may have been created by issues with older wa_sales code.
+
+If these dupes exist, the sysadmin must solve the problem by hand
+with the freeeside-wa-tax-table-resolve script
+
+Returns 1 unless problem sales tax entries are detected
+
+=cut
+
+sub wa_sales_update_tax_table_sanity_check {
+ FS::cust_main_county->find_wa_tax_dupes ? 0 : 1;
+}
+
sub log {
state $log = FS::Log->new('tax_rate_update');
$log;
sub log_error_and_die {
my $log_message = shift;
&log()->error( $log_message );
+ warn( "$log_message\n" );
die( "$log_message\n" );
}