From 0e17979577dacf0277de470ca7ee724f68a56cd5 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Fri, 10 Mar 2017 16:40:07 -0800 Subject: [PATCH] extend vitelity integration: start and complete port-in, RT#73618 --- FS/FS/Cron/lnp_vitelity.pm | 14 ++++++ FS/FS/Schema.pm | 3 ++ FS/FS/part_export/vitelity.pm | 105 +++++++++++++++++++++++++++++++++++++++--- FS/FS/svc_phone.pm | 16 +++++++ FS/bin/freeside-daily | 4 ++ 5 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 FS/FS/Cron/lnp_vitelity.pm diff --git a/FS/FS/Cron/lnp_vitelity.pm b/FS/FS/Cron/lnp_vitelity.pm new file mode 100644 index 000000000..ef4ceed62 --- /dev/null +++ b/FS/FS/Cron/lnp_vitelity.pm @@ -0,0 +1,14 @@ +package FS::Cron::lnp_vitelity; +use base qw( Exporter ); + +use vars qw( @EXPORT_OK ); +use FS::Record qw( qsearch ); +use FS::part_export; + +@EXPORT_OK = qw( lnp_vitelity ); + +sub lnp_vitelity { + $_->check_lnp foreach qsearch('part_export', {exporttype=>'vitelity'} ); +} + +1; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index b6d0f074f..d3619aa28 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -5993,6 +5993,9 @@ sub tables_hashref { 'lnp_other_provider', 'varchar', 'NULL', $char_d, '', '', 'lnp_other_provider_account', 'varchar', 'NULL', $char_d, '', '', 'lnp_reject_reason', 'varchar', 'NULL', $char_d, '', '', + 'lnp_portid', 'varchar', 'NULL', $char_d, '', '', + 'lnp_signature', 'char', 'NULL', 1, '', '', + 'lnp_bill', 'char', 'NULL', 1, '', '', 'sms_carrierid', 'int', 'NULL', '', '', '', 'sms_account', 'varchar', 'NULL', $char_d, '', '', 'max_simultaneous', 'int', 'NULL', '', '', '', diff --git a/FS/FS/part_export/vitelity.pm b/FS/FS/part_export/vitelity.pm index 51731260e..fe56ce247 100644 --- a/FS/FS/part_export/vitelity.pm +++ b/FS/FS/part_export/vitelity.pm @@ -3,8 +3,10 @@ use base qw( FS::part_export ); use vars qw( %info ); use Tie::IxHash; +use Geo::StreetAddress::US; use FS::Record qw( qsearch dbh ); use FS::phone_avail; +use FS::svc_phone; tie my %options, 'Tie::IxHash', 'login' => { label=>'Vitelity API login' }, @@ -258,6 +260,22 @@ sub vitelity_command { $vitelity->$command(@args); } +sub vitelity_lnp_command { + my( $self, $command, @args ) = @_; + + eval "use Net::Vitelity 0.04;"; + die $@ if $@; + + my $vitelity = Net::Vitelity->new( + 'login' => $self->option('login'), + 'pass' => $self->option('pass'), + 'apitype' => 'lnp', + #'debug' => $debug, + ); + + $vitelity->$command(@args); +} + sub _export_insert { my( $self, $svc_phone ) = (shift, shift); @@ -265,6 +283,47 @@ sub _export_insert { #we want to provision and catch errors now, not queue + #porting a number in? different code path + if ( $svc_phone->lnp_status eq 'portingin' ) { + + my %location = $svc_phone->location_hash; + my $sa = Geo::StreetAddress::US->parse_location( $location{'address1'} ); + + my $result = $self->vitelity_lnp_command('addport', + 'portnumber' => $svc_phone->phonenum, + 'partial' => 'no', + 'wireless' => 'no', + 'carrier' => $svc_phone->lnp_other_provider, + 'company' => $svc_phone->cust_svc->cust_pkg->cust_main->company, + 'accnumber' => $svc_phone->lnp_other_provider_account, + 'name' => $svc_phone->phone_name_or_cust, + 'streetnumber' => $sa->{number}, + 'streetprefix' => $sa->{prefix}, + 'streetname' => $sa->{street}. ' '. $street{type}, + 'streetsuffix' => $sa->{suffix}, + 'unit' => ( $sa->{sec_unit_num} + ? $sa->{sec_unit_type}. ' '. $sa->{sec_unit_num} + : '' + ), + 'city' => $location{'city'}, + 'state' => $location{'state'}, + 'zip' => $location{'zip'}, + 'billnumber' => $svc_phone->phonenum, #?? do we need a new field for this? + 'contactnumber' => $svc_phone->cust_svc->cust_pkg->cust_main->daytime, + ); + + if ( $result =~ /^ok:/i ) { + my($ok, $portid, $sig, $bill) = split(':', $result); + $svc_phone->lnp_portid($portid); + $svc_phone->lnp_signature('Y') if $sig =~ /y/i; + $svc_phone->lnp_bill('Y') if $bill =~ /y/i; + return $svc_phone->replace; + } else { + return "Error initiating Vitelity port: $result"; + } + + } + ### # 1. provision the DID ### @@ -329,18 +388,12 @@ sub e911send { my %location = $svc_phone->location_hash; my %e911send = ( 'did' => $svc_phone->phonenum, + 'name' => $svc_phone->phone_name_or_cust, 'address' => $location{'address1'}, 'city' => $location{'city'}, 'state' => $location{'state'}, 'zip' => $location{'zip'}, ); - if ( $svc_phone->phone_name ) { - $e911send{'name'} = $svc_phone->phone_name; - } else { - my $cust_main = $svc_phone->cust_svc->cust_pkg->cust_main; - $e911send{'name'} = $cust_main->company || $cust_main->first. ' '. - $cust_main->get('last'); - } if ( $location{address2} =~ /^\s*(\w+)\W*(\d+)\s*$/ ) { $e911send{'unittype'} = $1; $e911send{'unitnumber'} = $2; @@ -417,5 +470,43 @@ sub _export_unsuspend { ''; } +sub check_lnp { + my $self = shift; + + my $in_svcpart = 'IN ('. join( ',', map $_->svcpart, $self->export_svc). ')'; + + foreach my $svc_phone ( + qsearch({ 'table' => 'svc_phone', + 'hashref' => {lnp_status=>'portingin'}, + 'extra_sql' => "AND svcpart $in_svcpart", + }) + ) { + + my $result = $self->vitelity_lnp_command('checkstatus', + 'portid'=>$svc_phone->lnp_portid, + ); + + #XXX what $result values mean the port is done? + + if ( $result =~ /^complete$/ ) { #"complete"? nfi + + $svc_phone->lnp_status('portedin'); + my $error = $self->_export_insert($svc_phone); + if ( $error ) { + #XXX log this using our internal log instead, so we can alert on it + # properly + warn "ERROR provisioning ported-in DID ". $svc_phone->phonenum. ": $error"; + } else { + $error = $svc_phone->replace; #to set the lnp_status + #XXX log this using our internal log instead, so we can alert on it + warn "ERROR setting lnp_status for DID ". $svc_phone->phonenum. ": $error" if $error; + } + + } + + } + +} + 1; diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm index 2b2db8c81..725092edb 100644 --- a/FS/FS/svc_phone.pm +++ b/FS/FS/svc_phone.pm @@ -132,6 +132,19 @@ Account number of other provider. See lnp_other_provider. See lnp_status. If lnp_status is portin-reject or portout-reject, this is an optional reject reason. +=item lnp_portid + +Port identifier from porting provider, for checking status + +=item lnp_signature + +Boolean (empty or `Y') indicating if a signature is required for the port + +=item lnp_bill + +Boolean (empty or `Y') indicating if a copy of an existing bill is required for +the port + =item e911_class Class of Service for E911 service (per the NENA 2.1 standard). @@ -549,6 +562,9 @@ sub check { 'native', 'portin-reject', 'portout-reject']) || $self->ut_enumn('portable', ['','Y']) || $self->ut_textn('lnp_reject_reason') + || $self->ut_textn('lnp_portid') + || $self->ut_enumn('lnp_signature', ['','Y']) + || $self->ut_enumn('lnp_bill', ['','Y']) || $self->ut_domainn('sip_server') ; return $error if $error; diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily index e1463f5da..b12f90b23 100755 --- a/FS/bin/freeside-daily +++ b/FS/bin/freeside-daily @@ -24,6 +24,10 @@ $log->info('start'); use FS::Cron::nms_report qw(nms_report); nms_report(%opt); +#you can skip this by not having any vitelity exports configured +use FS::Cron::lnp_vitelity; +lnp_vitelity(%opt); + #no way to skip this yet, but should be harmless/quick use FS::Cron::expire_banned_pay qw(expire_banned_pay); expire_banned_pay(%opt); -- 2.11.0