Generic virtual field support
authorkhoff <khoff>
Mon, 23 Jun 2003 22:19:38 +0000 (22:19 +0000)
committerkhoff <khoff>
Mon, 23 Jun 2003 22:19:38 +0000 (22:19 +0000)
51 files changed:
FS/FS/Record.pm
FS/FS/addr_block.pm
FS/FS/agent.pm
FS/FS/agent_type.pm
FS/FS/cust_bill.pm
FS/FS/cust_bill_event.pm
FS/FS/cust_bill_pay.pm
FS/FS/cust_bill_pkg.pm
FS/FS/cust_bill_pkg_detail.pm
FS/FS/cust_credit.pm
FS/FS/cust_credit_bill.pm
FS/FS/cust_credit_refund.pm
FS/FS/cust_main.pm
FS/FS/cust_main_county.pm
FS/FS/cust_main_invoice.pm
FS/FS/cust_pay.pm
FS/FS/cust_pay_batch.pm
FS/FS/cust_pkg.pm
FS/FS/cust_refund.pm
FS/FS/cust_svc.pm
FS/FS/cust_tax_exempt.pm
FS/FS/domain_record.pm
FS/FS/export_svc.pm
FS/FS/msgcat.pm
FS/FS/nas.pm
FS/FS/part_bill_event.pm
FS/FS/part_export.pm
FS/FS/part_export_option.pm
FS/FS/part_pkg.pm
FS/FS/part_pop_local.pm
FS/FS/part_referral.pm
FS/FS/part_svc.pm
FS/FS/part_svc_column.pm
FS/FS/pkg_svc.pm
FS/FS/port.pm
FS/FS/prepay_credit.pm
FS/FS/queue.pm
FS/FS/queue_arg.pm
FS/FS/queue_depend.pm
FS/FS/radius_usergroup.pm
FS/FS/router.pm
FS/FS/session.pm
FS/FS/svc_Common.pm
FS/FS/svc_acct.pm
FS/FS/svc_acct_pop.pm
FS/FS/svc_broadband.pm
FS/FS/svc_domain.pm
FS/FS/svc_forward.pm
FS/FS/svc_www.pm
FS/FS/type_pkgs.pm
httemplate/edit/part_svc.cgi

index 9a724fe..8f31c20 100644 (file)
@@ -14,10 +14,14 @@ use FS::UID qw(dbh getotaker datasrc driver_name);
 use FS::SearchCache;
 use FS::Msgcat qw(gettext);
 
+use FS::part_virtual_field;
+
+use Tie::IxHash;
+
 @ISA = qw(Exporter);
 @EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch);
 
-$DEBUG = 0;
+$DEBUG = 2;
 $me = '[FS::Record]';
 
 #ask FS::UID to run this stuff for us later
@@ -200,12 +204,15 @@ sub qsearch {
   my $dbh = dbh;
 
   my $table = $cache ? $cache->table : $stable;
+  my $pkey = $dbdef->table($table)->primary_key;
 
-  my @fields = grep exists($record->{$_}), fields($table);
+  my @real_fields = grep exists($record->{$_}), real_fields($table);
+  my @virtual_fields = grep exists($record->{$_}), "FS::$table"->virtual_fields;
 
   my $statement = "SELECT $select FROM $stable";
-  if ( @fields ) {
-    $statement .= ' WHERE '. join(' AND ', map {
+  if ( @real_fields or @virtual_fields ) {
+    $statement .= ' WHERE '. join(' AND ',
+      ( map {
 
       my $op = '=';
       my $column = $_;
@@ -251,8 +258,45 @@ sub qsearch {
       } else {
         "$column $op ?";
       }
-    } @fields );
+    } @real_fields ), 
+    ( map {
+      my $op = '=';
+      my $column = $_;
+      if ( ref($record->{$_}) ) {
+        $op = $record->{$_}{'op'} if $record->{$_}{'op'};
+       if ( uc($op) eq 'ILIKE' ) {
+         $op = 'LIKE';
+         $record->{$_}{'value'} = lc($record->{$_}{'value'});
+         $column = "LOWER($_)";
+       }
+       $record->{$_} = $record->{$_}{'value'};
+      }
+
+      # ... EXISTS ( SELECT name, value FROM part_virtual_field
+      #              JOIN virtual_field
+      #              ON part_virtual_field.vfieldpart = virtual_field.vfieldpart
+      #              WHERE recnum = svc_acct.svcnum
+      #              AND (name, value) = ('egad', 'brain') )
+
+      my $value = $record->{$_};
+
+      my $subq;
+
+      $subq = ($value ? 'EXISTS ' : 'NOT EXISTS ') .
+      "( SELECT part_virtual_field.name, virtual_field.value ".
+      "FROM part_virtual_field JOIN virtual_field ".
+      "ON part_virtual_field.vfieldpart = virtual_field.vfieldpart ".
+      "WHERE virtual_field.recnum = ${table}.${pkey} ".
+      "AND part_virtual_field.name = '${column}'".
+      ($value ? 
+        " AND virtual_field.value ${op} '${value}'"
+      : "") . ")";
+      $subq;
+
+    } @virtual_fields ) );
+
   }
+
   $statement .= " $extra_sql" if defined($extra_sql);
 
   warn "[debug]$me $statement\n" if $DEBUG > 1;
@@ -262,7 +306,7 @@ sub qsearch {
   my $bind = 1;
 
   foreach my $field (
-    grep defined( $record->{$_} ) && $record->{$_} ne '', @fields
+    grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields
   ) {
     if ( $record->{$field} =~ /^\d+(\.\d+)?$/
          && $dbdef->table($table)->column($field)->type =~ /(int)/i
@@ -279,31 +323,62 @@ sub qsearch {
 
   $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
 
-  $dbh->commit or croak $dbh->errstr if $FS::UID::AutoCommit;
-
+  my %result;
+  tie %result, "Tie::IxHash";
+  @virtual_fields = "FS::$table"->virtual_fields;
+  if($pkey) {
+    %result = %{ $sth->fetchall_hashref( $pkey ) };
+  } else {
+    my @stuff = @{ $sth->fetchall_arrayref( {} ) };
+    @result{@stuff} = @stuff;
+  }
+  $sth->finish;
+  if ( keys(%result) and @virtual_fields ) {
+    $statement =
+      "SELECT virtual_field.recnum, part_virtual_field.name, ".
+             "virtual_field.value ".
+      "FROM part_virtual_field JOIN virtual_field USING (vfieldpart) ".
+      "WHERE part_virtual_field.dbtable = '$table' AND ".
+      "virtual_field.recnum IN (".
+      join(',', keys(%result)). ") AND part_virtual_field.name IN ('".
+      join(q!', '!, @virtual_fields) . "')";
+    warn "[debug]$me $statement\n" if $DEBUG > 1;
+    $sth = $dbh->prepare($statement) or croak "$dbh->errstr doing $statement";
+    $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
+
+    foreach (@{ $sth->fetchall_arrayref({}) }) {
+      my $recnum = $_->{recnum};
+      my $name = $_->{name};
+      my $value = $_->{value};
+      if (exists($result{$recnum})) {
+        $result{$recnum}->{$name} = $value;
+      }
+    }
+  }
+  
   if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
     if ( eval 'FS::'. $table. '->can(\'new\')' eq \&new ) {
       #derivied class didn't override new method, so this optimization is safe
       if ( $cache ) {
         map {
           new_or_cached( "FS::$table", { %{$_} }, $cache )
-        } @{$sth->fetchall_arrayref( {} )};
+        } values(%result);
       } else {
         map {
           new( "FS::$table", { %{$_} } )
-        } @{$sth->fetchall_arrayref( {} )};
+        } values(%result);
       }
     } else {
       warn "untested code (class FS::$table uses custom new method)";
       map {
         eval 'FS::'. $table. '->new( { %{$_} } )';
-      } @{$sth->fetchall_arrayref( {} )};
+      } values(%result);
     }
   } else {
     cluck "warning: FS::$table not loaded; returning FS::Record objects";
     map {
       FS::Record->new( $table, { %{$_} } );
-    } @{$sth->fetchall_arrayref( {} )};
+    } values(%result);
   }
 
 }
