1 package FS::export_svc;
5 use FS::Record qw( qsearch qsearchs dbh );
8 use FS::svc_export_machine;
10 @ISA = qw(FS::Record);
14 FS::export_svc - Object methods for export_svc records
20 $record = new FS::export_svc \%hash;
21 $record = new FS::export_svc { 'column' => 'value' };
23 $error = $record->insert;
25 $error = $new_record->replace($old_record);
27 $error = $record->delete;
29 $error = $record->check;
33 An FS::export_svc object links a service definition (see L<FS::part_svc>) to
34 an export (see L<FS::part_export>). FS::export_svc inherits from FS::Record.
35 The following fields are currently supported:
39 =item exportsvcnum - primary key
41 =item exportnum - export (see L<FS::part_export>)
43 =item svcpart - service definition (see L<FS::part_svc>)
45 =item role - export role (see export parameters)
55 Creates a new record. To add the record to the database, see L<"insert">.
57 Note that this stores the hash reference, not a distinct copy of the hash it
58 points to. You can ask the object for a copy with the I<hash> method.
62 # the new method can be inherited from FS::Record, if a table method is defined
64 sub table { 'export_svc'; }
66 =item insert [ JOB, OFFSET, MULTIPLIER ]
68 Adds this record to the database. If there is an error, returns the error,
69 otherwise returns false.
71 TODOC: JOB, OFFSET, MULTIPLIER
77 my( $job, $offset, $mult ) = ( '', 0, 100);
79 $offset = shift if @_;
82 local $SIG{HUP} = 'IGNORE';
83 local $SIG{INT} = 'IGNORE';
84 local $SIG{QUIT} = 'IGNORE';
85 local $SIG{TERM} = 'IGNORE';
86 local $SIG{TSTP} = 'IGNORE';
87 local $SIG{PIPE} = 'IGNORE';
89 my $oldAutoCommit = $FS::UID::AutoCommit;
90 local $FS::UID::AutoCommit = 0;
93 my $error = $self->check;
94 return $error if $error;
96 #check for duplicates!
98 my $svcdb = $self->part_svc->svcdb;
99 if ( $svcdb eq 'svc_acct' ) {
101 if ( $self->part_export->nodomain =~ /^Y/i ) {
103 label => 'usernames',
104 method => 'username',
105 sortby => sub { $a cmp $b },
109 label => 'username@domain',
112 my($auser, $adomain) = split('@', $a);
113 my($buser, $bdomain) = split('@', $b);
114 $adomain cmp $bdomain || $auser cmp $buser;
119 unless ( $self->part_svc->part_svc_column('uid')->columnflag eq 'F' ) {
123 sortby => sub { $a <=> $b },
127 } elsif ( $svcdb eq 'svc_domain' ) {
131 sortby => sub { $a cmp $b },
134 warn "WARNING: No duplicate checking done on merge of $svcdb exports";
140 my $percheck = $mult / scalar(@checks);
142 foreach my $check ( @checks ) {
145 $error = $job->update_statustext(int( $offset + ($done+.33) *$percheck ));
147 $dbh->rollback if $oldAutoCommit;
152 my @current_svc = $self->part_export->svc_x;
153 #warn "current: ". scalar(@current_svc). " $current_svc[0]\n";
156 $error = $job->update_statustext(int( $offset + ($done+.67) *$percheck ));
158 $dbh->rollback if $oldAutoCommit;
163 my @new_svc = $self->part_svc->svc_x;
164 #warn "new: ". scalar(@new_svc). " $new_svc[0]\n";
167 $error = $job->update_statustext(int( $offset + ($done+1) *$percheck ));
169 $dbh->rollback if $oldAutoCommit;
174 my $method = $check->{'method'};
175 my %cur_svc = map { $_->$method() => $_ } @current_svc;
176 my @dup_svc = grep { $cur_svc{$_->$method()} } @new_svc;
177 #my @diff_customer = grep {
178 # $_->cust_pkg->custnum != $cur_svc{$_->$method()}->cust_pkg->custnum
183 if ( @dup_svc ) { #aye, that's the rub
184 #error out for now, eventually accept different options of adjustments
185 # to make to allow us to continue forward
186 $dbh->rollback if $oldAutoCommit;
188 my @diff_customer_svc = grep {
189 my $cust_pkg = $_->cust_svc->cust_pkg;
190 my $custnum = $cust_pkg ? $cust_pkg->custnum : 0;
191 my $other_cust_pkg = $cur_svc{$_->$method()}->cust_svc->cust_pkg;
192 my $other_custnum = $other_cust_pkg ? $other_cust_pkg->custnum : 0;
193 $custnum != $other_custnum;
196 my $label = $check->{'label'};
197 my $sortby = $check->{'sortby'};
198 return "Can't export ".
199 $self->part_svc->svcpart.':'.$self->part_svc->svc. " service to ".
200 $self->part_export->exportnum.':'.$self->part_export->exporttype.
201 ' on '. $self->part_export->machine.
202 ' : '. scalar(@dup_svc). " duplicate $label".
203 ' ('. scalar(@diff_customer_svc). " from different customers)".
204 ": ". join(', ', sort $sortby map { $_->$method() } @dup_svc )
205 #": ". join(', ', sort $sortby map { $_->$method() } @diff_customer_svc )
212 } #end of duplicate check, whew
214 $error = $self->SUPER::insert;
216 my $part_export = $self->part_export;
217 if ( !$error and $part_export->default_machine ) {
218 foreach my $cust_svc ( $self->part_svc->cust_svc ) {
219 my $svc_export_machine = FS::svc_export_machine->new({
220 'exportnum' => $self->exportnum,
221 'svcnum' => $cust_svc->svcnum,
222 'machinenum' => $part_export->default_machine,
224 $error ||= $svc_export_machine->insert;
229 $dbh->rollback if $oldAutoCommit;
233 # if ( $self->part_svc->svcdb eq 'svc_acct' ) {
235 # if ( $self->part_export->nodomain =~ /^Y/i ) {
237 # select username from svc_acct where svcpart = $svcpart
238 # group by username having count(*) > 1;
242 # select username, domain
244 # join svc_domain on ( svc_acct.domsvc = svc_domain.svcnum )
245 # group by username, domain having count(*) > 1;
249 # } elsif ( $self->part_svc->svcdb eq 'svc_domain' ) {
251 # #similar but easier domain checking one
256 # map { $_->part_svc }
257 # grep { $_->svcpart != $self->svcpart }
258 # $self->part_export->export_svc;
260 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
266 Delete this record from the database.
273 my $oldAutoCommit = $FS::UID::AutoCommit;
274 local $FS::UID::AutoCommit = 0;
276 my $error = $self->SUPER::delete;
277 foreach ($self->svc_export_machine) {
278 $error ||= $_->delete;
281 $dbh->rollback if $oldAutoCommit;
284 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
288 =item replace OLD_RECORD
290 Replaces the OLD_RECORD with this one in the database. If there is an error,
291 returns the error, otherwise returns false.
295 # the replace method can be inherited from FS::Record
299 Checks all fields to make sure this is a valid record. If there is
300 an error, returns the error, otherwise returns false. Called by the insert
305 # the check method should currently be supplied - FS::Record contains some
306 # data checking routines
311 $self->ut_numbern('exportsvcnum')
312 || $self->ut_number('exportnum')
313 || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
314 || $self->ut_number('svcpart')
315 || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
316 || $self->ut_alphan('role')
317 || $self->SUPER::check
320 my $part_export = $self->part_export;
321 if ( exists $part_export->info->{roles} ) {
322 my $role = $self->get('role');
324 return 'must select an export role'
326 if ( ! exists($part_export->info->{roles}->{$role}) ) {
327 return "invalid role for export '".$part_export->exporttype."'";
330 $self->set('role', '');
338 Returns the FS::part_export object (see L<FS::part_export>).
344 qsearchs( 'part_export', { 'exportnum' => $self->exportnum } );
349 Returns the FS::part_svc object (see L<FS::part_svc>).
355 qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
358 =item svc_export_machine
360 Returns all export hostname records (L<FS::svc_export_machine>) for this
361 combination of svcpart and exportnum.
365 sub svc_export_machine {
368 'table' => 'svc_export_machine',
369 'select' => 'svc_export_machine.*',
370 'addl_from' => 'JOIN cust_svc USING (svcnum)',
371 'hashref' => { 'exportnum' => $self->exportnum },
372 'extra_sql' => ' AND cust_svc.svcpart = '.$self->svcpart,
382 L<FS::part_export>, L<FS::part_svc>, L<FS::Record>, schema.html from the base