From: levinse Date: Thu, 25 Nov 2010 01:46:34 +0000 (+0000) Subject: ikano.pm initial commit, svc_dsl UI initial commit, and svc_dsl on-going work, RT7111 X-Git-Tag: TORRUS_1_0_9~68 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=46ef8524cf2e6db7b851967062ce92ffb0773d10 ikano.pm initial commit, svc_dsl UI initial commit, and svc_dsl on-going work, RT7111 --- diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 89f3c63dd..39b1ab7d8 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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 index 000000000..c44db64d1 --- /dev/null +++ b/FS/FS/part_export/ikano.pm @@ -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 +Net::Ikano 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; diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm index 3ed153e0c..164bad079 100644 --- a/FS/FS/part_svc.pm +++ b/FS/FS/part_svc.pm @@ -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) 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 ] diff --git a/FS/FS/svc_dsl.pm b/FS/FS/svc_dsl.pm index 0b4b0d1c7..2e84dea63 100644 --- a/FS/FS/svc_dsl.pm +++ b/FS/FS/svc_dsl.pm @@ -155,8 +155,71 @@ points to. You can ask the object for a copy with the I 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, diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index 3d828478f..142d0c3ae 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -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 ); diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html index e74f44276..2e27f851d 100644 --- a/httemplate/edit/elements/svc_Common.html +++ b/httemplate/edit/elements/svc_Common.html @@ -29,6 +29,13 @@ 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 { diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi index 50bc79c02..d156ccd0a 100755 --- a/httemplate/edit/part_svc.cgi +++ b/httemplate/edit/part_svc.cgi @@ -16,6 +16,7 @@ Service definitions are the templates for items you offer to your customers.
  • svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.)
  • svc_domain - Domains
  • svc_cert - Certificates +
  • svc_dsl - DSL
  • svc_forward - Mail forwarding
  • svc_mailinglist - Mailing list
  • 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 index 000000000..627329a00 --- /dev/null +++ b/httemplate/edit/process/svc_dsl.html @@ -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? + + diff --git a/httemplate/edit/svc_dsl.cgi b/httemplate/edit/svc_dsl.cgi new file mode 100644 index 000000000..3568fbd7d --- /dev/null +++ b/httemplate/edit/svc_dsl.cgi @@ -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 + } +}; + diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html index 618d33eed..25845ddc5 100644 --- a/httemplate/view/elements/svc_Common.html +++ b/httemplate/view/elements/svc_Common.html @@ -52,18 +52,22 @@ function areyousure(href) { Service #<% $svcnum %> % my $url = $opt{'edit_url'} || $p. 'edit/'. $opt{'table'}. '.cgi?'; | Edit this <% $label %> + +% unless ( $opt{'disable_unprovision'} ) { | Unprovision this Service
    +% } <% ntable("#cccccc") %><% 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'}; diff --git a/httemplate/view/svc_dsl.cgi b/httemplate/view/svc_dsl.cgi new file mode 100644 index 000000000..a4b2d4327 --- /dev/null +++ b/httemplate/view/svc_dsl.cgi @@ -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,"
    "); + + $footer = "$statusLine"; + + # XXX: notes +}; +