2 use base qw(FS::Record);
5 use vars qw( @EXPORT_OK $DEBUG $conf $jobnums);
8 use Storable qw( nfreeze thaw );
9 use FS::UID qw(myconnect);
11 use FS::Record qw( qsearch qsearchs dbh );
15 use FS::CGI qw(rooturl);
17 @EXPORT_OK = qw( joblisting );
21 $FS::UID::callback{'FS::queue'} = sub {
29 FS::queue - Object methods for queue records
35 $record = new FS::queue \%hash;
36 $record = new FS::queue { 'column' => 'value' };
38 $error = $record->insert;
40 $error = $new_record->replace($old_record);
42 $error = $record->delete;
44 $error = $record->check;
48 An FS::queue object represents an queued job. FS::queue inherits from
49 FS::Record. The following fields are currently supported:
59 Fully-qualified subroutine name
63 Job status (new, locked, or failed)
67 Freeform text status message
73 if ( defined ( $_[0] ) ) {
74 $self->SUPER::statustext(@_);
76 my $value = $self->SUPER::statustext();
77 my $rooturl = rooturl();
78 $value =~ s/%%%ROOTURL%%%/$rooturl/g;
89 Optional link to service (see L<FS::cust_svc>).
93 Optional link to customer (see L<FS::cust_main>).
97 Secure flag, 'Y' indicates that when using encryption, the job needs to be
98 run on a machine with the private key.
110 Creates a new job. To add the job to the database, see L<"insert">.
112 Note that this stores the hash reference, not a distinct copy of the hash it
113 points to. You can ask the object for a copy with the I<hash> method.
117 # the new method can be inherited from FS::Record, if a table method is defined
119 sub table { 'queue'; }
121 =item insert [ ARGUMENT, ARGUMENT... ]
123 Adds this record to the database. If there is an error, returns the error,
124 otherwise returns false.
126 If any arguments are supplied, a queue_arg record for each argument is also
127 created (see L<FS::queue_arg>).
131 #false laziness w/part_export.pm
133 my( $self, @args ) = @_;
135 local $SIG{HUP} = 'IGNORE';
136 local $SIG{INT} = 'IGNORE';
137 local $SIG{QUIT} = 'IGNORE';
138 local $SIG{TERM} = 'IGNORE';
139 local $SIG{TSTP} = 'IGNORE';
140 local $SIG{PIPE} = 'IGNORE';
142 my $oldAutoCommit = $FS::UID::AutoCommit;
143 local $FS::UID::AutoCommit = 0;
152 $self->custnum( $args{'custnum'} ) if $args{'custnum'};
154 my $error = $self->SUPER::insert;
156 $dbh->rollback if $oldAutoCommit;
160 foreach my $arg ( @args ) {
161 my $freeze = ref($arg) ? 'Y' : '';
162 my $queue_arg = new FS::queue_arg ( {
163 'jobnum' => $self->jobnum,
165 'arg' => $freeze ? encode_base64(nfreeze($arg)) : $arg,# always freeze?
167 $error = $queue_arg->insert;
169 $dbh->rollback if $oldAutoCommit;
175 warn "jobnums global is active: $jobnums\n" if $DEBUG;
176 push @$jobnums, $self->jobnum;
179 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
187 Delete this record from the database. Any corresponding queue_arg records are
196 if ( $self->status =~/^done/ ) {
197 my $dropstring = rooturl(). '/misc/queued_report\?report=';
198 if ($self->statustext =~ /.*$dropstring([.\w]+)\>/) {
199 $reportname = "$FS::UID::cache_dir/cache.$FS::UID::datasrc/report.$1";
203 my $error = $self->SUPER::delete;
204 return $error if $error;
206 unlink $reportname if $reportname;
212 =item replace OLD_RECORD
214 Replaces the OLD_RECORD with this one in the database. If there is an error,
215 returns the error, otherwise returns false.
219 # the replace method can be inherited from FS::Record
223 Checks all fields to make sure this is a valid job. If there is
224 an error, returns the error, otherwise returns false. Called by the insert
232 $self->ut_numbern('jobnum')
233 || $self->ut_anything('job')
234 || $self->ut_numbern('_date')
235 || $self->ut_enum('status',['', qw( new locked failed done )])
236 || $self->ut_anything('statustext')
237 || $self->ut_numbern('svcnum')
239 return $error if $error;
241 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
242 $self->svcnum('') if $error;
244 $self->status('new') unless $self->status;
245 $self->_date(time) unless $self->_date;
252 Returns a list of the arguments associated with this job.
258 map { $_->frozen ? thaw(decode_base64($_->arg)) : $_->arg }
259 qsearch( 'queue_arg',
260 { 'jobnum' => $self->jobnum },
268 Returns the FS::cust_svc object associated with this job, if any.
272 Returns the FS::queue_depend objects associated with this job, if any.
273 (Dependancies that must complete before this job can be run).
275 =item depend_insert OTHER_JOBNUM
277 Inserts a dependancy for this job - it will not be run until the other job
278 specified completes. If there is an error, returns the error, otherwise
281 When using job dependancies, you should wrap the insertion of all relevant jobs
282 in a database transaction.
287 my($self, $other_jobnum) = @_;
288 my $queue_depend = new FS::queue_depend ( {
289 'jobnum' => $self->jobnum,
290 'depend_jobnum' => $other_jobnum,
292 $queue_depend->insert;
297 Returns the FS::queue_depend objects that associate other jobs with this job,
298 if any. (The jobs that are waiting for this job to complete before they can
305 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
308 =item depended_delete
310 Deletes the other queued jobs (FS::queue objects) that are waiting for this
311 job, if any. If there is an error, returns the error, otherwise returns false.
315 sub depended_delete {
319 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
321 $error = $job->depended_delete;
322 return $error if $error;
323 $error = $job->delete;
324 return $error if $error
328 =item update_statustext VALUE
330 Updates the statustext value of this job to supplied value, in the database.
331 If there is an error, returns the error, otherwise returns false.
335 use vars qw($_update_statustext_dbh);
336 sub update_statustext {
337 my( $self, $statustext ) = @_;
338 return '' if $statustext eq $self->get('statustext'); #avoid rooturl expansion
339 warn "updating statustext for $self to $statustext" if $DEBUG;
341 $_update_statustext_dbh ||= myconnect;
343 my $sth = $_update_statustext_dbh->prepare(
344 'UPDATE queue set statustext = ? WHERE jobnum = ?'
345 ) or return $_update_statustext_dbh->errstr;
347 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
348 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
349 $self->set('statustext', $statustext); #avoid rooturl expansion
352 #my $new = new FS::queue { $self->hash };
353 #$new->statustext($statustext);
354 #my $error = $new->replace($self);
355 #return $error if $error;
356 #$self->statustext($statustext);
366 =item joblisting HASHREF NOACTIONS
371 my($hashref, $noactions) = @_;
377 my @queue = qsearch( 'queue', $hashref );
378 return '' unless scalar(@queue);
380 my $p = FS::CGI::popurl(2);
382 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
383 FS::CGI::table(). <<END;
385 <TH COLSPAN=2>Job</TH>
390 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
393 my $dangerous = $conf->exists('queue_dangerous_controls');
397 foreach my $queue ( sort {
398 $a->getfield('jobnum') <=> $b->getfield('jobnum')
400 my $queue_hashref = $queue->hashref;
401 my $jobnum = $queue->jobnum;
404 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
405 $args = encode_entities( join(' ', $queue->args) );
410 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
411 my $status = $queue->status;
412 $status .= ': '. $queue->statustext if $queue->statustext;
413 my @queue_depend = $queue->queue_depend;
414 $status .= ' (waiting for '.
415 join(', ', map { $_->depend_jobnum } @queue_depend ).
418 my $changable = $dangerous
419 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
422 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
423 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
425 my $cust_svc = $queue->cust_svc;
430 <TD>$queue_hashref->{job}</TD>
436 unless ( $hashref->{svcnum} ) {
439 my $table = $cust_svc->part_svc->svcdb;
440 my $label = ( $cust_svc->label )[1];
441 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
446 $html .= "<TD>$account</TD>";
452 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
463 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
464 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
479 L<FS::Record>, schema.html from the base documentation.