This commit was generated by cvs2svn to compensate for changes in r3921,
[freeside.git] / FS / FS / export_svc.pm
1 package FS::export_svc;
2
3 use strict;
4 use vars qw( @ISA );
5 use FS::Record qw( qsearch qsearchs dbh );
6 use FS::part_export;
7 use FS::part_svc;
8
9 @ISA = qw(FS::Record);
10
11 =head1 NAME
12
13 FS::export_svc - Object methods for export_svc records
14
15 =head1 SYNOPSIS
16
17   use FS::export_svc;
18
19   $record = new FS::export_svc \%hash;
20   $record = new FS::export_svc { 'column' => 'value' };
21
22   $error = $record->insert;
23
24   $error = $new_record->replace($old_record);
25
26   $error = $record->delete;
27
28   $error = $record->check;
29
30 =head1 DESCRIPTION
31
32 An FS::export_svc object links a service definition (see L<FS::part_svc>) to
33 an export (see L<FS::part_export>).  FS::export_svc inherits from FS::Record.
34 The following fields are currently supported:
35
36 =over 4
37
38 =item exportsvcnum - primary key
39
40 =item exportnum - export (see L<FS::part_export>)
41
42 =item svcpart - service definition (see L<FS::part_svc>)
43
44 =back
45
46 =head1 METHODS
47
48 =over 4
49
50 =item new HASHREF
51
52 Creates a new record.  To add the record to the database, see L<"insert">.
53
54 Note that this stores the hash reference, not a distinct copy of the hash it
55 points to.  You can ask the object for a copy with the I<hash> method.
56
57 =cut
58
59 # the new method can be inherited from FS::Record, if a table method is defined
60
61 sub table { 'export_svc'; }
62
63 =item insert
64
65 Adds this record to the database.  If there is an error, returns the error,
66 otherwise returns false.
67
68 =cut
69
70 sub insert {
71   my $self = shift;
72   my $error;
73
74   local $SIG{HUP} = 'IGNORE';
75   local $SIG{INT} = 'IGNORE';
76   local $SIG{QUIT} = 'IGNORE';
77   local $SIG{TERM} = 'IGNORE';
78   local $SIG{TSTP} = 'IGNORE';
79   local $SIG{PIPE} = 'IGNORE';
80
81   my $oldAutoCommit = $FS::UID::AutoCommit;
82   local $FS::UID::AutoCommit = 0;
83   my $dbh = dbh;
84
85   $error = $self->check;
86   return $error if $error;
87
88   #check for duplicates!
89   my @checks = ();
90   my $svcdb = $self->part_svc->svcdb;
91   if ( $svcdb eq 'svc_acct' ) {
92
93     if ( $self->part_export->nodomain =~ /^Y/i ) {
94       push @checks, {
95         label  => 'usernames',
96         method => 'username',
97         sortby => sub { $a cmp $b },
98       };
99     } else {
100       push @checks, {
101         label  => 'username@domain',
102         method => 'email',
103         sortby => sub {
104                         my($auser, $adomain) = split('@', $a);
105                         my($buser, $bdomain) = split('@', $b);
106                         $adomain cmp $bdomain || $auser cmp $buser;
107                       },
108       };
109     }
110
111     unless ( $self->part_svc->part_svc_column('uid')->columnflag eq 'F' ) {
112       push @checks, {
113         label  => 'uids',
114         method => 'uid',
115         sortby => sub { $a <=> $b },
116       };
117     }
118
119   } elsif ( $svcdb eq 'svc_domain' ) {
120     push @checks, {
121       label  => 'domains',
122       method => 'domain',
123       sortby => sub { $a cmp $b },
124     };
125   } else {
126     warn "WARNING: No duplicate checking done on merge of $svcdb exports";
127   }
128
129   foreach my $check ( @checks ) {
130     my @current_svc = $self->part_export->svc_x;
131     #warn "current: ". scalar(@current_svc). " $current_svc[0]\n";
132     my @new_svc = $self->part_svc->svc_x;
133     #warn "new: ". scalar(@new_svc). " $new_svc[0]\n";
134     my $method = $check->{'method'};
135     my %cur_svc = map { $_->$method() => $_ } @current_svc;
136     my @dup_svc = grep { $cur_svc{$_->$method()} } @new_svc;
137     #my @diff_customer = grep { 
138     #                           $_->cust_pkg->custnum != $cur_svc{$_->$method()}->cust_pkg->custnum
139     #                         } @dup_svc;
140
141
142
143     if ( @dup_svc ) { #aye, that's the rub
144       #error out for now, eventually accept different options of adjustments
145       # to make to allow us to continue forward
146       $dbh->rollback if $oldAutoCommit;
147
148       my @diff_customer_svc = grep {
149         my $cust_pkg = $_->cust_svc->cust_pkg;
150         my $custnum = $cust_pkg ? $cust_pkg->custnum : 0;
151         my $other_cust_pkg = $cur_svc{$_->$method()}->cust_svc->cust_pkg;
152         my $other_custnum = $other_cust_pkg ? $other_cust_pkg->custnum : 0;
153         $custnum != $other_custnum;
154       } @dup_svc;
155
156       my $label = $check->{'label'};
157       my $sortby = $check->{'sortby'};
158       return "Can't export ".
159              $self->part_svc->svcpart.':'.$self->part_svc->svc. " service to ".
160              $self->part_export->exportnum.':'.$self->part_export->exporttype.
161                ' on '. $self->part_export->machine.
162              ' : '. scalar(@dup_svc). " duplicate $label".
163              ' ('. scalar(@diff_customer_svc). " from different customers)".
164              #": ". join(', ', sort $sortby map { $_->$method() } @dup_svc )
165              ": ". join(', ', sort $sortby map { $_->$method() } @diff_customer_svc )
166              ;
167     }
168   }
169
170   #end of duplicate check, whew
171
172   $error = $self->SUPER::insert;
173   if ( $error ) {
174     $dbh->rollback if $oldAutoCommit;
175     return $error;
176   }
177
178 #  if ( $self->part_svc->svcdb eq 'svc_acct' ) {
179 #
180 #    if ( $self->part_export->nodomain =~ /^Y/i ) {
181 #
182 #      select username from svc_acct where svcpart = $svcpart
183 #        group by username having count(*) > 1;
184 #
185 #    } else {
186 #
187 #      select username, domain
188 #        from   svc_acct
189 #          join svc_domain on ( svc_acct.domsvc = svc_domain.svcnum )
190 #        group by username, domain having count(*) > 1;
191 #
192 #    }
193 #
194 #  } elsif ( $self->part_svc->svcdb eq 'svc_domain' ) {
195 #
196 #    #similar but easier domain checking one
197 #
198 #  } #etc.?
199 #
200 #  my @services =
201 #    map  { $_->part_svc }
202 #    grep { $_->svcpart != $self->svcpart }
203 #         $self->part_export->export_svc;
204
205   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
206   ''; #no error
207 }
208
209 =item delete
210
211 Delete this record from the database.
212
213 =cut
214
215 # the delete method can be inherited from FS::Record
216
217 =item replace OLD_RECORD
218
219 Replaces the OLD_RECORD with this one in the database.  If there is an error,
220 returns the error, otherwise returns false.
221
222 =cut
223
224 # the replace method can be inherited from FS::Record
225
226 =item check
227
228 Checks all fields to make sure this is a valid record.  If there is
229 an error, returns the error, otherwise returns false.  Called by the insert
230 and replace methods.
231
232 =cut
233
234 # the check method should currently be supplied - FS::Record contains some
235 # data checking routines
236
237 sub check {
238   my $self = shift;
239
240   $self->ut_numbern('exportsvcnum')
241     || $self->ut_number('exportnum')
242     || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
243     || $self->ut_number('svcpart')
244     || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
245     || $self->SUPER::check
246   ;
247 }
248
249 =item part_export
250
251 Returns the FS::part_export object (see L<FS::part_export>).
252
253 =cut
254
255 sub part_export {
256   my $self = shift;
257   qsearchs( 'part_export', { 'exportnum' => $self->exportnum } );
258 }
259
260 =item part_svc
261
262 Returns the FS::part_svc object (see L<FS::part_svc>).
263
264 =cut
265
266 sub part_svc {
267   my $self = shift;
268   qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
269 }
270
271 =back
272
273 =head1 BUGS
274
275 =head1 SEE ALSO
276
277 L<FS::part_export>, L<FS::part_svc>, L<FS::Record>, schema.html from the base
278 documentation.
279
280 =cut
281
282 1;
283