[freeside-commits] branch FREESIDE_3_BRANCH updated. 9e6a4720dba6074864ad6464f663e161fd179052

Jonathan Prykop jonathan at 420.am
Thu May 21 17:06:53 PDT 2015


The branch, FREESIDE_3_BRANCH has been updated
       via  9e6a4720dba6074864ad6464f663e161fd179052 (commit)
       via  f498bc8ce9377bfac97dbf3edbf1934318b105c3 (commit)
       via  fb7f3203c7d839ae3964d5e2cac71892422a10d1 (commit)
       via  ec271a1445bf232cd172c38e2dd3fd9d3c5c7c4e (commit)
      from  20a10242931eaf6ee17d3cdd0857a99449f9cfbe (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 9e6a4720dba6074864ad6464f663e161fd179052
Author: jonathan <jonathan at freeside.biz>
Date:   Thu May 21 23:43:19 2015 +0000

    RT#34289: Flag service fields as mandatory [fieldname bug fix]

diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm
index beea3f1..8a5fc35 100644
--- a/FS/FS/part_svc.pm
+++ b/FS/FS/part_svc.pm
@@ -146,9 +146,9 @@ sub insert {
   foreach my $field (fields($svcdb), @fields) {
     next if $field eq 'svcnum';
     my $prefix = $svcdb.'__';
-    if ( defined( $self->getfield($prefix.$_.'_flag'))
-      or defined($self->getfield($prefix.$_.'_required'))
-      or length($self->getfield($prefix.$_.'_label'))
+    if ( defined( $self->getfield($prefix.$field.'_flag'))
+      or defined($self->getfield($prefix.$field.'_required'))
+      or length($self->getfield($prefix.$field.'_label'))
     ) {
       my $part_svc_column = $self->part_svc_column($field);
       my $previous = qsearchs('part_svc_column', {
@@ -285,9 +285,9 @@ sub replace {
     foreach my $field (fields($svcdb), at fields) {
       next if $field eq 'svcnum';
       my $prefix = $svcdb.'__';
-      if ( defined( $new->getfield($prefix.$_.'_flag'))
-        or defined($new->getfield($prefix.$_.'_required'))
-        or length($new->getfield($prefix.$_.'_label'))
+      if ( defined( $new->getfield($prefix.$field.'_flag'))
+        or defined($new->getfield($prefix.$field.'_required'))
+        or length($new->getfield($prefix.$field.'_label'))
       ) {
         my $part_svc_column = $new->part_svc_column($field);
         my $previous = qsearchs('part_svc_column', {

commit f498bc8ce9377bfac97dbf3edbf1934318b105c3
Author: jonathan <jonathan at freeside.biz>
Date:   Thu May 21 22:57:03 2015 +0000

    RT#34289: Flag service fields as mandatory [error msg tweak]

diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index 9853d5b..d70209a 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -192,7 +192,7 @@ sub check {
     foreach my $field (keys %$required) {
       unless (length($self->get($field)) > 0) {
         my $name = $labels->{$field} || $field;
-        return "Field $name is required\n"
+        return "$name is required\n"
       }
     }
   }

commit fb7f3203c7d839ae3964d5e2cac71892422a10d1
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue May 5 15:49:51 2015 -0500

    RT#34289: Flag service fields as mandatory

diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm
index abeb28b..beea3f1 100644
--- a/FS/FS/part_svc.pm
+++ b/FS/FS/part_svc.pm
@@ -143,56 +143,53 @@ sub insert {
   # add part_svc_column records
 
   my $svcdb = $self->svcdb;
-#  my @rows = map { /^${svcdb}__(.*)$/; $1 }
-#    grep ! /_flag$/,
-#      grep /^${svcdb}__/,
-#        fields('part_svc');
-  foreach my $field (
-    grep { $_ ne 'svcnum'
-           && ( defined( $self->getfield($svcdb.'__'.$_.'_flag') )
-                || defined($self->getfield($svcdb.'__'.$_.'_required'))
-                || $self->getfield($svcdb.'__'.$_.'_label') !~ /^\s*$/ )
-         } (fields($svcdb), @fields)
-  ) {
-    my $part_svc_column = $self->part_svc_column($field);
-    my $previous = qsearchs('part_svc_column', {
-      'svcpart'    => $self->svcpart,
-      'columnname' => $field,
-    } );
+  foreach my $field (fields($svcdb), @fields) {
+    next if $field eq 'svcnum';
+    my $prefix = $svcdb.'__';
+    if ( defined( $self->getfield($prefix.$_.'_flag'))
+      or defined($self->getfield($prefix.$_.'_required'))
+      or length($self->getfield($prefix.$_.'_label'))
+    ) {
+      my $part_svc_column = $self->part_svc_column($field);
+      my $previous = qsearchs('part_svc_column', {
+        'svcpart'    => $self->svcpart,
+        'columnname' => $field,
+      } );
 
-    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])$/ ) {
-        my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
-                     || sub { shift };
-        $part_svc_column->setfield('columnflag', $1);
-        $part_svc_column->setfield('columnvalue',
-          &$parser($self->getfield($svcdb.'__'.$field))
-        );
-      }
+      my $flag  = $self->getfield($prefix.$field.'_flag');
+      my $label = $self->getfield($prefix.$field.'_label');
+      my $required = $self->getfield($prefix.$field.'_required') ? 'Y' : '';
+      if ( uc($flag) =~ /^([A-Z])$/ || $label !~ /^\s*$/ ) {
 
-      $part_svc_column->setfield('columnlabel', $label)
-        if $label !~ /^\s*$/;
+        if ( uc($flag) =~ /^([A-Z])$/ ) {
+          my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
+                       || sub { shift };
+          $part_svc_column->setfield('columnflag', $1);
+          $part_svc_column->setfield('columnvalue',
+            &$parser($self->getfield($prefix.$field))
+          );
+        }
+
+        $part_svc_column->setfield('columnlabel', $label)
+          if $label !~ /^\s*$/;
 
-      $part_svc_column->setfield('required', $required);
+        $part_svc_column->setfield('required', $required);
+
+        if ( $previous ) {
+          $error = $part_svc_column->replace($previous);
+        } else {
+          $error = $part_svc_column->insert;
+        }
 
-      if ( $previous ) {
-        $error = $part_svc_column->replace($previous);
       } else {
-        $error = $part_svc_column->insert;
+        $error = $previous ? $previous->delete : '';
+      }
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
       }
 
-    } else {
-      $error = $previous ? $previous->delete : '';
-    }
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
     }
-
   }
 
   # add export_svc records
@@ -285,54 +282,54 @@ sub replace {
    # maintain part_svc_column records
 
     my $svcdb = $new->svcdb;
-    foreach my $field (
-      grep { $_ ne 'svcnum'
-             && ( defined( $new->getfield($svcdb.'__'.$_.'_flag') )
-                  || defined($new->getfield($svcdb.'__'.$_.'_required'))
-                  || $new->getfield($svcdb.'__'.$_.'_label') !~ /^\s*$/ )
-           } (fields($svcdb), at fields)
-    ) {
-
-      my $part_svc_column = $new->part_svc_column($field);
-      my $previous = qsearchs('part_svc_column', {
-        'svcpart'    => $new->svcpart,
-        'columnname' => $field,
-      } );
-
-      my $flag  = $new->getfield($svcdb.'__'.$field.'_flag');
-      my $label = $new->getfield($svcdb.'__'.$field.'_label');
-      my $required = $new->getfield($svcdb.'__'.$field.'_required') ? 'Y' : '';
+    foreach my $field (fields($svcdb), at fields) {
+      next if $field eq 'svcnum';
+      my $prefix = $svcdb.'__';
+      if ( defined( $new->getfield($prefix.$_.'_flag'))
+        or defined($new->getfield($prefix.$_.'_required'))
+        or length($new->getfield($prefix.$_.'_label'))
+      ) {
+        my $part_svc_column = $new->part_svc_column($field);
+        my $previous = qsearchs('part_svc_column', {
+          'svcpart'    => $new->svcpart,
+          'columnname' => $field,
+        } );
+
+        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*$/ ) {
-
-        if ( uc($flag) =~ /^([A-Z])$/ ) {
-          $part_svc_column->setfield('columnflag', $1);
-          my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
-                     || sub { shift };
-          $part_svc_column->setfield('columnvalue',
-            &$parser($new->getfield($svcdb.'__'.$field))
-          );
-        } else {
-          $part_svc_column->setfield('columnflag',  '');
-          $part_svc_column->setfield('columnvalue', '');
-        }
+        if ( uc($flag) =~ /^([A-Z])$/ || $label !~ /^\s*$/ ) {
+
+          if ( uc($flag) =~ /^([A-Z])$/ ) {
+            $part_svc_column->setfield('columnflag', $1);
+            my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
+                       || sub { shift };
+            $part_svc_column->setfield('columnvalue',
+              &$parser($new->getfield($svcdb.'__'.$field))
+            );
+          } else {
+            $part_svc_column->setfield('columnflag',  '');
+            $part_svc_column->setfield('columnvalue', '');
+          }
 
-        $part_svc_column->setfield('columnlabel', $label)
-          if $label !~ /^\s*$/;
+          $part_svc_column->setfield('columnlabel', $label)
+            if $label !~ /^\s*$/;
 
-        $part_svc_column->setfield('required', $required);
+          $part_svc_column->setfield('required', $required);
 
-        if ( $previous ) {
-          $error = $part_svc_column->replace($previous);
+          if ( $previous ) {
+            $error = $part_svc_column->replace($previous);
+          } else {
+            $error = $part_svc_column->insert;
+          }
         } else {
-          $error = $part_svc_column->insert;
+          $error = $previous ? $previous->delete : '';
+        }
+        if ( $error ) {
+          $dbh->rollback if $oldAutoCommit;
+          return $error;
         }
-      } else {
-        $error = $previous ? $previous->delete : '';
-      }
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return $error;
       }
     }
 
@@ -606,6 +603,7 @@ sub svc_x {
 =cut
 
 my $svc_defs;
+my $svc_info;
 sub _svc_defs {
 
   return $svc_defs if $svc_defs; #cache
@@ -660,7 +658,14 @@ sub _svc_defs {
     sort { $info{$a}->{'display_weight'} <=> $info{$b}->{'display_weight'} }
     keys %info,
   ;
-  
+
+  tie my %svc_info, 'Tie::IxHash',
+    map  { $_ => $info{$_} }
+    sort { $info{$a}->{'display_weight'} <=> $info{$b}->{'display_weight'} }
+    keys %info,
+  ;
+    
+  $svc_info = \%svc_info; #access via svc_table_info  
   $svc_defs = \%svc_defs; #cache
   
 }
@@ -736,6 +741,27 @@ sub svc_table_fields {
   $def;
 }
 
+=item svc_table_info TABLE
+
+Returns table_info for TABLE from cache, or empty
+hashref if none is found.
+
+Caution:  caches table_info for ALL services when run;
+access a service's table_info directly unless you know
+you're loading them all.
+
+Caution:  does not standardize fields into hashrefs;
+use L</svc_table_fields> to access fields.
+
+=cut
+
+sub svc_table_info {
+  my $class = shift;
+  my $table = shift;
+  $class->_svc_defs; #creates cache if needed
+  return $svc_info->{$table} || {};
+}
+
 =back
 
 =head1 SUBROUTINES
diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index 34ae219..9853d5b 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -170,28 +170,30 @@ sub check {
   my $required = {};
   my $labels = {};
   my $tinfo = $self->can('table_info') ? $self->table_info : {};
-  my $fields = $tinfo->{'fields'} || {};
-  foreach my $field (keys %$fields) {
-    if (ref($fields->{$field}) && $fields->{$field}->{'required'}) {
-      $required->{$field} = 1;
-      $labels->{$field} = $fields->{$field}->{'label'};
+  if ($tinfo->{'manual_require'}) {
+    my $fields = $tinfo->{'fields'} || {};
+    foreach my $field (keys %$fields) {
+      if (ref($fields->{$field}) && $fields->{$field}->{'required'}) {
+        $required->{$field} = 1;
+        $labels->{$field} = $fields->{$field}->{'label'};
+      }
     }
-  }
-  # add fields marked as required in database
-  foreach my $column (
-    qsearch('part_svc_column',{
-      'svcpart' => $self->svcpart,
-      'required' => 'Y'
-    })
-  ) {
-    $required->{$column->columnname} = 1;
-    $labels->{$column->columnname} = $column->columnlabel;
-  }
-  # do the actual checking
-  foreach my $field (keys %$required) {
-    unless ($self->$field) {
-      my $name = $labels->{$field} || $field;
-      return "Field $name is required\n"
+    # add fields marked as required in database
+    foreach my $column (
+      qsearch('part_svc_column',{
+        'svcpart' => $self->svcpart,
+        'required' => 'Y'
+      })
+    ) {
+      $required->{$column->columnname} = 1;
+      $labels->{$column->columnname} = $column->columnlabel;
+    }
+    # do the actual checking
+    foreach my $field (keys %$required) {
+      unless (length($self->get($field)) > 0) {
+        my $name = $labels->{$field} || $field;
+        return "Field $name is required\n"
+      }
     }
   }
 
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
index ffcadc9..9636b3e 100644
--- a/FS/FS/svc_acct.pm
+++ b/FS/FS/svc_acct.pm
@@ -261,6 +261,7 @@ sub table_info {
     'display_weight' => 10,
     'cancel_weight'  => 50, 
     'ip_field' => 'slipip',
+    'manual_require' => 1,
     'fields' => {
         'dir'       => 'Home directory',
         'uid'       => {
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
index 40f2c9b..2cdc284 100755
--- a/FS/FS/svc_broadband.pm
+++ b/FS/FS/svc_broadband.pm
@@ -103,6 +103,7 @@ sub table_info {
     'display_weight' => 50,
     'cancel_weight'  => 70,
     'ip_field' => 'ip_addr',
+    'manual_require' => 1,
     'fields' => {
       'svcnum'      => 'Service',
       'description' => 'Descriptive label',
diff --git a/FS/FS/svc_dish.pm b/FS/FS/svc_dish.pm
index 5c9e217..2d249d1 100644
--- a/FS/FS/svc_dish.pm
+++ b/FS/FS/svc_dish.pm
@@ -63,9 +63,10 @@ sub table_info {
     'name'           => 'Dish service',
     'display_weight' => 58,
     'cancel_weight'  => 85,
+    'manual_require' => 1,
     'fields' => {
       'svcnum'    =>  { label => 'Service' },
-      'acctnum'   =>  { label => 'DISH account#', %opts },
+      'acctnum'   =>  { label => 'DISH account#', required => 1, %opts },
       'installdate' => { label => 'Install date', %opts },
       'note'      => { label => 'Installation notes', %opts },
     }
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
index 78556cf..b01d673 100644
--- a/FS/FS/svc_domain.pm
+++ b/FS/FS/svc_domain.pm
@@ -134,10 +134,7 @@ sub table_info {
     'display_weight' => 20,
     'cancel_weight'  => 60,
     'fields' => {
-      'domain' => {
-                  label => 'Domain',
-                  required => 1,
-                },
+      'domain' => 'Domain',
       'parent_svcnum' => { 
                          label => 'Parent domain / Communigate administrator domain',
                          type  => 'select',
diff --git a/FS/FS/svc_hardware.pm b/FS/FS/svc_hardware.pm
index d9c3c46..2be8954 100644
--- a/FS/FS/svc_hardware.pm
+++ b/FS/FS/svc_hardware.pm
@@ -76,6 +76,7 @@ sub table_info {
     'name_plural'    => 'Hardware',
     'display_weight' => 59,
     'cancel_weight'  => 86,
+    'manual_require' => 1,
     'fields' => {
       'svcnum'    => { label => 'Service' },
       'typenum'   => { label => 'Device type',
@@ -84,6 +85,7 @@ sub table_info {
                        disable_fixed     => 1,
                        disable_default   => 1,
                        disable_inventory => 1,
+                       required => 1,
                      },
       'serial'    => { label => 'Serial number', %opts },
       'hw_addr'   => { label => 'Hardware address', %opts },
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index c80cdae..c9eceb4 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -395,6 +395,7 @@ Example:
 %			'value' => $opt{curr_value},
 %			'label' => $label,
 %			'noinit' => $f->{noinit},
+%           'required' => $f->{'required'},
 %		}
 %	);
 %     }
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
index a6ccaf8..d950032 100644
--- a/httemplate/edit/elements/part_svc_column.html
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -77,7 +77,7 @@ that field.
     <TH BGCOLOR="#cccccc">Field</TH>
     <TH BGCOLOR="#cccccc">Label</TH>
     <TH BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>
-    <TH BGCOLOR="#cccccc">Required?</TH>
+    <TH BGCOLOR="#cccccc"><% $manual_require ? 'Required?' : '' %></TH>
   </TR>
 % $part_svc->set('svcpart' => $opt{'clone'}) if $opt{'clone'}; # for now
 % my $i = 0;
@@ -212,7 +212,9 @@ that field.
 %   }
     </TD>
     <TD>
-%   if (!$def->{'type'} || !(grep {$_ eq $def->{'type'}} ('checkbox','disabled'))) {
+%   if ($manual_require && 
+%       (!$def->{'type'} || !(grep {$_ eq $def->{'type'}} ('checkbox','disabled')))
+%   ) {
       <INPUT ID="<% $name.'_required' %>" TYPE="checkbox" NAME="<% $svcdb %>__<% $field %>_required" VALUE="Y" 
         <% ($part_svc_column->required || $def->{'required'}) ? 'CHECKED' : '' %> 
         <% $def->{'required'} ? 'DISABLED' : '' %>
@@ -310,4 +312,5 @@ if ( $svcdb eq 'svc_acct'
 }
 
 my @defs = map { FS::part_svc->svc_table_fields($svcdb)->{$_} } @fields;
+my $manual_require = FS::part_svc->svc_table_info($svcdb)->{'manual_require'};
 </%init>
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
index 97b630f..a4e345e 100644
--- a/httemplate/edit/elements/svc_Common.html
+++ b/httemplate/edit/elements/svc_Common.html
@@ -102,6 +102,9 @@
                    my $columndef = $part_svc->part_svc_column($f->{'field'});
                    my $flag = $columndef->columnflag;
 
+                   $f->{'required'} = 1
+                     if $columndef->required;
+
                    if ( $flag eq 'F' ) { #fixed
                      $f->{'value'} = $columndef->columnvalue;
                      if (length($columndef->columnvalue)) {
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi
index 90f4823..a5e7789 100755
--- a/httemplate/edit/svc_acct.cgi
+++ b/httemplate/edit/svc_acct.cgi
@@ -30,8 +30,9 @@
   <TD BGCOLOR="#eeeeee"><% $part_svc->svc %></TD>
 </TR>
 
-<TR>
-  <TD ALIGN="right"><% mt('Username') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Username'),
+     'required' => $part_svc->part_svc_column('username')->required ) %>
 % if ( $svcnum && $conf->exists('svc_acct-no_edit_username') ) {
     <TD BGCOLOR="#eeeeee"><% $svc_acct->username() %></TD>
     <INPUT TYPE="hidden" NAME="username" VALUE="<% $username %>">
@@ -43,9 +44,10 @@
 </TR>
 
 %if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) {
-<TR>
 % #XXX eventually should require "Edit Password" ACL
-  <TD ALIGN="right"><% mt('Password') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Password'),
+     'required' => $part_svc->part_svc_column('_password')->required ) %>
   <TD>
     <INPUT TYPE="text" ID="clear_password" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>>
     <& /elements/random_pass.html, 'clear_password' &>
@@ -60,8 +62,9 @@
 %if ( $conf->exists('security_phrase') 
 %  && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) {
 
-  <TR>
-    <TD ALIGN="right"><% mt('Security phrase') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Security phrase'),
+     'required' => $part_svc->part_svc_column('sec_phrase')->required ) %>
     <TD>
       <INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32>
       (<% mt('for forgotten passwords') |h %>)
@@ -97,8 +100,9 @@
 %                                                )
 %                );
 
-  <TR>
-    <TD ALIGN="right"><% mt('Domain') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Domain'),
+     'required' => $part_svc->part_svc_column('domsvc')->required ) %>
     <TD>
       <SELECT NAME="domsvc" SIZE=1>
 % foreach my $svcnum (
@@ -143,8 +147,9 @@
   <INPUT TYPE="hidden" NAME="popnum" VALUE="<% $popnum %>">
 % } else { 
 
-  <TR>
-    <TD ALIGN="right"><% mt('Access number') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Access number'),
+     'required' => $part_svc->part_svc_column('popnum')->required ) %>
     <TD><% FS::svc_acct_pop::popselector($popnum) %></TD>
   </TR>
 % } 
@@ -156,6 +161,7 @@
          'curr_value' => $svc_acct->sectornum,
          #'part_svc'   => $part_svc,
          #'cust_pkg'   => $cust_pkg,
+         'required'   => $part_svc->part_svc_column('sectornum')->required,
     &>
 %} else {
     <INPUT TYPE="hidden" NAME="sectornum" VALUE="<% $svc_acct->sectornum %>">
@@ -176,6 +182,9 @@
 %  
 % if ( length($svc_acct->$xid()) ) { 
 
+<% include('/elements/tr-td-label.html',
+     'label'    => uc($xid),
+     'required' => $part_svc->part_svc_column($xid)->required ) %>
       <TR>
         <TD ALIGN="right"><% uc($xid) %></TD>
           <TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD>
@@ -187,8 +196,9 @@
     <INPUT TYPE="hidden" NAME="<% $xid %>" VALUE="<% $svc_acct->$xid() %>">
 % } else { 
   
-    <TR>
-      <TD ALIGN="right"><% uc($xid) %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => uc($xid),
+     'required' => $part_svc->part_svc_column($xid)->required ) %>
       <TD>
         <INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>">
       </TD>
@@ -205,8 +215,9 @@
 % } else { 
 
 
-  <TR>
-    <TD ALIGN="right"><% mt('Real Name') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Real Name'),
+     'required' => $part_svc->part_svc_column('finger')->required ) %>
     <TD>
       <INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>">
     </TD>
@@ -223,8 +234,9 @@
 % } else {
 
 
-  <TR>
-    <TD ALIGN="right"><% mt('Home directory') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Home directory'),
+     'required' => $part_svc->part_svc_column('dir')->required ) %>
     <TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD>
   </TR>
 % } 
@@ -240,8 +252,9 @@
 % } else { 
 
 
-  <TR>
-    <TD ALIGN="right"><% mt('Shell') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('Shell'),
+     'required' => $part_svc->part_svc_column('shell')->required ) %>
     <TD>
       <SELECT NAME="shell" SIZE=1>
 %
@@ -269,7 +282,9 @@
 %   # (should we show this if slipip is fixed?)
 <& /elements/tr-select-router_block_ip.html, 
   'object' => $svc_acct,
-  'ip_field' => 'slipip'
+  'ip_field' => 'slipip',
+  'required' => $part_svc->part_svc_column('routernum')->required,
+  'ip_addr_required' => $part_svc->part_svc_column('slipip')->required,
 &>
 % } else {
 %   # don't expose these to the user--they're only useful in the other case
@@ -278,8 +293,9 @@
 %   if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) { 
     <INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>">
 %   } else { 
-    <TR>
-      <TD ALIGN="right"><% mt('IP') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('IP'),
+     'required' => $part_svc->part_svc_column('slipip')->required ) %>
       <TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD>
     </TR>
 %   }
@@ -324,8 +340,9 @@
 % } 
 
 
-<TR>
-  <TD ALIGN="right"><% mt('RADIUS groups') |h %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => mt('RADIUS groups'),
+     'required' => $part_svc->part_svc_column('usergroup')->required ) %>
 % if ( $part_svc_usergroup->columnflag eq 'F' ) { 
     <TD BGCOLOR="#eeeeee"><% join('<BR>', @groupnames) %></TD>
 % } else { 
diff --git a/httemplate/edit/svc_acct/communigate.html b/httemplate/edit/svc_acct/communigate.html
index 6370a54..370bfb0 100644
--- a/httemplate/edit/svc_acct/communigate.html
+++ b/httemplate/edit/svc_acct/communigate.html
@@ -47,8 +47,9 @@
   <INPUT TYPE="hidden" NAME="quota" VALUE="<% $svc_acct->quota %>">
 % } else {
 %   my $quota_label = $communigate ? 'Mail storage limit' : 'Quota';
-    <TR>
-      <TD ALIGN="right"><% $quota_label %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => $quota_label,
+     'required' => $part_svc->part_svc_column('quota')->required ) %>
       <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD>
     </TR>
 % }
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
index 1b85460..81c694a 100644
--- a/httemplate/edit/svc_broadband.cgi
+++ b/httemplate/edit/svc_broadband.cgi
@@ -102,7 +102,16 @@ END
 my @fields = (
   qw( description speed_down speed_up ),
   { field=>'sectornum', type=>'select-tower_sector', },
-  { field=>'routernum', type=>'select-router_block_ip' },
+  { field=>'routernum', type=>'select-router_block_ip', 
+    include_opt_callback => sub { 
+      my $svc_broadband = shift;
+      my $part_svc = $svc_broadband->part_svc;
+      return () unless $part_svc; #sanity check
+      my $col = $part_svc->part_svc_column('ip_addr');
+      return () unless $col; #sanity check
+      return ('ip_addr_required' => $col->required);
+    },
+  },
   { field=>'mac_addr' , type=>'input-mac_addr' },
   qw(
       latitude longitude altitude
diff --git a/httemplate/elements/tr-input-date-field.html b/httemplate/elements/tr-input-date-field.html
index ff98551..f2a570b 100644
--- a/httemplate/elements/tr-input-date-field.html
+++ b/httemplate/elements/tr-input-date-field.html
@@ -13,6 +13,7 @@ Example:
           'usedatetime' => 1, #use DateTime->strftime to format the date
                               # instead of Date::Format->time2str
           'noinit'      => 1,          #first one on the page is enough
+          'required'    => 1,
        },
   &>
 
@@ -24,8 +25,9 @@ Example:
 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
 % }
 
-<TR>
-  <TD ALIGN="right"><% $label %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => $label,
+     'required' => $required ) %>
   <TD COLSPAN=<% $colspan %>>
     <INPUT TYPE="text" NAME="<% $name %>" ID="<% $name %>_text" VALUE="<% $value %>">
     <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $name  %>_button" STYLE="cursor: pointer" TITLE="<% mt('Select date') |h %>">
@@ -48,7 +50,7 @@ Example:
 
 <%init>
 
-my($name, $value, $label, $format, $usedatetime, $noinit, $colspan);
+my($name, $value, $label, $format, $usedatetime, $noinit, $colspan, $required);
 if ( ref($_[0]) ) {
   my $opt = shift;
   $name        = $opt->{'name'};
@@ -58,6 +60,7 @@ if ( ref($_[0]) ) {
   $usedatetime = $opt->{'usedatetime'};
   $noinit      = $opt->{'noinit'};
   $colspan     = $opt->{'colspan'} || 1;
+  $required    = $opt->{'required'};
 } else {
   ($name, $value, $label, $format, $usedatetime) = @_;
   $colspan = 1;
diff --git a/httemplate/elements/tr-select-hardware_type.html b/httemplate/elements/tr-select-hardware_type.html
index c306641..b51afc0 100644
--- a/httemplate/elements/tr-select-hardware_type.html
+++ b/httemplate/elements/tr-select-hardware_type.html
@@ -1,5 +1,6 @@
-<TR>
-  <TD ALIGN="right"><% $opt{'label'} || 'Device type: ' %></TD>
+<% include('/elements/tr-td-label.html',
+     'label'    => $opt{'label'} || 'Device type: ',
+     'required' => $opt{'required'} ) %>
   <TD><% include('select-hardware_type.html', %opt) %></TD>
 </TR>
 
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index 11f7c48..ee13568 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -30,7 +30,7 @@ function clearhint_ip_addr (what) {
     what.value = '';
 }
 </script>
-<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router') &>
+<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router'), required => $opt{'required'} &>
 <td>
   <& /elements/select-tiered.html, prefix => 'router_', tiers => [
   {
@@ -56,9 +56,9 @@ function clearhint_ip_addr (what) {
 ]
 &>
 </td></tr>
-<& /elements/tr-td-label.html, label => 'IP address' &>
+<& /elements/tr-td-label.html, label => 'IP address', required => $opt{'ip_addr_required'} &>
 <td>
-% warn Dumper \%fixed;
+% #warn Dumper \%fixed;
 % if ( exists $fixed{$ip_field} ) {
   <input type="hidden" id="input_ip_addr" name="<% $ip_field %>" 
     value="<% $opt{'ip_addr'} |h%>"><% $opt{'ip_addr'} || '' %>

commit ec271a1445bf232cd172c38e2dd3fd9d3c5c7c4e
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Fri Apr 24 22:19:34 2015 -0500

    RT#34289: Flag service fields as mandatory

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 5dcf3c3..3a27b74 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2447,6 +2447,7 @@ sub tables_hashref {
         'columnlabel', 'varchar', 'NULL', $char_d, '', '',
         'columnvalue', 'varchar', 'NULL',     512, '', '', 
         'columnflag',  'char',    'NULL',       1, '', '', 
+        'required',    'char',    'NULL',       1, '', '', 
       ],
       'primary_key' => 'columnnum',
       'unique' => [ [ 'svcpart', 'columnname' ] ],
diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm
index c2003c4..abeb28b 100644
--- a/FS/FS/part_svc.pm
+++ b/FS/FS/part_svc.pm
@@ -96,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
@@ -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 {
@@ -280,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), at fields)
     ) {
@@ -292,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*$/ ) {
 
@@ -310,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 {
@@ -700,6 +712,8 @@ some components specified by "select-.*.html", and a bunch more...
 
 =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
@@ -777,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;
 
diff --git a/FS/FS/part_svc_column.pm b/FS/FS/part_svc_column.pm
index 38ce1fa..75a2dfb 100644
--- a/FS/FS/part_svc_column.pm
+++ b/FS/FS/part_svc_column.pm
@@ -45,6 +45,8 @@ fields are currently supported:
 
 =item columnflag - null or empty (no default), `D' for default, `F' for fixed (unchangeable), `S' for selectable choice, `M' for manual selection from inventory, `A' for automatic selection from inventory, or `H' for selection from a hardware class.  For virtual fields, can also be 'X' for excluded.
 
+=item required - column value expected to be true
+
 =back
 
 =head1 METHODS
@@ -91,6 +93,7 @@ sub check {
     || $self->ut_alpha('columnname')
     || $self->ut_textn('columnlabel')
     || $self->ut_anything('columnvalue')
+    || $self->ut_flag('required')
   ;
   return $error if $error;
 
diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index 61a5f6a..34ae219 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -155,13 +155,46 @@ sub cust_linked {
 
 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).
+Only checks fields marked as required in table_info or 
+part_svc_column definition.  Should be invoked by service-specific
+check using SUPER.  Invokes FS::Record::check using SUPER.
 
 =cut
 
 sub check {
   my $self = shift;
+
+  ## Checking required fields
+
+  # get fields marked as required in table_info
+  my $required = {};
+  my $labels = {};
+  my $tinfo = $self->can('table_info') ? $self->table_info : {};
+  my $fields = $tinfo->{'fields'} || {};
+  foreach my $field (keys %$fields) {
+    if (ref($fields->{$field}) && $fields->{$field}->{'required'}) {
+      $required->{$field} = 1;
+      $labels->{$field} = $fields->{$field}->{'label'};
+    }
+  }
+  # add fields marked as required in database
+  foreach my $column (
+    qsearch('part_svc_column',{
+      'svcpart' => $self->svcpart,
+      'required' => 'Y'
+    })
+  ) {
+    $required->{$column->columnname} = 1;
+    $labels->{$column->columnname} = $column->columnlabel;
+  }
+  # do the actual checking
+  foreach my $field (keys %$required) {
+    unless ($self->$field) {
+      my $name = $labels->{$field} || $field;
+      return "Field $name is required\n"
+    }
+  }
+
   $self->SUPER::check;
 }
 
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
index 93e3f2c..ffcadc9 100644
--- a/FS/FS/svc_acct.pm
+++ b/FS/FS/svc_acct.pm
@@ -284,6 +284,7 @@ sub table_info {
                          disable_default => 1,
                          disable_fixed => 1,
                          disable_select => 1,
+                         required => 1,
                        },
         'password_selfchange' => { label => 'Password modification',
                                    type  => 'checkbox',
@@ -311,7 +312,9 @@ sub table_info {
                          type => 'text',
                          disable_inventory => 1,
                        },
-        '_password' => 'Password',
+        '_password' => { label => 'Password',
+                         required => 1
+                       },
         'gid'       => {
                          label    => 'GID',
 		         def_info => 'when blank, defaults to UID',
@@ -334,6 +337,7 @@ sub table_info {
                          select_key   => 'svcnum',
                          select_label => 'domain',
                          disable_inventory => 1,
+                         required => 1,
                        },
         'pbxsvc'    => { label => 'PBX',
                          type  => 'select-svc_pbx.html',
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
index b01d673..78556cf 100644
--- a/FS/FS/svc_domain.pm
+++ b/FS/FS/svc_domain.pm
@@ -134,7 +134,10 @@ sub table_info {
     'display_weight' => 20,
     'cancel_weight'  => 60,
     'fields' => {
-      'domain' => 'Domain',
+      'domain' => {
+                  label => 'Domain',
+                  required => 1,
+                },
       'parent_svcnum' => { 
                          label => 'Parent domain / Communigate administrator domain',
                          type  => 'select',
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
index 0d36853..ec5f321 100755
--- a/httemplate/browse/part_svc.cgi
+++ b/httemplate/browse/part_svc.cgi
@@ -61,6 +61,8 @@ function part_export_areyousure(href) {
 
     <TH COLSPAN=2 CLASS="grid" BGCOLOR="#cccccc">Modifier</TH>
 
+    <TH CLASS="grid" BGCOLOR="#cccccc" STYLE="font-size: smaller;">Required</TH>
+
   </TR>
 % my $conf = FS::Conf->new;
 % foreach my $part_svc ( @part_svc ) {
@@ -78,6 +80,9 @@ function part_export_areyousure(href) {
 %                $col->columnflag || ( $col->columnlabel !~ /^\S*$/
 %                                      && $col->columnlabel ne $def->{'label'}
 %                                    )
+%                                 || ( $col->required
+%                                      && !$def->{'required'}
+%                                    )
 %              )
 %            }
 %            @dfields ;
@@ -150,7 +155,7 @@ function part_export_areyousure(href) {
     </TD>
 
 %     unless ( @fields ) {
-%       for ( 1..4 ) {  
+%       for ( 1..5 ) {  
 	  <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"</TD>
 %       }
 %     }
@@ -170,7 +175,6 @@ function part_export_areyousure(href) {
      <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $field %></TD>
      <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $label %></TD>
      <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $flag{$flag} %></TD>
-
      <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
 % my $value = &$formatter($part_svc->part_svc_column($field)->columnvalue);
 % if ( $flag =~ /^[MAH]$/ ) { 
@@ -189,6 +193,11 @@ function part_export_areyousure(href) {
 % }
 
      </TD>
+     <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ($part_svc_column->required) {
+       Yes
+% }
+     </TD>
 %     $n1="</TR><TR>";
 %     } #foreach $field
 %   if ( $part_svc->restrict_edit_password ) {
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
index 2bb4f5e..a6ccaf8 100644
--- a/httemplate/edit/elements/part_svc_column.html
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -77,6 +77,7 @@ that field.
     <TH BGCOLOR="#cccccc">Field</TH>
     <TH BGCOLOR="#cccccc">Label</TH>
     <TH BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>
+    <TH BGCOLOR="#cccccc">Required?</TH>
   </TR>
 % $part_svc->set('svcpart' => $opt{'clone'}) if $opt{'clone'}; # for now
 % my $i = 0;
@@ -210,9 +211,17 @@ that field.
       &>
 %   }
     </TD>
+    <TD>
+%   if (!$def->{'type'} || !(grep {$_ eq $def->{'type'}} ('checkbox','disabled'))) {
+      <INPUT ID="<% $name.'_required' %>" TYPE="checkbox" NAME="<% $svcdb %>__<% $field %>_required" VALUE="Y" 
+        <% ($part_svc_column->required || $def->{'required'}) ? 'CHECKED' : '' %> 
+        <% $def->{'required'} ? 'DISABLED' : '' %>
+       >
+%   }
+    </TD>
   </TR>
   <TR CLASS="row<%$i%>">
-    <TD COLSPAN=2 CLASS="def_info">
+    <TD COLSPAN=3 CLASS="def_info">
 %   if ( $def->{def_info} ) {
       (<% $def->{def_info} %>)
     </TD>
@@ -228,7 +237,7 @@ that field.
     <TD COLSPAN=3 ALIGN="right">
       <% emt('Require "Provision" access right to edit password') %>
     </TD>
-    <TD>
+    <TD COLSPAN=2>
       <INPUT TYPE="checkbox" NAME="restrict_edit_password" VALUE="Y" \
       <% $part_svc->restrict_edit_password ? 'CHECKED' : '' %>>
     </TD>
@@ -244,7 +253,7 @@ that field.
     <TD COLSPAN=3 ALIGN="right">
       <% emt('This service has an attached router') %>
     </TD>
-    <TD>
+    <TD COLSPAN=2>
       <INPUT TYPE="checkbox" NAME="has_router" VALUE="Y" \
       <% $part_svc->has_router ? 'CHECKED' : '' %>>
     </TD>
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index 47b020c..7a47f15 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -101,6 +101,15 @@ function flag_changed(obj) {
       }
     }
   }
+  var required = document.getElementById(layer + '__' + field + '_required');
+  if (required && !required.disabledinit) {
+    if (newflag == "F") {
+      required.checked = false;
+      required.disabled = true;
+    } else {
+      required.disabled = false;
+    }
+  }
 }
 
 window.onload = function() {
@@ -111,6 +120,17 @@ window.onload = function() {
       obj.setAttribute('should_be_multiple', true);
     }
   }
+  var inputs = document.getElementsByTagName('INPUT');
+  for(i = 0; i < inputs.length; i++) {
+    var obj = inputs[i];
+    if (obj.type == 'checkbox') {
+      if ( obj.name.match(/_required$/) ) {
+        if ( obj.disabled ) {
+          obj.disabledinit = 1;
+        }
+      }
+    }
+  }
   for(i = 0; i < selects.length; i++) {
     var obj = selects[i];
     if ( obj.name.match(/_flag$/) ) {

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Schema.pm                                    |    1 +
 FS/FS/part_svc.pm                                  |  196 ++++++++++++--------
 FS/FS/part_svc_column.pm                           |    3 +
 FS/FS/svc_Common.pm                                |   39 +++-
 FS/FS/svc_acct.pm                                  |    7 +-
 FS/FS/svc_broadband.pm                             |    1 +
 FS/FS/svc_dish.pm                                  |    3 +-
 FS/FS/svc_hardware.pm                              |    2 +
 httemplate/browse/part_svc.cgi                     |   13 +-
 httemplate/edit/elements/edit.html                 |    1 +
 httemplate/edit/elements/part_svc_column.html      |   18 +-
 httemplate/edit/elements/svc_Common.html           |    3 +
 httemplate/edit/part_svc.cgi                       |   20 ++
 httemplate/edit/svc_acct.cgi                       |   63 ++++---
 httemplate/edit/svc_acct/communigate.html          |    5 +-
 httemplate/edit/svc_broadband.cgi                  |   11 +-
 httemplate/elements/tr-input-date-field.html       |    9 +-
 httemplate/elements/tr-select-hardware_type.html   |    5 +-
 httemplate/elements/tr-select-router_block_ip.html |    6 +-
 19 files changed, 285 insertions(+), 121 deletions(-)




More information about the freeside-commits mailing list