pick/enter a location when ordering a package, RT#4499
authorivan <ivan>
Fri, 9 Jan 2009 04:06:26 +0000 (04:06 +0000)
committerivan <ivan>
Fri, 9 Jan 2009 04:06:26 +0000 (04:06 +0000)
13 files changed:
FS/FS/Mason.pm
FS/FS/cust_location.pm
FS/FS/cust_main.pm
httemplate/edit/cust_main/contact.html
httemplate/elements/location.html [new file with mode: 0644]
httemplate/elements/select-country.html
httemplate/elements/select-county.html
httemplate/elements/select-state.html
httemplate/elements/tr-select-part_referral.html
httemplate/misc/location.cgi [new file with mode: 0644]
httemplate/misc/order_pkg.html
httemplate/view/cust_main/packages.html
httemplate/view/cust_main/packages/location.html

index 0a608dd..e0f58b8 100644 (file)
@@ -105,6 +105,7 @@ Initializes the Mason environment, loads all Freeside and RT libraries, etc.
   use FS::cust_main qw(smart_search);
   use FS::cust_main::Import;
   use FS::cust_main_county;
+  use FS::cust_location;
   use FS::cust_pay;
   use FS::cust_pkg;
   use FS::part_pkg_taxclass;
index 0544dcf..50d2a18 100644 (file)
@@ -158,6 +158,27 @@ sub country_full {
   code2country($self->country);
 }
 
+=item line
+
+Returns this location on one line
+
+=cut
+
+sub line {
+  my $self = shift;
+  my $cydefault = FS::conf->new->config('countrydefault') || 'US';
+
+  my $line =       $self->address1;
+  $line   .= ', '. $self->address2              if $self->address2;
+  $line   .= ', '. $self->city;
+  $line   .= ' ('. $self->county. ' county)'    if $self->county;
+  $line   .= ', '. $self->state                 if $self->state;
+  $line   .= '  '. $self->zip                   if $self->zip;
+  $line   .= '  '. code2country($self->country) if $self->country ne $cydefault;
+
+  $line;
+}
+
 =back
 
 =head1 BUGS
index 2b94dca..fb68081 100644 (file)
@@ -36,6 +36,7 @@ use FS::cust_credit;
 use FS::cust_refund;
 use FS::part_referral;
 use FS::cust_main_county;
+use FS::cust_location;
 use FS::tax_rate;
 use FS::cust_tax_location;
 use FS::part_pkg_taxrate;
@@ -1721,6 +1722,17 @@ sub cust_pkg {
   shift->all_pkgs(@_);
 }
 
+=item cust_location
+
+Returns all locations (see L<FS::cust_location>) for this customer.
+
+=cut
+
+sub cust_location {
+  my $self = shift;
+  qsearch('cust_location', { 'custnum' => $self->custnum } );
+}
+
 =item ncancelled_pkgs
 
 Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer.
index d93d417..27dd385 100644 (file)
   </TD>
 </TR>
 
