diff options
author | ivan <ivan> | 2006-06-24 16:41:45 +0000 |
---|---|---|
committer | ivan <ivan> | 2006-06-24 16:41:45 +0000 |
commit | 9608be1f5c73517fc348f1ab458892b34ed7facb (patch) | |
tree | 4b1e446078ba4bcfadcc826b83a0cf4a016ed985 | |
parent | 8af88c7428552043516f529007645ab489b32063 (diff) |
Add the ability to link customer service definition fields to inventory
classes, with an "automatic/manual" flag. Add the ability for the web
interface to maintain these links. Start prettying up the service def.
edit in preparation for Bigger Changes.
-rw-r--r-- | FS/FS/part_svc.pm | 21 | ||||
-rw-r--r-- | FS/FS/part_svc_column.pm | 10 | ||||
-rwxr-xr-x | httemplate/browse/part_svc.cgi | 28 | ||||
-rwxr-xr-x | httemplate/edit/part_svc.cgi | 271 | ||||
-rw-r--r-- | httemplate/elements/select-table.html | 16 | ||||
-rw-r--r-- | httemplate/elements/table-grid.html | 8 |
6 files changed, 273 insertions, 81 deletions
diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm index 1a478a9..7f79194 100644 --- a/FS/FS/part_svc.pm +++ b/FS/FS/part_svc.pm @@ -11,7 +11,7 @@ use FS::cust_svc; @ISA = qw(FS::Record); -$DEBUG = 0; +$DEBUG = 1; =head1 NAME @@ -79,7 +79,7 @@ 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>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed. For virtual fields, can also be 'X' for excluded. +=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null or empty (no default), `D' for default, `F' for fixed (unchangeable), `M' for manual selection from inventory, or `A' for automatic selection from inventory. For virtual fields, can also be 'X' for excluded. =back @@ -142,7 +142,8 @@ sub insert { } ); my $flag = $self->getfield($svcdb.'__'.$field.'_flag'); - if ( uc($flag) =~ /^([DFX])$/ ) { + #if ( uc($flag) =~ /^([DFMAX])$/ ) { + if ( uc($flag) =~ /^([A-Z])$/ ) { #part_svc_column will test it $part_svc_column->setfield('columnflag', $1); $part_svc_column->setfield('columnvalue', $self->getfield($svcdb.'__'.$field) @@ -260,7 +261,8 @@ sub replace { } ); my $flag = $new->getfield($svcdb.'__'.$field.'_flag'); - if ( uc($flag) =~ /^([DFX])$/ ) { + #if ( uc($flag) =~ /^([DFMAX])$/ ) { + if ( uc($flag) =~ /^([A-Z])$/ ) { #part_svc_column will test it $part_svc_column->setfield('columnflag', $1); $part_svc_column->setfield('columnvalue', $new->getfield($svcdb.'__'.$field) @@ -536,7 +538,16 @@ sub process { map { my $svcdb = $_; my @fields = fields($svcdb); push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge - map { ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' ) } @fields; + + map { + if ( $param->{ $svcdb.'__'.$_.'_flag' } =~ /^[MA]$/ ) { + $param->{ $svcdb.'__'.$_ } = + delete( $param->{ $svcdb.'__'.$_.'_classnum' } ); + } + ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' ); + } + @fields; + } grep defined( dbdef->table($_) ), qw( svc_acct svc_domain svc_forward svc_www svc_broadband ) ) diff --git a/FS/FS/part_svc_column.pm b/FS/FS/part_svc_column.pm index 0450b35..fb08eaa 100644 --- a/FS/FS/part_svc_column.pm +++ b/FS/FS/part_svc_column.pm @@ -41,7 +41,7 @@ fields are currently supported: =item columnvalue - default or fixed value for the column -=item columnflag - null, D, F, X (virtual fields) +=item columnflag - null or empty (no default), `D' for default, `F' for fixed (unchangeable), `M' for manual selection from inventory, or `A' for automatic selection from inventory. For virtual fields, can also be 'X' for excluded. =back @@ -91,10 +91,16 @@ sub check { ; return $error if $error; - $self->columnflag =~ /^([DFX])$/ + $self->columnflag =~ /^([DFMAX])$/ or return "illegal columnflag ". $self->columnflag; $self->columnflag(uc($1)); + if ( $self->columnflag =~ /^[MA]$/ ) { + $error = + $self->ut_foreign_key( 'columnvalue', 'inventory_class', 'classnum' ); + return $error if $error; + } + $self->SUPER::check; } diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi index eef2676..805bd88 100755 --- a/httemplate/browse/part_svc.cgi +++ b/httemplate/browse/part_svc.cgi @@ -1,9 +1,15 @@ <% +#code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm my %flag = ( - 'D' => 'Default', - 'F' => 'Fixed', '' => '', + 'D' => 'Default', + 'F' => 'Fixed (unchangeable)', + #'M' => 'Manual selection from inventory', + 'M' => 'Manual selected from inventory', + #'A' => 'Automatically fill in from inventory', + 'A' => 'Automatically filled in from inventory', + 'X' => 'Excluded', ); my %search; @@ -27,6 +33,8 @@ if ( $cgi->param('orderby') eq 'active' ) { @part_svc = sort { lc($a->svc) cmp lc($b->svc) } @part_svc; } +my %inventory_class = (); + %> <%= include("/elements/header.html",'Service Definition Listing', menubar( 'Main Menu' => $p) ) %> @@ -125,7 +133,21 @@ map { qsearchs('part_export', { exportnum => $_->exportnum } ) } qsearch('export <%= $n1 %> <TD><%= $field %></TD> <TD><%= $flag{$flag} %></TD> - <TD><%= $part_svc->part_svc_column($field)->columnvalue%></TD> + + <TD> + <% my $value = $part_svc->part_svc_column($field)->columnvalue; + if ( $flag =~ /^[MA]$/ ) { + $inventory_class{$value} + ||= qsearchs('inventory_class', { 'classnum' => $value } ); + %> + <%= $inventory_class{$value} + ? $inventory_class{$value}->classname + : "WARNING: inventory_class.classnum $value not found" %> + <% } else { %> + <%= $value %> + <% } %> + </TD> + <% $n1="</TR><TR>"; } diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi index c5fff25..77011e9 100755 --- a/httemplate/edit/part_svc.cgi +++ b/httemplate/edit/part_svc.cgi @@ -38,28 +38,24 @@ Service <INPUT TYPE="text" NAME="svc" VALUE="<%= $hashref->{svc} %>"><BR> Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR> <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $hashref->{svcpart} %>"> <BR> -Services are items you offer to your customers. -<UL><LI>svc_acct - Shell accounts, POP mailboxes, SLIP/PPP and ISDN accounts +Service definitions are the templates for items you offer to your customers. +<UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, etc.) <LI>svc_domain - Domains <LI>svc_forward - mail forwarding <LI>svc_www - Virtual domain website - <LI>svc_broadband - Broadband/High-speed Internet service + <LI>svc_broadband - Broadband/High-speed Internet service (always-on) <LI>svc_external - Externally-tracked service <!-- <LI>svc_charge - One-time charges (Partially unimplemented) <LI>svc_wo - Work orders (Partially unimplemented) --> </UL> For the selected table, you can give fields default or fixed (unchangable) -values. For example, a SLIP/PPP account may have a default (or perhaps fixed) -<B>slipip</B> of <B>0.0.0.0</B>, while a POP mailbox will probably have a fixed -blank <B>slipip</B> as well as a fixed shell something like <B>/bin/true</B> or -<B>/usr/bin/passwd</B>. +values, or select an inventory class to manually or automatically fill in +that field. <BR><BR> <% -my %vfields; - #these might belong somewhere else for other user interfaces #pry need to eventually create stuff that's shared amount UIs my $conf = new FS::Conf; @@ -77,28 +73,42 @@ my %defs = ( select_label => 'city', }, 'username' => { - desc => 'Username', - type => 'disabled', + desc => 'Username', + type => 'text', + disable_default => 1, + disable_fixed => 1, + }, + 'quota' => { + desc => '', + type => 'text', + disable_inventory => 1, }, - 'quota' => '', '_password' => 'Password', 'gid' => 'GID (when blank, defaults to UID)', 'shell' => { - desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file, set to blank for no shell tracking)', + #desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file, set to blank for no shell tracking)', + desc =>'Shell ( set to blank for no shell tracking)', type =>'select', select_list => [ $conf->config('shells') ], + disable_inventory => 1, }, - 'finger' => 'GECOS', + 'finger' => 'Real name (GECOS)', 'domsvc' => { desc =>'svcnum from svc_domain', type =>'select', select_table => 'svc_domain', select_key => 'svcnum', select_label => 'domain', + disable_inventory => 1, }, 'usergroup' => { desc =>'RADIUS groups', type =>'radius_usergroup_selector', + disable_inventory => 1, + }, + 'seconds' => { desc => '', + type => 'text', + disable_inventory => 1, }, }, 'svc_domain' => { @@ -132,6 +142,7 @@ my %defs = ( }, ); + my %vfields; foreach my $svcdb (grep dbdef->table($_), keys %defs ) { my $self = "FS::$svcdb"->new; $vfields{$svcdb} = {}; @@ -149,6 +160,37 @@ my %defs = ( warn "\$vfields{$svcdb}->{$field} = $pvf"; } #next $field } #next $svcdb + + #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm + # and generalize the subs + # condition sub is tested to see whether to disable display of this choice + # params: ( $def, $layer, $field ) (see SUB below) + my $inv_sub = sub { + ref($_[0]) && ( $_[0]->{disable_inventory} + || $_[0]->{'type'} ne 'text' ) + }; + tie my %flag, 'Tie::IxHash', + '' => { 'desc' => 'No default', }, + 'D' => { 'desc' => 'Default', + 'condition' => + sub { ref($_[0]) && $_[0]->{disable_default} }, + }, + 'F' => { 'desc' => 'Fixed (unchangeable)', + 'condition' => + sub { ref($_[0]) && $_[0]->{disable_fixed} }, + }, + 'M' => { 'desc' => 'Manual selection from inventory', + 'condition' => $inv_sub, + }, + 'A' => { 'desc' => 'Automatically fill in from inventory', + 'condition' => $inv_sub, + }, + 'X' => { 'desc' => 'Excluded', + 'condition' => + sub { ! $vfields{$_[1]}->{$_[2]} }, + + }, + ; my @dbs = $hashref->{svcdb} ? ( $hashref->{svcdb} ) @@ -174,8 +216,8 @@ my %defs = ( my @part_export = map { qsearch( 'part_export', {exporttype => $_ } ) } keys %{FS::part_export::export_info($layer)}; - $html .= '<BR><BR>'. table(). - table(). "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>"; + $html .= '<BR><BR>'. table(). + "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>"; foreach my $part_export ( @part_export ) { $html .= '<TD><INPUT TYPE="checkbox"'. ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" '; @@ -191,75 +233,176 @@ my %defs = ( } $html .= '</TR></TABLE><BR><BR>'; - $html .= table(). "<TH>Field</TH><TH COLSPAN=2>Modifier</TH>"; + $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ). + '<TR>'. + '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'. + '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'. + '</TR>'; + + my $bgcolor1 = '#eeeeee'; + my $bgcolor2 = '#ffffff'; + my $bgcolor; + #yucky kludge my @fields = defined( dbdef->table($layer) ) ? grep { $_ ne 'svcnum' } fields($layer) : (); push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge $part_svc->svcpart($clone) if $clone; #haha, undone below + + foreach my $field (@fields) { + my $part_svc_column = $part_svc->part_svc_column($field); my $value = $part_svc_column->columnvalue; my $flag = $part_svc_column->columnflag; my $def = $defs{$layer}{$field}; my $desc = ref($def) ? $def->{desc} : $def; + + if ( $bgcolor eq $bgcolor1 ) { + $bgcolor = $bgcolor2; + } else { + $bgcolor = $bgcolor1; + } - $html .= "<TR><TD>$field"; + $html .= qq!<TR><TD CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!. + $field; $html .= "- <FONT SIZE=-1>$desc</FONT>" if $desc; $html .= "</TD>"; $flag = '' if ref($def) && $def->{type} eq 'disabled'; - $html .= - qq!<TD><INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE=""!. - ' CHECKED'x($flag eq ''). ">Off</TD>". - '<TD>'; - unless ( ref($def) && $def->{type} eq 'disabled' ) { - $html .= - qq!<INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="D"!. - ' CHECKED'x($flag eq 'D'). ">Default ". - qq!<INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="F"!. - ' CHECKED'x($flag eq 'F'). ">Fixed "; - $html .= '<BR>'; - } - if ( ref($def) ) { - if ( $def->{type} eq 'select' ) { - $html .= qq!<SELECT NAME="${layer}__${field}">!; - $html .= '<OPTION> </OPTION>' unless $value; - 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( - [ split(',', $value) ], "${layer}__${field}" ); - } elsif ( $def->{type} eq 'disabled' ) { - $html .= - qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!; - } else { - $html .= '<font color="#ff0000">unknown type'. $def->{type}; - } + + $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!; + + if ( ref($def) && $def->{type} eq 'disabled' ) { + + $html .= 'No default'; + } else { - $html .= - qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value">!; + + $html .= qq!<SELECT NAME="${layer}__${field}_flag"!. + qq! onChange="${layer}__${field}_flag_changed(this)">!; + + foreach my $f ( keys %flag ) { + + #here is where the SUB from above is called, to skip some choices + next if $flag{$f}->{condition} + && &{ $flag{$f}->{condition} }( $def, $layer, $field ); + + $html .= qq!<OPTION VALUE="$f"!. + ' SELECTED'x($flag eq $f ). + '>'. $flag{$f}->{desc}; + + } + + $html .= '</SELECT>'; + + $html .= join("\n", + '<SCRIPT>', + " function ${layer}__${field}_flag_changed(what) {", + ' var f = what.options[what.selectedIndex].value;', + ' if ( f == "" || f == "X" ) { //disable', + " what.form.${layer}__${field}.disabled = true;". + " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';". + " if ( what.form.${layer}__${field}_classnum ) {". + " what.form.${layer}__${field}_classnum.disabled = true;". + " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';". + " }". + ' } else if ( f == "D" || f == "F" ) { //enable, text box', + " what.form.${layer}__${field}.disabled = false;". + " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';". + " what.form.${layer}__${field}.style.display = '';". + " if ( what.form.${layer}__${field}_classnum ) {". + " what.form.${layer}__${field}_classnum.disabled = false;". + " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';". + " what.form.${layer}__${field}_classnum.style.display = 'none';". + " }". + ' } else if ( f == "M" || f == "A" ) { //enable, inventory', + " what.form.${layer}__${field}.disabled = false;". + " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';". + " what.form.${layer}__${field}.style.display = 'none';". + " if ( what.form.${layer}__${field}_classnum ) {". + " what.form.${layer}__${field}_classnum.disabled = false;". + " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';". + " what.form.${layer}__${field}_classnum.style.display = '';". + " }". + ' }', + ' }', + '</SCRIPT>', + ); + } - if($vfields{$layer}->{$field}) { - $html .= qq!<BR><INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="X"!. - ' CHECKED'x($flag eq 'X'). ">Excluded "; + $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!; + + my $disabled = $flag ? '' + : 'DISABLED STYLE="background-color: #dddddd"'; + + if ( ! ref($def) || $def->{type} eq 'text' ) { + + my $nodisplay = ' STYLE="display:none"'; + my $is_inv = ( $flag =~ /^[MA]$/ ); + + $html .= + qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !. + $disabled. + ( $is_inv ? $nodisplay : $disabled ). + '>'; + + $html .= include('/elements/select-table.html', + 'element_name' => "${layer}__${field}_classnum", + 'element_etc' => ( $is_inv + ? $disabled + : $nodisplay + ), + 'table' => 'inventory_class', + 'name_col' => 'classname', + 'value' => $value, + 'empty_label' => 'Select inventory class', + ); + + } elsif ( $def->{type} eq 'select' ) { + + $html .= qq!<SELECT NAME="${layer}__${field}" $disabled>!; + $html .= '<OPTION> </OPTION>' unless $value; + 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' ) { + + #XXX disable the RADIUS usergroup selector? ugh it sure does need + #an overhaul, people have dum group problems because of it + + $html .= FS::svc_acct::radius_usergroup_selector( + [ split(',', $value) ], "${layer}__${field}" ); + + } elsif ( $def->{type} eq 'disabled' ) { + + $html .= + qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!; + + } else { + + $html .= '<font color="#ff0000">unknown type'. $def->{type}; + } + $html .= "</TD></TR>\n"; - } + + } #foreach my $field (@fields) { + $part_svc->svcpart('') if $clone; #undone $html .= "</TABLE>"; diff --git a/httemplate/elements/select-table.html b/httemplate/elements/select-table.html index 6c8089b..36eb4e2 100644 --- a/httemplate/elements/select-table.html +++ b/httemplate/elements/select-table.html @@ -8,11 +8,15 @@ # 'value' => 'current_value', # ##opt - # 'empty_label' => '', #better specify it though, the default might change - # 'hashref' => {}, - # 'extra_sql' => '', - # 'records' => \@records, #instead of hashref - # 'pre_options' => [ 'value' => 'option' ], #before normal options + # 'empty_label' => '', #better specify it though, the default might change + # 'hashref' => {}, + # 'extra_sql' => '', + # 'records' => \@records, #instead of hashref + # 'pre_options' => [ 'value' => 'option' ], #before normal options + # 'element_name' => '', #HTML element name, defaults to the name of + # # the primary key column + # 'element_etc' => '', #additional attributes (i.e. "DISABLED") for the + # #<SELECT> element my( %opt ) = @_; @@ -37,7 +41,7 @@ %> -<SELECT NAME="<%= $key %>"> +<SELECT NAME="<%= $opt{'element_name'} || $key %>" <%= $opt{'element_etc'} %>> <% while ( @pre_options ) { %> <OPTION VALUE="<%= shift(@pre_options) %>"><%= shift(@pre_options) %> diff --git a/httemplate/elements/table-grid.html b/httemplate/elements/table-grid.html index 80611f5..17eafdf 100644 --- a/httemplate/elements/table-grid.html +++ b/httemplate/elements/table-grid.html @@ -1,8 +1,14 @@ +<% + my %opt = @_; + $opt{cellspacing} ||= 0; + $opt{cellpadding} ||= 0; + +%> <STYLE TYPE="text/css"> .grid table { border: solid; empty-cells: show } .grid TH { padding-left: 3px; padding-right: 3px; border: 1px solid #dddddd; border-bottom: dashed 1px black; border-right: none } .grid TD { padding-left: 3px; padding-right: 3px; empty-cells: show; border: 1px solid #cccccc; border-bottom: none; border-right: none } </STYLE> -<TABLE CLASS="grid" CELLSPACING=0 CELLPADDING=0 BORDER=1 BORDERCOLOR="#000000" STYLE="border: solid 1px black; empty-cells: show"> +<TABLE CLASS="grid" CELLSPACING=<%= $opt{cellspacing} %> CELLPADDING=<%= $opt{cellpadding} %> BORDER=1 BORDERCOLOR="#000000" STYLE="border: solid 1px black; empty-cells: show"> |