5 use FS::Record qw( qsearch qsearchs fields dbh );
6 use FS::part_svc_column;
11 @ISA = qw(FS::Record);
15 FS::part_svc - Object methods for part_svc objects
21 $record = new FS::part_svc \%hash
22 $record = new FS::part_svc { 'column' => 'value' };
24 $error = $record->insert;
25 $error = $record->insert( [ 'pseudofield' ] );
26 $error = $record->insert( [ 'pseudofield' ], \%exportnums );
28 $error = $new_record->replace($old_record);
29 $error = $new_record->replace($old_record, '1.3-COMPAT', [ 'pseudofield' ] );
30 $error = $new_record->replace($old_record, '1.3-COMPAT', [ 'pseudofield' ], \%exportnums );
32 $error = $record->delete;
34 $error = $record->check;
38 An FS::part_svc represents a service definition. FS::part_svc inherits from
39 FS::Record. The following fields are currently supported:
43 =item svcpart - primary key (assigned automatically for new service definitions)
45 =item svc - text name of this service definition
47 =item svcdb - table used for this service. See L<FS::svc_acct>,
48 L<FS::svc_domain>, and L<FS::svc_forward>, among others.
50 =item disabled - Disabled flag, empty or `Y'
60 Creates a new service definition. To add the service definition to the
61 database, see L<"insert">.
65 sub table { 'part_svc'; }
67 =item insert [ EXTRA_FIELDS_ARRAYREF [ , EXPORTNUMS_HASHREF ] ]
69 Adds this service definition to the database. If there is an error, returns
70 the error, otherwise returns false.
72 The following pseudo-fields may be defined, and will be maintained in
73 the part_svc_column table appropriately (see L<FS::part_svc_column>).
77 =item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
79 =item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed
83 If you want to add part_svc_column records for fields that do not exist as
84 (real or virtual) fields in the I<svcdb> table, make sure to list then in
85 EXTRA_FIELDS_ARRAYREF also.
87 If EXPORTNUMS_HASHREF is specified (keys are exportnums and values are
88 boolean), the appopriate export_svc records will be inserted.
96 @fields = @{shift(@_)} if @_;
98 my $exportnums = shift;
99 @exportnums = grep $exportnums->{$_}, keys %$exportnums;
102 local $SIG{HUP} = 'IGNORE';
103 local $SIG{INT} = 'IGNORE';
104 local $SIG{QUIT} = 'IGNORE';
105 local $SIG{TERM} = 'IGNORE';
106 local $SIG{TSTP} = 'IGNORE';
107 local $SIG{PIPE} = 'IGNORE';
109 my $oldAutoCommit = $FS::UID::AutoCommit;
110 local $FS::UID::AutoCommit = 0;
113 my $error = $self->SUPER::insert;
115 $dbh->rollback if $oldAutoCommit;
119 # add part_svc_column records
121 my $svcdb = $self->svcdb;
122 # my @rows = map { /^${svcdb}__(.*)$/; $1 }
124 # grep /^${svcdb}__/,
125 # fields('part_svc');
127 grep { $_ ne 'svcnum'
128 && defined( $self->getfield($svcdb.'__'.$_.'_flag') )
129 } (fields($svcdb), @fields)
131 my $part_svc_column = $self->part_svc_column($field);
132 my $previous = qsearchs('part_svc_column', {
133 'svcpart' => $self->svcpart,
134 'columnname' => $field,
137 my $flag = $self->getfield($svcdb.'__'.$field.'_flag');
138 if ( uc($flag) =~ /^([DF])$/ ) {
139 $part_svc_column->setfield('columnflag', $1);
140 $part_svc_column->setfield('columnvalue',
141 $self->getfield($svcdb.'__'.$field)
144 $error = $part_svc_column->replace($previous);
146 $error = $part_svc_column->insert;
149 $error = $previous ? $previous->delete : '';
152 $dbh->rollback if $oldAutoCommit;
158 # add export_svc records
160 foreach my $exportnum ( @exportnums ) {
161 my $export_svc = new FS::export_svc ( {
162 'exportnum' => $exportnum,
163 'svcpart' => $self->svcpart,
165 $error = $export_svc->insert;
167 $dbh->rollback if $oldAutoCommit;
172 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
179 Currently unimplemented. Set the "disabled" field instead.
184 return "Can't (yet?) delete service definitions.";
185 # check & make sure the svcpart isn't in cust_svc or pkg_svc (in any packages)?
188 =item replace OLD_RECORD [ '1.3-COMPAT' [ , EXTRA_FIELDS_ARRAYREF [ , EXPORTNUMS_HASHREF ] ] ]
190 Replaces OLD_RECORD with this one in the database. If there is an error,
191 returns the error, otherwise returns false.
195 TODOC: EXTRA_FIELDS_ARRAYREF (same as insert method)
200 my ( $new, $old ) = ( shift, shift );
202 return "Can't change svcdb for an existing service definition!"
203 unless $old->svcdb eq $new->svcdb;
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';
212 my $oldAutoCommit = $FS::UID::AutoCommit;
213 local $FS::UID::AutoCommit = 0;
216 my $error = $new->SUPER::replace( $old );
218 $dbh->rollback if $oldAutoCommit;
222 if ( @_ && $_[0] eq '1.3-COMPAT' ) {
225 @fields = @{shift(@_)} if @_;
226 my $exportnums = @_ ? shift : '';
228 # maintain part_svc_column records
230 my $svcdb = $new->svcdb;
232 grep { $_ ne 'svcnum'
233 && defined( $new->getfield($svcdb.'__'.$_.'_flag') )
234 } (fields($svcdb),@fields)
236 my $part_svc_column = $new->part_svc_column($field);
237 my $previous = qsearchs('part_svc_column', {
238 'svcpart' => $new->svcpart,
239 'columnname' => $field,
242 my $flag = $new->getfield($svcdb.'__'.$field.'_flag');
243 if ( uc($flag) =~ /^([DF])$/ ) {
244 $part_svc_column->setfield('columnflag', $1);
245 $part_svc_column->setfield('columnvalue',
246 $new->getfield($svcdb.'__'.$field)
249 $error = $part_svc_column->replace($previous);
251 $error = $part_svc_column->insert;
254 $error = $previous ? $previous->delete : '';
257 $dbh->rollback if $oldAutoCommit;
262 # maintain export_svc records
266 #false laziness w/ edit/process/agent_type.cgi
267 foreach my $part_export ( qsearch('part_export', {}) ) {
268 my $exportnum = $part_export->exportnum;
270 'exportnum' => $exportnum,
271 'svcpart' => $new->svcpart,
273 my $export_svc = qsearchs('export_svc', $hashref);
275 if ( $export_svc && ! $exportnums->{$exportnum} ) {
276 $error = $export_svc->delete;
278 $dbh->rollback if $oldAutoCommit;
281 } elsif ( ! $export_svc && $exportnums->{$exportnum} ) {
282 $export_svc = new FS::export_svc ( $hashref );
283 $error = $export_svc->insert;
285 $dbh->rollback if $oldAutoCommit;
295 $dbh->rollback if $oldAutoCommit;
296 return 'non-1.3-COMPAT interface not yet written';
300 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
307 Checks all fields to make sure this is a valid service definition. If there is
308 an error, returns the error, otherwise returns false. Called by the insert
315 my $recref = $self->hashref;
319 $self->ut_numbern('svcpart')
320 || $self->ut_text('svc')
321 || $self->ut_alpha('svcdb')
322 || $self->ut_enum('disabled', [ '', 'Y' ] )
324 return $error if $error;
326 my @fields = eval { fields( $recref->{svcdb} ) }; #might die
327 return "Unknown svcdb!" unless @fields;
329 ##REPLACED BY part_svc_column
331 # foreach $svcdb ( qw(
332 # svc_acct svc_acct_sm svc_domain
334 # my @rows = map { /^${svcdb}__(.*)$/; $1 }
336 # grep /^${svcdb}__/,
337 # fields('part_svc');
338 # foreach my $row (@rows) {
339 # unless ( $svcdb eq $recref->{svcdb} ) {
340 # $recref->{$svcdb.'__'.$row}='';
341 # $recref->{$svcdb.'__'.$row.'_flag'}='';
344 # $recref->{$svcdb.'__'.$row.'_flag'} =~ /^([DF]?)$/
345 # or return "Illegal flag for $svcdb $row";
346 # $recref->{$svcdb.'__'.$row.'_flag'} = $1;
348 # my $error = $self->ut_anything($svcdb.'__'.$row);
349 # return $error if $error;
357 =item part_svc_column COLUMNNAME
359 Returns the part_svc_column object (see L<FS::part_svc_column>) for the given
360 COLUMNNAME, or a new part_svc_column object if none exists.
364 sub part_svc_column {
366 my $columnname = shift;
367 qsearchs('part_svc_column', {
368 'svcpart' => $self->svcpart,
369 'columnname' => $columnname,
371 ) or new FS::part_svc_column {
372 'svcpart' => $self->svcpart,
373 'columnname' => $columnname,
377 =item all_part_svc_column
381 sub all_part_svc_column {
383 qsearch('part_svc_column', { 'svcpart' => $self->svcpart } );
386 =item part_export [ EXPORTTYPE ]
388 Returns all exports (see L<FS::part_export>) for this service, or, if an
389 export type is specified, only returns exports of the given type.
396 $search{'exporttype'} = shift if @_;
397 map { qsearchs('part_export', { 'exportnum' => $_->exportnum, %search } ) }
398 qsearch('export_svc', { 'svcpart' => $self->svcpart } );
403 Returns a list of associated FS::cust_svc records.
409 qsearch('cust_svc', { 'svcpart' => $self->svcpart } );
414 Returns a list of associated FS::svc_* records.
420 map { $_->svc_x } $self->cust_svc;
427 Delete is unimplemented.
429 The list of svc_* tables is hardcoded. When svc_acct_pop is renamed, this
432 all_part_svc_column method should be documented
436 L<FS::Record>, L<FS::part_svc_column>, L<FS::part_pkg>, L<FS::pkg_svc>,
437 L<FS::cust_svc>, L<FS::svc_acct>, L<FS::svc_forward>, L<FS::svc_domain>,
438 schema.html from the base documentation.