This commit was generated by cvs2svn to compensate for changes in r4407,
[freeside.git] / FS / FS / agent.pm
1 package FS::agent;
2
3 use strict;
4 use vars qw( @ISA );
5 use FS::Record qw( dbh qsearch qsearchs );
6 use FS::cust_main;
7 use FS::cust_pkg;
8 use FS::agent_type;
9 use FS::reg_code;
10 #use Crypt::YAPassGen;
11
12 @ISA = qw( FS::Record );
13
14 =head1 NAME
15
16 FS::agent - Object methods for agent records
17
18 =head1 SYNOPSIS
19
20   use FS::agent;
21
22   $record = new FS::agent \%hash;
23   $record = new FS::agent { 'column' => 'value' };
24
25   $error = $record->insert;
26
27   $error = $new_record->replace($old_record);
28
29   $error = $record->delete;
30
31   $error = $record->check;
32
33   $agent_type = $record->agent_type;
34
35   $hashref = $record->pkgpart_hashref;
36   #may purchase $pkgpart if $hashref->{$pkgpart};
37
38 =head1 DESCRIPTION
39
40 An FS::agent object represents an agent.  Every customer has an agent.  Agents
41 can be used to track things like resellers or salespeople.  FS::agent inherits
42 from FS::Record.  The following fields are currently supported:
43
44 =over 4
45
46 =item agentnum - primary key (assigned automatically for new agents)
47
48 =item agent - Text name of this agent
49
50 =item typenum - Agent type.  See L<FS::agent_type>
51
52 =item prog - For future use.
53
54 =item freq - For future use.
55
56 =item disabled - Disabled flag, empty or 'Y'
57
58 =item username - Username for the Agent interface
59
60 =item _password - Password for the Agent interface
61
62 =back
63
64 =head1 METHODS
65
66 =over 4
67
68 =item new HASHREF
69
70 Creates a new agent.  To add the agent to the database, see L<"insert">.
71
72 =cut
73
74 sub table { 'agent'; }
75
76 =item insert
77
78 Adds this agent to the database.  If there is an error, returns the error,
79 otherwise returns false.
80
81 =item delete
82
83 Deletes this agent from the database.  Only agents with no customers can be
84 deleted.  If there is an error, returns the error, otherwise returns false.
85
86 =cut
87
88 sub delete {
89   my $self = shift;
90
91   return "Can't delete an agent with customers!"
92     if qsearch( 'cust_main', { 'agentnum' => $self->agentnum } );
93
94   $self->SUPER::delete;
95 }
96
97 =item replace OLD_RECORD
98
99 Replaces OLD_RECORD with this one in the database.  If there is an error,
100 returns the error, otherwise returns false.
101
102 =item check
103
104 Checks all fields to make sure this is a valid agent.  If there is an error,
105 returns the error, otherwise returns false.  Called by the insert and replace
106 methods.
107
108 =cut
109
110 sub check {
111   my $self = shift;
112
113   my $error =
114     $self->ut_numbern('agentnum')
115       || $self->ut_text('agent')
116       || $self->ut_number('typenum')
117       || $self->ut_numbern('freq')
118       || $self->ut_textn('prog')
119   ;
120   return $error if $error;
121
122   if ( $self->dbdef_table->column('disabled') ) {
123     $error = $self->ut_enum('disabled', [ '', 'Y' ] );
124     return $error if $error;
125   }
126
127   if ( $self->dbdef_table->column('username') ) {
128     $error = $self->ut_alphan('username');
129     return $error if $error;
130     if ( length($self->username) ) {
131       my $conflict = qsearchs('agent', { 'username' => $self->username } );
132       return 'duplicate agent username (with '. $conflict->agent. ')'
133         if $conflict && $conflict->agentnum != $self->agentnum;
134       $error = $self->ut_text('password'); # ut_text... arbitrary choice
135     } else {
136       $self->_password('');
137     }
138   }
139
140   return "Unknown typenum!"
141     unless $self->agent_type;
142
143   $self->SUPER::check;
144 }
145
146 =item agent_type
147
148 Returns the FS::agent_type object (see L<FS::agent_type>) for this agent.
149
150 =cut
151
152 sub agent_type {
153   my $self = shift;
154   qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
155 }
156
157 =item pkgpart_hashref
158
159 Returns a hash reference.  The keys of the hash are pkgparts.  The value is
160 true if this agent may purchase the specified package definition.  See
161 L<FS::part_pkg>.
162
163 =cut
164
165 sub pkgpart_hashref {
166   my $self = shift;
167   $self->agent_type->pkgpart_hashref;
168 }
169
170 =item num_prospect_cust_main
171
172 Returns the number of prospects (customers with no packages ever ordered) for
173 this agent.
174
175 =cut
176
177 sub num_prospect_cust_main {
178   shift->num_sql(FS::cust_main->prospect_sql);
179 }
180
181 sub num_sql {
182   my( $self, $sql ) = @_;
183   my $statement = "SELECT COUNT(*) FROM cust_main WHERE agentnum = ? AND $sql";
184   my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
185   $sth->execute($self->agentnum) or die $sth->errstr. "executing $statement";
186   $sth->fetchrow_arrayref->[0];
187 }
188
189 =item prospect_cust_main
190
191 Returns the prospects (customers with no packages ever ordered) for this agent,
192 as cust_main objects.
193
194 =cut
195
196 sub prospect_cust_main {
197   shift->cust_main_sql(FS::cust_main->prospect_sql);
198 }
199
200 sub cust_main_sql {
201   my( $self, $sql ) = @_;
202   qsearch( 'cust_main',
203            { 'agentnum' => $self->agentnum },
204            '',
205            " AND $sql"
206   );
207 }
208
209 =item num_active_cust_main
210
211 Returns the number of active customers for this agent.
212
213 =cut
214
215 sub num_active_cust_main {
216   shift->num_sql(FS::cust_main->active_sql);
217 }
218
219 =item active_cust_main
220
221 Returns the active customers for this agent, as cust_main objects.
222
223 =cut
224
225 sub active_cust_main {
226   shift->cust_main_sql(FS::cust_main->active_sql);
227 }
228
229 =item num_susp_cust_main
230
231 Returns the number of suspended customers for this agent.
232
233 =cut
234
235 sub num_susp_cust_main {
236   shift->num_sql(FS::cust_main->susp_sql);
237 }
238
239 =item susp_cust_main
240
241 Returns the suspended customers for this agent, as cust_main objects.
242
243 =cut
244
245 sub susp_cust_main {
246   shift->cust_main_sql(FS::cust_main->susp_sql);
247 }
248
249 =item num_cancel_cust_main
250
251 Returns the number of cancelled customer for this agent.
252
253 =cut
254
255 sub num_cancel_cust_main {
256   shift->num_sql(FS::cust_main->cancel_sql);
257 }
258
259 =item cancel_cust_main
260
261 Returns the cancelled customers for this agent, as cust_main objects.
262
263 =cut
264
265 sub cancel_cust_main {
266   shift->cust_main_sql(FS::cust_main->cancel_sql);
267 }
268
269 =item num_active_cust_pkg
270
271 Returns the number of active customer packages for this agent.
272
273 =cut
274
275 sub num_active_cust_pkg {
276   shift->num_pkg_sql(FS::cust_pkg->active_sql);
277 }
278
279 sub num_pkg_sql {
280   my( $self, $sql ) = @_;
281   my $statement = 
282     "SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum )".
283     " WHERE agentnum = ? AND $sql";
284   my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
285   $sth->execute($self->agentnum) or die $sth->errstr. "executing $statement";
286   $sth->fetchrow_arrayref->[0];
287 }
288
289 =item num_susp_cust_pkg
290
291 Returns the number of suspended customer packages for this agent.
292
293 =cut
294
295 sub num_susp_cust_pkg {
296   shift->num_pkg_sql(FS::cust_pkg->susp_sql);
297 }
298
299 =item num_cancel_cust_pkg
300
301 Returns the number of cancelled customer packages for this agent.
302
303 =cut
304
305 sub num_cancel_cust_pkg {
306   shift->num_pkg_sql(FS::cust_pkg->cancel_sql);
307 }
308
309 =item generate_reg_codes NUM PKGPART_ARRAYREF
310
311 Generates the specified number of registration codes, allowing purchase of the
312 specified package definitions.  Returns an array reference of the newly
313 generated codes, or a scalar error message.
314
315 =cut
316
317 #false laziness w/prepay_credit::generate
318 sub generate_reg_codes {
319   my( $self, $num, $pkgparts ) = @_;
320
321   my @codeset = ( 'A'..'Z' );
322
323   local $SIG{HUP} = 'IGNORE';
324   local $SIG{INT} = 'IGNORE';
325   local $SIG{QUIT} = 'IGNORE';
326   local $SIG{TERM} = 'IGNORE';
327   local $SIG{TSTP} = 'IGNORE';
328   local $SIG{PIPE} = 'IGNORE';
329
330   my $oldAutoCommit = $FS::UID::AutoCommit;
331   local $FS::UID::AutoCommit = 0;
332   my $dbh = dbh;
333
334   my @codes = ();
335   for ( 1 ... $num ) {
336     my $reg_code = new FS::reg_code {
337       'agentnum' => $self->agentnum,
338       'code'     => join('', map($codeset[int(rand $#codeset)], (0..7) ) ),
339     };
340     my $error = $reg_code->insert($pkgparts);
341     if ( $error ) {
342       $dbh->rollback if $oldAutoCommit;
343       return $error;
344     }
345     push @codes, $reg_code->code;
346   }
347
348   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
349
350   \@codes;
351
352 }
353
354 =item num_reg_code
355
356 Returns the number of unused registration codes for this agent.
357
358 =cut
359
360 sub num_reg_code {
361   my $self = shift;
362   my $sth = dbh->prepare(
363     "SELECT COUNT(*) FROM reg_code WHERE agentnum = ?"
364   ) or die dbh->errstr;
365   $sth->execute($self->agentnum) or die $sth->errstr;
366   $sth->fetchrow_arrayref->[0];
367 }
368
369 =item num_prepay_credit
370
371 Returns the number of unused prepaid cards for this agent.
372
373 =cut
374
375 sub num_prepay_credit {
376   my $self = shift;
377   my $sth = dbh->prepare(
378     "SELECT COUNT(*) FROM prepay_credit WHERE agentnum = ?"
379   ) or die dbh->errstr;
380   $sth->execute($self->agentnum) or die $sth->errstr;
381   $sth->fetchrow_arrayref->[0];
382 }
383
384
385 =back
386
387 =head1 BUGS
388
389 =head1 SEE ALSO
390
391 L<FS::Record>, L<FS::agent_type>, L<FS::cust_main>, L<FS::part_pkg>, 
392 schema.html from the base documentation.
393
394 =cut
395
396 1;
397