1 package FS::svc_Common;
4 use vars qw( @ISA $noexport_hack $DEBUG );
5 use FS::Record qw( qsearch qsearchs fields dbh );
6 use FS::cust_main_Mixin;
12 @ISA = qw( FS::cust_main_Mixin FS::Record );
18 FS::svc_Common - Object method for all svc_ records
24 @ISA = qw( FS::svc_Common );
28 FS::svc_Common is intended as a base class for table-specific classes to
29 inherit from, i.e. FS::svc_acct. FS::svc_Common inherits from FS::Record.
39 # This restricts the fields based on part_svc_column and the svcpart of
40 # the service. There are four possible cases:
41 # 1. svcpart passed as part of the svc_x hash.
42 # 2. svcpart fetched via cust_svc based on svcnum.
43 # 3. No svcnum or svcpart. In this case, return ALL the fields with
44 # dbtable eq $self->table.
45 # 4. Called via "fields('svc_acct')" or something similar. In this case
46 # there is no $self object.
50 my @vfields = $self->SUPER::virtual_fields;
52 return @vfields unless (ref $self); # Case 4
54 if ($self->svcpart) { # Case 1
55 $svcpart = $self->svcpart;
56 } elsif ( $self->svcnum
57 && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
59 $svcpart = $self->cust_svc->svcpart;
64 if ($svcpart) { #Cases 1 and 2
65 my %flags = map { $_->columnname, $_->columnflag } (
66 qsearch ('part_svc_column', { svcpart => $svcpart } )
68 return grep { not ($flags{$_} eq 'X') } @vfields;
77 Checks the validity of fields in this record.
79 At present, this does nothing but call FS::Record::check (which, in turn,
80 does nothing but run virtual field checks).
89 =item insert [ , OPTION => VALUE ... ]
91 Adds this record to the database. If there is an error, returns the error,
92 otherwise returns false.
94 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
95 defined. An FS::cust_svc record will be created and inserted.
97 Currently available options are: I<jobnums>, I<child_objects> and
100 If I<jobnum> is set to an array reference, the jobnums of any export jobs will
101 be added to the referenced array.
103 If I<child_objects> is set to an array reference of FS::tablename objects (for
104 example, FS::acct_snarf objects), they will have their svcnum field set and
105 will be inserted after this record, but before any exports are run. Each
106 element of the array can also optionally be a two-element array reference
107 containing the child object and the name of an alternate field to be filled in
108 with the newly-inserted svcnum, for example C<[ $svc_forward, 'srcsvc' ]>
110 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
111 jobnums), all provisioning jobs will have a dependancy on the supplied
112 jobnum(s) (they will not run until the specific job(s) complete(s)).
119 warn "FS::svc_Common::insert called with options ".
120 join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
124 local $FS::queue::jobnums = \@jobnums;
125 warn "FS::svc_Common::insert: set \$FS::queue::jobnums to $FS::queue::jobnums"
127 my $objects = $options{'child_objects'} || [];
128 my $depend_jobnums = $options{'depend_jobnum'} || [];
129 $depend_jobnums = [ $depend_jobnums ] unless ref($depend_jobnums);
132 local $SIG{HUP} = 'IGNORE';
133 local $SIG{INT} = 'IGNORE';
134 local $SIG{QUIT} = 'IGNORE';
135 local $SIG{TERM} = 'IGNORE';
136 local $SIG{TSTP} = 'IGNORE';
137 local $SIG{PIPE} = 'IGNORE';
139 my $oldAutoCommit = $FS::UID::AutoCommit;
140 local $FS::UID::AutoCommit = 0;
143 $error = $self->check;
144 return $error if $error;
146 my $svcnum = $self->svcnum;
147 my $cust_svc = $svcnum ? qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) : '';
148 #unless ( $svcnum ) {
149 if ( !$svcnum or !$cust_svc ) {
150 $cust_svc = new FS::cust_svc ( {
151 #hua?# 'svcnum' => $svcnum,
152 'svcnum' => $self->svcnum,
153 'pkgnum' => $self->pkgnum,
154 'svcpart' => $self->svcpart,
156 $error = $cust_svc->insert;
158 $dbh->rollback if $oldAutoCommit;
161 $svcnum = $self->svcnum($cust_svc->svcnum);
163 #$cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
164 unless ( $cust_svc ) {
165 $dbh->rollback if $oldAutoCommit;
166 return "no cust_svc record found for svcnum ". $self->svcnum;
168 $self->pkgnum($cust_svc->pkgnum);
169 $self->svcpart($cust_svc->svcpart);
172 $error = $self->SUPER::insert;
174 $dbh->rollback if $oldAutoCommit;
178 foreach my $object ( @$objects ) {
180 if ( ref($object) eq 'ARRAY' ) {
181 ($obj, $field) = @$object;
186 $obj->$field($self->svcnum);
187 $error = $obj->insert;
189 $dbh->rollback if $oldAutoCommit;
195 unless ( $noexport_hack ) {
197 warn "FS::svc_Common::insert: \$FS::queue::jobnums is $FS::queue::jobnums"
200 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
201 my $error = $part_export->export_insert($self);
203 $dbh->rollback if $oldAutoCommit;
204 return "exporting to ". $part_export->exporttype.
205 " (transaction rolled back): $error";
209 foreach my $depend_jobnum ( @$depend_jobnums ) {
210 warn "inserting dependancies on supplied job $depend_jobnum\n"
212 foreach my $jobnum ( @jobnums ) {
213 my $queue = qsearchs('queue', { 'jobnum' => $jobnum } );
214 warn "inserting dependancy for job $jobnum on $depend_jobnum\n"
216 my $error = $queue->depend_insert($depend_jobnum);
218 $dbh->rollback if $oldAutoCommit;
219 return "error queuing job dependancy: $error";
226 if ( exists $options{'jobnums'} ) {
227 push @{ $options{'jobnums'} }, @jobnums;
230 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
237 Deletes this account from the database. If there is an error, returns the
238 error, otherwise returns false.
240 The corresponding FS::cust_svc record will be deleted as well.
248 local $SIG{HUP} = 'IGNORE';
249 local $SIG{INT} = 'IGNORE';
250 local $SIG{QUIT} = 'IGNORE';
251 local $SIG{TERM} = 'IGNORE';
252 local $SIG{TSTP} = 'IGNORE';
253 local $SIG{PIPE} = 'IGNORE';
255 my $svcnum = $self->svcnum;
257 my $oldAutoCommit = $FS::UID::AutoCommit;
258 local $FS::UID::AutoCommit = 0;
261 $error = $self->SUPER::delete;
262 return $error if $error;
265 unless ( $noexport_hack ) {
266 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
267 my $error = $part_export->export_delete($self);
269 $dbh->rollback if $oldAutoCommit;
270 return "exporting to ". $part_export->exporttype.
271 " (transaction rolled back): $error";
276 return $error if $error;
278 my $cust_svc = $self->cust_svc;
279 $error = $cust_svc->delete;
280 return $error if $error;
282 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
287 =item replace OLD_RECORD
289 Replaces OLD_RECORD with this one. If there is an error, returns the error,
290 otherwise returns false.
295 my ($new, $old) = (shift, shift);
297 local $SIG{HUP} = 'IGNORE';
298 local $SIG{INT} = 'IGNORE';
299 local $SIG{QUIT} = 'IGNORE';
300 local $SIG{TERM} = 'IGNORE';
301 local $SIG{TSTP} = 'IGNORE';
302 local $SIG{PIPE} = 'IGNORE';
304 my $oldAutoCommit = $FS::UID::AutoCommit;
305 local $FS::UID::AutoCommit = 0;
308 my $error = $new->SUPER::replace($old);
310 $dbh->rollback if $oldAutoCommit;
315 unless ( $noexport_hack ) {
317 #not quite false laziness, but same pattern as FS::svc_acct::replace and
318 #FS::part_export::sqlradius::_export_replace. List::Compare or something
319 #would be useful but too much of a pain in the ass to deploy
321 my @old_part_export = $old->cust_svc->part_svc->part_export;
322 my %old_exportnum = map { $_->exportnum => 1 } @old_part_export;
323 my @new_part_export =
325 ? qsearchs('part_svc', { svcpart=>$new->svcpart } )->part_export
326 : $new->cust_svc->part_svc->part_export;
327 my %new_exportnum = map { $_->exportnum => 1 } @new_part_export;
329 foreach my $delete_part_export (
330 grep { ! $new_exportnum{$_->exportnum} } @old_part_export
332 my $error = $delete_part_export->export_delete($old);
334 $dbh->rollback if $oldAutoCommit;
335 return "error deleting, export to ". $delete_part_export->exporttype.
336 " (transaction rolled back): $error";
340 foreach my $replace_part_export (
341 grep { $old_exportnum{$_->exportnum} } @new_part_export
343 my $error = $replace_part_export->export_replace($new,$old);
345 $dbh->rollback if $oldAutoCommit;
346 return "error exporting to ". $replace_part_export->exporttype.
347 " (transaction rolled back): $error";
351 foreach my $insert_part_export (
352 grep { ! $old_exportnum{$_->exportnum} } @new_part_export
354 my $error = $insert_part_export->export_insert($new);
356 $dbh->rollback if $oldAutoCommit;
357 return "error inserting export to ". $insert_part_export->exporttype.
358 " (transaction rolled back): $error";
364 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
371 Sets any fixed fields for this service (see L<FS::part_svc>). If there is an
372 error, returns the error, otherwise returns the FS::part_svc object (use ref()
373 to test the return). Usually called by the check method.
384 Sets all fields to their defaults (see L<FS::part_svc>), overriding their
385 current values. If there is an error, returns the error, otherwise returns
386 the FS::part_svc object (use ref() to test the return).
402 $self->ut_numbern('svcnum')
404 return $error if $error;
408 if ( $self->get('svcpart') ) {
409 $svcpart = $self->get('svcpart');
410 } elsif ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
411 my $cust_svc = $self->cust_svc;
412 return "Unknown svcnum" unless $cust_svc;
413 $svcpart = $cust_svc->svcpart;
415 my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
416 return "Unkonwn svcpart" unless $part_svc;
418 #set default/fixed/whatever fields from part_svc
419 my $table = $self->table;
420 foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
421 my $part_svc_column = $part_svc->part_svc_column($field);
422 if ( $part_svc_column->columnflag eq $x ) {
423 $self->setfield( $field, $part_svc_column->columnvalue );
433 Returns the cust_svc record associated with this svc_ record, as a FS::cust_svc
434 object (see L<FS::cust_svc>).
440 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
445 Runs export_suspend callbacks.
452 local $SIG{HUP} = 'IGNORE';
453 local $SIG{INT} = 'IGNORE';
454 local $SIG{QUIT} = 'IGNORE';
455 local $SIG{TERM} = 'IGNORE';
456 local $SIG{TSTP} = 'IGNORE';
457 local $SIG{PIPE} = 'IGNORE';
459 my $oldAutoCommit = $FS::UID::AutoCommit;
460 local $FS::UID::AutoCommit = 0;
464 unless ( $noexport_hack ) {
465 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
466 my $error = $part_export->export_suspend($self);
468 $dbh->rollback if $oldAutoCommit;
469 return "error exporting to ". $part_export->exporttype.
470 " (transaction rolled back): $error";
475 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
482 Runs export_unsuspend callbacks.
489 local $SIG{HUP} = 'IGNORE';
490 local $SIG{INT} = 'IGNORE';
491 local $SIG{QUIT} = 'IGNORE';
492 local $SIG{TERM} = 'IGNORE';
493 local $SIG{TSTP} = 'IGNORE';
494 local $SIG{PIPE} = 'IGNORE';
496 my $oldAutoCommit = $FS::UID::AutoCommit;
497 local $FS::UID::AutoCommit = 0;
501 unless ( $noexport_hack ) {
502 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
503 my $error = $part_export->export_unsuspend($self);
505 $dbh->rollback if $oldAutoCommit;
506 return "error exporting to ". $part_export->exporttype.
507 " (transaction rolled back): $error";
512 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
519 Stub - returns false (no error) so derived classes don't need to define these
520 methods. Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
526 =item clone_suspended
528 Constructor used by FS::part_export::_export_suspend fallback. Stub returning
529 same object for svc_ classes which don't implement a suspension fallback
530 (everything except svc_acct at the moment). Document better.
534 sub clone_suspended {
538 =item clone_kludge_unsuspend
540 Constructor used by FS::part_export::_export_unsuspend fallback. Stub returning
541 same object for svc_ classes which don't implement a suspension fallback
542 (everything except svc_acct at the moment). Document better.
546 sub clone_kludge_unsuspend {
554 The setfixed method return value.
558 L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, schema.html
559 from the base documentation.