per-agent disable_previous_balance, #15863
[freeside.git] / FS / FS / contact.pm
1 package FS::contact;
2
3 use strict;
4 use base qw( FS::Record );
5 use FS::Record qw( qsearch qsearchs dbh );
6 use FS::prospect_main;
7 use FS::cust_main;
8 use FS::cust_location;
9 use FS::contact_phone;
10 use FS::contact_email;
11
12 =head1 NAME
13
14 FS::contact - Object methods for contact records
15
16 =head1 SYNOPSIS
17
18   use FS::contact;
19
20   $record = new FS::contact \%hash;
21   $record = new FS::contact { 'column' => 'value' };
22
23   $error = $record->insert;
24
25   $error = $new_record->replace($old_record);
26
27   $error = $record->delete;
28
29   $error = $record->check;
30
31 =head1 DESCRIPTION
32
33 An FS::contact object represents an example.  FS::contact inherits from
34 FS::Record.  The following fields are currently supported:
35
36 =over 4
37
38 =item contactnum
39
40 primary key
41
42 =item prospectnum
43
44 prospectnum
45
46 =item custnum
47
48 custnum
49
50 =item locationnum
51
52 locationnum
53
54 =item last
55
56 last
57
58 =item first
59
60 first
61
62 =item title
63
64 title
65
66 =item comment
67
68 comment
69
70 =item disabled
71
72 disabled
73
74
75 =back
76
77 =head1 METHODS
78
79 =over 4
80
81 =item new HASHREF
82
83 Creates a new example.  To add the example to the database, see L<"insert">.
84
85 Note that this stores the hash reference, not a distinct copy of the hash it
86 points to.  You can ask the object for a copy with the I<hash> method.
87
88 =cut
89
90 # the new method can be inherited from FS::Record, if a table method is defined
91
92 sub table { 'contact'; }
93
94 =item insert
95
96 Adds this record to the database.  If there is an error, returns the error,
97 otherwise returns false.
98
99 =cut
100
101 sub insert {
102   my $self = shift;
103
104   local $SIG{INT} = 'IGNORE';
105   local $SIG{QUIT} = 'IGNORE';
106   local $SIG{TERM} = 'IGNORE';
107   local $SIG{TSTP} = 'IGNORE';
108   local $SIG{PIPE} = 'IGNORE';
109
110   my $oldAutoCommit = $FS::UID::AutoCommit;
111   local $FS::UID::AutoCommit = 0;
112   my $dbh = dbh;
113
114   my $error = $self->SUPER::insert;
115   if ( $error ) {
116     $dbh->rollback if $oldAutoCommit;
117     return $error;
118   }
119
120   foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) =~ /\S/ }
121                         keys %{ $self->hashref } ) {
122     $pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)";
123     my $phonetypenum = $1;
124
125     my $contact_phone = new FS::contact_phone {
126       'contactnum' => $self->contactnum,
127       'phonetypenum' => $phonetypenum,
128       _parse_phonestring( $self->get($pf) ),
129     };
130     $error = $contact_phone->insert;
131     if ( $error ) {
132       $dbh->rollback if $oldAutoCommit;
133       return $error;
134     }
135   }
136
137   if ( $self->get('emailaddress') =~ /\S/ ) {
138     my $contact_email = new FS::contact_email {
139       'contactnum'   => $self->contactnum,
140       'emailaddress' => $self->get('emailaddress'),
141     };
142     $error = $contact_email->insert;
143     if ( $error ) {
144       $dbh->rollback if $oldAutoCommit;
145       return $error;
146     }
147   }
148
149   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
150
151   '';
152
153 }
154
155 =item delete
156
157 Delete this record from the database.
158
159 =cut
160
161 # the delete method can be inherited from FS::Record
162
163 # XXX delete contact_phone, contact_email
164
165 =item replace OLD_RECORD
166
167 Replaces the OLD_RECORD with this one in the database.  If there is an error,
168 returns the error, otherwise returns false.
169
170 =cut
171
172 sub replace {
173   my $self = shift;
174
175   local $SIG{INT} = 'IGNORE';
176   local $SIG{QUIT} = 'IGNORE';
177   local $SIG{TERM} = 'IGNORE';
178   local $SIG{TSTP} = 'IGNORE';
179   local $SIG{PIPE} = 'IGNORE';
180
181   my $oldAutoCommit = $FS::UID::AutoCommit;
182   local $FS::UID::AutoCommit = 0;
183   my $dbh = dbh;
184
185   my $error = $self->SUPER::replace(@_);
186   if ( $error ) {
187     $dbh->rollback if $oldAutoCommit;
188     return $error;
189   }
190
191   foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) }
192                         keys %{ $self->hashref } ) {
193     $pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)";
194     my $phonetypenum = $1;
195
196     my %cp = ( 'contactnum'   => $self->contactnum,
197                'phonetypenum' => $phonetypenum,
198              );
199     my $contact_phone = qsearchs('contact_phone', \%cp)
200                         || new FS::contact_phone   \%cp;
201
202     my %cpd = _parse_phonestring( $self->get($pf) );
203     $contact_phone->set( $_ => $cpd{$_} ) foreach keys %cpd;
204
205     my $method = $contact_phone->contactphonenum ? 'replace' : 'insert';
206
207     $error = $contact_phone->$method;
208     if ( $error ) {
209       $dbh->rollback if $oldAutoCommit;
210       return $error;
211     }
212   }
213
214   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
215
216   '';
217
218 }
219
220 #i probably belong in contact_phone.pm
221 sub _parse_phonestring {
222   my $value = shift;
223
224   my($countrycode, $extension) = ('1', '');
225
226   #countrycode
227   if ( $value =~ s/^\s*\+\s*(\d+)// ) {
228     $countrycode = $1;
229   } else {
230     $value =~ s/^\s*1//;
231   }
232   #extension
233   if ( $value =~ s/\s*(ext|x)\s*(\d+)\s*$//i ) {
234      $extension = $2;
235   }
236
237   ( 'countrycode' => $countrycode,
238     'phonenum'    => $value,
239     'extension'   => $extension,
240   );
241 }
242
243 =item check
244
245 Checks all fields to make sure this is a valid example.  If there is
246 an error, returns the error, otherwise returns false.  Called by the insert
247 and replace methods.
248
249 =cut
250
251 # the check method should currently be supplied - FS::Record contains some
252 # data checking routines
253
254 sub check {
255   my $self = shift;
256
257   my $error = 
258     $self->ut_numbern('contactnum')
259     || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum')
260     || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum')
261     || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum')
262     || $self->ut_textn('last')
263     || $self->ut_textn('first')
264     || $self->ut_textn('title')
265     || $self->ut_textn('comment')
266     || $self->ut_enum('disabled', [ '', 'Y' ])
267   ;
268   return $error if $error;
269
270   return "No prospect or customer!" unless $self->prospectnum || $self->custnum;
271   return "Prospect and customer!"       if $self->prospectnum && $self->custnum;
272
273   return "One of first name, last name, or title must have a value"
274     if ! grep $self->$_(), qw( first last title);
275
276   $self->SUPER::check;
277 }
278
279 sub line {
280   my $self = shift;
281   my $data = $self->first. ' '. $self->last;
282   $data .= ', '. $self->title
283     if $self->title;
284   $data .= ' ('. $self->comment. ')'
285     if $self->comment;
286   $data;
287 }
288
289 =back
290
291 =head1 BUGS
292
293 =head1 SEE ALSO
294
295 L<FS::Record>, schema.html from the base documentation.
296
297 =cut
298
299 1;
300