4 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
8 use FS::Record qw( qsearch qsearchs dbh );
14 @ISA = qw(FS::Record);
15 @EXPORT_OK = qw( joblisting );
20 $FS::UID::callback{'FS::queue'} = sub {
28 FS::queue - Object methods for queue records
34 $record = new FS::queue \%hash;
35 $record = new FS::queue { 'column' => 'value' };
37 $error = $record->insert;
39 $error = $new_record->replace($old_record);
41 $error = $record->delete;
43 $error = $record->check;
47 An FS::queue object represents an queued job. FS::queue inherits from
48 FS::Record. The following fields are currently supported:
52 =item jobnum - primary key
54 =item job - fully-qualified subroutine name
56 =item status - job status
58 =item statustext - freeform text status message
60 =item _date - UNIX timestamp
62 =item svcnum - optional link to service (see L<FS::cust_svc>)
72 Creates a new job. To add the example to the database, see L<"insert">.
74 Note that this stores the hash reference, not a distinct copy of the hash it
75 points to. You can ask the object for a copy with the I<hash> method.
79 # the new method can be inherited from FS::Record, if a table method is defined
81 sub table { 'queue'; }
83 =item insert [ ARGUMENT, ARGUMENT... ]
85 Adds this record to the database. If there is an error, returns the error,
86 otherwise returns false.
88 If any arguments are supplied, a queue_arg record for each argument is also
89 created (see L<FS::queue_arg>).
93 #false laziness w/part_export.pm
97 local $SIG{HUP} = 'IGNORE';
98 local $SIG{INT} = 'IGNORE';
99 local $SIG{QUIT} = 'IGNORE';
100 local $SIG{TERM} = 'IGNORE';
101 local $SIG{TSTP} = 'IGNORE';
102 local $SIG{PIPE} = 'IGNORE';
104 my $oldAutoCommit = $FS::UID::AutoCommit;
105 local $FS::UID::AutoCommit = 0;
108 my $error = $self->SUPER::insert;
110 $dbh->rollback if $oldAutoCommit;
114 foreach my $arg ( @_ ) {
115 my $queue_arg = new FS::queue_arg ( {
116 'jobnum' => $self->jobnum,
119 $error = $queue_arg->insert;
121 $dbh->rollback if $oldAutoCommit;
127 warn "jobnums global is active: $jobnums\n" if $DEBUG;
128 push @$jobnums, $self->jobnum;
131 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
139 Delete this record from the database. Any corresponding queue_arg records are
147 local $SIG{HUP} = 'IGNORE';
148 local $SIG{INT} = 'IGNORE';
149 local $SIG{QUIT} = 'IGNORE';
150 local $SIG{TERM} = 'IGNORE';
151 local $SIG{TSTP} = 'IGNORE';
152 local $SIG{PIPE} = 'IGNORE';
154 my $oldAutoCommit = $FS::UID::AutoCommit;
155 local $FS::UID::AutoCommit = 0;
158 my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
159 push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
161 my $error = $self->SUPER::delete;
163 $dbh->rollback if $oldAutoCommit;
167 foreach my $del ( @del ) {
168 $error = $del->delete;
170 $dbh->rollback if $oldAutoCommit;
175 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
181 =item replace OLD_RECORD
183 Replaces the OLD_RECORD with this one in the database. If there is an error,
184 returns the error, otherwise returns false.
188 # the replace method can be inherited from FS::Record
192 Checks all fields to make sure this is a valid job. If there is
193 an error, returns the error, otherwise returns false. Called by the insert
201 $self->ut_numbern('jobnum')
202 || $self->ut_anything('job')
203 || $self->ut_numbern('_date')
204 || $self->ut_enum('status',['', qw( new locked failed )])
205 || $self->ut_anything('statustext')
206 || $self->ut_numbern('svcnum')
208 return $error if $error;
210 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
211 $self->svcnum('') if $error;
213 $self->status('new') unless $self->status;
214 $self->_date(time) unless $self->_date;
221 Returns a list of the arguments associated with this job.
227 map $_->arg, qsearch( 'queue_arg',
228 { 'jobnum' => $self->jobnum },
236 Returns the FS::cust_svc object associated with this job, if any.
242 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
247 Returns the FS::queue_depend objects associated with this job, if any.
248 (Dependancies that must complete before this job can be run).
254 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
257 =item depend_insert OTHER_JOBNUM
259 Inserts a dependancy for this job - it will not be run until the other job
260 specified completes. If there is an error, returns the error, otherwise
263 When using job dependancies, you should wrap the insertion of all relevant jobs
264 in a database transaction.
269 my($self, $other_jobnum) = @_;
270 my $queue_depend = new FS::queue_depend ( {
271 'jobnum' => $self->jobnum,
272 'depend_jobnum' => $other_jobnum,
274 $queue_depend->insert;
279 Returns the FS::queue_depend objects that associate other jobs with this job,
280 if any. (The jobs that are waiting for this job to complete before they can
287 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
290 =item depended_delete
292 Deletes the other queued jobs (FS::queue objects) that are waiting for this
293 job, if any. If there is an error, returns the error, otherwise returns false.
297 sub depended_delete {
301 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
303 $error = $job->depended_delete;
304 return $error if $error;
305 $error = $job->delete;
306 return $error if $error
316 =item joblisting HASHREF NOACTIONS
321 my($hashref, $noactions) = @_;
327 my @queue = qsearch( 'queue', $hashref );
328 return '' unless scalar(@queue);
330 my $p = FS::CGI::popurl(2);
332 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
333 FS::CGI::table(). <<END;
335 <TH COLSPAN=2>Job</TH>
340 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
343 my $dangerous = $conf->exists('queue_dangerous_controls');
347 foreach my $queue ( sort {
348 $a->getfield('jobnum') <=> $b->getfield('jobnum')
350 my $queue_hashref = $queue->hashref;
351 my $jobnum = $queue->jobnum;
354 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
355 $args = encode_entities( join(' ', $queue->args) );
360 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
361 my $status = $queue->status;
362 $status .= ': '. $queue->statustext if $queue->statustext;
363 my @queue_depend = $queue->queue_depend;
364 $status .= ' (waiting for '.
365 join(', ', map { $_->depend_jobnum } @queue_depend ).
368 my $changable = $dangerous
369 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
372 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
373 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
375 my $cust_svc = $queue->cust_svc;
380 <TD>$queue_hashref->{job}</TD>
386 unless ( $hashref->{svcnum} ) {
389 my $table = $cust_svc->part_svc->svcdb;
390 my $label = ( $cust_svc->label )[1];
391 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
396 $html .= "<TD>$account</TD>";
402 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
413 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
414 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
429 L<FS::Record>, schema.html from the base documentation.