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:
57 Fully-qualified subroutine name
61 Job status (new, locked, or failed)
65 Freeform text status message
73 Optional link to service (see L<FS::cust_svc>)
83 Creates a new job. To add the job to the database, see L<"insert">.
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.
90 # the new method can be inherited from FS::Record, if a table method is defined
92 sub table { 'queue'; }
94 =item insert [ ARGUMENT, ARGUMENT... ]
96 Adds this record to the database. If there is an error, returns the error,
97 otherwise returns false.
99 If any arguments are supplied, a queue_arg record for each argument is also
100 created (see L<FS::queue_arg>).
104 #false laziness w/part_export.pm
108 local $SIG{HUP} = 'IGNORE';
109 local $SIG{INT} = 'IGNORE';
110 local $SIG{QUIT} = 'IGNORE';
111 local $SIG{TERM} = 'IGNORE';
112 local $SIG{TSTP} = 'IGNORE';
113 local $SIG{PIPE} = 'IGNORE';
115 my $oldAutoCommit = $FS::UID::AutoCommit;
116 local $FS::UID::AutoCommit = 0;
119 my $error = $self->SUPER::insert;
121 $dbh->rollback if $oldAutoCommit;
125 foreach my $arg ( @_ ) {
126 my $queue_arg = new FS::queue_arg ( {
127 'jobnum' => $self->jobnum,
130 $error = $queue_arg->insert;
132 $dbh->rollback if $oldAutoCommit;
138 warn "jobnums global is active: $jobnums\n" if $DEBUG;
139 push @$jobnums, $self->jobnum;
142 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
150 Delete this record from the database. Any corresponding queue_arg records are
158 local $SIG{HUP} = 'IGNORE';
159 local $SIG{INT} = 'IGNORE';
160 local $SIG{QUIT} = 'IGNORE';
161 local $SIG{TERM} = 'IGNORE';
162 local $SIG{TSTP} = 'IGNORE';
163 local $SIG{PIPE} = 'IGNORE';
165 my $oldAutoCommit = $FS::UID::AutoCommit;
166 local $FS::UID::AutoCommit = 0;
169 my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
170 push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
172 my $error = $self->SUPER::delete;
174 $dbh->rollback if $oldAutoCommit;
178 foreach my $del ( @del ) {
179 $error = $del->delete;
181 $dbh->rollback if $oldAutoCommit;
186 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
192 =item replace OLD_RECORD
194 Replaces the OLD_RECORD with this one in the database. If there is an error,
195 returns the error, otherwise returns false.
199 # the replace method can be inherited from FS::Record
203 Checks all fields to make sure this is a valid job. If there is
204 an error, returns the error, otherwise returns false. Called by the insert
212 $self->ut_numbern('jobnum')
213 || $self->ut_anything('job')
214 || $self->ut_numbern('_date')
215 || $self->ut_enum('status',['', qw( new locked failed )])
216 || $self->ut_anything('statustext')
217 || $self->ut_numbern('svcnum')
219 return $error if $error;
221 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
222 $self->svcnum('') if $error;
224 $self->status('new') unless $self->status;
225 $self->_date(time) unless $self->_date;
232 Returns a list of the arguments associated with this job.
238 map $_->arg, qsearch( 'queue_arg',
239 { 'jobnum' => $self->jobnum },
247 Returns the FS::cust_svc object associated with this job, if any.
253 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
258 Returns the FS::queue_depend objects associated with this job, if any.
259 (Dependancies that must complete before this job can be run).
265 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
268 =item depend_insert OTHER_JOBNUM
270 Inserts a dependancy for this job - it will not be run until the other job
271 specified completes. If there is an error, returns the error, otherwise
274 When using job dependancies, you should wrap the insertion of all relevant jobs
275 in a database transaction.
280 my($self, $other_jobnum) = @_;
281 my $queue_depend = new FS::queue_depend ( {
282 'jobnum' => $self->jobnum,
283 'depend_jobnum' => $other_jobnum,
285 $queue_depend->insert;
290 Returns the FS::queue_depend objects that associate other jobs with this job,
291 if any. (The jobs that are waiting for this job to complete before they can
298 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
301 =item depended_delete
303 Deletes the other queued jobs (FS::queue objects) that are waiting for this
304 job, if any. If there is an error, returns the error, otherwise returns false.
308 sub depended_delete {
312 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
314 $error = $job->depended_delete;
315 return $error if $error;
316 $error = $job->delete;
317 return $error if $error
321 =item update_statustext VALUE
323 Updates the statustext value of this job to supplied value, in the database.
324 If there is an error, returns the error, otherwise returns false.
328 use vars qw($_update_statustext_dbh);
329 sub update_statustext {
330 my( $self, $statustext ) = @_;
331 return '' if $statustext eq $self->statustext;
332 warn "updating statustext for $self to $statustext" if $DEBUG;
334 $_update_statustext_dbh ||= myconnect;
336 my $sth = $_update_statustext_dbh->prepare(
337 'UPDATE queue set statustext = ? WHERE jobnum = ?'
338 ) or return $_update_statustext_dbh->errstr;
340 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
341 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
342 $self->statustext($statustext);
345 #my $new = new FS::queue { $self->hash };
346 #$new->statustext($statustext);
347 #my $error = $new->replace($self);
348 #return $error if $error;
349 #$self->statustext($statustext);
359 =item joblisting HASHREF NOACTIONS
364 my($hashref, $noactions) = @_;
370 my @queue = qsearch( 'queue', $hashref );
371 return '' unless scalar(@queue);
373 my $p = FS::CGI::popurl(2);
375 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
376 FS::CGI::table(). <<END;
378 <TH COLSPAN=2>Job</TH>
383 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
386 my $dangerous = $conf->exists('queue_dangerous_controls');
390 foreach my $queue ( sort {
391 $a->getfield('jobnum') <=> $b->getfield('jobnum')
393 my $queue_hashref = $queue->hashref;
394 my $jobnum = $queue->jobnum;
397 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
398 $args = encode_entities( join(' ', $queue->args) );
403 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
404 my $status = $queue->status;
405 $status .= ': '. $queue->statustext if $queue->statustext;
406 my @queue_depend = $queue->queue_depend;
407 $status .= ' (waiting for '.
408 join(', ', map { $_->depend_jobnum } @queue_depend ).
411 my $changable = $dangerous
412 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
415 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
416 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
418 my $cust_svc = $queue->cust_svc;
423 <TD>$queue_hashref->{job}</TD>
429 unless ( $hashref->{svcnum} ) {
432 my $table = $cust_svc->part_svc->svcdb;
433 my $label = ( $cust_svc->label )[1];
434 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
439 $html .= "<TD>$account</TD>";
445 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
456 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
457 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
472 L<FS::Record>, schema.html from the base documentation.