add dsl_device to track mac addresses, RT#13656
authorivan <ivan>
Thu, 6 Oct 2011 07:02:49 +0000 (07:02 +0000)
committerivan <ivan>
Thu, 6 Oct 2011 07:02:49 +0000 (07:02 +0000)
13 files changed:
FS/FS/ClientAPI/MyAccount.pm
FS/FS/ClientAPI_XMLRPC.pm
FS/FS/Record.pm
FS/FS/dsl_device.pm
FS/FS/svc_dsl.pm
FS/MANIFEST
FS/t/dsl_device.t [new file with mode: 0644]
fs_selfservice/FS-SelfService/SelfService.pm
httemplate/edit/dsl_device.html [new file with mode: 0644]
httemplate/edit/process/dsl_device.html [new file with mode: 0644]
httemplate/misc/delete-dsl_device.html [new file with mode: 0755]
httemplate/view/elements/svc_devices.html [new file with mode: 0644]
httemplate/view/svc_dsl.cgi

index 3976ab2..e3afac1 100644 (file)
@@ -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,
index b077bf2..2913ffd 100644 (file)
@@ -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',   
index bccc8e6..2fec8e0 100644 (file)
@@ -2385,6 +2385,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
index 276eedd..2efe1f5 100644 (file)
@@ -106,7 +106,7 @@ sub check {
   my $error = 
     $self->ut_numbern('devicenum')
     || $self->ut_foreign_key('svcnum', 'svc_dsl', 'svcnum' )
-    || $self->ut_mac_addrn('mac_addr')
+    || $self->ut_mac_addr('mac_addr')
   ;
   return $error if $error;
 
index ce4342e..89f1159 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use vars qw( @ISA $conf $DEBUG $me );
 use FS::Record qw( qsearch qsearchs );
 use FS::svc_Common;
+use FS::dsl_device;
 use FS::dsl_note;
 use FS::qual;
 
@@ -273,6 +274,18 @@ sub check {
   $self->SUPER::check;
 }
 
+=item dsl_device
+
+Returns the MAC addresses associated with this DSL service, as FS::dsl_device
+objects.
+
+=cut
+
+sub dsl_device {
+  my $self = shift;
+  qsearch('dsl_device', { 'svcnum' => $self->svcnum });
+}
+
 sub predelete_hook_first {
     my $self = shift;
     my @exports = $self->part_svc->part_export_dsl_pull;
index 43a1ed8..557e612 100644 (file)
@@ -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 (file)
index 0000000..2e97424
--- /dev/null
@@ -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";
index e778f79..3567846 100644 (file)
@@ -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 (file)
index 0000000..0d20fe5
--- /dev/null
@@ -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 (file)
index 0000000..4dd53d4
--- /dev/null
@@ -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 (executable)
index 0000000..0b985fa
--- /dev/null
@@ -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 (file)
index 0000000..d71c82f
--- /dev/null
@@ -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>
index 9d9134a..477da26 100644 (file)
@@ -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 ) {