fix regionselector for CR in region names for some reason
[freeside.git] / FS / FS / cust_main_county.pm
1 package FS::cust_main_county;
2
3 use strict;
4 use vars qw( @ISA @EXPORT_OK $conf
5              @cust_main_county %cust_main_county $countyflag );
6 use Exporter;
7 use FS::Record qw( qsearch );
8
9 @ISA = qw( FS::Record );
10 @EXPORT_OK = qw( regionselector );
11
12 @cust_main_county = ();
13 $countyflag = '';
14
15 #ask FS::UID to run this stuff for us later
16 $FS::UID::callback{'FS::cust_main_county'} = sub { 
17   $conf = new FS::Conf;
18 };
19
20 =head1 NAME
21
22 FS::cust_main_county - Object methods for cust_main_county objects
23
24 =head1 SYNOPSIS
25
26   use FS::cust_main_county;
27
28   $record = new FS::cust_main_county \%hash;
29   $record = new FS::cust_main_county { 'column' => 'value' };
30
31   $error = $record->insert;
32
33   $error = $new_record->replace($old_record);
34
35   $error = $record->delete;
36
37   $error = $record->check;
38
39   ($county_html, $state_html, $country_html) =
40     FS::cust_main_county::regionselector( $county, $state, $country );
41
42 =head1 DESCRIPTION
43
44 An FS::cust_main_county object represents a tax rate, defined by locale.
45 FS::cust_main_county inherits from FS::Record.  The following fields are
46 currently supported:
47
48 =over 4
49
50 =item taxnum - primary key (assigned automatically for new tax rates)
51
52 =item state
53
54 =item county
55
56 =item country
57
58 =item tax - percentage
59
60 =item taxclass
61
62 =item exempt_amount
63
64 =item setuptax - if 'Y', this tax does not apply to setup fees
65
66 =item recurtax - if 'Y', this tax does not apply to recurring fees
67
68 =back
69
70 =head1 METHODS
71
72 =over 4
73
74 =item new HASHREF
75
76 Creates a new tax rate.  To add the tax rate to the database, see L<"insert">.
77
78 =cut
79
80 sub table { 'cust_main_county'; }
81
82 =item insert
83
84 Adds this tax rate to the database.  If there is an error, returns the error,
85 otherwise returns false.
86
87 =item delete
88
89 Deletes this tax rate from the database.  If there is an error, returns the
90 error, otherwise returns false.
91
92 =item replace OLD_RECORD
93
94 Replaces the OLD_RECORD with this one in the database.  If there is an error,
95 returns the error, otherwise returns false.
96
97 =item check
98
99 Checks all fields to make sure this is a valid tax rate.  If there is an error,
100 returns the error, otherwise returns false.  Called by the insert and replace
101 methods.
102
103 =cut
104
105 sub check {
106   my $self = shift;
107
108   $self->exempt_amount(0) unless $self->exempt_amount;
109
110   $self->ut_numbern('taxnum')
111     || $self->ut_anything('state')
112     || $self->ut_textn('county')
113     || $self->ut_text('country')
114     || $self->ut_float('tax')
115     || $self->ut_textn('taxclass') # ...
116     || $self->ut_money('exempt_amount')
117     || $self->ut_textn('taxname')
118     || $self->ut_enum('setuptax', [ '', 'Y' ] )
119     || $self->ut_enum('recurtax', [ '', 'Y' ] )
120   ;
121
122 }
123
124 sub taxname {
125   my $self = shift;
126   if ( $self->dbdef_table->column('taxname') ) {
127     return $self->setfield('taxname', $_[0]) if @_;
128     return $self->getfield('taxname');
129   }  
130   return '';
131 }
132
133 sub setuptax {
134   my $self = shift;
135   if ( $self->dbdef_table->column('setuptax') ) {
136     return $self->setfield('setuptax', $_[0]) if @_;
137     return $self->getfield('setuptax');
138   }  
139   return '';
140 }
141
142 sub recurtax {
143   my $self = shift;
144   if ( $self->dbdef_table->column('recurtax') ) {
145     return $self->setfield('recurtax', $_[0]) if @_;
146     return $self->getfield('recurtax');
147   }  
148   return '';
149 }
150
151 =back
152
153 =head1 SUBROUTINES
154
155 =over 4
156
157 =item regionselector [ COUNTY STATE COUNTRY [ PREFIX [ ONCHANGE ] ] ]
158
159 =cut
160
161 sub regionselector {
162   my ( $selected_county, $selected_state, $selected_country,
163        $prefix, $onchange ) = @_;
164
165   $prefix = '' unless defined $prefix;
166
167   $countyflag = 0;
168
169 #  unless ( @cust_main_county ) { #cache 
170     @cust_main_county = qsearch('cust_main_county', {} );
171     foreach my $c ( @cust_main_county ) {
172       $countyflag=1 if $c->county;
173       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
174       $cust_main_county{$c->country}{$c->state}{$c->county} = 1;
175     }
176 #  }
177   $countyflag=1 if $selected_county;
178
179   my $script_html = <<END;
180     <SCRIPT>
181     function opt(what,value,text) {
182       var optionName = new Option(text, value, false, false);
183       var length = what.length;
184       what.options[length] = optionName;
185     }
186     function ${prefix}country_changed(what) {
187       country = what.options[what.selectedIndex].text;
188       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
189           what.form.${prefix}state.options[i] = null;
190 END
191       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
192
193   foreach my $country ( sort keys %cust_main_county ) {
194     $script_html .= "\nif ( country == \"$country\" ) {\n";
195     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
196       my( $dstate = $state ) =~ s/\n//g;
197       my $text = $dstate || '(n/a)';
198       $script_html .= qq!opt(what.form.${prefix}state, "$dstate", "$text");\n!;
199     }
200     $script_html .= "}\n";
201   }
202
203   $script_html .= <<END;
204     }
205     function ${prefix}state_changed(what) {
206 END
207
208   if ( $countyflag ) {
209     $script_html .= <<END;
210       state = what.options[what.selectedIndex].text;
211       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
212       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
213           what.form.${prefix}county.options[i] = null;
214 END
215
216     foreach my $country ( sort keys %cust_main_county ) {
217       $script_html .= "\nif ( country == \"$country\" ) {\n";
218       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
219         $script_html .= "\nif ( state == \"$state\" ) {\n";
220           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
221           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
222             my $text = $county || '(n/a)';
223             $script_html .=
224               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
225           }
226         $script_html .= "}\n";
227       }
228       $script_html .= "}\n";
229     }
230   }
231
232   $script_html .= <<END;
233     }
234     </SCRIPT>
235 END
236
237   my $county_html = $script_html;
238   if ( $countyflag ) {
239     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$onchange">!;
240     $county_html .= '</SELECT>';
241   } else {
242     $county_html .=
243       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$selected_county">!;
244   }
245
246   my $state_html = qq!<SELECT NAME="${prefix}state" !.
247                    qq!onChange="${prefix}state_changed(this); $onchange">!;
248   foreach my $state ( sort keys %{ $cust_main_county{$selected_country} } ) {
249     my $text = $state || '(n/a)';
250     my $selected = $state eq $selected_state ? 'SELECTED' : '';
251     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
252   }
253   $state_html .= '</SELECT>';
254
255   $state_html .= '</SELECT>';
256
257   my $country_html = qq!<SELECT NAME="${prefix}country" !.
258                      qq!onChange="${prefix}country_changed(this); $onchange">!;
259   my $countrydefault = $conf->config('countrydefault') || 'US';
260   foreach my $country (
261     sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
262       keys %cust_main_county
263   ) {
264     my $selected = $country eq $selected_country ? ' SELECTED' : '';
265     $country_html .= "\n<OPTION$selected>$country</OPTION>"
266   }
267   $country_html .= '</SELECT>';
268
269   ($county_html, $state_html, $country_html);
270
271 }
272
273 =back
274
275 =head1 BUGS
276
277 regionselector?  putting web ui components in here?  they should probably live
278 somewhere else...
279
280 =head1 SEE ALSO
281
282 L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill>, schema.html from the base
283 documentation.
284
285 =cut
286
287 1;
288