summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2014-11-27 15:21:29 -0800
committerMark Wells <mark@freeside.biz>2014-11-27 15:21:29 -0800
commit1af8ff7f48f7259fc99f090c301c84b9680fdb4d (patch)
treee21d72df5e6dd2eddebc8debc3b954bf2e0830e8
parent0f0bc1ef7aafc6b3869c0f71ee2528c1c9897ce6 (diff)
svc_circuit, #23879, #25933, #30830
-rw-r--r--FS/FS/AccessRight.pm1
-rw-r--r--FS/FS/Mason.pm4
-rw-r--r--FS/FS/Schema.pm74
-rw-r--r--FS/FS/UI/Web.pm19
-rwxr-xr-xFS/FS/addr_block.pm18
-rw-r--r--FS/FS/circuit_provider.pm101
-rw-r--r--FS/FS/circuit_termination.pm98
-rw-r--r--FS/FS/circuit_type.pm98
-rwxr-xr-xFS/FS/router.pm7
-rw-r--r--FS/FS/svc_circuit.pm230
-rw-r--r--FS/FS/svc_phone.pm35
-rw-r--r--FS/MANIFEST8
-rw-r--r--FS/t/circuit_provider.t5
-rw-r--r--FS/t/circuit_termination.t5
-rw-r--r--FS/t/circuit_type.t5
-rw-r--r--FS/t/svc_circuit.t5
-rw-r--r--httemplate/browse/circuit_provider.html11
-rw-r--r--httemplate/browse/circuit_termination.html11
-rw-r--r--httemplate/browse/circuit_type.html11
-rw-r--r--httemplate/browse/elements/browse-simple.html57
-rw-r--r--httemplate/docs/part_svc-table.html1
-rw-r--r--httemplate/edit/circuit_provider.html21
-rw-r--r--httemplate/edit/circuit_termination.html21
-rw-r--r--httemplate/edit/circuit_type.html21
-rw-r--r--httemplate/edit/elements/part_svc_column.html5
-rw-r--r--httemplate/edit/elements/svc_Common.html46
-rw-r--r--httemplate/edit/process/circuit_provider.html11
-rw-r--r--httemplate/edit/process/circuit_termination.html11
-rw-r--r--httemplate/edit/process/circuit_type.html11
-rw-r--r--httemplate/edit/process/elements/svc_Common.html2
-rw-r--r--httemplate/edit/process/svc_circuit.html11
-rw-r--r--httemplate/edit/svc_circuit.cgi54
-rw-r--r--httemplate/edit/svc_phone.cgi36
-rw-r--r--httemplate/elements/menu.html8
-rw-r--r--httemplate/elements/tr-select-svc_circuit.html41
-rwxr-xr-xhttemplate/search/svc_circuit.cgi65
-rw-r--r--httemplate/view/elements/svc_Common.html163
-rw-r--r--httemplate/view/svc_circuit.html80
-rw-r--r--httemplate/view/svc_phone.cgi7
39 files changed, 1341 insertions, 77 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index bad831a..92cede6 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -310,6 +310,7 @@ tie my %rights, 'Tie::IxHash',
'Services: Mailing lists',
'Services: Alarm services',
'Services: Video',
+ 'Services: Circuits',
'Services: External services',
'Usage: RADIUS sessions',
'Usage: Call Detail Records (CDRs)',
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 900da10..d3e45df 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -392,6 +392,10 @@ if ( -e $addl_handler_use_file ) {
use FS::deploy_zone_vertex;
use FS::TaxEngine;
use FS::tax_status;
+ use FS::circuit_type;
+ use FS::circuit_provider;
+ use FS::circuit_termination;
+ use FS::svc_circuit;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 715a603..91dfc5d 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -5653,6 +5653,7 @@ sub tables_hashref {
'max_simultaneous', 'int', 'NULL', '', '', '',
'e911_class', 'char', 'NULL', 1, '', '',
'e911_type', 'char', 'NULL', 1, '', '',
+ 'circuit_svcnum', 'int', 'NULL', '', '', '',
],
'primary_key' => 'svcnum',
'unique' => [ [ 'sms_carrierid', 'sms_account'] ],
@@ -5678,6 +5679,10 @@ sub tables_hashref {
table => 'cdr_carrier',
references => [ 'carrierid' ],
},
+ { columns => [ 'circuit_svcnum' ],
+ table => 'svc_circuit',
+ references => [ 'svcnum' ],
+ },
],
},
@@ -6527,6 +6532,75 @@ sub tables_hashref {
],
},
+ 'circuit_type' => {
+ 'columns' => [
+ 'typenum', 'serial', '', '', '', '',
+ 'typename', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ # speed? number of voice lines? anything else?
+ ],
+ 'primary_key' => 'typenum',
+ 'unique' => [ [ 'typename' ] ],
+ 'index' => [],
+ },
+
+ 'circuit_provider' => {
+ 'columns' => [
+ 'providernum', 'serial', '', '', '', '',
+ 'provider', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'providernum',
+ 'unique' => [ [ 'provider' ], ],
+ 'index' => [],
+ },
+
+ 'circuit_termination' => {
+ 'columns' => [
+ 'termnum', 'serial', '', '', '', '',
+ 'termination','varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'termnum',
+ 'unique' => [ [ 'termination' ] ],
+ 'index' => [],
+ },
+
+ 'svc_circuit' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'typenum', 'int', '', '', '', '',
+ 'providernum', 'int', '', '', '', '',
+ 'termnum', 'int', '', '', '', '',
+ 'circuit_id', 'varchar', '', 64, '', '',
+ 'desired_due_date', 'int', 'NULL', '', '', '',
+ 'due_date', 'int', 'NULL', '', '', '',
+ 'vendor_order_id', 'varchar', 'NULL', $char_d, '', '',
+ 'vendor_qual_id', 'varchar', 'NULL', $char_d, '', '',
+ 'vendor_order_type', 'varchar', 'NULL', $char_d, '', '',
+ 'vendor_order_status', 'varchar', 'NULL', $char_d, '', '',
+ 'endpoint_ip_addr', 'varchar', 'NULL', 40, '', '',
+ 'endpoint_mac_addr', 'varchar', 'NULL', 12, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ [ 'providernum' ], [ 'typenum' ] ],
+ 'foreign_keys' => [
+ { columns => [ 'svcnum' ],
+ table => 'cust_svc',
+ },
+ { columns => [ 'typenum' ],
+ table => 'circuit_type',
+ },
+ { columns => [ 'providernum' ],
+ table => 'circuit_provider',
+ },
+ { columns => [ 'termnum' ],
+ table => 'circuit_termination',
+ },
+ ],
+ },
+
'vend_main' => {
'columns' => [
'vendnum', 'serial', '', '', '', '',
diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm
index 99c3560..e138692 100644
--- a/FS/FS/UI/Web.pm
+++ b/FS/FS/UI/Web.pm
@@ -113,16 +113,16 @@ sub svc_url {
if $DEBUG;
if ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.cgi") ) {
$url = "$svcdb.cgi?";
+ } elsif ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.html") ) {
+ $url = "$svcdb.html?";
} else {
-
my $generic = $opt{action} eq 'search' ? 'cust_svc' : 'svc_Common';
$url = "$generic.html?svcdb=$svcdb;";
$url .= 'svcnum=' if $query =~ /^\d+(;|$)/ or $query eq '';
}
- import FS::CGI 'rooturl'; #WTF! why is this necessary
- my $return = rooturl(). "$opt{action}/$url$query";
+ my $return = FS::CGI::rooturl(). "$opt{action}/$url$query";
$return = qq!<A HREF="$return">! if $opt{ahref};
@@ -574,6 +574,19 @@ sub cust_aligns {
}
}
+=item cust_links
+
+Returns an array of links to view/cust_main.cgi, for use with cust_fields.
+
+=cut
+
+sub cust_links {
+ my $link = [ FS::CGI::rooturl().'view/cust_main.cgi?', 'custnum' ];
+
+ return map { $_ eq 'cust_status_label' ? '' : $link }
+ @cust_fields;
+}
+
=item is_mobile
Utility function to determine if the client is a mobile browser.
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index 3e62a68..ba0f61d 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -388,6 +388,24 @@ sub label {
($router ? $router->routername : '(unallocated)'). ':'. $self->NetAddr;
}
+=item router
+
+Returns the router assigned to this block.
+
+=cut
+
+# necessary, because this can't be foreign keyed
+
+sub router {
+ my $self = shift;
+ my $routernum = $self->routernum;
+ if ( $routernum ) {
+ return FS::router->by_key($routernum);
+ } else {
+ return;
+ }
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/circuit_provider.pm b/FS/FS/circuit_provider.pm
new file mode 100644
index 0000000..6cb7841
--- /dev/null
+++ b/FS/FS/circuit_provider.pm
@@ -0,0 +1,101 @@
+package FS::circuit_provider;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::circuit_provider - Object methods for circuit_provider records
+
+=head1 SYNOPSIS
+
+ use FS::circuit_provider;
+
+ $record = new FS::circuit_provider \%hash;
+ $record = new FS::circuit_provider { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::circuit_provider object represents a telecom carrier that provides
+physical circuits (L<FS::svc_circuit>). FS::circuit_provider inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item providernum - primary key
+
+=item provider - provider name
+
+=item disabled - disabled
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'circuit_provider'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the 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 example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('providernum')
+ || $self->ut_text('provider')
+ || $self->ut_flag('disabled')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/circuit_termination.pm b/FS/FS/circuit_termination.pm
new file mode 100644
index 0000000..3f0afc1
--- /dev/null
+++ b/FS/FS/circuit_termination.pm
@@ -0,0 +1,98 @@
+package FS::circuit_termination;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::circuit_termination - Object methods for circuit_termination records
+
+=head1 SYNOPSIS
+
+ use FS::circuit_termination;
+
+ $record = new FS::circuit_termination \%hash;
+ $record = new FS::circuit_termination { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::circuit_termination object represents a central office circuit
+interface type. FS::circuit_termination inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item termnum - primary key
+
+=item termination - description of the termination type
+
+=item disabled - 'Y' if this is disabled
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+=cut
+
+sub table { 'circuit_termination'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the 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 example. 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->ut_numbern('termnum')
+ || $self->ut_text('termination')
+ || $self->ut_flag('disabled')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/circuit_type.pm b/FS/FS/circuit_type.pm
new file mode 100644
index 0000000..3b36536
--- /dev/null
+++ b/FS/FS/circuit_type.pm
@@ -0,0 +1,98 @@
+package FS::circuit_type;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::circuit_type - Object methods for circuit_type records
+
+=head1 SYNOPSIS
+
+ use FS::circuit_type;
+
+ $record = new FS::circuit_type \%hash;
+ $record = new FS::circuit_type { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::circuit_type object represents a circuit type (such as "DS1" or "OC3").
+FS::circuit_type inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item typenum - primary key
+
+=item typename - name of the circuit type
+
+=item disabled - 'Y' if this is disabled
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+=cut
+
+sub table { 'circuit_type'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the 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 example. 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->ut_numbern('typenum')
+ || $self->ut_text('typename')
+ || $self->ut_flag('disabled')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/router.pm b/FS/FS/router.pm
index 4011bb0..c0c93dd 100755
--- a/FS/FS/router.pm
+++ b/FS/FS/router.pm
@@ -200,6 +200,13 @@ sub delete {
Returns a list of FS::addr_block objects (address blocks) associated
with this object.
+=cut
+
+sub addr_block {
+ my $self = shift;
+ qsearch('addr_block', { routernum => $self->routernum });
+}
+
=item auto_addr_block
Returns a list of address blocks on which auto-assignment of IP addresses
diff --git a/FS/FS/svc_circuit.pm b/FS/FS/svc_circuit.pm
new file mode 100644
index 0000000..f705c68
--- /dev/null
+++ b/FS/FS/svc_circuit.pm
@@ -0,0 +1,230 @@
+package FS::svc_circuit;
+
+use strict;
+use base qw(
+ FS::svc_IP_Mixin
+ FS::MAC_Mixin
+ FS::svc_Common
+);
+use FS::Record qw( qsearch qsearchs );
+use FS::circuit_provider;
+use FS::circuit_type;
+use FS::circuit_termination;
+
+=head1 NAME
+
+FS::svc_circuit - Object methods for svc_circuit records
+
+=head1 SYNOPSIS
+
+ use FS::svc_circuit;
+
+ $record = new FS::svc_circuit \%hash;
+ $record = new FS::svc_circuit { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::svc_circuit object represents a telecom circuit service (other than
+an analog phone line, which is svc_phone, or a DSL Internet connection,
+which is svc_dsl). FS::svc_circuit inherits from FS::svc_IP_Mixin,
+FS::MAC_Mixin, and FS::svc_Common. The following fields are currently
+supported:
+
+=over 4
+
+=item svcnum - primary key; see also L<FS::cust_svc>
+
+=item typenum - circuit type (such as DS1, DS1-PRI, DS3, OC3, etc.); foreign
+key to L<FS::circuit_type>.
+
+=item providernum - circuit provider (telco); foreign key to
+L<FS::circuit_provider>.
+
+=item termnum - circuit termination type; foreign key to
+L<FS::circuit_termination>
+
+=item circuit_id - circuit ID string defined by the provider
+
+=item desired_due_date - the requested date for completion of the circuit
+order
+
+=item due_date - the provider's committed date for completion of the circuit
+order
+
+=item vendor_order_id - the provider's order number
+
+=item vendor_qual_id - the qualification number, if a qualification was
+performed
+
+=item vendor_order_type -
+
+=item vendor_order_status - the order status: ACCEPTED, PENDING, COMPLETED,
+etc.
+
+=item endpoint_ip_addr - the IP address of the endpoint equipment, if any.
+This will be validated as an IP address but not assigned from managed address
+space or checked for uniqueness.
+
+=item endpoint_mac_addr - the MAC address of the endpoint.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new circuit service. To add the record to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'svc_circuit'; }
+
+sub table_info {
+ my %dis = ( disable_default => 1, disable_fixed => 1,
+ disabled_inventory => 1, disable_select => 1 );
+
+ tie my %fields, 'Tie::IxHash', (
+ 'svcnum' => 'Service',
+ 'providernum' => {
+ label => 'Provider',
+ type => 'select',
+ select_table => 'circuit_provider',
+ select_key => 'providernum',
+ select_label => 'provider',
+ disable_inventory => 1,
+ },
+ 'typenum' => {
+ label => 'Circuit type',
+ type => 'select',
+ select_table => 'circuit_type',
+ select_key => 'typenum',
+ select_label => 'typename',
+ disable_inventory => 1,
+ },
+ 'termnum' => {
+ label => 'Termination type',
+ type => 'select',
+ select_table => 'circuit_termination',
+ select_key => 'termnum',
+ select_label => 'termination',
+ disable_inventory => 1,
+ },
+ 'circuit_id' => { label => 'Circuit ID', %dis },
+ 'desired_due_date' => { label => 'Desired due date',
+ %dis
+ },
+ 'due_date' => { label => 'Due date',
+ %dis
+ },
+ 'vendor_order_id' => { label => 'Vendor order ID', %dis },
+ 'vendor_qual_id' => { label => 'Vendor qualification ID', %dis },
+ 'vendor_order_type' => {
+ label => 'Vendor order type',
+ disable_inventory => 1
+ }, # should be a select?
+ 'vendor_order_status' => {
+ label => 'Vendor order status',
+ disable_inventory => 1
+ }, # should also be a select?
+ 'endpoint_ip_addr' => {
+ label => 'Endpoint IP address',
+ },
+ 'endpoint_mac_addr' => {
+ label => 'Endpoint MAC address',
+ type => 'input-mac_addr',
+ disable_inventory => 1,
+ },
+ );
+ return {
+ 'name' => 'Circuit',
+ 'name_plural' => 'Circuits',
+ 'longname_plural' => 'Voice and data circuit services',
+ 'display_weight' => 72,
+ 'cancel_weight' => 85, # after svc_phone
+ 'fields' => \%fields,
+ };
+}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the 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 service. 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 $mac_addr = uc($self->get('endpoint_mac_addr'));
+ $mac_addr =~ s/[\W_]//g;
+ $self->set('endpoint_mac_addr', $mac_addr);
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ || $self->ut_number('typenum')
+ || $self->ut_number('providernum')
+ || $self->ut_text('circuit_id')
+ || $self->ut_numbern('desired_due_date')
+ || $self->ut_numbern('due_date')
+ || $self->ut_textn('vendor_order_id')
+ || $self->ut_textn('vendor_qual_id')
+ || $self->ut_textn('vendor_order_type')
+ || $self->ut_textn('vendor_order_status')
+ || $self->ut_ipn('endpoint_ip_addr')
+ || $self->ut_textn('endpoint_mac_addr')
+ ;
+
+ # no canonical values yet for vendor_order_status or _type
+
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item label
+
+Returns the circuit ID.
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->get('circuit_id');
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm
index 4ca8d82..bd35cba 100644
--- a/FS/FS/svc_phone.pm
+++ b/FS/FS/svc_phone.pm
@@ -189,6 +189,14 @@ sub table_info {
select_label => 'domain',
disable_inventory => 1,
},
+ 'circuit_svcnum' => { label => 'Circuit',
+ type => 'select',
+ select_table => 'svc_domain',
+ select_key => 'svcnum',
+ select_label => 'circuit_label',
+ disable_inventory => 1,
+ },
+
'sms_carrierid' => { label => 'SMS Carrier',
type => 'select',
select_table => 'cdr_carrier',
@@ -711,6 +719,8 @@ sub radius_groups {
=item sms_cdr_carrier
+Returns the L<FS::cdr_carrier> assigned as the SMS carrier for this phone.
+
=cut
sub sms_cdr_carrier {
@@ -721,6 +731,8 @@ sub sms_cdr_carrier {
=item sms_carriername
+Returns the name of the SMS carrier, or an empty string if there isn't one.
+
=cut
sub sms_carriername {
@@ -729,6 +741,29 @@ sub sms_carriername {
$cdr_carrier->carriername;
}
+=item svc_circuit
+
+Returns the L<FS::svc_circuit> assigned as the trunk for this phone line.
+
+=item circuit_label
+
+Returns the label of the circuit (the part_svc label followed by the
+circuit ID), or an empty string if there isn't one.
+
+=cut
+
+sub svc_circuit {
+ my $self = shift;
+ my $svcnum = $self->get('circuit_svcnum') or return '';
+ return FS::svc_circuit->by_key($svcnum);
+}
+
+sub circuit_label {
+ my $self = shift;
+ my $svc_circuit = $self->svc_circuit or return '';
+ return join(' ', $svc_circuit->part_svc->svc, $svc_circuit->circuit_id);
+}
+
=item phone_device
Returns any FS::phone_device records associated with this service.
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 79a7dc5..105447c 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -821,3 +821,11 @@ FS/deploy_zone_vertex.pm
t/deploy_zone_vertex.t
FS/tax_status.pm
t/tax_status.t
+FS/circuit_type.pm
+t/circuit_type.t
+FS/circuit_provider.pm
+t/circuit_provider.t
+FS/circuit_termination.pm
+t/circuit_termination.t
+FS/svc_circuit.pm
+t/svc_circuit.t
diff --git a/FS/t/circuit_provider.t b/FS/t/circuit_provider.t
new file mode 100644
index 0000000..753a156
--- /dev/null
+++ b/FS/t/circuit_provider.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::circuit_provider;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/circuit_termination.t b/FS/t/circuit_termination.t
new file mode 100644
index 0000000..6f51271
--- /dev/null
+++ b/FS/t/circuit_termination.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::circuit_termination;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/circuit_type.t b/FS/t/circuit_type.t
new file mode 100644
index 0000000..dbb6e0a
--- /dev/null
+++ b/FS/t/circuit_type.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::circuit_type;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_circuit.t b/FS/t/svc_circuit.t
new file mode 100644
index 0000000..7fefcc0
--- /dev/null
+++ b/FS/t/svc_circuit.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_circuit;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/browse/circuit_provider.html b/httemplate/browse/circuit_provider.html
new file mode 100644
index 0000000..12f6532
--- /dev/null
+++ b/httemplate/browse/circuit_provider.html
@@ -0,0 +1,11 @@
+<& elements/browse-simple.html,
+ 'table' => 'circuit_provider',
+ 'title' => 'Circuit providers',
+ 'menubar' => [ 'Circuit types' => 'circuit_type.html',
+ 'Circuit terminations' => 'circuit_termination.html'
+ ],
+ 'name_singular' => 'provider',
+ 'name_header' => 'Provider name',
+ 'name_col' => 'provider',
+ 'acl' => 'Configuration',
+&>
diff --git a/httemplate/browse/circuit_termination.html b/httemplate/browse/circuit_termination.html
new file mode 100644
index 0000000..830ccf7
--- /dev/null
+++ b/httemplate/browse/circuit_termination.html
@@ -0,0 +1,11 @@
+<& elements/browse-simple.html,
+ 'table' => 'circuit_termination',
+ 'title' => 'Circuit terminations',
+ 'menubar' => [ 'Circuit types' => 'circuit_type.html',
+ 'Circuit providers' => 'circuit_provider.html'
+ ],
+ 'name_singular' => 'termination type',
+ 'name_header' => 'Termination type',
+ 'name_col' => 'termination',
+ 'acl' => 'Configuration',
+&>
diff --git a/httemplate/browse/circuit_type.html b/httemplate/browse/circuit_type.html
new file mode 100644
index 0000000..a145d54
--- /dev/null
+++ b/httemplate/browse/circuit_type.html
@@ -0,0 +1,11 @@
+<& elements/browse-simple.html,
+ 'table' => 'circuit_type',
+ 'title' => 'Circuit types',
+ 'menubar' => [ 'Circuit providers' => 'circuit_provider.html',
+ 'Circuit terminations' => 'circuit_termination.html'
+ ],
+ 'name_singular' => 'circuit type',
+ 'name_header' => 'Circuit type',
+ 'name_col' => 'typename',
+ 'acl' => 'Configuration',
+&>
diff --git a/httemplate/browse/elements/browse-simple.html b/httemplate/browse/elements/browse-simple.html
new file mode 100644
index 0000000..cfa27e8
--- /dev/null
+++ b/httemplate/browse/elements/browse-simple.html
@@ -0,0 +1,57 @@
+<& browse.html,
+ 'query' => { 'table' => $table },
+ 'count_query' => "SELECT COUNT(*) FROM $table",
+ 'header' => [ '#', $opt{name_header} ],
+ 'fields' => [ $table_key, $opt{name_col} ],
+ 'links' => [ '', '' ],
+ 'link_onclicks' => [ '', $sub_edit_popup ],
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'html_init' => $html_init,
+ %opt,
+&>
+<%doc>
+A simple wrapper around search/elements/search.html for browsing/editing
+tables that only have a primary key, a 'disabled' field, and one other column
+which is the object's name or description. Usage:
+
+<& browse-simple.html,
+ # required
+ 'table' => 'mytable',
+ 'title' => 'My Things',
+ 'name_singular' => 'thing',
+ 'name_col' => 'thingname',
+ 'name_header' => 'Thing name'
+ 'acl' => 'Configure things',
+&>
+
+</%doc>
+<%init>
+my %opt = @_;
+
+my $table = delete $opt{table};
+my $name_singular = $opt{name_singular};
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right($opt{acl});
+
+my $table_key = dbdef->table($table)->primary_key;
+my $sub_edit_popup = sub {
+ my $pkey = $_[0]->get($table_key);
+ include('/elements/popup_link_onclick.html',
+ 'action' => $p."edit/$table.html?$pkey",
+ 'actionlabel' => "Edit $name_singular",
+ 'width' => 350,
+ 'height' => 220,
+ );
+};
+
+my $html_init = include('/elements/popup_link.html',
+ 'action' => $p."edit/$table.html?",
+ 'actionlabel' => "Add $name_singular",
+ 'width' => 350,
+ 'height' => 220,
+ 'label' => "Add a new $name_singular",
+) . '<BR>';
+
+</%init>
diff --git a/httemplate/docs/part_svc-table.html b/httemplate/docs/part_svc-table.html
index 8d3711d..5e8d9e5 100644
--- a/httemplate/docs/part_svc-table.html
+++ b/httemplate/docs/part_svc-table.html
@@ -23,6 +23,7 @@
<LI><B>svc_broadband</B>: Wireless broadband
<LI><B>svc_cable</B>: Cable
<LI><B>svc_dish</B>: DISH Network
+ <LI><B>svc_circuit</B>: Phone circuits other than DSL
</UL>
</TD>
<TD VALIGN="top">
diff --git a/httemplate/edit/circuit_provider.html b/httemplate/edit/circuit_provider.html
new file mode 100644
index 0000000..6c8dced
--- /dev/null
+++ b/httemplate/edit/circuit_provider.html
@@ -0,0 +1,21 @@
+<& elements/edit.html,
+ 'popup' => 1,
+ 'table' => 'circuit_provider',
+ 'name_singular' => 'provider',
+ 'labels' => \%labels,
+ 'fields' => \@fields,
+&>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @fields = (
+ 'provider',
+ { field => 'disabled', type => 'checkbox', value => 'Y' }
+);
+my %labels = (
+ 'providernum' => '',
+ 'provider' => 'Provider name',
+ 'disabled' => 'Disabled'
+);
+</%init>
diff --git a/httemplate/edit/circuit_termination.html b/httemplate/edit/circuit_termination.html
new file mode 100644
index 0000000..0317bce
--- /dev/null
+++ b/httemplate/edit/circuit_termination.html
@@ -0,0 +1,21 @@
+<& elements/edit.html,
+ 'popup' => 1,
+ 'table' => 'circuit_termination',
+ 'name_singular' => 'termination type',
+ 'labels' => \%labels,
+ 'fields' => \@fields,
+&>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @fields = (
+ 'termination',
+ { field => 'disabled', type => 'checkbox', value => 'Y' }
+);
+my %labels = (
+ 'termnum' => '',
+ 'termination' => 'Termination type',
+ 'disabled' => 'Disabled'
+);
+</%init>
diff --git a/httemplate/edit/circuit_type.html b/httemplate/edit/circuit_type.html
new file mode 100644
index 0000000..8977588
--- /dev/null
+++ b/httemplate/edit/circuit_type.html
@@ -0,0 +1,21 @@
+<& elements/edit.html,
+ 'popup' => 1,
+ 'table' => 'circuit_type',
+ 'name_singular' => 'circuit type',
+ 'labels' => \%labels,
+ 'fields' => \@fields,
+&>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @fields = (
+ 'typename',
+ { field => 'disabled', type => 'checkbox', value => 'Y' }
+);
+my %labels = (
+ 'typenum' => '',
+ 'typename' => 'Circuit type',
+ 'disabled' => 'Disabled'
+);
+</%init>
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
index 64901a8..6dcb602 100644
--- a/httemplate/edit/elements/part_svc_column.html
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -249,7 +249,10 @@ that field.
</TR>
% }
% # special case: services with attached routers (false laziness...)
-% if ( $svcdb eq 'svc_acct' or $svcdb eq 'svc_broadband' or $svcdb eq 'svc_dsl' ) {
+% if ( $svcdb eq 'svc_acct'
+% or $svcdb eq 'svc_broadband'
+% or $svcdb eq 'svc_dsl'
+% or $svcdb eq 'svc_circuit' ) {
% push @fields, 'has_router';
<TR>
<TD COLSPAN=3 ALIGN="right">
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
index fc29327..97b630f 100644
--- a/httemplate/edit/elements/svc_Common.html
+++ b/httemplate/edit/elements/svc_Common.html
@@ -103,10 +103,42 @@
my $flag = $columndef->columnflag;
if ( $flag eq 'F' ) { #fixed
- $f->{'type'} = length($columndef->columnvalue)
- ? 'fixed'
- : 'hidden';
$f->{'value'} = $columndef->columnvalue;
+ if (length($columndef->columnvalue)) {
+
+ if ( $f->{'type'} =~ /^select-?(.*)/ ) {
+ # try to display this in a user-friendly manner
+ if ( $f->{'table'} ) { # find matching records
+ $f->{'value_col'} ||=
+ dbdef->table($f->{'table'})->primary_key;
+
+ my @values = split(',', $f->{'value'});
+ my @recs;
+ foreach (@values) {
+ push @recs, qsearchs( $f->{'table'},
+ { $f->{'value_col'} => $_ }
+ );
+ }
+ if ( @recs ) {
+ my $method = $f->{'name_col'};
+ if ( $f->{'multiple'} ) {
+ $f->{'formatted_value'} = [
+ map { $_->method } @recs
+ ];
+ } else { # there shouldn't be more than one...
+ $f->{'formatted_value'} = $recs[0]->$method;
+ }
+ } # if not, then just let tr-fixed display the
+ # values as-is
+
+ } # other select types probably don't matter
+ } # if it's a select
+
+ $f->{'type'} = 'fixed';
+
+ } else { # fixed, null
+ $f->{'type'} = 'hidden';
+ }
} elsif ( $flag eq 'A' ) { #auto assign from inventory
$f->{'type'} = 'hidden';
@@ -127,16 +159,14 @@
};
} elsif ( $flag eq 'S' #selectable choice
- && $f->{type} !~ /^select-svc(-domain|_pbx)$/ ) {
+ && $f->{type} !~ /^select-svc/ ) {
$f->{type} = 'select';
$f->{options} = [ split( /\s*,\s*/,
$columndef->columnvalue)
];
- }
+ } # shouldn't this be enforced for all 'S' fields?
- if ( $f->{'type'} eq 'select-svc_pbx'
- || $f->{'type'} eq 'select-svc-domain'
- )
+ if ( $f->{'type'} =~ /^select-svc/ )
{
$f->{'include_opt_callback'} =
sub { ( 'pkgnum' => $pkgnum,
diff --git a/httemplate/edit/process/circuit_provider.html b/httemplate/edit/process/circuit_provider.html
new file mode 100644
index 0000000..0a91a17
--- /dev/null
+++ b/httemplate/edit/process/circuit_provider.html
@@ -0,0 +1,11 @@
+<& elements/process.html,
+ 'table' => 'circuit_provider',
+ 'viewall_dir' => 'browse',
+ 'popup_reload' => 'Updating',
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/circuit_termination.html b/httemplate/edit/process/circuit_termination.html
new file mode 100644
index 0000000..94d29c0
--- /dev/null
+++ b/httemplate/edit/process/circuit_termination.html
@@ -0,0 +1,11 @@
+<& elements/process.html,
+ 'table' => 'circuit_termination',
+ 'viewall_dir' => 'browse',
+ 'popup_reload' => 'Updating',
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/circuit_type.html b/httemplate/edit/process/circuit_type.html
new file mode 100644
index 0000000..58f461e
--- /dev/null
+++ b/httemplate/edit/process/circuit_type.html
@@ -0,0 +1,11 @@
+<& elements/process.html,
+ 'table' => 'circuit_type',
+ 'viewall_dir' => 'browse',
+ 'popup_reload' => 'Updating',
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/elements/svc_Common.html b/httemplate/edit/process/elements/svc_Common.html
index 55ecc5f..ca336a1 100644
--- a/httemplate/edit/process/elements/svc_Common.html
+++ b/httemplate/edit/process/elements/svc_Common.html
@@ -29,7 +29,7 @@ my $args_callback = sub {
map { $_ => $cgi->param("router_$_") }
qw( routernum routername blocknum )
});
- if (length($router->routername) == 0) {
+ if ($router->blocknum and length($router->routername) == 0) {
#sensible default
$router->set('routername', $svc->label);
}
diff --git a/httemplate/edit/process/svc_circuit.html b/httemplate/edit/process/svc_circuit.html
new file mode 100644
index 0000000..d28f913
--- /dev/null
+++ b/httemplate/edit/process/svc_circuit.html
@@ -0,0 +1,11 @@
+<& elements/svc_Common.html,
+ table => 'svc_circuit',
+ edit_ext => 'html',
+ redirect => popurl(3)."view/svc_circuit.html?",
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
diff --git a/httemplate/edit/svc_circuit.cgi b/httemplate/edit/svc_circuit.cgi
new file mode 100644
index 0000000..3f9bad5
--- /dev/null
+++ b/httemplate/edit/svc_circuit.cgi
@@ -0,0 +1,54 @@
+<& elements/svc_Common.html,
+ 'table' => 'svc_circuit',
+ 'fields' => \@fields,
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my $conf = new FS::Conf;
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
+
+my @fields = (
+ { field => 'providernum',
+ type => 'select-table',
+ table => 'circuit_provider',
+ name_col => 'provider',
+ disable_empty => 1,
+ },
+ { field => 'typenum',
+ type => 'select-table',
+ table => 'circuit_type',
+ name_col => 'typename',
+ disable_empty => 1,
+ },
+ { field => 'termnum',
+ type => 'select-table',
+ table => 'circuit_termination',
+ name_col => 'termination',
+ disable_empty => 1,
+ },
+ { field => 'circuit_id',
+ size => 40,
+ },
+ { field => 'desired_due_date',
+ type => 'input-date-field',
+ },
+ { field => 'due_date',
+ type => 'input-date-field',
+ },
+ 'vendor_order_id',
+ 'vendor_qual_id',
+ 'vendor_order_status',
+ 'endpoint_ip_addr',
+ { field => 'endpoint_mac_addr',
+ type => 'input-mac_addr',
+ },
+);
+
+# needed: a new_callback to migrate vendor quals over to circuits
+
+#my ($svc_new_callback, $svc_edit_callback, $svc_error_callback);
+
+</%init>
diff --git a/httemplate/edit/svc_phone.cgi b/httemplate/edit/svc_phone.cgi
index f858205..f9c0d40 100644
--- a/httemplate/edit/svc_phone.cgi
+++ b/httemplate/edit/svc_phone.cgi
@@ -2,17 +2,12 @@
'table' => 'svc_phone',
'fields' => [],
'begin_callback' => $begin_callback,
- 'svc_new_callback' => sub {
- my( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt ) = @_;
- $svc_x->locationnum($cust_pkg->locationnum) if $cust_pkg;
- },
- 'svc_edit_callback' => sub {
- my( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt) = @_;
- my $conf = new FS::Conf;
- $svc_x->sip_password('*HIDDEN*') unless $conf->exists('showpasswords');
- },
+ 'svc_new_callback' => $svc_callback,
+ 'svc_edit_callback' => $svc_callback,
+ 'svc_error_callback' => $svc_callback,
&>
<%init>
+my $conf = new FS::Conf;
my $begin_callback = sub {
my( $cgi, $fields, $opt ) = @_;
@@ -25,8 +20,6 @@ my $begin_callback = sub {
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right($right);
- my $conf = new FS::Conf;
-
push @$fields,
'countrycode',
{ field => 'phonenum',
@@ -149,7 +142,26 @@ my $begin_callback = sub {
}
-};
+}; # begin_callback
+# svc_edit_callback / svc_new_callback
+my $svc_callback = sub {
+ my ($cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt) = @_;
+ push @$fields, {
+ field => 'circuit_svcnum',
+ type => 'select-svc_circuit',
+ cust_pkg => $cust_pkg,
+ part_svc => $part_svc,
+ };
+
+ if ( $cust_pkg and not $svc_x->svcnum ) {
+ # new service, default to package location
+ $svc_x->set('locationnum', $cust_pkg->locationnum);
+ }
+
+ if ( not $conf->exists('showpasswords') and $svc_x->svcnum ) {
+ $svc_x->sip_password('*HIDDEN*');
+ }
+};
</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index f26882b..b4ecdc4 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -552,6 +552,12 @@ tie my %config_conferencing, 'Tie::IxHash',
'Quality levels' => [ $fsurl.'browse/conferencing_quality.html', '' ],
;
+tie my %config_circuit, 'Tie::IxHash',
+ 'Circuit types' => [ $fsurl.'browse/circuit_type.html', '' ],
+ 'Circuit providers' => [ $fsurl.'browse/circuit_provider.html', '' ],
+ 'Termination types' => [ $fsurl.'browse/circuit_termination.html', '' ],
+;
+
tie my %config_export_svc, 'Tie::IxHash', ();
if ( $curuser->access_right('Configuration') ) {
$config_export_svc{'Service definitions'} = [ $fsurl.'browse/part_svc.cgi', 'Services are items you offer to your customers' ];
@@ -572,6 +578,8 @@ $config_export_svc{'Conferencing'} = [ \%config_conferencing, '' ]
if $curuser->access_right('Configuration');
$config_export_svc{'Alarm'} = [ \%config_alarm, '' ]
if $curuser->access_right(['Alarm configuration', 'Alarm global configuration']);
+$config_export_svc{'Circuits'} = [ \%config_circuit, '' ]
+ if $curuser->access_right('Configuration');
$config_export_svc{'Hardware types'} = [ $fsurl.'browse/hardware_class.html', 'Set up hardware type catalog' ]
if $curuser->access_right('Configuration');
diff --git a/httemplate/elements/tr-select-svc_circuit.html b/httemplate/elements/tr-select-svc_circuit.html
new file mode 100644
index 0000000..fb55501
--- /dev/null
+++ b/httemplate/elements/tr-select-svc_circuit.html
@@ -0,0 +1,41 @@
+% if ( $columnflag eq 'F' ) { # no good reason for this, but support it anyway
+ <INPUT TYPE="hidden" NAME="circuit_svcnum" VALUE="<% $circuit_svcnum %>">
+% } else {
+ <& tr-select-table.html,
+ 'table' => 'svc_circuit',
+ 'name_col' => 'circuit_id',
+ 'empty_label' => ' ',
+ %select_hash,
+ %opt
+ &>
+% }
+<%init>
+
+my %opt = @_;
+
+my $circuit_svcnum;
+if ( $opt{'curr_value'} =~ /^(\d+)$/ ) {
+ $circuit_svcnum = $1;
+}
+
+# generally not the svcpart of the circuit service (or any circuit service)
+my $part_svc = $opt{'part_svc'}
+ || qsearchs('part_svc', { 'svcpart' => $opt{'svcpart'} });
+
+my $columnflag = $part_svc->part_svc_column('circuit_svcnum')->columnflag;
+
+my $cust_pkg = $opt{'cust_pkg'};
+my $custnum;
+$custnum = $cust_pkg->custnum if $cust_pkg;
+
+my %select_hash;
+if ( $custnum =~ /^(\d+)$/ ) {
+ %select_hash = (
+ 'addl_from' => ' LEFT JOIN cust_svc USING (svcnum)' .
+ ' LEFT JOIN cust_pkg USING (pkgnum)',
+ 'extra_sql' => " WHERE cust_pkg.custnum = $custnum".
+ " OR svcnum = $circuit_svcnum",
+ );
+}
+
+</%init>
diff --git a/httemplate/search/svc_circuit.cgi b/httemplate/search/svc_circuit.cgi
new file mode 100755
index 0000000..c14c55f
--- /dev/null
+++ b/httemplate/search/svc_circuit.cgi
@@ -0,0 +1,65 @@
+<& elements/svc_Common.html,
+ 'title' => 'Circuit Search Results',
+ 'name' => 'circuit services',
+ 'query' => $query,
+ 'count_query' => $query->{'count_query'},
+ 'redirect' => [ popurl(2). "view/svc_circuit.html?", 'svcnum' ],
+ 'header' => [ '#',
+ 'Provider',
+ 'Type',
+ 'Termination',
+ 'Circuit ID',
+ 'IP Address',
+ FS::UI::Web::cust_header($cgi->param('cust_fields')),
+ ],
+ 'fields' => [ 'svcnum',
+ 'provider',
+ 'typename',
+ 'termination',
+ 'circuit_id',
+ 'ip_addr',
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ '',
+ '',
+ '',
+ $link,
+ $link,
+ FS::UI::Web::cust_links($cgi->param('cust_fields')),
+ ],
+ 'align' => 'rlllll'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ ('') x 6,
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ ('') x 6,
+ FS::UI::Web::cust_styles(),
+ ],
+
+&>
+<%init>
+
+die "access denied" unless
+ $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $conf = new FS::Conf;
+
+my %search_hash;
+if ( $cgi->param('magic') eq 'unlinked' ) {
+ %search_hash = ( 'unlinked' => 1 );
+} else {
+ foreach (qw( custnum agentnum svcpart cust_fields )) {
+ $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+ }
+ foreach (qw(pkgpart routernum towernum sectornum)) {
+ $search_hash{$_} = [ $cgi->param($_) ] if $cgi->param($_);
+ }
+}
+
+my $query = FS::svc_circuit->search(\%search_hash);
+
+my $link = [ $p.'view/svc_circuit.html?', 'svcnum' ];
+
+</%init>
diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html
index b12d2dd..6c5c902 100644
--- a/httemplate/view/elements/svc_Common.html
+++ b/httemplate/view/elements/svc_Common.html
@@ -32,6 +32,15 @@ function areyousure(href) {
window.location.href = href;
}
</SCRIPT>
+<STYLE>
+ td.content {
+ background-color: #ffffff;
+ }
+ .error {
+ color: #ff0000;
+ font-weight: bold;
+ }
+</STYLE>
% if ( $custnum ) {
@@ -67,61 +76,20 @@ 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);
-% if ( ref($f) ) {
-% $field = $f->{'field'};
-% $type = $f->{'type'} || 'text';
-% if ( $f->{'value_callback'} ) {
-% my $hack_strict_refs = \&{ $f->{'value_callback'} };
-% $value = &$hack_strict_refs($svc_x);
-% } else {
-% $value = encode_entities($svc_x->$field);
-% }
-% } else {
-% $field = $f;
-% $type = 'text';
-% $value = encode_entities($svc_x->$field);
-% }
-%
-% 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)) {
-
+% my ($field, $label, $value) = &{ $format_field }($f);
+% next if !$field;
<TR>
<TD ALIGN="right">
- <% ( $opt{labels} && exists $opt{labels}->{$field} )
- ? $opt{labels}->{$field}
- : $field
- %>
+ <% $label %>
</TD>
-% $value = time2str($date_format,$value)
-% if $type eq 'date' && $value;
-% $value = time2str("$date_format %H:%M",$value)
-% if $type eq 'datetime' && $value;
-% $value = $value eq 'Y' ? emt('Yes') : emt('No')
-% if $type eq 'checkbox';
-% $value .= ' ('. (Net::MAC::Vendor::lookup($value))->[0]. ')'
-% if $type =~ /mac_addr$/ && $value =~ /\w/i;
-% #eventually more options for <SELECT>, etc. fields
-
- <TD BGCOLOR="#ffffff"><% $value %><TD>
-
+ <TD CLASS="content">
+ <% $value %>
+ </TD>
</TR>
-% }
-%
-% }
+% }
% foreach (sort { $a cmp $b } $svc_x->virtual_fields) {
<% $svc_x->pvf($_)->widget('HTML', 'view', $svc_x->getfield($_)) %>
@@ -193,7 +161,7 @@ my $svc_x = qsearchs({
my $cust_svc = $svc_x->cust_svc;
my ($label, $value, $svcdb, $part_svc );
-my $labels = $opt{labels}; #not -> here
+my $labels = $opt{labels} || {};
if ( $cust_svc ) {
($label, $value, $svcdb) = $cust_svc->label;
@@ -227,7 +195,10 @@ if ($pkgnum) {
# attached routers
if ( my $router = qsearchs('router', { svcnum => $svc_x->svcnum }) ) {
- push @$fields, qw(router_routername router_block);
+ push @$fields,
+ 'router_routername',
+ 'router_block';
+
$labels->{'router_routername'} = 'Attached router';
$labels->{'router_block'} = 'Attached address block';
$svc_x->set('router_routername', $router->routername);
@@ -235,10 +206,100 @@ if ( my $router = qsearchs('router', { svcnum => $svc_x->svcnum }) ) {
if ( $block ) {
$svc_x->set('router_block', $block->cidr);
} else {
- $svc_x->set('router_block', '<i>(none)</i>');
+ $svc_x->set('router_block', '(none)');
}
}
+my @inventory_items = $svc_x->inventory_item;
+
+my $format_field = sub {
+ my $f = shift;
+ my($field, $type, $value);
+ if ( ref($f) ) {
+ $field = $f->{'field'};
+ $type = $f->{'type'} || 'text';
+ } else {
+ $field = $f;
+ $type = 'text';
+ }
+
+ my $columndef = $part_svc->part_svc_column($field);
+ # skip fields that are fixed and empty
+ if ( $columndef->columnflag eq 'F'
+ and length($columndef->columnvalue) == 0 ) {
+ return;
+ }
+
+ # things that override the column value: value_callback, select
+ if ( ref($f) and $f->{'value_callback'} ) {
+
+ my $hack_strict_refs = \&{ $f->{'value_callback'} };
+ $value = &$hack_strict_refs($svc_x);
+
+ } elsif ( $type eq 'select-table' ) {
+ # imitates the /elements/select-table interface
+ $value = $svc_x->$field;
+
+ my $value_col = $f->{'value_col'} ||
+ dbdef->table($f->{'table'})->primary_key;
+ my $name_col = $f->{'name_col'} or die 'name_col required';
+ # we don't yet support multiple-valued fields here
+ my $obj = qsearchs($f->{'table'}, { $value_col => $value });
+ if ( $obj ) {
+ $value = $obj->$name_col; # can be any method of the object
+ } else {
+ # show the raw value, but mark it as an error
+ $value = '<SPAN CLASS="error">' . $f->{'table'} . ' ' .
+ encode_entities($value) . '</SPAN>';
+ }
+
+ } else {
+ $value = encode_entities($svc_x->$field);
+ }
+
+ # inventory-select field with multiple classes
+ # show the class name to disambiguate
+ if ( $columndef->columnflag =~ /^[MA]$/ && $columndef->columnvalue =~ /,/ )
+ {
+ my ($item) = grep { $_->svc_field eq $field } @inventory_items;
+ my $class = qsearchs('inventory_class', { classnum => $item->classnum });
+ $value .= ' <i>('. $class->classname . ')</i>' if $class;
+ }
+
+ # formatting tweaks
+ if ( $type eq 'date' and $value ) {
+ $value = time2str($date_format,$value)
+ } elsif ( $type eq 'datetime' and $value ) {
+ $value = time2str("$date_format %H:%M",$value)
+ } elsif ( $type eq 'checkbox' ) {
+ $value = $value eq 'Y' ? emt('Yes') : emt('No');
+ } elsif ( $type eq 'mac_addr' and $value =~ /\w/) {
+ $value .= ' ('. (Net::MAC::Vendor::lookup($value))->[0]. ')'
+ }
+
+ # 'link' option
+ my $href;
+ if ( ref($f) and exists $f->{'link'} ) {
+ my $link = $f->{'link'};
+ if ( ref($link) eq 'CODE' ) {
+ $link = &{$link}($svc_x);
+ }
+ if ( ref($link) eq 'ARRAY' ) {
+ my ($base, $method) = @$link;
+ $href = $base . $svc_x->$method();
+ } elsif ( !ref($link) ) {
+ $href = $link;
+ }
+
+ if ( $href ) {
+ $value = qq!<A HREF="$href">$value</A>!;
+ }
+ }
+
+ my $label = $opt{labels}->{$field} || $field;
+ return ($field, $label, $value);
+};
+
&{ $opt{'svc_callback'} }( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, \%opt )
if $opt{'svc_callback'};
</%init>
diff --git a/httemplate/view/svc_circuit.html b/httemplate/view/svc_circuit.html
new file mode 100644
index 0000000..c8d5d23
--- /dev/null
+++ b/httemplate/view/svc_circuit.html
@@ -0,0 +1,80 @@
+<& elements/svc_Common.html,
+ 'table' => 'svc_circuit',
+ 'labels' => \%labels,
+ 'fields' => \@fields,
+ 'html_foot' => sub { $self->call_method('.foot', @_) },
+&>
+<%method .foot>
+% my $svc_circuit = shift;
+% my $link = [ 'svc_phone.cgi?', 'svcnum' ];
+% if ( FS::svc_phone->count('circuit_svcnum = '.$svc_circuit->svcnum) ) {
+<& /search/elements/search.html,
+
+ 'title' => 'Provisioned phone services',
+ 'name_singular' => 'phone number',
+ 'query' => { 'table' => 'svc_phone',
+ 'hashref' => { 'circuit_svcnum' => $svc_circuit->svcnum },
+ 'addl_from' => ' LEFT JOIN cust_svc USING (svcnum)'.
+ ' LEFT JOIN part_svc USING (svcpart)',
+ 'select' => 'svc_phone.*, part_svc.*',
+ },
+ 'count_query' => 'SELECT COUNT(*) FROM svc_phone WHERE circuit_svcnum = '.
+ $svc_circuit->svcnum,
+ 'header' => [ '#', 'Service', 'Phone number', ],
+ 'fields' => [ 'svcnum', 'svc', 'phonenum' ],
+ 'links' => [ $link, $link, $link ],
+ 'align' => 'rlr',
+
+ 'html_form' => '<SPAN CLASS="fsinnerbox-title">Phone services</SPAN>',
+ 'nohtmlheader' => 1,
+ 'disable_total' => 1,
+ 'disable_maxselect' => 1,
+ 'really_disable_download' => 1,
+&>
+ <BR>
+% }
+</%method>
+<%init>
+
+my @fields = (
+ 'circuit_id',
+ { field => 'providernum',
+ type => 'select-table',
+ table => 'circuit_provider',
+ name_col => 'provider',
+ },
+ { field => 'typenum',
+ type => 'select-table',
+ table => 'circuit_type',
+ name_col => 'typename',
+ },
+ { field => 'termnum',
+ type => 'select-table',
+ table => 'circuit_termination',
+ name_col => 'termination',
+ },
+ qw( vendor_qual_id vendor_order_id vendor_order_type vendor_order_status
+ desired_due_date due_date
+ endpoint_ip_addr
+ ),
+ { field => 'endpoint_mac_addr', type => 'mac_addr' },
+);
+
+
+my %labels = (
+ circuit_id => 'Circuit ID',
+ providernum => 'Provider',
+ typenum => 'Circuit type',
+ termnum => 'Termination',
+ vendor_qual_id => 'Qualification ID',
+ vendor_order_id => 'Order ID',
+ vendor_order_type => 'Order type',
+ vendor_order_status => 'Order status',
+ desired_due_date => 'Desired due date',
+ due_date => 'Due date',
+ endpoint_ip_addr => 'Endpoint IP address',
+ endpoint_mac_addr => 'MAC address',
+);
+
+my $self = $m->request_comp;
+</%init>
diff --git a/httemplate/view/svc_phone.cgi b/httemplate/view/svc_phone.cgi
index 2a2ef24..1c0fb39 100644
--- a/httemplate/view/svc_phone.cgi
+++ b/httemplate/view/svc_phone.cgi
@@ -1,7 +1,7 @@
<& elements/svc_Common.html,
'table' => 'svc_phone',
'fields' => \@fields,
- 'labels' => \%labels,
+ 'labels' => \%labels,
'html_foot' => $html_foot,
&>
<%init>
@@ -53,6 +53,11 @@ if ( $conf->exists('svc_phone-lnp') ) {
;
}
+$labels{circuit_label} = mt('Circuit');
+push @fields, { field => 'circuit_label',
+ link => [ $p.'view/svc_circuit.html?', 'circuit_svcnum' ]
+ };
+
my $html_foot = sub {
my $svc_phone = shift;