1 package FS::cust_main::Location;
4 use vars qw( $DEBUG $me @location_fields );
5 use FS::Record qw(qsearch qsearchs);
12 $me = '[FS::cust_main::Location]';
16 # set up accessors for location fields
20 qw( address1 address2 city county state zip country district
21 latitude longitude coord_auto censustract censusyear geocode
24 foreach my $f (@location_fields) {
25 *{"FS::cust_main::Location::$f"} = sub {
26 carp "WARNING: tried to set cust_main.$f with accessor" if (@_ > 1);
27 shift->bill_location->$f
29 *{"FS::cust_main::Location::ship_$f"} = sub {
30 carp "WARNING: tried to set cust_main.ship_$f with accessor" if (@_ > 1);
31 shift->ship_location->$f
38 #debugging shim--probably a performance hit, so remove this at some point
42 if ( $DEBUG and grep (/^(ship_)?($field)$/, @location_fields) ) {
43 carp "WARNING: tried to get() location field $field";
46 $self->FS::Record::get($field);
51 FS::cust_main::Location - Location-related methods for cust_main
55 These methods are available on FS::cust_main objects;
63 Returns an L<FS::cust_location> object for the customer's billing address.
69 $self->hashref->{bill_location}
70 ||= FS::cust_location->by_key($self->bill_locationnum);
75 Returns an L<FS::cust_location> object for the customer's service address.
81 $self->hashref->{ship_location}
82 ||= FS::cust_location->by_key($self->ship_locationnum);
87 An alternative way of saying "bill_location or ship_location, depending on
88 if TYPE is 'bill' or 'ship'".
94 return $self->bill_location if $_[0] eq 'bill';
95 return $self->ship_location if $_[0] eq 'ship';
96 die "bad location type '$_[0]'";
105 =item location_fields
107 Returns a list of fields found in the location objects. All of these fields
108 can be read (but not written) by calling them as methods on the
109 L<FS::cust_main> object (prefixed with 'ship_' for the service address
114 sub location_fields { @location_fields }
118 eval "use FS::contact;
119 use FS::contact_class;
120 use FS::contact_phone;
123 local $FS::cust_location::import = 1;
127 # Step 0: set up contact classes and phone types
128 my $service_contact_class =
129 qsearchs('contact_class', { classname => 'Service'})
130 || new FS::contact_class { classname => 'Service'};
132 if ( !$service_contact_class->classnum ) {
133 $error = $service_contact_class->insert;
134 die "error creating contact class for Service: $error" if $error;
136 my %phone_type = ( # fudge slightly
143 foreach (keys %phone_type) {
144 $phone_type{$_} = qsearchs('phone_type', { typename => $phone_type{$_}})
145 || new FS::phone_type { typename => $phone_type{$_},
147 # just in case someone still doesn't have these
148 if ( !$phone_type{$_}->phonetypenum ) {
149 $error = $phone_type{$_}->insert;
150 die "error creating phone type '$_': $error" if $error;
154 foreach my $cust_main (qsearch('cust_main', { bill_locationnum => '' })) {
155 # Step 1: extract billing and service addresses into cust_location
156 my $custnum = $cust_main->custnum;
157 my $bill_location = FS::cust_location->new(
160 map { $_ => $cust_main->get($_) } location_fields(),
163 $bill_location->set('censustract', ''); # properly goes with ship_location
164 my $ship_location = $bill_location; # until proven otherwise
166 if ( $cust_main->get('ship_address1') ) {
169 foreach (location_fields()) {
170 if ( length($cust_main->get("ship_$_")) and
171 $cust_main->get($_) ne $cust_main->get("ship_$_") ) {
177 $ship_location = FS::cust_location->new(
180 map { $_ => $cust_main->get("ship_$_") } location_fields()
183 } # else it stays equal to $bill_location
185 $ship_location->set('censustract', $cust_main->get('censustract'));
187 # Step 2: Extract shipping address contact fields into contact
188 my %unlike = map { $_ => 1 }
189 grep { $cust_main->get($_) ne $cust_main->get("ship_$_") }
190 qw( last first company daytime night fax mobile );
193 # then there IS a service contact
194 my $contact = FS::contact->new({
195 'custnum' => $custnum,
196 'classnum' => $service_contact_class->classnum,
197 'locationnum' => $ship_location->locationnum,
198 'last' => $cust_main->get('ship_last'),
199 'first' => $cust_main->get('ship_first'),
201 if ( $unlike{'company'} ) {
202 # there's no contact.company field, but keep a record of it
203 $contact->set(comment => 'Company: '.$cust_main->get('ship_company'));
205 $error = $contact->insert;
206 die "error migrating service contact for customer $custnum: $error"
209 foreach ( grep { $unlike{$_} } qw( daytime night fax mobile ) ) {
210 my $phone = $cust_main->get("ship_$_");
212 my $contact_phone = FS::contact_phone->new({
213 'contactnum' => $contact->contactnum,
214 'phonetypenum' => $phone_type{$_}->phonetypenum,
215 FS::contact::_parse_phonestring( $phone )
217 $error = $contact_phone->insert;
218 # die "whose responsible this"
219 die "error migrating service contact phone for customer $custnum: $error"
221 $cust_main->set("ship_$_" => '');
224 $cust_main->set("ship_$_" => '') foreach qw(last first company);
227 $error = $bill_location->insert;
228 die "error migrating billing address for customer $custnum: $error"
231 $cust_main->set(bill_locationnum => $bill_location->locationnum);
233 if (!$ship_location->locationnum) {
234 $error = $ship_location->insert;
235 die "error migrating service address for customer $custnum: $error"
239 $cust_main->set(ship_locationnum => $ship_location->locationnum);
241 # Step 3: Wipe the migrated fields and update the cust_main
243 $cust_main->set("ship_$_" => '') foreach location_fields();
244 $cust_main->set($_ => '') foreach location_fields();
246 $error = $cust_main->replace;
247 die "error migrating addresses for customer $custnum: $error"
250 # Step 4: set packages at the "default service location" to ship_location
251 foreach my $cust_pkg (
252 qsearch('cust_pkg', { custnum => $custnum, locationnum => '' })
254 # not a location change
255 $cust_pkg->set('locationnum', $cust_main->ship_locationnum);
256 $error = $cust_pkg->replace;
257 die "error migrating package ".$cust_pkg->pkgnum.": $error"
261 } #foreach $cust_main