d4c0f14d437a87a9616301a9cf2af8da44969f09
[freeside.git] / httemplate / elements / location.html
1 <%doc>
2
3 Example:
4
5   include( '/elements/location.html',
6              'object'         => $cust_location
7              'prefix'         => $pre, # prefixed to form field names
8              'onchange'       => $javascript,
9              'geocode'        => $geocode, #passed through
10              'censustract'    => $censustract, #passed through
11              'no_asterisks'   => 0, #set true to disable the red asterisks next
12                                     #to required fields
13              'address1_label' => 'Address', #label for address
14              'enable_coords'  => 1, #show latitude/longitude fields
15              'enable_district' => 1, #show tax district field
16              'enable_censustract' => 1, #show censustract field
17              
18          )
19
20 </%doc>
21
22 <SCRIPT SRC="<% $fsurl %>elements/jquery.deserialize.min.js"></SCRIPT>
23 <SCRIPT SRC="<% $fsurl %>elements/polyfill.js"></SCRIPT>
24
25 % if ( $opt{'alt_format'} ) {
26
27 <TR>
28     <<%$th%> ALIGN="right">Location&nbsp;kind</<%$th%>>
29     <TD>
30     <% include('/elements/select.html',
31                  'cgi'        => $cgi,
32                  'field'      => 'location_kind',
33                  'id'         => 'location_kind',
34                  'disabled'   => $disabled,
35                  #'style'      => \@style,
36                  'options'    => \@location_kind_options,
37                  'labels'     => $location_kind_labels,
38                  'curr_value' => scalar($cgi->param('location_kind'))
39                                    || $object->get('location_kind'),
40               )
41     %>
42     </TD>
43   </TR>
44
45 % } 
46
47 % if ( $label_prefix eq '_location' ) {
48
49     <TR>
50       <TH ALIGN="right" ><% $opt{'locationname_label'} || emt('Location ID') %></TD>
51       <TD COLSPAN=7>
52         <INPUT TYPE     = "text"
53                NAME     = "<%$pre%>locationname"
54                ID       = "<%$pre%>locationname"
55                VALUE    = "<% $object->get('locationname') |h %>"
56                SIZE     = 24
57                onChange = "<% $onchange %>"
58                <% $disabled %>
59                <% $style %>
60         >
61       </TD>
62     </TR>
63
64 % } else {
65     <& hidden.html, field => $pre.'locationname', value => $object->get('locationname') &>
66
67 % }
68
69 <TR>
70   <<%$th%> STYLE="width:16ex" ALIGN="right"><%$r%><% $opt{'address1_label'} || emt('Address') %></<%$th%>>
71   <TD COLSPAN=7>
72     <INPUT TYPE     = "text"
73            NAME     = "<%$pre%>address1"
74            ID       = "<%$pre%>address1"
75            VALUE    = "<% $object->get('address1') |h %>"
76            SIZE     = 54
77            onChange = "<% $onchange %>"
78            <% $disabled %>
79            <% $style %>
80     >
81   </TD>
82 </TR>
83
84 % if ( ! $opt{'alt_format'} ) { #regular format
85
86 <TR>
87       <TH ALIGN="right"><FONT ID="<% $pre %>address2_required" color="#ff0000" <% $address2_label_style %>>*</FONT>&nbsp;<FONT ID="<% $pre %>address2_label" <% $address2_label_style %>><B>Unit&nbsp;#</B></FONT></TD>
88       <TD COLSPAN=7>
89         <INPUT TYPE     = "text"
90                NAME     = "<%$pre%>address2"
91                ID       = "<%$pre%>address2"
92                VALUE    = "<% $object->get('address2') |h %>"
93                SIZE     = 54
94                onChange = "<% $onchange %>"
95                <% $disabled %>
96                <% $style %>
97         >
98       </TD>
99 </TR>
100
101 % } else { # alternate format
102
103 <& hidden.html, field => $pre.'address2', value => $object->get('address2') &>
104
105 <TR>
106     <<%$th%> ALIGN="right">Unit&nbsp;type&nbsp;and&nbsp;#</<%$th%>>
107     <TD COLSPAN=7>
108
109 %     my $location_type = scalar($cgi->param('location_type'))
110 %                           || $object->get('location_type');
111 %     #my $location_number = scalar($cgi->param('location_number'))
112 %     #                        || $object->get($pre.'location_number');
113 %
114 %   if ( $object->get($pre.'address2') && ! $location_type ) {
115 %   }
116 %
117 %     if ( 1 ) { #ikano, switch on via config
118 %       tie my %location_types, 'Tie::IxHash',
119 %         FS::part_export::ikano->location_types;
120         <% include('/elements/select.html',
121                      'cgi'        => $cgi,
122                      'field'      => 'location_type',
123                      'id'         => 'location_type',
124                      'disabled'   => $disabled,
125                      #'style'      => \@style,
126                      'options'    => [ keys %location_types ],
127                      'labels'     => \%location_types,
128                      'curr_value' => $location_type,
129                      'onchange'   => 'location_type_changed',
130                   )
131         %>
132         <SCRIPT TYPE="text/javascript">
133           function location_type_changed (what) {
134             if ( what.options[what.selectedIndex].value == '' ) {
135               what.form.location_number.disabled = true;
136               what.form.location_number.style.backgroundColor = '#dddddd';
137             } else {
138               what.form.location_number.disabled = false;
139               what.form.location_number.style.backgroundColor = '#ffffff';
140             }
141           }
142         </SCRIPT>
143 %     } else {
144         <INPUT TYPE  = "text" 
145                NAME  = "location_type" 
146                ID    = "location_type"
147                VALUE = "<% $location_type |h %>"
148                SIZE  = "10"
149                <% $disabled %>
150                <% $style %>
151         >
152 %     }
153
154     <INPUT TYPE="text" 
155                NAME  = "location_number"
156                ID    = "location_number"
157                VALUE = "<% scalar($cgi->param('location_number')) || $object->get('location_number') |h %>"
158                SIZE  = "5"
159                <% $disabled || ($location_type ? '' : 'DISABLED') %>
160                <% $style %>
161         >
162
163 %    #XXX i don't work so well when the dropdown is changed :/  i probably need to be triggered by "default service address"
164 %    $alt_err =~ s/(ship_)?address2/'<B>'.encode_entities($object->get($1.'address2')).'<\/B>'/e;
165      <% $alt_err %>
166
167     </TD>
168
169 </TR>
170
171 % } 
172
173
174 <TR>
175   <<%$th%> ALIGN="right">
176 % unless ($conf->exists('cust_main-no_city_in_address')) {
177   <% $r %><% mt('City') |h %>
178 % }
179   </<%$th%>>
180   <TD WIDTH="1"><% include('/elements/city.html', %select_hash, 'text_style' => \@style ) %></TD>
181   <<%$th%> ALIGN="right" WIDTH="1" ID="<%$pre%>countylabel" <%$county_style%>><%$r%>County</<%$th%>>
182   <TD WIDTH="1"><% include('/elements/select-county.html', %select_hash ) %></TD>
183   <<%$th%> ALIGN="right" WIDTH="1"><%$r%><% mt('State') |h %></<%$th%>>
184   <TD WIDTH="1">
185     <% include('/elements/select-state.html', %select_hash ) %>
186   </TD>
187   <<%$th%> ALIGN="right" WIDTH="1"><%$r%><% mt('Zip') |h %></<%$th%>>
188   <TD>
189     <INPUT TYPE     = "text"
190            NAME     = "<%$pre%>zip"
191            ID       = "<%$pre%>zip"
192            VALUE    = "<% $object->get('zip') |h %>"
193            SIZE     = 11
194            onChange = "<% $onchange %>"
195            <% $disabled %>
196            <% $style %>
197     >
198   </TD>
199 </TR>
200
201 <TR>
202   <<%$th%> ALIGN="right"><%$r%><% mt('Country') |h %></<%$th%>>
203   <TD COLSPAN=6><% include('/elements/select-country.html', %select_hash ) %></TD>
204 </TR>
205
206 % if ( $opt{enable_coords} ) {
207 <TR>
208   <TH ALIGN="right"><% mt('Latitude') |h %></TD>
209   <TD COLSPAN=7>
210     <INPUT TYPE  = "text"
211            NAME  = "<%$pre%>latitude"
212            ID    = "<%$pre%>latitude"
213            VALUE = "<% $object->get('latitude') |h %>"
214            <% $disabled %>
215            <% $style %>
216     >
217     <FONT SIZE="-1" COLOR="#666666"><% mt('Longitude') |h %></FONT>
218     <INPUT TYPE  = "text"
219            NAME  = "<%$pre%>longitude"
220            ID    = "<%$pre%>longitude"
221            VALUE = "<% $object->get('longitude') |h %>"
222            <% $disabled %>
223            <% $style %>
224     >
225   </TD>
226 </TR>
227 % } else {
228 %   foreach (qw(latitude longitude)) {
229 <& hidden.html, field => $pre.$_, value => $object->get($_) &>
230 %   }
231 % }
232 %
233 % foreach (qw(coord_auto geocode censustract censusyear)) {
234   <& hidden.html, field => $pre.$_, value => $object->get($_) &>
235 % }
236 %
237 % if ( $opt{enable_censustract} ) {
238 <TR>
239   <TH ALIGN="right">Census&nbsp;tract</TD>
240   <TD COLSPAN=8>
241     <INPUT TYPE="text" SIZE=15
242            ID="<% $pre %>enter_censustract" 
243            NAME="<% $pre %>enter_censustract" 
244            VALUE="<% $object->censustract |h %>">
245     <FONT SIZE="-1" COLOR="#333333"><% '(automatic)' %></FONT>
246   </TD>
247 </TR>
248 % }
249 % if ( $opt{enable_district} and $conf->config('tax_district_method') ) {
250   <TR>
251     <TD ALIGN="right">Tax&nbsp;district</TD>
252     <TD COLSPAN=8>
253       <INPUT TYPE="text" SIZE=15
254              NAME="<%$pre%>district" 
255              ID="<%$pre%>district"
256              VALUE="<% $object->district |h %>">
257     <% '(automatic)' %>
258     </TD>
259   </TR>
260 % } else {
261   <& hidden.html, field => $pre.'district', value => $object->get('district') &>
262 % }
263
264 %# For address standardization:
265 %# keep a clean copy of the address so we know if we need
266 %# to re-standardize
267 % foreach (qw(locationname address1 city state country zip latitude
268 %             longitude censustract district addr_clean
269 %             ) ) {
270 <& hidden.html, field => 'old_'.$pre.$_, value => $object->get($_) &>
271 % }
272 %# Placeholders
273 <& hidden.html, field => $pre.'cachenum', value => '' &>
274 <& hidden.html, field => $pre.'addr_clean', value => '' &>
275
276 <SCRIPT TYPE="text/javascript">
277 <&| /elements/onload.js &>
278   var clear_coords_ids = [
279     '<%$pre%>latitude',
280     '<%$pre%>longitude',
281     '<%$pre%>enter_censustract',
282     '<%$pre%>censustract',
283     '<%$pre%>district'
284   ];
285   function clear_coords() {
286     for (var i=0; i < clear_coords_ids.length; i++) {
287       var el = document.getElementById(clear_coords_ids[i]);
288       if ( el ) {
289         el.value = '';
290       }
291     }
292   }
293   var clear_coords_on_change = [
294     '<%$pre%>address1',
295     '<%$pre%>address2',
296     <% $conf->exists('cust_main-no_city_in_address') ? '' : qq('${pre}city',) %>
297     '<%$pre%>state',
298     '<%$pre%>zip',
299     '<%$pre%>country'
300   ];
301   for (var i=0; i < clear_coords_on_change.length; i++) {
302     var el = document.getElementById(clear_coords_on_change[i]);
303     if ( el.addEventListener ) {
304       el.addEventListener('change', clear_coords);
305     } else if ( el.attachEvent ) {
306       el.attachEvent('onchange', clear_coords);
307     }
308   }
309   function clear_censustract() {
310     // if the user manually edits the census tract, clear the 'hard' census
311     // tract field so that we can re-verify and present a confirmation popup 
312
313     // get the ID of the hidden censustract field
314     var censustract_id = this.id.replace('enter_', '');
315     var el = document.getElementById(censustract_id);
316     if (el) {
317       el.value = '';
318     }
319   }
320   var el = document.getElementById('<%$pre%>enter_censustract');
321   if (el) {
322     if ( el.addEventListener ) {
323       el.addEventListener('change', clear_censustract);
324     } else if ( el.attachEvent ) {
325       el.attachEvent('onchange', clear_censustract);
326     }
327   }
328
329 </&>
330
331 function Location(fieldset) {
332   if ( typeof fieldset == 'String' ) {
333     fieldset = $('#' + fieldset);
334   }
335   this.fieldset = $(fieldset);
336   var errorbox = document.createElement('DIV');
337   errorbox.className = 'error';
338   fieldset.append(errorbox); // after the <table>
339   $(errorbox).position({
340     my: 'left',
341     at: 'right+20px',
342     of: fieldset
343   });
344   this.errorbox = $(errorbox); // so we can find it
345
346   var img_tick = $('<IMG SRC="http://localhost/freeside/images/tick.png">');
347   var img_wait = $('<IMG SRC="http://localhost/freeside/images/wait-orange.gif">');
348
349   // get/set the serialized (URL parameter string) contents of the form fields
350   this.value = function(newvalue) {
351     if (newvalue) {
352       try {
353         this.fieldset.deserialize(newvalue);
354         this.errorbox.empty();
355         if ( newvalue['error'] ) {
356           this.errorbox.text(newvalue['error']);
357         } else {
358           this.errorbox.append(img_tick);
359         }
360       } catch(err) {
361         console.log("Couldn't parse returned data:\n" + newvalue);
362         // show an error also
363       }
364     }
365     return this.fieldset.serialize();
366   };
367
368   // send a standardization request and do something with the result
369   this.standardize = function(callback) {
370     this.errorbox.empty();
371     this.errorbox.append(img_wait);
372     $.ajax({
373       type: 'POST',
374       url: '<% $fsurl %>misc/address_standardize.cgi',
375       success: callback,
376       data: this.value()
377     });
378   };
379
380   // check if required fields are filled, and if so, standardize
381   var standardize_if_ready = function() {
382     var loc = this;
383     var ready = true;
384     var required_fields = this.fieldset.find(':data(required)');
385     for ( var i = 0; ready && i < required_fields.length; i++ ) {
386       if ( required_fields[i].prop('value').length == 0 ) {
387         ready = false;
388       }
389     }
390
391     if ( ready ) {
392       // pass the "value" method, prebound to the location object
393       this.standardize( this.value.bind(loc) );
394     }
395   };
396
397   // event handler; the Location object is passed in event.data
398   var location_change_timer;
399   var location_changed = function( ev ) {
400     if ( location_change_timer ) {
401       window.clearTimeout(location_change_timer);
402     }
403     location_change_timer = window.setTimeout(
404       standardize_if_ready.bind(ev.data),
405       2000
406     );
407   };
408
409   fieldset.find('input').on('change', this, location_changed);
410   fieldset.find('select').on('change', this, location_changed);
411 }
412
413 </SCRIPT>
414
415 <%init>
416
417 my %opt = @_;
418
419 my $pre      = $opt{'prefix'};
420 my $object   = $opt{'object'};
421 my $onchange = $opt{'onchange'};
422 my $disabled = $opt{'disabled'};
423
424 my $r = $opt{'no_asterisks'} ? '' : qq!<font color="#ff0000">*</font>&nbsp;!;
425
426 my $conf = new FS::Conf;
427 my $countrydefault = $conf->config('countrydefault') || 'US';
428 my $statedefault   = $conf->config('statedefault') 
429                        || ($countrydefault eq 'US' ? 'CA' : '');
430 my $label_prefix   = $conf->config('cust_location-label_prefix');
431
432 $object ||= FS::cust_location->new({
433   'country' => $countrydefault,
434   'state'   => $statedefault,
435 });
436
437 my $alt_err = ($opt{'alt_format'} && !$disabled) ? $object->alternize : '';
438
439 my @style = ();
440 push @style, 'background-color: #dddddd' if $disabled;
441
442 my @address2_label_style = ();
443 push @address2_label_style, 'visibility:hidden'
444   if $disabled
445   || ! $conf->exists('cust_main-require_address2')
446   || ( !$pre && !$opt{'same_checked'} );
447
448 my @counties = counties( $object->get('state'),
449                          $object->get('country'),
450                        );
451 my @county_style = ();
452 push @county_style, 'display:none' # 'visibility:hidden'
453   unless scalar(@counties) > 1;
454
455 my $style =
456   scalar(@style)
457     ? 'STYLE="'. join(';', @style). '"'
458     : '';
459 my $address2_label_style =
460   scalar(@address2_label_style)
461     ? 'STYLE="'. join(';', @address2_label_style). '"'
462     : '';
463 my $county_style = 
464   scalar(@county_style)
465     ? 'STYLE="'. join(';', @county_style). '"'
466     : '';
467
468 my %select_hash = (
469   'city'     => $object->get('city'),
470   'county'   => $object->get('county'),
471   'state'    => $object->get('state'),
472   'country'  => $object->get('country'),
473   'prefix'   => $pre,
474   'onchange' => $onchange,
475   'disabled' => $disabled,
476   #'style'    => \@style,
477 );
478
479 my $th = $opt{'no_bold'} ? 'TD' : 'TH';
480
481 my @location_kind_options = ( '', 'R', 'B' );
482 my $location_kind_labels = { '' => '', 'R' => 'Residential', 'B' => 'Business' };
483
484 </%init>