-<TR>
-  <TH ALIGN="right"><%$r%>Address</TH>
-  <TD COLSPAN=7>
-    <INPUT TYPE="text" NAME="<%$pre%>address1" VALUE="<% $cust_main->get($pre.'address1') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%>>
-  </TD>
-</TR>
-
-% my $address2_label_style =
-%   ( $disabled
-%     || ! $conf->exists('cust_main-require_address2')
-%     || ( !$pre && !$opt{'same_checked'} )
-%   )
-%     ? 'visibility:hidden'
-%     : '';
-
-<TR>
-  <TD ALIGN="right"><FONT ID="<% $pre %>address2_required" color="#ff0000" STYLE="<% $address2_label_style %>">*</FONT>&nbsp;<FONT ID="<% $pre %>address2_label" STYLE="<% $address2_label_style %>"><B>Unit&nbsp;#</B></FONT></TD>
-  <TD COLSPAN=7>
-    <INPUT TYPE="text" NAME="<%$pre%>address2" VALUE="<% $cust_main->get($pre.'address2') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%>>
-  </TD>
-</TR>
-
-<TR>
-  <TH ALIGN="right"><%$r%>City</TH>
-  <TD>
-    <INPUT TYPE="text" NAME="<%$pre%>city" VALUE="<% $cust_main->get($pre.'city') %>" onChange="<% $onchange %>" <%$disabled%>>
-  </TD>
-  <TH ALIGN="right" ID="<%$pre%>countylabel" <%$county_style%>><%$r%>County</TH>
-  <TD>
-    <% include('/elements/select-county.html', %select_hash ) %>
-  </TD>
-  <TH ALIGN="right"><%$r%>State</TH>
-  <TD>
-    <% include('/elements/select-state.html', %select_hash ) %>
-  </TD>
-  <TH><%$r%>Zip</TH>
-  <TD>
-    <INPUT TYPE="text" NAME="<%$pre%>zip" VALUE="<% $cust_main->get($pre.'zip') %>" SIZE=10 onChange="<% $onchange %>" <%$disabled%>>
-  </TD>
-</TR>
-
-<TR>
-  <TH ALIGN="right"><%$r%>Country</TH>
-  <TD COLSPAN=5><% include('/elements/select-country.html', %select_hash ) %></TD>
-% if ( !$pre ) { 
-  <TD><INPUT TYPE="hidden" NAME="geocode" VALUE="<% $opt{geocode} %>"></TD>
-% } 
-
-</TR>
+<% include('/elements/location.html',
+             'prefix'       => $pre,
+             'object'       => $cust_main,
+             'onchange'     => $onchange,
+             'disabled'     => $disabled,
+             'same_checked' => $opt{'same_checked'},
+             'geocode'      => $opt{'geocode'},
+          )
+%>
 
 <TR>
   <TD ALIGN="right"><% $daytime_label %></TD>
@@ -159,20 +119,6 @@ $cust_main->set('stateid_state', $cust_main->state )
 #                                        $disabled,
 #                                      );
 
-my %select_hash = (
-  'county'   => $cust_main->get($pre.'county'),
-  'state'    => $cust_main->get($pre.'state'),
-  'country'  => $cust_main->get($pre.'country'),
-  'prefix'   => $pre,
-  'onchange' => $onchange,
-  'disabled' => $disabled,
-);
-
-my @counties = counties( $cust_main->get($pre.'state'),
-                         $cust_main->get($pre.'country'),
-                       );
-my $county_style = scalar(@counties) > 1 ? '' : 'STYLE="visibility:hidden"';
-
 my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
                   ? 'Day Phone'
                   : FS::Msgcat::_gettext('daytime');
diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html
new file mode 100644 (file)
index 0000000..bf34089
--- /dev/null
@@ -0,0 +1,150 @@
+<%doc>
+
+Example:
+
+  include( '/elements/location.html',
+             'object'       => $cust_main,  # or $cust_location
+             'prefix'       => $pre,        #only for cust_main objects
+             'onchange'     => $javascript,
+             'disabled'     => $disabled,
+             'same_checked' => $same_checked,
+             'geocode'      => $geocode, #passed through
+             'no_asterisks' => 0, #set true to disable the red asterisks next
+                                  #to required fields
+         )
+
+</%doc>
+
+<TR>
+  <TH ALIGN="right"><%$r%>Address</TH>
+  <TD COLSPAN=7>
+    <INPUT TYPE     = "text"
+           NAME     = "<%$pre%>address1"
+           VALUE    = "<% $object->get($pre.'address1') |h %>"
+           SIZE     = 70
+           onChange = "<% $onchange %>"
+           <% $disabled %>
+           <% $style %>
+    >
+  </TD>
+</TR>
+
+<TR>
+  <TD ALIGN="right"><FONT ID="<% $pre %>address2_required" color="#ff0000" STYLE="<% $address2_label_style %>">*</FONT>&nbsp;<FONT ID="<% $pre %>address2_label" STYLE="<% $address2_label_style %>"><B>Unit&nbsp;#</B></FONT></TD>
+  <TD COLSPAN=7>
+    <INPUT TYPE     = "text"
+           NAME     = "<%$pre%>address2"
+           VALUE    = "<% $object->get($pre.'address2') |h %>"
+           SIZE     = 70
+           onChange = "<% $onchange %>"
+           <% $disabled %>
+           <% $style %>
+    >
+  </TD>
+</TR>
+
+<TR>
+  <TH ALIGN="right"><%$r%>City</TH>
+  <TD>
+    <INPUT TYPE     = "text"
+           NAME     = "<%$pre%>city"
+           VALUE    = "<% $object->get($pre.'city') |h %>"
+           onChange = "<% $onchange %>"
+           <% $disabled %>
+           <% $style %>
+    >
+  </TD>
+  <TH ALIGN="right" ID="<%$pre%>countylabel" <%$county_style%>><%$r%>County</TH>
+  <TD>
+    <% include('/elements/select-county.html', %select_hash ) %>
+  </TD>
+  <TH ALIGN="right"><%$r%>State</TH>
+  <TD>
+    <% include('/elements/select-state.html', %select_hash ) %>
+  </TD>
+  <TH><%$r%>Zip</TH>
+  <TD>
+    <INPUT TYPE     = "text"
+           NAME     = "<%$pre%>zip"
+           VALUE    = "<% $object->get($pre.'zip') |h %>"
+           SIZE     = 10
+           onChange = "<% $onchange %>"
+           <% $disabled %>
+           <% $style %>
+    >
+  </TD>
+</TR>
+
+<TR>
+  <TH ALIGN="right"><%$r%>Country</TH>
+  <TD COLSPAN=5><% include('/elements/select-country.html', %select_hash ) %></TD>
+</TR>
+
+% if ( !$pre ) { 
+  <INPUT TYPE="hidden" NAME="geocode" VALUE="<% $opt{geocode} %>">
+% } 
+
+<%init>
+
+my %opt = @_;
+
+my $pre      = $opt{'prefix'};
+my $object   = $opt{'object'};
+my $onchange = $opt{'onchange'};
+my $disabled = $opt{'disabled'};
+
+my $conf = new FS::Conf;
+
+my $r = $opt{'no_asterisks'} ? '' : qq!<font color="#ff0000">*</font>&nbsp;!;
+
+#false laziness with ship state
+my $countrydefault = $conf->config('countrydefault') || 'US';
+$object->set($pre.'country', $countrydefault )
+  unless $object->get($pre.'country');
+
+my $statedefault = $conf->config('statedefault')
+                   || ($countrydefault eq 'US' ? 'CA' : '');
+$object->set($pre.'state', $statedefault )
+  unless $object->get($pre.'state')
+         || $object->get($pre.'country') ne $countrydefault;
+
+my @style = ();
+push @style, 'background-color: #dddddd"' if $disabled;
+
+my @address2_label_style = ();
+push @address2_label_style, 'visibility:hidden'
+  if $disabled
+  || ! $conf->exists('cust_main-require_address2')
+  || ( !$pre && !$opt{'same_checked'} );
+
+my @counties = counties( $object->get($pre.'state'),
+                         $object->get($pre.'country'),
+                       );
+my @county_style = ();
+push @county_style, 'visibility:hidden'
+  unless scalar(@counties) > 1;
+
+my $style =
+  scalar(@style)
+    ? 'STYLE="'. join(';', @style). '"'
+    : '';
+my $address2_label_style =
+  scalar(@address2_label_style)
+    ? 'STYLE="'. join(';', @address2_label_style). '"'
+    : '';
+my $county_style = 
+  scalar(@county_style)
+    ? 'STYLE="'. join(';', @county_style). '"'
+    : '';
+
+my %select_hash = (
+  'county'   => $object->get($pre.'county'),
+  'state'    => $object->get($pre.'state'),
+  'country'  => $object->get($pre.'country'),
+  'prefix'   => $pre,
+  'onchange' => $onchange,
+  'disabled' => $disabled,
+  'style'    => \@style,
+);
+
+</%init>
index c4368c2..45b0ccd 100644 (file)
@@ -13,7 +13,7 @@ Example:
     disable_empty => 1, #defaults to 1, disable the empty option
     empty_label   => 'all', #label for empty option
     disable_stateupdate => 0, #bool - disabled update of the select-state.html
