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>).
77 Optional link to customer (see L<FS::cust_main>).
81 Secure flag, 'Y' indicates that when using encryption, the job needs to be
82 run on a machine with the private key.
94 Creates a new job. To add the job to the database, see L<"insert">.
96 Note that this stores the hash reference, not a distinct copy of the hash it
97 points to. You can ask the object for a copy with the I<hash> method.
101 # the new method can be inherited from FS::Record, if a table method is defined
103 sub table { 'queue'; }
105 =item insert [ ARGUMENT, ARGUMENT... ]
107 Adds this record to the database. If there is an error, returns the error,
108 otherwise returns false.
110 If any arguments are supplied, a queue_arg record for each argument is also
111 created (see L<FS::queue_arg>).
115 #false laziness w/part_export.pm
117 my( $self, @args ) = @_;
119 local $SIG{HUP} = 'IGNORE';
120 local $SIG{INT} = 'IGNORE';
121 local $SIG{QUIT} = 'IGNORE';
122 local $SIG{TERM} = 'IGNORE';
123 local $SIG{TSTP} = 'IGNORE';
124 local $SIG{PIPE} = 'IGNORE';
126 my $oldAutoCommit = $FS::UID::AutoCommit;
127 local $FS::UID::AutoCommit = 0;
136 $self->custnum( $args{'custnum'} ) if $args{'custnum'};
138 my $error = $self->SUPER::insert;
140 $dbh->rollback if $oldAutoCommit;
144 foreach my $arg ( @args ) {
145 my $queue_arg = new FS::queue_arg ( {
146 'jobnum' => $self->jobnum,
149 $error = $queue_arg->insert;
151 $dbh->rollback if $oldAutoCommit;
157 warn "jobnums global is active: $jobnums\n" if $DEBUG;
158 push @$jobnums, $self->jobnum;
161 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
169 Delete this record from the database. Any corresponding queue_arg records are
177 local $SIG{HUP} = 'IGNORE';
178 local $SIG{INT} = 'IGNORE';
179 local $SIG{QUIT} = 'IGNORE';
180 local $SIG{TERM} = 'IGNORE';
181 local $SIG{TSTP} = 'IGNORE';
182 local $SIG{PIPE} = 'IGNORE';
184 my $oldAutoCommit = $FS::UID::AutoCommit;
185 local $FS::UID::AutoCommit = 0;
188 my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
189 push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
191 my $error = $self->SUPER::delete;
193 $dbh->rollback if $oldAutoCommit;
197 foreach my $del ( @del ) {
198 $error = $del->delete;
200 $dbh->rollback if $oldAutoCommit;
205 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
211 =item replace OLD_RECORD
213 Replaces the OLD_RECORD with this one in the database. If there is an error,
214 returns the error, otherwise returns false.
218 # the replace method can be inherited from FS::Record
222 Checks all fields to make sure this is a valid job. If there is
223 an error, returns the error, otherwise returns false. Called by the insert
231 $self->ut_numbern('jobnum')
232 || $self->ut_anything('job')
233 || $self->ut_numbern('_date')
234 || $self->ut_enum('status',['', qw( new locked failed )])
235 || $self->ut_anything('statustext')
236 || $self->ut_numbern('svcnum')
238 return $error if $error;
240 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
241 $self->svcnum('') if $error;
243 $self->status('new') unless $self->status;
244 $self->_date(time) unless $self->_date;
251 Returns a list of the arguments associated with this job.
257 map $_->arg, qsearch( 'queue_arg',
258 { 'jobnum' => $self->jobnum },
266 Returns the FS::cust_svc object associated with this job, if any.
272 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
277 Returns the FS::queue_depend objects associated with this job, if any.
278 (Dependancies that must complete before this job can be run).
284 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
287 =item depend_insert OTHER_JOBNUM
289 Inserts a dependancy for this job - it will not be run until the other job
290 specified completes. If there is an error, returns the error, otherwise
293 When using job dependancies, you should wrap the insertion of all relevant jobs
294 in a database transaction.
299 my($self, $other_jobnum) = @_;
300 my $queue_depend = new FS::queue_depend ( {
301 'jobnum' => $self->jobnum,
302 'depend_jobnum' => $other_jobnum,
304 $queue_depend->insert;
309 Returns the FS::queue_depend objects that associate other jobs with this job,
310 if any. (The jobs that are waiting for this job to complete before they can
317 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
320 =item depended_delete
322 Deletes the other queued jobs (FS::queue objects) that are waiting for this
323 job, if any. If there is an error, returns the error, otherwise returns false.
327 sub depended_delete {
331 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
333 $error = $job->depended_delete;
334 return $error if $error;
335 $error = $job->delete;
336 return $error if $error
340 =item update_statustext VALUE
342 Updates the statustext value of this job to supplied value, in the database.
343 If there is an error, returns the error, otherwise returns false.
347 use vars qw($_update_statustext_dbh);
348 sub update_statustext {
349 my( $self, $statustext ) = @_;
350 return '' if $statustext eq $self->statustext;
351 warn "updating statustext for $self to $statustext" if $DEBUG;
353 $_update_statustext_dbh ||= myconnect;
355 my $sth = $_update_statustext_dbh->prepare(
356 'UPDATE queue set statustext = ? WHERE jobnum = ?'
357 ) or return $_update_statustext_dbh->errstr;
359 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
360 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
361 $self->statustext($statustext);
364 #my $new = new FS::queue { $self->hash };
365 #$new->statustext($statustext);
366 #my $error = $new->replace($self);
367 #return $error if $error;
368 #$self->statustext($statustext);
378 =item joblisting HASHREF NOACTIONS
383 my($hashref, $noactions) = @_;
389 my @queue = qsearch( 'queue', $hashref );
390 return '' unless scalar(@queue);
392 my $p = FS::CGI::popurl(2);
394 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
395 FS::CGI::table(). <<END;
397 <TH COLSPAN=2>Job</TH>
402 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
405 my $dangerous = $conf->exists('queue_dangerous_controls');
409 foreach my $queue ( sort {
410 $a->getfield('jobnum') <=> $b->getfield('jobnum')
412 my $queue_hashref = $queue->hashref;
413 my $jobnum = $queue->jobnum;
416 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
417 $args = encode_entities( join(' ', $queue->args) );
422 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
423 my $status = $queue->status;
424 $status .= ': '. $queue->statustext if $queue->statustext;
425 my @queue_depend = $queue->queue_depend;
426 $status .= ' (waiting for '.
427 join(', ', map { $_->depend_jobnum } @queue_depend ).
430 my $changable = $dangerous
431 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
434 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
435 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
437 my $cust_svc = $queue->cust_svc;
442 <TD>$queue_hashref->{job}</TD>
448 unless ( $hashref->{svcnum} ) {
451 my $table = $cust_svc->part_svc->svcdb;
452 my $label = ( $cust_svc->label )[1];
453 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
458 $html .= "<TD>$account</TD>";
464 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
475 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
476 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
491 L<FS::Record>, schema.html from the base documentation.