L<FS::pbx_extension> - PBX extension class
+L<FS::pbx_device> - PBX device class
+
L<FS::svc_cert> - Certificate service class
L<FS::svc_dish> - Dish network service class
--- /dev/null
+package FS::MAC_Mixin;
+
+use strict;
+#use FS::Record qw(qsearch);
+#use FS::Conf;
+# careful about importing anything here--it will end up in a LOT of
+# namespaces
+
+#use vars qw(@subclasses $DEBUG $conf);
+
+#$DEBUG = 0;
+
+# any subclass that can have MAC addresses needs to be added here
+#@subclasses = (qw(FS::svc_broadband FS::svc_cable));
+
+#sub conf {
+# $conf ||= FS::Conf->new;
+#}
+
+=head1 NAME
+
+FS::MAC_Mixin - Mixin class for objects that have MAC addresses assigned.
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=head1 METHODS
+
+=over 4
+
+=item mac_addr_pretty
+
+=cut
+
+sub mac_addr_pretty {
+ my $self = shift;
+ $self->mac_addr_formatted('U',':');
+}
+
+=item mac_addr_formatted CASE DELIMITER
+
+Format the MAC address (for use by exports). If CASE starts with "l"
+(for "lowercase"), it's returned in lowercase. DELIMITER is inserted
+between octets.
+
+=cut
+
+sub mac_addr_formatted {
+ my $self = shift;
+ my ($case, $delim) = @_;
+ my $addr = $self->mac_addr;
+ $addr = lc($addr) if $case =~ /^l/i;
+ join( $delim || '', $addr =~ /../g );
+}
+
+=back
+
+=head1 BUGS
+
+=cut
+
+1;
use FS::part_pkg_usageprice;
use FS::cust_pkg_usageprice;
use FS::pbx_extension;
+ use FS::pbx_device;
+ use FS::extension_device;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
],
},
+ 'pbx_device' => {
+ 'columns' => [
+ 'devicenum', 'serial', '', '', '', '',
+ 'devicepart', 'int', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'mac_addr', 'varchar', 'NULL', 12, '', '',
+ ],
+ 'primary_key' => 'devicenum',
+ 'unique' => [ [ 'mac_addr' ], ],
+ 'index' => [ [ 'devicepart' ], [ 'svcnum' ], ],
+ 'foreign_keys' => [
+ { columns => [ 'devicepart' ],
+ table => 'part_device',
+ },
+ { columns => [ 'svcnum' ],
+ table => 'svc_pbx',
+ },
+ ],
+ },
+
+ 'extension_device' => {
+ 'columns' => [
+ 'extensiondevicenum', 'serial', '', '', '', '',
+ 'extensionnum', 'int', '', '', '', '',
+ 'devicenum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'extensiondevicenum',
+ 'unique' => [ [ 'extensionnum', 'devicenum' ] ],
+ 'index' => [],#both? which way do we need to query?
+ 'foreign_keys' => [
+ { columns => [ 'extensionnum' ],
+ table => 'pbx_extension',
+ },
+ { columns => [ 'devicenum' ],
+ table => 'pbx_device',
+ },
+ ],
+ },
+
'svc_mailinglist' => { #svc_group?
'columns' => [
'svcnum', 'int', '', '', '', '',
package FS::dsl_device;
-use base qw( FS::Record );
+use base qw( FS::MAC_Mixin FS::Record );
use strict;
--- /dev/null
+package FS::extension_device;
+use base qw( FS::Record );
+
+use strict;
+#use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::extension_device - Object methods for extension_device records
+
+=head1 SYNOPSIS
+
+ use FS::extension_device;
+
+ $record = new FS::extension_device \%hash;
+ $record = new FS::extension_device { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::extension_device object represents a PBX extension association with a
+specific PBX device (SIP phone or ATA). FS::extension_device inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item extensiondevicenum
+
+primary key
+
+=item extensionnum
+
+extensionnum
+
+=item devicenum
+
+devicenum
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'extension_device'; }
+
+=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 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->ut_numbern('extensiondevicenum')
+ || $self->ut_foreign_keyn('extensionnum', 'pbx_extension', 'extensionnum')
+ || $self->ut_foreign_keyn('devicenum', 'pbx_device', 'devicenum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
--- /dev/null
+package FS::pbx_device;
+use base qw( FS::MAC_Mixin FS::Record );
+
+use strict;
+#use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::pbx_device - Object methods for pbx_device records
+
+=head1 SYNOPSIS
+
+ use FS::pbx_device;
+
+ $record = new FS::pbx_device \%hash;
+ $record = new FS::pbx_device { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::pbx_device object represents a specific customer phone device, such
+as a SIP phone or ATA. FS::pbx_device inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item devicenum
+
+primary key
+
+=item devicepart
+
+devicepart
+
+=item svcnum
+
+svcnum
+
+=item mac_addr
+
+mac_addr
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'pbx_device'; }
+
+=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 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->ut_numbern('devicenum')
+ || $self->ut_foreign_key('devicepart', 'part_device', 'devicepart')
+ || $self->ut_foreign_key('svcnum', 'svc_pbx', 'svcnum')
+ || $self->ut_mac_addr('mac_addr')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
package FS::phone_device;
-use base qw( FS::Record );
+use base qw( FS::MAC_Mixin FS::Record );
use strict;
use Scalar::Util qw( blessed );
=head1 NAME
-FS::svc_external - Object methods for svc_external records
+FS::svc_External_Common - Base class for svc_X classes which track external databases
=head1 SYNOPSIS
- use FS::svc_external;
-
- $record = new FS::svc_external \%hash;
- $record = new FS::svc_external { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
+ package FS::svc_newservice;
+ use base qw( FS::svc_External_Common );
=head1 DESCRIPTION
+++ /dev/null
-package FS::svc_MAC_Mixin;
-
-use strict;
-#use FS::Record qw(qsearch);
-#use FS::Conf;
-# careful about importing anything here--it will end up in a LOT of
-# namespaces
-
-#use vars qw(@subclasses $DEBUG $conf);
-
-#$DEBUG = 0;
-
-# any subclass that can have MAC addresses needs to be added here
-#@subclasses = (qw(FS::svc_broadband FS::svc_cable));
-
-#sub conf {
-# $conf ||= FS::Conf->new;
-#}
-
-=head1 NAME
-
-FS::MAC_Mixin - Mixin class for objects that have MAC addresses assigned.
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=head1 METHODS
-
-=over 4
-
-=item mac_addr_pretty
-
-=cut
-
-sub mac_addr_pretty {
- my $self = shift;
- $self->mac_addr_formatted('U',':');
-}
-
-=item mac_addr_formatted CASE DELIMITER
-
-Format the MAC address (for use by exports). If CASE starts with "l"
-(for "lowercase"), it's returned in lowercase. DELIMITER is inserted
-between octets.
-
-=cut
-
-sub mac_addr_formatted {
- my $self = shift;
- my ($case, $delim) = @_;
- my $addr = $self->mac_addr;
- $addr = lc($addr) if $case =~ /^l/i;
- join( $delim || '', $addr =~ /../g );
-}
-
-=back
-
-=head1 BUGS
-
-=cut
-
-1;
FS::svc_Radius_Mixin
FS::svc_Tower_Mixin
FS::svc_IP_Mixin
- FS::svc_MAC_Mixin
+ FS::MAC_Mixin
FS::svc_Common
);
package FS::svc_cable;
-use base qw( FS::svc_MAC_Mixin
+use base qw( FS::MAC_Mixin
FS::svc_Common
); #FS::device_Common
package FS::svc_pbx;
-use base qw( FS::o2m_Common FS::svc_External_Common );
+use base qw( FS::o2m_Common FS::device_Common FS::svc_External_Common );
use strict;
use Tie::IxHash;
package FS::svc_video;
-use base qw( FS::svc_MAC_Mixin FS::svc_Common );
+use base qw( FS::MAC_Mixin FS::svc_Common );
use strict;
use Tie::IxHash;
t/cust_pkg_usageprice.t
FS/pbx_extension.pm
t/pbx_extension.t
+FS/pbx_device.pm
+t/pbx_device.t
+FS/extension_device.pm
+t/extension_device.t
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::extension_device;
+$loaded=1;
+print "ok 1\n";
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pbx_device;
+$loaded=1;
+print "ok 1\n";
--- /dev/null
+<& edit.html,
+ 'labels' => {
+ 'devicenum' => 'Device',
+ 'devicepart' => 'Device type',
+ 'mac_addr' => 'MAC address',
+ },
+ 'fields' => [ { 'field' => 'devicepart',
+ 'type' => 'select-table',
+ 'table' => 'part_device',
+ 'name_col' => 'devicename',
+ 'onchange' => 'devicepart_changed',
+ 'empty_label' =>'Select device type',
+ #'hashref' =>{ disabled => '' },
+ },
+ { field => 'mac_addr',
+ type => 'select-mac',
+ },
+ { 'field' => 'svcnum',
+ 'type' => 'hidden',
+ },
+ ],
+ 'menubar' => [], #disable viewall
+ #'viewall_dir' => 'browse',
+ 'new_callback' => sub {
+ my( $cgi, $object ) = @_;
+ $object->svcnum( $cgi->param('svcnum') );
+ },
+ 'html_foot' => $html_foot,
+ %opt,
+&>
+<%init>
+
+my %opt = @_;
+
+my @deviceparts_with_inventory =
+ map $_->devicepart,
+ qsearch({ 'table' => 'part_device',
+ 'extra_sql' => 'WHERE inventory_classnum IS NOT NULL',
+ });
+
+my $html_foot = sub {
+ my $js = "
+<SCRIPT TYPE=\"text/javascript\">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function devicepart_changed(what){
+
+ var macsel = document.getElementById('sel_mac_addr');
+ var mac = document.getElementById('mac_addr');
+
+ function update_macs(macs) {
+ for ( var i = macsel.length; i >= 0; i-- )
+ macsel.options[i] = null;
+
+ var macArray = eval('(' + macs + ')' );
+ if(macArray.length == 0)
+ opt(macsel,'','No MAC addresses found in inventory for this device type');
+ else
+ opt(macsel,'','Select MAC address');
+
+ for ( var i = 0; i < macArray.length; i++ ) {
+ opt(macsel,macArray[i],macArray[i]);
+ }
+
+ }
+
+ var devicepart = what.options[what.selectedIndex].value;
+
+ var deviceparts_with_inventory = new Array(";
+$js .= join(',', map qq("$_"), @deviceparts_with_inventory);
+$js .= ");
+
+ var hasInventory = false;
+ for ( i = 0; i < deviceparts_with_inventory.length; i++ ) {
+ if ( deviceparts_with_inventory[i] == devicepart )
+ hasInventory = true;
+ }
+
+
+ if(hasInventory) { // do the AJAX thing, disable text field
+ macsel.style.display = 'inline';
+ mac.style.display = 'none';
+ mac.value = '';
+ get_macs( devicepart, update_macs );
+ } else { // clear & display text field only, clear/hide select
+ mac.style.display = 'inline';
+ macsel.style.display = 'none';
+ macsel.selectedIndex = 0;
+ }
+
+ }
+
+ devicepart_changed(document.getElementById('devicepart'));
+</SCRIPT>";
+
+ $js;
+};
+
+# :/ needs agent-virt so you can't futz with arbitrary devices
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+
+</%init>
--- /dev/null
+<& elements/device_Common.html,
+ 'name' => 'PBX device',
+ 'table' => 'pbx_device',
+&>
--- /dev/null
+<& process.html,
+ 'redirect' => sub {
+ my( $cgi, $X_device ) = @_;
+ popurl(3)."view/$svc_table.cgi?".
+ 'svcnum='. $X_device->svcnum.
+ ';devicenum=';
+ },
+ %opt,
+&>
+<%init>
+
+my %opt = @_;
+
+warn my $table = $opt{table};
+( my $svc_table = $table ) =~ s/_device//;
+$svc_table = "svc_$svc_table";
+
+if($cgi->param('sel_mac_addr') && !$cgi->param('mac_addr')) {
+ $cgi->param('mac_addr',$cgi->param('sel_mac_addr'));
+}
+
+# :/ needs agent-virt so you can't futz with arbitrary devices
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
--- /dev/null
+<& elements/device_Common.html,
+ 'table' => 'pbx_device',
+&>
--- /dev/null
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/svc_pbx.cgi?". $svcnum) %>
+% }
+<%init>
+
+# :/ needs agent-virt so you can't futz with arbitrary devices
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+#untaint devicenum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal devicenum";
+my $devicenum = $1;
+
+my $pbx_device = qsearchs('pbx_device', { 'devicenum' => $devicenum } );
+my $svcnum = $pbx_device->svcnum;
+
+my $error = $pbx_device->delete;
+
+</%init>
%if ( @devices || $num_part_device || $table eq 'dsl_device' ) {
% my $svcnum = $svc_x->svcnum;
- Devices
+ <FONT CLASS="fsinnerbox-title">Devices</FONT>
(<A HREF="<%$p%>edit/<%$table%>.html?svcnum=<%$svcnum%>">Add device</A>)
<BR>
<& /elements/table-grid.html &>
<TR>
-% if ( $table eq 'phone_device' || $table eq 'cable_device' ) {
+% if ( $table ne 'dsl_device' ) { # ( $table eq 'phone_device' || $table eq 'cable_device' || $table eq 'pbx_device' ) {
<TH CLASS="grid" BGCOLOR="#cccccc">Type</TH>
% }
<TH CLASS="grid" BGCOLOR="#cccccc">MAC Addr</TH>
% if ( $table eq 'phone_device' || $svc_x->isa('FS::device_Common') ) {
<% $td %><% $device->part_device->devicename |h %></TD>
% }
- <% $td %><% $device->mac_addr %></TD>
+ <% $td %><% $device->mac_addr_pretty %></TD>
<% $td %><% $export_links %></TD>
<% $td %>(
% unless ( $opt{'no_edit'} ) {
my $svc_x = $opt{'svc_x'};
my $num_part_device = 0;
-if ( $table eq 'phone_device' || $table eq 'cable_device' ) {
+if ( $table ne 'dsl_device' ) { # ( $table eq 'phone_device' || $table eq 'cable_device' || $table eq 'pbx_device' ) {
my $sth = dbh->prepare("SELECT COUNT(*) FROM part_device")
#WHERE disabled = '' OR disabled IS NULL;");
or die dbh->errstr;