summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2013-03-18 19:33:34 -0700
committerMark Wells <mark@freeside.biz>2013-03-18 19:33:34 -0700
commitc2e126583354b58ef54ffa7f580b115b8eed1dd3 (patch)
tree0e0c25a9e8ccff3085b6caf55408f8c383720471 /httemplate
parent04f99991071acf5c390ea4d52db37798543ff9d8 (diff)
multiple inventory classes for service columns, #21442
Diffstat (limited to 'httemplate')
-rwxr-xr-xhttemplate/browse/part_svc.cgi12
-rw-r--r--httemplate/edit/elements/edit.html1
-rw-r--r--httemplate/edit/elements/part_svc_column.html303
-rw-r--r--httemplate/edit/elements/svc_Common.html27
-rwxr-xr-xhttemplate/edit/part_svc.cgi555
-rw-r--r--httemplate/edit/process/elements/svc_Common.html5
-rw-r--r--httemplate/elements/tr-select-inventory_item.html48
-rw-r--r--httemplate/search/elements/svc_Common.html48
-rwxr-xr-xhttemplate/search/svc_acct.cgi2
-rwxr-xr-xhttemplate/search/svc_broadband.cgi2
-rwxr-xr-xhttemplate/search/svc_dish.cgi2
-rwxr-xr-xhttemplate/search/svc_external.cgi2
-rw-r--r--httemplate/search/svc_hardware.cgi2
-rw-r--r--httemplate/search/svc_phone.cgi2
-rwxr-xr-xhttemplate/search/svc_www.cgi2
-rw-r--r--httemplate/view/elements/svc_Common.html9
-rw-r--r--httemplate/view/svc_hardware.cgi3
17 files changed, 574 insertions, 451 deletions
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
index f941ae586..0d3685355 100755
--- a/httemplate/browse/part_svc.cgi
+++ b/httemplate/browse/part_svc.cgi
@@ -175,12 +175,14 @@ function part_export_areyousure(href) {
% my $value = &$formatter($part_svc->part_svc_column($field)->columnvalue);
% if ( $flag =~ /^[MAH]$/ ) {
% my $select_table = ($flag eq 'H') ? 'hardware_class' : 'inventory_class';
-% $select_class{$value} ||=
-% qsearchs($select_table, { 'classnum' => $value } );
+% foreach my $classnum ( split(',', $value) ) {
+% $select_class{$classnum} =
+% qsearchs($select_table, { 'classnum' => $classnum } );
%
- <% $select_class{$value}
- ? $select_class{$value}->classname
- : "WARNING: $select_table.classnum $value not found" %>
+ <% $select_class{$classnum}
+ ? $select_class{$classnum}->classname
+ : "WARNING: $select_table.classnum $classnum not found" %><BR>
+% }
% } else {
<% $value %>
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index a24f23805..3e6bd5bec 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -329,6 +329,7 @@ Example:
% qw( country ), #select-country
% qw( width height ), #htmlarea
% qw( alt_format ), #select-cust_location
+% qw( classnum ), # select-inventory_item
% ;
%
% #select-table
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
new file mode 100644
index 000000000..d03c49d2f
--- /dev/null
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -0,0 +1,303 @@
+<%doc>
+To be called from part_svc.cgi.
+<& elements/part_svc_column.html,
+ 'svc_acct',
+ # options...
+ 'part_svc' => $part_svc, # the existing part_svc to edit
+ 'clone' => 0, # or a svcpart to clone from
+&>
+
+</%doc>
+<%once>
+# the semantics of this could be better
+
+# all of these conditions are when NOT to allow that flag choice
+# don't allow the 'inventory' flags (M, A) to be chosen for
+# fields that aren't free-text
+my $inv_sub = sub { $_[0]->{disable_inventory} || $_[0]->{type} ne 'text' };
+tie my %flag, 'Tie::IxHash',
+ '' => { 'desc' => 'No default', 'condition' => sub { 0 } },
+ 'D' => { 'desc' => 'Default',
+ 'condition' =>
+ sub { $_[0]->{disable_default } }
+ },
+ 'F' => { 'desc' => 'Fixed (unchangeable)',
+ 'condition' =>
+ sub { $_[0]->{disable_fixed} },
+ },
+ 'S' => { 'desc' => 'Selectable Choice',
+ 'condition' =>
+ sub { $_[0]->{disable_select} },
+ },
+ 'M' => { 'desc' => 'Manual selection from inventory',
+ 'condition' => $inv_sub,
+ },
+ 'A' => { 'desc' => 'Automatically fill in from inventory',
+ 'condition' => $inv_sub,
+ },
+ 'H' => { 'desc' => 'Select from hardware class',
+ 'condition' => sub { $_[0]->{type} ne 'select-hardware' },
+ },
+ 'X' => { 'desc' => 'Excluded',
+ 'condition' => sub { 1 }, # obsolete
+ },
+;
+
+# the semantics of this could be much better
+sub flag_condition {
+ my $f = shift;
+ not &{ $flag{$f}->{'condition'} }(@_);
+}
+
+my %communigate_fields = (
+ 'svc_acct' => { map { $_=>1 }
+ qw( file_quota file_maxnum file_maxsize
+ password_selfchange password_recover
+ ),
+ grep /^cgp_/, fields('svc_acct')
+ },
+ 'svc_domain' => { map { $_=>1 }
+ qw( max_accounts trailer parent_svcnum ),
+ grep /^(cgp|acct_def)_/, fields('svc_domain')
+ },
+);
+</%once>
+<INPUT TYPE="hidden" NAME="svcdb" VALUE="<% $svcdb %>">
+<BR><BR>
+<& /elements/table.html &>
+ <TR><TH COLSPAN=<% $columns %>>Exports</TH></TR>
+ <TR>
+% # exports
+% foreach my $part_export (@part_export) {
+ <TD>
+ <INPUT TYPE="checkbox" \
+ NAME="exportnum<% $part_export->exportnum %>" \
+ VALUE=1 \
+ <% $has_export_svc{$part_export->exportnum} ? 'CHECKED' : '' %>>
+ <% $part_export->label_html %>
+ </TD>
+% $count++;
+% if ( $count % $columns == 0 ) {
+ </TR>
+ <TR>
+% }
+% }
+ </TR>
+</TABLE><BR><BR>
+For the selected table, you can give fields default or fixed (unchangeable)
+values, or select an inventory class to manually or automatically fill in
+that field.
+<& /elements/table-grid.html, cellpadding => 4 &>
+ <TR>
+ <TH BGCOLOR="#cccccc">Field</TH>
+ <TH BGCOLOR="#cccccc">Label</TH>
+ <TH BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>
+ </TR>
+% $part_svc->set('svcpart' => $opt{'clone'}) if $opt{'clone'}; # for now
+% my $i = 0;
+% foreach my $field (@fields) {
+% my $def = shift @defs;
+% my $part_svc_column = $part_svc->part_svc_column($field);
+% my $flag = $part_svc_column->columnflag;
+% my $formatter = $def->{'format'} || sub { shift };
+% my $value = &{$formatter}($part_svc_column->columnvalue);
+ <TR CLASS="row<%$i%>">
+ <TD ROWSPAN=2 CLASS="grid" ALIGN="right">
+ <% $def->{'label'} || $field %>
+ </TD>
+ <TD ROWSPAN=2 CLASS="grid">
+ <INPUT NAME="<% $svcdb %>__<% $field %>_label"
+ STYLE="text-align: right"
+ VALUE="<% $part_svc_column->columnlabel || $def->{'label'} |h %>">
+ </TD>
+
+ <TD ROWSPAN=1 CLASS="grid">
+% # flag selection
+% if ( $def->{'type'} eq 'disabled' ) {
+% $flag = '';
+ No default
+% } else {
+% my $name = $svcdb.'__'.$field.'_flag';
+ <SELECT NAME="<%$name%>"
+ ID="<%$name%>"
+ STYLE="width:100%"
+ onchange="flag_changed(this)">
+% foreach my $f (keys %flag) {
+% if ( flag_condition($f, $def, $svcdb, $field) ) {
+ <OPTION VALUE="<%$f%>"<% $flag eq $f ? ' SELECTED' : ''%>>
+ <% $flag{$f}->{desc} %>
+ </OPTION>
+% }
+% }
+ </SELECT>
+% } # if $def->{'type'} eq 'disabled'
+ </TD>
+ <TD CLASS="grid">
+% # value entry/selection
+% my $name = $svcdb.'__'.$field;
+% # These are all MANDATORY SELECT types. Regardless of the flag value,
+% # there will never be a text input (either in svc_* or in part_svc) for
+% # these fields.
+% if ( $def->{'type'} eq 'checkbox' ) {
+ <& /elements/checkbox.html,
+ 'field' => $name,
+ 'curr_value' => $value,
+ 'value' => 'Y' &>
+%
+% } elsif ( $def->{'type'} eq 'select' ) {
+%
+% if ( $def->{'select_table'} ) {
+ <& /elements/select-table.html,
+ 'field' => $name,
+ 'id' => $name.'_select',
+ 'table' => $def->{'select_table'},
+ 'name_col' => $def->{'select_label'},
+ 'value_col' => $def->{'select_key'},
+ 'order_by' => dbdef->table($def->{'select_table'})->primary_key,
+ 'multiple' => $def->{'multiple'},
+ 'disable_empty' => 1,
+ 'curr_value' => $value,
+ &>
+% } else {
+% my (@options, %labels);
+% if ( $def->{'select_list'} ) {
+% @options = @{ $def->{'select_list'} };
+% @labels{@options} = @options;
+% } elsif ( $def->{'select_hash'} ) {
+% if ( ref($def->{'select_hash'}) eq 'ARRAY' ) {
+% tie my %hash, 'Tie::IxHash', @{ $def->{'select_hash'} };
+% $def->{'select_hash'} = \%hash;
+% }
+% @options = keys( %{ $def->{'select_hash'} } );
+% %labels = %{ $def->{'select_hash'} };
+% }
+ <& /elements/select.html,
+ 'field' => $name,
+ 'id' => $name.'_select',
+ 'options' => \@options,
+ 'labels' => \%labels,
+ 'multiple' => $def->{'multiple'},
+ 'curr_value' => $value,
+ &>
+% }
+% } elsif ( $def->{'type'} =~ /select-(.*?).html/ ) {
+ <& '/elements/'.$def->{'type'},
+ 'field' => $name,
+ 'id' => $name.'_select',
+ 'multiple' => $def->{'multiple'},
+ 'curr_value' => $value,
+ &>
+% } elsif ( $def->{'type'} eq 'communigate_pro-accessmodes' ) {
+ <& /elements/communigate_pro-accessmodes.html,
+ 'element_name_prefix' => $name.'_',
+ 'curr_value' => $value,
+ &>
+% } elsif ( $def->{'type'} eq 'textarea' ) {
+% # special cases
+ <TEXTAREA NAME="<%$name%>"><% $value |h %></TEXTAREA>
+% } elsif ( $def->{'type'} eq 'disabled' ) {
+ <INPUT TYPE="hidden" NAME="<%$name%>" VALUE="">
+% } else {
+% # the normal case: a text input, and a _select which is an inventory
+% # or hardware class
+ <INPUT TYPE="text"
+ NAME="<%$name%>"
+ ID="<%$name%>"
+ VALUE="<%$value%>">
+% # inventory class selection
+ <& /elements/select-table.html,
+ 'field' => $name.'_classnum',
+ 'id' => $name.'_select',
+ 'table' => 'inventory_class',
+ 'name_col' => 'classname',
+ 'curr_value' => $value,
+ 'empty_label' => 'Select inventory class',
+ 'multiple' => 1,
+ &>
+% }
+ </TD>
+ </TR>
+ <TR CLASS="row<%$i%>">
+ <TD COLSPAN=2 CLASS="def_info">
+% if ( $def->{def_info} ) {
+ (<% $def->{def_info} %>)
+ </TD>
+ </TR>
+% }
+% $i = 1-$i;
+% } # foreach my $field
+%
+% # special case: svc_acct password edit ACL
+% if ( $svcdb eq 'svc_acct' ) {
+% push @fields, 'restrict_edit_password';
+ <TR>
+ <TD COLSPAN=3 ALIGN="right">
+ <% emt('Require "Provision" access right to edit password') %>
+ </TD>
+ <TD>
+ <INPUT TYPE="checkbox" NAME="restrict_edit_password" VALUE="Y" \
+ <% $part_svc->restrict_edit_password ? 'CHECKED' : '' %>>
+ </TD>
+ </TR>
+% }
+</TABLE>
+<& /elements/progress-init.html,
+ $svcdb, #form name
+ [ # form fields to send
+ qw(svc svcpart classnum selfservice_access disabled preserve exportnum),
+ @fields
+ ],
+ 'process/part_svc.cgi', # target
+ $p.'browse/part_svc.cgi', # redirect landing
+ $svcdb, #key
+&>
+% $svcpart = '' if $opt{clone};
+<BR>
+<INPUT NAME="submit"
+ TYPE="button"
+ VALUE="<% emt($svcpart ? 'Apply changes' : 'Add service') %>"
+ onclick="fixup_submit('<%$svcdb%>')"
+>
+<%init>
+my $svcdb = shift;
+my %opt = @_;
+my $columns = 3;
+my $count = 0;
+my $communigate = 0;
+my $conf = FS::Conf->new;
+
+my $part_svc = $opt{'part_svc'} || FS::part_svc->new;
+
+my @part_export;
+my $export_info = FS::part_export::export_info($svcdb);
+foreach (keys %{ $export_info }) {
+ push @part_export, qsearch('part_export', { exporttype => $_ });
+}
+$communigate = scalar(grep {$_->exporttype =~ /^communigate/} @part_export);
+
+my $svcpart = $opt{'clone'} || $part_svc->svcpart;
+my %has_export_svc;
+if ( $svcpart ) {
+ foreach (qsearch('export_svc', { svcpart => $svcpart })) {
+ $has_export_svc{$_->exportnum} = 1;
+ }
+}
+
+my @fields;
+if ( defined( dbdef->table($svcdb) ) ) { # when is it ever not defined?
+ @fields = grep {
+ $_ ne 'svcnum'
+ and ( $communigate || ! $communigate_fields{$svcdb}->{$_} )
+ and ( !FS::part_svc->svc_table_fields($svcdb)->{$_}->{disable_part_svc_column}
+ || $part_svc->part_svc_column($_)->columnflag )
+ } fields($svcdb);
+}
+if ( $svcdb eq 'svc_acct'
+ or ( $svcdb eq 'svc_broadband' and $conf->exists('svc_broadband-radius') )
+ )
+{
+ push @fields, 'usergroup';
+}
+
+my @defs = map { FS::part_svc->svc_table_fields($svcdb)->{$_} } @fields;
+</%init>
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
index 0d9d36c07..d46d1cb42 100644
--- a/httemplate/edit/elements/svc_Common.html
+++ b/httemplate/edit/elements/svc_Common.html
@@ -88,30 +88,13 @@
} elsif ( $flag eq 'A' ) {
$f->{'type'} = 'hidden';
} elsif ( $flag eq 'M' ) {
+ $f->{'type'} = 'select-inventory_item';
$f->{'empty_label'} = 'Select inventory item';
- $f->{'type'} = 'select-table';
- $f->{'table'} = 'inventory_item';
- $f->{'name_col'} = 'item';
- $f->{'value_col'} = 'item';
- $f->{'agent_virt'} = 1;
- $f->{'agent_null'} = 1;
- $f->{'hashref'} = {
- 'classnum'=>$columndef->columnvalue,
- #'svcnum' => '',
- };
- $f->{'extra_sql'} = 'AND ( svcnum IS NULL ';
- $f->{'extra_sql'} .= ' OR svcnum = '. $object->svcnum
- if $object->svcnum;
- $f->{'extra_sql'} .= ' ) ';
+ $f->{'extra_sql'} = 'WHERE ( svcnum IS NULL ' .
+ ($object->svcnum && ' OR svcnum = '.$object->svcnum) .
+ ')';
+ $f->{'classnum'} = $columndef->columnvalue;
$f->{'disable_empty'} = $object->svcnum ? 1 : 0;
- if ( $f->{'field'} eq 'mac_addr' ) {
- $f->{'compare_sub'} = sub {
- my($a, $b) = @_;
- $a =~ s/[-: ]//g;
- $b =~ s/[-: ]//g;
- lc($a) eq lc($b);
- };
- }
} elsif ( $flag eq 'H' ) {
$f->{'type'} = 'select-hardware_type';
$f->{'hashref'} = {
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index 8a84b208a..58c237efd 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -1,11 +1,111 @@
-<& /elements/header.html, "$action Service Definition",
- menubar('View all service definitions' => "${p}browse/part_svc.cgi"),
+<& /elements/header.html, "$action Service Definition" &>
+<& /elements/menubar.html,
+ 'View all service definitions' => "${p}browse/part_svc.cgi"
#" onLoad=\"visualize()\""
&>
<& /elements/init_overlib.html &>
-<BR>
+<BR><BR>
+
+<STYLE TYPE="text/css">
+.disabled {
+ background-color: #dddddd;
+}
+.hidden {
+ display: none;
+}
+.enabled {
+ background-color: #ffffff;
+}
+.row0 TD {
+ background-color: #eeeeee;
+}
+.row1 TD {
+ background-color: #ffffff;
+}
+.def_info {
+ text-align: center;
+ padding: 0px;
+ border-top: none;
+ font-size: smaller;
+ font-style: italic;
+}
+</STYLE>
+<SCRIPT TYPE="text/javascript">
+function fixup_submit(layer) {
+ document.forms[layer].submit.disabled = true;
+ fixup(document.forms[layer]);
+ window[layer+'process'].call();
+}
+
+function flag_changed(obj) {
+ var newflag = obj.value;
+ var a = obj.name.match(/(.*)__(.*)_flag/);
+ var layer = a[1];
+ var field = a[2];
+ var input = document.getElementById(layer + '__' + field);
+ // for fields that have both 'input' and 'select', 'select' is 'select from
+ // inventory class'.
+ var select = document.getElementById(layer + '__' + field + '_select');
+ if (newflag == "" || newflag == "X") { // disable
+ if ( input ) {
+ input.disabled = true;
+ input.className = 'disabled';
+ }
+ if ( select ) {
+ select.disabled = true;
+ select.className = 'hidden';
+ }
+ } else if ( newflag == 'D' || newflag == 'F' || newflag == 'S' ) {
+ if ( input ) {
+ // enable text box, disable inventory select
+ input.disabled = false;
+ input.className = 'enabled';
+ if ( select ) {
+ select.disabled = false;
+ select.className = 'hidden';
+ }
+ } else if ( select ) {
+ // enable select
+ select.disabled = false;
+ select.className = 'enabled';
+ if ( newflag == 'S' || select.getAttribute('should_be_multiple') ) {
+ select.multiple = true;
+ } else {
+ select.multiple = false;
+ }
+ }
+ } else if ( newflag == 'M' || newflag == 'A' || newflag == 'H' ) {
+ // these all require a class selection
+ if ( select ) {
+ select.disabled = false;
+ select.className = 'enabled';
+ if ( input ) {
+ input.disabled = false;
+ input.className = 'hidden';
+ }
+ }
+ }
+}
+
+window.onload = function() {
+ var selects = document.getElementsByTagName('SELECT');
+ for(i = 0; i < selects.length; i++) {
+ var obj = selects[i];
+ if ( obj.multiple ) {
+ obj.setAttribute('should_be_multiple', true);
+ }
+ }
+ for(i = 0; i < selects.length; i++) {
+ var obj = selects[i];
+ if ( obj.name.match(/_flag$/) ) {
+ flag_changed(obj);
+ }
+ }
+};
+
+</SCRIPT>
<FORM NAME="dummy">
@@ -53,400 +153,6 @@
<BR>
-% my %vfields;
-% #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 {
-% $_[0]->{disable_inventory}
-% || $_[0]->{'type'} ne 'text'
-% };
-% tie my %flag, 'Tie::IxHash',
-% '' => { 'desc' => 'No default', },
-% 'D' => { 'desc' => 'Default',
-% 'condition' =>
-% sub { $_[0]->{disable_default} },
-% },
-% 'F' => { 'desc' => 'Fixed (unchangeable)',
-% 'condition' =>
-% sub { $_[0]->{disable_fixed} },
-% },
-% 'S' => { 'desc' => 'Selectable Choice',
-% 'condition' =>
-% sub { !ref($_[0]) || $_[0]->{disable_select} },
-% },
-% 'M' => { 'desc' => 'Manual selection from inventory',
-% 'condition' => $inv_sub,
-% },
-% 'A' => { 'desc' => 'Automatically fill in from inventory',
-% 'condition' => $inv_sub,
-% },
-% 'H' => { 'desc' => 'Select from hardware class',
-% 'condition' => sub { $_[0]->{type} ne 'select-hardware' },
-% },
-% 'X' => { 'desc' => 'Excluded',
-% 'condition' =>
-% sub { ! $vfields{$_[1]}->{$_[2]} },
-%
-% },
-% ;
-%
-% my @dbs = $hashref->{svcdb}
-% ? ( $hashref->{svcdb} )
-% : FS::part_svc->svc_tables();
-%
-% my $help = '';
-% unless ( $hashref->{svcpart} ) {
-% $help = '&nbsp;'.
-% include('/elements/popup_link.html',
-% 'action' => $p.'docs/part_svc-table.html',
-% 'label' => 'help',
-% 'actionlabel' => 'Service table help',
-% 'width' => 763,
-% #'height' => 400,
-% );
-% }
-%
-% tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
-% my $widget = new HTML::Widgets::SelectLayers(
-% #'selected_layer' => $p_svcdb,
-% 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
-% 'options' => \%svcdb,
-% 'form_name' => 'dummy',
-% #'form_action' => 'process/part_svc.cgi',
-% 'form_action' => 'part_svc.cgi', #self
-% 'form_elements' => [qw( svc svcpart classnum selfservice_access
-% disabled preserve
-% )],
-% 'html_between' => $help,
-% 'layer_callback' => sub {
-% my $layer = shift;
-%
-% my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
-%
-% #$html .= $svcdb_info;
-%
-% my $columns = 3;
-% my $count = 0;
-% my $communigate = 0;
-% my @part_export =
-% map { qsearch( 'part_export', {exporttype => $_ } ) }
-% keys %{FS::part_export::export_info($layer)};
-% $html .= '<BR><BR>'. include('/elements/table.html') .
-% "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
-% foreach my $part_export ( @part_export ) {
-% $communigate++ if $part_export->exporttype =~ /^communigate/;
-% $html .= '<TD><INPUT TYPE="checkbox"'.
-% ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
-% $html .= 'CHECKED'
-% if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
-% && qsearchs( 'export_svc', {
-% exportnum => $part_export->exportnum,
-% svcpart => $clone || $part_svc->svcpart });
-% $html .= '>'. $part_export->label_html. '</TD>';
-% $count++;
-% $html .= '</TR><TR>' unless $count % $columns;
-% }
-% $html .= '</TR></TABLE><BR><BR>'. $mod_info;
-%
-% $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ).
-% '<TR>'.
-% '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'.
-% '<TH CLASS="grid" BGCOLOR="#cccccc">Label</TH>'.
-% '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'.
-% '</TR>';
-%
-% my $bgcolor1 = '#eeeeee';
-% my $bgcolor2 = '#ffffff';
-% my $bgcolor;
-%
-% #yucky kludge
-% my @fields = ();
-% if ( defined( dbdef->table($layer) ) ) {
-% @fields = grep {
-% $_ ne 'svcnum'
-% && ( $communigate || !$communigate_fields{$layer}->{$_} )
-% && ( !FS::part_svc->svc_table_fields($layer)
-% ->{$_}->{disable_part_svc_column}
-% || $part_svc->part_svc_column($_)->columnflag
-% )
-% } fields($layer);
-% }
-% push @fields, 'usergroup'
-% if $layer eq 'svc_acct'
-% or ( $layer eq 'svc_broadband' and
-% $conf->exists('svc_broadband-radius') ); # double kludge
-% # (but we do want to check the config, right?)
-% $part_svc->svcpart($clone) if $clone; #haha, undone below
-%
-%
-% foreach my $field (@fields) {
-%
-% #a few lines of false laziness w/browse/part_svc.cgi
-% my $def = FS::part_svc->svc_table_fields($layer)->{$field};
-% my $def_info = $def->{'def_info'};
-% my $formatter = $def->{'format'} || sub { shift };
-%
-% my $part_svc_column = $part_svc->part_svc_column($field);
-% my $label = $part_svc_column->columnlabel || $def->{'label'};
-% my $value = &$formatter($part_svc_column->columnvalue);
-% my $flag = $part_svc_column->columnflag;
-%
-% if ( $bgcolor eq $bgcolor1 ) {
-% $bgcolor = $bgcolor2;
-% } else {
-% $bgcolor = $bgcolor1;
-% }
-%
-% $html .= qq!<TR><TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!.
-% ( $def->{'label'} || $field ).
-% "</TD>";
-%
-% $html .= qq!<TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor"><INPUT NAME="${layer}__${field}_label" VALUE="!. encode_entities($label). '" STYLE="text-align:right"></TD>';
-%
-% $flag = '' if $def->{type} eq 'disabled';
-%
-% $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!;
-%
-% if ( $def->{type} eq 'disabled' ) {
-%
-% $html .= 'No default';
-%
-% } else {
-%
-% $html .= qq!<SELECT NAME="${layer}__${field}_flag"!.
-% qq! onChange="${layer}__${field}_flag_changed(this)">!;
-%
-% foreach my $f ( keys %flag ) {
-%
-% # need to template-ize more httemplate/edit/svc_* first
-% next if $f eq 'M' and $layer !~ /^svc_(broadband|external|phone|dish)$/;
-%
-% #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" || f =="S" ) { //enable, text box',
-% " what.form.${layer}__${field}.disabled = false;".
-% " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
-% " if ( f == 'S' || '${field}' == 'usergroup' ) {". # kludge
-% " what.form.${layer}__${field}.multiple = true;".
-% " } else {".
-% " what.form.${layer}__${field}.multiple = false;".
-% " }".
-% " 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" || f == "H" ) { '.
-% '//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>',
-% );
-%
-% }
-%
-% $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!;
-%
-% my $disabled = $flag ? ''
-% : 'DISABLED STYLE="background-color: #dddddd"';
-% my $nodisplay = ' STYLE="display:none"';
-%
-% if ( !$def->{type} || $def->{type} eq 'text' ) {
-%
-% 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",
-% 'id' => "${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 'checkbox' ) {
-%
-% $html .= include('/elements/checkbox.html',
-% 'field' => $layer.'__'.$field,
-% 'curr_value' => $value,
-% 'value' => 'Y',
-% );
-%
-% } elsif ( $def->{type} eq 'select' ) {
-%
-% $html .= qq!<SELECT NAME="${layer}__${field}" $disabled!;
-% $html .= ' MULTIPLE' if $flag eq 'S';
-% $html .= '>';
-% $html .= '<OPTION> </OPTION>' unless $value;
-% if ( $def->{select_table} ) {
-% foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
-% my $rvalue = $record->getfield($def->{select_key});
-% my $select_label = $def->{select_label};
-% $html .= qq!<OPTION VALUE="$rvalue"!.
-% (grep(/^$rvalue$/, split(',',$value)) ? ' SELECTED>' : '>' ).
-% $record->$select_label(). '</OPTION>';
-% } #next $record
-% } elsif ( $def->{select_list} ) {
-% foreach my $item ( @{$def->{select_list}} ) {
-% $html .= qq!<OPTION VALUE="$item"!.
-% (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ).
-% $item. '</OPTION>';
-% } #next $item
-% } elsif ( $def->{select_hash} ) {
-% if ( ref($def->{select_hash}) eq 'ARRAY' ) {
-% tie my %hash, 'Tie::IxHash', @{ $def->{select_hash} };
-% $def->{select_hash} = \%hash;
-% }
-% foreach my $key ( keys %{$def->{select_hash}} ) {
-% $html .= qq!<OPTION VALUE="$key"!.
-% (grep(/^$key$/, split(',',$value)) ? ' SELECTED>' : '>' ).
-% $def->{select_hash}{$key}. '</OPTION>';
-% } #next $key
-% } #endif
-% $html .= '</SELECT>';
-%
-% } elsif ( $def->{type} eq 'textarea' ) {
-%
-% $html .=
-% qq!<TEXTAREA NAME="${layer}__${field}">!. encode_entities($value).
-% '</TEXTAREA>';
-%
-% } elsif ( $def->{type} =~ /select-(.*?).html/ ) {
-%
-% $html .= include("/elements/".$def->{type},
-% 'curr_value' => $value,
-% 'element_name' => "${layer}__${field}",
-% 'element_etc' => $disabled,
-% 'multiple' => ($def->{multiple} ||
-% $flag eq 'S'),
-% # allow the table def to force 'multiple'
-% );
-%
-% } elsif ( $def->{type} eq 'communigate_pro-accessmodes' ) {
-%
-% $html .= include('/elements/communigate_pro-accessmodes.html',
-% 'element_name_prefix' => "${layer}__${field}_",
-% 'curr_value' => $value,
-% #doesn't work#'element_etc' => $disabled,
-% );
-%
-% } elsif ( $def->{type} eq 'select-hardware' ) {
-%
-% $html .= qq!<INPUT TYPE="text" NAME="${layer}__${field}" $disabled>!;
-% $html .= include('/elements/select-hardware_class.html',
-% 'curr_value' => $value,
-% 'element_name' => "${layer}__${field}_classnum",
-% 'id' => "${layer}__${field}_classnum",
-% 'element_etc' => $flag ne 'H' && $nodisplay,
-% 'empty_label' => 'Select hardware class',
-% );
-%
-% } 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";
-
-% $def_info = "($def_info)" if $def_info;
-% $html .=
-% qq!<TR>!.
-% qq! <TD COLSPAN=2 BGCOLOR="$bgcolor" ALIGN="center" !.
-% qq! STYLE="padding:0; border-top: none">!.
-% qq! <FONT SIZE="-1"><I>$def_info</I></FONT>!.
-% qq! </TD>!.
-% qq!</TR>\n!;
-%
-% } #foreach my $field (@fields) {
-%
-% if ( $layer eq 'svc_acct' ) {
-% # eww, more ugly special-caseyness
-% $html .=
-% '<TR><TD COLSPAN=3 ALIGN="right">'.
-% emt('Require "Provision" access right to edit password').
-% '</TD><TD>'.
-% '<INPUT TYPE="checkbox" NAME="restrict_edit_password" VALUE="Y"'.
-% ($part_svc->restrict_edit_password ? ' CHECKED' : '').
-% '></TD></TR>';
-% } else {
-% $html .=
-% '<INPUT TYPE="hidden" NAME="restrict_edit_password" VALUE="">';
-% }
-%
-% $part_svc->svcpart('') if $clone; #undone
-% $html .= "</TABLE>";
-%
-% $html .= include('/elements/progress-init.html',
-% $layer, #form name
-% [ qw(svc svcpart classnum selfservice_access
-% disabled preserve
-% exportnum restrict_edit_password),
-% @fields ],
-% 'process/part_svc.cgi',
-% $p.'browse/part_svc.cgi',
-% $layer,
-% );
-% $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'.
-% ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '.
-% ' onClick="document.'. "$layer.submit.disabled=true; ".
-% "fixup(document.$layer); $layer". 'process();">';
-%
-% #$html .= '<BR><INPUT TYPE="submit" VALUE="'.
-% # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
-%
-% $html;
-%
-% },
-% );
-
<BR>
Table <% $widget->html %>
@@ -479,28 +185,43 @@ my $action = $part_svc->svcpart ? 'Edit' : 'Add';
my $hashref = $part_svc->hashref;
# my $p_svcdb = $part_svc->svcdb || 'svc_acct';
-my %communigate_fields = (
- 'svc_acct' => { map { $_=>1 }
- qw( file_quota file_maxnum file_maxsize
- password_selfchange password_recover
- ),
- grep /^cgp_/, fields('svc_acct')
- },
- 'svc_domain' => { map { $_=>1 }
- qw( max_accounts trailer parent_svcnum ),
- grep /^(cgp|acct_def)_/, fields('svc_domain')
- },
- #'svc_forward' => { map { $_=>1 } qw( ) },
- #'svc_mailinglist' => { map { $_=>1 } qw( ) },
- #'svc_cert' => { map { $_=>1 } qw( ) },
-);
-my $mod_info = '
-For the selected table, you can give fields default or fixed (unchangable)
-values, or select an inventory class to manually or automatically fill in
-that field.
-';
+my @dbs = $hashref->{svcdb}
+ ? ( $hashref->{svcdb} )
+ : FS::part_svc->svc_tables();
+
+my $help = '';
+unless ( $hashref->{svcpart} ) {
+ $help = '&nbsp;'.
+ include('/elements/popup_link.html',
+ 'action' => $p.'docs/part_svc-table.html',
+ 'label' => 'help',
+ 'actionlabel' => 'Service table help',
+ 'width' => 763,
+ #'height' => 400,
+ );
+}
+tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
+my $widget = new HTML::Widgets::SelectLayers(
+ #'selected_layer' => $p_svcdb,
+ 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
+ 'options' => \%svcdb,
+ 'form_name' => 'dummy',
+ #'form_action' => 'process/part_svc.cgi',
+ 'form_action' => 'part_svc.cgi', #self
+ 'form_elements' => [qw( svc svcpart classnum selfservice_access
+ disabled preserve
+ )],
+ 'html_between' => $help,
+ 'layer_callback' => sub {
+ include('elements/part_svc_column.html',
+ shift,
+ 'part_svc' => $part_svc,
+ 'clone' => $clone
+ )
+ }
+);
</%init>
diff --git a/httemplate/edit/process/elements/svc_Common.html b/httemplate/edit/process/elements/svc_Common.html
index 5a8afbd6c..06f4c00b1 100644
--- a/httemplate/edit/process/elements/svc_Common.html
+++ b/httemplate/edit/process/elements/svc_Common.html
@@ -10,5 +10,10 @@ my %opt = @_;
my $table = $opt{'table'};
$opt{'fields'} ||= [ fields($table) ];
push @{ $opt{'fields'} }, qw( pkgnum svcpart );
+foreach (fields($table)) {
+ if ( $cgi->param($_.'_classnum') ) {
+ push @{ $opt{'fields'} }, $_.'_classnum';
+ }
+}
</%init>
diff --git a/httemplate/elements/tr-select-inventory_item.html b/httemplate/elements/tr-select-inventory_item.html
new file mode 100644
index 000000000..669e85f27
--- /dev/null
+++ b/httemplate/elements/tr-select-inventory_item.html
@@ -0,0 +1,48 @@
+% if ( scalar(@classnums) == 0 ) {
+<& tr-fixed.html, %opt &>
+% } elsif ( scalar(@classnums) == 1 ) {
+% $opt{'extra_sql'} .= ' AND '.$classnum_sql;
+<& tr-select-table.html,
+ 'table' => 'inventory_item',
+ 'name_col' => 'item',
+ 'value_col' => 'item',
+ %opt
+&>
+% } else {
+<& tr-td-label.html, %opt &>
+<TD>
+<& select-tiered.html,
+ 'prefix' => $opt{'field'}.'_',
+ 'tiers' => [
+ {
+ field => $opt{'field'}.'_classnum',
+ table => 'inventory_class',
+ extra_sql => "WHERE $classnum_sql",
+ name_col => 'classname',
+ empty_label => '(all)',
+ },
+ {
+ field => $opt{'field'},
+ table => 'inventory_item',
+ name_col => 'item',
+ value_col => 'item',
+ link_col => 'classnum',
+ extra_sql => delete($opt{'extra_sql'}),
+ disable_empty => 1,
+ },
+ ],
+ %opt,
+&>
+</TD>
+</TR>
+% }
+<%init>
+my %opt = @_;
+my @classnums;
+if (ref($opt{'classnum'})) {
+ @classnums = @{ $opt{'classnum'} };
+} else {
+ @classnums = split(',', $opt{'classnum'});
+}
+my $classnum_sql = 'classnum IN('.join(',', @classnums).')';
+</%init>
diff --git a/httemplate/search/elements/svc_Common.html b/httemplate/search/elements/svc_Common.html
new file mode 100644
index 000000000..56c75bba3
--- /dev/null
+++ b/httemplate/search/elements/svc_Common.html
@@ -0,0 +1,48 @@
+<& search.html, %opt &>
+<%doc>
+Currently does nothing but insert the classnames for fields chosen from an
+inventory class.
+</%doc>
+<%init>
+my %opt = @_;
+my $query = $opt{query};
+my $svcdb = $query->{'table'};
+
+# to avoid looking up the inventory class of every service in the database,
+# keep as much of the base query as possible.
+my $item_query = { %$query };
+$item_query->{'table'} = 'inventory_item';
+$item_query->{'addl_from'} =
+ " JOIN ( $svcdb ". $query->{'addl_from'} .
+ ") ON inventory_item.svcnum = $svcdb.svcnum ".
+ " JOIN inventory_class ON (inventory_item.classnum = inventory_class.classnum)";
+# avoid conflict with inventory_item.agentnum
+$item_query->{'extra_sql'} =~ s/ agentnum/ cust_main.agentnum/g;
+$item_query->{'select'} = 'inventory_item.svcnum, '.
+ 'inventory_item.svc_field, '.
+ 'inventory_class.classname';
+my @items = qsearch($item_query);
+my %item_fields;
+foreach my $i (@items) {
+ $item_fields{ $i->svc_field } ||= {};
+ $item_fields{ $i->svc_field }{ $i->svcnum } = $i->classname;
+}
+
+$opt{'sort_fields'} ||= [];
+for ( my $i = 0; $i < @{ $opt{'fields'} }; $i++ ) {
+ my $f = $opt{'fields'}[$i];
+ next if ref($f); # it's not a plain table column
+ $opt{'sort_fields'}[$i] ||= $f;
+ my $classnames = $item_fields{$f}; # hashref of svcnum -> classname
+ next if !$classnames; # there are no inventory items in this column
+ $opt{'fields'}[$i] = sub {
+ my $svc = $_[0];
+ if ( exists($classnames->{$svc->svcnum}) ) {
+ return $svc->$f . '<BR><I>('. $classnames->{$svc->svcnum} . ')</I>';
+ } else {
+ return $svc->$f;
+ }
+ }; #sub
+}
+
+</%init>
diff --git a/httemplate/search/svc_acct.cgi b/httemplate/search/svc_acct.cgi
index 92e1c500c..b9e5a7cc9 100755
--- a/httemplate/search/svc_acct.cgi
+++ b/httemplate/search/svc_acct.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => emt('Account Search Results'),
'name' => emt('accounts'),
'query' => $sql_query,
diff --git a/httemplate/search/svc_broadband.cgi b/httemplate/search/svc_broadband.cgi
index c22887e08..8366d214b 100755
--- a/httemplate/search/svc_broadband.cgi
+++ b/httemplate/search/svc_broadband.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => 'Broadband Search Results',
'name' => 'broadband services',
'html_init' => $html_init,
diff --git a/httemplate/search/svc_dish.cgi b/httemplate/search/svc_dish.cgi
index 1578aa1e3..1f8cbc395 100755
--- a/httemplate/search/svc_dish.cgi
+++ b/httemplate/search/svc_dish.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => 'Dish Network Search Results',
'name' => 'services',
'query' => $sql_query,
diff --git a/httemplate/search/svc_external.cgi b/httemplate/search/svc_external.cgi
index 19099878a..b282939a7 100755
--- a/httemplate/search/svc_external.cgi
+++ b/httemplate/search/svc_external.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => 'External service search results',
'name' => 'external services',
'query' => $sql_query,
diff --git a/httemplate/search/svc_hardware.cgi b/httemplate/search/svc_hardware.cgi
index 28aa13217..93fc2c391 100644
--- a/httemplate/search/svc_hardware.cgi
+++ b/httemplate/search/svc_hardware.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => 'Hardware service search results',
'name' => 'installations',
'query' => $sql_query,
diff --git a/httemplate/search/svc_phone.cgi b/httemplate/search/svc_phone.cgi
index cc4aa60f5..f3a056475 100644
--- a/httemplate/search/svc_phone.cgi
+++ b/httemplate/search/svc_phone.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => "Phone number search results",
'name' => 'phone numbers',
'query' => $sql_query,
diff --git a/httemplate/search/svc_www.cgi b/httemplate/search/svc_www.cgi
index eefe89302..7410262e8 100755
--- a/httemplate/search/svc_www.cgi
+++ b/httemplate/search/svc_www.cgi
@@ -1,4 +1,4 @@
-<& elements/search.html,
+<& elements/svc_Common.html,
'title' => 'Virtual Host Search Results',
'name' => 'virtual hosts',
'query' => $sql_query,
diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html
index a7f8f6df4..d735195fe 100644
--- a/httemplate/view/elements/svc_Common.html
+++ b/httemplate/view/elements/svc_Common.html
@@ -57,6 +57,7 @@ function areyousure(href) {
<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+% my @inventory_items = $svc_x->inventory_item;
% foreach my $f ( @$fields ) {
%
% my($field, $type, $value);
@@ -76,6 +77,14 @@ function areyousure(href) {
% }
%
% my $columndef = $part_svc->part_svc_column($field);
+% if ( $columndef->columnflag =~ /^[MA]$/ && $columndef->columnvalue =~ /,/ )
+% {
+% # inventory-select field with multiple classes
+% # show the class name to disambiguate
+% my ($item) = grep { $_->svc_field eq $field } @inventory_items;
+% my $class = qsearchs('inventory_class', { classnum => $item->classnum });
+% $value .= ' <i>('. $class->classname . ')</i>' if $class;
+% }
% unless ($columndef->columnflag eq 'F' && !length($columndef->columnvalue)) {
<TR>
diff --git a/httemplate/view/svc_hardware.cgi b/httemplate/view/svc_hardware.cgi
index aa3ff0091..eef1c1140 100644
--- a/httemplate/view/svc_hardware.cgi
+++ b/httemplate/view/svc_hardware.cgi
@@ -13,6 +13,9 @@ my %labels = map { $_ => ( ref($fields->{$_})
: $fields->{$_}
);
} keys %$fields;
+
+$labels{'display_hw_addr'} = 'Hardware address';
+
my $model = { field => 'typenum',
type => 'text',
value_callback => sub { $_[0]->hardware_type->description }