remove need for old Locale::SubCountry
[freeside.git] / FS / FS / state.pm
1 package FS::state;
2
3 use strict;
4 use base qw( FS::Record );
5 use FS::Record qw( qsearch qsearchs );
6 use Locale::SubCountry;
7
8 =head1 NAME
9
10 FS::state - Object methods for state/province records
11
12 =head1 SYNOPSIS
13
14   use FS::state;
15
16   $record = new FS::state \%hash;
17   $record = new FS::state { 'column' => 'value' };
18
19   $error = $record->insert;
20
21   $error = $new_record->replace($old_record);
22
23   $error = $record->delete;
24
25   $error = $record->check;
26
27 =head1 DESCRIPTION
28
29 An FS::state object represents a state, province, or other top-level 
30 subdivision of a sovereign nation.  FS::state inherits from FS::Record.  
31 The following fields are currently supported:
32
33 =over 4
34
35 =item statenum
36
37 primary key
38
39 =item country
40
41 two-letter country code
42
43 =item state
44
45 state code/abbreviation/name (as used in cust_location.state)
46
47 =item fips
48
49 FIPS 10-4 code (not including country code)
50
51 =back
52
53 =head1 METHODS
54
55 =cut
56
57 sub table { 'state'; }
58
59 # no external API; this table maintains itself
60
61 sub check {
62   my $self = shift;
63
64   my $error = 
65     $self->ut_numbern('statenum')
66     || $self->ut_alpha('country')
67     || $self->ut_alpha('state')
68     || $self->ut_alpha('fips')
69   ;
70   return $error if $error;
71
72   $self->SUPER::check;
73 }
74
75 =back
76
77 =cut
78
79 our %state2fips = (
80   'AL' => '01',
81   'AK' => '02',
82   'AZ' => '04',
83   'AR' => '05',
84   'CA' => '06',
85   'CO' => '08',
86   'CT' => '09',
87   'DE' => '10',
88   'DC' => '11',
89   'FL' => '12',
90   'GA' => '13',
91   'HI' => '15',
92   'ID' => '16',
93   'IL' => '17',
94   'IN' => '18',
95   'IA' => '19',
96   'KS' => '20',
97   'KY' => '21',
98   'LA' => '22',
99   'ME' => '23',
100   'MD' => '24',
101   'MA' => '25',
102   'MI' => '26',
103   'MN' => '27',
104   'MS' => '28',
105   'MO' => '29',
106   'MT' => '30',
107   'NE' => '31',
108   'NV' => '32',
109   'NH' => '33',
110   'NJ' => '34',
111   'NM' => '35',
112   'NY' => '36',
113   'NC' => '37',
114   'ND' => '38',
115   'OH' => '39',
116   'OK' => '40',
117   'OR' => '41',
118   'PA' => '42',
119   'RI' => '44',
120   'SC' => '45',
121   'SD' => '46',
122   'TN' => '47',
123   'TX' => '48',
124   'UT' => '49',
125   'VT' => '50',
126   'VA' => '51',
127   'WA' => '53',
128   'WV' => '54',
129   'WI' => '55',
130   'WY' => '56',
131
132   'AS' => '60', #American Samoa
133   'GU' => '66', #Guam
134   'MP' => '69', #Northern Mariana Islands
135   'PR' => '72', #Puerto Rico
136   'VI' => '78', #Virgin Islands
137 );
138
139 sub _upgrade_data {
140   # we only need U.S. state codes at this point (for FCC 477 reporting)
141   warn "Updating state FIPS codes...\n";
142   my %existing;
143   foreach my $state ( qsearch('state', {'country'=>'US'}) ) {
144     $existing{$state->country} ||= {};
145     $existing{$state->country}{$state->state} = $state;
146   }
147   foreach my $country_code ('US') {
148     my $country = Locale::SubCountry->new($country_code);
149     next unless $country->has_sub_countries;
150     $existing{$country} ||= {};
151     foreach my $state_code ($country->all_codes) {
152       my $fips = $state2fips{$state_code} || next;
153       my $this_state = $existing{$country_code}{$state_code};
154       if ($this_state) {
155         if ($this_state->fips ne $fips) { # this should never happen...
156           die "guru meditation #414: State FIPS codes shouldn't change";
157           #$this_state->set(fips => $fips);
158           #my $error = $this_state->replace;
159           #die "error updating $country_code/$state_code:\n$error\n" if $error;
160         }
161         delete $existing{$country_code}{$state_code};
162       } else {
163         $this_state = FS::state->new({
164           country => $country_code,
165           state   => $state_code,
166           fips    => $fips,
167         });
168         my $error = $this_state->insert;
169         die "error inserting $country_code/$state_code:\n$error\n" if $error;
170       }
171     }
172     # clean up states that no longer exist (does this ever happen?)
173     foreach my $state (values %{ $existing{$country_code} }) {
174       die "guru meditation #415: State that no longer exists?";
175       #my $error = $state->delete;
176       #die "error removing expired state ".$state->country.'/'.$state->state.
177       #    "\n$error\n" if $error;
178     }
179   } # foreach $country_code
180   '';
181 }
182
183 =head1 BUGS
184
185 =head1 SEE ALSO
186
187 L<FS::Record>
188
189 =cut
190
191 1;
192