autoload methods returning foreign records, RT#13971
[freeside.git] / FS / FS / prospect_main.pm
1 package FS::prospect_main;
2 use base qw( FS::Quotable_Mixin FS::o2m_Common FS::Record );
3
4 use strict;
5 use vars qw( $DEBUG @location_fields );
6 use Scalar::Util qw( blessed );
7 use FS::Record qw( dbh qsearch ); # qsearchs );
8 use FS::cust_location;
9
10 $DEBUG = 0;
11
12 #started as false laziness w/cust_main/Location.pm
13
14 use Carp qw(carp);
15
16 my $init = 0;
17 BEGIN {
18   # set up accessors for location fields
19   if (!$init) {
20     no strict 'refs';
21     @location_fields = 
22       qw( address1 address2 city county state zip country district
23         latitude longitude coord_auto censustract censusyear geocode
24         addr_clean );
25
26     foreach my $f (@location_fields) {
27       *{"FS::prospect_main::$f"} = sub {
28         carp "WARNING: tried to set cust_main.$f with accessor" if (@_ > 1);
29         my @cust_location = shift->cust_location or return '';
30         #arbitrarily picking the first because the UI only lets you add one
31         $cust_location[0]->$f
32       };
33     }
34     $init++;
35   }
36 }
37
38 #debugging shim--probably a performance hit, so remove this at some point
39 sub get {
40   my $self = shift;
41   my $field = shift;
42   if ( $DEBUG and grep { $_ eq $field } @location_fields ) {
43     carp "WARNING: tried to get() location field $field";
44     $self->$field;
45   }
46   $self->FS::Record::get($field);
47 }
48
49 =head1 NAME
50
51 FS::prospect_main - Object methods for prospect_main records
52
53 =head1 SYNOPSIS
54
55   use FS::prospect_main;
56
57   $record = new FS::prospect_main \%hash;
58   $record = new FS::prospect_main { 'column' => 'value' };
59
60   $error = $record->insert;
61
62   $error = $new_record->replace($old_record);
63
64   $error = $record->delete;
65
66   $error = $record->check;
67
68 =head1 DESCRIPTION
69
70 An FS::prospect_main object represents a prospect.  FS::prospect_main inherits
71 from FS::Record.  The following fields are currently supported:
72
73 =over 4
74
75 =item prospectnum
76
77 primary key
78
79 =item agentnum
80
81 Agent
82
83 =item company
84
85 company
86
87 =back
88
89 =head1 METHODS
90
91 =over 4
92
93 =item new HASHREF
94
95 Creates a new prospect.  To add the prospect to the database, see L<"insert">.
96
97 Note that this stores the hash reference, not a distinct copy of the hash it
98 points to.  You can ask the object for a copy with the I<hash> method.
99
100 =cut
101
102 sub table { 'prospect_main'; }
103
104 =item insert
105
106 Adds this record to the database.  If there is an error, returns the error,
107 otherwise returns false.
108
109 =cut
110
111 sub insert {
112   my $self = shift;
113   my %options = @_;
114   warn "FS::prospect_main::insert called on $self with options ".
115        join(', ', map "$_=>$options{$_}", keys %options)
116     if $DEBUG;
117
118   local $SIG{HUP} = 'IGNORE';
119   local $SIG{INT} = 'IGNORE';
120   local $SIG{QUIT} = 'IGNORE';
121   local $SIG{TERM} = 'IGNORE';
122   local $SIG{TSTP} = 'IGNORE';
123   local $SIG{PIPE} = 'IGNORE';
124
125   my $oldAutoCommit = $FS::UID::AutoCommit;
126   local $FS::UID::AutoCommit = 0;
127   my $dbh = dbh;
128
129   warn "  inserting prospect_main record" if $DEBUG;
130   my $error = $self->SUPER::insert;
131   if ( $error ) {
132     $dbh->rollback if $oldAutoCommit;
133     return $error;
134   }
135
136   if ( $options{'cust_location'} ) {
137     warn "  inserting cust_location record" if $DEBUG;
138     my $cust_location = $options{'cust_location'};
139     $cust_location->prospectnum($self->prospectnum);
140     $error = $cust_location->insert;
141     if ( $error ) {
142       $dbh->rollback if $oldAutoCommit;
143       return $error;
144     }
145   }
146
147   warn "  commiting transaction" if $DEBUG;
148   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
149
150   '';
151 }
152
153 =item delete
154
155 Delete this record from the database.
156
157 =cut
158
159 #delete dangling locations?
160
161 =item replace OLD_RECORD
162
163 Replaces the OLD_RECORD with this one in the database.  If there is an error,
164 returns the error, otherwise returns false.
165
166 =cut
167
168 sub replace {
169   my $new = shift;
170
171   my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
172               ? shift
173               : $new->replace_old;
174
175   my %options = @_;
176
177   warn "FS::prospect_main::replace called on $new to replace $old with options".
178        " ". join(', ', map "$_ => ". $options{$_}, keys %options)
179     if $DEBUG;
180
181   local $SIG{HUP} = 'IGNORE';
182   local $SIG{INT} = 'IGNORE';
183   local $SIG{QUIT} = 'IGNORE';
184   local $SIG{TERM} = 'IGNORE';
185   local $SIG{TSTP} = 'IGNORE';
186   local $SIG{PIPE} = 'IGNORE';
187
188   my $oldAutoCommit = $FS::UID::AutoCommit;
189   local $FS::UID::AutoCommit = 0;
190   my $dbh = dbh;
191
192   warn "  replacing prospect_main record" if $DEBUG;
193   my $error = $new->SUPER::replace($old);
194   if ( $error ) {
195     $dbh->rollback if $oldAutoCommit;
196     return $error;
197   }
198
199   if ( $options{'cust_location'} ) {
200     my $cust_location = $options{'cust_location'};
201     $cust_location->prospectnum($new->prospectnum);
202     my $method = $cust_location->locationnum ? 'replace' : 'insert';
203     warn "  ${method}ing cust_location record" if $DEBUG;
204     $error = $cust_location->$method();
205     if ( $error ) {
206       $dbh->rollback if $oldAutoCommit;
207       return $error;
208     }
209   } elsif ( exists($options{'cust_location'}) ) {
210     foreach my $cust_location (
211       qsearch('cust_location', { 'prospectnum' => $new->prospectnum } )
212     ) {
213       $error = $cust_location->delete();
214       if ( $error ) {
215         $dbh->rollback if $oldAutoCommit;
216         return $error;
217       }
218     }
219   }
220
221   warn "  commiting transaction" if $DEBUG;
222   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
223
224   '';
225 }
226
227 =item check
228
229 Checks all fields to make sure this is a valid prospect.  If there is
230 an error, returns the error, otherwise returns false.  Called by the insert
231 and replace methods.
232
233 =cut
234
235 sub check {
236   my $self = shift;
237
238   my $error = 
239     $self->ut_numbern('prospectnum')
240     || $self->ut_foreign_key('agentnum', 'agent', 'agentnum' )
241     || $self->ut_textn('company')
242   ;
243   return $error if $error;
244
245   my $company = $self->company;
246   $company =~ s/^\s+//; 
247   $company =~ s/\s+$//; 
248   $company =~ s/\s+/ /g;
249   $self->company($company);
250
251   $self->SUPER::check;
252 }
253
254 =item name
255
256 Returns a name for this prospect, as a string (company name for commercial
257 prospects, contact name for residential prospects).
258
259 =cut
260
261 sub name {
262   my $self = shift;
263   return $self->company if $self->company;
264
265   my $contact = ($self->contact)[0]; #first contact?  good enough for now
266   return $contact->line if $contact;
267
268   'Prospect #'. $self->prospectnum;
269 }
270
271 =item contact
272
273 Returns the contacts (see L<FS::contact>) associated with this prospect.
274
275 =item cust_location
276
277 Returns the locations (see L<FS::cust_location>) associated with this prospect.
278
279 =cut
280
281 sub cust_location {
282   my $self = shift;
283   qsearch( 'cust_location', { 'prospectnum' => $self->prospectnum,
284                               'custnum'     => '' } );
285 }
286
287 =item qual
288
289 Returns the qualifications (see L<FS::qual>) associated with this prospect.
290
291 =item agent
292
293 Returns the agent (see L<FS::agent>) for this customer.
294
295 =item search HASHREF
296
297 (Class method)
298
299 Returns a qsearch hash expression to search for the parameters specified in
300 HASHREF.  Valid parameters are:
301
302 =over 4
303
304 =item agentnum
305
306 =back
307
308 =cut
309
310 sub search {
311   my( $class, $params ) = @_;
312
313   my @where = ();
314   my $orderby;
315
316   ##
317   # parse agent
318   ##
319
320   if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
321     push @where,
322       "prospect_main.agentnum = $1";
323   }
324
325   ##
326   # setup queries, subs, etc. for the search
327   ##
328
329   $orderby ||= 'ORDER BY prospectnum';
330
331   # here is the agent virtualization
332   push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
333
334   my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
335
336   my $count_query = "SELECT COUNT(*) FROM prospect_main $extra_sql";
337   
338   my $sql_query = {
339     'table'         => 'prospect_main',
340     #'select'        => $select,
341     'hashref'       => {},
342     'extra_sql'     => $extra_sql,
343     'order_by'      => $orderby,
344     'count_query'   => $count_query,
345     #'extra_headers' => \@extra_headers,
346     #'extra_fields'  => \@extra_fields,
347   };
348
349 }
350
351 =back
352
353 =head1 BUGS
354
355 =head1 SEE ALSO
356
357 L<FS::Record>, schema.html from the base documentation.
358
359 =cut
360
361 1;
362