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 my $class = ref($proto) || $proto;
41 bless ($self, $class);
43 unless ( defined ( $self->table ) ) {
44 $self->{'Table'} = shift;
45 carp "warning: FS::Record::new called with table name ". $self->{'Table'};
48 #$self->{'Hash'} = shift;
50 $self->{'Hash'} = { map { $_ => $newhash->{$_} } qw(svcnum svcpart) };
52 $self->{'Hash'}{$_} = $newhash->{$_}
53 foreach #grep length($newhash->{$_}),
56 foreach my $field ( grep !defined($self->{'Hash'}{$_}), $self->fields ) {
57 $self->{'Hash'}{$field}='';
60 $self->_rebless if $self->can('_rebless');
62 $self->{'modified'} = 0;
64 $self->_cache($self->{'Hash'}, shift) if $self->can('_cache') && @_;
71 # This restricts the fields based on part_svc_column and the svcpart of
72 # the service. There are four possible cases:
73 # 1. svcpart passed as part of the svc_x hash.
74 # 2. svcpart fetched via cust_svc based on svcnum.
75 # 3. No svcnum or svcpart. In this case, return ALL the fields with
76 # dbtable eq $self->table.
77 # 4. Called via "fields('svc_acct')" or something similar. In this case
78 # there is no $self object.
82 my @vfields = $self->SUPER::virtual_fields;
84 return @vfields unless (ref $self); # Case 4
86 if ($self->svcpart) { # Case 1
87 $svcpart = $self->svcpart;
88 } elsif ( $self->svcnum
89 && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
91 $svcpart = $self->cust_svc->svcpart;
96 if ($svcpart) { #Cases 1 and 2
97 my %flags = map { $_->columnname, $_->columnflag } (
98 qsearch ('part_svc_column', { svcpart => $svcpart } )
100 return grep { not ($flags{$_} eq 'X') } @vfields;
109 Checks the validity of fields in this record.
111 At present, this does nothing but call FS::Record::check (which, in turn,
112 does nothing but run virtual field checks).
121 =item insert [ , OPTION => VALUE ... ]
123 Adds this record to the database. If there is an error, returns the error,
124 otherwise returns false.
126 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
127 defined. An FS::cust_svc record will be created and inserted.
129 Currently available options are: I<jobnums>, I<child_objects> and
132 If I<jobnum> is set to an array reference, the jobnums of any export jobs will
133 be added to the referenced array.
135 If I<child_objects> is set to an array reference of FS::tablename objects (for
136 example, FS::acct_snarf objects), they will have their svcnum field set and
137 will be inserted after this record, but before any exports are run. Each
138 element of the array can also optionally be a two-element array reference
139 containing the child object and the name of an alternate field to be filled in
140 with the newly-inserted svcnum, for example C<[ $svc_forward, 'srcsvc' ]>
142 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
143 jobnums), all provisioning jobs will have a dependancy on the supplied
144 jobnum(s) (they will not run until the specific job(s) complete(s)).
151 warn "FS::svc_Common::insert called with options ".
152 join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
156 local $FS::queue::jobnums = \@jobnums;
157 warn "FS::svc_Common::insert: set \$FS::queue::jobnums to $FS::queue::jobnums"
159 my $objects = $options{'child_objects'} || [];
160 my $depend_jobnums = $options{'depend_jobnum'} || [];
161 $depend_jobnums = [ $depend_jobnums ] unless ref($depend_jobnums);
164 local $SIG{HUP} = 'IGNORE';
165 local $SIG{INT} = 'IGNORE';
166 local $SIG{QUIT} = 'IGNORE';
167 local $SIG{TERM} = 'IGNORE';
168 local $SIG{TSTP} = 'IGNORE';
169 local $SIG{PIPE} = 'IGNORE';
171 my $oldAutoCommit = $FS::UID::AutoCommit;
172 local $FS::UID::AutoCommit = 0;
175 $error = $self->check;
176 return $error if $error;
178 my $svcnum = $self->svcnum;
179 my $cust_svc = $svcnum ? qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) : '';
180 #unless ( $svcnum ) {
181 if ( !$svcnum or !$cust_svc ) {
182 $cust_svc = new FS::cust_svc ( {
183 #hua?# 'svcnum' => $svcnum,
184 'svcnum' => $self->svcnum,
185 'pkgnum' => $self->pkgnum,
186 'svcpart' => $self->svcpart,
188 $error = $cust_svc->insert;
190 $dbh->rollback if $oldAutoCommit;
193 $svcnum = $self->svcnum($cust_svc->svcnum);
195 #$cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
196 unless ( $cust_svc ) {
197 $dbh->rollback if $oldAutoCommit;
198 return "no cust_svc record found for svcnum ". $self->svcnum;
200 $self->pkgnum($cust_svc->pkgnum);
201 $self->svcpart($cust_svc->svcpart);
204 $error = $self->SUPER::insert;
206 $dbh->rollback if $oldAutoCommit;
210 foreach my $object ( @$objects ) {
212 if ( ref($object) eq 'ARRAY' ) {
213 ($obj, $field) = @$object;
218 $obj->$field($self->svcnum);
219 $error = $obj->insert;
221 $dbh->rollback if $oldAutoCommit;
227 unless ( $noexport_hack ) {
229 warn "FS::svc_Common::insert: \$FS::queue::jobnums is $FS::queue::jobnums"
232 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
233 my $error = $part_export->export_insert($self);
235 $dbh->rollback if $oldAutoCommit;
236 return "exporting to ". $part_export->exporttype.
237 " (transaction rolled back): $error";
241 foreach my $depend_jobnum ( @$depend_jobnums ) {
242 warn "inserting dependancies on supplied job $depend_jobnum\n"
244 foreach my $jobnum ( @jobnums ) {
245 my $queue = qsearchs('queue', { 'jobnum' => $jobnum } );
246 warn "inserting dependancy for job $jobnum on $depend_jobnum\n"
248 my $error = $queue->depend_insert($depend_jobnum);
250 $dbh->rollback if $oldAutoCommit;
251 return "error queuing job dependancy: $error";
258 if ( exists $options{'jobnums'} ) {
259 push @{ $options{'jobnums'} }, @jobnums;
262 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
269 Deletes this account from the database. If there is an error, returns the
270 error, otherwise returns false.
272 The corresponding FS::cust_svc record will be deleted as well.
280 local $SIG{HUP} = 'IGNORE';
281 local $SIG{INT} = 'IGNORE';
282 local $SIG{QUIT} = 'IGNORE';
283 local $SIG{TERM} = 'IGNORE';
284 local $SIG{TSTP} = 'IGNORE';
285 local $SIG{PIPE} = 'IGNORE';
287 my $svcnum = $self->svcnum;
289 my $oldAutoCommit = $FS::UID::AutoCommit;
290 local $FS::UID::AutoCommit = 0;
293 $error = $self->SUPER::delete;
294 return $error if $error;
297 unless ( $noexport_hack ) {
298 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
299 my $error = $part_export->export_delete($self);
301 $dbh->rollback if $oldAutoCommit;
302 return "exporting to ". $part_export->exporttype.
303 " (transaction rolled back): $error";
308 return $error if $error;
310 my $cust_svc = $self->cust_svc;
311 $error = $cust_svc->delete;
312 return $error if $error;
314 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
319 =item replace OLD_RECORD
321 Replaces OLD_RECORD with this one. If there is an error, returns the error,
322 otherwise returns false.
327 my ($new, $old) = (shift, shift);
329 local $SIG{HUP} = 'IGNORE';
330 local $SIG{INT} = 'IGNORE';
331 local $SIG{QUIT} = 'IGNORE';
332 local $SIG{TERM} = 'IGNORE';
333 local $SIG{TSTP} = 'IGNORE';
334 local $SIG{PIPE} = 'IGNORE';
336 my $oldAutoCommit = $FS::UID::AutoCommit;
337 local $FS::UID::AutoCommit = 0;
340 my $error = $new->SUPER::replace($old);
342 $dbh->rollback if $oldAutoCommit;
347 unless ( $noexport_hack ) {
349 #not quite false laziness, but same pattern as FS::svc_acct::replace and
350 #FS::part_export::sqlradius::_export_replace. List::Compare or something
351 #would be useful but too much of a pain in the ass to deploy
353 my @old_part_export = $old->cust_svc->part_svc->part_export;
354 my %old_exportnum = map { $_->exportnum => 1 } @old_part_export;
355 my @new_part_export =
357 ? qsearchs('part_svc', { svcpart=>$new->svcpart } )->part_export
358 : $new->cust_svc->part_svc->part_export;
359 my %new_exportnum = map { $_->exportnum => 1 } @new_part_export;
361 foreach my $delete_part_export (
362 grep { ! $new_exportnum{$_->exportnum} } @old_part_export
364 my $error = $delete_part_export->export_delete($old);
366 $dbh->rollback if $oldAutoCommit;
367 return "error deleting, export to ". $delete_part_export->exporttype.
368 " (transaction rolled back): $error";
372 foreach my $replace_part_export (
373 grep { $old_exportnum{$_->exportnum} } @new_part_export
375 my $error = $replace_part_export->export_replace($new,$old);
377 $dbh->rollback if $oldAutoCommit;
378 return "error exporting to ". $replace_part_export->exporttype.
379 " (transaction rolled back): $error";
383 foreach my $insert_part_export (
384 grep { ! $old_exportnum{$_->exportnum} } @new_part_export
386 my $error = $insert_part_export->export_insert($new);
388 $dbh->rollback if $oldAutoCommit;
389 return "error inserting export to ". $insert_part_export->exporttype.
390 " (transaction rolled back): $error";
396 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
403 Sets any fixed fields for this service (see L<FS::part_svc>). If there is an
404 error, returns the error, otherwise returns the FS::part_svc object (use ref()
405 to test the return). Usually called by the check method.
416 Sets all fields to their defaults (see L<FS::part_svc>), overriding their
417 current values. If there is an error, returns the error, otherwise returns
418 the FS::part_svc object (use ref() to test the return).
434 $self->ut_numbern('svcnum')
436 return $error if $error;
440 if ( $self->get('svcpart') ) {
441 $svcpart = $self->get('svcpart');
442 } elsif ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
443 my $cust_svc = $self->cust_svc;
444 return "Unknown svcnum" unless $cust_svc;
445 $svcpart = $cust_svc->svcpart;
447 my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
448 return "Unkonwn svcpart" unless $part_svc;
450 #set default/fixed/whatever fields from part_svc
451 my $table = $self->table;
452 foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
453 my $part_svc_column = $part_svc->part_svc_column($field);
454 if ( $part_svc_column->columnflag eq $x ) {
455 $self->setfield( $field, $part_svc_column->columnvalue );
465 Returns the cust_svc record associated with this svc_ record, as a FS::cust_svc
466 object (see L<FS::cust_svc>).
472 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
477 Runs export_suspend callbacks.
484 local $SIG{HUP} = 'IGNORE';
485 local $SIG{INT} = 'IGNORE';
486 local $SIG{QUIT} = 'IGNORE';
487 local $SIG{TERM} = 'IGNORE';
488 local $SIG{TSTP} = 'IGNORE';
489 local $SIG{PIPE} = 'IGNORE';
491 my $oldAutoCommit = $FS::UID::AutoCommit;
492 local $FS::UID::AutoCommit = 0;
496 unless ( $noexport_hack ) {
497 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
498 my $error = $part_export->export_suspend($self);
500 $dbh->rollback if $oldAutoCommit;
501 return "error exporting to ". $part_export->exporttype.
502 " (transaction rolled back): $error";
507 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
514 Runs export_unsuspend callbacks.
521 local $SIG{HUP} = 'IGNORE';
522 local $SIG{INT} = 'IGNORE';
523 local $SIG{QUIT} = 'IGNORE';
524 local $SIG{TERM} = 'IGNORE';
525 local $SIG{TSTP} = 'IGNORE';
526 local $SIG{PIPE} = 'IGNORE';
528 my $oldAutoCommit = $FS::UID::AutoCommit;
529 local $FS::UID::AutoCommit = 0;
533 unless ( $noexport_hack ) {
534 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
535 my $error = $part_export->export_unsuspend($self);
537 $dbh->rollback if $oldAutoCommit;
538 return "error exporting to ". $part_export->exporttype.
539 " (transaction rolled back): $error";
544 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
551 Stub - returns false (no error) so derived classes don't need to define these
552 methods. Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
558 =item clone_suspended
560 Constructor used by FS::part_export::_export_suspend fallback. Stub returning
561 same object for svc_ classes which don't implement a suspension fallback
562 (everything except svc_acct at the moment). Document better.
566 sub clone_suspended {
570 =item clone_kludge_unsuspend
572 Constructor used by FS::part_export::_export_unsuspend fallback. Stub returning
573 same object for svc_ classes which don't implement a suspension fallback
574 (everything except svc_acct at the moment). Document better.
578 sub clone_kludge_unsuspend {
586 The setfixed method return value.
590 L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, schema.html
591 from the base documentation.