summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkhoff <khoff>2003-08-04 23:47:48 +0000
committerkhoff <khoff>2003-08-04 23:47:48 +0000
commita61b1651b6cee90f6195d8ac59da657d8966567d (patch)
treeef6ecf0fbe4d34967d319c4fd6a5be077bf7883a
parent5b48183ad64fb7ea119c994ed1a921fcda4a0f9d (diff)
Virtual field documentation updates.virtual_fields
Removed sb_field/router_field and friends (yay!). Interface changes to support virtual fields.
-rwxr-xr-xFS/FS/part_router_field.pm134
-rwxr-xr-xFS/FS/part_sb_field.pm267
-rwxr-xr-xFS/FS/part_virtual_field.pm303
-rwxr-xr-xFS/FS/router_field.pm146
-rwxr-xr-xFS/FS/sb_field.pm148
-rw-r--r--FS/MANIFEST5
-rwxr-xr-xFS/bin/freeside-setup49
-rw-r--r--htetc/global.asa5
-rw-r--r--htetc/handler.pl5
-rw-r--r--htetc/handler.pl-1.0x5
-rw-r--r--httemplate/browse/part_sb_field.cgi31
-rw-r--r--httemplate/browse/part_virtual_field.cgi39
-rw-r--r--httemplate/docs/upgrade10.html54
-rw-r--r--httemplate/edit/part_router_field.cgi71
-rw-r--r--httemplate/edit/part_sb_field.cgi79
-rw-r--r--httemplate/edit/part_virtual_field.cgi92
-rw-r--r--httemplate/edit/process/router.cgi41
-rw-r--r--httemplate/edit/process/svc_broadband.cgi36
-rwxr-xr-xhttemplate/edit/router.cgi4
-rwxr-xr-xhttemplate/view/svc_acct.cgi5
-rw-r--r--httemplate/view/svc_broadband.cgi21
21 files changed, 466 insertions, 1074 deletions
diff --git a/FS/FS/part_router_field.pm b/FS/FS/part_router_field.pm
deleted file mode 100755
index 73ca50f..0000000
--- a/FS/FS/part_router_field.pm
+++ /dev/null
@@ -1,134 +0,0 @@
-package FS::part_router_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::router_field;
-use FS::router;
-
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::part_router_field - Object methods for part_router_field records
-
-=head1 SYNOPSIS
-
- use FS::part_router_field;
-
- $record = new FS::part_router_field \%hash;
- $record = new FS::part_router_field { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-A part_router_field represents an xfield definition for routers. For more
-information on xfields, see L<FS::part_sb_field>.
-
-The following fields are supported:
-
-=over 4
-
-=item routerfieldpart - primary key (assigned automatically)
-
-=item name - name of field
-
-=item length
-
-=item check_block
-
-=item list_source
-
-(See L<FS::part_sb_field> for details on these fields.)
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'part_router_field'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my $self = shift;
- my $error = '';
-
- $self->name =~ /^([a-z0-9_\-\.]{1,15})$/i
- or return "Invalid field name for part_router_field";
-
- ''; #no error
-}
-
-=item list_values
-
-Equivalent to "eval($part_router_field->list_source)".
-
-=cut
-
-sub list_values {
- my $self = shift;
- return () unless $self->list_source;
- my @opts = eval($self->list_source);
- if($@) {
- warn $@;
- return ();
- } else {
- return @opts;
- }
-}
-
-=back
-
-=head1 VERSION
-
-$Id:
-
-=head1 BUGS
-
-Needless duplication of much of FS::part_sb_field, with the result that most of
-the warnings about it apply here also.
-
-=head1 SEE ALSO
-
-FS::svc_broadband, FS::router, FS::router_field, schema.html
-from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/part_sb_field.pm b/FS/FS/part_sb_field.pm
deleted file mode 100755
index 8dca946..0000000
--- a/FS/FS/part_sb_field.pm
+++ /dev/null
@@ -1,267 +0,0 @@
-package FS::part_sb_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::part_sb_field - Object methods for part_sb_field records
-
-=head1 SYNOPSIS
-
- use FS::part_sb_field;
-
- $record = new FS::part_sb_field \%hash;
- $record = new FS::part_sb_field { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_sb_field object represents an extended field (xfield) definition
-for svc_broadband's sb_field mechanism (see L<FS::svc_broadband>).
-FS::part_sb_field inherits from FS::Record. The following fields are
-currently supported:
-
-=over 2
-
-=item sbfieldpart - primary key (assigned automatically)
-
-=item name - name of the field
-
-=item svcpart - service type for which this field is available (see L<FS::part_svc>)
-
-=item length - length of the contents of the field (see note #1)
-
-=item check_block - validation routine (see note #2)
-
-=item list_source - enumeration routine (see note #3)
-
-=back
-
-=head1 BACKGROUND
-
-Broadband services, unlike dialup services, are provided over a wide
-variety of physical media (DSL, wireless, cable modems, digital circuits)
-and network architectures (Ethernet, PPP, ATM). For many of these access
-mechanisms, adding a new customer requires knowledge of some properties
-of the physical connection (circuit number, the type of CPE in use, etc.).
-It is unreasonable to expect ISPs to alter Freeside's schema (and the
-associated library and UI code) to make each of these parameters a field in
-svc_broadband.
-
-Hence sb_field and part_sb_field. They allow the Freeside administrator to
-define 'extended fields' ('xfields') associated with svc_broadband records.
-These are I<not> processed in any way by Freeside itself; they exist solely for
-use by exports (see L<FS::part_export>) and technical support staff.
-
-For a parallel mechanism (at the per-router level rather than per-service),
-see L<FS::part_router_field>.
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'part_sb_field'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my $self = shift;
- my $error = '';
-
- $error = $self->ut_numbern('svcpart');
- return $error if $error;
-
- unless (qsearchs('part_svc', { svcpart => $self->svcpart }))
- { return "Unknown svcpart: " . $self->svcpart;}
-
- $self->name =~ /^([a-z0-9_\-\.]{1,15})$/i
- or return "Invalid field name for part_sb_field";
-
- #How to check input_block, display_block, and check_block?
-
- ''; #no error
-}
-
-=item list_values
-
-If the I<list_source> field is set, this method eval()s it and
-returns its output. If the field is empty, list_values returns
-an empty list.
-
-Any arguments passed to this method will be received by the list_source
-code, but this behavior is a fortuitous accident and may be removed in
-the future.
-
-=cut
-
-sub list_values {
- my $self = shift;
- return () unless $self->list_source;
-
- my @opts = eval($self->list_source);
- if($@) {
- warn $@;
- return ();
- } else {
- return @opts;
- }
-}
-
-=item part_svc
-
-Returns the FS::part_svc object associated with this field definition.
-
-=cut
-
-sub part_svc {
- my $self = shift;
- return qsearchs('part_svc', { svcpart => $self->svcpart });
-}
-
-=back
-
-=head1 VERSION
-
-$Id:
-
-=head1 NOTES
-
-=over
-
-=item 1.
-
-The I<length> field is not enforced. It provides a hint to UI
-code about how to display the field on a form. If you want to enforce a
-minimum or maximum length for a field, use a I<check_block>.
-
-=item 2.
-
-The check_block mechanism used here as well as in
-FS::part_router_field allows the user to define validation rules.
-
-When FS::sb_field::check is called, the proposed value of the xfield is
-assigned to $_. The check_block is then eval()'d and its return value
-captured. If the return value is false (empty/zero/undef), $_ is then assigned
-back into the field and stored in the database.
-
-Therefore a check_block can do three different things with the value: allow
-it, allow it with a modification, or reject it. This is very flexible, but
-somewhat dangerous. Some warnings:
-
-=over 2
-
-=item *
-
-Assume that $_ has had I<no> error checking prior to the
-check_block. That's what the check_block is for, after all. It could
-contain I<anything>: evil shell commands in backquotes, 100kb JPEG images,
-the Klez virus, whatever.
-
-=item *
-
-If your check_block modifies the input value, it should probably
-produce a value that wouldn't be modified by going through the same
-check_block again. (That is, it should map input values into its own
-eigenspace.) The reason is that if someone calls $new->replace($old),
-where $new and $old contain the same value for the field, they probably
-want the field to keep its old value, not to get transformed by the
-check_block again. So don't do silly things like '$_++' or
-'tr/A-Za-z/a-zA-Z/'.
-
-=item *
-
-Don't alter the contents of the database. I<Reading> the database
-is perfectly reasonable, but writing to it is a bad idea. Remember that
-check() might get called more than once, as described above.
-
-=item *
-
-The check_block probably won't even get called if the user submits
-an I<empty> sb_field. So at present, you can't set up a default value with
-something like 's/^$/foo/'. Conversely, don't replace the submitted value
-with an empty string. It probably will get stored, but might be deleted at
-any time.
-
-=back
-
-=item 3.
-
-The list_source mechanism is a UI hint (like length) to generate
-drop-down or list boxes. If list_source contains a value, the UI code can
-eval() it and use the results as the options on the list.
-
-Note 'can'. This is not a substitute for check_block. The HTML interface
-currently requires that the user pick one of the options on the list
-because that's the way HTML drop-down boxes work, but in the future the UI
-code might add an 'Other (please specify)' option and a text box so that
-the user can enter something else. Or it might ignore list_source and just
-generate a text box. Or the interface might be rewritten in MS Access,
-where drop-down boxes have text boxes built in. Data validation is the job
-of check(), not the front end.
-
-Note also that a list of literals evaluates to itself, so a list_source
-like
-
-C<('Windows', 'MacOS', 'Linux')>
-
-or
-
-C<qw(Windows MacOS Linux)>
-
-means exactly what you'd think.
-
-=head1 BUGS
-
-The lack of any way to do default values. We might add this as another UI
-hint (since, for the most part, it's the UI's job to figure out which fields
-have had values entered into them). In fact, there are lots of things we
-should add as UI hints.
-
-Oh, and the documentation is probably full of lies.
-
-=head1 SEE ALSO
-
-FS::svc_broadband, FS::sb_field, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/part_virtual_field.pm b/FS/FS/part_virtual_field.pm
new file mode 100755
index 0000000..3283f6a
--- /dev/null
+++ b/FS/FS/part_virtual_field.pm
@@ -0,0 +1,303 @@
+package FS::part_virtual_field;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs qsearch dbdef );
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::part_virtual_field - Object methods for part_virtual_field records
+
+=head1 SYNOPSIS
+
+ use FS::part_virtual_field;
+
+ $record = new FS::part_virtual_field \%hash;
+ $record = new FS::part_virtual_field { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_virtual_field object represents the definition of a virtual field
+(see the BACKGROUND section). FS::part_virtual_field contains the name and
+base table of the field, as well as validation rules and UI hints about the
+display of the field. The actual data is stored in FS::virtual_field; see
+its manpage for details.
+
+FS::part_virtual_field inherits from FS::Record. The following fields are
+currently supported:
+
+=over 2
+
+=item vfieldpart - primary key (assigned automatically)
+
+=item name - name of the field
+
+=item dbtable - table for which this virtual field is defined
+
+=item check_block - Perl code to validate/normalize data
+
+=item list_source - Perl code to generate a list of values (UI hint)
+
+=item length - expected length of the value (UI hint)
+
+=item label - descriptive label for the field (UI hint)
+
+=item sequence - sort key (UI hint; unimplemented)
+
+=back
+
+=head1 BACKGROUND
+
+"Form is none other than emptiness,
+ and emptiness is none other than form."
+-- Heart Sutra
+
+The virtual field mechanism allows site admins to make trivial changes to
+the Freeside database schema without modifying the code. Specifically, the
+user can add custom-defined 'fields' to the set of data tracked by Freeside
+about objects such as customers and services. These fields are not associated
+with any logic in the core Freeside system, but may be referenced in peripheral
+code such as exports, price calculations, or alternate interfaces, or may just
+be stored in the database for future reference.
+
+This system was originally devised for svc_broadband, which (by necessity)
+comprises such a wide range of access technologies that no static set of fields
+could contain all the information needed by the exports. In an appalling
+display of False Laziness, a parallel mechanism was implemented for the
+router table, to store properties such as passwords to configure routers.
+
+The original system treated svc_broadband custom fields (sb_fields) as records
+in a completely separate table. Any code that accessed or manipulated these
+fields had to be aware that they were I<not> fields in svc_broadband, but
+records in sb_field. For example, code that inserted a svc_broadband with
+several custom fields had to create an FS::svc_broadband object, call its
+insert() method, and then create several FS::sb_field objects and call I<their>
+insert() methods.
+
+This created a problem for exports. The insert method on any FS::svc_Common
+object (including svc_broadband) automatically triggers exports after the
+record has been inserted. However, at this point, the sb_fields had not yet
+been inserted, so the export could not rely on their presence, which was the
+original purpose of sb_fields.
+
+Hence the new system. Virtual fields are appended to the field list of every
+record at the FS::Record level, whether the object is created ex nihilo with
+new() or fetched with qsearch(). The fields() method now returns a list of
+both real and virtual fields. The insert(), replace(), and delete() methods
+now update both the base table and the virtual fields, in a single transaction.
+
+A new method is provided, virtual_fields(), which gives only the virtual
+fields. UI code that dynamically generates form widgets to edit virtual field
+data should use this to figure out what fields are defined. (See below.)
+
+Subclasses may override virtual_fields() to restrict the set of virtual
+fields available. Some discipline and sanity on the part of the programmer
+are required; in particular, this function should probably not depend on any
+fields in the record other than the primary key, since the others may change
+after the object is instantiated. (Making it depend on I<virtual> fields is
+just asking for pain.) One use of this is seen in FS::svc_Common; another
+possibility is field-level access control based on FS::UID::getotaker().
+
+As a trivial case, a subclass may opt out of supporting virtual fields with
+the following code:
+
+sub virtual_fields { () }
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see "insert".
+
+=cut
+
+sub table { 'part_virtual_field'; }
+sub virtual_fields { () }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+If there is an error, returns the error, otherwise returns false.
+Called by the insert and replace methods.
+
+=back
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_text('name') ||
+ $self->ut_text('dbtable') ||
+ $self->ut_number('length')
+ ;
+ return $error if $error;
+
+ # Make sure it's a real table with a numeric primary key
+ my ($table, $pkey);
+ if($table = $FS::Record::dbdef->table($self->dbtable)) {
+ if($pkey = $table->primary_key) {
+ if($table->column($pkey)->type =~ /int/i) {
+ # this is what it should be
+ } else {
+ $error = "$table.$pkey is not an integer";
+ }
+ } else {
+ $error = "$table does not have a single-field primary key";
+ }
+ } else {
+ $error = "$table does not exist in the schema";
+ }
+ return $error if $error;
+
+ # Possibly some sanity checks for check_block and list_source?
+
+ $self->SUPER::check;
+}
+
+=item list
+
+Evaluates list_source.
+
+=cut
+
+sub list {
+ my $self = shift;
+ return () unless $self->list_source;
+
+ my @opts = eval($self->list_source);
+ if($@) {
+ warn $@;
+ return ();
+ } else {
+ return @opts;
+ }
+}
+
+=item widget UI_TYPE MODE [ VALUE ]
+
+Generates UI code for a widget suitable for editing/viewing the field, based on
+list_source and length.
+
+The only UI_TYPE currently supported is 'HTML', and the only MODE is 'view'.
+Others will be added later.
+
+In HTML, all widgets are assumed to be table rows. View widgets look like
+<TR><TD ALIGN="right">Label</TD><TD BGCOLOR="#ffffff">Value</TD></TR>
+
+(Most of the display style stuff, such as the colors, should probably go into
+a separate module specific to the UI. That can wait, though. The API for
+this function won't change.)
+
+VALUE (optional) is the current value of the field.
+
+=cut
+
+sub widget {
+ my $self = shift;
+ my ($ui_type, $mode, $value) = @_;
+ my $text;
+ my $label = $self->label || $self->name;
+
+ if ($ui_type eq 'HTML') {
+ if ($mode eq 'view') {
+ $text = q!<TR><TD ALIGN="right">! . $label .
+ q!</TD><TD BGCOLOR="#ffffff">! . $value .
+ q!</TD></TR>! . "\n";
+ } elsif ($mode eq 'edit') {
+ $text = q!<TR><TD ALIGN="right">! . $label .
+ q!</TD><TD>!;
+ if ($self->list_source) {
+ $text .= q!<SELECT NAME="! . $self->name .
+ q!" SIZE=1>! . "\n";
+ foreach ($self->list) {
+ $text .= q!<OPTION VALUE="! . $_ . q!"!;
+ $text .= ' SELECTED' if ($_ eq $value);
+ $text .= '>' . $_ . '</OPTION>' . "\n";
+ }
+ } else {
+ $text .= q!<INPUT NAME="! . $self->name .
+ q!" VALUE="! . $value . q!"!;
+ if ($self->length) {
+ $text .= q! SIZE="! . $self->length . q!"!;
+ }
+ $text .= '>';
+ }
+ $text .= q!</TD></TR>! . "\n";
+ } else {
+ return '';
+ }
+ } else {
+ return '';
+ }
+ return $text;
+}
+
+=head1 VERSION
+
+$Id: part_virtual_field.pm,v 1.1.2.1 2003-08-04 23:47:45 khoff Exp $
+
+=head1 NOTES
+
+=head2 Semantics of check_block:
+
+This has been changed from the sb_field implementation to make check_blocks
+simpler and more natural to Perl programmers who work on things other than
+Freeside.
+
+The check_block is eval'd with the (proposed) new value of the field in $_,
+and the object to be updated in $self. Its return value is ignored. The
+check_block may change the value of $_ to override the proposed value, or
+call die() (with an appropriate error message) to reject the update entirely;
+the error string will be returned as the output of the check() method.
+
+This makes check_blocks like
+
+C<s/foo/bar/>
+
+do what you expect.
+
+The check_block is expected NOT to do anything freaky to $self, like modifying
+other fields or calling $self->check(). You have been warned.
+
+(FIXME: Rewrite some of the warnings from part_sb_field and insert here.)
+
+=head1 BUGS
+
+None. It's absolutely falwless.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::virtual_field>
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/router_field.pm b/FS/FS/router_field.pm
deleted file mode 100755
index eee21ab..0000000
--- a/FS/FS/router_field.pm
+++ /dev/null
@@ -1,146 +0,0 @@
-package FS::router_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::part_router_field;
-use FS::router;
-
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::router_field - Object methods for router_field records
-
-=head1 SYNOPSIS
-
- use FS::router_field;
-
- $record = new FS::router_field \%hash;
- $record = new FS::router_field { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-FS::router_field contains values of router xfields. See FS::part_sb_field
-for details on the xfield mechanism.
-
-=over 4
-
-=item routerfieldpart - Type of router_field as defined by
-FS::part_router_field
-
-=item routernum - The FS::router to which this value belongs.
-
-=item value - The contents of the field.
-
-=back
-
-=head1 METHODS
-
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'router_field'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- return "routernum must be defined" unless $self->routernum;
- return "routerfieldpart must be defined" unless $self->routerfieldpart;
-
- my $part_router_field = $self->part_router_field;
- $_ = $self->value;
-
- my $check_block = $part_router_field->check_block;
- if ($check_block) {
- $@ = '';
- my $error = (eval($check_block) or $@);
- return $error if $error;
- $self->setfield('value' => $_);
- }
-
- ''; #no error
-}
-
-=item part_router_field
-
-Returns a reference to the FS:part_router_field that defines this
-FS::router_field
-
-=cut
-
-sub part_router_field {
- my $self = shift;
-
- return qsearchs('part_router_field',
- { routerfieldpart => $self->routerfieldpart });
-}
-
-=item router
-
-Returns a reference to the FS::router to which this FS::router_field
-belongs.
-
-=cut
-
-sub router {
- my $self = shift;
-
- return qsearchs('router', { routernum => $self->routernum });
-}
-
-=back
-
-=head1 VERSION
-
-$Id:
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-FS::svc_broadband, FS::router, FS::router_block, FS::router_field,
-schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/sb_field.pm b/FS/FS/sb_field.pm
deleted file mode 100755
index d4eb378..0000000
--- a/FS/FS/sb_field.pm
+++ /dev/null
@@ -1,148 +0,0 @@
-package FS::sb_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::part_sb_field;
-
-use UNIVERSAL qw( can );
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::sb_field - Object methods for sb_field records
-
-=head1 SYNOPSIS
-
- use FS::sb_field;
-
- $record = new FS::sb_field \%hash;
- $record = new FS::sb_field { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-See L<FS::part_sb_field> for details on this table's mission in life.
-FS::sb_field contains the actual values of the xfields defined in
-part_sb_field.
-
-The following fields are supported:
-
-=over 4
-
-=item sbfieldpart - Type of sb_field as defined by FS::part_sb_field
-
-=item svcnum - The svc_broadband to which this value belongs.
-
-=item value - The contents of the field.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'sb_field'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks the value against the check_block of the corresponding part_sb_field.
-Returns whatever the check_block returned (unless the check_block dies, in
-which case check returns the die message). Therefore, if the check_block
-wants to allow the value to be stored, it must return false. See
-L<FS::part_sb_field> for details.
-
-=cut
-
-sub check {
- my $self = shift;
-
- return "svcnum must be defined" unless $self->svcnum;
- return "sbfieldpart must be defined" unless $self->sbfieldpart;
-
- my $part_sb_field = $self->part_sb_field;
-
- $_ = $self->value;
-
- my $check_block = $self->part_sb_field->check_block;
- if ($check_block) {
- $@ = '';
- my $error = (eval($check_block) or $@); # treat fatal errors as errors
- return $error if $error;
- $self->setfield('value' => $_);
- }
-
- ''; #no error
-}
-
-=item part_sb_field
-
-Returns a reference to the FS::part_sb_field that defines this FS::sb_field.
-
-=cut
-
-sub part_sb_field {
- my $self = shift;
-
- return qsearchs('part_sb_field', { sbfieldpart => $self->sbfieldpart });
-}
-
-=back
-
-=item svc_broadband
-
-Returns a reference to the FS::svc_broadband to which this value is attached.
-Nobody's ever going to use this function, but here it is anyway.
-
-=cut
-
-sub svc_broadband {
- my $self = shift;
-
- return qsearchs('svc_broadband', { svcnum => $self->svcnum });
-}
-
-=head1 VERSION
-
-$Id:
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_broadband>, schema.html
-from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 272b5b7..2f80a33 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -92,9 +92,8 @@ FS/part_pop_local.pm
FS/part_referral.pm
FS/part_svc.pm
FS/part_svc_column.pm
-FS/part_router_field.pm
-FS/part_sb_field.pm
FS/part_svc_router.pm
+FS/part_virtual_field.pm
FS/pkg_svc.pm
FS/svc_Common.pm
FS/svc_acct.pm
@@ -102,7 +101,6 @@ FS/svc_acct_pop.pm
FS/svc_broadband.pm
FS/svc_domain.pm
FS/router.pm
-FS/router_field.pm
FS/type_pkgs.pm
FS/nas.pm
FS/port.pm
@@ -111,7 +109,6 @@ FS/domain_record.pm
FS/prepay_credit.pm
FS/svc_www.pm
FS/svc_forward.pm
-FS/sb_field.pm
FS/raddb.pm
FS/radius_usergroup.pm
FS/queue.pm
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
index a5cfc16..2cb555e 100755
--- a/FS/bin/freeside-setup
+++ b/FS/bin/freeside-setup
@@ -1048,30 +1048,6 @@ sub tables_hash_hack {
'index' => [],
},
- 'part_router_field' => {
- 'columns' => [
- 'routerfieldpart', 'serial', '', '',
- 'name', 'varchar', '', $char_d,
- 'length', 'int', '', '',
- 'check_block', 'text', 'NULL', '',
- 'list_source', 'text', 'NULL', '',
- ],
- 'primary_key' => 'routerfieldpart',
- 'unique' => [],
- 'index' => [],
- },
-
- 'router_field' => {
- 'columns' => [
- 'routerfieldpart', 'int', '', '',
- 'routernum', 'int', '', '',
- 'value', 'varchar', '', 128,
- ],
- 'primary_key' => '',
- 'unique' => [ [ 'routerfieldpart', 'routernum' ] ],
- 'index' => [],
- },
-
'addr_block' => {
'columns' => [
'blocknum', 'serial', '', '',
@@ -1084,31 +1060,6 @@ sub tables_hash_hack {
'index' => [],
},
- 'part_sb_field' => {
- 'columns' => [
- 'sbfieldpart', 'serial', '', '',
- 'svcpart', 'int', '', '',
- 'name', 'varchar', '', $char_d,
- 'length', 'int', '', '',
- 'check_block', 'text', 'NULL', '',
- 'list_source', 'text', 'NULL', '',
- ],
- 'primary_key' => 'sbfieldpart',
- 'unique' => [ [ 'sbfieldpart', 'svcpart' ] ],
- 'index' => [],
- },
-
- 'sb_field' => {
- 'columns' => [
- 'sbfieldpart', 'int', '', '',
- 'svcnum', 'int', '', '',
- 'value', 'varchar', '', 128,
- ],
- 'primary_key' => '',
- 'unique' => [ [ 'sbfieldpart', 'svcnum' ] ],
- 'index' => [],
- },
-
'svc_broadband' => {
'columns' => [
'svcnum', 'int', '', '',
diff --git a/htetc/global.asa b/htetc/global.asa
index 5553642..796136b 100644
--- a/htetc/global.asa
+++ b/htetc/global.asa
@@ -42,6 +42,7 @@ use FS::part_pkg;
use FS::part_referral;
use FS::part_svc;
use FS::part_svc_router;
+use FS::part_virtual_field;
use FS::pkg_svc;
use FS::port;
use FS::queue qw(joblisting);
@@ -53,11 +54,7 @@ use FS::svc_domain;
use FS::svc_forward;
use FS::svc_www;
use FS::router;
-use FS::part_router_field;
-use FS::router_field;
use FS::addr_block;
-use FS::part_sb_field;
-use FS::sb_field;
use FS::svc_broadband;
use FS::type_pkgs;
use FS::part_export;
diff --git a/htetc/handler.pl b/htetc/handler.pl
index 481d5a2..c81db86 100644
--- a/htetc/handler.pl
+++ b/htetc/handler.pl
@@ -100,6 +100,7 @@ sub handler
use FS::part_referral;
use FS::part_svc;
use FS::part_svc_router;
+ use FS::part_virtual_field;
use FS::pkg_svc;
use FS::port;
use FS::queue qw(joblisting);
@@ -112,11 +113,7 @@ sub handler
use FS::svc_forward;
use FS::svc_www;
use FS::router;
- use FS::part_router_field;
- use FS::router_field;
use FS::addr_block;
- use FS::part_sb_field;
- use FS::sb_field;
use FS::svc_broadband;
use FS::type_pkgs;
use FS::part_export;
diff --git a/htetc/handler.pl-1.0x b/htetc/handler.pl-1.0x
index 8840b08..def579f 100644
--- a/htetc/handler.pl-1.0x
+++ b/htetc/handler.pl-1.0x
@@ -95,6 +95,7 @@ sub handler
use FS::part_referral;
use FS::part_svc;
use FS::part_svc_router;
+ use FS::part_virtual_field;
use FS::pkg_svc;
use FS::port;
use FS::queue qw(joblisting);
@@ -107,11 +108,7 @@ sub handler
use FS::svc_forward;
use FS::svc_www;
use FS::router;
- use FS::part_router_field;
- use FS::router_field;
use FS::addr_block;
- use FS::part_sb_field;
- use FS::sb_field;
use FS::svc_broadband;
use FS::type_pkgs;
use FS::part_export;
diff --git a/httemplate/browse/part_sb_field.cgi b/httemplate/browse/part_sb_field.cgi
deleted file mode 100644
index 4c9641e..0000000
--- a/httemplate/browse/part_sb_field.cgi
+++ /dev/null
@@ -1,31 +0,0 @@
-<%= header('svc_broadband extended fields', menubar('Main Menu' => $p)) %>
-<%
-
-my @psf = qsearch('part_sb_field', {});
-my $block;
-my $p2 = popurl(2);
-
-%>
-
-<% if ($cgi->param('error')) { %>
- <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
- <BR><BR>
-<% } %>
-
-<A HREF="<%=$p2%>edit/part_sb_field.cgi"><I>Add a new field</I></A><BR><BR>
-
-<%=table()%>
-<TH><TD>Field name</TD><TD>Service type</TD></TH>
-<% foreach $psf (sort {$a->name cmp $b->name} @psf) { %>
- <TR>
- <TD></TD>
- <TD>
- <A HREF="<%=$p2%>edit/part_sb_field.cgi?<%=$psf->sbfieldpart%>">
- <%=$psf->name%></A></TD>
- <TD><%=$psf->part_svc->svc%></TD>
- </TR>
-<% } %>
-</TABLE>
-</BODY>
-</HTML>
-
diff --git a/httemplate/browse/part_virtual_field.cgi b/httemplate/browse/part_virtual_field.cgi
new file mode 100644
index 0000000..9bb5c13
--- /dev/null
+++ b/httemplate/browse/part_virtual_field.cgi
@@ -0,0 +1,39 @@
+<%= header('Virtual field definitions', menubar('Main Menu' => $p)) %>
+<%
+
+my %pvfs;
+my $block;
+my $p2 = popurl(2);
+my $dbtable;
+
+foreach (qsearch('part_virtual_field', {})) {
+ push @{ $pvfs{$_->dbtable} }, $_;
+}
+%>
+
+<% if ($cgi->param('error')) { %>
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
+ <BR><BR>
+<% } %>
+
+<A HREF="<%=$p2%>edit/part_virtual_field.cgi"><I>Add a new field</I></A><BR><BR>
+
+<% foreach $dbtable (sort { $a cmp $b } keys (%pvfs)) { %>
+<H3><%=$dbtable%></H3>
+
+<%=table()%>
+<TH><TD>Field name</TD><TD>Description</TD></TH>
+<% foreach $pvf (sort {$a->name cmp $b->name} @{ $pvfs{$dbtable} }) { %>
+ <TR>
+ <TD></TD>
+ <TD>
+ <A HREF="<%=$p2%>edit/part_virtual_field.cgi?<%=$pvf->vfieldpart%>">
+ <%=$pvf->name%></A></TD>
+ <TD><%=$pvf->label%></TD>
+ </TR>
+<% } %>
+</TABLE>
+<% } %>
+</BODY>
+</HTML>
+
diff --git a/httemplate/docs/upgrade10.html b/httemplate/docs/upgrade10.html
index 1035510..4c2c17b 100644
--- a/httemplate/docs/upgrade10.html
+++ b/httemplate/docs/upgrade10.html
@@ -14,6 +14,24 @@ CREATE TABLE cust_bill_pkg_detail (
);
CREATE INDEX cust_bill_pkg_detail1 ON cust_bill_pkg_detail ( pkgnum, invnum );
+CREATE TABLE part_virtual_field (
+ vfieldpart int NOT NULL,
+ dbtable varchar(32) NOT NULL,
+ name varchar(32) NOT NULL,
+ check_block text,
+ list_source text,
+ length integer,
+ label varchar(80),
+ PRIMARY KEY (vfieldpart)
+);
+
+CREATE TABLE virtual_field (
+ recnum integer NOT NULL,
+ vfieldpart integer NOT NULL,
+ value varchar(128) NOT NULL,
+ PRIMARY KEY (vfieldpart, recnum)
+);
+
CREATE TABLE router (
routernum serial,
routername varchar(80),
@@ -26,22 +44,6 @@ CREATE TABLE part_svc_router (
routernum int NOT NULL
);
-CREATE TABLE part_router_field (
- routerfieldpart serial,
- name varchar(80),
- length int NOT NULL,
- check_block text,
- list_source text,
- PRIMARY KEY (routerfieldpart)
-);
-
-CREATE TABLE router_field (
- routerfieldpart int NOT NULL,
- routernum int NOT NULL,
- value varchar(128)
-);
-CREATE UNIQUE INDEX router_field1 ON router_field ( routerfieldpart, routernum );
-
CREATE TABLE addr_block (
blocknum serial,
routernum int NOT NULL,
@@ -51,24 +53,6 @@ CREATE TABLE addr_block (
);
CREATE UNIQUE INDEX addr_block1 ON addr_block ( blocknum, routernum );
-CREATE TABLE part_sb_field (
- sbfieldpart serial,
- svcpart int NOT NULL,
- name varchar(80) NOT NULL,
- length int NOT NULL,
- check_block text NULL,
- list_source text NULL,
- PRIMARY key (sbfieldpart)
-);
-CREATE UNIQUE INDEX part_sb_field1 ON part_sb_field ( sbfieldpart, svcpart );
-
-CREATE TABLE sb_field (
- sbfieldpart int NOT NULL,
- svcnum int NOT NULL,
- value varchar(128)
-);
-CREATE UNIQUE INDEX sb_field1 ON sb_field ( sbfieldpart, svcnum );
-
CREATE TABLE svc_broadband (
svcnum int NOT NULL,
blocknum int NOT NULL,
@@ -112,7 +96,7 @@ optionally:
mandatory again:
dbdef-create username
-create-history-tables username cust_bill_pkg_detail router part_svc_router part_router_field router_field addr_block part_sb_field sb_field svc_broadband
+create-history-tables username cust_bill_pkg_detail router part_svc_router addr_block svc_broadband
dbdef-create username
diff --git a/httemplate/edit/part_router_field.cgi b/httemplate/edit/part_router_field.cgi
deleted file mode 100644
index 02962b1..0000000
--- a/httemplate/edit/part_router_field.cgi
+++ /dev/null
@@ -1,71 +0,0 @@
-<!-- mason kludge -->
-<%
-my ($routerfieldpart, $part_router_field);
-
-if ( $cgi->param('error') ) {
- $part_router_field = new FS::part_router_field ( {
- map { $_, scalar($cgi->param($_)) } fields('part_router_field')});
- $routerfieldpart = $part_router_field->routerfieldpart;
-} else {
- my($query) = $cgi->keywords;
- if ( $query =~ /^(\d+)$/ ) { #editing
- $routerfieldpart=$1;
- $part_router_field=qsearchs('part_router_field',
- {'routerfieldpart' => $routerfieldpart})
- or die "Unknown routerfieldpart!";
-
- } else { #adding
- $part_router_field = new FS::part_router_field({});
- }
-}
-my $action = $part_router_field->routerfieldpart ? 'Edit' : 'Add';
-
-my $p1 = popurl(1);
-print header("$action Router Extended Field Definition",
- menubar('Main Menu' => $p,
- 'View all Extended Fields' => $p. 'browse/generic.cgi?part_router_field')
- );
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
-%>
-<FORM ACTION="<%=$p1%>process/generic.cgi" METHOD=POST>
-
-<INPUT TYPE="hidden" NAME="table" VALUE="part_router_field">
-<INPUT TYPE="hidden" NAME="routerfieldpart" VALUE="<%=
- $routerfieldpart%>">
-Field #<B><%=$routerfieldpart or "(NEW)"%></B><BR><BR>
-
-<%=ntable("#cccccc",2)%>
- <TR>
- <TD ALIGN="right">Name</TD>
- <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=15 VALUE="<%=
- $part_router_field->name%>"></TD>
- </TR>
- <TR>
- <TD ALIGN="right">Length</TD>
- <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<%=
- $part_router_field->length%>"></TD>
- </TR>
- <TR>
- <TD ALIGN="right">check_block</TD>
- <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><%=
- $part_router_field->check_block%></TEXTAREA></TD>
- </TR>
- <TR>
- <TD ALIGN="right">list_source</TD>
- <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><%=
- $part_router_field->list_source%></TEXTAREA></TD>
- </TR>
-</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">
-
-</FORM>
-
-<BR><BR>
-<FONT SIZE=-2>If you don't understand what <I>check_block</I> and
-<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT>
-
-
-</BODY>
-</HTML>
diff --git a/httemplate/edit/part_sb_field.cgi b/httemplate/edit/part_sb_field.cgi
deleted file mode 100644
index 9e0cc9e..0000000
--- a/httemplate/edit/part_sb_field.cgi
+++ /dev/null
@@ -1,79 +0,0 @@
-<!-- mason kludge -->
-<%
-my ($sbfieldpart, $part_sb_field);
-
-if ( $cgi->param('error') ) {
- $part_sb_field = new FS::part_sb_field ( {
- map { $_, scalar($cgi->param($_)) } fields('part_sb_field')});
- $sbfieldpart = $part_sb_field->sbfieldpart;
-} else {
- my($query) = $cgi->keywords;
- if ( $query =~ /^(\d+)$/ ) { #editing
- $sbfieldpart=$1;
- $part_sb_field=qsearchs('part_sb_field',
- {'sbfieldpart' => $sbfieldpart})
- or die "Unknown sbfieldpart!";
-
- } else { #adding
- $part_sb_field = new FS::part_sb_field({});
- }
-}
-my $action = $part_sb_field->sbfieldpart ? 'Edit' : 'Add';
-
-my $p1 = popurl(1);
-print header("$action svc_broadband Extended Field Definition", '');
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
-%>
-<FORM ACTION="<%=$p1%>process/generic.cgi" METHOD="POST">
-
-<INPUT TYPE="hidden" NAME="table" VALUE="part_sb_field">
-<INPUT TYPE="hidden" NAME="redirect_ok"
- VALUE="<%=popurl(2)%>browse/part_sb_field.cgi">
-<INPUT TYPE="hidden" NAME="sbfieldpart" VALUE="<%=
- $sbfieldpart%>">
-Field #<B><%=$sbfieldpart or "(NEW)"%></B><BR><BR>
-
-<%=ntable("#cccccc",2)%>
- <TR>
- <TD ALIGN="right">Name</TD>
- <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=15 VALUE="<%=
- $part_sb_field->name%>"></TD>
- </TR>
- <TR>
- <TD ALIGN="right">Length</TD>
- <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<%=
- $part_sb_field->length%>"></TD>
- </TR>
- <TR>
- <TD ALIGN="right">Service</TD>
- <TD><SELECT SIZE=1 NAME="svcpart"><%
- foreach my $part_svc (qsearch('part_svc', {svcdb => 'svc_broadband'})) {
- %><OPTION VALUE="<%=$part_svc->svcpart%>"<%=
- ($part_svc->svcpart == $part_sb_field->svcpart) ? ' SELECTED' : ''%>">
- <%=$part_svc->svc%>
- <% } %>
- </SELECT></TD>
- <TR>
- <TD ALIGN="right">check_block</TD>
- <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><%=
- $part_sb_field->check_block%></TEXTAREA></TD>
- </TR>
- <TR>
- <TD ALIGN="right">list_source</TD>
- <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><%=
- $part_sb_field->list_source%></TEXTAREA></TD>
- </TR>
-</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">
-
-</FORM>
-
-<BR><BR>
-<FONT SIZE=-2>If you don't understand what <I>check_block</I> and
-<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT>
-
-
-</BODY>
-</HTML>
diff --git a/httemplate/edit/part_virtual_field.cgi b/httemplate/edit/part_virtual_field.cgi
new file mode 100644
index 0000000..8bc8438
--- /dev/null
+++ b/httemplate/edit/part_virtual_field.cgi
@@ -0,0 +1,92 @@
+<!-- mason kludge -->
+<%
+my ($vfieldpart, $part_virtual_field);
+
+if ( $cgi->param('error') ) {
+ $part_virtual_field = new FS::part_virtual_field ( {
+ map { $_, scalar($cgi->param($_)) } fields('part_virtual_field')});
+ $vfieldpart = $part_virtual_field->vfieldpart;
+} else {
+ my($query) = $cgi->keywords;
+ if ( $query =~ /^(\d+)$/ ) { #editing
+ $vfieldpart=$1;
+ $part_virtual_field=qsearchs('part_virtual_field',
+ {'vfieldpart' => $vfieldpart})
+ or die "Unknown vfieldpart!";
+
+ } else { #adding
+ $part_virtual_field = new FS::part_virtual_field({});
+ }
+}
+my $action = $part_virtual_field->vfieldpart ? 'Edit' : 'Add';
+
+my $p1 = popurl(1);
+print header("$action Virtual Field Definition", '');
+
+print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
+ "</FONT>"
+ if $cgi->param('error');
+%>
+<FORM ACTION="<%=$p1%>process/generic.cgi" METHOD="POST">
+
+<INPUT TYPE="hidden" NAME="table" VALUE="part_virtual_field">
+<INPUT TYPE="hidden" NAME="redirect_ok"
+ VALUE="<%=popurl(2)%>browse/part_virtual_field.cgi">
+<INPUT TYPE="hidden" NAME="vfieldpart" VALUE="<%=
+ $vfieldpart%>">
+Field #<B><%=$vfieldpart or "(NEW)"%></B><BR><BR>
+
+<%=ntable("#cccccc",2)%>
+ <TR>
+ <TD ALIGN="right">Name</TD>
+ <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=15 VALUE="<%=
+ $part_virtual_field->name%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Table</TD>
+ <TD><% if ($action eq 'Add') { %>
+ <SELECT SIZE=1 NAME="dbtable"><%
+ my $dbdef = dbdef; # ick
+ foreach my $dbtable ($dbdef->tables) {
+ if ($dbtable !~ /^h_/
+ and $dbdef->table($dbtable)->primary_key) { %>
+ <OPTION VALUE="<%=$dbtable%>"><%=$dbtable%></OPTION><%
+ }
+ }
+ %></SELECT><%
+ } else { # Edit
+ %><%=$part_virtual_field->dbtable%>
+ <INPUT TYPE="hidden" NAME="dbtable" VALUE="<%=$part_virtual_field->dbtable%>">
+ <% } %>
+ </TD>
+ <TR>
+ <TD ALIGN="right">Label</TD>
+ <TD><INPUT TYPE="text" NAME="label" MAXLENGTH="20" VALUE="<%=
+ $part_virtual_field->label%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Length</TD>
+ <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<%=
+ $part_virtual_field->length%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Check</TD>
+ <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><%=
+ $part_virtual_field->check_block%></TEXTAREA></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">List source</TD>
+ <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><%=
+ $part_virtual_field->list_source%></TEXTAREA></TD>
+ </TR>
+</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<BR><BR>
+<FONT SIZE=-2>If you don't understand what <I>check_block</I> and
+<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT>
+
+
+</BODY>
+</HTML>
diff --git a/httemplate/edit/process/router.cgi b/httemplate/edit/process/router.cgi
index 1b7fc38..7e40c48 100644
--- a/httemplate/edit/process/router.cgi
+++ b/httemplate/edit/process/router.cgi
@@ -19,19 +19,16 @@ my $error = '';
my $routernum = $cgi->param('routernum');
my $routername = $cgi->param('routername');
my $old = qsearchs('router', { routernum => $routernum });
-my @old_rf;
my @old_psr;
my $new = new FS::router {
- routernum => $routernum,
- routername => $routername,
- svcnum => 0
- };
+ map {
+ ($_, scalar($cgi->param($_)));
+ } fields('router')
+};
if($old) {
- if($old->routername ne $new->routername) {
- $error = $new->replace($old);
- } #else do nothing
+ $error = $new->replace($old);
} else {
$error = $new->insert;
$routernum = $new->routernum;
@@ -49,21 +46,6 @@ if ($old) {
}
}
check($error);
- @old_rf = $old->router_field;
- foreach $rf (@old_rf) {
- if(my $new_val = $cgi->param('rf_'.$rf->routerfieldpart)) {
- if($new_val ne $rf->value) {
- my $new_rf = new FS::router_field
- { routernum => $routernum,
- value => $new_val,
- routerfieldpart => $rf->routerfieldpart };
- $error = $new_rf->replace($rf);
- } #else do nothing
- } else {
- $error = $rf->delete;
- }
- check($error);
- }
}
foreach($cgi->param) {
@@ -77,23 +59,10 @@ foreach($cgi->param) {
$error = $new_psr->insert;
}
check($error);
- } elsif($cgi->param($_) ne '' and /^rf_(\d+)$/) {
- my $part = $1;
- if(my @x = grep {$_->routerfieldpart == $part} @old_rf) {
- # already handled all of these
- } else {
- my $new_rf = new FS::router_field
- { routernum => $routernum,
- value => $cgi->param('rf_'.$part),
- routerfieldpart => $part };
- $error = $new_rf->insert;
- check($error);
- }
}
}
-
# Yay, everything worked!
$dbh->commit or die $dbh->errstr;
print $cgi->redirect(popurl(3). "browse/router.cgi");
diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi
index ab8b9f9..4912a3a 100644
--- a/httemplate/edit/process/svc_broadband.cgi
+++ b/httemplate/edit/process/svc_broadband.cgi
@@ -9,11 +9,10 @@ my $dbh = FS::UID::dbh;
$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
my $svcnum = $1;
-my $old; my @old_sbf;
+my $old;
if ( $svcnum ) {
$old = qsearchs('svc_broadband', { 'svcnum' => $svcnum } )
or die "fatal: can't find broadband service (svcnum $svcnum)!";
- @old_sbf = $old->sb_field;
} else {
$old = '';
}
@@ -32,39 +31,6 @@ if ( $svcnum ) {
$svcnum = $new->svcnum;
}
-unless ($error) {
- my $sb_field;
-
- foreach ($cgi->param) {
- #warn "\$cgi->param $_: " . $cgi->param($_);
- if(/^sbf_(\d+)/) {
- my $part = $1;
- #warn "\$part $part";
- $sb_field = new FS::sb_field
- { svcnum => $svcnum,
- value => $cgi->param($_),
- sbfieldpart => $part };
- if (my @x = grep { $_->sbfieldpart eq $part } @old_sbf) {
- #if (my $old_sb_field = (grep { $_->sbfieldpart eq $part} @old_Sbf)[0]) {
- #warn "array: " . scalar(@x);
- if (length($sb_field->value) && ($sb_field->value ne $x[0]->value)) {
- #warn "replacing " . $x[0]->value . " with " . $sb_field->value;
- $error = $sb_field->replace($x[0]);
- #$error = $sb_field->replace($old_sb_field);
- } elsif (length($sb_field->value) == 0) {
- #warn "delete";
- $error = $x[0]->delete;
- }
- } else {
- if (length($sb_field->value) > 0) {
- #warn "insert";
- $error = $sb_field->insert;
- }
- # else do nothing
- }
- }
- }
-}
if ( $error ) {
$cgi->param('error', $error);
diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi
index de0fa6d..5c365a2 100755
--- a/httemplate/edit/router.cgi
+++ b/httemplate/edit/router.cgi
@@ -26,7 +26,7 @@ if($cgi->param('error')) {
%> <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
<% } %>
-<FORM ACTION="<%=popurl(1)%>process/generic.cgi" METHOD=POST>
+<FORM ACTION="<%=popurl(1)%>process/router.cgi" METHOD=POST>
<INPUT TYPE="hidden" NAME="table" VALUE="router">
<INPUT TYPE="hidden" NAME="redirect_ok" VALUE="<%=$p3%>/browse/router.cgi">
<INPUT TYPE="hidden" NAME="redirect_error" VALUE="<%=$p3%>/edit/router.cgi">
@@ -59,7 +59,7 @@ foreach my $part_svc ( qsearch('part_svc', { svcdb => 'svc_broadband',
<BR>
<INPUT TYPE="checkbox" NAME="svcpart_<%=$part_svc->svcpart%>"<%=
qsearchs('part_svc_router', { svcpart => $part_svc->svcpart,
- routernum => $routernum } ) ? 'CHECKED' : ''%> VALUE="ON">
+ routernum => $routernum } ) ? ' CHECKED' : ''%> VALUE="ON">
<A HREF="<%=${p}%>edit/part_svc.cgi?<%=$part_svc->svcpart%>">
<%=$part_svc->svcpart%>: <%=$part_svc->svc%></A>
<% } %>
diff --git a/httemplate/view/svc_acct.cgi b/httemplate/view/svc_acct.cgi
index 83cea05..4e01db3 100755
--- a/httemplate/view/svc_acct.cgi
+++ b/httemplate/view/svc_acct.cgi
@@ -206,11 +206,6 @@ foreach (sort { $a cmp $b } $svc_acct->virtual_fields) {
%>
</TABLE></TD></TR></TABLE>
<%
-foreach (sort { $a cmp $b } keys(%{$sb_field})) {
- print q!<TR><TD ALIGN="right">! . $_ .
- q!</TD><TD BGCOLOR="#ffffff">! . $sb_field->{$_} .
- q!</TD></TR>!;
-}
print '<BR><BR>';
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
index dd5997d..677a4b0 100644
--- a/httemplate/view/svc_broadband.cgi
+++ b/httemplate/view/svc_broadband.cgi
@@ -69,26 +69,7 @@ foreach (sort { $a cmp $b } $svc_broadband->virtual_fields) {
$svc_broadband->getfield($_)), "\n";
}
-
-# foreach my $sb_field
-# ( qsearch('sb_field', { svcnum => $svcnum }) ) {
-# my $part_sb_field = qsearchs('part_sb_field',
-# { sbfieldpart => $sb_field->sbfieldpart });
-# print q!<TR><TD ALIGN="right">! . $part_sb_field->name .
-# q!</TD><TD BGCOLOR="#ffffff">! . $sb_field->value .
-# q!</TD></TR>!;
-# }
-# print '</TABLE>';
-
-
- my $sb_field = $svc_broadband->sb_field_hashref;
- foreach (sort { $a cmp $b } keys(%{$sb_field})) {
- print q!<TR><TD ALIGN="right">! . $_ .
- q!</TD><TD BGCOLOR="#ffffff">! . $sb_field->{$_} .
- q!</TD></TR>!;
- }
- print '</TABLE>';
-
+print '</TABLE>';
print '<BR>'. joblisting({'svcnum'=>$svcnum}, 1).
'</BODY></HTML>'