1 package FS::svc_Common;
4 use vars qw( @ISA $noexport_hack $DEBUG );
6 use FS::Record qw( qsearch qsearchs fields dbh );
7 use FS::cust_main_Mixin;
13 @ISA = qw( FS::cust_main_Mixin FS::Record );
19 FS::svc_Common - Object method for all svc_ records
25 @ISA = qw( FS::svc_Common );
29 FS::svc_Common is intended as a base class for table-specific classes to
30 inherit from, i.e. FS::svc_acct. FS::svc_Common inherits from FS::Record.
40 my $class = ref($proto) || $proto;
42 bless ($self, $class);
44 unless ( defined ( $self->table ) ) {
45 $self->{'Table'} = shift;
46 carp "warning: FS::Record::new called with table name ". $self->{'Table'};
49 #$self->{'Hash'} = shift;
51 $self->{'Hash'} = { map { $_ => $newhash->{$_} } qw(svcnum svcpart) };
53 $self->{'Hash'}{$_} = $newhash->{$_}
54 foreach grep { defined($newhash->{$_}) && length($newhash->{$_}) }
57 foreach my $field ( grep !defined($self->{'Hash'}{$_}), $self->fields ) {
58 $self->{'Hash'}{$field}='';
61 $self->_rebless if $self->can('_rebless');
63 $self->{'modified'} = 0;
65 $self->_cache($self->{'Hash'}, shift) if $self->can('_cache') && @_;
72 # This restricts the fields based on part_svc_column and the svcpart of
73 # the service. There are four possible cases:
74 # 1. svcpart passed as part of the svc_x hash.
75 # 2. svcpart fetched via cust_svc based on svcnum.
76 # 3. No svcnum or svcpart. In this case, return ALL the fields with
77 # dbtable eq $self->table.
78 # 4. Called via "fields('svc_acct')" or something similar. In this case
79 # there is no $self object.
83 my @vfields = $self->SUPER::virtual_fields;
85 return @vfields unless (ref $self); # Case 4
87 if ($self->svcpart) { # Case 1
88 $svcpart = $self->svcpart;
89 } elsif ( $self->svcnum
90 && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
92 $svcpart = $self->cust_svc->svcpart;
97 if ($svcpart) { #Cases 1 and 2
98 my %flags = map { $_->columnname, $_->columnflag } (
99 qsearch ('part_svc_column', { svcpart => $svcpart } )
101 return grep { not ($flags{$_} eq 'X') } @vfields;
110 Checks the validity of fields in this record.
112 At present, this does nothing but call FS::Record::check (which, in turn,
113 does nothing but run virtual field checks).
122 =item insert [ , OPTION => VALUE ... ]
124 Adds this record to the database. If there is an error, returns the error,
125 otherwise returns false.
127 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
128 defined. An FS::cust_svc record will be created and inserted.
130 Currently available options are: I<jobnums>, I<child_objects> and
133 If I<jobnum> is set to an array reference, the jobnums of any export jobs will
134 be added to the referenced array.
136 If I<child_objects> is set to an array reference of FS::tablename objects (for
137 example, FS::acct_snarf objects), they will have their svcnum field set and
138 will be inserted after this record, but before any exports are run. Each
139 element of the array can also optionally be a two-element array reference
140 containing the child object and the name of an alternate field to be filled in
141 with the newly-inserted svcnum, for example C<[ $svc_forward, 'srcsvc' ]>
143 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
144 jobnums), all provisioning jobs will have a dependancy on the supplied
145 jobnum(s) (they will not run until the specific job(s) complete(s)).
152 warn "FS::svc_Common::insert called with options ".
153 join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
157 local $FS::queue::jobnums = \@jobnums;
158 warn "FS::svc_Common::insert: set \$FS::queue::jobnums to $FS::queue::jobnums"
160 my $objects = $options{'child_objects'} || [];
161 my $depend_jobnums = $options{'depend_jobnum'} || [];
162 $depend_jobnums = [ $depend_jobnums ] unless ref($depend_jobnums);
165 local $SIG{HUP} = 'IGNORE';
166 local $SIG{INT} = 'IGNORE';
167 local $SIG{QUIT} = 'IGNORE';
168 local $SIG{TERM} = 'IGNORE';
169 local $SIG{TSTP} = 'IGNORE';
170 local $SIG{PIPE} = 'IGNORE';
172 my $oldAutoCommit = $FS::UID::AutoCommit;
173 local $FS::UID::AutoCommit = 0;
176 $error = $self->check;
177 return $error if $error;
179 my $svcnum = $self->svcnum;
180 my $cust_svc = $svcnum ? qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) : '';
181 #unless ( $svcnum ) {
182 if ( !$svcnum or !$cust_svc ) {
183 $cust_svc = new FS::cust_svc ( {
184 #hua?# 'svcnum' => $svcnum,
185 'svcnum' => $self->svcnum,
186 'pkgnum' => $self->pkgnum,
187 'svcpart' => $self->svcpart,
189 $error = $cust_svc->insert;
191 $dbh->rollback if $oldAutoCommit;
194 $svcnum = $self->svcnum($cust_svc->svcnum);
196 #$cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
197 unless ( $cust_svc ) {
198 $dbh->rollback if $oldAutoCommit;
199 return "no cust_svc record found for svcnum ". $self->svcnum;
201 $self->pkgnum($cust_svc->pkgnum);
202 $self->svcpart($cust_svc->svcpart);
205 $error = $self->SUPER::insert;
207 $dbh->rollback if $oldAutoCommit;
211 foreach my $object ( @$objects ) {
213 if ( ref($object) eq 'ARRAY' ) {
214 ($obj, $field) = @$object;
219 $obj->$field($self->svcnum);
220 $error = $obj->insert;
222 $dbh->rollback if $oldAutoCommit;
228 unless ( $noexport_hack ) {
230 warn "FS::svc_Common::insert: \$FS::queue::jobnums is $FS::queue::jobnums"
233 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
234 my $error = $part_export->export_insert($self);
236 $dbh->rollback if $oldAutoCommit;
237 return "exporting to ". $part_export->exporttype.
238 " (transaction rolled back): $error";
242 foreach my $depend_jobnum ( @$depend_jobnums ) {
243 warn "inserting dependancies on supplied job $depend_jobnum\n"
245 foreach my $jobnum ( @jobnums ) {
246 my $queue = qsearchs('queue', { 'jobnum' => $jobnum } );
247 warn "inserting dependancy for job $jobnum on $depend_jobnum\n"
249 my $error = $queue->depend_insert($depend_jobnum);
251 $dbh->rollback if $oldAutoCommit;
252 return "error queuing job dependancy: $error";
259 if ( exists $options{'jobnums'} ) {
260 push @{ $options{'jobnums'} }, @jobnums;
263 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
270 Deletes this account from the database. If there is an error, returns the
271 error, otherwise returns false.
273 The corresponding FS::cust_svc record will be deleted as well.
281 local $SIG{HUP} = 'IGNORE';
282 local $SIG{INT} = 'IGNORE';
283 local $SIG{QUIT} = 'IGNORE';
284 local $SIG{TERM} = 'IGNORE';
285 local $SIG{TSTP} = 'IGNORE';
286 local $SIG{PIPE} = 'IGNORE';
288 my $svcnum = $self->svcnum;
290 my $oldAutoCommit = $FS::UID::AutoCommit;
291 local $FS::UID::AutoCommit = 0;
294 $error = $self->SUPER::delete;
295 return $error if $error;
298 unless ( $noexport_hack ) {
299 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
300 my $error = $part_export->export_delete($self);
302 $dbh->rollback if $oldAutoCommit;
303 return "exporting to ". $part_export->exporttype.
304 " (transaction rolled back): $error";
309 return $error if $error;
311 my $cust_svc = $self->cust_svc;
312 $error = $cust_svc->delete;
313 return $error if $error;
315 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
320 =item replace OLD_RECORD
322 Replaces OLD_RECORD with this one. If there is an error, returns the error,
323 otherwise returns false.
328 my ($new, $old) = (shift, shift);
330 local $SIG{HUP} = 'IGNORE';
331 local $SIG{INT} = 'IGNORE';
332 local $SIG{QUIT} = 'IGNORE';
333 local $SIG{TERM} = 'IGNORE';
334 local $SIG{TSTP} = 'IGNORE';
335 local $SIG{PIPE} = 'IGNORE';
337 my $oldAutoCommit = $FS::UID::AutoCommit;
338 local $FS::UID::AutoCommit = 0;
341 my $error = $new->SUPER::replace($old);
343 $dbh->rollback if $oldAutoCommit;
348 unless ( $noexport_hack ) {
350 #not quite false laziness, but same pattern as FS::svc_acct::replace and
351 #FS::part_export::sqlradius::_export_replace. List::Compare or something
352 #would be useful but too much of a pain in the ass to deploy
354 my @old_part_export = $old->cust_svc->part_svc->part_export;
355 my %old_exportnum = map { $_->exportnum => 1 } @old_part_export;
356 my @new_part_export =
358 ? qsearchs('part_svc', { svcpart=>$new->svcpart } )->part_export
359 : $new->cust_svc->part_svc->part_export;
360 my %new_exportnum = map { $_->exportnum => 1 } @new_part_export;
362 foreach my $delete_part_export (
363 grep { ! $new_exportnum{$_->exportnum} } @old_part_export
365 my $error = $delete_part_export->export_delete($old);
367 $dbh->rollback if $oldAutoCommit;
368 return "error deleting, export to ". $delete_part_export->exporttype.
369 " (transaction rolled back): $error";
373 foreach my $replace_part_export (
374 grep { $old_exportnum{$_->exportnum} } @new_part_export
376 my $error = $replace_part_export->export_replace($new,$old);
378 $dbh->rollback if $oldAutoCommit;
379 return "error exporting to ". $replace_part_export->exporttype.
380 " (transaction rolled back): $error";
384 foreach my $insert_part_export (
385 grep { ! $old_exportnum{$_->exportnum} } @new_part_export
387 my $error = $insert_part_export->export_insert($new);
389 $dbh->rollback if $oldAutoCommit;
390 return "error inserting export to ". $insert_part_export->exporttype.
391 " (transaction rolled back): $error";
397 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
404 Sets any fixed fields for this service (see L<FS::part_svc>). If there is an
405 error, returns the error, otherwise returns the FS::part_svc object (use ref()
406 to test the return). Usually called by the check method.
417 Sets all fields to their defaults (see L<FS::part_svc>), overriding their
418 current values. If there is an error, returns the error, otherwise returns
419 the FS::part_svc object (use ref() to test the return).
435 $self->ut_numbern('svcnum')
437 return $error if $error;
441 if ( $self->get('svcpart') ) {
442 $svcpart = $self->get('svcpart');
443 } elsif ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
444 my $cust_svc = $self->cust_svc;
445 return "Unknown svcnum" unless $cust_svc;
446 $svcpart = $cust_svc->svcpart;
448 my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
449 return "Unkonwn svcpart" unless $part_svc;
451 #set default/fixed/whatever fields from part_svc
452 my $table = $self->table;
453 foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
454 my $part_svc_column = $part_svc->part_svc_column($field);
455 if ( $part_svc_column->columnflag eq $x ) {
456 $self->setfield( $field, $part_svc_column->columnvalue );
466 Returns the cust_svc record associated with this svc_ record, as a FS::cust_svc
467 object (see L<FS::cust_svc>).
473 qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
478 Runs export_suspend callbacks.
485 local $SIG{HUP} = 'IGNORE';
486 local $SIG{INT} = 'IGNORE';
487 local $SIG{QUIT} = 'IGNORE';
488 local $SIG{TERM} = 'IGNORE';
489 local $SIG{TSTP} = 'IGNORE';
490 local $SIG{PIPE} = 'IGNORE';
492 my $oldAutoCommit = $FS::UID::AutoCommit;
493 local $FS::UID::AutoCommit = 0;
497 unless ( $noexport_hack ) {
498 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
499 my $error = $part_export->export_suspend($self);
501 $dbh->rollback if $oldAutoCommit;
502 return "error exporting to ". $part_export->exporttype.
503 " (transaction rolled back): $error";
508 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
515 Runs export_unsuspend callbacks.
522 local $SIG{HUP} = 'IGNORE';
523 local $SIG{INT} = 'IGNORE';
524 local $SIG{QUIT} = 'IGNORE';
525 local $SIG{TERM} = 'IGNORE';
526 local $SIG{TSTP} = 'IGNORE';
527 local $SIG{PIPE} = 'IGNORE';
529 my $oldAutoCommit = $FS::UID::AutoCommit;
530 local $FS::UID::AutoCommit = 0;
534 unless ( $noexport_hack ) {
535 foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
536 my $error = $part_export->export_unsuspend($self);
538 $dbh->rollback if $oldAutoCommit;
539 return "error exporting to ". $part_export->exporttype.
540 " (transaction rolled back): $error";
545 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
552 Stub - returns false (no error) so derived classes don't need to define these
553 methods. Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
559 =item clone_suspended
561 Constructor used by FS::part_export::_export_suspend fallback. Stub returning
562 same object for svc_ classes which don't implement a suspension fallback
563 (everything except svc_acct at the moment). Document better.
567 sub clone_suspended {
571 =item clone_kludge_unsuspend
573 Constructor used by FS::part_export::_export_unsuspend fallback. Stub returning
574 same object for svc_ classes which don't implement a suspension fallback
575 (everything except svc_acct at the moment). Document better.
579 sub clone_kludge_unsuspend {
587 The setfixed method return value.
591 L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, schema.html
592 from the base documentation.