first try at duplicate checking on new export associations
[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 #TODO:
90 #- XXX here
91 #
92 #- have edit/process/part_svc.cgi redirect with error back to 
93 #edit/part_svc.cgi rather than eidiot out
94 #
95 #- rewrite in SQL for efficiency
96
97   my $label = '';
98   my $method = '';
99   my $svcdb = $self->part_svc->svcdb;
100   if ( $svcdb eq 'svc_acct' ) { #XXX AND UID!  sheesh @method or %method not $method
101     if ( $self->part_export->nodomain =~ /^Y/i ) {
102       $label = 'usernames';
103       $method = 'username';
104     } else {
105       $label = 'username@domain';
106       $method = 'email';
107     }
108   } elsif ( $svcdb eq 'domain' ) {
109     $label = 'domains';
110     $method = 'domain';
111   } else {
112     warn "WARNING: XXX fill in this warning";
113   }
114
115   #warn "$method\n";
116   if ( $method ) {
117     my @current_svc = $self->part_export->svc_x;
118     #warn "current: ". scalar(@current_svc). " $current_svc[0]\n";
119     my @new_svc = $self->part_svc->svc_x;
120     #warn "new: ". scalar(@new_svc). " $new_svc[0]\n";
121     my %cur_svc = map { $_->$method() => 1 } @current_svc;
122     my @dup_svc = grep { $cur_svc{$_->$method()} } @new_svc;
123
124     if ( @dup_svc ) { #aye, that's the rub
125       #error out for now, eventually accept different options of adjustments
126       # to make to allow us to continue forward
127       $dbh->rollback if $oldAutoCommit;
128       return "Can't export ".
129              $self->part_svc->svcpart.':'.$self->part_svc->svc. " service to ".
130              $self->part_export->exportnum.':'.$self->part_export->exporttype.
131                ' on '. $self->part_export->machine.
132              ' : '. scalar(@dup_svc). " duplicate $label: ".
133                join(', ', sort map { $_->$method() } @dup_svc );
134              #XXX eventually a sort sub so usernames and domains are default alpha, username@domain is domain first then username, and uid is numeric
135     }
136   }
137
138   #end of duplicate check, whew
139
140   $error = $self->SUPER::insert;
141   if ( $error ) {
142     $dbh->rollback if $oldAutoCommit;
143     return $error;
144   }
145
146 #  if ( $self->part_svc->svcdb eq 'svc_acct' ) {
147 #
148 #    if ( $self->part_export->nodomain =~ /^Y/i ) {
149 #
150 #      select username from svc_acct where svcpart = $svcpart
151 #        group by username having count(*) > 1;
152 #
153 #    } else {
154 #
155 #      select username, domain
156 #        from   svc_acct
157 #          join svc_domain on ( svc_acct.domsvc = svc_domain.svcnum )
158 #        group by username, domain having count(*) > 1;
159 #
160 #    }
161 #
162 #  } elsif ( $self->part_svc->svcdb eq 'svc_domain' ) {
163 #
164 #    #similar but easier domain checking one
165 #
166 #  } #etc.?
167 #
168 #  my @services =
169 #    map  { $_->part_svc }
170 #    grep { $_->svcpart != $self->svcpart }
171 #         $self->part_export->export_svc;
172
173   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
174   ''; #no error
175 }
176
177 =item delete
178
179 Delete this record from the database.
180
181 =cut
182
183 # the delete method can be inherited from FS::Record
184
185 =item replace OLD_RECORD
186
187 Replaces the OLD_RECORD with this one in the database.  If there is an error,
188 returns the error, otherwise returns false.
189
190 =cut
191
192 # the replace method can be inherited from FS::Record
193
194 =item check
195
196 Checks all fields to make sure this is a valid record.  If there is
197 an error, returns the error, otherwise returns false.  Called by the insert
198 and replace methods.
199
200 =cut
201
202 # the check method should currently be supplied - FS::Record contains some
203 # data checking routines
204
205 sub check {
206   my $self = shift;
207
208   $self->ut_numbern('exportsvcnum')
209     || $self->ut_number('exportnum')
210     || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
211     || $self->ut_number('svcpart')
212     || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
213     || $self->SUPER::check
214   ;
215 }
216
217 =item part_export
218
219 Returns the FS::part_export object (see L<FS::part_export>).
220
221 =cut
222
223 sub part_export {
224   my $self = shift;
225   qsearchs( 'part_export', { 'exportnum' => $self->exportnum } );
226 }
227
228 =item part_svc
229
230 Returns the FS::part_svc object (see L<FS::part_svc>).
231
232 =cut
233
234 sub part_svc {
235   my $self = shift;
236   qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
237 }
238
239 =back
240
241 =head1 BUGS
242
243 =head1 SEE ALSO
244
245 L<FS::part_export>, L<FS::part_svc>, L<FS::Record>, schema.html from the base
246 documentation.
247
248 =cut
249
250 1;
251