1 package FS::prospect_main;
4 use base qw( FS::Quotable_Mixin FS::o2m_Common FS::Record );
5 use vars qw( $DEBUG @location_fields );
6 use Scalar::Util qw( blessed );
8 use FS::Record qw( dbh qsearch qsearchs );
10 use FS::cust_location;
14 use FS::part_referral;
18 #started as false laziness w/cust_main/Location.pm
24 # set up accessors for location fields
28 qw( address1 address2 city county state zip country district
29 latitude longitude coord_auto censustract censusyear geocode
32 foreach my $f (@location_fields) {
33 *{"FS::prospect_main::$f"} = sub {
34 carp "WARNING: tried to set cust_main.$f with accessor" if (@_ > 1);
35 my @cust_location = shift->cust_location or return '';
36 #arbitrarily picking the first because the UI only lets you add one
44 #debugging shim--probably a performance hit, so remove this at some point
48 if ( $DEBUG and grep { $_ eq $field } @location_fields ) {
49 carp "WARNING: tried to get() location field $field";
52 $self->FS::Record::get($field);
57 FS::prospect_main - Object methods for prospect_main records
61 use FS::prospect_main;
63 $record = new FS::prospect_main \%hash;
64 $record = new FS::prospect_main { 'column' => 'value' };
66 $error = $record->insert;
68 $error = $new_record->replace($old_record);
70 $error = $record->delete;
72 $error = $record->check;
76 An FS::prospect_main object represents a prospect. FS::prospect_main inherits
77 from FS::Record. The following fields are currently supported:
87 Agent (see L<FS::agent>)
91 Referral (see L<FS::part_referral>)
105 Creates a new prospect. To add the prospect to the database, see L<"insert">.
107 Note that this stores the hash reference, not a distinct copy of the hash it
108 points to. You can ask the object for a copy with the I<hash> method.
112 sub table { 'prospect_main'; }
116 Adds this record to the database. If there is an error, returns the error,
117 otherwise returns false.
124 warn "FS::prospect_main::insert called on $self with options ".
125 join(', ', map "$_=>$options{$_}", keys %options)
128 local $SIG{HUP} = 'IGNORE';
129 local $SIG{INT} = 'IGNORE';
130 local $SIG{QUIT} = 'IGNORE';
131 local $SIG{TERM} = 'IGNORE';
132 local $SIG{TSTP} = 'IGNORE';
133 local $SIG{PIPE} = 'IGNORE';
135 my $oldAutoCommit = $FS::UID::AutoCommit;
136 local $FS::UID::AutoCommit = 0;
139 warn " inserting prospect_main record" if $DEBUG;
140 my $error = $self->SUPER::insert;
142 $dbh->rollback if $oldAutoCommit;
146 if ( $options{'cust_location'} ) {
147 warn " inserting cust_location record" if $DEBUG;
148 my $cust_location = $options{'cust_location'};
149 $cust_location->prospectnum($self->prospectnum);
150 $error = $cust_location->insert;
152 $dbh->rollback if $oldAutoCommit;
157 warn " commiting transaction" if $DEBUG;
158 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
165 Delete this record from the database.
169 #delete dangling locations?
171 =item replace OLD_RECORD
173 Replaces the OLD_RECORD with this one in the database. If there is an error,
174 returns the error, otherwise returns false.
181 my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
187 warn "FS::prospect_main::replace called on $new to replace $old with options".
188 " ". join(', ', map "$_ => ". $options{$_}, keys %options)
191 local $SIG{HUP} = 'IGNORE';
192 local $SIG{INT} = 'IGNORE';
193 local $SIG{QUIT} = 'IGNORE';
194 local $SIG{TERM} = 'IGNORE';
195 local $SIG{TSTP} = 'IGNORE';
196 local $SIG{PIPE} = 'IGNORE';
198 my $oldAutoCommit = $FS::UID::AutoCommit;
199 local $FS::UID::AutoCommit = 0;
202 warn " replacing prospect_main record" if $DEBUG;
203 my $error = $new->SUPER::replace($old);
205 $dbh->rollback if $oldAutoCommit;
209 if ( $options{'cust_location'} ) {
210 my $cust_location = $options{'cust_location'};
211 $cust_location->prospectnum($new->prospectnum);
212 my $method = $cust_location->locationnum ? 'replace' : 'insert';
213 warn " ${method}ing cust_location record" if $DEBUG;
214 $error = $cust_location->$method();
216 $dbh->rollback if $oldAutoCommit;
219 } elsif ( exists($options{'cust_location'}) ) {
220 foreach my $cust_location (
221 qsearch('cust_location', { 'prospectnum' => $new->prospectnum } )
223 $error = $cust_location->delete();
225 $dbh->rollback if $oldAutoCommit;
231 warn " commiting transaction" if $DEBUG;
232 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
239 Checks all fields to make sure this is a valid prospect. If there is
240 an error, returns the error, otherwise returns false. Called by the insert
249 $self->ut_numbern('prospectnum')
250 || $self->ut_foreign_key( 'agentnum', 'agent', 'agentnum' )
251 || $self->ut_foreign_key( 'refnum', 'part_referral', 'refnum' )
252 || $self->ut_textn('company')
254 return $error if $error;
256 my $company = $self->company;
257 $company =~ s/^\s+//;
258 $company =~ s/\s+$//;
259 $company =~ s/\s+/ /g;
260 $self->company($company);
267 Returns a name for this prospect, as a string (company name for commercial
268 prospects, contact name for residential prospects).
274 return $self->company if $self->company;
276 my $contact = ($self->contact)[0]; #first contact? good enough for now
277 return $contact->line if $contact;
279 'Prospect #'. $self->prospectnum;
284 Returns the contacts (see L<FS::contact>) associated with this prospect.
290 qsearch( 'contact', { 'prospectnum' => $self->prospectnum } );
295 Returns the locations (see L<FS::cust_location>) associated with this prospect.
301 qsearch( 'cust_location', { 'prospectnum' => $self->prospectnum,
307 Returns the qualifications (see L<FS::qual>) associated with this prospect.
313 qsearch( 'qual', { 'prospectnum' => $self->prospectnum } );
318 Returns the agent (see L<FS::agent>) for this customer.
324 qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
329 Returns the advertising source (see L<FS::part_referral>) for this customer.
335 qsearchs( 'part_referral', { 'refnum' => $self->refnum } );
338 =item convert_cust_main
340 Converts this prospect to a customer.
342 If there is an error, returns an error message, otherwise, returns the
343 newly-created FS::cust_main object.
347 sub convert_cust_main {
350 my @cust_location = $self->cust_location;
351 #the interface only allows one, so we're just gonna go with that for now
353 my @contact = $self->contact;
355 #XXX define one contact type as "billing", then we could pick just that one
356 my @invoicing_list = map $_->emailaddress, map $_->contact_email, @contact;
358 #XXX i'm not compatible with cust_main-require_phone (which is kind of a
359 # pre-contact thing anyway)
361 my $cust_main = new FS::cust_main {
362 'bill_location' => $cust_location[0],
363 'ship_location' => $cust_location[0],
364 ( map { $_ => $self->$_ } qw( agentnum refnum company ) ),
367 $cust_main->refnum( FS::Conf->new->config('referraldefault') || 1 )
368 unless $cust_main->refnum;
370 #XXX again, arbitrary, if one contact was "billing", that would be better
372 $cust_main->set($_, $contact[0]->get($_)) foreach qw( first last );
374 $cust_main->set('first', 'Unknown');
375 $cust_main->set('last', 'Unknown');
379 $cust_main->payby('BILL');
380 $cust_main->paydate('12/2037');
382 $cust_main->insert( {}, \@invoicing_list,
383 'prospectnum' => $self->prospectnum,
392 Returns a qsearch hash expression to search for the parameters specified in
393 HASHREF. Valid parameters are:
404 my( $class, $params ) = @_;
410 if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
412 "prospect_main.agentnum = $1";
416 if ( $params->{'refnum'} =~ /^(\d+)$/ and $1 ) {
418 "prospect_main.refnum = $1";
422 # setup queries, subs, etc. for the search
425 $orderby ||= 'ORDER BY prospectnum';
427 # here is the agent virtualization
428 push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
430 my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
432 my $count_query = "SELECT COUNT(*) FROM prospect_main $extra_sql";
435 'table' => 'prospect_main',
436 #'select' => $select,
438 'extra_sql' => $extra_sql,
439 'order_by' => $orderby,
440 'count_query' => $count_query,
441 #'extra_headers' => \@extra_headers,
442 #'extra_fields' => \@extra_fields,
453 L<FS::Record>, schema.html from the base documentation.