ikano.pm initial commit, svc_dsl UI initial commit, and svc_dsl on-going work, RT7111
authorlevinse <levinse>
Thu, 25 Nov 2010 01:46:34 +0000 (01:46 +0000)
committerlevinse <levinse>
Thu, 25 Nov 2010 01:46:34 +0000 (01:46 +0000)
FS/FS/Schema.pm
FS/FS/part_export/ikano.pm [new file with mode: 0644]
FS/FS/part_svc.pm
FS/FS/svc_dsl.pm
httemplate/edit/elements/edit.html
httemplate/edit/elements/svc_Common.html
httemplate/edit/part_svc.cgi
httemplate/edit/process/svc_dsl.html [new file with mode: 0644]
httemplate/edit/svc_dsl.cgi [new file with mode: 0644]
httemplate/view/elements/svc_Common.html
httemplate/view/svc_dsl.cgi [new file with mode: 0644]

index 89f3c63..39b1ab7 100644 (file)
@@ -1682,7 +1682,7 @@ sub tables_hashref {
       'columns' => [
         'qualnum',  'serial',     '',     '', '', '', 
         'contactnum',    'int',     '',     '', '', '',
-       'svctn',     'int', 'NULL',       '', '', '',
+       'svctn',     'varchar', 'NULL',       24, '', '',
         'svcdb',      'varchar', '', $char_d, '', '', 
         'vendor_qual_id',      'varchar', 'NULL', $char_d, '', '', 
         'status',      'char', '', 1, '', '', 
@@ -1837,16 +1837,16 @@ sub tables_hashref {
       'columns' => [
         'svcnum',           'int',    '',        '', '', '',
        'pushed',     'int', 'NULL',       '', '', '',
-       'desired_dd',     'int', '',       '', '', '',
+       'desired_dd',     'int', 'NULL',       '', '', '',
        'dd',     'int', 'NULL',       '', '', '',
         'vendor_order_id',              'varchar', 'NULL', $char_d,  '', '',
         'vendor_qual_id',              'varchar', 'NULL', $char_d,  '', '',
-        'vendor_order_type',   'char', '',       1,  '', '', 
+        'vendor_order_type',   'char', 'NULL',       1,  '', '', 
         'vendor_order_status',   'char', 'NULL',       1,  '', '', 
-        'first',              'varchar', '', $char_d,  '', '',
-        'last',              'varchar', '', $char_d,  '', '',
+        'first',              'varchar', 'NULL', $char_d,  '', '',
+        'last',              'varchar', 'NULL', $char_d,  '', '',
         'company',              'varchar', 'NULL', $char_d,  '', '',
-       'svctn',     'int', 'NULL',       '', '', '',
+       'svctn',     'varchar', 'NULL',       24, '', '',
         'loop_type',   'char', 'NULL',       1,  '', '', 
         'lvp',              'varchar', 'NULL', $char_d,  '', '',
         'cktnum',              'varchar', 'NULL', $char_d,  '', '',
@@ -1856,7 +1856,7 @@ sub tables_hashref {
         'username',              'varchar', 'NULL', $char_d,  '', '',
         'password',              'varchar', 'NULL', $char_d,  '', '',
         'staticips',             'text', 'NULL', '',  '', '',
-        'monitored',           'char', '',       1,  '', '', 
+        'monitored',           'char', 'NULL',       1,  '', '', 
        'last_pull',     'int', 'NULL',       '', '', '',
        'notes',     'text', 'NULL',       '', '', '',
       ],
diff --git a/FS/FS/part_export/ikano.pm b/FS/FS/part_export/ikano.pm
new file mode 100644 (file)
index 0000000..c44db64
--- /dev/null
@@ -0,0 +1,102 @@
+package FS::part_export::ikano;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use Date::Format qw( time2str );
+use FS::Record qw(qsearch dbh);
+use FS::part_export;
+use FS::svc_dsl;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+  'keyid'         => { label=>'Ikano keyid' },
+  'username'      => { label=>'Ikano username',
+                       default => 'admin',
+                       },
+  'password'      => { label=>'Ikano password' },
+  'check_networks' => { label => 'Check Networks',
+                   default => 'ATT,BELLCA',
+                   },
+;
+
+%info = (
+  'svc'     => 'svc_dsl',
+  'desc'    => 'Provision DSL to Ikano',
+  'options' => \%options,
+  'notes'   => <<'END'
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-Ikano">Net::Ikano</a> from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+sub dsl_pull {
+    '';
+}
+
+sub status_line {
+    my($svc_dsl,$date_format,$separator) = (shift,shift,shift);
+    my %orderTypes = ( 'N' => 'New', 'X' => 'Cancel', 'C' => 'Change' );
+    my %orderStatus = ( 'N' => 'New', 'P' => 'Pending', 'X' => 'Cancelled',
+                       'C' => 'Completed', 'E' => 'Error' );
+    my $status = "Ikano ".$orderTypes{$svc_dsl->vendor_order_type}." order #"
+       . $svc_dsl->vendor_order_id . " (Status: " 
+       . $orderStatus{$svc_dsl->vendor_order_status} . ") $separator ";
+    my $monitored = $svc_dsl->monitored eq 'Y' ? 'Yes' : 'No';
+    my $pushed = $svc_dsl->pushed ? 
+               time2str("$date_format %k:%M",$svc_dsl->pushed) : "never";
+    my $last_pull = $svc_dsl->last_pull ? 
+               time2str("$date_format %k:%M",$svc_dsl->last_pull) : "never";
+    my $ddd = $svc_dsl->desired_dd ? time2str($date_format,$svc_dsl->desired_dd)
+                                  : "";
+    my $dd = $svc_dsl->dd ? time2str($date_format,$svc_dsl->dd) : "";
+    $status .= "$separator Pushed: $pushed   Monitored: $monitored  Last Pull: ";
+    $status .= "$lastpull $separator $separator Desired Due Date: $ddd  ";
+    $status .= "Due Date: $dd";
+    return $status;    
+}
+
+sub ikano_command {
+  my( $self, $command, @args ) = @_;
+
+  eval "use Net::Ikano;";
+  die $@ if $@;
+
+  my $ikano = Net::Ikano->new(
+    'keyid' => $self->option('keyid'),
+    'username'  => $self->option('username'),
+    'password'  => $self->option('password'),
+    #'debug'    => 1,
+  );
+
+  $ikano->$command(@args);
+}
+
+sub _export_insert {
+  my( $self, $svc_dsl ) = (shift, shift);
+  '';
+}
+
+sub _export_replace {
+  my( $self, $new, $old ) = (shift, shift, shift);
+  '';
+}
+
+sub _export_delete {
+  my( $self, $svc_dsl ) = (shift, shift);
+  '';
+}
+
+sub _export_suspend {
+  my( $self, $svc_dsl ) = (shift, shift);
+  '';
+}
+
+sub _export_unsuspend {
+  my( $self, $svc_dsl ) = (shift, shift);
+  '';
+}
+
+1;
index 3ed153e..164bad0 100644 (file)
@@ -459,6 +459,17 @@ sub part_export_did {
   grep $_->can('get_dids'), $self->part_export;
 }
 
+=item part_export_dsl_pull
+
+Returns a list of any exports (see L<FS::part_export>) for this service that
+are capable of pulling/pushing DSL orders.
+
+=cut
+
+sub part_export_dsl_pull {
+    my $self = shift;
+    grep $_->can('dsl_pull'), $self->part_export;
+}
 
 =item cust_svc [ PKGPART ] 
 
index 0b4b0d1..2e84dea 100644 (file)
@@ -155,8 +155,71 @@ points to.  You can ask the object for a copy with the I<hash> method.
 
 # the new method can be inherited from FS::Record, if a table method is defined
 
+sub table_info {
+    my %dis1 = ( disable_default=>1, disable_fixed=>1, disable_inventory=>1, disable_select=>1 );
+    my %dis2 = ( disable_inventory=>1, disable_select=>1 );
+
+    {
+       'name' => 'DSL',
+       'sorts' => [ 'svctn' ],
+       'display_weight' => 55,
+       'cancel_weight' => 75,
+       'fields' => {
+           'pushed' => {       label => 'Pushed', 
+                               type => 'disabled' },
+           'desired_dd' => {   label => 'Desired Due Date', %dis2, },
+           'dd' => {           label => 'Due Date', %dis2, },
+           'vendor_order_id' => { label => 'Vendor Order Id', %dis2, },
+           'vendor_qual_id' => { label => 'Vendor Qualification Id', 
+                               type => 'disabled' },
+           'vendor_order_type' => { label => 'Vendor Order Type',
+                                   disable_inventory => 1,
+                               },
+           'vendor_order_status' => { label => 'Vendor Order Status',
+                                   disable_inventory => 1,
+                                   },
+           'first' => {        label => 'First Name', %dis2, },
+           'last' => {         label => 'Last Name', %dis2, },
+           'company' => {      label => 'Company Name', %dis2, },
+           'svctn' => {        label => 'Service Telephone Number', },
+           'loop_type' => {    label => 'Loop Type',
+                                   disable_inventory => 1,
+                       },
+           'lvp' => {          label => 'Local Voice Provider',
+                                   disable_inventory => 1,
+                       },
+           'cktnum' => {       label => 'Circuit #',   },
+           'rate_band' => {    label => 'Rate Band',
+                                   disable_inventory => 1,
+                       },
+           'isp_chg' => {      label => 'ISP Changing?', 
+                               type => 'checkbox', %dis2 },
+           'isp_prev' => {     label => 'Current or Previous ISP',
+                                   disable_inventory => 1,
+                       },
+           'username' => {     label => 'PPPoE Username',
+                               type => 'text',
+                       },
+           'password' => {     label => 'PPPoE Password', %dis2 },
+           'staticips' => {    label => 'Static IPs', %dis1 },
+           'monitored' => {    label => 'Monitored', 
+                               type => 'checkbox', %dis2 },
+           'last_pull' => {    label => 'Last Pull', type => 'disabled' },
+           'notes' => {        label => 'Order Notes', %dis1 },
+       },
+    };
+}
+
 sub table { 'svc_dsl'; }
 
+sub label {
+   my $self = shift;
+   return $self->svctn if $self->svctn;
+   return $self->username if $self->username;
+   return $self->vendor_order_id if $self->vendor_order_id;
+   return $self->svcnum;
+}
+
 =item insert
 
 Adds this record to the database.  If there is an error, returns the error,
index 3d82847..142d0c3 100644 (file)
@@ -311,6 +311,11 @@ Example:
 %     foreach grep exists($f->{$_}),
 %       qw( hashref agent_virt agent_null agent_null_right );
 %
+%   # fixed
+%   $include_common{$_} = $f->{$_}
+%     foreach grep exists($f->{$_}),
+%       qw( formatted_value );
+%
 %   #htmlarea
 %   $include_common{$_} = $f->{$_}
 %     foreach grep exists($f->{$_}), qw( width height );
index e74f442..2e27f85 100644 (file)
                    die "No part_svc entry!" unless $part_svc;
 
                    label_fixup($part_svc, $opt);
+                  
+                  if ( my $cb = $opt{'svc_edit_callback'} ) {
+                     my $cust_pkg = $pkgnum
+                                      ? qsearchs('cust_pkg', {pkgnum=>$pkgnum})
+                                      : ''; #?
+                      &{ $cb }( $cgi,$svc_x, $part_svc,$cust_pkg, $fields,$opt);
+                    }
                  },
 
                  'new_hashref_callback' => sub {
index 50bc79c..d156ccd 100755 (executable)
@@ -16,6 +16,7 @@ Service definitions are the templates for items you offer to your customers.
 <UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.)
     <LI>svc_domain - Domains
     <LI>svc_cert - Certificates
+    <LI>svc_dsl - DSL
     <LI>svc_forward - Mail forwarding
     <LI>svc_mailinglist - Mailing list
     <LI>svc_www - Virtual domain website
diff --git a/httemplate/edit/process/svc_dsl.html b/httemplate/edit/process/svc_dsl.html
new file mode 100644 (file)
index 0000000..627329a
--- /dev/null
@@ -0,0 +1,10 @@
+<% include( 'elements/svc_Common.html',
+               'table'    => 'svc_dsl',
+           )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
diff --git a/httemplate/edit/svc_dsl.cgi b/httemplate/edit/svc_dsl.cgi
new file mode 100644 (file)
index 0000000..3568fbd
--- /dev/null
@@ -0,0 +1,125 @@
+<% include( 'elements/svc_Common.html',
+            'table'    => 'svc_dsl',
+           'fields'    => \@fields,
+           'svc_new_callback' => $new_cb,
+           'svc_edit_callback' => $edit_cb,
+         )
+%>
+<%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 $ti_fields = FS::svc_dsl->table_info->{'fields'};
+
+my @fields = ();
+my @uneditable = qw( pushed vendor_qual_id isp_chg isp_prev staticips last_pull notes );
+
+my $edit_cb = sub {
+    my( $cgi,$svc_x, $part_svc,$cust_pkg, $fields1,$opt) = @_;
+    my @exports = $part_svc->part_export_dsl_pull;
+    die "more than one DSL-pulling export attached to svcpart ".$part_svc->svcpart
+       if ( scalar(@exports) > 1 );
+
+    if ( scalar(@exports) == 1 ) {
+       my $export = @exports[0];               
+       if($export->exporttype eq 'ikano') {
+           @fields = ( 'password', 'monitored', );
+
+           foreach my $hf ( keys %$ti_fields ) {
+               push @fields, {
+                   field => $hf,
+                   type => 'hidden',
+                   value => $svc_x->$hf,
+               } unless ( $hf eq 'password' || $hf eq 'monitored' );
+           }
+       }
+       else {
+           # XXX
+       }
+    }
+    else {
+       # XXX
+    }
+};
+
+my $new_cb = sub {
+    my( $cgi,$svc_x, $part_svc,$cust_pkg, $fields1,$opt) = @_;
+    my @exports = $part_svc->part_export_dsl_pull;
+    die "more than one DSL-pulling export attached to svcpart ".$part_svc->svcpart
+       if ( scalar(@exports) > 1 );
+
+    if ( scalar(@exports) == 1 ) {
+       my $cust_main = $cust_pkg->cust_main;
+       my $defsvctn = $cust_main->ship_daytime ? $cust_main->ship_daytime
+                                               : $cust_main->daytime;
+       $defsvctn =~ s/[^0-9]//g;
+
+       @fields = (
+           { field => 'first',
+             value => $cust_main->ship_first ? $cust_main->ship_first
+                                             : $cust_main->first,
+           },
+           { field => 'last',
+             value => $cust_main->ship_last ? $cust_main->ship_last
+                                            : $cust_main->last,
+           },
+           { field => 'company',
+             value => $cust_pkg->cust_main->ship_company,
+             value => $cust_main->ship_company ? $cust_main->ship_company
+                                               : $cust_main->company,
+           },
+           { field => 'svctn',
+             value => $defsvctn,
+           },
+       );
+
+       my $loop_type = { field => 'loop_type' };
+
+       my $export = @exports[0];               
+       if($export->exporttype eq 'ikano') {
+           $cgi->param('vendor_qual_id') =~ /^(\d+)$/ 
+               or die 'unparsable vendor_qual_id';
+           my $vendor_qual_id = $1;
+
+           die "no start date set on customer package" if !$cust_pkg->start_date;
+
+           $loop_type = { field => 'loop_type',
+                   type => 'select',
+                   options => [ '', '0' ],
+                   labels => { '' => 'Line-share', '0', => 'Standalone' },
+                  # onchange => "document.getElementById('svctn').value = ''",
+               };
+           push @fields, { field => 'isp_chg', type => 'checkbox', };
+           push @fields, 'isp_prev';
+           push @fields, { field => 'vendor_qual_id',
+                           type => 'fixed',
+                           value => $vendor_qual_id, 
+                         };
+       }
+       else {
+           push @fields, 'username';
+       }
+       
+       push @fields, 'password';
+
+       push @fields, $loop_type;
+           
+       push @fields, { field => 'vendor_order_type',
+                       type => 'hidden',
+                       value => 'N' };
+       push @fields, { field => 'desired_dd',
+                       type => 'fixed',
+                       formatted_value => 
+                           time2str($date_format,$cust_pkg->start_date),
+                       value => $cust_pkg->start_date, 
+                     };
+    }
+    else {
+       # XXX
+    }
+};
+</%init>
index 618d33e..25845dd 100644 (file)
@@ -52,18 +52,22 @@ function areyousure(href) {
 Service #<B><% $svcnum %></B>
 % my $url = $opt{'edit_url'} || $p. 'edit/'. $opt{'table'}. '.cgi?';
 | <A HREF="<%$url%><%$svcnum%>">Edit this <% $label %></A>
+
+% unless ( $opt{'disable_unprovision'} ) {
 | <A HREF="javascript:areyousure('<%$p.'misc/unprovision.cgi?'.$svcnum%>')">
 Unprovision this Service</A>
 <BR>
+% }
 
 <% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
 
 % foreach my $f ( @$fields ) {
 %
-%   my($field, $type, $value);
+%   my($field, $type, $value, $hack_strict_refs);
 %   if ( ref($f) ) {
 %     $field = $f->{'field'},
-%     $value = $f->{'value'} ? &{ $f->{'value'} }($svc_x) : $svc_x->$field;
+%     $hack_strict_refs = \&{ $f->{'value'} } if $f->{'value'};
+%     $value = $f->{'value'} ? &$hack_strict_refs($svc_x) : $svc_x->$field;
 %     $type  = $f->{'type'} || 'text',
 %   } else {
 %     $field = $f;
@@ -172,4 +176,6 @@ if ($pkgnum) {
   $custnum = '';
 }
 
+&{ $opt{'svc_callback'} }( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, \%opt ) 
+    if $opt{'svc_callback'};
 </%init>
diff --git a/httemplate/view/svc_dsl.cgi b/httemplate/view/svc_dsl.cgi
new file mode 100644 (file)
index 0000000..a4b2d43
--- /dev/null
@@ -0,0 +1,62 @@
+<% include('elements/svc_Common.html',
+            'table'     => 'svc_dsl',
+            'labels'    => \%labels,
+            'fields' => \@fields,
+           'svc_callback' => $svc_cb,
+           'html_foot' => $html_foot,
+          )
+%>
+<%init>
+my $conf = new FS::Conf;
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
+
+my $fields = FS::svc_dsl->table_info->{'fields'};
+my %labels = map { $_ =>  ( ref($fields->{$_})
+                             ? $fields->{$_}{'label'}
+                             : $fields->{$_}
+                         );
+                 } keys %$fields;
+my @fields = keys %$fields;
+
+my $footer;
+
+my $html_foot = sub {
+    return $footer;
+};
+
+my $svc_cb = sub {
+    my( $cgi,$svc_x, $part_svc,$cust_pkg, $fields1,$opt) = @_;
+
+    my @exports = $part_svc->part_export_dsl_pull;
+    die "more than one DSL-pulling export attached to svcpart ".$part_svc->svcpart
+       if ( scalar(@exports) > 1 );
+    
+    # if no DSL-pulling exports, then just display everything, which is the
+    # default behaviour implemented above
+    return if ( scalar(@exports) == 0 );
+
+    $opt->{'disable_unprovision'} = 1;
+    my $exporttype = @exports[0]->exporttype;  
+
+    # XXX: AJAX auto-pull
+       
+    @fields = qw( svctn first last company username password );
+
+    if($exporttype eq 'ikano') {
+       push @fields, 'isp_chg';
+       push @fields, 'isp_prev';
+       push @fields, 'staticips';
+    }
+    else {
+       # XXX
+    }
+    
+    # hack against "can't use string ... as a subroutine ref while 'strict refs' in use"
+    my $statusSub = \&{'FS::part_export::'.$exporttype.'::status_line'};
+    my $statusLine = &$statusSub($svc_x,$date_format,"<BR>");
+    
+    $footer = "<B>$statusLine</B>";
+
+    # XXX: notes
+};
+</%init>