1 <% include('/elements/header.html', "$action Service Definition",
2 menubar('View all service definitions' => "${p}browse/part_svc.cgi"),
3 #" onLoad=\"visualize()\""
9 Service Part #<% $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
11 Service <INPUT TYPE="text" NAME="svc" VALUE="<% $hashref->{svc} %>"><BR>
12 Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR>
13 <INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $hashref->{svcpart} %>">
15 Service definitions are the templates for items you offer to your customers.
16 <UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.)
17 <LI>svc_domain - Domains
18 <LI>svc_cert - Certificates
19 <LI>svc_forward - Mail forwarding
20 <LI>svc_mailinglist - Mailing list
21 <LI>svc_www - Virtual domain website
22 <LI>svc_broadband - Broadband/High-speed Internet service (always-on)
23 <LI>svc_phone - Customer phone numbers
24 <LI>svc_pbx - Customer PBXs
25 <LI>svc_external - Externally-tracked service
26 <!-- <LI>svc_charge - One-time charges (Partially unimplemented)
27 <LI>svc_wo - Work orders (Partially unimplemented)
30 For the selected table, you can give fields default or fixed (unchangable)
31 values, or select an inventory class to manually or automatically fill in
35 % #YUCK. false laziness w/part_svc.pm. go away virtual fields, please
37 % foreach my $svcdb ( FS::part_svc->svc_tables() ) {
38 % eval "use FS::$svcdb;";
39 % my $self = "FS::$svcdb"->new;
40 % $vfields{$svcdb} = {};
41 % foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
42 % my $pvf = $self->pvf($field);
43 % $vfields{$svcdb}->{$field} = $pvf;
44 % #warn "\$vfields{$svcdb}->{$field} = $pvf";
48 % #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm
49 % # and generalize the subs
50 % # condition sub is tested to see whether to disable display of this choice
51 % # params: ( $def, $layer, $field ) (see SUB below)
53 % $_[0]->{disable_inventory}
54 % || $_[0]->{'type'} ne 'text'
56 % tie my %flag, 'Tie::IxHash',
57 % '' => { 'desc' => 'No default', },
58 % 'D' => { 'desc' => 'Default',
60 % sub { $_[0]->{disable_default} },
62 % 'F' => { 'desc' => 'Fixed (unchangeable)',
64 % sub { $_[0]->{disable_fixed} },
66 % 'S' => { 'desc' => 'Selectable Choice',
68 % sub { !ref($_[0]) || $_[0]->{disable_select} },
70 % 'M' => { 'desc' => 'Manual selection from inventory',
71 % 'condition' => $inv_sub,
73 % 'A' => { 'desc' => 'Automatically fill in from inventory',
74 % 'condition' => $inv_sub,
76 % 'X' => { 'desc' => 'Excluded',
78 % sub { ! $vfields{$_[1]}->{$_[2]} },
83 % my @dbs = $hashref->{svcdb}
84 % ? ( $hashref->{svcdb} )
85 % : FS::part_svc->svc_tables();
87 % tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
88 % my $widget = new HTML::Widgets::SelectLayers(
89 % #'selected_layer' => $p_svcdb,
90 % 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
91 % 'options' => \%svcdb,
92 % 'form_name' => 'dummy',
93 % #'form_action' => 'process/part_svc.cgi',
94 % 'form_action' => 'part_svc.cgi', #self
95 % 'form_text' => [ qw( svc svcpart ) ],
96 % 'form_checkbox' => [ 'disabled' ],
97 % 'layer_callback' => sub {
100 % my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
104 % my $communigate = 0;
106 % map { qsearch( 'part_export', {exporttype => $_ } ) }
107 % keys %{FS::part_export::export_info($layer)};
108 % $html .= '<BR><BR>'. table().
109 % "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
110 % foreach my $part_export ( @part_export ) {
111 % $communigate++ if $part_export->exporttype =~ /^communigate/;
112 % $html .= '<TD><INPUT TYPE="checkbox"'.
113 % ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
115 % if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
116 % && qsearchs( 'export_svc', {
117 % exportnum => $part_export->exportnum,
118 % svcpart => $clone || $part_svc->svcpart });
119 % $html .= '>'.$part_export->exportnum. ': ';
120 % $html .= $part_export->exportname . '<DIV ALIGN="right"><FONT SIZE=-1>'
121 % if ( $part_export->exportname );
122 % $html .= $part_export->exporttype. ' to '. $part_export->machine;
123 % $html .= '</FONT></DIV>' if ( $part_export->exportname );
126 % $html .= '</TR><TR>' unless $count % $columns;
128 % $html .= '</TR></TABLE><BR><BR>';
130 % $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ).
132 % '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'.
133 % '<TH CLASS="grid" BGCOLOR="#cccccc">Label</TH>'.
134 % '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'.
137 % my $bgcolor1 = '#eeeeee';
138 % my $bgcolor2 = '#ffffff';
143 % if ( defined( dbdef->table($layer) ) ) {
146 % && ( $communigate || !$communigate_fields{$layer}->{$_} )
147 % && ( !FS::part_svc->svc_table_fields($layer)
148 % ->{$_}->{disable_part_svc_column}
149 % || $part_svc->part_svc_column($_)->columnflag
153 % push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge
154 % $part_svc->svcpart($clone) if $clone; #haha, undone below
157 % foreach my $field (@fields) {
159 % #a few lines of false laziness w/browse/part_svc.cgi
160 % my $def = FS::part_svc->svc_table_fields($layer)->{$field};
161 % my $def_info = $def->{'def_info'};
162 % my $formatter = $def->{'format'} || sub { shift };
164 % my $part_svc_column = $part_svc->part_svc_column($field);
165 % my $label = $part_svc_column->columnlabel || $def->{'label'};
166 % my $value = &$formatter($part_svc_column->columnvalue);
167 % my $flag = $part_svc_column->columnflag;
169 % if ( $bgcolor eq $bgcolor1 ) {
170 % $bgcolor = $bgcolor2;
172 % $bgcolor = $bgcolor1;
175 % $html .= qq!<TR><TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!.
176 % ( $def->{'label'} || $field ).
179 % $html .= qq!<TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor"><INPUT NAME="${layer}__${field}_label" VALUE="!. encode_entities($label). '" STYLE="text-align:right"></TD>';
181 % $flag = '' if $def->{type} eq 'disabled';
183 % $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!;
185 % if ( $def->{type} eq 'disabled' ) {
187 % $html .= 'No default';
191 % $html .= qq!<SELECT NAME="${layer}__${field}_flag"!.
192 % qq! onChange="${layer}__${field}_flag_changed(this)">!;
194 % foreach my $f ( keys %flag ) {
196 % # need to template-ize more httemplate/edit/svc_* first
197 % next if $f eq 'M' and $layer !~ /^svc_(broadband|external|phone)$/;
199 % #here is where the SUB from above is called, to skip some choices
200 % next if $flag{$f}->{condition}
201 % && &{ $flag{$f}->{condition} }( $def, $layer, $field );
203 % $html .= qq!<OPTION VALUE="$f"!.
204 % ' SELECTED'x($flag eq $f ).
205 % '>'. $flag{$f}->{desc};
209 % $html .= '</SELECT>';
211 % $html .= join("\n",
213 % " function ${layer}__${field}_flag_changed(what) {",
214 % ' var f = what.options[what.selectedIndex].value;',
215 % ' if ( f == "" || f == "X" ) { //disable',
216 % " what.form.${layer}__${field}.disabled = true;".
217 % " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';".
218 % " if ( what.form.${layer}__${field}_classnum ) {".
219 % " what.form.${layer}__${field}_classnum.disabled = true;".
220 % " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';".
222 % ' } else if ( f == "D" || f == "F" || f =="S" ) { //enable, text box',
223 % " what.form.${layer}__${field}.disabled = false;".
224 % " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
225 % " if ( f == 'S' || '${field}' == 'usergroup' ) {". # kludge
226 % " what.form.${layer}__${field}.multiple = true;".
228 % " what.form.${layer}__${field}.multiple = false;".
230 % " what.form.${layer}__${field}.style.display = '';".
231 % " if ( what.form.${layer}__${field}_classnum ) {".
232 % " what.form.${layer}__${field}_classnum.disabled = false;".
233 % " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
234 % " what.form.${layer}__${field}_classnum.style.display = 'none';".
236 % ' } else if ( f == "M" || f == "A" ) { //enable, inventory',
237 % " what.form.${layer}__${field}.disabled = false;".
238 % " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
239 % " what.form.${layer}__${field}.style.display = 'none';".
240 % " if ( what.form.${layer}__${field}_classnum ) {".
241 % " what.form.${layer}__${field}_classnum.disabled = false;".
242 % " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
243 % " what.form.${layer}__${field}_classnum.style.display = '';".
252 % $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!;
254 % my $disabled = $flag ? ''
255 % : 'DISABLED STYLE="background-color: #dddddd"';
257 % if ( !$def->{type} || $def->{type} eq 'text' ) {
259 % my $nodisplay = ' STYLE="display:none"';
260 % my $is_inv = ( $flag =~ /^[MA]$/ );
263 % qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !.
265 % ( $is_inv ? $nodisplay : $disabled ).
268 % $html .= include('/elements/select-table.html',
269 % 'element_name' => "${layer}__${field}_classnum",
270 % 'element_etc' => ( $is_inv
274 % 'table' => 'inventory_class',
275 % 'name_col' => 'classname',
277 % 'empty_label' => 'Select inventory class',
280 % } elsif ( $def->{type} eq 'checkbox' ) {
282 % $html .= include('/elements/checkbox.html',
283 % 'field' => $layer.'__'.$field,
284 % 'curr_value' => $value,
288 % } elsif ( $def->{type} eq 'select' ) {
290 % $html .= qq!<SELECT NAME="${layer}__${field}" $disabled!;
291 % $html .= ' MULTIPLE' if $flag eq 'S';
293 % $html .= '<OPTION> </OPTION>' unless $value;
294 % if ( $def->{select_table} ) {
295 % foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
296 % my $rvalue = $record->getfield($def->{select_key});
297 % my $select_label = $def->{select_label};
298 % $html .= qq!<OPTION VALUE="$rvalue"!.
299 % (grep(/^$rvalue$/, split(',',$value)) ? ' SELECTED>' : '>' ).
300 % $record->$select_label(). '</OPTION>';
302 % } elsif ( $def->{select_list} ) {
303 % foreach my $item ( @{$def->{select_list}} ) {
304 % $html .= qq!<OPTION VALUE="$item"!.
305 % (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ).
306 % $item. '</OPTION>';
308 % } elsif ( $def->{select_hash} ) {
309 % if ( ref($def->{select_hash}) eq 'ARRAY' ) {
310 % tie my %hash, 'Tie::IxHash', @{ $def->{select_hash} };
311 % $def->{select_hash} = \%hash;
313 % foreach my $key ( keys %{$def->{select_hash}} ) {
314 % $html .= qq!<OPTION VALUE="$key"!.
315 % (grep(/^$key$/, split(',',$value)) ? ' SELECTED>' : '>' ).
316 % $def->{select_hash}{$key}. '</OPTION>';
319 % $html .= '</SELECT>';
321 % } elsif ( $def->{type} eq 'textarea' ) {
324 % qq!<TEXTAREA NAME="${layer}__${field}">!. encode_entities($value).
327 % } elsif ( $def->{type} eq 'select-svc_pbx.html' ) {
329 % $html .= include('/elements/select-svc_pbx.html',
330 % 'curr_value' => $value,
331 % 'element_name' => "${layer}__${field}",
332 % 'element_etc' => $disabled,
333 % 'multiple' => ($flag eq 'S'),
336 % } elsif ( $def->{type} eq 'select-lnp_status.html' ) {
338 % $html .= include('/elements/select-lnp_status.html',
339 % 'curr_value' => $value,
340 % 'element_name' => "${layer}__${field}",
341 % 'element_etc' => $disabled,
342 % 'multiple' => ($flag eq 'S'),
345 % } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
347 % #XXX disable the RADIUS usergroup selector? ugh it sure does need
348 % #an overhaul, people have dum group problems because of it
350 % $html .= FS::svc_acct::radius_usergroup_selector(
351 % [ split(',', $value) ], "${layer}__${field}" );
353 % } elsif ( $def->{type} eq 'communigate_pro-accessmodes' ) {
355 % $html .= include('/elements/communigate_pro-accessmodes.html',
356 % 'element_name_prefix' => "${layer}__${field}_",
357 % 'curr_value' => $value,
358 % #doesn't work#'element_etc' => $disabled,
361 % } elsif ( $def->{type} eq 'disabled' ) {
364 % qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!;
368 % $html .= '<font color="#ff0000">unknown type '. $def->{type};
372 % $html .= "</TD></TR>\n";
374 % $def_info = "($def_info)" if $def_info;
377 % qq! <TD COLSPAN=2 BGCOLOR="$bgcolor" ALIGN="center" !.
378 % qq! STYLE="padding:0; border-top: none">!.
379 % qq! <FONT SIZE="-1"><I>$def_info</I></FONT>!.
383 % } #foreach my $field (@fields) {
385 % $part_svc->svcpart('') if $clone; #undone
386 % $html .= "</TABLE>";
388 % $html .= include('/elements/progress-init.html',
390 % [ qw(svc svcpart disabled exportnum), @fields ],
391 % 'process/part_svc.cgi',
392 % $p.'browse/part_svc.cgi',
395 % $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'.
396 % ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '.
397 % ' onClick="document.'. "$layer.submit.disabled=true; ".
398 % "fixup(document.$layer); $layer". 'process();">';
400 % #$html .= '<BR><INPUT TYPE="submit" VALUE="'.
401 % # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
410 Table <% $widget->html %>
412 <% include('/elements/footer.html') %>
417 unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
421 if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone
422 #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query";
423 $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } )
424 or die "unknown svcpart: $1";
425 $clone = $part_svc->svcpart;
426 $part_svc->svcpart('');
427 } elsif ( $cgi->keywords ) { #edit
428 my($query) = $cgi->keywords;
429 $query =~ /^(\d+)$/ or die "malformed query: $query";
430 $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } )
431 or die "unknown svcpart: $1";
433 $part_svc = new FS::part_svc {};
436 my $action = $part_svc->svcpart ? 'Edit' : 'Add';
437 my $hashref = $part_svc->hashref;
438 # my $p_svcdb = $part_svc->svcdb || 'svc_acct';
440 my %communigate_fields = (
441 'svc_acct' => { map { $_=>1 }
442 qw( file_quota file_maxnum file_maxsize
443 password_selfchange password_recover
445 grep /^cgp_/, fields('svc_acct')
447 'svc_domain' => { map { $_=>1 }
448 qw( max_accounts trailer parent_svcnum ),
449 grep /^(cgp|acct_def)_/, fields('svc_domain')
451 #'svc_forward' => { map { $_=>1 } qw( ) },
452 #'svc_mailinglist' => { map { $_=>1 } qw( ) },
453 #'svc_cert' => { map { $_=>1 } qw( ) },