diff --git a/lib/NGCP/Panel/Controller/API/BillingProfilesItem.pm b/lib/NGCP/Panel/Controller/API/BillingProfilesItem.pm index b278ce4617..2f7e8b3375 100644 --- a/lib/NGCP/Panel/Controller/API/BillingProfilesItem.pm +++ b/lib/NGCP/Panel/Controller/API/BillingProfilesItem.pm @@ -145,9 +145,7 @@ sub PUT :Allow { my $old_resource = { $profile->get_inflated_columns }; my $form = $self->get_form($c); - use Data::Printer; p $profile; $profile = $self->update_profile($c, $profile, $old_resource, $resource, $form); - use Data::Printer; p $profile; last unless $profile; $guard->commit; diff --git a/lib/NGCP/Panel/Controller/API/DomainsItem.pm b/lib/NGCP/Panel/Controller/API/DomainsItem.pm index 2935945005..201b86c268 100644 --- a/lib/NGCP/Panel/Controller/API/DomainsItem.pm +++ b/lib/NGCP/Panel/Controller/API/DomainsItem.pm @@ -105,7 +105,6 @@ sub DELETE :Allow { $domain->delete; try { - use Data::Printer; p $self->config->{features}; unless($c->config->{features}->{debug}) { $self->xmpp_domain_disable($c, $domain); $self->sip_domain_reload($c); diff --git a/lib/NGCP/Panel/Controller/API/Subscribers.pm b/lib/NGCP/Panel/Controller/API/Subscribers.pm index f84024000f..f0d6e70183 100644 --- a/lib/NGCP/Panel/Controller/API/Subscribers.pm +++ b/lib/NGCP/Panel/Controller/API/Subscribers.pm @@ -68,9 +68,7 @@ sub GET :Allow { my (@embedded, @links); my $form = $self->get_form($c); for my $subscriber ($subscribers->search({}, {order_by => {-asc => 'me.id'}})->all) { - say ">>>>>>>>>>> transforming item into resource"; my $resource = $self->transform_resource($c, $subscriber, $form); - use Data::Printer; p $resource; push @embedded, $self->hal_from_item($c, $subscriber, $resource, $form); push @links, Data::HAL::Link->new( relation => 'ngcp:'.$self->resource_name, diff --git a/lib/NGCP/Panel/Controller/API/SubscribersItem.pm b/lib/NGCP/Panel/Controller/API/SubscribersItem.pm index a160f7b227..639215e790 100644 --- a/lib/NGCP/Panel/Controller/API/SubscribersItem.pm +++ b/lib/NGCP/Panel/Controller/API/SubscribersItem.pm @@ -90,8 +90,9 @@ sub OPTIONS :Allow { sub PUT :Allow { my ($self, $c, $id) = @_; - my $guard = $c->model('DB')->txn_scope_guard; - + my $schema = $c->model('DB'); + my $guard = $schema->txn_scope_guard; + { my $preference = $self->require_preference($c); last unless $preference; @@ -103,21 +104,66 @@ sub PUT :Allow { media_type => 'application/json', ); last unless $resource; + my $update = 1; + my $r = $self->prepare_resource($c, $schema, $resource, $update); + last unless $r; + $resource = $r->{resource}; + + my $form = $self->get_form($c); + $subscriber = $self->update_item($c, $subscriber, $r, $resource, $form); + last unless $subscriber; - say ">>>>>>>>>>>>>> new resource:"; - use Data::Printer; p $resource; + $guard->commit; + + if ('minimal' eq $preference) { + $c->response->status(HTTP_NO_CONTENT); + $c->response->header(Preference_Applied => 'return=minimal'); + $c->response->body(q()); + } else { + $resource = $self->transform_resource($c, $subscriber, $form); + my $hal = $self->hal_from_item($c, $subscriber, $resource, $form); + my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( + $hal->http_headers, + ), $hal->as_json); + $c->response->headers($response->headers); + $c->response->header(Preference_Applied => 'return=representation'); + $c->response->body($response->content); + } + } + return; +} + +sub PATCH :Allow { + my ($self, $c, $id) = @_; + my $schema = $c->model('DB'); + my $guard = $schema->txn_scope_guard; + { + my $preference = $self->require_preference($c); + last unless $preference; + + my $subscriber = $self->item_by_id($c, $id); + last unless $self->resource_exists($c, subscriber => $subscriber); + my $json = $self->get_valid_patch_data( + c => $c, + id => $id, + media_type => 'application/json-patch+json', + ops => ["add", "replace", "copy", "remove"], + ); + last unless $json; my $form = $self->get_form($c); my $old_resource = $self->transform_resource($c, $subscriber, $form); + my $resource = $self->apply_patch($c, $old_resource, $json); + last unless $resource; - say ">>>>>>>>>>>>>> old resource:"; - use Data::Printer; p $old_resource; + my $update = 1; + my $r = $self->prepare_resource($c, $schema, $resource, $update); + last unless $r; + $resource = $r->{resource}; - $subscriber = $self->update_item($c, $subscriber, $old_resource, $resource, $form); + $subscriber = $self->update_item($c, $subscriber, $r, $resource, $form); last unless $subscriber; - say ">>>>>>>>>>>>> updated item"; - $guard->commit; if ('minimal' eq $preference) { @@ -125,7 +171,8 @@ sub PUT :Allow { $c->response->header(Preference_Applied => 'return=minimal'); $c->response->body(q()); } else { - my $hal = $self->hal_from_item($c, $subscriber, $form); + $resource = $self->transform_resource($c, $subscriber, $form); + my $hal = $self->hal_from_item($c, $subscriber, $resource, $form); my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( $hal->http_headers, ), $hal->as_json); @@ -133,7 +180,7 @@ sub PUT :Allow { $c->response->header(Preference_Applied => 'return=representation'); $c->response->body($response->content); } - + } return; } diff --git a/lib/NGCP/Panel/Controller/Calls.pm b/lib/NGCP/Panel/Controller/Calls.pm index 0d019e8830..26e190fbf6 100644 --- a/lib/NGCP/Panel/Controller/Calls.pm +++ b/lib/NGCP/Panel/Controller/Calls.pm @@ -47,8 +47,6 @@ sub calls_matrix_ajax :Chained('/') :PathPart('calls/ajax') :Args(0) { $to_epoch = NGCP::Panel::Utils::DateTime::current_local->truncate(to => 'day')->add(days => 1)->epoch(); } - use Data::Printer; p $from_epoch; p $to_epoch; - my $rs = $c->model('DB')->resultset('cdr')->search({ -and => [ start_time => { '>=' => $from_epoch }, diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 26c3d0b897..6ad1e1820e 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -1993,7 +1993,10 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet unless ($subadmin_pbx) { for my $num($subscriber->voip_numbers->all) { next if($subscriber->primary_number && $num->id == $subscriber->primary_number->id); - $num->delete; + $num->update({ + subscriber_id => undef, + reseller_id => undef, + }); } } diff --git a/lib/NGCP/Panel/Field/DataTable.pm b/lib/NGCP/Panel/Field/DataTable.pm index 8995f4fe7b..ded7bc2cab 100644 --- a/lib/NGCP/Panel/Field/DataTable.pm +++ b/lib/NGCP/Panel/Field/DataTable.pm @@ -53,6 +53,7 @@ sub render { sub validate { my ( $self ) = @_; + return $self->add_error($self->label . " is invalid") if($self->required and ( !defined $self->value or !length($self->value) diff --git a/lib/NGCP/Panel/Field/E164.pm b/lib/NGCP/Panel/Field/E164.pm index 896d3868f7..21e70e7b3c 100644 --- a/lib/NGCP/Panel/Field/E164.pm +++ b/lib/NGCP/Panel/Field/E164.pm @@ -50,9 +50,9 @@ sub validate { for my $sub_error( keys %sub_errors ) { $self->add_error($sub_error); } - $self->field('cc')->clear_errors; - $self->field('ac')->clear_errors; - $self->field('sn')->clear_errors; + $self->field('cc')->clear_errors if $self->field('cc'); + $self->field('ac')->clear_errors if $self->field('ac'); + $self->field('sn')->clear_errors if $self->field('sn'); if ($self->has_errors) { #dont add more errors diff --git a/lib/NGCP/Panel/Form/Subscriber/Webfax.pm b/lib/NGCP/Panel/Form/Subscriber/Webfax.pm index 398f673c3d..3f198e6506 100644 --- a/lib/NGCP/Panel/Form/Subscriber/Webfax.pm +++ b/lib/NGCP/Panel/Form/Subscriber/Webfax.pm @@ -65,8 +65,6 @@ sub validate { my $data = $self->field('data')->value; my $upload = $self->field('faxfile')->value; - use Data::Printer; print ">>>>>>>>>>>>>>>>>>>>>> upload\n"; p $data; p $upload; p $self->fields; - unless($data || $upload) { $self->field('faxfile')->add_error("You need to specify a file to fax, if no text is entered in the content field"); } diff --git a/lib/NGCP/Panel/Role/API/Subscribers.pm b/lib/NGCP/Panel/Role/API/Subscribers.pm index 003d995691..ae3cf01b86 100644 --- a/lib/NGCP/Panel/Role/API/Subscribers.pm +++ b/lib/NGCP/Panel/Role/API/Subscribers.pm @@ -12,6 +12,7 @@ use Test::More; use NGCP::Panel::Form::Subscriber::SubscriberAPI; use NGCP::Panel::Utils::XMLDispatcher; use NGCP::Panel::Utils::Prosody; +use NGCP::Panel::Utils::Subscriber; sub get_form { my ($self, $c) = @_; @@ -34,8 +35,7 @@ sub transform_resource { } $form //= $self->get_form($c); - - $self->validate_form( + last unless $self->validate_form( c => $c, resource => \%resource, form => $form, @@ -167,7 +167,7 @@ sub get_billing_profile { } sub prepare_resource { - my ($self, $c, $schema, $resource) = @_; + my ($self, $c, $schema, $resource, $update) = @_; my $domain; if($resource->{domain}) { @@ -189,7 +189,6 @@ sub prepare_resource { delete $resource->{domain}; $resource->{domain_id} = $domain->id; } - $resource->{e164} = delete $resource->{primary_number}; $resource->{contract_id} = delete $resource->{customer_id}; $resource->{status} //= 'active'; @@ -221,7 +220,7 @@ sub prepare_resource { my $customer = $self->get_customer($c, $resource->{contract_id}); return unless($customer); - if(defined $customer->max_subscribers && $customer->voip_subscribers->search({ + if(!$update && defined $customer->max_subscribers && $customer->voip_subscribers->search({ status => { '!=' => 'terminated' } })->count >= $customer->max_subscribers) { @@ -241,7 +240,6 @@ sub prepare_resource { contract => $customer, show_locked => 1, ); - use Data::Printer; say ">>>>>>>>>>>>>>>>>>>> subs"; p $subs; my $admin_subscribers = NGCP::Panel::Utils::Subscriber::get_admin_subscribers( voip_subscribers => $subs->{subscribers}); unless(@{ $admin_subscribers }) { @@ -278,9 +276,16 @@ sub prepare_resource { domain_id => $resource->{domain_id}, status => { '!=' => 'terminated' }, }); - if($subscriber) { - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber already exists."); - return; + if($update) { + unless($subscriber) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber does not exist."); + return; + } + } else { + if($subscriber) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber already exists."); + return; + } } my $alias_numbers = []; @@ -297,7 +302,6 @@ sub prepare_resource { } elsif(ref $resource->{alias_numbers} eq "HASH") { push @{ $alias_numbers }, { e164 => $resource->{alias_numbers} }; } else { - use Data::Printer; p $resource->{alias_numbers}; say ">>>>>>>>>>> '".(ref $resource->{alias_numbers})."'"; $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid parameter 'alias_numbers', must be hash or array of hashes."); return; } @@ -320,25 +324,44 @@ sub prepare_resource { } sub update_item { - my ($self, $c, $item, $old_resource, $resource, $form) = @_; + my ($self, $c, $item, $full_resource, $resource, $form) = @_; - $form //= $self->get_form($c); + my $subscriber = $item; + my $customer = $full_resource->{customer}; + my $admin = $full_resource->{admin}; + my $alias_numbers = $full_resource->{alias_numbers}; + my $preferences = $full_resource->{preferences}; - print ">>>>>>>>>>>>> validate before update\n"; + NGCP::Panel::Utils::Subscriber::update_subscriber_numbers( + schema => $c->model('DB'), + primary_number => $resource->{e164}, + alias_numbers => $alias_numbers, + reseller_id => $customer->contact->reseller_id, + subscriber_id => $subscriber->id, + ); - $resource->{e164} = delete $resource->{primary_number}; + my $billing_res = { + external_id => $resource->{external_id}, + status => $resource->{status}, + }; + my $provisioning_res = { + password => $resource->{password}, + webusername => $resource->{webusername}, + webpassword => $resource->{webpassword}, + admin => $resource->{administrative}, + is_pbx_group => $resource->{is_pbx_group}, + pbx_group_id => $resource->{pbx_group_id}, + modify_timestamp => NGCP::Panel::Utils::DateTime::current_local, - return unless $self->validate_form( - c => $c, - form => $form, - resource => $resource, - ); + }; + + $subscriber->update($billing_res); + $subscriber->provisioning_voip_subscriber->update($provisioning_res); + $subscriber->discard_changes; - print ">>>>>>>>>>>>> update\n"; - $item->update($resource); - print ">>>>>>>>>>>>> done update\n"; + # TODO: status handling (termination, ...) - return $item; + return $subscriber; } 1; diff --git a/lib/NGCP/Panel/Utils/Subscriber.pm b/lib/NGCP/Panel/Utils/Subscriber.pm index 685711e344..a6d3108f58 100644 --- a/lib/NGCP/Panel/Utils/Subscriber.pm +++ b/lib/NGCP/Panel/Utils/Subscriber.pm @@ -138,7 +138,6 @@ sub create_subscriber { my $reseller = $contract->contact->reseller; my $billing_domain = $schema->resultset('domains') ->find($params->{domain}{id} // $params->{domain_id}); - use Data::Printer; print ">>>>>>>>>>>>>>>>>>>>>>>>>>> billing_dom\n"; p $billing_domain; my $prov_domain = $schema->resultset('voip_domains') ->find({domain => $billing_domain->domain}); @@ -328,8 +327,14 @@ sub update_subscriber_numbers { id => $subscriber_id, }); my $prov_subs = $billing_subs->provisioning_voip_subscriber; + my @nums = (); my @dbnums = (); - if (defined $primary_number) { + if(exists $params{primary_number} && !defined $primary_number) { + $billing_subs->update({ + primary_number_id => undef, + }); + } + elsif(defined $primary_number) { my $old_cc; my $old_ac; @@ -383,12 +388,21 @@ sub update_subscriber_numbers { primary_number_id => $number->id, }); if(defined $prov_subs) { - $schema->resultset('voip_dbaliases')->create({ + my $dbalias = $prov_subs->voip_dbaliases->find({ username => $cli, - domain_id => $prov_subs->domain->id, - subscriber_id => $prov_subs->id, - is_primary => 1, }); + if($dbalias) { + if(!$dbalias->is_primary) { + $dbalias->update({ is_primary => 1 }); + } + } else { + $dbalias = $prov_subs->voip_dbaliases->create({ + username => $cli, + domain_id => $prov_subs->domain->id, + is_primary => 1, + }); + } + push @dbnums, $dbalias->id; if(defined $prov_subs->voicemail_user) { $prov_subs->voicemail_user->update({ mailbox => $cli, @@ -456,9 +470,6 @@ sub update_subscriber_numbers { if(defined $alias_numbers && ref($alias_numbers) eq 'ARRAY') { - # note that this only adds new alias numbers - # old entries in voip_numbers and voip_dbaliases are usually deleted - # before calling this sub my $number; for my $alias(@$alias_numbers) { @@ -488,15 +499,38 @@ sub update_subscriber_numbers { subscriber_id => $subscriber_id, }); } - $schema->resultset('voip_dbaliases')->create({ - username => $number->cc . ($number->ac // '') . $number->sn, - subscriber_id => $prov_subs->id, - domain_id => $prov_subs->domain->id, - is_primary => 0, + push @nums, $number->id; + my $cli = $number->cc . ($number->ac // '') . $number->sn; + my $dbalias = $prov_subs->voip_dbaliases->find({ + username => $cli, }); + if($dbalias) { + if($dbalias->is_primary) { + $dbalias->update({ is_primary => 0 }); + } + } else { + $dbalias = $prov_subs->voip_dbaliases->create({ + username => $cli, + domain_id => $prov_subs->domain->id, + is_primary => 0, + }); + } + push @dbnums, $dbalias->id; } } + push @nums, $billing_subs->primary_number_id + if($billing_subs->primary_number_id); + $billing_subs->voip_numbers->search({ + id => { 'not in' => \@nums }, + })->update_all({ + subscriber_id => undef, + reseller_id => undef, + }); + $prov_subs->voip_dbaliases->search({ + id => { 'not in' => \@dbnums }, + })->delete; + return; }