summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS.pm2
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm78
-rw-r--r--FS/FS/ClientAPI_XMLRPC.pm3
-rw-r--r--FS/FS/Record.pm36
-rw-r--r--FS/FS/Schema.pm13
-rw-r--r--FS/MANIFEST2
-rw-r--r--FS/t/dsl_device.t5
-rw-r--r--fs_selfservice/FS-SelfService/SelfService.pm3
-rw-r--r--httemplate/edit/dsl_device.html31
-rw-r--r--httemplate/edit/process/dsl_device.html22
-rwxr-xr-xhttemplate/misc/delete-dsl_device.html23
-rw-r--r--httemplate/view/elements/svc_devices.html102
-rw-r--r--httemplate/view/svc_dsl.cgi65
13 files changed, 358 insertions, 27 deletions
diff --git a/FS/FS.pm b/FS/FS.pm
index 2f98920..972a21a 100644
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -154,6 +154,8 @@ L<FS::svc_broadband> - DSL, wireless and other broadband class.
L<FS::svc_dsl> - DSL
+L<FS::dsl_device> - DSL device MAC addresses
+
L<FS::dsl_note> - DSL order notes
L<FS::addr_block> - Address block class
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
index 3976ab2..e3afac1 100644
--- a/FS/FS/ClientAPI/MyAccount.pm
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -25,6 +25,8 @@ use FS::svc_acct;
use FS::svc_domain;
use FS::svc_phone;
use FS::svc_external;
+use FS::svc_dsl;
+use FS::dsl_device;
use FS::part_svc;
use FS::cust_main;
use FS::cust_bill;
@@ -1403,6 +1405,82 @@ sub list_svcs {
}
+sub _customer_svc_x {
+ my($custnum, $svcnum, $table) = @_;
+
+ $custnum =~ /^(\d+)$/ or die "illegal custnum";
+ my $search = " AND custnum = $1";
+ #$search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
+
+ qsearchs( {
+ 'table' => ($table || 'svc_acct'),
+ 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum ) '.
+ 'LEFT JOIN cust_pkg USING ( pkgnum ) ',#.
+ #'LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $svcnum, },
+ 'extra_sql' => $search, #important
+ } );
+
+}
+
+sub list_dsl_devices {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' )
+ or return { 'error' => "Service not found" };
+
+ return {
+ 'devices' => [ map {
+ +{ 'mac_addr' => $_->mac_addr };
+ } $svc_dsl->dsl_device
+ ],
+ };
+
+}
+
+sub add_dsl_device {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' )
+ or return { 'error' => "Service not found" };
+
+ return { 'error' => 'No MAC address supplied' }
+ unless length($p->{'mac_addr'});
+
+ my $dsl_device = new FS::dsl_device { 'svcnum' => $svc_dsl->svcnum,
+ 'mac_addr' => scalar($p->{'mac_addr'}),
+ };
+ my $error = $dsl_device->insert;
+ return { 'error' => $error };
+
+}
+
+sub delete_dsl_device {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' )
+ or return { 'error' => "Service not found" };
+
+ my $dsl_device = qsearchs('dsl_device', { 'svcnum' => $svc_dsl->svcnum,
+ 'mac_addr' => scalar($p->{'mac_addr'}),
+ }
+ )
+ or return { 'error' => 'Unknown MAC address: '. $p->{'mac_addr'} };
+
+ my $error = $dsl_device->delete;
+ return { 'error' => $error };
+
+}
+
sub port_graph {
my $p = shift;
_usage_details( \&_port_graph, $p,
diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm
index b077bf2..2913ffd 100644
--- a/FS/FS/ClientAPI_XMLRPC.pm
+++ b/FS/FS/ClientAPI_XMLRPC.pm
@@ -87,6 +87,9 @@ sub ss2clientapi {
'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
'list_svc_usage' => 'MyAccount/list_svc_usage',
+ 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
+ 'add_dsl_device' => 'MyAccount/add_dsl_device',
+ 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
'port_graph' => 'MyAccount/port_graph',
'list_cdr_usage' => 'MyAccount/list_cdr_usage',
'list_support_usage' => 'MyAccount/list_support_usage',
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index e52d094..9596a30 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -2217,6 +2217,42 @@ sub ut_hexn {
$self->setfield($field, uc($1));
'';
}
+
+=item ut_mac_addr COLUMN
+
+Check/untaint mac addresses. May be null.
+
+=cut
+
+sub ut_mac_addr {
+ my($self, $field) = @_;
+
+ my $mac = $self->get($field);
+ $mac =~ s/\s+//g;
+ $mac =~ s/://g;
+ $self->set($field, $mac);
+
+ my $e = $self->ut_hex($field);
+ return $e if $e;
+
+ return "Illegal (mac address) $field: ". $self->getfield($field)
+ unless length($self->getfield($field)) == 12;
+
+ '';
+
+}
+
+=item ut_mac_addrn COLUMN
+
+Check/untaint mac addresses. May be null.
+
+=cut
+
+sub ut_mac_addrn {
+ my($self, $field) = @_;
+ ($self->getfield($field) eq '') ? '' : $self->ut_mac_addr($field);
+}
+
=item ut_ip COLUMN
Check/untaint ip addresses. IPv4 only for now, though ::1 is auto-translated
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index c0bbd0a..5d5bd4e 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -2020,6 +2020,19 @@ sub tables_hashref {
'unique' => [ ],
'index' => [ ['phonenum'], ['vendor_order_id'] ],
},
+
+ 'dsl_device' => {
+ 'columns' => [
+ 'devicenum', 'serial', '', '', '', '',
+ #part_device? or our own part_dsl_device?
+ #'devicepart', 'int', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'mac_addr', 'varchar', '', 12, '', '',
+ ],
+ 'primary_key' => 'devicenum',
+ 'unique' => [ [ 'mac_addr' ], ],
+ 'index' => [ [ 'svcnum' ], ], # [ 'devicepart' ] ],
+ },
'dsl_note' => {
'columns' => [
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 43a1ed8..557e612 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -610,3 +610,5 @@ FS/radius_group.pm
t/radius_group.t
FS/template_content.pm
t/template_content.t
+FS/dsl_device.pm
+t/dsl_device.t
diff --git a/FS/t/dsl_device.t b/FS/t/dsl_device.t
new file mode 100644
index 0000000..2e97424
--- /dev/null
+++ b/FS/t/dsl_device.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::dsl_device;
+$loaded=1;
+print "ok 1\n";
diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm
index e778f79..3567846 100644
--- a/fs_selfservice/FS-SelfService/SelfService.pm
+++ b/fs_selfservice/FS-SelfService/SelfService.pm
@@ -49,6 +49,9 @@ $socket .= '.'.$tag if defined $tag && length($tag);
'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
'list_svc_usage' => 'MyAccount/list_svc_usage',
+ 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
+ 'add_dsl_device' => 'MyAccount/add_dsl_device',
+ 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
'port_graph' => 'MyAccount/port_graph',
'list_cdr_usage' => 'MyAccount/list_cdr_usage',
'list_support_usage' => 'MyAccount/list_support_usage',
diff --git a/httemplate/edit/dsl_device.html b/httemplate/edit/dsl_device.html
new file mode 100644
index 0000000..0d20fe5
--- /dev/null
+++ b/httemplate/edit/dsl_device.html
@@ -0,0 +1,31 @@
+<% include( 'elements/edit.html',
+ 'name' => 'DSL device',
+ 'table' => 'dsl_device',
+ 'labels' => {
+ 'devicenum' => 'Device',
+ 'mac_addr' => 'MAC address',
+ },
+ 'fields' => [ { 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') );
+ },
+ )
+%>
+<%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?
+
+
+</%init>
diff --git a/httemplate/edit/process/dsl_device.html b/httemplate/edit/process/dsl_device.html
new file mode 100644
index 0000000..4dd53d4
--- /dev/null
+++ b/httemplate/edit/process/dsl_device.html
@@ -0,0 +1,22 @@
+<% include( 'elements/process.html',
+ 'table' => 'dsl_device',
+ 'redirect' => sub {
+ my( $cgi, $dsl_device ) = @_;
+ popurl(3).'view/svc_dsl.cgi?'.
+ 'svcnum='. $dsl_device->svcnum.
+ ';devicenum=';
+ },
+ )
+%>
+<%init>
+
+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>
diff --git a/httemplate/misc/delete-dsl_device.html b/httemplate/misc/delete-dsl_device.html
new file mode 100755
index 0000000..0b985fa
--- /dev/null
+++ b/httemplate/misc/delete-dsl_device.html
@@ -0,0 +1,23 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/svc_dsl.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 $dsl_device = qsearchs('dsl_device', { 'devicenum' => $devicenum } );
+my $svcnum = $dsl_device->svcnum;
+
+my $error = $dsl_device->delete;
+
+</%init>
diff --git a/httemplate/view/elements/svc_devices.html b/httemplate/view/elements/svc_devices.html
new file mode 100644
index 0000000..d71c82f
--- /dev/null
+++ b/httemplate/view/elements/svc_devices.html
@@ -0,0 +1,102 @@
+<%doc>
+
+#Example:
+
+ include( 'elements/svc_devices.html',
+ #required
+ 'svc_x' => $svc_phone, #or $svc_dsl
+ 'table' => 'phone_device', #or dsl_device
+
+ #optional
+ 'no_edit' => 0, #set true to turn off edit link
+ )
+
+</%doc>
+<% $devices %>
+<%init>
+
+ my %opt = @_;
+ my $table = $opt{'table'}; #part_device, dsl_device
+ my $svc_x = $opt{'svc_x'};
+
+ my $devices = '';
+
+ my $num_part_device = 0;
+ if ( $table eq 'phone_device' ) {
+ my $sth = dbh->prepare("SELECT COUNT(*) FROM part_device")
+ #WHERE disabled = '' OR disabled IS NULL;");
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ $num_part_device = $sth->fetchrow_arrayref->[0];
+}
+
+ my @devices = $svc_x->$table();
+
+ #should move the below to proper mason code above instead of making $devices
+ if ( @devices || $num_part_device || $table eq 'dsl_device' ) {
+ my $svcnum = $svc_x->svcnum;
+ $devices .=
+ qq[Devices (<A HREF="${p}edit/$table.html?svcnum=$svcnum">Add device</A>)<BR>];
+ if ( @devices ) {
+
+ $devices .= qq!
+ <SCRIPT>
+ function areyousure(href) {
+ if (confirm("Are you sure you want to delete this device?") == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+ !;
+
+
+ $devices .=
+ include('/elements/table-grid.html').
+ '<TR>';
+
+ $devices .=
+ '<TH CLASS="grid" BGCOLOR="#cccccc">Type</TH>'
+ if $table eq 'phone_device';
+
+ $devices .=
+ '<TH CLASS="grid" BGCOLOR="#cccccc">MAC Addr</TH>'.
+ '<TH CLASS="grid" BGCOLOR="#cccccc"></TH>'.
+ '<TH CLASS="grid" BGCOLOR="#cccccc"></TH>'.
+ '</TR>';
+ my $bgcolor1 = '#eeeeee';
+ my $bgcolor2 = '#ffffff';
+ my $bgcolor = '';
+
+ foreach my $device ( @devices ) {
+
+ if ( $bgcolor eq $bgcolor1 ) {
+ $bgcolor = $bgcolor2;
+ } else {
+ $bgcolor = $bgcolor1;
+ }
+ my $td = qq(<TD CLASS="grid" BGCOLOR="$bgcolor">);
+
+ my $devicenum = $device->devicenum;
+ my $export_links = join( '<BR>', @{ $device->export_links } )
+ if $device->can('export_links');
+
+ $devices .= '<TR>';
+ $devices .= $td. $device->part_device->devicename. '</TD>'
+ if $table eq 'phone_device'; #$devices->can('part_device');
+
+ $devices .= $td. $device->mac_addr. '</TD>'.
+ $td. $export_links. '</TD>'.
+ "$td( ";
+
+ $devices .= qq(<A HREF="${p}edit/$table.html?$devicenum">edit</A> | )
+ unless $opt{'no_edit'};
+
+ $devices .= qq(<A HREF="javascript:areyousure('${p}misc/delete-$table.html?$devicenum')">delete</A>).
+ ' )</TD>'.
+ '</TR>';
+ }
+ $devices .= '</TABLE><BR>';
+ }
+ $devices .= '<BR>';
+ }
+
+</%init>
diff --git a/httemplate/view/svc_dsl.cgi b/httemplate/view/svc_dsl.cgi
index 9d9134a..477da26 100644
--- a/httemplate/view/svc_dsl.cgi
+++ b/httemplate/view/svc_dsl.cgi
@@ -33,33 +33,44 @@ my $svc_cb = sub {
# if no DSL-pulling exports, then just display everything, which is the
# default behaviour implemented above
- return if ( scalar(@exports) == 0 );
-
- my $export = @exports[0];
-
- @fields = ( 'phonenum',
- { field => 'loop_type',
- value => 'FS::part_export::'.$export->exporttype.'::loop_type_long'
- },
- { field => 'desired_due_date', type => 'date', },
- { field => 'due_date', type => 'date', },
- { field => 'pushed', type => 'datetime', },
- { field => 'monitored', type => 'checkbox', },
- { field => 'last_pull', type => 'datetime', },
- 'first',
- 'last',
- 'company' );
-
- my $status = '';
- if($export->exporttype eq 'ikano') {
- push @fields, qw ( username password isp_chg isp_prev staticips );
- $status = "Ikano " . $svc_dsl->vendor_order_type . " order #"
- . $svc_dsl->vendor_order_id . " &nbsp; Status: "
- . $svc_dsl->vendor_order_status;
- }
- # else add any other export-specific stuff here
+ if ( scalar(@exports) ) {
+
+ my $export = @exports[0];
+
+ @fields = (
+ 'phonenum',
+ { field => 'loop_type',
+ value => 'FS::part_export::'.$export->exporttype.'::loop_type_long'
+ },
+ { field => 'desired_due_date', type => 'date', },
+ { field => 'due_date', type => 'date', },
+ { field => 'pushed', type => 'datetime', },
+ { field => 'monitored', type => 'checkbox', },
+ { field => 'last_pull', type => 'datetime', },
+ 'first',
+ 'last',
+ 'company',
+ );
+
+ my $status = '';
+ if($export->exporttype eq 'ikano') {
+ push @fields, qw ( username password isp_chg isp_prev staticips );
+ $status = "Ikano " . $svc_dsl->vendor_order_type . " order #"
+ . $svc_dsl->vendor_order_id . " &nbsp; Status: "
+ . $svc_dsl->vendor_order_status;
+ }
+ # else add any other export-specific stuff here
- $footer = "<B>$status</B>";
+ $footer = "<B>$status</B>";
+
+ }
+
+ $footer .= '<BR><BR>'.
+ include( '/view/elements/svc_devices.html',
+ 'svc_x' => $svc_dsl,
+ 'table' => 'dsl_device',
+ 'no_edit' => 1,
+ );
my @notes = $svc_dsl->notes;
if ( @notes ) {
@@ -68,7 +79,7 @@ my $svc_cb = sub {
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
$footer .=
- "<BR><BR>Order Notes<BR>". ntable('#cccccc', 2). #id="dsl_notes"
+ "Order Notes<BR>". ntable('#cccccc', 2). #id="dsl_notes"
'<TR><TH>Date</TH><TH>By</TH><TH>Priority</TH><TH>Note</TH></TR>';
foreach my $note ( @notes ) {