diff --git a/debian/control b/debian/control index 5191671f84..36780df4af 100644 --- a/debian/control +++ b/debian/control @@ -23,7 +23,7 @@ Build-Depends: debhelper (>= 8), libdatetime-format-iso8601-perl, libdatetime-format-mysql-perl, libdatetime-format-rfc3339-perl, - libtime-fake-perl, + libtime-warp-perl, libtext-table-perl, libdbix-class-resultset-recursiveupdate-perl (>= 0.30~), libdevel-cover-perl, @@ -97,7 +97,7 @@ Depends: gettext, libdatetime-format-mysql-perl, libdatetime-format-rfc3339-perl, libdatetime-perl, - libtime-fake-perl, + libtime-warp-perl, libtext-table-perl, libdbix-class-resultset-recursiveupdate-perl (>= 0.30~), libemail-mime-perl, diff --git a/lib/NGCP/Panel/Controller/API/Customers.pm b/lib/NGCP/Panel/Controller/API/Customers.pm index 7b4b887321..bdefdd0007 100644 --- a/lib/NGCP/Panel/Controller/API/Customers.pm +++ b/lib/NGCP/Panel/Controller/API/Customers.pm @@ -98,6 +98,17 @@ class_has 'query_params' => ( second => sub { }, }, }, + { + param => 'package_id', + description => 'Filter for customers with specific profile package id', + query => { + first => sub { + my $q = shift; + { 'me.profile_package_id' => $q }; + }, + second => sub { }, + }, + }, ]}, ); diff --git a/lib/NGCP/Panel/Controller/API/ProfilePackagesItem.pm b/lib/NGCP/Panel/Controller/API/ProfilePackagesItem.pm index 57312f9900..316a454588 100644 --- a/lib/NGCP/Panel/Controller/API/ProfilePackagesItem.pm +++ b/lib/NGCP/Panel/Controller/API/ProfilePackagesItem.pm @@ -178,7 +178,37 @@ sub PUT :Allow { return; } -# we don't allow to DELETE a profile package +sub DELETE :Allow { + my ($self, $c, $id) = @_; + my $guard = $c->model('DB')->txn_scope_guard; + { + last unless $self->valid_id($c, $id); + my $package = $self->item_by_id($c, $id); + last unless $self->resource_exists($c, profilepackage => $package); + + unless($package->get_column('contract_cnt') == 0) { + $self->error($c, HTTP_LOCKED, "Cannnot delete profile package that is still assigned to contracts"); + last; + } + unless($package->get_column('voucher_cnt') == 0) { + $self->error($c, HTTP_LOCKED, "Cannnot delete profile package that is assigned to vouchers"); + last; + } + + last unless $self->add_delete_journal_item_hal($c,sub { + my $self = shift; + my ($c) = @_; + #my $_form = $self->get_form($c); + return $self->hal_from_item($c, $package, "profilepackages"); }); + + $package->delete; + $guard->commit; + + $c->response->status(HTTP_NO_CONTENT); + $c->response->body(q()); + } + return; +} sub item_base_journal :Journal { my $self = shift @_; diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 6491c9732a..6162241a63 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -110,6 +110,29 @@ sub ajax_reseller_filter :Chained('list_customer') :PathPart('ajax/reseller') :A $c->detach( $c->view("JSON") ); } +sub ajax_package_filter :Chained('list_customer') :PathPart('ajax/package') :Args(1) { + my ($self, $c, $package_id) = @_; + + unless($package_id && $package_id->is_int) { + NGCP::Panel::Utils::Message->error( + c => $c, + log => 'Invalid profile package id detected', + desc => $c->loc('Invalid profile package id detected'), + ); + $c->response->redirect($c->uri_for()); + return; + } + + my $rs = $c->stash->{contract_select_rs}->search_rs({ + 'profile_package_id' => $package_id, + },undef); + my $package_customer_columns = NGCP::Panel::Utils::Datatables::set_columns($c, [ + NGCP::Panel::Utils::ProfilePackages::get_customer_datatable_cols($c) + ]); + NGCP::Panel::Utils::Datatables::process($c, $rs, $package_customer_columns); + $c->detach( $c->view("JSON") ); +} + sub create :Chained('list_customer') :PathPart('create') :Args(0) { my ($self, $c) = @_; diff --git a/lib/NGCP/Panel/Controller/Package.pm b/lib/NGCP/Panel/Controller/Package.pm index 5cc02c2548..6c777c963c 100644 --- a/lib/NGCP/Panel/Controller/Package.pm +++ b/lib/NGCP/Panel/Controller/Package.pm @@ -8,6 +8,7 @@ use NGCP::Panel::Form::ProfilePackage::Reseller; use NGCP::Panel::Utils::Message; use NGCP::Panel::Utils::Navigation; use NGCP::Panel::Utils::ProfilePackages qw(); +use NGCP::Panel::Utils::Voucher qw(); sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) { my ($self, $c) = @_; @@ -38,11 +39,11 @@ sub _package_resultset_admin { return $c->model('DB')->resultset('profile_packages')->search_rs( undef, { group_by => 'me.id', - })->search_rs({ - 'me.status' => { '!=' => 'terminated' }, - }, - { '+select' => { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' }, - #{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_package_count_stmt() ] , -as => 'package_cnt' }, + })->search_rs( + undef, + { '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' }, + ], }); } @@ -55,11 +56,11 @@ sub _package_resultset_reseller { ->search_related('profile_packages')->search_rs( undef, { group_by => 'me.id', - })->search_rs({ - 'me.status' => { '!=' => 'terminated' }, - }, - { '+select' => { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' }, - #{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_package_count_stmt() ] , -as => 'package_cnt' }, + })->search_rs( + undef, + { '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' }, + ], }); } @@ -210,7 +211,7 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) { $c->model('DB')->schema->txn_do( sub { unless($c->stash->{'package_result'}->get_column('contract_cnt') == 0) { die('Cannnot modify profile package that is still assigned to contracts'); - } + } my $profile_package = $c->stash->{'package_result'}->update($form->values); $profile_package->profiles->delete; foreach my $mapping (@mappings_to_create) { @@ -240,30 +241,31 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) { ); } -sub terminate :Chained('base') :PathPart('terminate') :Args(0) { +sub delete :Chained('base') :PathPart('delete') :Args(0) { my ($self, $c) = @_; my $package = $c->stash->{package_result}; try { #todo: putting the package fetch into a transaction wouldn't help since the count columns a prone to phantom reads... unless($package->get_column('contract_cnt') == 0) { - die('Cannnot terminate profile package that is still assigned to contracts'); + die('Cannnot delete profile package that is still assigned to contracts'); + } + unless($package->get_column('voucher_cnt') == 0) { + die('Cannnot delete profile package that is assigned to vouchers'); } - $package->update({ - status => 'terminated', - #terminate_timestamp => NGCP::Panel::Utils::DateTime::current_local, - }); + + $package->delete; NGCP::Panel::Utils::Message->info( c => $c, data => $c->stash->{package}, - desc => $c->loc('Profile package successfully terminated'), + desc => $c->loc('Profile package successfully deleted'), ); } catch ($e) { NGCP::Panel::Utils::Message->error( c => $c, error => $e, data => $c->stash->{package}, - desc => $c->loc('Failed to terminate profile package'), + desc => $c->loc('Failed to delete profile package'), ); }; NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/package')); @@ -287,6 +289,85 @@ sub ajax_filter_reseller :Chained('package_list') :PathPart('ajax/filter_reselle $c->detach( $c->view("JSON") ); } + +sub details_base :Chained('/') :PathPart('package') :CaptureArgs(1) { + my ($self, $c, $package_id) = @_; + + my $dispatch_to = '_package_resultset_' . $c->user->roles; + my $package_rs = $self->$dispatch_to($c); + + unless($package_id && $package_id->is_integer) { + $package_id //= ''; + NGCP::Panel::Utils::Message->error( + c => $c, + data => { id => $package_id }, + desc => $c->loc('Invalid package id detected'), + ); + $c->response->redirect($c->uri_for()); + $c->detach; + return; + } + + my $res = $package_rs->find($package_id); + unless(defined($res)) { + NGCP::Panel::Utils::Message->error( + c => $c, + desc => $c->loc('Profile package does not exist'), + ); + $c->response->redirect($c->uri_for()); + $c->detach; + return; + } + + $c->stash->{profile_set_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ + #{ name => 'id', search => 1, title => $c->loc('#') }, + { name => 'billing_profile.name', search => 1, title => $c->loc('Billing Profile') }, + { name => 'billing_network.name', search => 1, title => $c->loc('Billing Network') }, + ]); + $c->stash->{customer_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ + NGCP::Panel::Utils::ProfilePackages::get_customer_datatable_cols($c) + ]); + $c->stash->{voucher_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ + NGCP::Panel::Utils::Voucher::get_datatable_cols($c,1) + ]); + + $c->stash(package_result => $res); +} + +sub details :Chained('details_base') :PathPart('details') :Args(0) { + my ($self, $c) = @_; + $c->stash(template => 'package/details.tt'); +} + +sub details_ajax :Chained('details_base') :PathPart('ajax') :CaptureArgs(0) { + my ($self, $c) = @_; + +} + +sub ajax_initial_profiles :Chained('details_ajax') :PathPart('initial_profiles') :Args(0) { + my ($self, $c) = @_; + + my $resultset = $c->stash->{package_result}->initial_profiles; + NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{profile_set_dt_columns}); + $c->detach( $c->view("JSON") ); +} + +sub ajax_topup_profiles :Chained('details_ajax') :PathPart('topup_profiles') :Args(0) { + my ($self, $c) = @_; + + my $resultset = $c->stash->{package_result}->topup_profiles; + NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{profile_set_dt_columns}); + $c->detach( $c->view("JSON") ); +} + +sub ajax_underrun_profiles :Chained('details_ajax') :PathPart('underrun_profiles') :Args(0) { + my ($self, $c) = @_; + + my $resultset = $c->stash->{package_result}->underrun_profiles; + NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{profile_set_dt_columns}); + $c->detach( $c->view("JSON") ); +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/lib/NGCP/Panel/Controller/Voucher.pm b/lib/NGCP/Panel/Controller/Voucher.pm index 4268482873..0d3653bb18 100644 --- a/lib/NGCP/Panel/Controller/Voucher.pm +++ b/lib/NGCP/Panel/Controller/Voucher.pm @@ -29,7 +29,14 @@ sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRol sub voucher_list :Chained('/') :PathPart('voucher') :CaptureArgs(0) { my ( $self, $c ) = @_; - my $voucher_rs = $c->model('DB')->resultset('vouchers'); + my $voucher_rs = $c->model('DB')->resultset('vouchers'); #->search_rs(undef, { + #'join' => { 'customer' => 'contact'}, + #'+select' => [ + # 'contact.email', + #], + #'+as' => [ + # 'customer_contact_email', + #],}); if($c->user->roles eq "reseller") { $voucher_rs = $voucher_rs->search({ reseller_id => $c->user->reseller_id, @@ -38,14 +45,7 @@ sub voucher_list :Chained('/') :PathPart('voucher') :CaptureArgs(0) { $c->stash(voucher_rs => $voucher_rs); $c->stash->{voucher_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ - { name => "id", "search" => 1, "title" => $c->loc("#") }, - $c->user->billing_data ? { name => "code", "search" => 1, "title" => $c->loc("Code") } : (), - { name => "amount", "search" => 1, "title" => $c->loc("Amount") }, - { name => "reseller.name", "search" => 1, "title" => $c->loc("Reseller") }, - { name => "profile_package.name", "search" => 1, "title" => $c->loc("Profile Package") }, - { name => "valid_until", "search" => 1, "title" => $c->loc("Valid Until") }, - { name => "used_at", "search" => 1, "title" => $c->loc("Used At") }, - { name => "used_by_subscriber.id", "search" => 1, "title" => $c->loc("Used By Subscriber #") }, + NGCP::Panel::Utils::Voucher::get_datatable_cols($c) ]); $c->stash(template => 'voucher/list.tt'); @@ -55,24 +55,41 @@ sub root :Chained('voucher_list') :PathPart('') :Args(0) { my ($self, $c) = @_; } +sub _process_dt_voucher_rows :Private { + my ($c,$row) = @_; + my $v = { $row->get_inflated_columns }; + if($c->user->billing_data) { + $v->{code} = NGCP::Panel::Utils::Voucher::decrypt_code($c, $row->code); + } else { + $v->{code} = ""; + } + foreach my $k(keys %{ $v }) { + if(blessed($v->{$k}) && $v->{$k}->isa('DateTime')) { + $v->{$k} = NGCP::Panel::Utils::DateTime::to_string($v->{$k}); + } + } + return %{ $v }; +} + sub ajax :Chained('voucher_list') :PathPart('ajax') :Args(0) { my ($self, $c) = @_; my $resultset = $c->stash->{voucher_rs}; NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{voucher_dt_columns}, sub { my $row = shift; - my $v = { $row->get_inflated_columns }; - if($c->user->billing_data) { - $v->{code} = NGCP::Panel::Utils::Voucher::decrypt_code($c, $row->code); - } else { - $v->{code} = ""; - } - foreach my $k(keys %{ $v }) { - if(blessed($v->{$k}) && $v->{$k}->isa('DateTime')) { - $v->{$k} = NGCP::Panel::Utils::DateTime::to_string($v->{$k}); - } - } - return %{ $v }; + return _process_dt_voucher_rows($c,$row); + }); + + $c->detach( $c->view("JSON") ); +} + +sub ajax_package_filter :Chained('voucher_list') :PathPart('ajax/package') :Args(1) { + my ($self, $c, $package_id) = @_; + + my $resultset = $c->stash->{voucher_rs}->search_rs({ package_id => $package_id },undef); + NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{voucher_dt_columns}, sub { + my $row = shift; + return _process_dt_voucher_rows($c,$row); }); $c->detach( $c->view("JSON") ); diff --git a/lib/NGCP/Panel/Form/ProfilePackage/PackageAPI.pm b/lib/NGCP/Panel/Form/ProfilePackage/PackageAPI.pm index 71c4162fd3..c022a0269f 100644 --- a/lib/NGCP/Panel/Form/ProfilePackage/PackageAPI.pm +++ b/lib/NGCP/Panel/Form/ProfilePackage/PackageAPI.pm @@ -37,19 +37,6 @@ has_field 'description' => ( }, ); -has_field 'status' => ( - type => 'Hidden', - options => [ - { value => 'active', label => 'active' }, - { value => 'terminated', label => 'terminated' }, - ], - element_attr => { - rel => ['tooltip'], - title => ['The status of this package. Only active profile packages can be assigned to customers/profile packages.'] - }, -); - - has_field 'initial_balance' => ( type => 'Money', element_attr => { diff --git a/lib/NGCP/Panel/Role/API/ProfilePackages.pm b/lib/NGCP/Panel/Role/API/ProfilePackages.pm index 2ecabdea7e..fa5299c2c6 100644 --- a/lib/NGCP/Panel/Role/API/ProfilePackages.pm +++ b/lib/NGCP/Panel/Role/API/ProfilePackages.pm @@ -91,10 +91,10 @@ sub hal_from_item { sub item_rs { my ($self, $c) = @_; - my $item_rs = $c->model('DB')->resultset('profile_packages')->search_rs({ 'me.status' => { '!=' => 'terminated' } }); + my $item_rs = $c->model('DB')->resultset('profile_packages')->search_rs(); #{ 'me.status' => { '!=' => 'terminated' } }); my $search_xtra = { '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' }, - #{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_package_count_stmt() ] , -as => 'package_cnt' }, + { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' }, ], }; if($c->user->roles eq "admin") { @@ -117,11 +117,6 @@ sub item_by_id { sub update_item { my ($self, $c, $item, $old_resource, $resource, $form) = @_; - if ($item->status eq 'terminated') { - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, 'Profile package is already terminated and cannot be changed.'); - return; - } - delete $resource->{id}; my $schema = $c->model('DB'); @@ -145,18 +140,11 @@ sub update_item { $self->error($c, HTTP_UNPROCESSABLE_ENTITY, $err); }); - #if(exists $resource->{status} && $resource->{status} eq 'terminated') { - unless($item->get_column('contract_cnt') == 0) { - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, - "Cannnot modify or terminate profile_package that is still assigned to contracts"); - return; - } - #unless($item->get_column('contract_cnt') == 0) { - # $self->error($c, HTTP_UNPROCESSABLE_ENTITY, - # "Cannnot terminate billing_network that is still used in profile sets of profile packages"); - # return; - #} - #} + unless($item->get_column('contract_cnt') == 0) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, + "Cannnot modify or terminate profile_package that is still assigned to contracts"); + return; + } my $mappings_to_create = []; return unless NGCP::Panel::Utils::ProfilePackages::prepare_profile_package( diff --git a/lib/NGCP/Panel/Utils/Billing.pm b/lib/NGCP/Panel/Utils/Billing.pm index 44be488bf4..70d9088bd6 100644 --- a/lib/NGCP/Panel/Utils/Billing.pm +++ b/lib/NGCP/Panel/Utils/Billing.pm @@ -96,7 +96,7 @@ sub get_contract_count_stmt { return "select count(distinct c.id) from `billing`.`billing_mappings` bm join `billing`.`contracts` c on c.id = bm.contract_id where bm.`billing_profile_id` = `me`.`id` and c.status != 'terminated' and (bm.end_date is null or bm.end_date >= now())"; } sub get_package_count_stmt { - return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`profile_id` = `me`.`id` and pp.status != 'terminated'"; + return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`profile_id` = `me`.`id`"; # and pp.status != 'terminated'"; } sub get_datatable_cols { diff --git a/lib/NGCP/Panel/Utils/BillingNetworks.pm b/lib/NGCP/Panel/Utils/BillingNetworks.pm index fd2e5730fd..ef157bc1ea 100644 --- a/lib/NGCP/Panel/Utils/BillingNetworks.pm +++ b/lib/NGCP/Panel/Utils/BillingNetworks.pm @@ -131,7 +131,7 @@ sub get_contract_count_stmt { return "select count(distinct c.id) from `billing`.`billing_mappings` bm join `billing`.`contracts` c on c.id = bm.contract_id where bm.`network_id` = `me`.`id` and c.status != 'terminated' and (bm.end_date is null or bm.end_date >= now())"; } sub get_package_count_stmt { - return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`network_id` = `me`.`id` and pp.status != 'terminated'"; + return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`network_id` = `me`.`id`"; # and pp.status != 'terminated'"; } sub get_datatable_cols { diff --git a/lib/NGCP/Panel/Utils/Contract.pm b/lib/NGCP/Panel/Utils/Contract.pm index cc10021ef4..5ea625132d 100644 --- a/lib/NGCP/Panel/Utils/Contract.pm +++ b/lib/NGCP/Panel/Utils/Contract.pm @@ -765,9 +765,7 @@ sub _check_profile_package { unless($package) { return 0 unless &{$err_code}("Invalid 'profile_package_id'.",$field); } - if ($package->status eq 'terminated') { - return 0 unless &{$err_code}("Invalid 'profile_package_id', already terminated.",$field); - } + if (defined $reseller_id && defined $package->reseller_id && $reseller_id != $package->reseller_id) { return 0 unless &{$err_code}("The reseller of the contact doesn't match the reseller of the profile package (" . $package->name . ").",$field); } diff --git a/lib/NGCP/Panel/Utils/DateTime.pm b/lib/NGCP/Panel/Utils/DateTime.pm index 4f1669a799..942ba985f3 100644 --- a/lib/NGCP/Panel/Utils/DateTime.pm +++ b/lib/NGCP/Panel/Utils/DateTime.pm @@ -3,18 +3,27 @@ package NGCP::Panel::Utils::DateTime; #use Sipwise::Base; seg fault when creating threads in test scripts use strict; use warnings; -use Time::Fake; #load this before any use DateTime +use Time::HiRes; #prevent warning from Time::Warp +use Time::Warp; +#use Time::Fake; #load this before any use DateTime use DateTime; -#use DateTime::Infinite; use DateTime::Format::ISO8601; use DateTime::Format::Strptime; use constant RFC_1123_FORMAT_PATTERN => '%a, %d %b %Y %T %Z'; +my $is_fake_time = 0; + sub current_local { - return DateTime->now( - time_zone => DateTime::TimeZone->new(name => 'local') - ); + if ($is_fake_time) { + return DateTime->from_epoch(epoch => Time::Warp::time, + time_zone => DateTime::TimeZone->new(name => 'local') + ); + } else { + return DateTime->now( + time_zone => DateTime::TimeZone->new(name => 'local') + ); + } } sub infinite_past { @@ -53,20 +62,36 @@ sub is_infinite { } sub set_fake_time { - my ($o) = @_; + my ($o) = @_; + $is_fake_time = 1; if (defined $o) { - Time::Fake->offset(ref $o eq 'DateTime' ? $o->epoch : $o); + if (ref $o eq 'DateTime') { + $o = $o->epoch; + } else { + my %mult = ( + s => 1, + m => 60, + h => 60*60, + d => 60*60*24, + M => 60*60*24*30, + y => 60*60*24*365, + ); + + if (!$o) { + $o = time; + } elsif ($o =~ m/^([+-]\d+)([smhdMy]?)$/) { + $o = time + $1 * $mult{ $2 || "s" }; + } elsif ($o !~ m/\D/) { + + } else { + die("Invalid time offset: '$o'"); + } + } + Time::Warp::to($o); } else { - Time::Fake->reset(); + Time::Warp::reset(); } } -#sub infinite_past { -# DateTime::Infinite::Past->new(); -#} - -#sub infinite_future { -# return DateTime::Infinite::Future->new(); -#} sub last_day_of_month { my $dt = shift; diff --git a/lib/NGCP/Panel/Utils/Message.pm b/lib/NGCP/Panel/Utils/Message.pm index 6daa3648a6..028be6841d 100644 --- a/lib/NGCP/Panel/Utils/Message.pm +++ b/lib/NGCP/Panel/Utils/Message.pm @@ -3,7 +3,6 @@ package NGCP::Panel::Utils::Message; use Catalyst; use Sipwise::Base; use Data::Dumper; -use Time::Fake; #load this before any use DateTime use DateTime qw(); use DateTime::Format::RFC3339 qw(); use Time::HiRes qw(); diff --git a/lib/NGCP/Panel/Utils/ProfilePackages.pm b/lib/NGCP/Panel/Utils/ProfilePackages.pm index b8b8d49f5b..0c098b436d 100644 --- a/lib/NGCP/Panel/Utils/ProfilePackages.pm +++ b/lib/NGCP/Panel/Utils/ProfilePackages.pm @@ -782,6 +782,10 @@ sub get_contract_count_stmt { return "select count(distinct c.id) from `billing`.`contracts` c where c.`profile_package_id` = `me`.`id` and c.status != 'terminated'"; } +sub get_voucher_count_stmt { + return "select count(distinct v.id) from `billing`.`vouchers` v where v.`package_id` = `me`.`id`"; # and v.`used_by_subscriber_id` is null"; +} + sub _get_profile_set_group_stmt { my ($discriminator) = @_; my $grp_stmt = "group_concat(if(bn.`name` is null,bp.`name`,concat(bp.`name`,'/',bn.`name`)) separator ', ')"; @@ -793,7 +797,8 @@ sub get_datatable_cols { my ($c) = @_; return ( - { name => "contract_cnt", "search" => 0, "title" => $c->loc("Used (contracts)"), }, + { name => "contract_cnt", "search" => 0, "title" => $c->loc("Contracts"), }, + { name => "voucher_cnt", "search" => 0, "title" => $c->loc("Vouchers"), }, { name => 'initial_profiles_grp', accessor => "initial_profiles_grp", search => 0, title => $c->loc('Initial Profiles'), literal_sql => _get_profile_set_group_stmt(INITIAL_PROFILE_DISCRIMINATOR) }, { name => 'underrun_profiles_grp', accessor => "underrun_profiles_grp", search => 0, title => $c->loc('Underrun Profiles'), @@ -808,5 +813,17 @@ sub get_datatable_cols { ); } + +sub get_customer_datatable_cols { + + my ($c) = @_; + return ( + { name => "id", search => 1, title => $c->loc("#") }, + { name => "external_id", search => 1, title => $c->loc("External #") }, + #{ name => "billing_mappings_actual.billing_mappings.product.name", search => 1, title => $c->loc("Product") }, + { name => "contact.email", search => 1, title => $c->loc("Contact Email") }, + { name => "status", search => 1, title => $c->loc("Status") }, + ); +} 1; \ No newline at end of file diff --git a/lib/NGCP/Panel/Utils/Voucher.pm b/lib/NGCP/Panel/Utils/Voucher.pm index ff63fd9b62..06ac6f6a87 100644 --- a/lib/NGCP/Panel/Utils/Voucher.pm +++ b/lib/NGCP/Panel/Utils/Voucher.pm @@ -41,4 +41,21 @@ sub decrypt_code { return $plain; } +sub get_datatable_cols { + + my ($c,$hide_package) = @_; + return ( + { name => "id", "search" => 1, "title" => $c->loc("#") }, + $c->user->billing_data ? { name => "code", "search" => 1, "title" => $c->loc("Code") } : (), + { name => "amount", "search" => 1, "title" => $c->loc("Amount") }, + { name => "reseller.name", "search" => 1, "title" => $c->loc("Reseller") }, + $hide_package ? () : { name => "profile_package.name", "search" => 1, "title" => $c->loc("Profile Package") }, + #{ name => "customer_contact_email", "search" => 1, "title" => $c->loc("Reserved for Customer") }, + { name => "customer_id", "search" => 1, "title" => $c->loc("For Contract #") }, + { name => "valid_until", "search" => 1, "title" => $c->loc("Valid Until") }, + { name => "used_at", "search" => 1, "title" => $c->loc("Used At") }, + { name => "used_by_subscriber.id", "search" => 1, "title" => $c->loc("Used By Subscriber #") }, + ); +} + 1; diff --git a/share/templates/package/details.tt b/share/templates/package/details.tt new file mode 100644 index 0000000000..cd1f43a6f3 --- /dev/null +++ b/share/templates/package/details.tt @@ -0,0 +1,156 @@ +[% site_config.title = c.loc('Profile Package Details for [_1]', package_result.name) -%] + + + +[% back_created = 1 -%] + +
+ +
+ [% FOREACH m IN messages -%] +
[% m.text %]
+ [% END -%] +[% messages = [] -%] +
+ +
+ +
+ +
+
+[% + helper.name = c.loc('Billing Profile/Network'); + helper.identifier = 'initial_profile_set'; + helper.messages = messages; + helper.dt_columns = profile_set_dt_columns; + helper.paginate = 'true'; + helper.filter = 'true'; + helper.close_target = close_target; + helper.create_flag = create_flag; + helper.edit_flag = edit_flag; + helper.form_object = form; + helper.ajax_uri = c.uri_for('/package/' _ package_result.id _ '/ajax/initial_profiles'); + + PROCESS 'helpers/datatables.tt'; +-%] +
+
+
+ +
+ +
+
+[% + helper.name = c.loc('Billing Profile/Network'); + helper.identifier = 'topup_profile_set'; + helper.messages = messages; + helper.dt_columns = profile_set_dt_columns; + helper.paginate = 'true'; + helper.filter = 'true'; + helper.close_target = close_target; + helper.create_flag = create_flag; + helper.edit_flag = edit_flag; + helper.form_object = form; + helper.ajax_uri = c.uri_for('/package/' _ package_result.id _ '/ajax/topup_profiles'); + + PROCESS 'helpers/datatables.tt'; +-%] +
+
+
+ +
+ +
+
+[% + helper.name = c.loc('Billing Profile/Network'); + helper.identifier = 'underrun_profile_set'; + helper.messages = messages; + helper.dt_columns = profile_set_dt_columns; + helper.paginate = 'true'; + helper.filter = 'true'; + helper.close_target = close_target; + helper.create_flag = create_flag; + helper.edit_flag = edit_flag; + helper.form_object = form; + helper.ajax_uri = c.uri_for('/package/' _ package_result.id _ '/ajax/underrun_profiles'); + + PROCESS 'helpers/datatables.tt'; +-%] +
+
+
+ +
+ +
+
+[% + helper.name = c.loc('Contract'); + helper.identifier = 'customers'; + helper.messages = messages; + helper.dt_columns = customer_dt_columns; + helper.paginate = 'true'; + helper.filter = 'true'; + helper.close_target = close_target; + helper.create_flag = create_flag; + helper.edit_flag = edit_flag; + helper.form_object = form; + helper.ajax_uri = c.uri_for_action('/customer/ajax_package_filter', package_result.id ); + + PROCESS 'helpers/datatables.tt'; +-%] +
+
+
+ +
+ +
+
+[% + helper.name = c.loc('Voucher'); + helper.identifier = 'vouchers'; + helper.messages = messages; + helper.dt_columns = voucher_dt_columns; + helper.paginate = 'true'; + helper.filter = 'true'; + helper.close_target = close_target; + helper.create_flag = create_flag; + helper.edit_flag = edit_flag; + helper.form_object = form; + helper.ajax_uri = c.uri_for_action('/voucher/ajax_package_filter', package_result.id ); + + PROCESS 'helpers/datatables.tt'; +-%] +
+
+
+ +
+ +[% IF edit_flag || create_flag -%] + IF form.has_for_js; + form.render_repeatable_js; + END; + [%PROCESS 'helpers/modal.tt' -%] +[% END -%] + +[% # vim: set tabstop=4 syntax=html expandtab: -%] diff --git a/share/templates/package/list.tt b/share/templates/package/list.tt index dcf9ba9d9f..4d7fd80783 100644 --- a/share/templates/package/list.tt +++ b/share/templates/package/list.tt @@ -16,11 +16,16 @@ UNLESS c.user.read_only; helper.dt_buttons = [ { name = c.loc('Edit'), uri = "/package/'+full.id+'/edit", class = 'btn-small btn-primary', icon = 'icon-edit' }, - { name = c.loc('Terminate'), uri = "/package/'+full[\"id\"]+'/terminate", class = 'btn-small btn-secondary', icon = 'icon-remove', condition => 'full.contract_cnt == "0"' }, + { name = c.loc('Delete'), uri = "/package/'+full[\"id\"]+'/delete", class = 'btn-small btn-secondary', icon = 'icon-remove', condition => 'full.contract_cnt == "0" && full.voucher_cnt == "0"' }, + { name = c.loc('Details'), uri = "/package/'+full.id+'/details", class = 'btn-small btn-tertiary', icon = 'icon-list' }, ]; helper.top_buttons = [ { name = c.loc('Create Profile Package'), uri = c.uri_for('/package/create'), icon = 'icon-star' }, ]; + ELSE; + helper.dt_buttons = [ + { name = c.loc('Details'), uri = "/package/'+full.id+'/details", class = 'btn-small btn-tertiary', icon = 'icon-list' }, + ]; END; PROCESS 'helpers/datatables.tt'; diff --git a/share/templates/reseller/details.tt b/share/templates/reseller/details.tt index cfb233ee14..36197fc439 100644 --- a/share/templates/reseller/details.tt +++ b/share/templates/reseller/details.tt @@ -270,7 +270,7 @@ UNLESS c.user.read_only; helper.dt_buttons = [ { name = c.loc('Edit'), uri = "/package/'+full.id+'/edit", class = 'btn-small btn-primary', icon = 'icon-edit' }, - { name = c.loc('Terminate'), uri = "/package/'+full[\"id\"]+'/terminate", class = 'btn-small btn-secondary', icon = 'icon-remove', condition => 'full.contract_cnt == "0"' }, + { name = c.loc('Delete'), uri = "/package/'+full[\"id\"]+'/delete", class = 'btn-small btn-secondary', icon = 'icon-remove', condition => 'full.contract_cnt == "0" && full.voucher_cnt == "0"' }, ]; END; diff --git a/t/api-balanceintervals.t b/t/api-balanceintervals.t index 5128ceb2cc..8d60881508 100644 --- a/t/api-balanceintervals.t +++ b/t/api-balanceintervals.t @@ -9,8 +9,8 @@ use Net::Domain qw(hostfqdn); use LWP::UserAgent; use JSON qw(); use Test::More; -#use Storable qw(); -use Time::Fake; +use Time::HiRes; #prevent warning from Time::Warp +use Time::Warp qw(); use DateTime::Format::Strptime; use DateTime::Format::ISO8601; use Data::Dumper; @@ -118,9 +118,6 @@ my $infinite_future; ok($past->epoch < $fake_now->epoch,$offset_label . 'past is smaller than now (epoch)'); ok(!($past->epoch > $fake_now->epoch),$offset_label . 'past is not greater than now (epoch)'); } - #use DateTime::Infinite; - #$past = DateTime::Infinite::Past->new(); - #$future = DateTime::Infinite::Future->new(); _set_time(); } @@ -327,7 +324,7 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { $req_identifier = $cnt . '. switch customer ' . $customer->{id} . ' to package ' . $prof_package_create30d->{name}; diag($req_identifier); $cnt++; $customer = _switch_package($customer,$prof_package_create30d); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '2014-01-01 00:00:00', stop => '2014-01-31 23:59:59'}, { start => '2014-02-01 00:00:00', stop => '2014-02-28 23:59:59'}, @@ -459,26 +456,26 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { { start => '~2014-10-04 13:00:00', stop => $infinite_future}, ],NGCP::Panel::Utils::DateTime::from_string($t1)); - $req_identifier = $cnt . '. create topup_start_mode_test1 voucher'; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. create topup_start_mode_test1 voucher'; diag($req_identifier); $cnt++; my $voucher1 = _create_voucher(10,'topup_start_mode_test1'.$t,$customer); - $req_identifier = $cnt . '. create topup_start_mode_test2 voucher'; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. create topup_start_mode_test2 voucher'; diag($req_identifier); $cnt++; my $voucher2 = _create_voucher(10,'topup_start_mode_test2'.$t,$customer,$prof_package_create1m); - $req_identifier = $cnt . '. create subscriber for customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. create subscriber for customer ' . $customer->{id}; diag($req_identifier); $cnt++; my $subscriber = _create_subscriber($customer); $t1 = $ts; $ts = '2014-10-23 13:00:00'; _set_time(NGCP::Panel::Utils::DateTime::from_string($ts)); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '~2014-10-04 13:00:00', stop => $infinite_future}, ],NGCP::Panel::Utils::DateTime::from_string($t1)); - $req_identifier = $cnt . '. perform topup with voucher ' . $voucher1->{code}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. perform topup with voucher ' . $voucher1->{code}; diag($req_identifier); $cnt++; _perform_topup_voucher($subscriber,$voucher1); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '~2014-10-04 13:00:00', stop => '~2014-10-23 13:00:00'}, { start => '~2014-10-23 13:00:00', stop => $infinite_future}, @@ -488,23 +485,23 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { $ts = '2014-11-29 13:00:00'; _set_time(NGCP::Panel::Utils::DateTime::from_string($ts)); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '~2014-10-23 13:00:00', stop => $infinite_future}, ],NGCP::Panel::Utils::DateTime::from_string($t1)); - $req_identifier = $cnt . '. perform topup with voucher ' . $voucher2->{code}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. perform topup with voucher ' . $voucher2->{code}; diag($req_identifier); $cnt++; _perform_topup_voucher($subscriber,$voucher2); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '~2014-10-23 13:00:00', stop => '2014-12-06 23:59:59'}, ],NGCP::Panel::Utils::DateTime::from_string($t1)); - $req_identifier = $cnt . '. switch customer ' . $customer->{id} . ' to no package '; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. switch customer ' . $customer->{id} . ' to no package '; diag($req_identifier); $cnt++; $customer = _switch_package($customer); - $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); diag($req_identifier); $cnt++; + $req_identifier = $cnt . '. get balance history of customer ' . $customer->{id}; diag($req_identifier); $cnt++; _check_interval_history($customer,[ { start => '~2014-10-23 13:00:00', stop => '2014-11-30 23:59:59', cash => 20}, ],NGCP::Panel::Utils::DateTime::from_string($t1)); @@ -546,8 +543,6 @@ if (_get_allow_fake_client_time() && $enable_profile_packages) { _set_time(); my $t1 = time; - #_fetch_intervals_worker(0,'asc'); - #_fetch_intervals_worker(0,'desc'); my $delay = 2; my $t_a = threads->create(\&_fetch_intervals_worker,$delay,'id','asc'); @@ -870,12 +865,11 @@ sub _set_time { pattern => '%F %T', ); if (defined $o) { - $o = $o->epoch if ref $o eq 'DateTime'; - Time::Fake->offset($o); + NGCP::Panel::Utils::DateTime::set_fake_time($o); my $now = NGCP::Panel::Utils::DateTime::current_local; diag("applying fake time offset '$o' - current time: " . $dtf->format_datetime($now)); } else { - Time::Fake->reset(); + NGCP::Panel::Utils::DateTime::set_fake_time(); my $now = NGCP::Panel::Utils::DateTime::current_local; diag("resetting fake time - current time: " . $dtf->format_datetime($now)); } @@ -975,9 +969,6 @@ sub _create_base_profile_package { $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/'); $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); - #$req->header('X-Request-Identifier' => $req_identifier) if $req_identifier; - #my $name = $start_mode . ($interval_unit ? '/' . $interval_value . ' ' . $interval_unit : ''); my $req_data = { name => "base profile package " . $t, description => "base prof package descr " . $t, @@ -998,8 +989,6 @@ sub _create_base_profile_package { my $profilepackage_uri = $uri.'/'.$res->header('Location'); my $request = $req; $req = HTTP::Request->new('GET', $profilepackage_uri); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); - #$req->header('X-Request-Identifier' => $req_identifier) if $req_identifier; $res = $ua->request($req); is($res->code, 200, "fetch POSTed base profilepackage"); my $package = JSON::from_json($res->decoded_content); @@ -1015,9 +1004,6 @@ sub _create_silver_profile_package { $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/'); $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); - #$req->header('X-Request-Identifier' => $req_identifier) if $req_identifier; - #my $name = $start_mode . ($interval_unit ? '/' . $interval_value . ' ' . $interval_unit : ''); my $req_data = { name => "silver profile package " . $t, description => "silver prof package descr " . $t, @@ -1041,7 +1027,6 @@ sub _create_silver_profile_package { my $profilepackage_uri = $uri.'/'.$res->header('Location'); my $request = $req; $req = HTTP::Request->new('GET', $profilepackage_uri); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); $res = $ua->request($req); is($res->code, 200, "fetch POSTed silver profilepackage"); my $package = JSON::from_json($res->decoded_content); @@ -1057,8 +1042,6 @@ sub _create_extension_profile_package { $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/'); $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); - #my $name = $start_mode . ($interval_unit ? '/' . $interval_value . ' ' . $interval_unit : ''); my $req_data = { name => "extension profile package " . $t, description => "extension prof package descr " . $t, @@ -1082,7 +1065,6 @@ sub _create_extension_profile_package { my $profilepackage_uri = $uri.'/'.$res->header('Location'); my $request = $req; $req = HTTP::Request->new('GET', $profilepackage_uri); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); $res = $ua->request($req); is($res->code, 200, "fetch POSTed extension profilepackage"); my $package = JSON::from_json($res->decoded_content); @@ -1098,8 +1080,6 @@ sub _create_gold_profile_package { $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/'); $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); - #my $name = $start_mode . ($interval_unit ? '/' . $interval_value . ' ' . $interval_unit : ''); my $req_data = { name => "gold profile package " . $t, description => "gold prof package descr " . $t, @@ -1123,7 +1103,6 @@ sub _create_gold_profile_package { my $profilepackage_uri = $uri.'/'.$res->header('Location'); my $request = $req; $req = HTTP::Request->new('GET', $profilepackage_uri); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); $res = $ua->request($req); is($res->code, 200, "fetch POSTed gold profilepackage"); my $package = JSON::from_json($res->decoded_content); @@ -1141,7 +1120,6 @@ sub _create_voucher { ); $req = HTTP::Request->new('POST', $uri.'/api/vouchers/'); $req->header('Content-Type' => 'application/json'); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); my $req_data = { amount => $amount * 100.0, code => $code, @@ -1156,7 +1134,6 @@ sub _create_voucher { is($res->code, 201, "create " . $label); my $request = $req; $req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location')); - #$req->header('X-Fake-Clienttime' => _get_rfc_1123_now()); $res = $ua->request($req); is($res->code, 200, "fetch " . $label); my $voucher = JSON::from_json($res->decoded_content); diff --git a/t/api-customers.t b/t/api-customers.t index f461130ee9..2da2c82a2f 100644 --- a/t/api-customers.t +++ b/t/api-customers.t @@ -955,23 +955,13 @@ if ($enable_profile_packages) { $res = $ua->request($req); is($res->code, 200, "prof-package: terminate customer"); - $req = HTTP::Request->new('PATCH', $profile_package_uri); - $req->header('Prefer' => 'return=representation'); - $req->header('Content-Type' => 'application/json-patch+json'); - $req->content(JSON::to_json( - [ { op => 'replace', path => '/status', value => 'terminated' } ] - )); + $req = HTTP::Request->new('DELETE', $profile_package_uri); $res = $ua->request($req); - is($res->code, 200, "prof-package: terminate profile package"); + is($res->code, 204, "prof-package: delete profile package"); - $req = HTTP::Request->new('PATCH', $second_profile_package_uri); - $req->header('Prefer' => 'return=representation'); - $req->header('Content-Type' => 'application/json-patch+json'); - $req->content(JSON::to_json( - [ { op => 'replace', path => '/status', value => 'terminated' } ] - )); + $req = HTTP::Request->new('DELETE', $second_profile_package_uri); $res = $ua->request($req); - is($res->code, 200, "prof-package: terminate second profile package"); + is($res->code, 204, "prof-package: delete second profile package"); $req = HTTP::Request->new('PATCH', $uri.'/api/billingprofiles/'.$third_billing_profile_id); $req->header('Prefer' => 'return=representation'); diff --git a/t/api-journals.t b/t/api-journals.t index 228c9daf23..c44c7feef1 100644 --- a/t/api-journals.t +++ b/t/api-journals.t @@ -175,8 +175,8 @@ sub test_profilepackage { $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); $req->content(JSON::to_json({ - name => "test profile package " . $t, - description => "test profile package description " . $t, + name => "test profile package " . ($t-1), + description => "test profile package description " . ($t-1), reseller_id => $reseller->{id}, initial_profiles => [{ profile_id => $profile->{id}, }, ] })); @@ -198,8 +198,8 @@ sub test_profilepackage { $req->header('Content-Type' => 'application/json'); $req->header('Prefer' => 'return=representation'); $req->content(JSON::to_json({ - name => "test profile package ".$t." PUT", - description => "test profile package description ".$t." PUT", + name => "test profile package ".($t-1)." PUT", + description => "test profile package description ".($t-1)." PUT", #reseller_id => $reseller_id, initial_profiles => [{ profile_id => $profile->{id}, }, ], })); @@ -217,7 +217,7 @@ sub test_profilepackage { $req->header('Content-Type' => 'application/json-patch+json'); $req->header('Prefer' => 'return=representation'); $req->content(JSON::to_json( - [ { op => 'replace', path => '/name', value => "test profile package ".$t." PATCH" } ] + [ { op => 'replace', path => '/name', value => "test profile package ".($t-1)." PATCH" } ] )); $res = $ua->request($req); is($res->code, 200, "PATCH test profilepackage"); @@ -228,9 +228,32 @@ sub test_profilepackage { _test_item_journal_link('profilepackages',$profilepackage,$profilepackage->{id}); $journal = _test_journal_top_journalitem('profilepackages',$profilepackage->{id},$profilepackage,'update',$journals,$journal); + + $req = HTTP::Request->new('DELETE', $profilepackage_uri); + $res = $ua->request($req); + is($res->code, 204, "delete POSTed test profilepackage"); + + $journal = _test_journal_top_journalitem('profilepackages',$profilepackage->{id},$profilepackage,'delete',$journals,$journal); _test_journal_collection('profilepackages',$profilepackage->{id},$journals); + $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/'); + $req->header('Content-Type' => 'application/json'); + $req->header('Prefer' => 'return=representation'); + $req->content(JSON::to_json({ + name => "test profile package " . $t, + description => "test profile package description " . $t, + reseller_id => $reseller->{id}, + initial_profiles => [{ profile_id => $profile->{id}, }, ] + })); + $res = $ua->request($req); + is($res->code, 201, "POST another test profilepackage"); + $profilepackage_uri = $uri.'/'.$res->header('Location'); + $req = HTTP::Request->new('GET', $profilepackage_uri); + $res = $ua->request($req); + is($res->code, 200, "fetch POSTed profilepackage"); + $profilepackage = JSON::from_json($res->decoded_content); + return $profilepackage; } diff --git a/t/api-profilepackages.t b/t/api-profilepackages.t index 0108c45c30..874a62d6f5 100644 --- a/t/api-profilepackages.t +++ b/t/api-profilepackages.t @@ -144,17 +144,13 @@ if ($enable_profile_packages) { is($res->code, 200, "fetch PATCHed test profilepackage"); $profilepackage = JSON::from_json($res->decoded_content); - $req = HTTP::Request->new('PATCH', $profilepackage_uri); - $req->header('Content-Type' => 'application/json-patch+json'); - $req->header('Prefer' => 'return=representation'); - $req->content(JSON::to_json( - [ { op => 'replace', path => '/status', value => "terminated" } ] - )); + $req = HTTP::Request->new('DELETE', $profilepackage_uri); $res = $ua->request($req); - is($res->code, 200, "terminate test profilepackage"); + is($res->code, 204, "delete profilepackage"); + $req = HTTP::Request->new('GET', $profilepackage_uri); $res = $ua->request($req); - is($res->code, 404, "try to fetch terminated test profilepackage"); + is($res->code, 404, "try to fetch deleted test profilepackage"); } @@ -184,7 +180,7 @@ sub _post_profile_package { name => "test profile package ".$i . ' ' . $t, description => "test profile package description ".$i . $t, reseller_id => $default_reseller_id, - status => 'active', + #status => 'active', initial_profiles => [{ profile_id => $billingprofile->{id}, network_id => undef }, { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}], initial_balance => 0.0,