-    
+    style         => [ 'attribute:value', 'another:value' ],
   );
 
 </%doc>
@@ -70,6 +70,7 @@ Example:
         ID       = "<% $pre %>country"
         onChange = "<% $onchange %>"
         <% $opt{'disabled'} %>
+        <% $style %>
 >
 
 % unless ( $opt{'disable_empty'} ) {
@@ -101,6 +102,12 @@ my $onchange =
   ( $opt{'disable_stateupdate'} ? '' : $pre.'country_changed(this); ' ).
   $opt{'onchange'};
 
+$opt{'style'} ||= [];
+my $style =
+  scalar(@{$opt{style}})
+    ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+    : '';
+
 my $conf = new FS::Conf;
 my $default = $conf->config('countrydefault') || 'US';
 
index 8ef34db..59f235a 100644 (file)
@@ -14,6 +14,7 @@ Example:
     disabled      => 0, #bool
     disable_empty => 1, #defaults to 1, disable the empty option
     empty_label   => 'all', #label for empty option
+    style         => [ 'attribute:value', 'another:value' ],
   );
 
 </%doc>
@@ -79,6 +80,7 @@ Example:
           ID      = "<% $pre %>county"
           onChange= "<% $opt{'onchange'} %>"
           <% $opt{'disabled'} %>
