From 6fd39bf8dfa989aaedea59e5e3cd609642f9e024 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 25 Feb 2016 17:10:56 -0800 Subject: [PATCH] sipwise export, part 2 --- FS/FS/Schema.pm | 1 + FS/FS/part_export/sipwise.pm | 143 +++++++++++++-------- FS/FS/svc_acct.pm | 11 ++ FS/FS/svc_phone.pm | 14 +- httemplate/edit/svc_phone.cgi | 3 + .../elements/tr-select-svc_phone-forward.html | 50 +++++++ httemplate/view/svc_phone.cgi | 17 +++ 7 files changed, 181 insertions(+), 58 deletions(-) create mode 100644 httemplate/elements/tr-select-svc_phone-forward.html diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 36418acca..d20385e8a 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -5842,6 +5842,7 @@ sub tables_hashref { 'pbxsvc', 'int', 'NULL', '', '', '', 'domsvc', 'int', 'NULL', '', '', '', 'locationnum', 'int', 'NULL', '', '', '', + 'forward_svcnum', 'int', 'NULL', '', '', '', 'forwarddst', 'varchar', 'NULL', 15, '', '', 'email', 'varchar', 'NULL', 255, '', '', 'lnp_status', 'varchar', 'NULL', $char_d, '', '', diff --git a/FS/FS/part_export/sipwise.pm b/FS/FS/part_export/sipwise.pm index 690a14cbf..d2af7daf0 100644 --- a/FS/FS/part_export/sipwise.pm +++ b/FS/FS/part_export/sipwise.pm @@ -5,7 +5,6 @@ use strict; use FS::Record qw(qsearch qsearchs dbh); use Tie::IxHash; -use Carp; use LWP::UserAgent; use URI; use Cpanel::JSON::XS; @@ -13,6 +12,7 @@ use HTTP::Request::Common qw(GET POST PUT DELETE); use FS::Misc::DateTime qw(parse_datetime); use DateTime; use Number::Phone; +use Try::Tiny; our $me = '[sipwise]'; our $DEBUG = 2; @@ -34,7 +34,7 @@ tie my %options, 'Tie::IxHash', tie my %roles, 'Tie::IxHash', 'subscriber' => { label => 'Subscriber', - svcdb => 'svc_phone', + svcdb => 'svc_acct', multiple => 1, }, 'did' => { label => 'DID', @@ -44,7 +44,7 @@ tie my %roles, 'Tie::IxHash', ; our %info = ( - 'svc' => [qw( svc_phone )], + 'svc' => [qw( svc_acct svc_phone )], 'desc' => 'Provision to a Sipwise sip:provider server', 'options' => \%options, 'roles' => \%roles, @@ -52,12 +52,12 @@ our %info = (

Export to a sip:provider server.

This requires two service definitions to be configured on the same package:

    -
  1. A phone service for a SIP client account ("subscriber"). The - phonenum will be the SIP username. The domsvc should point +
  2. An account service for a SIP client account ("subscriber"). The + username will be the SIP username. The domsvc should point to a domain service to use as the SIP domain name.
  3. A phone service for a DID. The phonenum here will be a PSTN - number. The forwarddst field should be set to the SIP username - of the subscriber who should receive calls directed to this number.
  4. + number. The forward_svcnum field should be set to the account that + will receive calls at this number.

Export options: @@ -68,63 +68,61 @@ END sub export_insert { my($self, $svc_x) = (shift, shift); - local $@; + my $error; my $role = $self->svc_role($svc_x); if ( $role eq 'subscriber' ) { - eval { $self->insert_subscriber($svc_x) }; - return "$me $@" if $@; + try { $self->insert_subscriber($svc_x) } + catch { $error = $_ }; } elsif ( $role eq 'did' ) { - # only export the DID if it's set to forward to somewhere... - return if $svc_x->forwarddst eq ''; - my $subscriber = qsearchs('svc_phone', { phonenum => $svc_x->forwarddst }); - # and there is a service for the forwarding destination... - return if !$subscriber; - # and that service is managed by this export. - return if !$self->svc_role($subscriber); - - eval { $self->replace_subscriber($subscriber) }; - return "$me $@" if $@; + try { $self->export_did($svc_x) } + catch { $error = $_ }; } + return "$me $error" if $error; ''; } sub export_replace { my ($self, $svc_new, $svc_old) = @_; my $role = $self->svc_role($svc_new); - local $@; + + my $error; if ( $role eq 'subscriber' ) { - eval { $self->replace_subscriber($svc_new, $svc_old) }; + + try { $self->replace_subscriber($svc_new, $svc_old) } + catch { $error = $_ }; + } elsif ( $role eq 'did' ) { - eval { $self->replace_did($svc_new, $svc_old) }; + + try { $self->export_did($svc_new, $svc_old) } + catch { $error = $_ }; + } - return "$me $@" if $@; + return "$me $error" if $error; ''; } sub export_delete { my ($self, $svc_x) = (shift, shift); my $role = $self->svc_role($svc_x); - local $@; + my $error; + if ( $role eq 'subscriber' ) { # no need to remove DIDs from it, just drop the subscriber record - eval { $self->delete_subscriber($svc_x) }; + try { $self->delete_subscriber($svc_x) } + catch { $error = $_ }; } elsif ( $role eq 'did' ) { - return if !$svc_x->forwarddst; - my $subscriber = qsearchs('svc_phone', { phonenum => $svc_x->forwarddst }); - return if !$subscriber; - return if !$self->svc_role($subscriber); - - eval { $self->delete_did($svc_x, $subscriber) }; + try { $self->export_did($svc_x) } + catch { $error = $_ }; } - return "$me $@" if $@; + return "$me $error" if $error; ''; } @@ -194,7 +192,7 @@ sub find_or_create_customer { ] ); if (!$billing_profile) { - croak "can't find billing profile '". $self->option('billing_profile') . "'"; + die "can't find billing profile '". $self->option('billing_profile') . "'\n"; } my $bpid = $billing_profile->{id}; @@ -255,6 +253,41 @@ sub find_or_create_domain { ); } +######## +# DIDS # +######## + +=item acct_for_did SVC_PHONE + +Returns the subscriber svc_acct linked to SVC_PHONE. + +=cut + +sub acct_for_did { + my $self = shift; + my $svc_phone = shift; + my $svcnum = $svc_phone->forward_svcnum or return; + my $svc_acct = FS::svc_acct->by_key($svcnum) or return; + $self->svc_role($svc_acct) eq 'subscriber' or return; + $svc_acct; +} + +=item export_did NEW, OLD + +Refreshes the subscriber information for the service the DID was linked to +previously, and the one it's linked to now. + +=cut + +sub export_did { + my $self = shift; + my ($new, $old) = @_; + if ( $old and $new->forward_svcnum ne $old->forward_svcnum ) { + $self->replace_subscriber( $self->acct_for_did($old) ); + } + $self->replace_subscriber( $self->acct_for_did($new) ); +} + ############### # SUBSCRIBERS # ############### @@ -270,7 +303,7 @@ sub get_subscriber { my $svc = shift; my $svcnum = $svc->svcnum; - my $svcid = "svc_phone#$svcnum"; + my $svcid = "svc#$svcnum"; my $pkgnum = $svc->cust_svc->pkgnum; my $custid = "cust_pkg#$pkgnum"; @@ -291,12 +324,11 @@ sub did_numbers_for_svc { my $self = shift; my $svc = shift; my @numbers; - my @possible_dids = qsearch({ + my @dids = qsearch({ 'table' => 'svc_phone', - 'hashref' => { 'forwarddst' => $svc->phonenum }, - 'order_by' => ' ORDER BY phonenum' + 'hashref' => { 'forward_svcnum' => $svc->svcnum } }); - foreach my $did (@possible_dids) { + foreach my $did (@dids) { # only include them if they're interesting to this export if ( $self->svc_role($did) eq 'did' ) { my $phonenum; @@ -308,7 +340,7 @@ sub did_numbers_for_svc { $phonenum = Number::Phone->new($country, $did->phonenum); } if (!$phonenum) { - croak "Can't process phonenum ".$did->countrycode . $did->phonenum; + die "Can't process phonenum ".$did->countrycode . $did->phonenum . "\n"; } push @numbers, { 'cc' => $phonenum->country_code, @@ -325,7 +357,7 @@ sub insert_subscriber { my $svc = shift; my $cust = $self->find_or_create_customer($svc); - my $svcid = "svc_phone#" . $svc->svcnum; + my $svcid = "svc#" . $svc->svcnum; my $status = $svc->cust_svc->cust_pkg->susp ? 'locked' : 'active'; my $domain = $self->find_or_create_domain($svc->domain); @@ -336,14 +368,13 @@ sub insert_subscriber { { 'alias_numbers' => \@numbers, 'customer_id' => $cust->{id}, - 'display_name' => $svc->phone_name, + 'display_name' => $svc->finger, 'domain_id' => $domain->{id}, - 'email' => $svc->email, 'external_id' => $svcid, - 'password' => $svc->sip_password, + 'password' => $svc->_password, 'primary_number' => $first_number, 'status' => $status, - 'username' => $svc->phonenum, + 'username' => $svc->username, } ); } @@ -351,8 +382,8 @@ sub insert_subscriber { sub replace_subscriber { my $self = shift; my $svc = shift; - my $old = shift; - my $svcid = "svc_phone#" . $svc->svcnum; + my $old = shift || $svc->replace_old; + my $svcid = "svc#" . $svc->svcnum; my $cust = $self->find_or_create_customer($svc); my $status = $svc->cust_svc->cust_pkg->susp ? 'locked' : 'active'; @@ -365,7 +396,7 @@ sub replace_subscriber { if ( $subscriber ) { my $id = $subscriber->{id}; - if ( $svc->phonenum ne $old->phonenum ) { + if ( $svc->username ne $old->username ) { # have to delete and recreate $self->api_delete("subscribers/$id"); $self->insert_subscriber($svc); @@ -374,14 +405,14 @@ sub replace_subscriber { { 'alias_numbers' => \@numbers, 'customer_id' => $cust->{id}, - 'display_name' => $svc->phone_name, + 'display_name' => $svc->finger, 'domain_id' => $domain->{id}, 'email' => $svc->email, 'external_id' => $svcid, - 'password' => $svc->sip_password, + 'password' => $svc->_password, 'primary_number' => $first_number, 'status' => $status, - 'username' => $svc->phonenum, + 'username' => $svc->username, } ); } @@ -394,7 +425,7 @@ sub replace_subscriber { sub delete_subscriber { my $self = shift; my $svc = shift; - my $svcid = "svc_phone#" . $svc->svcnum; + my $svcid = "svc#" . $svc->svcnum; my $pkgnum = $svc->cust_svc->pkgnum; my $custid = "cust_pkg#$pkgnum"; @@ -490,7 +521,7 @@ sub api_create { if ( $result->{location} ) { return $self->api_request('GET', $result->{location}); } else { - croak $result->{message}; + die $result->{message} . "\n"; } } @@ -508,7 +539,7 @@ sub api_update { my ($endpoint, $content) = @_; my $result = $self->api_request('PUT', $endpoint, $content); if ( $result->{message} ) { - croak $result->{message}; + die $result->{message} . "\n"; } return; } @@ -529,7 +560,7 @@ sub api_delete { warn "$me api_delete $endpoint: does not exist\n"; return; } elsif ( $result->{message} ) { - croak $result->{message}; + die $result->{message} . "\n"; } return; } @@ -591,7 +622,7 @@ sub api_request { if ( $@ ) { # then it can't be parsed; probably a low-level error of some kind. warn "$me Parse error.\n".$response->content."\n\n"; - croak $response->content; + die "$me Parse error:".$response->content . "\n"; } } if ( $response->header('Location') ) { diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index c2f5d7122..67fce4129 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -847,6 +847,17 @@ sub delete { } } + foreach my $svc_phone ( + qsearch( 'svc_phone', { 'forward_svcnum' => $self->svcnum }) + ) { + $svc_phone->set('forward_svcnum', ''); + my $error = $svc_phone->replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + my $error = $self->delete_password_history || $self->SUPER::delete; # usergroup here if ( $error ) { diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm index cf9d9b440..3a58b465c 100644 --- a/FS/FS/svc_phone.pm +++ b/FS/FS/svc_phone.pm @@ -93,9 +93,14 @@ Voicemail PIN Optional svcnum from svc_pbx +=item forward_svcnum + +Forward destination, if it's another service. Some exports use this +configuration. + =item forwarddst -Forwarding destination +Forwarding destination, if it's not a service. =item email @@ -225,6 +230,9 @@ sub table_info { 'forwarddst' => { label => 'Forward Destination', %dis2, }, + 'forward_svcnum' => { label => 'Route to service', + %dis2, + }, 'email' => { label => 'Email', %dis2, }, @@ -529,7 +537,9 @@ sub check { || $self->ut_alphan('sms_account') || $self->ut_numbern('max_simultaneous') || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum') - || $self->ut_numbern('forwarddst') + || $self->ut_numbern('forward_svcnum') + || $self->ut_foreign_keyn('forward_svcnum', 'cust_svc', 'svcnum') + || $self->ut_textn('forwarddst') || $self->ut_textn('email') || $self->ut_numbern('lrn') || $self->ut_numbern('lnp_desired_due_date') diff --git a/httemplate/edit/svc_phone.cgi b/httemplate/edit/svc_phone.cgi index f1471e283..e74ffbb84 100644 --- a/httemplate/edit/svc_phone.cgi +++ b/httemplate/edit/svc_phone.cgi @@ -60,6 +60,9 @@ my $begin_callback = sub { type => 'text', maxlength => $conf->config('svc_phone-phone_name-max_length'), }, + { field => 'forward_svcnum', + type => 'select-svc_phone-forward', + }, 'forwarddst', 'email', diff --git a/httemplate/elements/tr-select-svc_phone-forward.html b/httemplate/elements/tr-select-svc_phone-forward.html new file mode 100644 index 000000000..ef9ef17e7 --- /dev/null +++ b/httemplate/elements/tr-select-svc_phone-forward.html @@ -0,0 +1,50 @@ +% if ( $hide ) { + + + +% } else { + <& tr-select-table.html, + 'table' => 'svc_acct', # for now + 'name_col' => 'email', + 'order_by' => 'order by username', + 'empty_label' => ' ', + %select_hash, + %opt + &> +% } +<%init> + +my %opt = @_; +my $pkgnum = $opt{pkgnum}; +my $svcpart = $opt{svcpart}; + +my $field = $opt{'field'} ||= 'forward_svcnum'; + +my $part_svc = FS::part_svc->by_key($svcpart); +# kludgey assumptions for now: +# - this is only used to route DIDs to their real destinations +# - which is a svc_acct +# - in the same package (part_export::svc_with_role assumes this) +# - and shares an export + +my $cust_pkg = FS::cust_pkg->by_key($pkgnum); +my @svcparts; +foreach my $part_export ( $part_svc->part_export ) { + foreach my $export_svc ( $part_export->export_svc ) { + push @svcparts, $export_svc->svcpart; + } +} + +$pkgnum =~ /^(\d+)$/ or die "bad pkgnum $pkgnum"; + +my %select_hash = ( + 'addl_from' => ' JOIN cust_svc USING (svcnum) ', + 'extra_sql' => "WHERE pkgnum = $pkgnum AND svcpart IN(". + join(',', @svcparts) . ")" +); + +my $hide = 0; +$hide = 1 if $part_svc->part_svc_column($field) eq 'F'; +$hide = 1 if !@svcparts; + + diff --git a/httemplate/view/svc_phone.cgi b/httemplate/view/svc_phone.cgi index ab69c4f6f..416f13841 100644 --- a/httemplate/view/svc_phone.cgi +++ b/httemplate/view/svc_phone.cgi @@ -18,6 +18,23 @@ my %labels = map { $_ => ( ref($fields->{$_}) my @fields = qw( countrycode phonenum sim_imsi ); push @fields, 'domain' if $conf->exists('svc_phone-domain'); + +$labels{forward_svcnum} = mt('Route to service'); +push @fields, { field => 'forward_svcnum', + link => [ $p.'view/cust_svc.cgi?', 'forward_svcnum' ], + value_callback => sub { + my $self = shift; + if ($self->forward_svcnum) { + my $cust_svc = FS::cust_svc->by_key($self->forward_svcnum); + if ( $cust_svc ) { + return $cust_svc->svc_x->label; + } + } + ''; + }, + }; + + push @fields, qw( pbx_title ); $labels{pbx_title} = 'PBX'; -- 2.11.0