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