4 if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone
5 #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query";
6 $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } )
7 or die "unknown svcpart: $1";
8 $clone = $part_svc->svcpart;
9 $part_svc->svcpart('');
10 } elsif ( $cgi->keywords ) { #edit
11 my($query) = $cgi->keywords;
12 $query =~ /^(\d+)$/ or die "malformed query: $query";
13 $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } )
14 or die "unknown svcpart: $1";
16 $part_svc = new FS::part_svc {};
19 my $action = $part_svc->svcpart ? 'Edit' : 'Add';
20 my $hashref = $part_svc->hashref;
21 # my $p_svcdb = $part_svc->svcdb || 'svc_acct';
24 #" onLoad=\"visualize()\""
26 <%= include("/elements/header.html","$action Service Definition",
27 menubar( 'Main Menu' => $p,
28 'View all service definitions' => "${p}browse/part_svc.cgi"
35 Service Part #<%= $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
37 Service <INPUT TYPE="text" NAME="svc" VALUE="<%= $hashref->{svc} %>"><BR>
38 Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR>
39 <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $hashref->{svcpart} %>">
41 Service definitions are the templates for items you offer to your customers.
42 <UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.)
43 <LI>svc_domain - Domains
44 <LI>svc_forward - mail forwarding
45 <LI>svc_www - Virtual domain website
46 <LI>svc_broadband - Broadband/High-speed Internet service (always-on)
47 <LI>svc_phone - Customer phone numbers
48 <LI>svc_external - Externally-tracked service
49 <!-- <LI>svc_charge - One-time charges (Partially unimplemented)
50 <LI>svc_wo - Work orders (Partially unimplemented)
53 For the selected table, you can give fields default or fixed (unchangable)
54 values, or select an inventory class to manually or automatically fill in
60 #these might belong somewhere else for other user interfaces
61 #pry need to eventually create stuff that's shared amount UIs
62 my $conf = new FS::Conf;
66 'dir' => 'Home directory',
67 'uid' => 'UID (set to fixed and blank for no UIDs)',
68 'slipip' => 'IP address',
69 # 'popnum' => qq!<A HREF="$p/browse/svc_acct_pop.cgi/">POP number</A>!,
71 desc => 'Access number',
73 select_table => 'svc_acct_pop',
74 select_key => 'popnum',
75 select_label => 'city',
86 disable_inventory => 1,
88 '_password' => 'Password',
89 'gid' => 'GID (when blank, defaults to UID)',
91 #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)',
92 desc =>'Shell ( set to blank for no shell tracking)',
94 select_list => [ $conf->config('shells') ],
95 disable_inventory => 1,
97 'finger' => 'Real name (GECOS)',
99 desc =>'svcnum from svc_domain',
101 select_table => 'svc_domain',
102 select_key => 'svcnum',
103 select_label => 'domain',
104 disable_inventory => 1,
107 desc =>'RADIUS groups',
108 type =>'radius_usergroup_selector',
109 disable_inventory => 1,
111 'seconds' => { desc => '',
113 disable_inventory => 1,
118 'domain' => 'Domain',
122 'srcsvc' => 'service from which mail is to be forwarded',
123 'dstsvc' => 'service to which mail is to be forwarded',
124 'dst' => 'someone@another.domain.com to use when dstsvc is 0',
128 # 'amount' => 'amount',
131 # 'worker' => 'Worker',
141 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
142 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
143 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
144 'blocknum' => 'Address block.',
148 'countrycode' => { desc => 'Country code',
150 disable_inventory => 1,
152 'phonenum' => 'Phone number',
153 'pin' => { desc => 'Personal Identification Number',
155 disable_inventory => 1,
167 foreach my $svcdb (grep dbdef->table($_), keys %defs ) {
168 my $self = "FS::$svcdb"->new;
169 $vfields{$svcdb} = {};
170 foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
171 my $pvf = $self->pvf($field);
172 my @list = $pvf->list;
174 $defs{$svcdb}->{$field} = { desc => $pvf->label,
176 select_list => \@list };
178 $defs{$svcdb}->{$field} = $pvf->label;
180 $vfields{$svcdb}->{$field} = $pvf;
181 warn "\$vfields{$svcdb}->{$field} = $pvf";
185 #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm
186 # and generalize the subs
187 # condition sub is tested to see whether to disable display of this choice
188 # params: ( $def, $layer, $field ) (see SUB below)
190 ref($_[0]) && ( $_[0]->{disable_inventory}
191 || $_[0]->{'type'} ne 'text' )
193 tie my %flag, 'Tie::IxHash',
194 '' => { 'desc' => 'No default', },
195 'D' => { 'desc' => 'Default',
197 sub { ref($_[0]) && $_[0]->{disable_default} },
199 'F' => { 'desc' => 'Fixed (unchangeable)',
201 sub { ref($_[0]) && $_[0]->{disable_fixed} },
203 # need to template-ize httemplate/edit/svc_* first
204 # 'M' => { 'desc' => 'Manual selection from inventory',
205 # 'condition' => $inv_sub,
207 'A' => { 'desc' => 'Automatically fill in from inventory',
208 'condition' => $inv_sub,
210 'X' => { 'desc' => 'Excluded',
212 sub { ! $vfields{$_[1]}->{$_[2]} },
217 my @dbs = $hashref->{svcdb}
218 ? ( $hashref->{svcdb} )
219 : qw( svc_acct svc_domain svc_forward svc_www svc_broadband svc_phone svc_external );
221 tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
222 my $widget = new HTML::Widgets::SelectLayers(
223 #'selected_layer' => $p_svcdb,
224 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
225 'options' => \%svcdb,
226 'form_name' => 'dummy',
227 #'form_action' => 'process/part_svc.cgi',
228 'form_action' => 'part_svc.cgi', #self
229 'form_text' => [ qw( svc svcpart ) ],
230 'form_checkbox' => [ 'disabled' ],
231 'layer_callback' => sub {
234 my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
239 map { qsearch( 'part_export', {exporttype => $_ } ) }
240 keys %{FS::part_export::export_info($layer)};
241 $html .= '<BR><BR>'. table().
242 "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
243 foreach my $part_export ( @part_export ) {
244 $html .= '<TD><INPUT TYPE="checkbox"'.
245 ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
247 if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
248 && qsearchs( 'export_svc', {
249 exportnum => $part_export->exportnum,
250 svcpart => $clone || $part_svc->svcpart });
251 $html .= '>'. $part_export->exportnum. ': '. $part_export->exporttype.
252 ' to '. $part_export->machine. '</TD>';
254 $html .= '</TR><TR>' unless $count % $columns;
256 $html .= '</TR></TABLE><BR><BR>';
258 $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ).
260 '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'.
261 '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'.
264 my $bgcolor1 = '#eeeeee';
265 my $bgcolor2 = '#ffffff';
269 my @fields = defined( dbdef->table($layer) )
270 ? grep { $_ ne 'svcnum' } fields($layer)
272 push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge
273 $part_svc->svcpart($clone) if $clone; #haha, undone below
276 foreach my $field (@fields) {
278 my $part_svc_column = $part_svc->part_svc_column($field);
279 my $value = $part_svc_column->columnvalue;
280 my $flag = $part_svc_column->columnflag;
281 my $def = $defs{$layer}{$field};
282 my $desc = ref($def) ? $def->{desc} : $def;
284 if ( $bgcolor eq $bgcolor1 ) {
285 $bgcolor = $bgcolor2;
287 $bgcolor = $bgcolor1;
290 $html .= qq!<TR><TD CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!.
292 $html .= "- <FONT SIZE=-1>$desc</FONT>" if $desc;
294 $flag = '' if ref($def) && $def->{type} eq 'disabled';
296 $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!;
298 if ( ref($def) && $def->{type} eq 'disabled' ) {
300 $html .= 'No default';
304 $html .= qq!<SELECT NAME="${layer}__${field}_flag"!.
305 qq! onChange="${layer}__${field}_flag_changed(this)">!;
307 foreach my $f ( keys %flag ) {
309 #here is where the SUB from above is called, to skip some choices
310 next if $flag{$f}->{condition}
311 && &{ $flag{$f}->{condition} }( $def, $layer, $field );
313 $html .= qq!<OPTION VALUE="$f"!.
314 ' SELECTED'x($flag eq $f ).
315 '>'. $flag{$f}->{desc};
319 $html .= '</SELECT>';
323 " function ${layer}__${field}_flag_changed(what) {",
324 ' var f = what.options[what.selectedIndex].value;',
325 ' if ( f == "" || f == "X" ) { //disable',
326 " what.form.${layer}__${field}.disabled = true;".
327 " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';".
328 " if ( what.form.${layer}__${field}_classnum ) {".
329 " what.form.${layer}__${field}_classnum.disabled = true;".
330 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';".
332 ' } else if ( f == "D" || f == "F" ) { //enable, text box',
333 " what.form.${layer}__${field}.disabled = false;".
334 " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
335 " what.form.${layer}__${field}.style.display = '';".
336 " if ( what.form.${layer}__${field}_classnum ) {".
337 " what.form.${layer}__${field}_classnum.disabled = false;".
338 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
339 " what.form.${layer}__${field}_classnum.style.display = 'none';".
341 ' } else if ( f == "M" || f == "A" ) { //enable, inventory',
342 " what.form.${layer}__${field}.disabled = false;".
343 " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
344 " what.form.${layer}__${field}.style.display = 'none';".
345 " if ( what.form.${layer}__${field}_classnum ) {".
346 " what.form.${layer}__${field}_classnum.disabled = false;".
347 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
348 " what.form.${layer}__${field}_classnum.style.display = '';".
357 $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!;
359 my $disabled = $flag ? ''
360 : 'DISABLED STYLE="background-color: #dddddd"';
362 if ( ! ref($def) || $def->{type} eq 'text' ) {
364 my $nodisplay = ' STYLE="display:none"';
365 my $is_inv = ( $flag =~ /^[MA]$/ );
368 qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !.
370 ( $is_inv ? $nodisplay : $disabled ).
373 $html .= include('/elements/select-table.html',
374 'element_name' => "${layer}__${field}_classnum",
375 'element_etc' => ( $is_inv
379 'table' => 'inventory_class',
380 'name_col' => 'classname',
382 'empty_label' => 'Select inventory class',
385 } elsif ( $def->{type} eq 'select' ) {
387 $html .= qq!<SELECT NAME="${layer}__${field}" $disabled>!;
388 $html .= '<OPTION> </OPTION>' unless $value;
389 if ( $def->{select_table} ) {
390 foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
391 my $rvalue = $record->getfield($def->{select_key});
392 $html .= qq!<OPTION VALUE="$rvalue"!.
393 ( $rvalue==$value ? ' SELECTED>' : '>' ).
394 $record->getfield($def->{select_label}). '</OPTION>';
396 } else { # select_list
397 foreach my $item ( @{$def->{select_list}} ) {
398 $html .= qq!<OPTION VALUE="$item"!.
399 ( $item eq $value ? ' SELECTED>' : '>' ).
403 $html .= '</SELECT>';
405 } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
407 #XXX disable the RADIUS usergroup selector? ugh it sure does need
408 #an overhaul, people have dum group problems because of it
410 $html .= FS::svc_acct::radius_usergroup_selector(
411 [ split(',', $value) ], "${layer}__${field}" );
413 } elsif ( $def->{type} eq 'disabled' ) {
416 qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!;
420 $html .= '<font color="#ff0000">unknown type'. $def->{type};
424 $html .= "</TD></TR>\n";
426 } #foreach my $field (@fields) {
428 $part_svc->svcpart('') if $clone; #undone
431 $html .= include('/elements/progress-init.html',
433 [ qw(svc svcpart disabled exportnum), @fields ],
434 'process/part_svc.cgi',
435 $p.'browse/part_svc.cgi',
438 $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'.
439 ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '.
440 ' onClick="document.'. "$layer.submit.disabled=true; ".
441 "fixup(document.$layer); $layer". 'process();">';
443 #$html .= '<BR><INPUT TYPE="submit" VALUE="'.
444 # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
452 Table <%= $widget->html %>