Merge branch 'master' of git.freeside.biz:/home/git/freeside
[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   local $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   FS::cust_main_county->lock_table;
44   if ($method eq 'wa_sales') {
45     # download the update file
46     my $now = DateTime->now;
47     my $yr = $now->year;
48     my $qt = $now->quarter;
49     my $file = "Rates${yr}Q${qt}.zip";
50     my $url = 'http://dor.wa.gov/downloads/Add_Data/'.$file;
51     my $dir = tempdir();
52     chdir($dir);
53     my $ua = LWP::UserAgent->new;
54     warn "Downloading $url...\n" if $DEBUG;
55     my $response = $ua->get($url);
56     if ( ! $response->is_success ) {
57       die $response->status_line;
58     }
59     write_file($file, $response->decoded_content);
60
61     # parse it
62     system('unzip', $file);
63     $file =~ s/\.zip$/.csv/;
64     if (! -f $file) {
65       die "$file not found in zip archive.\n";
66     }
67     open my $fh, '<', $file
68       or die "couldn't open $file: $!\n";
69     my $csv = Text::CSV->new;
70     my $header = $csv->getline($fh);
71     $csv->column_names(@$header);
72     # columns we care about are headed 'Code' and 'Rate'
73
74     my $total_changed = 0;
75     my $total_skipped = 0;
76     while ( !$csv->eof ) {
77       my $line = $csv->getline_hr($fh);
78       my $district = $line->{Code} or next;
79       $district = sprintf('%04d', $district);
80       my $tax = sprintf('%.1f', $line->{Rate} * 100);
81       my $changed = 0;
82       my $skipped = 0;
83       # find rate(s) in this country+state+district+taxclass that have the
84       # wa_sales flag and the configured taxname, and haven't been disabled.
85       my @rates = qsearch('cust_main_county', {
86           country   => 'US',
87           state     => 'WA', # this is specific to WA
88           district  => $district,
89           taxname   => $taxname,
90           source    => 'wa_sales',
91           tax       => { op => '>', value => '0' },
92       });
93       foreach my $rate (@rates) {
94         if ( $rate->tax == $tax ) {
95           $skipped++;
96         } else {
97           $rate->set('tax', $tax);
98           my $error = $rate->replace;
99           die "error updating district $district: $error\n" if $error;
100           $changed++;
101         }
102       } # foreach $taxclass
103       print "$district: updated $changed, skipped $skipped\n"
104         if $DEBUG and ($changed or $skipped);
105       $total_changed += $changed;
106       $total_skipped += $skipped;
107     }
108     print "Updated $total_changed tax rates.\nSkipped $total_skipped unchanged rates.\n" if $DEBUG;
109     dbh->commit;
110   } # else $method isn't wa_sales, no other methods exist yet
111   '';
112 }
113
114 1;