1 package FS::prospect_main;
2 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 );
14 #started as false laziness w/cust_main/Location.pm
20 # set up accessors for location fields
24 qw( address1 address2 city county state zip country district
25 latitude longitude coord_auto censustract censusyear geocode
28 foreach my $f (@location_fields) {
29 *{"FS::prospect_main::$f"} = sub {
30 carp "WARNING: tried to set cust_main.$f with accessor" if (@_ > 1);
31 my @cust_location = shift->cust_location or return '';
32 #arbitrarily picking the first because the UI only lets you add one
40 #debugging shim--probably a performance hit, so remove this at some point
44 if ( $DEBUG and grep { $_ eq $field } @location_fields ) {
45 carp "WARNING: tried to get() location field $field";
48 $self->FS::Record::get($field);
53 FS::prospect_main - Object methods for prospect_main records
57 use FS::prospect_main;
59 $record = new FS::prospect_main \%hash;
60 $record = new FS::prospect_main { 'column' => 'value' };
62 $error = $record->insert;
64 $error = $new_record->replace($old_record);
66 $error = $record->delete;
68 $error = $record->check;
72 An FS::prospect_main object represents a prospect. FS::prospect_main inherits
73 from FS::Record. The following fields are currently supported:
83 Agent (see L<FS::agent>)
87 Referral (see L<FS::part_referral>)
101 Creates a new prospect. To add the prospect to the database, see L<"insert">.
103 Note that this stores the hash reference, not a distinct copy of the hash it
104 points to. You can ask the object for a copy with the I<hash> method.
108 sub table { 'prospect_main'; }
112 Adds this record to the database. If there is an error, returns the error,
113 otherwise returns false.
120 warn "FS::prospect_main::insert called on $self with options ".
121 join(', ', map "$_=>$options{$_}", keys %options)
124 local $SIG{HUP} = 'IGNORE';
125 local $SIG{INT} = 'IGNORE';
126 local $SIG{QUIT} = 'IGNORE';
127 local $SIG{TERM} = 'IGNORE';
128 local $SIG{TSTP} = 'IGNORE';
129 local $SIG{PIPE} = 'IGNORE';
131 my $oldAutoCommit = $FS::UID::AutoCommit;
132 local $FS::UID::AutoCommit = 0;
135 warn " inserting prospect_main record" if $DEBUG;
136 my $error = $self->SUPER::insert;
138 $dbh->rollback if $oldAutoCommit;
142 if ( $options{'cust_location'} ) {
143 warn " inserting cust_location record" if $DEBUG;
144 my $cust_location = $options{'cust_location'};
145 $cust_location->prospectnum($self->prospectnum);
146 $error = $cust_location->insert;
148 $dbh->rollback if $oldAutoCommit;
153 warn " commiting transaction" if $DEBUG;
154 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
161 Delete this record from the database.
165 #delete dangling locations?
167 =item replace OLD_RECORD
169 Replaces the OLD_RECORD with this one in the database. If there is an error,
170 returns the error, otherwise returns false.
177 my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
183 warn "FS::prospect_main::replace called on $new to replace $old with options".
184 " ". join(', ', map "$_ => ". $options{$_}, keys %options)
187 local $SIG{HUP} = 'IGNORE';
188 local $SIG{INT} = 'IGNORE';
189 local $SIG{QUIT} = 'IGNORE';
190 local $SIG{TERM} = 'IGNORE';
191 local $SIG{TSTP} = 'IGNORE';
192 local $SIG{PIPE} = 'IGNORE';
194 my $oldAutoCommit = $FS::UID::AutoCommit;
195 local $FS::UID::AutoCommit = 0;
198 warn " replacing prospect_main record" if $DEBUG;
199 my $error = $new->SUPER::replace($old);
201 $dbh->rollback if $oldAutoCommit;
205 if ( $options{'cust_location'} ) {
206 my $cust_location = $options{'cust_location'};
207 $cust_location->prospectnum($new->prospectnum);
208 my $method = $cust_location->locationnum ? 'replace' : 'insert';
209 warn " ${method}ing cust_location record" if $DEBUG;
210 $error = $cust_location->$method();
212 $dbh->rollback if $oldAutoCommit;
215 } elsif ( exists($options{'cust_location'}) ) {
216 foreach my $cust_location (
217 qsearch('cust_location', { 'prospectnum' => $new->prospectnum } )
219 $error = $cust_location->delete();
221 $dbh->rollback if $oldAutoCommit;
227 warn " commiting transaction" if $DEBUG;
228 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
235 Checks all fields to make sure this is a valid prospect. If there is
236 an error, returns the error, otherwise returns false. Called by the insert
245 $self->ut_numbern('prospectnum')
246 || $self->ut_foreign_key( 'agentnum', 'agent', 'agentnum' )
247 || $self->ut_foreign_keyn( 'refnum', 'part_referral', 'refnum' )
248 || $self->ut_textn('company')
249 || $self->ut_foreign_keyn( 'taxstatusnum', 'tax_status', 'taxstatusnum' )
251 return $error if $error;
253 my $company = $self->company;
254 $company =~ s/^\s+//;
255 $company =~ s/\s+$//;
256 $company =~ s/\s+/ /g;
257 $self->company($company);
264 Returns a name for this prospect, as a string (company name for commercial
265 prospects, contact name for residential prospects).
271 return $self->company if $self->company;
273 my $prospect_contact = ($self->prospect_contact)[0]; #first contact? good enough for now
274 my $contact = $prospect_contact->contact if $prospect_contact;
275 return $contact->line if $prospect_contact && $contact;
279 'Prospect #'. $self->prospectnum;
282 =item contact_firstlast
284 If this prospect has a company, returns the empty string. If not, returns the
285 first contact's first and last name.
287 Primarily intended for use in quotation substitutions, like the FS::cust_main
288 method of the same name.
292 sub contact_firstlast {
294 return '' if $self->company;
295 my @contacts = $self->contact;
296 #return '' unless @contacts;
297 warn $contacts[0]->first;
298 warn $contacts[0]->get('last');
299 $contacts[0]->first. ' '. $contacts[0]->get('last');
304 Returns the contacts (see L<FS::contact>) associated with this prospect.
312 addl_from => ' JOIN prospect_contact USING (contactnum)',
313 extra_sql => ' WHERE prospect_contact.prospectnum = '.$self->prospectnum,
316 #classnum argument like FS::cust_main->contact?
323 Returns the locations (see L<FS::cust_location>) associated with this prospect.
330 'table' => 'cust_location',
331 'hashref' => { 'prospectnum' => $self->prospectnum,
334 'order_by' => 'ORDER BY country, LOWER(state), LOWER(city), LOWER(county), LOWER(address1), LOWER(address2)',
340 Returns the qualifications (see L<FS::qual>) associated with this prospect.
344 Returns the agent (see L<FS::agent>) for this customer.
348 Returns the external tax status, as an FS::tax_status object, or the empty
349 string if there is no tax status.
355 if ( $self->taxstatusnum ) {
356 qsearchs('tax_status', { 'taxstatusnum' => $self->taxstatusnum } );
364 Returns the tax status code if there is one.
370 my $tax_status = $self->tax_status;
372 ? $tax_status->taxstatus
376 =item convert_cust_main
378 Converts this prospect to a customer.
380 If there is an error, returns an error message, otherwise, returns the
381 newly-created FS::cust_main object.
385 sub convert_cust_main {
388 my @cust_location = $self->cust_location;
389 #the interface only allows one, so we're just gonna go with that for now
391 my @contact = map $_->contact, $self->prospect_contact;
393 #XXX i'm not compatible with cust_main-require_phone (which is kind of a
394 # pre-contact thing anyway)
396 my $cust_main = new FS::cust_main {
397 'bill_location' => $cust_location[0],
398 'ship_location' => $cust_location[0],
399 ( map { $_ => $self->$_ } qw( agentnum refnum company taxstatusnum ) ),
402 $cust_main->refnum( FS::Conf->new->config('referraldefault') || 1 )
403 unless $cust_main->refnum;
405 #XXX again, arbitrary, if one contact was "billing", that would be better
407 $cust_main->set($_, $contact[0]->get($_)) foreach qw( first last );
409 $cust_main->set('first', 'Unknown');
410 $cust_main->set('last', 'Unknown');
413 #v3 payby no longer allowed
414 #$cust_main->payby('BILL');
415 #$cust_main->paydate('12/2037');
417 $cust_main->insert( {},
418 'prospectnum' => $self->prospectnum,
427 Returns a qsearch hash expression to search for the parameters specified in
428 HASHREF. Valid parameters are:
439 my( $class, $params ) = @_;
445 if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
447 "prospect_main.agentnum = $1";
451 if ( $params->{'refnum'} =~ /^(\d+)$/ and $1 ) {
453 "prospect_main.refnum = $1";
457 # setup queries, subs, etc. for the search
460 $orderby ||= 'ORDER BY prospectnum';
462 # here is the agent virtualization
463 push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
465 my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
467 my $count_query = "SELECT COUNT(*) FROM prospect_main $extra_sql";
470 'table' => 'prospect_main',
471 #'select' => $select,
473 'extra_sql' => $extra_sql,
474 'order_by' => $orderby,
475 'count_query' => $count_query,
476 #'extra_headers' => \@extra_headers,
477 #'extra_fields' => \@extra_fields,
482 # stub this so that calling ->cust_bill doesn't return an empty string
487 # XXX should have real localization here eventually
489 FS::Conf->new->config('locale');
498 L<FS::Record>, schema.html from the base documentation.