better error message for no location_kind
[freeside.git] / FS / FS / cust_location.pm
1 package FS::cust_location;
2
3 use strict;
4 use base qw( FS::geocode_Mixin FS::Record );
5 use Locale::Country;
6 use FS::UID qw( dbh );
7 use FS::Record qw( qsearch ); #qsearchs );
8 use FS::Conf;
9 use FS::prospect_main;
10 use FS::cust_main;
11 use FS::cust_main_county;
12
13 =head1 NAME
14
15 FS::cust_location - Object methods for cust_location records
16
17 =head1 SYNOPSIS
18
19   use FS::cust_location;
20
21   $record = new FS::cust_location \%hash;
22   $record = new FS::cust_location { 'column' => 'value' };
23
24   $error = $record->insert;
25
26   $error = $new_record->replace($old_record);
27
28   $error = $record->delete;
29
30   $error = $record->check;
31
32 =head1 DESCRIPTION
33
34 An FS::cust_location object represents a customer location.  FS::cust_location
35 inherits from FS::Record.  The following fields are currently supported:
36
37 =over 4
38
39 =item locationnum
40
41 primary key
42
43 =item custnum
44
45 custnum
46
47 =item address1
48
49 Address line one (required)
50
51 =item address2
52
53 Address line two (optional)
54
55 =item city
56
57 City
58
59 =item county
60
61 County (optional, see L<FS::cust_main_county>)
62
63 =item state
64
65 State (see L<FS::cust_main_county>)
66
67 =item zip
68
69 Zip
70
71 =item country
72
73 Country (see L<FS::cust_main_county>)
74
75 =item geocode
76
77 Geocode
78
79 =item disabled
80
81 Disabled flag; set to 'Y' to disable the location.
82
83 =back
84
85 =head1 METHODS
86
87 =over 4
88
89 =item new HASHREF
90
91 Creates a new location.  To add the location to the database, see L<"insert">.
92
93 Note that this stores the hash reference, not a distinct copy of the hash it
94 points to.  You can ask the object for a copy with the I<hash> method.
95
96 =cut
97
98 sub table { 'cust_location'; }
99
100 =item insert
101
102 Adds this record to the database.  If there is an error, returns the error,
103 otherwise returns false.
104
105 =item delete
106
107 Delete this record from the database.
108
109 =item replace OLD_RECORD
110
111 Replaces the OLD_RECORD with this one in the database.  If there is an error,
112 returns the error, otherwise returns false.
113
114 =item check
115
116 Checks all fields to make sure this is a valid location.  If there is
117 an error, returns the error, otherwise returns false.  Called by the insert
118 and replace methods.
119
120 =cut
121
122 #some false laziness w/cust_main, but since it should eventually lose these
123 #fields anyway...
124 sub check {
125   my $self = shift;
126
127   my $error = 
128     $self->ut_numbern('locationnum')
129     || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum')
130     || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum')
131     || $self->ut_text('address1')
132     || $self->ut_textn('address2')
133     || $self->ut_text('city')
134     || $self->ut_textn('county')
135     || $self->ut_textn('state')
136     || $self->ut_country('country')
137     || $self->ut_zip('zip', $self->country)
138     || $self->ut_alphan('location_type')
139     || $self->ut_textn('location_number')
140     || $self->ut_enum('location_kind', [ '', 'R', 'B' ] )
141     || $self->ut_alphan('geocode')
142   ;
143   return $error if $error;
144
145   return "No prospect or customer!" unless $self->prospectnum || $self->custnum;
146   return "Prospect and customer!"       if $self->prospectnum && $self->custnum;
147
148   my $conf = new FS::Conf;
149   return 'Location kind is required'
150     if $self->prospectnum
151     && $conf->exists('prospect_main-alt_address_format')
152     && ! $self->location_kind;
153
154   unless ( qsearch('cust_main_county', {
155     'country' => $self->country,
156     'state'   => '',
157    } ) ) {
158     return "Unknown state/county/country: ".
159       $self->state. "/". $self->county. "/". $self->country
160       unless qsearch('cust_main_county',{
161         'state'   => $self->state,
162         'county'  => $self->county,
163         'country' => $self->country,
164       } );
165   }
166
167   $self->SUPER::check;
168 }
169
170 =item country_full
171
172 Returns this locations's full country name
173
174 =cut
175
176 sub country_full {
177   my $self = shift;
178   code2country($self->country);
179 }
180
181 =item line
182
183 Synonym for location_label
184
185 =cut
186
187 sub line {
188   my $self = shift;
189   $self->location_label;
190 }
191
192 =item has_ship_address
193
194 Returns false since cust_location objects do not have a separate shipping
195 address.
196
197 =cut
198
199 sub has_ship_address {
200   '';
201 }
202
203 =item location_hash
204
205 Returns a list of key/value pairs, with the following keys: address1, address2,
206 city, county, state, zip, country, geocode.
207
208 =cut
209
210 =item move_to HASHREF
211
212 Takes a hashref with one or more cust_location fields.  Creates a duplicate 
213 of the existing location with all fields set to the values in the hashref.  
214 Moves all packages that use the existing location to the new one, then sets 
215 the "disabled" flag on the old location.  Returns nothing on success, an 
216 error message on error.
217
218 =cut
219
220 sub move_to {
221   my $old = shift;
222   my $hashref = shift;
223
224   local $SIG{HUP} = 'IGNORE';
225   local $SIG{INT} = 'IGNORE';
226   local $SIG{QUIT} = 'IGNORE';
227   local $SIG{TERM} = 'IGNORE';
228   local $SIG{TSTP} = 'IGNORE';
229   local $SIG{PIPE} = 'IGNORE';
230
231   my $oldAutoCommit = $FS::UID::AutoCommit;
232   local $FS::UID::AutoCommit = 0;
233   my $dbh = dbh;
234   my $error = '';
235
236   my $new = FS::cust_location->new({
237       $old->location_hash,
238       'custnum'     => $old->custnum,
239       'prospectnum' => $old->prospectnum,
240       %$hashref
241     });
242   $error = $new->insert;
243   if ( $error ) {
244     $dbh->rollback if $oldAutoCommit;
245     return "Error creating location: $error";
246   }
247
248   my @pkgs = qsearch('cust_pkg', { 
249       'locationnum' => $old->locationnum,
250       'cancel' => '' 
251     });
252   foreach my $cust_pkg (@pkgs) {
253     $error = $cust_pkg->change(
254       'locationnum' => $new->locationnum,
255       'keep_dates'  => 1
256     );
257     if ( $error and not ref($error) ) {
258       $dbh->rollback if $oldAutoCommit;
259       return "Error moving pkgnum ".$cust_pkg->pkgnum.": $error";
260     }
261   }
262
263   $old->disabled('Y');
264   $error = $old->replace;
265   if ( $error ) {
266     $dbh->rollback if $oldAutoCommit;
267     return "Error disabling old location: $error";
268   }
269
270   $dbh->commit if $oldAutoCommit;
271   return;
272 }
273
274 =back
275
276 =head1 BUGS
277
278 Not yet used for cust_main billing and shipping addresses.
279
280 =head1 SEE ALSO
281
282 L<FS::cust_main_county>, L<FS::cust_pkg>, L<FS::Record>,
283 schema.html from the base documentation.
284
285 =cut
286
287 1;
288