@@ -512,15 +587,15 @@ sub insert {
 
   my $table = $self->table;
   #false laziness w/delete
-  my @fields =
+  my @real_fields =
     grep defined($self->getfield($_)) && $self->getfield($_) ne "",
-    $self->fields
+    real_fields($table)
   ;
-  my @values = map { _quote( $self->getfield($_), $table, $_) } @fields;
+  my @values = map { _quote( $self->getfield($_), $table, $_) } @real_fields;
   #eslaf
 
   my $statement = "INSERT INTO $table ( ".
-      join( ', ', @fields ).
+      join( ', ', @real_fields ).
     ") VALUES (".
       join( ', ', @values ).
     ")"
@@ -537,9 +612,9 @@ sub insert {
 
   $sth->execute or return $sth->errstr;
 
+  my $insertid = '';
   if ( $db_seq ) { # get inserted id from the database, if applicable
     warn "[debug]$me retreiving sequence from database\n" if $DEBUG;
-    my $insertid = '';
     if ( driver_name eq 'Pg' ) {
 
       my $oid = $sth->{'pg_oid_status'};
@@ -581,6 +656,34 @@ sub insert {
     $self->setfield($primary_key, $insertid);
   }
 
+  my @virtual_fields = 
+      grep defined($self->getfield($_)) && $self->getfield($_) ne "",
+          $self->virtual_fields;
+  if (@virtual_fields) {
+    my %v_values = map { $_, $self->getfield($_) } @virtual_fields;
+
+    my $vfieldpart = vfieldpart_hashref($table);
+
+    my $v_statement = "INSERT INTO virtual_field(recnum, vfieldpart, value) ".
+                    "VALUES (?, ?, ?)";
+
+    my $v_sth = dbh->prepare($v_statement) or do {
+      dbh->rollback if $FS::UID::AutoCommit;
+      return dbh->errstr;
+    };
+
+    foreach (keys(%v_values)) {
+      $v_sth->execute($self->getfield($primary_key),
+                      $vfieldpart->{$_},
+                      $v_values{$_})
+      or do {
+        dbh->rollback if $FS::UID::AutoCommit;
+        return $v_sth->errstr;
+      };
+    }
+  }
+
+
   my $h_sth;
   if ( defined $dbdef->table('h_'. $table) ) {
     my $h_statement = $self->_h_statement('insert');
@@ -631,7 +734,7 @@ sub delete {
         : "$_ = ". _quote($self->getfield($_),$self->table,$_)
     } ( $self->dbdef_table->primary_key )
           ? ( $self->dbdef_table->primary_key)
-          : $self->fields
+          : real_fields($self->table)
   );
   warn "[debug]$me $statement\n" if $DEBUG > 1;
   my $sth = dbh->prepare($statement) or return dbh->errstr;
@@ -645,6 +748,19 @@ sub delete {
     $h_sth = '';
   }
 
+  my $primary_key = $self->dbdef_table->primary_key;
+  my $v_sth;
+  my @del_vfields;
+  my $vfp = vfieldpart_hashref($self->table);
+  foreach($self->virtual_fields) {
+    next if $self->getfield($_) eq '';
+    unless(@del_vfields) {
+      my $st = "DELETE FROM virtual_field WHERE recnum = ? AND vfieldpart = ?";
+      $v_sth = dbh->prepare($st) or return dbh->errstr;
+    }
+    push @del_vfields, $_;
+  }
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE'; 
@@ -655,6 +771,10 @@ sub delete {
   my $rc = $sth->execute or return $sth->errstr;
   #not portable #return "Record not found, statement:\n$statement" if $rc eq "0E0";
   $h_sth->execute or return $h_sth->errstr if $h_sth;
+  $v_sth->execute($self->getfield($primary_key), $vfp->{$_}) 
+    or return $v_sth->errstr 
+        foreach (@del_vfields);
+  
   dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
 
   #no need to needlessly destoy the data either (causes problems actually)
@@ -695,8 +815,11 @@ sub replace {
   my $error = $new->check;
   return $error if $error;
 
-  my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
-  unless ( @diff ) {
+  #my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
+  my %diff = map { ($new->getfield($_) ne $old->getfield($_))
+                   ? ($_, $new->getfield($_)) : () } $old->fields;
+                   
+  unless ( keys(%diff) ) {
     carp "[warning]$me $new -> replace $old: records identical";
     return '';
   }
@@ -704,7 +827,7 @@ sub replace {
   my $statement = "UPDATE ". $old->table. " SET ". join(', ',
     map {
       "$_ = ". _quote($new->getfield($_),$old->table,$_) 
-    } @diff
+    } real_fields($old->table)
   ). ' WHERE '.
     join(' AND ',
       map {
@@ -715,7 +838,7 @@ sub replace {
                 : "( $_ IS NULL OR $_ = \"\" )"
             )
           : "$_ = ". _quote($old->getfield($_),$old->table,$_)
-      } ( $primary_key ? ( $primary_key ) : $old->fields )
+      } ( $primary_key ? ( $primary_key ) : real_fields($old->table) )
     )
   ;
   warn "[debug]$me $statement\n" if $DEBUG > 1;
@@ -739,6 +862,44 @@ sub replace {
     $h_new_sth = '';
   }
 
+  # For virtual fields we have three cases with different SQL 
+  # statements: add, replace, delete
+  my $v_add_sth;
+  my $v_rep_sth;
+  my $v_del_sth;
+  my (@add_vfields, @rep_vfields, @del_vfields);
+  my $vfp = vfieldpart_hashref($old->table);
+  foreach(grep { exists($diff{$_}) } $new->virtual_fields) {
+    if($diff{$_} eq '') {
+      # Delete
+      unless(@del_vfields) {
+        my $st = "DELETE FROM virtual_field WHERE recnum = ? ".
+                 "AND vfieldpart = ?";
+        warn "[debug]$me $st\n" if $DEBUG > 2;
+        $v_del_sth = dbh->prepare($st) or return dbh->errstr;
+      }
+      push @del_vfields, $_;
+    } elsif($old->getfield($_) eq '') {
+      # Add
+      unless(@add_vfields) {
+        my $st = "INSERT INTO virtual_field (value, recnum, vfieldpart) ".
+                "VALUES (?, ?, ?)";
+        warn "[debug]$me $st\n" if $DEBUG > 2;
+        $v_add_sth = dbh->prepare($st) or return dbh->errstr;
+      }
+      push @add_vfields, $_;
+    } else {
+      # Replace
+      unless(@rep_vfields) {
+        my $st = "UPDATE virtual_field SET value = ? ".
+                 "WHERE recnum = ? AND vfieldpart = ?";
+        warn "[debug]$me $st\n" if $DEBUG > 2;
+        $v_rep_sth = dbh->prepare($st) or return dbh->errstr;
+      }
+      push @rep_vfields, $_;
+    }
+  }
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE'; 
@@ -750,6 +911,24 @@ sub replace {
   #not portable #return "Record not found (or records identical)." if $rc eq "0E0";
   $h_old_sth->execute or return $h_old_sth->errstr if $h_old_sth;
   $h_new_sth->execute or return $h_new_sth->errstr if $h_new_sth;
+
+  $v_del_sth->execute($old->getfield($primary_key),
+                      $vfp->{$_})
+        or return $v_del_sth->errstr
+      foreach(@del_vfields);
+
+  $v_add_sth->execute($new->getfield($_),
+                      $old->getfield($primary_key),
+                      $vfp->{$_})
+        or return $v_add_sth->errstr
+      foreach(@add_vfields);
+
+  $v_rep_sth->execute($new->getfield($_),
+                      $old->getfield($primary_key),
+                      $vfp->{$_})
+        or return $v_rep_sth->errstr
+      foreach(@rep_vfields);
+
   dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
 
   '';
@@ -769,12 +948,28 @@ sub rep {
 
 =item check
 
-Not yet implemented, croaks.  Derived classes should provide a check method.
+Checks virtual fields (using check_blocks).  Subclasses should still provide 
+a check method to validate real fields, foreign keys, etc., and call this 
+method via $self->SUPER::check.
+
+(FIXME: Should this method try to make sure that it I<is> being called from 
+a subclass's check method, to keep the current semantics as far as possible?)
 
 =cut
 
 sub check {
-  confess "FS::Record::check not implemented; supply one in subclass!";
+  #confess "FS::Record::check not implemented; supply one in subclass!";
+  my $self = shift;
+
+  foreach my $field ($self->virtual_fields) {
+    for ($self->getfield($field)) {
+      # See notes on check_block in FS::part_virtual_field.
+      eval $self->pvf($field)->check_block;
+      return $@ if $@;
+      $self->setfield($field, $_);
+    }
+  }
+  '';
 }
 
 sub _h_statement {
@@ -782,7 +977,7 @@ sub _h_statement {
 
   my @fields =
     grep defined($self->getfield($_)) && $self->getfield($_) ne "",
-    $self->fields
+    real_fields($self->table);
   ;
   my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
 
@@ -1163,36 +1358,89 @@ sub ut_foreign_keyn {
     : '';
 }
 
+
+=item virtual_fields [ TABLE ]
+
+Returns a list of virtual fields defined for the table.  This should not 
+be exported, and should only be called as an instance or class method.
+
+=cut
+
+sub virtual_fields {
+  my $something = shift;
+  my $table;
+  $table = $something->table or confess "virtual_fields called on non-table";
+
+  confess "Unknown table $table" unless $dbdef->table($table);
+
+  # This should be smart enough to cache results.
+
+  my $query = 'SELECT name from part_virtual_field ' .
+              "WHERE dbtable = '$table'";
+  my $dbh = dbh;
+  my $result = $dbh->selectcol_arrayref($query);
+  confess $dbh->errstr if $dbh->err;
+  return @$result;
+}
+
+
 =item fields [ TABLE ]
 
-This can be used as both a subroutine and a method call.  It returns a list
-of the columns in this record's table, or an explicitly specified table.
-(See L<DBIx::DBSchema::Table>).
+This is a wrapper for real_fields and virtual_fields.  Code that called
+fields before should probably continue to call fields.
 
 =cut
 
-# Usage: @fields = fields($table);
-#        @fields = $record->fields;
 sub fields {
   my $something = shift;
   my $table;
-  if ( ref($something) ) {
+  if($something->isa('FS::Record')) {
     $table = $something->table;
   } else {
     $table = $something;
+    $something = "FS::$table";
   }
-  #croak "Usage: \@fields = fields(\$table)\n   or: \@fields = \$record->fields" unless $table;
-  my($table_obj) = $dbdef->table($table);
-  confess "Unknown table $table" unless $table_obj;
-  $table_obj->columns;
+  return (real_fields($table), $something->virtual_fields());
 }
 
 =back
 
+=item pvf FIELD_NAME
+
+Returns the FS::part_virtual_field object corresponding to a field in the 
+record (specified by FIELD_NAME).
+
+=cut
+
+sub pvf {
+  my ($self, $name) = (shift, shift);
+
+  if(grep /^$name$/, $self->virtual_fields) {
+    return qsearchs('part_virtual_field', { dbtable => $self->table,
+                                            name    => $name } );
+  }
+  ''
+}
+
 =head1 SUBROUTINES
 
 =over 4
 
+=item real_fields [ TABLE ]
+
+Returns a list of the real columns in the specified table.  Called only by 
+fields() and other subroutines elsewhere in FS::Record.
+
+=cut
+
+sub real_fields {
+  my $table = shift;
+
+  my($table_obj) = $dbdef->table($table);
+  confess "Unknown table $table" unless $table_obj;
+  $table_obj->columns;
+}
+
 =item reload_dbdef([FILENAME])
 
 Load a database definition (see L<DBIx::DBSchema>), optionally from a
@@ -1251,6 +1499,28 @@ sub _quote {
   }
 }
 
+=item vfieldpart_hashref TABLE
+
+Returns a hashref of virtual field names and vfieldparts applicable to the given
+TABLE.
+
+=cut
+
+sub vfieldpart_hashref {
+  my ($table) = @_;
+
+  return () unless $table;
+  my $dbh = dbh;
+  my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ".
+                  "dbtable = '$table'";
+  my $sth = $dbh->prepare($statement);
+  $sth->execute or croak "Execution of '$statement' failed: ".$dbh->errstr;
+  return { map { $_->{name}, $_->{vfieldpart} } 
+    @{$sth->fetchall_arrayref({})} };
+
+}
+
+
 =item hfields TABLE
 
 This is deprecated.  Don't use it.
index c5ddca7..4b034ef 100755 (executable)
@@ -122,7 +122,7 @@ sub check {
     }
   }
 
-  '';
+  $self->SUPER::check;
 }
 
 
index f11a28d..ec0f9ac 100644 (file)
@@ -113,8 +113,7 @@ sub check {
   return "Unknown typenum!"
     unless $self->agent_type;
 
-  '';
-
+  $self->SUPER::check;
 }
 
 =item agent_type
@@ -145,7 +144,7 @@ sub pkgpart_hashref {
 
 =head1 VERSION
 
-$Id: agent.pm,v 1.3 2002-03-24 18:23:47 ivan Exp $
+$Id: agent.pm,v 1.3.6.1 2003-06-23 22:19:30 khoff Exp $
 
 =head1 BUGS
 
index 988533a..68f3e33 100644 (file)
@@ -102,7 +102,8 @@ sub check {
   my $self = shift;
 
   $self->ut_numbern('typenum')
-  or $self->ut_text('atype');
+  or $self->ut_text('atype')
+  or $self->SUPER::check;
 
 }
 
@@ -150,7 +151,7 @@ sub pkgpart {
 
 =head1 VERSION
 
-$Id: agent_type.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
+$Id: agent_type.pm,v 1.1.16.1 2003-06-23 22:19:30 khoff Exp $
 
 =head1 BUGS
 
index a22f44b..4793608 100644 (file)
@@ -161,7 +161,7 @@ sub check {
 
   $self->printed(0) if $self->printed eq '';
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item previous
index c977347..ddd6762 100644 (file)
@@ -122,7 +122,7 @@ sub check {
   return "Unknown eventpart ". $self->eventpart
     unless qsearchs( 'part_bill_event' ,{ 'eventpart' => $self->eventpart } );
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_bill_event
index 913704b..4a89360 100644 (file)
@@ -170,7 +170,7 @@ sub check {
 
   $self->_date(time) unless $self->_date;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item cust_pay 
@@ -199,7 +199,7 @@ sub cust_bill {
 
 =head1 VERSION
 
-$Id: cust_bill_pay.pm,v 1.12 2002-02-07 22:29:34 ivan Exp $
+$Id: cust_bill_pay.pm,v 1.12.8.1 2003-06-23 22:19:30 khoff Exp $
 
 =head1 BUGS
 
index a6615d0..6800707 100644 (file)
@@ -171,7 +171,7 @@ sub check {
   return "Unknown invnum"
     unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item cust_pkg
index 199de43..261aa80 100644 (file)
@@ -105,7 +105,8 @@ sub check {
     || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum')
     || $self->ut_foreign_key('invnum', 'cust_pkg', 'invnum')
     || $self->ut_text('detail')
-  ;
+    || $self->SUPER::check
+    ;
 
 }
 
index 284d59d..cb32085 100644 (file)
@@ -174,7 +174,7 @@ sub check {
 
   $self->otaker(getotaker);
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item cust_refund
@@ -242,7 +242,7 @@ sub credited {
 
 =head1 VERSION
 
-$Id: cust_credit.pm,v 1.16 2002-06-04 14:35:52 ivan Exp $
+$Id: cust_credit.pm,v 1.16.6.1 2003-06-23 22:19:31 khoff Exp $
 
 =head1 BUGS
 
index 6221541..1ef42ab 100644 (file)
@@ -127,7 +127,7 @@ sub check {
   return "Cannot apply more than remaining value of invoice"
     unless $self->amount <= $cust_bill->owed;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item sub cust_credit
@@ -145,7 +145,7 @@ sub cust_credit {
 
 =head1 VERSION
 
-$Id: cust_credit_bill.pm,v 1.7 2002-01-24 16:58:47 ivan Exp $
+$Id: cust_credit_bill.pm,v 1.7.8.1 2003-06-23 22:19:31 khoff Exp $
 
 =head1 BUGS
 
index cc3b32c..2ff826b 100644 (file)
@@ -156,7 +156,7 @@ sub check {
   return "unknown cust_credit.crednum: ". $self->crednum
     unless qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item cust_refund
@@ -185,7 +185,7 @@ sub cust_credit {
 
 =head1 VERSION
 
-$Id: cust_credit_refund.pm,v 1.9 2002-01-26 01:52:31 ivan Exp $
+$Id: cust_credit_refund.pm,v 1.9.8.1 2003-06-23 22:19:31 khoff Exp $
 
 =head1 BUGS
 
index b455400..aa2cafc 100644 (file)
@@ -798,7 +798,7 @@ sub check {
 
   #warn "AFTER: \n". $self->_dump;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item all_pkgs
index d8796e4..f631d8c 100644 (file)
@@ -113,7 +113,9 @@ sub check {
     || $self->ut_textn('taxclass') # ...
     || $self->ut_money('exempt_amount')
     || $self->ut_textn('taxname')
-  ;
+    || $self->SUPER::check
+    ;
+
 
 }
 
index bcb1437..e20005a 100644 (file)
@@ -107,7 +107,7 @@ sub check {
   return "Unknown customer"
     unless qsearchs('cust_main',{ 'custnum' => $self->custnum });
 
-  ''; #noerror
+  $self->SUPER::check;
 }
 
 =item checkdest
@@ -163,7 +163,7 @@ sub address {
 
 =head1 VERSION
 
-$Id: cust_main_invoice.pm,v 1.13 2002-09-18 22:50:44 ivan Exp $
+$Id: cust_main_invoice.pm,v 1.13.2.1 2003-06-23 22:19:31 khoff Exp $
 
 =head1 BUGS
 
index 55f2fc4..8a4111a 100644 (file)
@@ -354,8 +354,7 @@ sub check {
     return $error if $error;
   }
 
-  ''; #no error
-
+  $self->SUPER::check;
 }
 
 =item cust_bill_pay
@@ -390,7 +389,7 @@ sub unapplied {
 
 =head1 VERSION
 
-$Id: cust_pay.pm,v 1.24 2003-05-19 12:00:44 ivan Exp $
+$Id: cust_pay.pm,v 1.24.2.1 2003-06-23 22:19:32 khoff Exp $
 
 =head1 BUGS
 
index c4427c3..62a4783 100644 (file)
@@ -185,14 +185,14 @@ sub check {
 
   #check invnum, custnum, ?
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =back
 
 =head1 VERSION
 
-$Id: cust_pay_batch.pm,v 1.6 2002-02-22 23:08:11 ivan Exp $
+$Id: cust_pay_batch.pm,v 1.6.6.1 2003-06-23 22:19:32 khoff Exp $
 
 =head1 BUGS
 
index a423c55..f59b45a 100644 (file)
@@ -249,7 +249,7 @@ sub check {
     $self->manual_flag($1);
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item cancel
index 7636717..7c53c7b 100644 (file)
@@ -260,14 +260,14 @@ sub check {
 
   $self->otaker(getotaker);
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =back
 
 =head1 VERSION
 
-$Id: cust_refund.pm,v 1.20 2002-11-19 09:51:58 ivan Exp $
+$Id: cust_refund.pm,v 1.20.2.1 2003-06-23 22:19:32 khoff Exp $
 
 =head1 BUGS
 
index c0cb6f4..7aa311b 100644 (file)
@@ -234,7 +234,7 @@ sub check {
       if scalar(@cust_svc) >= $quantity && (!$ignore_quantity || !$quantity);
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_svc
index ab873c0..da0de00 100644 (file)
@@ -111,6 +111,7 @@ sub check {
     || $self->ut_number('year') #check better
     || $self->ut_number('month') #check better
     || $self->ut_money('amount')
+    || $self->SUPER::check
   ;
 }
 
index 77b9550..f2700a5 100644 (file)
@@ -271,7 +271,7 @@ sub check {
     die "ack!";
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item increment_serial
@@ -332,7 +332,7 @@ sub zone {
 
 =head1 VERSION
 
-$Id: domain_record.pm,v 1.15 2003-04-29 18:28:50 khoff Exp $
+$Id: domain_record.pm,v 1.15.2.1 2003-06-23 22:19:32 khoff Exp $
 
 =head1 BUGS
 
index da9ac69..c104e45 100644 (file)
@@ -105,6 +105,7 @@ sub check {
     || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
     || $self->ut_number('svcpart')
     || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
+    || $self->SUPER::check
   ;
 }
 
index fa10d34..855b8b2 100644 (file)
@@ -113,7 +113,7 @@ sub check {
   $self->locale =~ /^([\w\@]+)$/ or return "illegal locale: ". $self->locale;
   $self->locale($1);
 
-  ''; #no error
+  $self->SUPER::check
 }
 
 =back
index 58c6827..6b5b4f4 100644 (file)
@@ -114,7 +114,9 @@ sub check {
     || $self->ut_text('nas')
     || $self->ut_ip('nasip')
     || $self->ut_domain('nasfqdn')
-    || $self->ut_numbern('last');
+    || $self->ut_numbern('last')
+    || $self->SUPER::check
+    ;
 }
 
 =item heartbeat TIMESTAMP
@@ -136,7 +138,7 @@ sub heartbeat {
 
 =head1 VERSION
 
-$Id: nas.pm,v 1.6 2002-03-04 12:48:49 ivan Exp $
+$Id: nas.pm,v 1.6.6.1 2003-06-23 22:19:33 khoff Exp $
 
 =head1 BUGS
 
index e0e4f3f..9e5d821 100644 (file)
@@ -162,8 +162,7 @@ sub check {
     }
   }
 
-  '';
-
+  $self->SUPER::check;
 }
 
 =back
index f1a0b1a..2277f82 100644 (file)
@@ -285,7 +285,7 @@ sub check {
 
   #check exporttype?
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 #=item part_svc
@@ -667,6 +667,19 @@ END
   },
 ;
 
+tie my %router_options, 'Tie::IxHash',
+  'protocol' => {
+         label=>'Protocol',
+         type =>'select',
+         options => [qw(telnet ssh)],
+         default => 'telnet'},
+  'insert' => {label=>'Insert command', default=>'' },
+  'delete' => {label=>'Delete command', default=>'' },
+  'replace' => {label=>'Replace command', default=>'' },
+  'Timeout' => {label=>'Time to wait for prompt', default=>'20' },
+  'Prompt' => {label=>'Prompt string', default=>'#' }
+;
+
 tie my %domain_shellcommands_options, 'Tie::IxHash',
   'user' => { lable=>'Remote username', default=>'root' },
   'useradd' => { label=>'Insert command',
@@ -1000,8 +1013,12 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
   },
 
   'svc_broadband' => {
+    'router' => {
+      'desc' => 'Send a command to a router.',
+      'options' => \%router_options,
+      'notes' => '',
+    },
   },
-
 );
 
 =back
index a0b19fd..33b5e5a 100644 (file)
@@ -115,7 +115,7 @@ sub check {
 
   #check options & values?
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =back
index 60b0e01..599115d 100644 (file)
@@ -241,6 +241,7 @@ sub check {
       || $self->ut_enum('recurtax', [ '', 'Y' ] )
       || $self->ut_textn('taxclass')
       || $self->ut_enum('disabled', [ '', 'Y' ] )
+      || $self->SUPER::check
     ;
 }
 
index 0b7cdf6..5ef29d0 100644 (file)
@@ -92,6 +92,7 @@ sub check {
       or $self->ut_text('state')
       or $self->ut_number('npa')
       or $self->ut_number('nxx')
+      or $self->SUPER::check
   ;
 
 }
@@ -100,7 +101,7 @@ sub check {
 
 =head1 VERSION
 
-$Id: part_pop_local.pm,v 1.1 2001-09-26 09:17:06 ivan Exp $
+$Id: part_pop_local.pm,v 1.1.8.1 2003-06-23 22:19:34 khoff Exp $
 
 =head1 BUGS
 
index 23885df..f30ddad 100644 (file)
@@ -93,6 +93,7 @@ sub check {
 
   $self->ut_numbern('refnum')
     || $self->ut_text('referral')
+    || $self->SUPER::check
   ;
 }
 
index 63bc2ad..aacc3ab 100644 (file)
@@ -68,7 +68,7 @@ TODOC:
 
 =item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
 
-=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed
+=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed.  For virtual fields, can also be 'X' for excluded.
 
 TODOC: EXTRA_FIELDS_ARRAYREF
 
@@ -113,7 +113,7 @@ sub insert {
     } );
 
     my $flag = $self->getfield($svcdb.'__'.$field.'_flag');
-    if ( uc($flag) =~ /^([DF])$/ ) {
+    if ( uc($flag) =~ /^([DFX])$/ ) {
       $part_svc_column->setfield('columnflag', $1);
       $part_svc_column->setfield('columnvalue',
         $self->getfield($svcdb.'__'.$field)
@@ -201,7 +201,7 @@ sub replace {
       } );
 
       my $flag = $new->getfield($svcdb.'__'.$field.'_flag');
-      if ( uc($flag) =~ /^([DF])$/ ) {
+      if ( uc($flag) =~ /^([DFX])$/ ) {
         $part_svc_column->setfield('columnflag', $1);
         $part_svc_column->setfield('columnvalue',
           $new->getfield($svcdb.'__'.$field)
@@ -254,7 +254,7 @@ sub check {
   my @fields = eval { fields( $recref->{svcdb} ) }; #might die
   return "Unknown svcdb!" unless @fields;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_svc_column COLUMNNAME
index 37e841e..90bdf28 100644 (file)
@@ -41,7 +41,7 @@ fields are currently supported:
 
 =item columnvalue - default or fixed value for the column
 
-=item columnflag - null, D or F
+=item columnflag - null, D, F, X (virtual fields)
 
 =back
 
@@ -91,18 +91,18 @@ sub check {
   ;
   return $error if $error;
 
-  $self->columnflag =~ /^([DF])$/
+  $self->columnflag =~ /^([DFX])$/
     or return "illegal columnflag ". $self->columnflag;
   $self->columnflag(uc($1));
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =back
 
 =head1 VERSION
 
-$Id: part_svc_column.pm,v 1.1 2001-09-07 20:49:15 ivan Exp $
+$Id: part_svc_column.pm,v 1.1.8.1 2003-06-23 22:19:35 khoff Exp $
 
 =head1 BUGS
 
index 3c544ff..6e6ac15 100644 (file)
@@ -108,7 +108,7 @@ sub check {
   return "Unknown pkgpart!" unless $self->part_pkg;
   return "Unknown svcpart!" unless $self->part_svc;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_pkg
@@ -137,7 +137,7 @@ sub part_svc {
 
 =head1 VERSION
 
-$Id: pkg_svc.pm,v 1.3 2002-06-10 01:39:50 khoff Exp $
+$Id: pkg_svc.pm,v 1.3.4.1 2003-06-23 22:19:35 khoff Exp $
 
 =head1 BUGS
 
index 13455ca..f0d1ec4 100644 (file)
@@ -113,7 +113,7 @@ sub check {
     unless $self->ip || $self->nasport;
   return "Unknown nasnum"
     unless qsearchs('nas', { 'nasnum' => $self->nasnum } );
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item session
@@ -133,7 +133,7 @@ sub session {
 
 =head1 VERSION
 
-$Id: port.pm,v 1.5 2001-02-14 04:33:06 ivan Exp $
+$Id: port.pm,v 1.5.14.1 2003-06-23 22:19:35 khoff Exp $
 
 =head1 BUGS
 
index 7ed9b83..a9d26d1 100644 (file)
@@ -108,6 +108,7 @@ sub check {
   || $self->ut_alpha('identifier')
   || $self->ut_money('amount')
   || $self->utnumbern('seconds')
+  || $self->SUPER::check
   ;
 
 }
index d35dc88..90ffaac 100644 (file)
@@ -207,7 +207,7 @@ sub check {
   $self->status('new') unless $self->status;
   $self->_date(time) unless $self->_date;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item args
@@ -385,7 +385,7 @@ END
 
 =head1 VERSION
 
-$Id: queue.pm,v 1.15 2002-07-02 06:48:59 ivan Exp $
+$Id: queue.pm,v 1.15.6.1 2003-06-23 22:19:35 khoff Exp $
 
 =head1 BUGS
 
index 08fe473..430a8c8 100644 (file)
@@ -100,14 +100,14 @@ sub check {
   ;
   return $error if $error;
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =back
 
 =head1 VERSION
 
-$Id: queue_arg.pm,v 1.1 2001-09-11 00:08:18 ivan Exp $
+$Id: queue_arg.pm,v 1.1.8.1 2003-06-23 22:19:35 khoff Exp $
 
 =head1 BUGS
 
index 4a4e3c5..bc910d8 100644 (file)
@@ -103,6 +103,7 @@ sub check {
   $self->ut_numbern('dependnum')
     || $self->ut_foreign_key('jobnum',        'queue', 'jobnum')
     || $self->ut_foreign_key('depend_jobnum', 'queue', 'jobnum')
+    || $self->SUPER::check
   ;
 }
 
index 647621d..9bba057 100644 (file)
@@ -100,6 +100,7 @@ sub check {
     || $self->ut_number('svcnum')
     || $self->ut_foreign_key('svcnum','svc_acct','svcnum')
     || $self->ut_text('groupname')
+    || $self->SUPER::check
   ;
 }
 
index 3f9459a..2554ce8 100755 (executable)
@@ -85,7 +85,7 @@ sub check {
     || $self->ut_text('routername');
   return $error if $error;
 
-  '';
+  $self->SUPER::check;
 }
 
 =item addr_block
@@ -100,18 +100,6 @@ sub addr_block {
   return qsearch('addr_block', { routernum => $self->routernum });
 }
 
-=item router_field
-
-Returns a list of FS::router_field objects assigned to this object.
-
-=cut
-
-sub router_field {
-  my $self = shift;
-
-  return qsearch('router_field', { routernum => $self->routernum });
-}
-
 =item part_svc_router
 
 Returns a list of FS::part_svc_router objects associated with this 
@@ -147,7 +135,7 @@ $Id:
 
 =head1 SEE ALSO
 
-FS::svc_broadband, FS::router, FS::addr_block, FS::router_field, FS::part_svc,
+FS::svc_broadband, FS::router, FS::addr_block, FS::part_svc,
 schema.html from the base documentation.
 
 =cut
index de0f2a7..8bc1a81 100644 (file)
@@ -216,7 +216,7 @@ sub check {
   return $error if $error;
   return "Unknown svcnum"
     unless qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
-  '';
+  $self->SUPER::check;
 }
 
 =item nas_heartbeat
@@ -247,7 +247,7 @@ sub svc_acct {
 
 =head1 VERSION
 
-$Id: session.pm,v 1.7 2001-04-15 13:35:12 ivan Exp $
+$Id: session.pm,v 1.7.14.1 2003-06-23 22:19:36 khoff Exp $
 
 =head1 BUGS
 
index 87b6097..693778f 100644 (file)
@@ -2,7 +2,7 @@ package FS::svc_Common;
 
 use strict;
 use vars qw( @ISA $noexport_hack );
-use FS::Record qw( qsearchs fields dbh );
+use FS::Record qw( qsearch qsearchs fields dbh );
 use FS::cust_svc;
 use FS::part_svc;
 use FS::queue;
@@ -28,6 +28,58 @@ inherit from, i.e. FS::svc_acct.  FS::svc_Common inherits from FS::Record.
 
 =over 4
 
+=cut
+
+sub virtual_fields {
+
+  # This restricts the fields based on part_svc_column and the svcpart of 
+  # the service.  There are four possible cases:
+  # 1.  svcpart passed as part of the svc_x hash.
+  # 2.  svcpart fetched via cust_svc based on svcnum.
+  # 3.  No svcnum or svcpart.  In this case, return ALL the fields with 
+  #     dbtable eq $self->table.
+  # 4.  Called via "fields('svc_acct')" or something similar.  In this case
+  #     there is no $self object.
+
+  my $self = shift;
+  my $svcpart;
+  my @vfields = $self->SUPER::virtual_fields;
+
+  return @vfields unless (ref $self); # Case 4
+
+  if ($self->svcpart) { # Case 1
+    $svcpart = $self->svcpart;
+  } elsif (my $cust_svc = $self->cust_svc) { # Case 2
+    $svcpart = $cust_svc->svcpart;
+  } else { # Case 3
+    $svcpart = '';
+  }
+
+  if ($svcpart) { #Cases 1 and 2
+    my %flags = map { $_->columnname, $_->columnflag } (
+        qsearch ('part_svc_column', { svcpart => $svcpart } )
+      );
+    return grep { not ($flags{$_} eq 'X') } @vfields;
+  } else { # Case 3
+    return @vfields;
+  } 
+  return ();
+}
+
+=item check
+
+Checks the validity of fields in this record.
+
+At present, this does nothing but call FS::Record::check (which, in turn, 
+does nothing but run virtual field checks).
+
+=cut
+
+sub check {
+  my $self = shift;
+  $self->SUPER::check;
+}
+
 =item insert [ JOBNUM_ARRAYREF ]
 
 Adds this record to the database.  If there is an error, returns the error,
@@ -254,7 +306,7 @@ sub setx {
 
   #set default/fixed/whatever fields from part_svc
   my $table = $self->table;
-  foreach my $field ( grep { $_ ne 'svcnum' } fields($table) ) {
+  foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
     my $part_svc_column = $part_svc->part_svc_column($field);
     if ( $part_svc_column->columnflag eq $x ) {
       $self->setfield( $field, $part_svc_column->columnvalue );
@@ -364,7 +416,7 @@ sub cancel { ''; }
 
 =head1 VERSION
 
-$Id: svc_Common.pm,v 1.12 2002-06-14 11:22:53 ivan Exp $
+$Id: svc_Common.pm,v 1.12.6.1 2003-06-23 22:19:36 khoff Exp $
 
 =head1 BUGS
 
index 282ef5a..62bb3ef 100644 (file)
@@ -827,7 +827,7 @@ sub check {
            ": ". $recref->{_password};
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item radius
index 3c9ea01..2daed53 100644 (file)
@@ -93,6 +93,7 @@ sub check {
       or $self->ut_number('ac')
       or $self->ut_number('exch')
       or $self->ut_numbern('loc')
+      or $self->SUPER::check
   ;
 
 }
@@ -182,7 +183,7 @@ END
 
 =head1 VERSION
 
-$Id: svc_acct_pop.pm,v 1.7 2002-04-10 13:42:48 ivan Exp $
+$Id: svc_acct_pop.pm,v 1.7.6.1 2003-06-23 22:19:37 khoff Exp $
 
 =head1 BUGS
 
index 45f6c36..ec91532 100755 (executable)
@@ -116,50 +116,6 @@ returns the error, otherwise returns false.
 
 # Standard FS::svc_Common::replace
 
-=item sb_field
-
-Returns a list of FS::sb_field objects assigned to this object.
-
-=cut
-
-sub sb_field {
-  my $self = shift;
-
-  return qsearch( 'sb_field', { svcnum => $self->svcnum } );
-}
-
-=item sb_field_hashref
-
-Returns a hashref of the FS::sb_field key/value pairs for this object.
-
-Deprecated.  Please don't use it.
-
-=cut
-
-# Kristian wrote this, but don't hold it against him.  He was under a powerful
-# distracting influence whom he evidently found much more interesting than
-# svc_broadband.pm.  I can't say I blame him.
-
-sub sb_field_hashref {
-  my $self = shift;
-  my $svcpart = shift;
-
-  if ((not $svcpart) && ($self->cust_svc)) {
-    $svcpart = $self->cust_svc->svcpart;
-  }
-
-  my $hashref = {};
-
-  map {
-    my $sb_field = qsearchs('sb_field', { sbfieldpart => $_->sbfieldpart,
-                                          svcnum => $self->svcnum });
-    $hashref->{$_->getfield('name')} = $sb_field ? $sb_field->getfield('value') : '';
-  } qsearch('part_sb_field', { svcpart => $svcpart });
-
-  return $hashref;
-
-}
-
 =item suspend
 
 Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
@@ -223,8 +179,7 @@ sub check {
     return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
   }
 
-
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item NetAddr
@@ -267,19 +222,11 @@ sub allowed_routers {
 
 =head1 BUGS
 
-I think there's one place in the code where we actually use sb_field_hashref.
-That's a bug in itself.
-
-The real problem with it is that we're still grappling with the question of how
-tightly xfields should be integrated with real fields.  There are a few
-different directions we could go with it--we I<could> override several
-functions in Record so that xfields behave almost exactly like real fields (can
-be set with setfield(), appear in fields() and hash(), used as criteria in
-qsearch(), etc.).
+The business with sb_field has been 'fixed', in a manner of speaking.
 
 =head1 SEE ALSO
 
-FS::svc_Common, FS::Record, FS::addr_block, FS::sb_field,
+FS::svc_Common, FS::Record, FS::addr_block,
 FS::part_svc, schema.html from the base documentation.
 
 =cut
index 2e8866a..289b3d8 100644 (file)
@@ -342,7 +342,9 @@ sub check {
     return "Unknown catchall" unless $svc_acct;
   }
 
-  $self->ut_textn('purpose');
+  my $error = $self->ut_textn('purpose')
+           or $self->SUPER::check;
+  return $error if $error;
 
 }
 
index 2b1fb92..7a121b8 100644 (file)
@@ -241,7 +241,7 @@ sub check {
     $self->dst('');
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item srcsvc_acct
index d7a42c8..7e89083 100644 (file)
@@ -234,7 +234,8 @@ sub check {
   return "Unknown usersvc (svc_acct.svcnum): ". $self->usersvc
     unless qsearchs('svc_acct', { 'svcnum' => $self->usersvc } );
 
-  ''; #no error
+  $self->SUPER::check;
+
 }
 
 =item domain_record
index efba60d..a926bf6 100644 (file)
@@ -91,7 +91,7 @@ sub check {
   return "Unknown pkgpart"
     unless qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_pkg
@@ -111,7 +111,7 @@ sub part_pkg {
 
 =head1 VERSION
 
-$Id: type_pkgs.pm,v 1.2 2002-10-04 12:57:06 ivan Exp $
+$Id: type_pkgs.pm,v 1.2.2.1 2003-06-23 22:19:37 khoff Exp $
 
 =head1 BUGS
 
index d4bb470..d1a726f 100755 (executable)
@@ -109,29 +109,42 @@ my %defs = (
     'dstsvc'    => 'service to which mail is to be forwarded',
     'dst'       => 'someone@another.domain.com to use when dstsvc is 0',
   },
-  'svc_charge' => {
-    'amount'    => 'amount',
-  },
-  'svc_wo' => {
-    'worker'    => 'Worker',
-    '_date'      => 'Date',
-  },
+#  'svc_charge' => {
+#    'amount'    => 'amount',
+#  },
+#  'svc_wo' => {
+#    'worker'    => 'Worker',
+#    '_date'      => 'Date',
+#  },
   'svc_www' => {
     #'recnum' => '',
     #'usersvc' => '',
   },
   'svc_broadband' => {
-    'actypenum' => 'This is the actypenum that refers to the type of AC that can be provisioned for this service.  This field must be set fixed.',
     'speed_down' => 'Maximum download speed for this service in Kbps.  0 denotes unlimited.',
     'speed_up' => 'Maximum upload speed for this service in Kbps.  0 denotes unlimited.',
-    'acnum' => 'acnum of a specific AC that this service is restricted to.  Not required',
     'ip_addr' => 'IP address.  Leave blank for automatic assignment.',
-    'ip_netmask' => 'Mask length, aka. netmask bits.  (Eg. 255.255.255.0 == 24)',
-    'mac_addr' => 'MAC address which is used by some ACs for access control.  Specified by 6 colon seperated hex octets. (Eg. 00:00:0a:bc:1a:2b)',
-    'location' => 'Defines the physically location at which this service was installed.  This is not necessarily the billing address',
+    'blocknum' => 'Address block.',
   },
 );
 
+  foreach $svcdb (keys(%defs)) {
+    my $self = "FS::$svcdb"->new;
+    foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
+      my $pvf = $self->pvf($field);
+      my @list = $pvf->list;
+      if (scalar @list) {
+        $defs{$svcdb}->{$field} = { desc        => $pvf->label,
+                                    type        => 'select',
+                                    select_list => \@list };
+      } else {
+        warn "$field";
+        $defs{$svcdb}->{$field} = $pvf->label;
+      } #endif
+    } #next $field
+  } #next $svcdb
+    
+
   my @dbs = $hashref->{svcdb}
              ? ( $hashref->{svcdb} )
              : qw( svc_acct svc_domain svc_forward svc_www svc_broadband );
@@ -209,12 +222,20 @@ my %defs = (
           if ( $def->{type} eq 'select' ) {
             $html .= qq!<SELECT NAME="${layer}__${field}">!;
             $html .= '<OPTION> </OPTION>' unless $value;
-            foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
-              my $rvalue = $record->getfield($def->{select_key});
-              $html .= qq!<OPTION VALUE="$rvalue"!.
-                       ( $rvalue==$value ? ' SELECTED>' : '>' ).
-                       $record->getfield($def->{select_label}). '</OPTION>';
-            }
+            if ( $def->{select_table} ) {
+              foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
+                my $rvalue = $record->getfield($def->{select_key});
+                $html .= qq!<OPTION VALUE="$rvalue"!.
+                         ( $rvalue==$value ? ' SELECTED>' : '>' ).
+                         $record->getfield($def->{select_label}). '</OPTION>';
+              } #next $record
+            } else { # select_list
+              foreach my $item ( @{$def->{select_list}} ) {
+                $html .= qq!<OPTION VALUE="$item"!.
+                         ( $item eq $value ? ' SELECTED>' : '>' ).
+                         $item. '</OPTION>';
+              } #next $item
+            } #endif
             $html .= '</SELECT>';
           } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
             $html .= FS::svc_acct::radius_usergroup_selector(