2c021b80e8abd252d4bb27103e1f6982131c4d89
[freeside.git] / FS / FS / Cron / tax_rate_update.pm
1 #!/usr/bin/perl
2
3 =head1 NAME
4
5 FS::Cron::tax_rate_update
6
7 =head1 DESCRIPTION
8
9 Cron routine to update city/district sales tax rates in I<cust_main_county>.
10 Currently supports sales tax in the state of Washington.
11
12 =cut
13
14 use strict;
15 use warnings;
16 use FS::Conf;
17 use FS::Record qw(qsearch qsearchs dbh);
18 use FS::cust_main_county;
19 use FS::part_pkg_taxclass;
20 use DateTime;
21 use LWP::UserAgent;
22 use File::Temp 'tempdir';
23 use File::Slurp qw(read_file write_file);
24 use Text::CSV;
25 use Exporter;
26
27 our @EXPORT_OK = qw(tax_rate_update);
28 our $DEBUG = 0;
29
30 sub tax_rate_update {
31   my %opt = @_;
32
33   my $oldAutoCommit = $FS::UID::AutoCommit;
34   $FS::UID::AutoCommit = 0;
35   my $dbh = dbh;
36
37   my $conf = FS::Conf->new;
38   my $method = $conf->config('tax_district_method');
39   return if !$method;
40
41   my $taxname = $conf->config('tax_district_taxname') || '';
42
43   if ($method eq 'wa_sales') {
44     # download the update file
45     my $now = DateTime->now;
46     my $yr = $now->year;
47     my $qt = $now->quarter;
48     my $file = "Rates${yr}Q${qt}.zip";
49     my $url = 'http://dor.wa.gov/downloads/Add_Data/'.$file;
50     my $dir = tempdir();
51     chdir($dir);
52     my $ua = LWP::UserAgent->new;
53     warn "Downloading $url...\n" if $DEBUG;
54     my $response = $ua->get($url);
55     if ( ! $response->is_success ) {
56       die $response->status_line;
57     }
58     write_file($file, $response->decoded_content);
59
60     # parse it
61     system('unzip', $file);
62     $file =~ s/\.zip$/.csv/;
63     if (! -f $file) {
64       die "$file not found in zip archive.\n";
65     }
66     open my $fh, '<', $file
67       or die "couldn't open $file: $!\n";
68     my $csv = Text::CSV->new;
69     my $header = $csv->getline($fh);
70     $csv->column_names(@$header);
71     # columns we care about are headed 'Code' and 'Rate'
72
73     my $total_changed = 0;
74     my $total_skipped = 0;
75     while ( !$csv->eof ) {
76       my $line = $csv->getline_hr($fh);
77       my $district = $line->{Code} or next;
78       $district = sprintf('%04d', $district);
79       my $tax = sprintf('%.1f', $line->{Rate} * 100);
80       my $changed = 0;
81       my $skipped = 0;
82       # find rate(s) in this country+state+district+taxclass that have the
83       # wa_sales flag and the configured taxname, and haven't been disabled.
84       my @rates = qsearch('cust_main_county', {
85           country   => 'US',
86           state     => 'WA', # this is specific to WA
87           district  => $district,
88           taxname   => $taxname,
89           source    => 'wa_sales',
90           tax       => { op => '>', value => '0' },
91       });
92       foreach my $rate (@rates) {
93         if ( $rate->tax == $tax ) {
94           $skipped++;
95         } else {
96           $rate->set('tax', $tax);
97           my $error = $rate->replace;
98           die "error updating district $district: $error\n" if $error;
99           $changed++;
100         }
101       } # foreach $taxclass
102       print "$district: updated $changed, skipped $skipped\n"
103         if $DEBUG and ($changed or $skipped);
104       $total_changed += $changed;
105       $total_skipped += $skipped;
106     }
107     print "Updated $total_changed tax rates.\nSkipped $total_skipped unchanged rates.\n" if $DEBUG;
108     dbh->commit;
109   } # else $method isn't wa_sales, no other methods exist yet
110   '';
111 }
112
113 1;