so Search.tsf and Search.rdf work
[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::agent_type;
8 use FS::reg_code;
9 #use Crypt::YAPassGen;
10
11 @ISA = qw( FS::Record );
12
13 =head1 NAME
14
15 FS::agent - Object methods for agent records
16
17 =head1 SYNOPSIS
18
19   use FS::agent;
20
21   $record = new FS::agent \%hash;
22   $record = new FS::agent { 'column' => 'value' };
23
24   $error = $record->insert;
25
26   $error = $new_record->replace($old_record);
27
28   $error = $record->delete;
29
30   $error = $record->check;
31
32   $agent_type = $record->agent_type;
33
34   $hashref = $record->pkgpart_hashref;
35   #may purchase $pkgpart if $hashref->{$pkgpart};
36
37 =head1 DESCRIPTION
38
39 An FS::agent object represents an agent.  Every customer has an agent.  Agents
40 can be used to track things like resellers or salespeople.  FS::agent inherits
41 from FS::Record.  The following fields are currently supported:
42
43 =over 4
44
45 =item agentnum - primary key (assigned automatically for new agents)
46
47 =item agent - Text name of this agent
48
49 =item typenum - Agent type.  See L<FS::agent_type>
50
51 =item prog - For future use.
52
53 =item freq - For future use.
54
55 =item disabled - Disabled flag, empty or 'Y'
56
57 =item username - Username for the Agent interface
58
59 =item _password - Password for the Agent interface
60
61 =back
62
63 =head1 METHODS
64
65 =over 4
66
67 =item new HASHREF
68
69 Creates a new agent.  To add the agent to the database, see L<"insert">.
70
71 =cut
72
73 sub table { 'agent'; }
74
75 =item insert
76
77 Adds this agent to the database.  If there is an error, returns the error,
78 otherwise returns false.
79
80 =item delete
81
82 Deletes this agent from the database.  Only agents with no customers can be
83 deleted.  If there is an error, returns the error, otherwise returns false.
84
85 =cut
86
87 sub delete {
88   my $self = shift;
89
90   return "Can't delete an agent with customers!"
91     if qsearch( 'cust_main', { 'agentnum' => $self->agentnum } );
92
93   $self->SUPER::delete;
94 }
95
96 =item replace OLD_RECORD
97
98 Replaces OLD_RECORD with this one in the database.  If there is an error,
99 returns the error, otherwise returns false.
100
101 =item check
102
103 Checks all fields to make sure this is a valid agent.  If there is an error,
104 returns the error, otherwise returns false.  Called by the insert and replace
105 methods.
106
107 =cut
108
109 sub check {
110   my $self = shift;
111
112   my $error =
113     $self->ut_numbern('agentnum')
114       || $self->ut_text('agent')
115       || $self->ut_number('typenum')
116       || $self->ut_numbern('freq')
117       || $self->ut_textn('prog')
118   ;
119   return $error if $error;
120
121   if ( $self->dbdef_table->column('disabled') ) {
122     $error = $self->ut_enum('disabled', [ '', 'Y' ] );
123     return $error if $error;
124   }
125
126   if ( $self->dbdef_table->column('username') ) {
127     $error = $self->ut_alphan('username');
128     return $error if $error;
129     if ( length($self->username) ) {
130       my $conflict = qsearchs('agent', { 'username' => $self->username } );
131       return 'duplicate agent username (with '. $conflict->agent. ')'
132         if $conflict && $conflict->agentnum != $self->agentnum;
133       $error = $self->ut_text('password'); # ut_text... arbitrary choice
134     } else {
135       $self->_password('');
136     }
137   }
138
139   return "Unknown typenum!"
140     unless $self->agent_type;
141
142   $self->SUPER::check;
143 }
144
145 =item agent_type
146
147 Returns the FS::agent_type object (see L<FS::agent_type>) for this agent.
148
149 =cut
150
151 sub agent_type {
152   my $self = shift;
153   qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
154 }
155
156 =item pkgpart_hashref
157
158 Returns a hash reference.  The keys of the hash are pkgparts.  The value is
159 true if this agent may purchase the specified package definition.  See
160 L<FS::part_pkg>.
161
162 =cut
163
164 sub pkgpart_hashref {
165   my $self = shift;
166   $self->agent_type->pkgpart_hashref;
167 }
168
169 =item num_prospect_cust_main
170
171 Returns the number of prospects (customers with no packages ever ordered) for
172 this agent.
173
174 =cut
175
176 sub num_prospect_cust_main {
177   shift->num_sql(FS::cust_main->prospect_sql);
178 }
179
180 sub num_sql {
181   my( $self, $sql ) = @_;
182   my $sth = dbh->prepare(
183     "SELECT COUNT(*) FROM cust_main WHERE agentnum = ? AND $sql"
184   ) or die dbh->errstr;
185   $sth->execute($self->agentnum) or die $sth->errstr;
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 generate_reg_codes NUM PKGPART_ARRAYREF
270
271 Generates the specified number of registration codes, allowing purchase of the
272 specified package definitions.  Returns an array reference of the newly
273 generated codes, or a scalar error message.
274
275 =cut
276
277 #false laziness w/prepay_credit::generate
278 sub generate_reg_codes {
279   my( $self, $num, $pkgparts ) = @_;
280
281   my @codeset = ( 'A'..'Z' );
282
283   local $SIG{HUP} = 'IGNORE';
284   local $SIG{INT} = 'IGNORE';
285   local $SIG{QUIT} = 'IGNORE';
286   local $SIG{TERM} = 'IGNORE';
287   local $SIG{TSTP} = 'IGNORE';
288   local $SIG{PIPE} = 'IGNORE';
289
290   my $oldAutoCommit = $FS::UID::AutoCommit;
291   local $FS::UID::AutoCommit = 0;
292   my $dbh = dbh;
293
294   my @codes = ();
295   for ( 1 ... $num ) {
296     my $reg_code = new FS::reg_code {
297       'agentnum' => $self->agentnum,
298       'code'     => join('', map($codeset[int(rand $#codeset)], (0..7) ) ),
299     };
300     my $error = $reg_code->insert($pkgparts);
301     if ( $error ) {
302       $dbh->rollback if $oldAutoCommit;
303       return $error;
304     }
305     push @codes, $reg_code->code;
306   }
307
308   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
309
310   \@codes;
311
312 }
313
314 =item num_reg_code
315
316 Returns the number of unused registration codes for this agent.
317
318 =cut
319
320 sub num_reg_code {
321   my $self = shift;
322   my $sth = dbh->prepare(
323     "SELECT COUNT(*) FROM reg_code WHERE agentnum = ?"
324   ) or die dbh->errstr;
325   $sth->execute($self->agentnum) or die $sth->errstr;
326   $sth->fetchrow_arrayref->[0];
327 }
328
329 =item num_prepay_credit
330
331 Returns the number of unused prepaid cards for this agent.
332
333 =cut
334
335 sub num_prepay_credit {
336   my $self = shift;
337   my $sth = dbh->prepare(
338     "SELECT COUNT(*) FROM prepay_credit WHERE agentnum = ?"
339   ) or die dbh->errstr;
340   $sth->execute($self->agentnum) or die $sth->errstr;
341   $sth->fetchrow_arrayref->[0];
342 }
343
344
345 =back
346
347 =head1 BUGS
348
349 =head1 SEE ALSO
350
351 L<FS::Record>, L<FS::agent_type>, L<FS::cust_main>, L<FS::part_pkg>, 
352 schema.html from the base documentation.
353
354 =cut
355
356 1;
357