+          <% $style %>
   >
 
 % unless ( $opt{'disable_empty'} ) {
@@ -123,6 +125,12 @@ $opt{'disable_empty'} = 1 unless exists($opt{'disable_empty'});
 
 my $pre = $opt{'prefix'};
 
+$opt{'style'} ||= [];
+my $style =
+  scalar(@{$opt{style}})
+    ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+    : '';
+
 my @counties = ();
 if ( $countyflag ) {
 
index f7ac2c7..9b358e2 100644 (file)
@@ -14,6 +14,7 @@ Example:
     disable_empty => 1, #defaults to 1, disable the empty option
     empty_label   => 'all', #label for empty option
     disable_countyupdate => 0, #bool - disabled update of the select-state.html
+    style         => [ 'attribute:value', 'another:value' ],
   );
 
 </%doc>
@@ -21,6 +22,8 @@ Example:
 <SELECT NAME     = "<% $pre %>state"
         ID       = "<% $pre %>state"
         onChange = "<% $onchange %>"
+        <% $opt{'disabled'} %>
+        <% $style %>
 >
 
 % unless ( $opt{'disable_empty'} ) {
@@ -51,6 +54,12 @@ my $onchange =
   ( $opt{'disable_countyupdate'} ? '' : $pre.'state_changed(this); ' ).
   $opt{'onchange'};
 
+$opt{'style'} ||= [];
+my $style =
+  scalar(@{$opt{style}})
+    ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+    : '';
+
 tie my %states, 'Tie::IxHash', states_hash( $opt{'country'} ); 
 
 </%init>
index 62795e9..a589528 100644 (file)
@@ -12,7 +12,7 @@
 %      } else {
          <TH ALIGN="right"><%$r%>Advertising source</TH>
 %      }
-       <TD>
+       <TD COLSPAN="<% $colspan %>">
          <% include( '/elements/select-part_referral.html',
                        'curr_value' => $refnum,
                        %opt
@@ -30,6 +30,8 @@ my $refnum = $opt{'curr_value'} || $opt{'value'};
 $opt{'part_referrals'} ||=
   [ FS::part_referral->all_part_referral( 1 ) ]; #1: include global
 
+my $colspan = delete($opt{'colspan'}) || 1;
+
 my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
 
 </%init>
diff --git a/httemplate/misc/location.cgi b/httemplate/misc/location.cgi
new file mode 100644 (file)
index 0000000..3c3a855
--- /dev/null
@@ -0,0 +1,18 @@
+<% objToJson(\%hash) %>
+<%init>
+
+my $locationnum = $cgi->param('arg');
+
+my $cust_location = qsearchs({
+  'table'     => 'cust_location',
+  'hashref'   => { 'locationnum' => $locationnum },
+  'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+  'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+
+my %hash = ();
+%hash = map { $_ => $cust_location->$_() }
+            qw( address1 address2 city county state zip country )
+  if $cust_location;
+
+</%init>
index 852b51c..f911431 100644 (file)
@@ -1,13 +1,79 @@
 <% include('/elements/header-popup.html', 'Order new package' ) %>
 
+<% include('/elements/xmlhttp.html',
+              'url'  => $p.'misc/location.cgi',
+              'subs' => [ 'get_location' ],
+           )
+%>
+
 <SCRIPT TYPE="text/javascript">
-function enable_order_pkg () {
-  if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
-    document.OrderPkgForm.submit.disabled = false;
-  } else {
-    document.OrderPkgForm.submit.disabled = true;
+
+  function locationnum_changed(what) {
+    var locationnum = what.options[what.selectedIndex].value;
+    if ( locationnum == -1 ) {
+
+%     for (@location_fields) { 
+        what.form.<%$_%>.disabled = false;
+        what.form.<%$_%>.style.backgroundColor = '#ffffff';
+%     } 
+
+      what.form.address1.value = '';
+      what.form.address2.value = '';
+      what.form.city.value = '';
+      what.form.zip.value = '';
+      changeSelect(what.form.country, <% $countrydefault |js_string %>);
+%#shouldn't we sleep/wait here until the state dropdown is updated?
+%#(is it even triggered???)
+      changeSelect(what.form.state, <% $statedefault |js_string %>);
+      what.form.county.selectedIndex = 0;
+
+    } else {
+
+      if ( locationnum == 0 ) {
+        what.form.address1.value = <% $cust_main->address1 |js_string %>;
+        what.form.address2.value = <% $cust_main->address2 |js_string %>;
+        what.form.city.value = <% $cust_main->city |js_string %>;
+        what.form.zip.value = <% $cust_main->zip |js_string %>;
+        changeSelect(what.form.country, <% $cust_main->country | js_string %> );
+%#shouldn't we sleep/wait here until the state dropdown is updated?
+%#(is it even triggered???)
+        changeSelect(what.form.state, <% $cust_main->state | js_string %> );
+%#shouldn't we sleep/wait here until the county dropdown is updated?
+%#(is it even triggered???)
+        changeSelect(what.form.county, <% $cust_main->county | js_string %> );
+      } else {
+        get_location( locationnum, update_location );
+      } 
+
+%#sleep/wait until dropdowns are updated?
+%     for (@location_fields) { 
+        what.form.<%$_%>.disabled = true;
+        what.form.<%$_%>.style.backgroundColor = '#dddddd';
+%     } 
+
+    }
   }
-}
+
+  function changeSelect(what, value) {
+    for ( var i=0; i<what.length; i++) {
+      if ( what.options[i].value == value ) {
+        what.selectedIndex = i;
+      }
+    }
+  }
+
+  function update_location( hash ) {
+    alert(hash);
+  }
+
+  function enable_order_pkg () {
+    if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
+      document.OrderPkgForm.submit.disabled = false;
+    } else {
+      document.OrderPkgForm.submit.disabled = true;
+    }
+  }
+
 </SCRIPT>
 
 <% include('/elements/error.html') %>
@@ -19,7 +85,7 @@ function enable_order_pkg () {
 <% ntable("#cccccc", 2) %>
 <TR>
   <TH ALIGN="right">Package</TH>
-  <TD>
+  <TD COLSPAN=7>
     <% include('/elements/select-cust-part_pkg.html',
                  'curr_value' => $pkgpart,
                  'cust_main'  => $cust_main,
@@ -34,10 +100,36 @@ function enable_order_pkg () {
                'curr_value'    => scalar( $cgi->param('refnum') ), #get rid of empty_label first# || $cust_main->refnum,
                'disable_empty' => 1,
                'multiple'      => $conf->exists('pkg_referral-multiple'),
+               'colspan'       => 7,
             )
   %>
 % }
 
+<TR>
+  <TH ALIGN="right">Service location</TH>
+  <TD COLSPAN=7>
+    <SELECT NAME="locationnum" onChange="locationnum_changed(this);">
+      <OPTION VALUE="">(default service address)
+%     foreach my $loc ( $cust_main->cust_location ) {
+        <OPTION VALUE="<% $loc->locationnum %>"
+                <% $locationnum == $loc->locationnum ? 'SELECTED' : '' %>
+        ><% $loc->line |h %>
+%     }
+      <OPTION VALUE="-1"
+              <% $locationnum == -1 ? 'SELECTED' : '' %>
+      >Add new location
+    </SELECT>
+  </TD>
+</TR>
+
+<% include('/elements/location.html',
+             'object'       => $cust_location,
+             #'onchange' ?  probably not
+             'disabled'     => ( $locationnum == -1 ? '' : 'DISABLED' ),
+             'no_asterisks' => 1,
+          )
+%>
+
 </TABLE>
 
 <BR>
@@ -46,14 +138,22 @@ function enable_order_pkg () {
 </FORM>
 </BODY>
 </HTML>
+<%once>
+
+my @location_fields = qw( address1 address2 city county state zip country );
+
+</%once>
 <%init>
 
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Order customer package');
 
 my $conf = new FS::Conf;
+my $countrydefault = $conf->config('countrydefault') || 'US';
+my $statedefault = $conf->config('statedefault')
+                   || ($countrydefault eq 'US' ? 'CA' : '');
 
-$cgi->param('custnum') =~ /^(\d+)$/;
+$cgi->param('custnum') =~ /^(\d+)$/ or die "no custnum";
 my $custnum = $1;
 my $cust_main = qsearchs({
   'table'     => 'cust_main',
@@ -63,4 +163,19 @@ my $cust_main = qsearchs({
 
 my $pkgpart = scalar($cgi->param('pkgpart'));
 
+$cgi->param('locationnum') =~ /^(\d*)$/ or die "illegal locationnum";
+my $locationnum = $1;
+my $cust_location;
+if ( $locationnum ) {
+  $cust_location = qsearchs('cust_location', { 'locationnum' => $locationnum } )
+    or die "unknown locationnum";
+} else {
+  $cust_location = new FS::cust_location;
+  if ( $cgi->param('error') && $locationnum == -1 ) {
+    $cust_location->$_( $cgi->param($_) ) foreach @location_fields;
+  } else {
+    $cust_location->$_( $cust_main->$_() ) foreach @location_fields;
+  }
+}
+
 </%init>
index 5fde2f3..9a4997b 100755 (executable)
@@ -212,6 +212,7 @@ sub order_pkg_link {
              'color'       => '#333399',
              'cust_main'   => shift,
              'closetext'   => 'Close',
+             'width'       => 763,
          )
 }
 
index 3f84148..0f58b36 100644 (file)
@@ -10,7 +10,7 @@
     <% $loc->address2 |h %><BR>
 % }
 
-  <% $loc->city |h %><% %>,
+  <% $loc->city |h %><% $loc->county ? ' ('.$loc->county.' county)' : '' |h %>,
   <% $loc->state |h %> &nbsp; <% $loc->zip |h %><BR>
 
 % if ( $loc->country ne $countrydefault ) {