8af413b1d97a74a14cce8a712bcff3dfe746552d
[freeside.git] / FS / FS / part_export.pm
1 package FS::part_export;
2
3 use strict;
4 use vars qw( @ISA );
5 use FS::Record qw( qsearch qsearchs dbh );
6 use FS::part_svc;
7 use FS::part_export_option;
8 use FS::export_svc;
9
10 @ISA = qw(FS::Record);
11
12 =head1 NAME
13
14 FS::part_export - Object methods for part_export records
15
16 =head1 SYNOPSIS
17
18   use FS::part_export;
19
20   $record = new FS::part_export \%hash;
21   $record = new FS::part_export { 'column' => 'value' };
22
23   #($new_record, $options) = $template_recored->clone( $svcpart );
24
25   $error = $record->insert( { 'option' => 'value' } );
26   $error = $record->insert( \%options );
27
28   $error = $new_record->replace($old_record);
29
30   $error = $record->delete;
31
32   $error = $record->check;
33
34 =head1 DESCRIPTION
35
36 An FS::part_export object represents an export of Freeside data to an external
37 provisioning system.  FS::part_export inherits from FS::Record.  The following
38 fields are currently supported:
39
40 =over 4
41
42 =item exportnum - primary key
43
44 =item machine - Machine name 
45
46 =item exporttype - Export type
47
48 =item nodomain - blank or "Y" : usernames are exported to this service with no domain
49
50 =back
51
52 =head1 METHODS
53
54 =over 4
55
56 =item new HASHREF
57
58 Creates a new export.  To add the export to the database, see L<"insert">.
59
60 Note that this stores the hash reference, not a distinct copy of the hash it
61 points to.  You can ask the object for a copy with the I<hash> method.
62
63 =cut
64
65 # the new method can be inherited from FS::Record, if a table method is defined
66
67 sub table { 'part_export'; }
68
69 =cut
70
71 #=item clone SVCPART
72 #
73 #An alternate constructor.  Creates a new export by duplicating an existing
74 #export.  The given svcpart is assigned to the new export.
75 #
76 #Returns a list consisting of the new export object and a hashref of options.
77 #
78 #=cut
79 #
80 #sub clone {
81 #  my $self = shift;
82 #  my $class = ref($self);
83 #  my %hash = $self->hash;
84 #  $hash{'exportnum'} = '';
85 #  $hash{'svcpart'} = shift;
86 #  ( $class->new( \%hash ),
87 #    { map { $_->optionname => $_->optionvalue }
88 #        qsearch('part_export_option', { 'exportnum' => $self->exportnum } )
89 #    }
90 #  );
91 #}
92
93 =item insert HASHREF
94
95 Adds this record to the database.  If there is an error, returns the error,
96 otherwise returns false.
97
98 If a hash reference of options is supplied, part_export_option records are
99 created (see L<FS::part_export_option>).
100
101 =cut
102
103 #false laziness w/queue.pm
104 sub insert {
105   my $self = shift;
106   my $options = shift;
107   local $SIG{HUP} = 'IGNORE';
108   local $SIG{INT} = 'IGNORE';
109   local $SIG{QUIT} = 'IGNORE';
110   local $SIG{TERM} = 'IGNORE';
111   local $SIG{TSTP} = 'IGNORE';
112   local $SIG{PIPE} = 'IGNORE';
113
114   my $oldAutoCommit = $FS::UID::AutoCommit;
115   local $FS::UID::AutoCommit = 0;
116   my $dbh = dbh;
117
118   my $error = $self->SUPER::insert;
119   if ( $error ) {
120     $dbh->rollback if $oldAutoCommit;
121     return $error;
122   }
123
124   foreach my $optionname ( keys %{$options} ) {
125     my $part_export_option = new FS::part_export_option ( {
126       'exportnum'   => $self->exportnum,
127       'optionname'  => $optionname,
128       'optionvalue' => $options->{$optionname},
129     } );
130     $error = $part_export_option->insert;
131     if ( $error ) {
132       $dbh->rollback if $oldAutoCommit;
133       return $error;
134     }
135   }
136
137   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
138
139   '';
140
141 };
142
143 =item delete
144
145 Delete this record from the database.
146
147 =cut
148
149 #foreign keys would make this much less tedious... grr dumb mysql
150 sub delete {
151   my $self = shift;
152   local $SIG{HUP} = 'IGNORE';
153   local $SIG{INT} = 'IGNORE';
154   local $SIG{QUIT} = 'IGNORE';
155   local $SIG{TERM} = 'IGNORE';
156   local $SIG{TSTP} = 'IGNORE';
157   local $SIG{PIPE} = 'IGNORE';
158
159   my $oldAutoCommit = $FS::UID::AutoCommit;
160   local $FS::UID::AutoCommit = 0;
161   my $dbh = dbh;
162
163   my $error = $self->SUPER::delete;
164   if ( $error ) {
165     $dbh->rollback if $oldAutoCommit;
166     return $error;
167   }
168
169   foreach my $part_export_option ( $self->part_export_option ) {
170     my $error = $part_export_option->delete;
171     if ( $error ) {
172       $dbh->rollback if $oldAutoCommit;
173       return $error;
174     }
175   }
176
177   foreach my $export_svc ( $self->export_svc ) {
178     my $error = $export_svc->delete;
179     if ( $error ) {
180       $dbh->rollback if $oldAutoCommit;
181       return $error;
182     }
183   }
184
185   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
186
187   '';
188
189 }
190
191 =item replace OLD_RECORD HASHREF
192
193 Replaces the OLD_RECORD with this one in the database.  If there is an error,
194 returns the error, otherwise returns false.
195
196 If a hash reference of options is supplied, part_export_option records are
197 created or modified (see L<FS::part_export_option>).
198
199 =cut
200
201 sub replace {
202   my $self = shift;
203   my $old = shift;
204   my $options = shift;
205   local $SIG{HUP} = 'IGNORE';
206   local $SIG{INT} = 'IGNORE';
207   local $SIG{QUIT} = 'IGNORE';
208   local $SIG{TERM} = 'IGNORE';
209   local $SIG{TSTP} = 'IGNORE';
210   local $SIG{PIPE} = 'IGNORE';
211
212   my $oldAutoCommit = $FS::UID::AutoCommit;
213   local $FS::UID::AutoCommit = 0;
214   my $dbh = dbh;
215
216   my $error = $self->SUPER::replace($old);
217   if ( $error ) {
218     $dbh->rollback if $oldAutoCommit;
219     return $error;
220   }
221
222   foreach my $optionname ( keys %{$options} ) {
223     my $old = qsearchs( 'part_export_option', {
224         'exportnum'   => $self->exportnum,
225         'optionname'  => $optionname,
226     } );
227     my $new = new FS::part_export_option ( {
228         'exportnum'   => $self->exportnum,
229         'optionname'  => $optionname,
230         'optionvalue' => $options->{$optionname},
231     } );
232     $new->optionnum($old->optionnum) if $old;
233     my $error = $old ? $new->replace($old) : $new->insert;
234     if ( $error ) {
235       $dbh->rollback if $oldAutoCommit;
236       return $error;
237     }
238   }
239
240   #remove extraneous old options
241   foreach my $opt (
242     grep { !exists $options->{$_->optionname} } $old->part_export_option
243   ) {
244     my $error = $opt->delete;
245     if ( $error ) {
246       $dbh->rollback if $oldAutoCommit;
247       return $error;
248     }
249   }
250
251   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
252
253   '';
254
255 };
256
257 =item check
258
259 Checks all fields to make sure this is a valid export.  If there is
260 an error, returns the error, otherwise returns false.  Called by the insert
261 and replace methods.
262
263 =cut
264
265 sub check {
266   my $self = shift;
267   my $error = 
268     $self->ut_numbern('exportnum')
269     || $self->ut_domain('machine')
270     || $self->ut_alpha('exporttype')
271   ;
272   return $error if $error;
273
274   warn $self->machine. "!!!\n";
275
276   $self->machine =~ /^([\w\-\.]*)$/
277     or return "Illegal machine: ". $self->machine;
278   $self->machine($1);
279
280   $self->nodomain =~ /^(Y?)$/ or return "Illegal nodomain: ". $self->nodomain;
281   $self->nodomain($1);
282
283   $self->deprecated(1); #BLAH
284
285   #check exporttype?
286
287   ''; #no error
288 }
289
290 #=item part_svc
291 #
292 #Returns the service definition (see L<FS::part_svc>) for this export.
293 #
294 #=cut
295 #
296 #sub part_svc {
297 #  my $self = shift;
298 #  qsearchs('part_svc', { svcpart => $self->svcpart } );
299 #}
300
301 sub part_svc {
302   use Carp;
303   croak "FS::part_export::part_svc deprecated";
304   #confess "FS::part_export::part_svc deprecated";
305 }
306
307 =item export_svc
308
309 Returns a list of associated FS::export_svc records.
310
311 =cut
312
313 sub export_svc {
314   my $self = shift;
315   qsearch('export_svc', { 'exportnum' => $self->exportnum } );
316 }
317
318 =item part_export_option
319
320 Returns all options as FS::part_export_option objects (see
321 L<FS::part_export_option>).
322
323 =cut
324
325 sub part_export_option {
326   my $self = shift;
327   qsearch('part_export_option', { 'exportnum' => $self->exportnum } );
328 }
329
330 =item options 
331
332 Returns a list of option names and values suitable for assigning to a hash.
333
334 =cut
335
336 sub options {
337   my $self = shift;
338   map { $_->optionname => $_->optionvalue } $self->part_export_option;
339 }
340
341 =item option OPTIONNAME
342
343 Returns the option value for the given name, or the empty string.
344
345 =cut
346
347 sub option {
348   my $self = shift;
349   my $part_export_option =
350     qsearchs('part_export_option', {
351       exportnum  => $self->exportnum,
352       optionname => shift,
353   } );
354   $part_export_option ? $part_export_option->optionvalue : '';
355 }
356
357 =item rebless
358
359 Reblesses the object into the FS::part_export::EXPORTTYPE class, where
360 EXPORTTYPE is the object's I<exporttype> field.  There should be better docs
361 on how to create new exports (and they should live in their own files and be
362 autoloaded-on-demand), but until then, see L</NEW EXPORT CLASSES>.
363
364 =cut
365
366 sub rebless {
367   my $self = shift;
368   my $exporttype = $self->exporttype;
369   my $class = ref($self). "::$exporttype";
370   eval "use $class;";
371   bless($self, $class);
372 }
373
374 =item export_insert SVC_OBJECT
375
376 =cut
377
378 sub export_insert {
379   my $self = shift;
380   $self->rebless;
381   $self->_export_insert(@_);
382 }
383
384 #sub AUTOLOAD {
385 #  my $self = shift;
386 #  $self->rebless;
387 #  my $method = $AUTOLOAD;
388 #  #$method =~ s/::(\w+)$/::_$1/; #infinite loop prevention
389 #  $method =~ s/::(\w+)$/_$1/; #infinite loop prevention
390 #  $self->$method(@_);
391 #}
392
393 =item export_replace NEW OLD
394
395 =cut
396
397 sub export_replace {
398   my $self = shift;
399   $self->rebless;
400   $self->_export_replace(@_);
401 }
402
403 =item export_delete
404
405 =cut
406
407 sub export_delete {
408   my $self = shift;
409   $self->rebless;
410   $self->_export_delete(@_);
411 }
412
413 #fallbacks providing useful error messages intead of infinite loops
414 sub _export_insert {
415   my $self = shift;
416   return "_export_insert: unknown export type ". $self->exporttype;
417 }
418
419 sub _export_replace {
420   my $self = shift;
421   return "_export_replace: unknown export type ". $self->exporttype;
422 }
423
424 sub _export_delete {
425   my $self = shift;
426   return "_export_delete: unknown export type ". $self->exporttype;
427 }
428
429 =back
430
431 =head1 NEW EXPORT CLASSES
432
433 Should be added to httemplate/edit/part_export.cgi and a module should
434 be FS/FS/part_export/ (an example may be found in eg/export_template.pm)
435
436 =head1 BUGS
437
438 Probably.
439
440 Hmm... cust_export class (not necessarily a database table...) ... ?
441
442 deprecated column...
443
444 =head1 SEE ALSO
445
446 L<FS::part_export_option>, L<FS::export_svc>, L<FS::svc_acct>,
447 L<FS::svc_domain>,
448 L<FS::svc_forward>, L<FS::Record>, schema.html from the base documentation.
449
450 =cut
451
452 1;
453