4 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
6 use FS::UID qw(myconnect);
8 use FS::Record qw( qsearch qsearchs dbh );
14 @ISA = qw(FS::Record);
15 @EXPORT_OK = qw( joblisting );
19 $FS::UID::callback{'FS::queue'} = sub {
27 FS::queue - Object methods for queue records
33 $record = new FS::queue \%hash;
34 $record = new FS::queue { 'column' => 'value' };
36 $error = $record->insert;
38 $error = $new_record->replace($old_record);
40 $error = $record->delete;
42 $error = $record->check;
46 An FS::queue object represents an queued job. FS::queue inherits from
47 FS::Record. The following fields are currently supported:
51 =item jobnum - primary key
53 =item job - fully-qualified subroutine name
55 =item status - job status
57 =item statustext - freeform text status message
59 =item _date - UNIX timestamp
61 =item svcnum - optional link to service (see L<FS::cust_svc>)
71 Creates a new job. To add the example to the database, see L<"insert">.
73 Note that this stores the hash reference, not a distinct copy of the hash it
74 points to. You can ask the object for a copy with the I<hash> method.
78 # the new method can be inherited from FS::Record, if a table method is defined
80 sub table { 'queue'; }
82 =item insert [ ARGUMENT, ARGUMENT... ]
84 Adds this record to the database. If there is an error, returns the error,
85 otherwise returns false.
87 If any arguments are supplied, a queue_arg record for each argument is also
88 created (see L<FS::queue_arg>).
92 #false laziness w/part_export.pm
96 local $SIG{HUP} = 'IGNORE';
97 local $SIG{INT} = 'IGNORE';
98 local $SIG{QUIT} = 'IGNORE';
99 local $SIG{TERM} = 'IGNORE';
100 local $SIG{TSTP} = 'IGNORE';
101 local $SIG{PIPE} = 'IGNORE';
103 my $oldAutoCommit = $FS::UID::AutoCommit;
104 local $FS::UID::AutoCommit = 0;
107 my $error = $self->SUPER::insert;
109 $dbh->rollback if $oldAutoCommit;
113 foreach my $arg ( @_ ) {
114 my $queue_arg = new FS::queue_arg ( {
115 'jobnum' => $self->jobnum,
118 $error = $queue_arg->insert;
120 $dbh->rollback if $oldAutoCommit;
126 warn "jobnums global is active: $jobnums\n" if $DEBUG;
127 push @$jobnums, $self->jobnum;
130 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
138 Delete this record from the database. Any corresponding queue_arg records are
146 local $SIG{HUP} = 'IGNORE';
147 local $SIG{INT} = 'IGNORE';
148 local $SIG{QUIT} = 'IGNORE';
149 local $SIG{TERM} = 'IGNORE';
150 local $SIG{TSTP} = 'IGNORE';
151 local $SIG{PIPE} = 'IGNORE';
153 my $oldAutoCommit = $FS::UID::AutoCommit;
154 local $FS::UID::AutoCommit = 0;
157 my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
158 push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
160 my $error = $self->SUPER::delete;
162 $dbh->rollback if $oldAutoCommit;
166 foreach my $del ( @del ) {
167 $error = $del->delete;
169 $dbh->rollback if $oldAutoCommit;
174 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
180 =item replace OLD_RECORD
182 Replaces the OLD_RECORD with this one in the database. If there is an error,
183 returns the error, otherwise returns false.
187 # the replace method can be inherited from FS::Record
191 Checks all fields to make sure this is a valid job. If there is
192 an error, returns the error, otherwise returns false. Called by the insert
200 $self->ut_numbern('jobnum')
201 || $self->ut_anything('job')
202 || $self->ut_numbern('_date')
203 || $self->ut_enum('status',['', qw( new locked failed )])
204 || $self->ut_anything('statustext')
205 || $self->ut_numbern('svcnum')
207 return $error if $error;
209 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
210 $self->svcnum('') if $error;
212 $self->status('new') unless $self->status;
213 $self->_date(time) unless $self->_date;
220 Returns a list of the arguments associated with this job.
226 map $_->arg, qsearch( 'queue_arg',
227 { 'jobnum' => $self->jobnum },
235 Returns the FS::cust_svc object associated with this job, if any.
241 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
246 Returns the FS::queue_depend objects associated with this job, if any.
247 (Dependancies that must complete before this job can be run).
253 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
256 =item depend_insert OTHER_JOBNUM
258 Inserts a dependancy for this job - it will not be run until the other job
259 specified completes. If there is an error, returns the error, otherwise
262 When using job dependancies, you should wrap the insertion of all relevant jobs
263 in a database transaction.
268 my($self, $other_jobnum) = @_;
269 my $queue_depend = new FS::queue_depend ( {
270 'jobnum' => $self->jobnum,
271 'depend_jobnum' => $other_jobnum,
273 $queue_depend->insert;
278 Returns the FS::queue_depend objects that associate other jobs with this job,
279 if any. (The jobs that are waiting for this job to complete before they can
286 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
289 =item depended_delete
291 Deletes the other queued jobs (FS::queue objects) that are waiting for this
292 job, if any. If there is an error, returns the error, otherwise returns false.
296 sub depended_delete {
300 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
302 $error = $job->depended_delete;
303 return $error if $error;
304 $error = $job->delete;
305 return $error if $error
309 =item update_statustext VALUE
311 Updates the statustext value of this job to supplied value, in the database.
312 If there is an error, returns the error, otherwise returns false.
316 use vars qw($_update_statustext_dbh);
317 sub update_statustext {
318 my( $self, $statustext ) = @_;
319 return '' if $statustext eq $self->statustext;
320 warn "updating statustext for $self to $statustext" if $DEBUG;
322 $_update_statustext_dbh ||= myconnect;
324 my $sth = $_update_statustext_dbh->prepare(
325 'UPDATE queue set statustext = ? WHERE jobnum = ?'
326 ) or return $_update_statustext_dbh->errstr;
328 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
329 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
330 $self->statustext($statustext);
333 #my $new = new FS::queue { $self->hash };
334 #$new->statustext($statustext);
335 #my $error = $new->replace($self);
336 #return $error if $error;
337 #$self->statustext($statustext);
347 =item joblisting HASHREF NOACTIONS
352 my($hashref, $noactions) = @_;
358 my @queue = qsearch( 'queue', $hashref );
359 return '' unless scalar(@queue);
361 my $p = FS::CGI::popurl(2);
363 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
364 FS::CGI::table(). <<END;
366 <TH COLSPAN=2>Job</TH>
371 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
374 my $dangerous = $conf->exists('queue_dangerous_controls');
378 foreach my $queue ( sort {
379 $a->getfield('jobnum') <=> $b->getfield('jobnum')
381 my $queue_hashref = $queue->hashref;
382 my $jobnum = $queue->jobnum;
385 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
386 $args = encode_entities( join(' ', $queue->args) );
391 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
392 my $status = $queue->status;
393 $status .= ': '. $queue->statustext if $queue->statustext;
394 my @queue_depend = $queue->queue_depend;
395 $status .= ' (waiting for '.
396 join(', ', map { $_->depend_jobnum } @queue_depend ).
399 my $changable = $dangerous
400 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
403 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
404 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
406 my $cust_svc = $queue->cust_svc;
411 <TD>$queue_hashref->{job}</TD>
417 unless ( $hashref->{svcnum} ) {
420 my $table = $cust_svc->part_svc->svcdb;
421 my $label = ( $cust_svc->label )[1];
422 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
427 $html .= "<TD>$account</TD>";
433 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
444 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
445 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
460 L<FS::Record>, schema.html from the base documentation.