4 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
7 use Storable qw( nfreeze thaw );
8 use FS::UID qw(myconnect);
10 use FS::Record qw( qsearch qsearchs dbh );
15 use FS::CGI qw(rooturl);
17 @ISA = qw(FS::Record);
18 @EXPORT_OK = qw( joblisting );
22 $FS::UID::callback{'FS::queue'} = sub {
30 FS::queue - Object methods for queue records
36 $record = new FS::queue \%hash;
37 $record = new FS::queue { 'column' => 'value' };
39 $error = $record->insert;
41 $error = $new_record->replace($old_record);
43 $error = $record->delete;
45 $error = $record->check;
49 An FS::queue object represents an queued job. FS::queue inherits from
50 FS::Record. The following fields are currently supported:
60 Fully-qualified subroutine name
64 Job status (new, locked, or failed)
68 Freeform text status message
74 if ( defined ( $_[0] ) ) {
75 $self->SUPER::statustext(@_);
77 my $value = $self->SUPER::statustext();
78 my $rooturl = rooturl();
79 $value =~ s/%%%ROOTURL%%%/$rooturl/g;
90 Optional link to service (see L<FS::cust_svc>).
94 Optional link to customer (see L<FS::cust_main>).
98 Secure flag, 'Y' indicates that when using encryption, the job needs to be
99 run on a machine with the private key.
111 Creates a new job. To add the job to the database, see L<"insert">.
113 Note that this stores the hash reference, not a distinct copy of the hash it
114 points to. You can ask the object for a copy with the I<hash> method.
118 # the new method can be inherited from FS::Record, if a table method is defined
120 sub table { 'queue'; }
122 =item insert [ ARGUMENT, ARGUMENT... ]
124 Adds this record to the database. If there is an error, returns the error,
125 otherwise returns false.
127 If any arguments are supplied, a queue_arg record for each argument is also
128 created (see L<FS::queue_arg>).
132 #false laziness w/part_export.pm
134 my( $self, @args ) = @_;
136 local $SIG{HUP} = 'IGNORE';
137 local $SIG{INT} = 'IGNORE';
138 local $SIG{QUIT} = 'IGNORE';
139 local $SIG{TERM} = 'IGNORE';
140 local $SIG{TSTP} = 'IGNORE';
141 local $SIG{PIPE} = 'IGNORE';
143 my $oldAutoCommit = $FS::UID::AutoCommit;
144 local $FS::UID::AutoCommit = 0;
153 $self->custnum( $args{'custnum'} ) if $args{'custnum'};
155 my $error = $self->SUPER::insert;
157 $dbh->rollback if $oldAutoCommit;
161 foreach my $arg ( @args ) {
162 my $freeze = ref($arg) ? 'Y' : '';
163 my $queue_arg = new FS::queue_arg ( {
164 'jobnum' => $self->jobnum,
166 'arg' => $freeze ? encode_base64(nfreeze($arg)) : $arg,# always freeze?
168 $error = $queue_arg->insert;
170 $dbh->rollback if $oldAutoCommit;
176 warn "jobnums global is active: $jobnums\n" if $DEBUG;
177 push @$jobnums, $self->jobnum;
180 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
188 Delete this record from the database. Any corresponding queue_arg records are
197 if ( $self->status =~/^done/ ) {
198 my $dropstring = rooturl(). '/misc/queued_report\?report=';
199 if ($self->statustext =~ /.*$dropstring([.\w]+)\>/) {
200 $reportname = "$FS::UID::cache_dir/cache.$FS::UID::datasrc/report.$1";
204 my $error = $self->SUPER::delete;
205 return $error if $error;
207 unlink $reportname if $reportname;
213 =item replace OLD_RECORD
215 Replaces the OLD_RECORD with this one in the database. If there is an error,
216 returns the error, otherwise returns false.
220 # the replace method can be inherited from FS::Record
224 Checks all fields to make sure this is a valid job. If there is
225 an error, returns the error, otherwise returns false. Called by the insert
233 $self->ut_numbern('jobnum')
234 || $self->ut_anything('job')
235 || $self->ut_numbern('_date')
236 || $self->ut_enum('status',['', qw( new locked failed done )])
237 || $self->ut_anything('statustext')
238 || $self->ut_numbern('svcnum')
240 return $error if $error;
242 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
243 $self->svcnum('') if $error;
245 $self->status('new') unless $self->status;
246 $self->_date(time) unless $self->_date;
253 Returns a list of the arguments associated with this job.
259 map { $_->frozen ? thaw(decode_base64($_->arg)) : $_->arg }
260 qsearch( 'queue_arg',
261 { 'jobnum' => $self->jobnum },
269 Returns the FS::cust_svc object associated with this job, if any.
275 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
280 Returns the FS::queue_depend objects associated with this job, if any.
281 (Dependancies that must complete before this job can be run).
287 qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
290 =item depend_insert OTHER_JOBNUM
292 Inserts a dependancy for this job - it will not be run until the other job
293 specified completes. If there is an error, returns the error, otherwise
296 When using job dependancies, you should wrap the insertion of all relevant jobs
297 in a database transaction.
302 my($self, $other_jobnum) = @_;
303 my $queue_depend = new FS::queue_depend ( {
304 'jobnum' => $self->jobnum,
305 'depend_jobnum' => $other_jobnum,
307 $queue_depend->insert;
312 Returns the FS::queue_depend objects that associate other jobs with this job,
313 if any. (The jobs that are waiting for this job to complete before they can
320 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
323 =item depended_delete
325 Deletes the other queued jobs (FS::queue objects) that are waiting for this
326 job, if any. If there is an error, returns the error, otherwise returns false.
330 sub depended_delete {
334 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
336 $error = $job->depended_delete;
337 return $error if $error;
338 $error = $job->delete;
339 return $error if $error
343 =item update_statustext VALUE
345 Updates the statustext value of this job to supplied value, in the database.
346 If there is an error, returns the error, otherwise returns false.
350 use vars qw($_update_statustext_dbh);
351 sub update_statustext {
352 my( $self, $statustext ) = @_;
353 return '' if $statustext eq $self->get('statustext'); #avoid rooturl expansion
354 warn "updating statustext for $self to $statustext" if $DEBUG;
356 $_update_statustext_dbh ||= myconnect;
358 my $sth = $_update_statustext_dbh->prepare(
359 'UPDATE queue set statustext = ? WHERE jobnum = ?'
360 ) or return $_update_statustext_dbh->errstr;
362 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
363 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
364 $self->set('statustext', $statustext); #avoid rooturl expansion
367 #my $new = new FS::queue { $self->hash };
368 #$new->statustext($statustext);
369 #my $error = $new->replace($self);
370 #return $error if $error;
371 #$self->statustext($statustext);
381 =item joblisting HASHREF NOACTIONS
386 my($hashref, $noactions) = @_;
392 my @queue = qsearch( 'queue', $hashref );
393 return '' unless scalar(@queue);
395 my $p = FS::CGI::popurl(2);
397 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
398 FS::CGI::table(). <<END;
400 <TH COLSPAN=2>Job</TH>
405 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
408 my $dangerous = $conf->exists('queue_dangerous_controls');
412 foreach my $queue ( sort {
413 $a->getfield('jobnum') <=> $b->getfield('jobnum')
415 my $queue_hashref = $queue->hashref;
416 my $jobnum = $queue->jobnum;
419 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
420 $args = encode_entities( join(' ', $queue->args) );
425 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
426 my $status = $queue->status;
427 $status .= ': '. $queue->statustext if $queue->statustext;
428 my @queue_depend = $queue->queue_depend;
429 $status .= ' (waiting for '.
430 join(', ', map { $_->depend_jobnum } @queue_depend ).
433 my $changable = $dangerous
434 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
437 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
438 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
440 my $cust_svc = $queue->cust_svc;
445 <TD>$queue_hashref->{job}</TD>
451 unless ( $hashref->{svcnum} ) {
454 my $table = $cust_svc->part_svc->svcdb;
455 my $label = ( $cust_svc->label )[1];
456 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
461 $html .= "<TD>$account</TD>";
467 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
478 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
479 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
494 L<FS::Record>, schema.html from the base documentation.