RT#34289: Flag service fields as mandatory
[freeside.git] / FS / FS / part_svc.pm
index da794dd..abeb28b 100644 (file)
@@ -65,6 +65,10 @@ empty for full access, "readonly" for read-only, "hidden" to hide it entirely
 right to change the password field, rather than just "Edit password".  Only
 relevant to svc_acct for now.
 
+=item has_router - Allow the service to have an L<FS::router> connected 
+through it.  Probably only relevant to svc_broadband, svc_acct, and svc_dsl
+for now.
+
 =back
 
 =head1 METHODS
@@ -92,8 +96,12 @@ the part_svc_column table appropriately (see L<FS::part_svc_column>).
 
 =item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
 
+=item I<svcdb>__I<field>_label
+
 =item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null or empty (no default), `D' for default, `F' for fixed (unchangeable), , `S' for selectable choice, `M' for manual selection from inventory, or `A' for automatic selection from inventory.  For virtual fields, can also be 'X' for excluded.
 
+=item I<svcdb>__I<field>_required - I<field> should always have a true value
+
 =back
 
 If you want to add part_svc_column records for fields that do not exist as
@@ -110,12 +118,8 @@ TODOC: JOB
 sub insert {
   my $self = shift;
   my @fields = ();
-  my @exportnums = ();
   @fields = @{shift(@_)} if @_;
-  if ( @_ ) {
-    my $exportnums = shift;
-    @exportnums = grep $exportnums->{$_}, keys %$exportnums;
-  }
+  my $exportnums = shift || {};
   my $job = '';
   $job = shift if @_;
 
@@ -146,6 +150,7 @@ sub insert {
   foreach my $field (
     grep { $_ ne 'svcnum'
            && ( defined( $self->getfield($svcdb.'__'.$_.'_flag') )
+                || defined($self->getfield($svcdb.'__'.$_.'_required'))
                 || $self->getfield($svcdb.'__'.$_.'_label') !~ /^\s*$/ )
          } (fields($svcdb), @fields)
   ) {
@@ -157,6 +162,7 @@ sub insert {
 
     my $flag  = $self->getfield($svcdb.'__'.$field.'_flag');
     my $label = $self->getfield($svcdb.'__'.$field.'_label');
+    my $required = $self->getfield($svcdb.'__'.$field.'_required') ? 'Y' : '';
     if ( uc($flag) =~ /^([A-Z])$/ || $label !~ /^\s*$/ ) {
 
       if ( uc($flag) =~ /^([A-Z])$/ ) {
@@ -171,6 +177,8 @@ sub insert {
       $part_svc_column->setfield('columnlabel', $label)
         if $label !~ /^\s*$/;
 
+      $part_svc_column->setfield('required', $required);
+
       if ( $previous ) {
         $error = $part_svc_column->replace($previous);
       } else {
@@ -188,12 +196,14 @@ sub insert {
   }
 
   # add export_svc records
+  my @exportnums = grep $exportnums->{$_}, keys %$exportnums;
   my $slice = 100/scalar(@exportnums) if @exportnums;
   my $done = 0;
   foreach my $exportnum ( @exportnums ) {
     my $export_svc = new FS::export_svc ( {
       'exportnum' => $exportnum,
       'svcpart'   => $self->svcpart,
+      'role'      => $exportnums->{$exportnum},
     } );
     $error = $export_svc->insert($job, $slice*$done++, $slice);
     if ( $error ) {
@@ -278,6 +288,7 @@ sub replace {
     foreach my $field (
       grep { $_ ne 'svcnum'
              && ( defined( $new->getfield($svcdb.'__'.$_.'_flag') )
+                  || defined($new->getfield($svcdb.'__'.$_.'_required'))
                   || $new->getfield($svcdb.'__'.$_.'_label') !~ /^\s*$/ )
            } (fields($svcdb),@fields)
     ) {
@@ -290,6 +301,7 @@ sub replace {
 
       my $flag  = $new->getfield($svcdb.'__'.$field.'_flag');
       my $label = $new->getfield($svcdb.'__'.$field.'_label');
+      my $required = $new->getfield($svcdb.'__'.$field.'_required') ? 'Y' : '';
  
       if ( uc($flag) =~ /^([A-Z])$/ || $label !~ /^\s*$/ ) {
 
@@ -308,6 +320,8 @@ sub replace {
         $part_svc_column->setfield('columnlabel', $label)
           if $label !~ /^\s*$/;
 
+        $part_svc_column->setfield('required', $required);
+
         if ( $previous ) {
           $error = $part_svc_column->replace($previous);
         } else {
@@ -324,9 +338,10 @@ sub replace {
 
     # maintain export_svc records
 
-    if ( $exportnums ) {
+    if ( $exportnums ) { # hash of exportnum => role
 
       #false laziness w/ edit/process/agent_type.cgi
+      #and, more importantly, with m2m_Common
       my @new_export_svc = ();
       foreach my $part_export ( qsearch('part_export', {}) ) {
         my $exportnum = $part_export->exportnum;
@@ -336,13 +351,23 @@ sub replace {
         };
         my $export_svc = qsearchs('export_svc', $hashref);
 
-        if ( $export_svc && ! $exportnums->{$exportnum} ) {
-          $error = $export_svc->delete;
-          if ( $error ) {
-            $dbh->rollback if $oldAutoCommit;
-            return $error;
+        if ( $export_svc ) {
+          my $old_role = $export_svc->role || 1; # 1 = null in the db
+          if ( ! $exportnums->{$exportnum}
+               or $old_role ne $exportnums->{$exportnum} ) {
+
+            $error = $export_svc->delete;
+            if ( $error ) {
+              $dbh->rollback if $oldAutoCommit;
+              return $error;
+            }
+            undef $export_svc; # on a role change, force it to be reinserted
+
           }
-        } elsif ( ! $export_svc && $exportnums->{$exportnum} ) {
+        } # if $export_svc
+        if ( ! $export_svc && $exportnums->{$exportnum} ) {
+          # also applies if it's been undef'd because of role change
+          $hashref->{role} = $exportnums->{$exportnum};
           push @new_export_svc, new FS::export_svc ( $hashref );
         }
 
@@ -394,11 +419,12 @@ sub check {
     $self->ut_numbern('svcpart')
     || $self->ut_text('svc')
     || $self->ut_alpha('svcdb')
-    || $self->ut_enum('disabled', [ '', 'Y' ] )
-    || $self->ut_enum('preserve', [ '', 'Y' ] )
+    || $self->ut_flag('disabled')
+    || $self->ut_flag('preserve')
     || $self->ut_enum('selfservice_access', [ '', 'hidden', 'readonly' ] )
     || $self->ut_foreign_keyn('classnum', 'part_svc_class', 'classnum' )
-    || $self->ut_enum('restrict_edit_password', [ '', 'Y' ] )
+    || $self->ut_flag('restrict_edit_password')
+    || $self->ut_flag('has_router')
 ;
   return $error if $error;
 
@@ -684,6 +710,10 @@ some components specified by "select-.*.html", and a bunch more...
 
 =item select_label - Used with select_table, this is the field name of labels
 
+=item select_allow_empty - Used with select_table, adds an empty option
+
+=item required - This field should always have a true value (do not use with type checkbox or disabled)
+
 =back
 
 =cut
@@ -761,7 +791,7 @@ sub process {
                          and ref($param->{ $f }) ) {
                       $param->{ $f } = join(',', @{ $param->{ $f } });
                    }
-                    ( $f, $f.'_flag', $f.'_label' );
+                    ( $f, $f.'_flag', $f.'_label', $f.'_required' );
                   }
                   @fields;
 
@@ -772,7 +802,13 @@ sub process {
   my %exportnums =
     map { $_->exportnum => ( $param->{'exportnum'.$_->exportnum} || '') }
         qsearch('part_export', {} );
-
+  foreach my $exportnum (%exportnums) {
+    my $role = $param->{'exportnum'.$exportnum.'_role'};
+    # role is undef if the export has no role selector
+    if ( $exportnums{$exportnum} && $role ) {
+      $exportnums{$exportnum} = $role;
+    }
+  }
   my $error;
   if ( $param->{'svcpart'} ) {
     $error = $new->replace( $old,