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 $oldAutoCommit = $FS::UID::AutoCommit;
210 local $FS::UID::AutoCommit = 0;
213 foreach my $cust_pay_pending (qsearch('cust_pay_pending',{ jobnum => $self->jobnum })) {
214 $cust_pay_pending->set('jobnum','');
215 my $error = $cust_pay_pending->replace();
217 $dbh->rollback if $oldAutoCommit;
222 my $error = $self->SUPER::delete;
224 $dbh->rollback if $oldAutoCommit;
228 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
230 unlink $reportname if $reportname;
236 =item replace OLD_RECORD
238 Replaces the OLD_RECORD with this one in the database. If there is an error,
239 returns the error, otherwise returns false.
243 # the replace method can be inherited from FS::Record
247 Checks all fields to make sure this is a valid job. If there is
248 an error, returns the error, otherwise returns false. Called by the insert
256 $self->ut_numbern('jobnum')
257 || $self->ut_anything('job')
258 || $self->ut_numbern('_date')
259 || $self->ut_enum('status',['', qw( new locked failed done )])
260 || $self->ut_anything('statustext')
261 || $self->ut_numbern('svcnum')
262 || $self->ut_foreign_keyn('usernum', 'access_user', 'usernum')
264 return $error if $error;
266 $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
267 $self->svcnum('') if $error;
269 $self->status('new') unless $self->status;
270 $self->_date(time) unless $self->_date;
277 Returns a list of the arguments associated with this job.
283 map { $_->frozen ? thaw(decode_base64($_->arg)) : $_->arg }
284 qsearch( 'queue_arg',
285 { 'jobnum' => $self->jobnum },
293 Returns the FS::cust_svc object associated with this job, if any.
297 Returns the FS::queue_depend objects associated with this job, if any.
298 (Dependancies that must complete before this job can be run).
300 =item depend_insert OTHER_JOBNUM
302 Inserts a dependancy for this job - it will not be run until the other job
303 specified completes. If there is an error, returns the error, otherwise
306 When using job dependancies, you should wrap the insertion of all relevant jobs
307 in a database transaction.
312 my($self, $other_jobnum) = @_;
313 my $queue_depend = new FS::queue_depend ( {
314 'jobnum' => $self->jobnum,
315 'depend_jobnum' => $other_jobnum,
317 $queue_depend->insert;
322 Returns the FS::queue_depend objects that associate other jobs with this job,
323 if any. (The jobs that are waiting for this job to complete before they can
330 qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
333 =item depended_delete
335 Deletes the other queued jobs (FS::queue objects) that are waiting for this
336 job, if any. If there is an error, returns the error, otherwise returns false.
340 sub depended_delete {
344 map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
346 $error = $job->depended_delete;
347 return $error if $error;
348 $error = $job->delete;
349 return $error if $error
353 =item update_statustext VALUE
355 Updates the statustext value of this job to supplied value, in the database.
356 If there is an error, returns the error, otherwise returns false.
360 use vars qw($_update_statustext_dbh);
361 sub update_statustext {
362 my( $self, $statustext ) = @_;
363 return '' if $statustext eq $self->get('statustext'); #avoid rooturl expansion
364 warn "updating statustext for $self to $statustext" if $DEBUG;
366 $_update_statustext_dbh ||= myconnect;
368 my $sth = $_update_statustext_dbh->prepare(
369 'UPDATE queue set statustext = ? WHERE jobnum = ?'
370 ) or return $_update_statustext_dbh->errstr;
372 $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
373 $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
374 $self->set('statustext', $statustext); #avoid rooturl expansion
377 #my $new = new FS::queue { $self->hash };
378 #$new->statustext($statustext);
379 #my $error = $new->replace($self);
380 #return $error if $error;
381 #$self->statustext($statustext);
388 #Returns FS::access_user object (if any) associated with this user.
390 #Returns nothing if not found.
396 # my $usernum = $self->usernum || return ();
397 # return qsearchs('access_user',{ 'usernum' => $usernum }) || ();
406 =item joblisting HASHREF NOACTIONS
411 my($hashref, $noactions) = @_;
417 my @queue = qsearch( 'queue', $hashref );
418 return '' unless scalar(@queue);
420 my $p = FS::CGI::popurl(2);
422 my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
423 FS::CGI::table(). <<END;
425 <TH COLSPAN=2>Job</TH>
430 $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
433 my $dangerous = $conf->exists('queue_dangerous_controls');
437 foreach my $queue ( sort {
438 $a->getfield('jobnum') <=> $b->getfield('jobnum')
440 my $queue_hashref = $queue->hashref;
441 my $jobnum = $queue->jobnum;
444 if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
445 $args = encode_entities( join(' ', $queue->args) );
450 my $date = time2str( "%a %b %e %T %Y", $queue->_date );
451 my $status = $queue->status;
452 $status .= ': '. $queue->statustext if $queue->statustext;
453 my @queue_depend = $queue->queue_depend;
454 $status .= ' (waiting for '.
455 join(', ', map { $_->depend_jobnum } @queue_depend ).
458 my $changable = $dangerous
459 || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
462 qq! ( <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A> |!.
463 qq! <A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A> )!;
465 my $cust_svc = $queue->cust_svc;
470 <TD>$queue_hashref->{job}</TD>
476 unless ( $hashref->{svcnum} ) {
479 my $table = $cust_svc->part_svc->svcdb;
480 my $label = ( $cust_svc->label )[1];
481 $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
486 $html .= "<TD>$account</TD>";
492 qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
503 $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
504 '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
519 L<FS::Record>, schema.html from the base documentation.