9 Tool to update city/district sales tax rates in I<cust_main_county> from
10 the Washington State Department of Revenue website.
12 This does not handle address standardization or geocoding addresses to
13 Washington tax district codes. That logic is still in FS::Misc::Geo,
14 and relies on a heinous screen-scraping of the interactive search tool.
15 This script just updates the cust_main_county records that already exist
16 with the latest quarterly tax rates.
20 -c <taxclass>: operate only on records with the named tax class. If not
21 specified, this operates on records with null tax class.
23 -t <taxname>: operate only on records with that tax name. If not specified,
24 it operates on records where the tax name is either null or 'Tax'.
28 use FS::Record qw(qsearch qsearchs dbh);
29 use FS::cust_main_county;
30 use FS::UID qw(adminsuidsetup);
33 use File::Temp 'tempdir';
34 use File::Slurp qw(read_file write_file);
39 my $user = shift or die usage();
41 # download the update file
42 my $now = DateTime->now;
44 my $qt = $now->quarter;
45 my $file = "Rates${yr}Q${qt}.zip";
46 my $url = 'http://dor.wa.gov/downloads/Add_Data/'.$file;
49 my $ua = LWP::UserAgent->new;
50 warn "Downloading $url...\n";
51 my $response = $ua->get($url);
52 if ( ! $response->is_success ) {
53 die $response->status_line;
55 write_file($file, $response->decoded_content);
58 system('unzip', $file);
59 $file =~ s/\.zip$/.csv/;
61 die "$file not found in zip archive.\n";
63 open my $fh, '<', $file
64 or die "couldn't open $file: $!\n";
65 my $csv = Text::CSV->new;
66 my $header = $csv->getline($fh);
67 $csv->column_names(@$header);
68 # columns we care about are headed 'Code' and 'Rate'
71 adminsuidsetup($user) or die "bad username '$user'\n";
72 $FS::UID::AutoCommit = 0;
74 $opt_c ||= ''; # taxclass
75 $opt_t ||= ''; # taxname
76 my $total_changed = 0;
77 my $total_skipped = 0;
78 while ( !$csv->eof ) {
79 my $line = $csv->getline_hr($fh);
80 my $district = $line->{Code} or next;
81 my $tax = sprintf('%.1f', $line->{Rate} * 100);
84 # find all rates in WA
85 my @rates = qsearch('cust_main_county', {
87 state => 'WA', # this is specific to WA
88 district => $district,
93 push @rates, qsearch('cust_main_county', {
95 state => 'WA', # this is specific to WA
96 district => $district,
101 foreach my $rate (@rates) {
102 if ( $rate->tax == $tax ) {
105 $rate->set('tax', $tax);
106 my $error = $rate->replace;
107 die "error updating district $district: $error\n" if $error;
111 print "$district: updated $changed, skipped $skipped\n"
112 if $changed or $skipped;
113 $total_changed += $changed;
114 $total_skipped += $skipped;
116 print "Updated $total_changed tax rates.\nSkipped $total_skipped unchanged rates.\n";
121 wa_tax_rate_update [ -c taxclass ] [ -t taxname ] user