5 Tests automatic lookup of Washington sales tax districts and rates.
7 This will set up two tax classes. One of them (class A) has only the sales
8 tax. The other (class B) will have an additional, manually created tax.
10 This will test the following sequence of actions (running
11 process_district_update() after each one):
13 1. Enter a customer in Washington for which there is not yet a district tax
15 2. Add a manual tax in class B.
16 3. Rename the sales taxes.
17 4. Delete the sales taxes.
18 5. Change the sales tax rates (to simulate a change in the actual rate).
19 6. Set the sales tax rate to zero.
21 The correct result is always for there to be exactly one tax entry for this
22 district in each class, with the correct rate, except after step 6, when
23 the rate should remain at zero (because setting the rate to zero is a way
24 of manually disabling the tax).
29 use Test::More tests => 6;
32 use FS::cust_location;
33 use FS::cust_main_county;
36 my $FS= FS::Test->new;
41 my @taxes = $FS->qsearch('cust_main_county', { city => 'SEATTLE' });
42 my @classes = $FS->qsearch('part_pkg_taxclass');
43 foreach (@taxes, @classes) {
45 BAIL_OUT("can't flush existing taxes: $error") if $error;
46 # we won't charge any of the taxes in this script so FK errors shouldn't
51 @classes = map { FS::part_pkg_taxclass->new({ taxclass => $_ }) }
55 BAIL_OUT("can't create tax class: $error") if $error;
58 # should be an FS::Test method to temporarily set this up
59 my $conf = FS::Conf->new;
60 $conf->set('tax_district_method', 'wa_sales');
61 $conf->set('tax_district_taxname', 'Sales Tax');
62 $conf->set('enable_taxclasses', 1);
65 my $cust = $FS->new_customer('WA Taxes');
66 # Sea-Tac International Airport
67 $cust->bill_location->address1('17801 International Blvd');
68 $cust->bill_location->city('Seattle');
69 $cust->bill_location->zip('98158');
70 $cust->bill_location->state('WA');
71 $cust->bill_location->country('US');
73 $error = $cust->insert;
74 BAIL_OUT("can't create test customer: $error") if $error;
76 my $location = $cust->bill_location;
79 # after each action, refresh the tax district (as if we'd added/edited a
80 # customer in that district) and then get the new list of defined taxes
82 # remember all the taxes from the last test
83 @prev_taxes = map { $_ && FS::cust_main_county->new({$_->hash}) } @taxes;
85 eval { FS::geocode_Mixin::process_district_update( 'FS::cust_location',
86 $location->locationnum )};
88 BAIL_OUT("can't update tax district: $error") if $error;
90 $location = $location->replace_old;
91 @taxes = $FS->qsearch({
92 table => 'cust_main_county',
93 hashref => { city => 'SEATTLE' },
94 order_by => 'ORDER BY taxclass ASC, taxname ASC', # make them easily findable
98 # and then we'll want to check that the total number of taxes is what we
102 is( scalar(@taxes), $num, "Number of taxes" )
103 or BAIL_OUT('Wrong number of tax records, can\'t continue.');
106 subtest 'Step 1: Initial tax lookup' => sub {
109 ok( $location->district, 'Found district '.$location->district);
112 and $taxes[0]->taxname eq 'Sales Tax'
113 and $taxes[0]->taxclass eq 'ClassA'
114 and $taxes[0]->district eq $location->district
115 and $taxes[0]->source eq 'wa_sales'
116 and $taxes[0]->tax > 0
118 'ClassA tax = '.$taxes[0]->tax )
119 or diag explain($taxes[0]);
121 and $taxes[1]->taxname eq 'Sales Tax'
122 and $taxes[1]->taxclass eq 'ClassB'
123 and $taxes[1]->district eq $location->district
124 and $taxes[1]->source eq 'wa_sales'
125 and $taxes[1]->tax > 0
127 'ClassB tax = '.$taxes[1]->tax )
128 or diag explain($taxes[1]);
131 # "Sales Tax" sorts before "USF"; this is intentional.
132 subtest 'Step 2: Add manual tax ("USF") to ClassB' => sub {
135 my $manual_tax = $taxes[1]->new({
142 $error = $manual_tax->insert;
143 BAIL_OUT("can't create manual tax: $error") if $error;
147 is_deeply( $taxes[0], $prev_taxes[0], 'ClassA sales tax was not changed' );
148 is_deeply( $taxes[1], $prev_taxes[1], 'ClassB sales tax was not changed' );
150 and $taxes[2]->taxname eq 'USF'
151 and $taxes[2]->taxclass eq 'ClassB'
152 and $taxes[2]->tax == 17
153 and $taxes[2]->source eq ''
154 ), 'Manual tax was accepted')
155 or diag explain($taxes[2]);
158 subtest 'Step 3: Rename ClassB sales tax. Does it stay renamed?' => sub {
161 $taxes[1]->set('taxname', 'Renamed Sales Tax');
162 $error = $taxes[1]->replace;
163 BAIL_OUT("can't rename tax: $error") if $error;
168 is_deeply( $taxes[0], $prev_taxes[0], 'ClassA sales tax was not changed' );
170 and $taxes[1]->taxname eq 'Renamed Sales Tax'
171 and $taxes[1]->source eq 'wa_sales'
172 and $taxes[1]->tax == $prev_taxes[1]->tax
173 ), $taxes[1]->taxclass .' sales tax was renamed')
174 or diag explain($taxes[1]);
175 is_deeply( $taxes[2], $prev_taxes[2], 'ClassB manual tax was not changed' );
178 subtest 'Step 4: Remove ClassB sales tax. Is it recreated?' => sub {
181 $error = $taxes[1]->delete;
182 BAIL_OUT("can't delete tax: $error") if $error;
186 is_deeply( $taxes[0], $prev_taxes[0], 'ClassA sales tax was not changed' );
188 and $taxes[1]->taxname eq 'Sales Tax'
189 and $taxes[1]->source eq 'wa_sales'
190 and $taxes[1]->tax == $prev_taxes[1]->tax
191 ), $taxes[1]->taxclass .' sales tax was deleted and recreated')
192 or diag explain($taxes[1]);
193 is_deeply( $taxes[2], $prev_taxes[2], 'ClassB manual tax was not changed' );
196 subtest 'Step 5: Simulate a change in tax rate. Do the taxes update?' => sub {
198 my $correct_rate = $taxes[0]->tax;
199 foreach (@taxes[0,1]) {
200 if ($_ and $_->source eq 'wa_sales') {
201 $_->tax( $correct_rate + 1 );
202 $error = $_->replace;
203 BAIL_OUT("can't change tax rate: $error") if $error;
208 ok( ( $taxes[0] and $taxes[0]->tax == $correct_rate
209 and $taxes[1] and $taxes[1]->tax == $correct_rate
210 ), 'Tax was reset to correct rate' )
211 or diag("Tax rates: ".$taxes[0]->tax.', '.$taxes[1]->tax);
212 is_deeply( $taxes[2], $prev_taxes[2], 'ClassB manual tax was not changed' );
215 subtest 'Step 6: Manually disable Class A sales tax. Does it stay disabled?' => sub {
218 $taxes[0]->set('tax', 0);
219 $error = $taxes[0]->replace;
220 BAIL_OUT("can't change tax rate to zero: $error") if $error;
224 ok( $taxes[0]->tax == 0, 'ClassA sales tax remains at zero')
225 or diag("Tax rate: ".$taxes[1]->tax);
226 is_deeply( $taxes[1], $prev_taxes[1], 'ClassB sales tax was not changed' );
227 is_deeply( $taxes[2], $prev_taxes[2], 'ClassB manual tax was not changed' );
230 $conf->delete('tax_district_method');
231 $conf->delete('tax_district_taxname');
232 $conf->delete('enable_taxclasses');