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.
102 For access_user that created the job
114 Creates a new job. To add the job to the database, see L<"insert">.
116 Note that this stores the hash reference, not a distinct copy of the hash it
117 points to. You can ask the object for a copy with the I<hash> method.
121 # the new method can be inherited from FS::Record, if a table method is defined
123 sub table { 'queue'; }
125 =item insert [ ARGUMENT, ARGUMENT... ]
127 Adds this record to the database. If there is an error, returns the error,
128 otherwise returns false.
130 If any arguments are supplied, a queue_arg record for each argument is also
131 created (see L<FS::queue_arg>).
135 #false laziness w/part_export.pm
137 my( $self, @args ) = @_;
139 local $SIG{HUP} = 'IGNORE';
140 local $SIG{INT} = 'IGNORE';
141 local $SIG{QUIT} = 'IGNORE';
142 local $SIG{TERM} = 'IGNORE';
143 local $SIG{TSTP} = 'IGNORE';
144 local $SIG{PIPE} = 'IGNORE';
146 my $oldAutoCommit = $FS::UID::AutoCommit;
147 local $FS::UID::AutoCommit = 0;
156 $self->custnum( $args{'custnum'} ) if $args{'custnum'};
158 $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
160 my $error = $self->SUPER::insert;
162 $dbh->rollback if $oldAutoCommit;
166 foreach my $arg ( @args ) {
167 my $freeze = ref($arg) ? 'Y' : '';
168 my $queue_arg = new FS::queue_arg ( {
169 'jobnum' => $self->jobnum,
171 'arg' => $freeze ? encode_base64(nfreeze($arg)) : $arg,# always freeze?
173 $error = $queue_arg->insert;
175 $dbh->rollback if $oldAutoCommit;
181 warn "jobnums global is active: $jobnums\n" if $DEBUG;
182 push @$jobnums, $self->jobnum;
185 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
193 Delete this record from the database. Any corresponding queue_arg records are
202 if ( $self->status =~/^done/ ) {
203 my $dropstring = rooturl(). '/misc/queued_report\?report=';
204 if ($self->statustext =~ /.*$dropstring([.\w]+)\>/) {
205 $reportname = "$FS::UID::cache_dir/cache.$FS::UID::datasrc/report.$1";
209 my $error = $self->SUPER::delete;
210 return $error if $error;
212 unlink $reportname if $reportname;
218 =item replace OLD_RECORD
220 Replaces the OLD_RECORD with this one in the database. If there is an error,
221 returns the error, otherwise returns false.
225 # the replace method can be inherited from FS::Record
229 Checks all fields to make sure this is a valid job. If there is
230 an error, returns the error, otherwise returns false. Called by the insert
238 $self->ut_numbern('jobnum')
239 || $self->ut_anything('job')
240 || $self->ut_numbern('_date')
241 || $self->ut_enum('status',['', qw( new locked failed done )])
242 || $self->ut_anything('statustext')
243 || $self->ut_numbern('svcnum')
244 || $self->ut_foreign_keyn('usernum', 'access_user', 'usernum')
246 return $error if $error;
248 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
249 $self->svcnum('') if $error;
251 $self->status('new') unless $self->status;
252 $self->_date(time) unless $self->_date;
259 Returns a list of the arguments associated with this job.
265 map { $_->frozen ? thaw(decode_base64($_->arg)) : $_->arg }
266 qsearch( 'queue_arg',
267 { 'jobnum' => $self->jobnum },
275 Returns the FS::cust_svc object associated with this job, if any.
279 Returns the FS::queue_depend objects associated with this job, if any.
280 (Dependancies that must complete before this job can be run).
282 =item depend_insert OTHER_JOBNUM
284 Inserts a dependancy for this job - it will not be run until the other job
285 specified completes. If there is an error, returns the error, otherwise
288 When using job dependancies, you should wrap the insertion of all relevant jobs
289 in a database transaction.
294 my($self, $other_jobnum) = @_;
295 my $queue_depend = new FS::queue_depend ( {
296 'jobnum' => $self->jobnum,
297 'depend_jobnum' => $other_jobnum,
299 $queue_depend->insert;
304 Returns the FS::queue_depend objects that associate other jobs with this job,
305 if any. (The jobs that are waiting for this job to complete before they can
312 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
315 =item depended_delete
317 Deletes the other queued jobs (FS::queue objects) that are waiting for this
318 job, if any. If there is an error, returns the error, otherwise returns false.
322 sub depended_delete {
326 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
328 $error = $job->depended_delete;
329 return $error if $error;
330 $error = $job->delete;
331 return $error if $error
335 =item update_statustext VALUE
337 Updates the statustext value of this job to supplied value, in the database.
338 If there is an error, returns the error, otherwise returns false.
342 use vars qw($_update_statustext_dbh);
343 sub update_statustext {
344 my( $self, $statustext ) = @_;
345 return '' if $statustext eq $self->get('statustext'); #avoid rooturl expansion
346 warn "updating statustext for $self to $statustext" if $DEBUG;
348 $_update_statustext_dbh ||= myconnect;
350 my $sth = $_update_statustext_dbh->prepare(
351 'UPDATE queue set statustext = ? WHERE jobnum = ?'
352 ) or return $_update_statustext_dbh->errstr;
354 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
355 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
356 $self->set('statustext', $statustext); #avoid rooturl expansion
359 #my $new = new FS::queue { $self->hash };
360 #$new->statustext($statustext);
361 #my $error = $new->replace($self);
362 #return $error if $error;
363 #$self->statustext($statustext);
370 #Returns FS::access_user object (if any) associated with this user.
372 #Returns nothing if not found.
378 # my $usernum = $self->usernum || return ();
379 # return qsearchs('access_user',{ 'usernum' => $usernum }) || ();
388 =item joblisting HASHREF NOACTIONS
393 my($hashref, $noactions) = @_;
399 my @queue = qsearch( 'queue', $hashref );
400 return '' unless scalar(@queue);
402 my $p = FS::CGI::popurl(2);
404 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
405 FS::CGI::table(). <<END;
407 <TH COLSPAN=2>Job</TH>
412 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
415 my $dangerous = $conf->exists('queue_dangerous_controls');
419 foreach my $queue ( sort {
420 $a->getfield('jobnum') <=> $b->getfield('jobnum')
422 my $queue_hashref = $queue->hashref;
423 my $jobnum = $queue->jobnum;
426 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
427 $args = encode_entities( join(' ', $queue->args) );
432 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
433 my $status = $queue->status;
434 $status .= ': '. $queue->statustext if $queue->statustext;
435 my @queue_depend = $queue->queue_depend;
436 $status .= ' (waiting for '.
437 join(', ', map { $_->depend_jobnum } @queue_depend ).
440 my $changable = $dangerous
441 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
444 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
445 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
447 my $cust_svc = $queue->cust_svc;
452 <TD>$queue_hashref->{job}</TD>
458 unless ( $hashref->{svcnum} ) {
461 my $table = $cust_svc->part_svc->svcdb;
462 my $label = ( $cust_svc->label )[1];
463 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
468 $html .= "<TD>$account</TD>";
474 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
485 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
486 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
501 L<FS::Record>, schema.html from the base documentation.