TT#32910 TT#33037 initial cash_balance, free_time_balance

+ create topup_log records for edit balance operations

Change-Id: I6cecbf6025111198b8c43a33b0ffd0f0e30955ad
(cherry picked from commit c81ca69b70)
changes/50/21150/2
Rene Krenn 7 years ago
parent a07c718042
commit 90dfbb534d

@ -21,7 +21,7 @@ sub allowed_methods{
}
sub api_description {
return 'Defines customer balances to access cash and free time balance.';
return 'Defines customer balances to access cash and free-time balance.';
};
sub query_params {

@ -62,7 +62,7 @@ sub auto :Private {
$self->set_body($c);
$self->log_request($c);
#$self->apply_fake_time($c);
#$self->apply_fake_time($c);
}
sub GET :Allow {
@ -119,7 +119,7 @@ sub PATCH :Allow {
last unless $preference;
my $json = $self->get_valid_patch_data(
c => $c,
c => $c,
id => $id,
media_type => 'application/json-patch+json',
);
@ -129,16 +129,18 @@ sub PATCH :Allow {
my $item = $self->item_by_id($c, $id, $now);
last unless $self->resource_exists($c, customerbalance => $item);
my $old_resource = { $item->get_inflated_columns };
$old_resource->{cash_balance} /= 100.0 if defined $old_resource->{cash_balance};
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form, $now);
last unless $item;
my $hal = $self->hal_from_item($c, $item, $form);
last unless $self->add_update_journal_item_hal($c,$hal);
last unless $self->add_update_journal_item_hal($c, $hal);
$guard->commit;
if ('minimal' eq $preference) {
@ -176,6 +178,7 @@ sub PUT :Allow {
);
last unless $resource;
my $old_resource = { $item->get_inflated_columns };
$old_resource->{cash_balance} /= 100.0 if defined $old_resource->{cash_balance};
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form, $now);
@ -183,8 +186,8 @@ sub PUT :Allow {
my $hal = $self->hal_from_item($c, $item, $form);
last unless $self->add_update_journal_item_hal($c,$hal);
$guard->commit;
$guard->commit;
if ('minimal' eq $preference) {
$c->response->status(HTTP_NO_CONTENT);

@ -1020,6 +1020,8 @@ sub edit_balance :Chained('base_restricted') :PathPart('balance/edit') :Args(0)
back_uri => $c->req->uri,
);
if($posted && $form->validated) {
my $entities = { contract => $contract, };
my $log_vals = {};
try {
my $schema = $c->model('DB');
$schema->set_transaction_isolation('READ COMMITTED');
@ -1027,11 +1029,21 @@ sub edit_balance :Chained('base_restricted') :PathPart('balance/edit') :Args(0)
$balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c,
contract => $contract,
now => $now);
$balance = NGCP::Panel::Utils::ProfilePackages::underrun_update_balance(c => $c,
balance =>$balance,
$balance = NGCP::Panel::Utils::ProfilePackages::set_contract_balance(
c => $c,
balance => $balance,
cash_balance => $form->values->{cash_balance},
free_time_balance => $form->values->{free_time_balance},
now => $now,
new_cash_balance => $form->values->{cash_balance} );
$balance->update($form->values);
log_vals => $log_vals);
my $topup_log = NGCP::Panel::Utils::ProfilePackages::create_topup_log_record(
c => $c,
now => $now,
entities => $entities,
log_vals => $log_vals,
request_token => NGCP::Panel::Utils::ProfilePackages::PANEL_TOPUP_REQUEST_TOKEN,
);
});
NGCP::Panel::Utils::Message::info(
c => $c,

@ -91,6 +91,18 @@ has_field 'cash_balance' => (
},
);
has_field 'initial_cash_balance' => (
type => 'Money',
#label => 'Cash Balance',
#required => 1,
#inflate_method => sub { return $_[1] * 100 },
#deflate_method => sub { return $_[1] / 100 },
element_attr => {
rel => ['tooltip'],
title => ['The interval\'s initial cash balance of the contract in EUR/USD/etc.']
},
);
has_field 'cash_debit' => (
type => 'Money',
#label => 'Cash Balance',
@ -113,6 +125,16 @@ has_field 'free_time_balance' => (
},
);
has_field 'initial_free_time_balance' => (
type => 'Integer',
#label => 'Free-Time Balance',
#required => 1,
element_attr => {
rel => ['tooltip'],
title => ['The interval\'s initial free-time balance of the contract in seconds.']
},
);
has_field 'free_time_spent' => (
type => 'Integer',
#label => 'Free-Time Balance',

@ -4,6 +4,16 @@ use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::Balance::CustomerBalance';
use Moose::Util::TypeConstraints;
has_field 'cash_balance' => (
type => 'Money',
label => 'Cash Balance',
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['The current cash balance of the customer in EUR/USD/etc.']
},
);
has_field 'cash_debit' => (
type => 'Money',
#label => 'Cash Balance (Interval)',

@ -25,6 +25,7 @@ has_field 'type' => (
options => [
{ value => 'cash', label => 'Cash top-up' },
{ value => 'voucher', label => 'Voucher top-up' },
{ value => 'set_balance', label => 'Balance edited' },
],
required => 1,
);

@ -15,29 +15,29 @@ use NGCP::Panel::Utils::ProfilePackages qw();
use NGCP::Panel::Utils::DateTime;
sub _contract_rs {
my ($self, $c, $include_terminated,$now) = @_;
my $item_rs = NGCP::Panel::Utils::Contract::get_contract_rs(
schema => $c->model('DB'),
include_terminated => (defined $include_terminated && $include_terminated ? 1 : 0),
now => $now,
);
);
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
$item_rs = $item_rs->search({
'contact.reseller_id' => $c->user->reseller_id
},{
join => 'contact',
});
}
return $item_rs;
return $item_rs;
#my $item_rs = $c->model('DB')->resultset('contract_balances');
#if($c->user->roles eq "admin") {
#} elsif($c->user->roles eq "reseller") {
# $item_rs = $item_rs->search({
# $item_rs = $item_rs->search({
# 'contact.reseller_id' => $c->user->reseller_id
# },{
# join => { contract => 'contact' },
@ -47,10 +47,10 @@ sub _contract_rs {
}
sub _item_rs {
my $self = shift;
return $self->_contract_rs(@_);
}
sub get_form {
@ -60,7 +60,7 @@ sub get_form {
sub hal_from_balance {
my ($self, $c, $item, $form, $now, $use_root_collection_link) = @_;
my $contract = $item->contract;
my $is_customer = (defined $contract->contact->reseller_id ? 1 : 0);
my $bm_start = NGCP::Panel::Utils::ProfilePackages::get_actual_billing_mapping(c => $c, contract => $contract, now => $item->start);
@ -75,30 +75,31 @@ sub hal_from_balance {
$notopup_expiration = NGCP::Panel::Utils::ProfilePackages::get_notopup_expiration(
package => $contract->profile_package,
contract => $contract,
balance => $item) if $is_actual;
balance => $item) if $is_actual;
#my $invoice = $item->invoice;
my %resource = $item->get_inflated_columns;
$resource{cash_balance} /= 100.0;
$resource{initial_cash_balance} /= 100.0;
$resource{cash_debit} = (delete $resource{cash_balance_interval}) / 100.0;
$resource{free_time_spent} = delete $resource{free_time_balance_interval};
my $datetime_fmt = DateTime::Format::Strptime->new(
pattern => '%F %T',
pattern => '%F %T',
);
$resource{start} = delete $resource{start};
$resource{stop} = delete $resource{end};
$resource{start} = $datetime_fmt->format_datetime($resource{start}) if defined $resource{start};
$resource{stop} = $datetime_fmt->format_datetime($resource{stop}) if defined $resource{stop};
$resource{billing_profile_id} = $profile_at_start->id;
$resource{timely_topup_start} = (defined $timely_start ? $datetime_fmt->format_datetime($timely_start) : undef);
$resource{timely_topup_stop} = (defined $timely_end ? $datetime_fmt->format_datetime($timely_end) : undef);
$resource{notopup_discard_expiry} = (defined $notopup_expiration ? $datetime_fmt->format_datetime($notopup_expiration) : undef);
$resource{is_actual} = $is_actual;
my $hal = NGCP::Panel::Utils::DataHal->new(
links => [
NGCP::Panel::Utils::DataHalLink->new(
@ -143,14 +144,14 @@ sub contract_by_id {
sub balances_rs {
my ($self, $c, $contract, $now) = @_;
$now //= NGCP::Panel::Utils::DateTime::current_local;
NGCP::Panel::Utils::ProfilePackages::catchup_contract_balances(c => $c,
contract => $contract,
now => $now);
return $self->apply_query_params($c,$self->can('query_params') ? $self->query_params : {},$contract->contract_balances);
}
sub balance_by_id {
@ -160,12 +161,12 @@ sub balance_by_id {
my $balance = NGCP::Panel::Utils::ProfilePackages::catchup_contract_balances(c => $c,
contract => $contract,
now => $now);
if (defined $id) {
$balance = $contract->contract_balances->find($id);
}
return $balance;
}
1;

@ -102,6 +102,14 @@ sub item_by_id {
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form, $now) = @_;
# remove any readonly field before validation:
my %ro_fields = map { $_ => 1; } keys %$resource;
$ro_fields{cash_balance} = 0;
$ro_fields{free_time_balance} = 0;
foreach my $field (keys %$resource) {
delete $resource->{$field} if $ro_fields{$field};
}
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
@ -109,17 +117,24 @@ sub update_item {
resource => $resource,
);
$item = NGCP::Panel::Utils::ProfilePackages::underrun_update_balance(c => $c,
my $entities = { contract => $item->contract, };
my $log_vals = {};
$item = NGCP::Panel::Utils::ProfilePackages::set_contract_balance(
c => $c,
balance => $item,
cash_balance => $resource->{cash_balance} * 100.0,
free_time_balance => $resource->{free_time_balance},
now => $now,
new_cash_balance => $resource->{cash_balance} * 100.0);
log_vals => $log_vals);
my $topup_log = NGCP::Panel::Utils::ProfilePackages::create_topup_log_record(
c => $c,
now => $now,
entities => $entities,
log_vals => $log_vals,
request_token => NGCP::Panel::Utils::ProfilePackages::API_DEFAULT_TOPUP_REQUEST_TOKEN,
);
$resource->{cash_balance} *= 100.0;
# ignoring cash_debit and free_time_spent:
$item->update({
cash_balance => $resource->{cash_balance},
free_time_balance => $resource->{free_time_balance},
});
$item->discard_changes;
return $item;

@ -251,7 +251,7 @@ sub insert_unique_billing_fees{
my($c,$schema,$profile,$fees,$return_created) = @params{qw/c schema profile fees return_created/};
$return_created //= 0;
#while we use lower id we don't need insert records from billing_fees, they are already contain in billing_fees with lower id
#while we use lower id we don't need insert records from billing_fees, they are already contain in billing_fees with lower id
$profile->billing_fees_raw->delete();
$schema->storage->dbh_do(sub{

@ -44,6 +44,7 @@ use constant _ENABLE_UNDERRUN_PROFILES => 1;
use constant _ENABLE_UNDERRUN_LOCK => 1;
use constant PANEL_TOPUP_REQUEST_TOKEN => 'panel';
use constant API_DEFAULT_TOPUP_REQUEST_TOKEN => 'api';
sub get_contract_balance {
my %params = @_;
@ -382,11 +383,56 @@ PREPARE_BALANCE_CATCHUP:
}
sub set_contract_balance {
my %params = @_;
my($c,$balance,$cash_balance,$free_time_balance,$now,$schema,$log_vals) = @params{qw/c balance cash_balance free_time_balance now schema log_vals/};
$schema //= $c->model('DB');
my $contract;
$contract = $balance->contract if $log_vals;
$now //= NGCP::Panel::Utils::DateTime::current_local;
$cash_balance //= $balance->cash_balance;
$free_time_balance //= $balance->free_time_balance;
$c->log->debug('set contract ' . $contract->id . ' cash_balance from ' . $balance->cash_balance . ' to ' . $cash_balance . ', free_time_balance from ' . $balance->free_time_balance . ' to ' . $free_time_balance);
if ($log_vals) {
my $package = $contract->profile_package;
$log_vals->{old_package} = ( $package ? { $package->get_inflated_columns } : undef);
$log_vals->{new_package} = $log_vals->{old_package};
$log_vals->{old_balance} = { $balance->get_inflated_columns };
my $bm_actual = get_actual_billing_mapping(schema => $schema, contract => $contract, now => $now);
my $profile = $bm_actual->billing_mappings->first->billing_profile;
$log_vals->{old_profile} = { $profile->get_inflated_columns };
$log_vals->{amount} = $cash_balance - $balance->cash_balance;
}
$balance = _underrun_update_balance(c => $c,
balance =>$balance,
now => $now,
new_cash_balance => $cash_balance );
$balance->update({
cash_balance => $cash_balance,
free_time_balance => $free_time_balance,
});
$contract->discard_changes();
if ($log_vals) {
$log_vals->{new_balance} = { $balance->get_inflated_columns };
my $bm_actual = get_actual_billing_mapping(schema => $schema, contract => $contract, now => $now);
my $profile = $bm_actual->billing_mappings->first->billing_profile;
$log_vals->{new_profile} = { $profile->get_inflated_columns };
}
return $balance;
}
sub topup_contract_balance {
my %params = @_;
my($c,$contract,$package,$voucher,$amount,$now,$request_token,$schema,$log_vals,$subscriber) = @params{qw/c contract package voucher amount now request_token schema log_vals subscriber/};
$schema = $c->model('DB');
$schema //= $c->model('DB');
$contract = lock_contracts(schema => $schema, contract_id => $contract->id);
$now //= NGCP::Panel::Utils::DateTime::current_local;
@ -508,8 +554,8 @@ sub create_topup_log_record {
return $c->model('DB')->resultset('topup_logs')->create({
username => $username,
timestamp => $now->hires_epoch,
type => ($is_cash ? 'cash' : 'voucher'),
outcome => ($is_success ? 'ok' : 'failed'),
type => (defined $is_cash ? ($is_cash ? 'cash' : 'voucher') : 'set_balance'),
outcome => ((not defined $is_cash or $is_success) ? 'ok' : 'failed'),
message => (defined $message ? substr($message,0,255) : undef),
subscriber_id => ($entities->{subscriber} ? $entities->{subscriber}->id : $resource->{subscriber_id}),
contract_id => ($entities->{contract} ? $entities->{contract}->id : $resource->{contract_id}),
@ -696,8 +742,10 @@ sub _get_balance_values {
$free_time_balance_interval = 0;
return [cash_balance => sprintf("%.4f",$cash_balance),
initial_cash_balance => sprintf("%.4f",$cash_balance),
cash_balance_interval => sprintf("%.4f",$cash_balance_interval),
free_time_balance => sprintf("%.0f",$free_time_balance),
initial_free_time_balance => sprintf("%.0f",$free_time_balance),
free_time_balance_interval => sprintf("%.0f",$free_time_balance_interval)];
}
@ -964,7 +1012,7 @@ sub underrun_lock_subscriber {
}
}
sub underrun_update_balance {
sub _underrun_update_balance {
my %params = @_;
my ($c,$balance,$new_cash_balance,$now,$schema) = @params{qw/c balance new_cash_balance now schema/};
$schema = $c->model('DB');
@ -1394,12 +1442,19 @@ sub get_balanceinterval_datatable_cols {
#convert_code => sub { my $s = shift; return $s if ($parser_date->parse_datetime($s) or $parser_datetime->parse_datetime($s)); } },
{ name => "end", search => 0, search_upper_column => 'interval', title => $c->loc('To'), },
#convert_code => sub { my $s = shift; return $s if ($parser_date->parse_datetime($s) or $parser_datetime->parse_datetime($s)); } },
{ name => "balance", search => 0, title => $c->loc('Cash'), literal_sql => "FORMAT(cash_balance / 100,2)" },
{ name => "initial_balance", search => 0, title => $c->loc('Initial Cash'), literal_sql => "FORMAT(initial_cash_balance / 100,2)" },
{ name => "balance", search => 0, title => $c->loc('Cash Balance'), literal_sql => "FORMAT(cash_balance / 100,2)" },
{ name => "debit", search => 0, title => $c->loc('Debit'), literal_sql => "FORMAT(cash_balance_interval / 100,2)" },
{ name => "topup_count", search => 0, title => $c->loc('#Top-ups') },
{ name => "timely_topup_count", search => 0, title => $c->loc('#Timely Top-ups') },
{ name => "underrun_profiles", search => 0, title => $c->loc('Underrun detected (Profiles)') },
{ name => "underrun_lock", search => 0, title => $c->loc('Underrun detected (Lock)') },
{ name => "initial_free_time_balance", search => 0, title => $c->loc('Initial Free-Time') },
{ name => "free_time_balance", search => 0, title => $c->loc('Free-Time Balance') },
{ name => "free_time_interval", search => 0, title => $c->loc('Free-Time spent') },
{ name => "topups", search => 0, title => $c->loc('#Top-ups (timely)'), literal_sql => 'CONCAT(topup_count," (",timely_topup_count,")")' },
{ name => "underrun_profiles", search => 0, title => $c->loc('Last Underrun (Profiles)') },
{ name => "underrun_lock", search => 0, title => $c->loc('Last Underrun (Lock)') },
);
}

@ -45,10 +45,10 @@ sub decrypt_code {
sub check_topup {
my %params = @_;
my ($c,$plain_code,$voucher_id,$now,$subscriber_id,$contract,$package_id,$schema,$err_code,$entities,$resource) = @params{qw/c plain_code voucher_id now subscriber_id contract package_id schema err_code entities resource/};
$schema //= $c->model('DB');
$now //= NGCP::Panel::Utils::DateTime::current_local;
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
@ -71,7 +71,7 @@ sub check_topup {
$entities->{subscriber} = $subscriber if defined $entities;
$contract //= $subscriber->contract;
}
$entities->{contract} = $contract if defined $entities;
unless($contract->status eq 'active') {
@ -80,17 +80,17 @@ sub check_topup {
unless($contract->contact->reseller) {
return 0 unless &{$err_code}('Contract is not a customer contract.');
}
# if reseller, check if subscriber_id belongs to the calling reseller
if($reseller_id && $reseller_id != $contract->contact->reseller_id) {
return 0 unless &{$err_code}('Subscriber customer contract belongs to another reseller.');
}
if (defined $plain_code || defined $voucher_id) {
my $voucher;
my $dtf = $schema->storage->datetime_parser;
if (defined $plain_code) {
$voucher = $schema->resultset('vouchers')->search_rs({
code => encrypt_code($c, $plain_code),
@ -120,21 +120,20 @@ sub check_topup {
if (defined $resource) {
$resource->{voucher_id} = undef if exists $resource->{voucher_id};
$resource->{voucher}->{id} = undef if (exists $resource->{voucher} && exists $resource->{voucher}->{id});
}
}
return 0 unless &{$err_code}("Invalid voucher ID $voucher_id, already used or expired.");
}
}
}
$entities->{voucher} = $voucher if defined $entities;
if($voucher->customer_id && $contract->id != $voucher->customer_id) {
return 0 unless &{$err_code}('Voucher is reserved for a different customer.');
}
}
unless($voucher->reseller_id == $contract->contact->reseller_id) {
return 0 unless &{$err_code}('Voucher belongs to another reseller.');
}
$entities->{voucher} = $voucher if defined $entities;
} else {
my $package = undef;
if (defined $package_id) {
@ -143,7 +142,7 @@ sub check_topup {
if (defined $resource) {
$resource->{package_id} = undef if exists $resource->{package_id};
$resource->{package}->{id} = undef if (exists $resource->{package} && exists $resource->{package}->{id});
}
}
return 0 unless &{$err_code}("Unknown profile package ID $package_id.");
}
$entities->{package} = $package if defined $entities;
@ -152,15 +151,15 @@ sub check_topup {
}
}
}
# TODO: add and check billing.vouchers.active flag for internal/emergency use
return 1;
}
sub get_datatable_cols {
my ($c,$hide_package) = @_;
return (
{ name => "id", "search" => 1, "title" => $c->loc("#") },

@ -446,7 +446,7 @@ $(function() {
<table class="table table-bordered table-striped table-highlight table-hover">
<thead>
<th>
<th colspan="4"></th>
<th colspan="6"></th>
</th>
</thead>
<tbody>
@ -454,17 +454,21 @@ $(function() {
[% money_format = format('%.2f') %]
<tr>
<td>[% c.loc('Cash balance') %]</td>
<td>[% money_format(balance.cash_balance / 100) %]</td>
<td>[% c.loc('Debit') %]</td>
<td>[% money_format( balance.cash_balance_interval / 100 ) %]</td>
<td>[% c.loc('Initial Cash Balance:') %]</td>
<td><b>[% money_format(balance.initial_cash_balance / 100) %]</b></td>
<td>[% c.loc('Cash Balance:') %]</td>
<td><b>[% money_format(balance.cash_balance / 100) %]</b></td>
<td>[% c.loc('Debit:') %]</td>
<td><b>[% money_format( balance.cash_balance_interval / 100 ) %]</b></td>
</tr>
<tr>
<td>[% c.loc('Free time balance') %]</td>
<td>[% balance.free_time_balance %]</td>
<td>[% c.loc('Free time spent') %]</td>
<td>[% balance.free_time_balance_interval %]</td>
<td>[% c.loc('Initial Free-Time Balance:') %]</td>
<td><b>[% balance.initial_free_time_balance %]</b></td>
<td>[% c.loc('Free-Time Balance:') %]</td>
<td><b>[% balance.free_time_balance %]</b></td>
<td>[% c.loc('Free-Time spent:') %]</td>
<td><b>[% balance.free_time_balance_interval %]</b></td>
</tr>
</tbody>
</table>
@ -477,22 +481,22 @@ $(function() {
</thead>
<tr>
<td>[% c.loc('Interval from') %]</td>
<td>[% balance.start %]</td>
<td>[% c.loc('Interval to') %]</td>
<td>[% balance.end %]</td>
<td>[% c.loc('Interval from:') %]</td>
<td><b>[% balance.start %]</b></td>
<td>[% c.loc('Interval to:') %]</td>
<td><b>[% balance.end %]</b></td>
</tr>
<tr>
<td>[% c.loc('"Timely" top-ups from') %]</td>
<td>[% timely_topup_start %]</td>
<td>[% c.loc('"Timely" top-ups to') %]</td>
<td>[% timely_topup_end %]</td>
<td>[% c.loc('"Timely" Top-ups from:') %]</td>
<td><b>[% timely_topup_start %]</b></td>
<td>[% c.loc('"Timely" Top-ups to:') %]</td>
<td><b>[% timely_topup_end %]</b></td>
</tr>
<tr>
<td colspan="2">[% c.loc('Balance will be discarded, if no tup-up happens until') %]</td>
<td colspan="2">[% notopup_expiration %]</td>
<td colspan="2">[% c.loc('Balance will be discarded, if no tup-up happens until:') %]</td>
<td colspan="2"><b>[% notopup_expiration %]</b></td>
</tr>
</tbody>
@ -509,16 +513,16 @@ $(function() {
[% money_format = format('%.2f') %]
<tr>
<td>[% c.loc('Actual profile package') %]</td>
<td>[% c.loc('Actual profile package:') %]</td>
<td>[% package.name %]</td>
<td>[% c.loc('Actual billing profile') %]</td>
<td>[% c.loc('Actual billing profile:') %]</td>
<td>[% mapping.billing_profile.name %]</td>
</tr>
<tr>
<td>[% c.loc('Balance threshold when underrun profiles get applied') %]</td>
<td>[% c.loc('Cash balance threshold when underrun profiles get applied:') %]</td>
<td>[% package.underrun_profile_threshold ? money_format( package.underrun_lock_threshold / 100 ) : '' %]</td>
<td>[% c.loc('Balance threshold when subscribers will be locked') %]</td>
<td>[% c.loc('Cash balance threshold when subscribers will be locked:') %]</td>
<td>[% package.underrun_lock_threshold ? money_format( package.underrun_lock_threshold / 100 ) : '' %]</td>
</tr>
</tbody>

@ -31,7 +31,7 @@ if ($is_local_env) {
}
}
$panel_config //= 'ngcp_panel.conf';
$catalyst_config = Config::General->new($panel_config);
$catalyst_config = Config::General->new($panel_config);
}
my %config = $catalyst_config->getall();
@ -85,7 +85,7 @@ my $request_count = 0;
#goto SKIP;
{
my $profile = _create_billing_profile('PROFILE_1');
my $customer = _create_customer(billing_profile_definition => 'id',
billing_profile_id => $profile->{id},);
my $subscriber = _create_subscriber($customer);
@ -97,32 +97,32 @@ my $request_count = 0;
_check_topup_log('failing topup cash validation (subscriber_id): ',[
{ outcome => 'failed', request_token => $request_token, message => 'Validation failed. field=\'subscriber_id\'' }
],'request_token='.$request_token);
$request_token = $t."_".$request_count; $request_count++;
_perform_topup_cash($subscriber,'invalid_amount',undef,$request_token,422);
_check_topup_log('failing topup cash validation (amount): ',[
{ outcome => 'failed', request_token => $request_token, message => 'Value cannot be converted to money' }
],'request_token='.$request_token);
$request_token = $t."_".$request_count; $request_count++;
_perform_topup_cash($subscriber,50,{ id => 'invalid' },$request_token,422);
_check_topup_log('failing topup cash validation (package_id): ',[
{ outcome => 'failed', request_token => $request_token, message => 'Validation failed. field=\'package_id\'' }
],'request_token='.$request_token);
],'request_token='.$request_token);
$request_token = $t."_".$request_count; $request_count++;
_perform_topup_voucher($subscriber,{ code => 'invalid' },$request_token,422);
_check_topup_log('failing topup voucher validation (voucher code): ',[
{ outcome => 'failed', request_token => $request_token, message => 'Invalid voucher code \'invalid\'' }
],'request_token='.$request_token);
],'request_token='.$request_token);
$request_token = $t."_".$request_count; $request_count++;
$request_token .= 'a' x (256 - length($request_token));
_perform_topup_voucher($subscriber,$voucher_1,$request_token,422);
_check_topup_log('failing topup voucher validation (request_token): ',[
{ outcome => 'failed', request_token => substr($request_token,0,255), message => 'Validation failed. field=\'request_token\'' }, #'Field should not exceed 255 characters' }
],'request_token='.substr($request_token,0,255));
$request_token = $t."_".$request_count; $request_count++;
_perform_topup_voucher($subscriber,$voucher_1,$request_token);
$request_token = $t."_".$request_count; $request_count++;
@ -130,16 +130,16 @@ my $request_count = 0;
_check_topup_log('failing topup voucher validation (voucher used): ',[
{ outcome => 'failed', request_token => $request_token, message => 'already used' }
],'request_token='.$request_token);
$request_token = $t."_".$request_count; $request_count++;
_perform_topup_voucher($subscriber,$voucher_2,$request_token,422);
_check_topup_log('failing topup voucher validation (voucher expired): ',[
{ outcome => 'failed', request_token => $request_token, message => 'expired' }
],'request_token='.$request_token);
],'request_token='.$request_token);
}
SKIP:
#SKIP:
{
my $profile_initial_1 = _create_billing_profile('INITIAL1');
my $profile_topup_1 = _create_billing_profile('TOPUP1');
@ -151,9 +151,7 @@ SKIP:
topup_profiles => [ { profile_id => $profile_topup_1->{id}, }, ],
#underrun_profiles => [ { profile_id => $profile_underrun->{id}, }, ],
);
my $customer = _create_customer(billing_profile_definition => 'package',
profile_package_id => $package_1->{id},);
my $subscriber = _create_subscriber($customer);
@ -163,12 +161,12 @@ SKIP:
_perform_topup_cash($subscriber,0.5,undef,$request_token_1);
my $request_token_2 = $t."_".$request_count; $request_count++;
_perform_topup_voucher($subscriber,$voucher_1,$request_token_2);
_check_topup_log('successful topups - subscriber_id, outcome filter: ',[
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_2 },
],'subscriber_id='.$subscriber->{id}.'&outcome=ok');
_check_topup_log('successful topups - contract_id filter: ',[
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_2 },
@ -184,7 +182,7 @@ SKIP:
topup_profiles => [ { profile_id => $profile_topup_2->{id}, }, ],
#underrun_profiles => [ { profile_id => $profile_underrun->{id}, }, ],
);
my $voucher_2 = _create_voucher(30,'test4'.$t,$customer,$package_2);
my $request_token_3 = $t."_".$request_count; $request_count++;
_perform_topup_voucher($subscriber,$voucher_2,$request_token_3);
@ -192,54 +190,107 @@ SKIP:
_check_topup_log('successful topups - voucher_id filter: ',[
{ outcome => 'ok', request_token => $request_token_3 },
],'voucher_id='.$voucher_2->{id});
_check_topup_log('successful topups - amount_above filter: ',[
{ outcome => 'ok', request_token => $request_token_2 },
{ outcome => 'ok', request_token => $request_token_2 },
{ outcome => 'ok', request_token => $request_token_3 },
],'amount_above=1&subscriber_id='.$subscriber->{id});
_check_topup_log('successful topups - amount_below filter: ',[
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_2 },
],'amount_below=10&subscriber_id='.$subscriber->{id});
_check_topup_log('successful topups - timestamp_from filter: ',[
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_1 },
{ outcome => 'ok', request_token => $request_token_2 },
{ outcome => 'ok', request_token => $request_token_3 },
{ outcome => 'ok', request_token => $request_token_3 },
],'timestamp_from=2000-01-01T00:00:00&subscriber_id='.$subscriber->{id});
_check_topup_log('successful topups - balance before/after: ',[
{ outcome => 'ok', cash_balance_before => 1, cash_balance_after => 1.5, request_token => $request_token_1 },
{ outcome => 'ok', cash_balance_before => 1, cash_balance_after => 1.5, request_token => $request_token_1 },
{ outcome => 'ok', cash_balance_before => 1.5, cash_balance_after => 11.5, request_token => $request_token_2 },
{ outcome => 'ok', cash_balance_before => 11.5, cash_balance_after => 41.5, request_token => $request_token_3 },
],'contract_id='.$customer->{id});
{ outcome => 'ok', cash_balance_before => 11.5, cash_balance_after => 41.5, request_token => $request_token_3 },
],'contract_id='.$customer->{id});
_check_topup_log('successful topups - package before/after: ',[
{ outcome => 'ok', package_before_id=> $package_1->{id}, package_after_id=> $package_1->{id}, request_token => $request_token_1 },
{ outcome => 'ok', package_before_id=> $package_1->{id}, package_after_id=> $package_1->{id}, request_token => $request_token_1 },
{ outcome => 'ok', package_before_id=> $package_1->{id}, package_after_id=> $package_1->{id}, request_token => $request_token_2 },
{ outcome => 'ok', package_before_id=> $package_1->{id}, package_after_id=> $package_2->{id}, request_token => $request_token_3 },
],'contract_id='.$customer->{id});
{ outcome => 'ok', package_before_id=> $package_1->{id}, package_after_id=> $package_2->{id}, request_token => $request_token_3 },
],'contract_id='.$customer->{id});
_check_topup_log('successful topups - profile before/after: ',[
{ outcome => 'ok', profile_before_id => $profile_initial_1->{id}, profile_after_id => $profile_topup_1->{id}, request_token => $request_token_1 },
{ outcome => 'ok', profile_before_id => $profile_initial_1->{id}, profile_after_id => $profile_topup_1->{id}, request_token => $request_token_1 },
{ outcome => 'ok', profile_before_id => $profile_topup_1->{id}, profile_after_id => $profile_topup_1->{id}, request_token => $request_token_2 },
{ outcome => 'ok', profile_before_id => $profile_topup_1->{id}, profile_after_id => $profile_topup_2->{id},request_token => $request_token_3 },
],'contract_id='.$customer->{id});
{ outcome => 'ok', profile_before_id => $profile_topup_1->{id}, profile_after_id => $profile_topup_2->{id},request_token => $request_token_3 },
],'contract_id='.$customer->{id});
}
SKIP:
{
my $profile = _create_billing_profile('PROFILE_2');
my $customer = _create_customer(billing_profile_definition => 'id',
billing_profile_id => $profile->{id},);
my $cash_balance = 5; # euro
_perform_set_balance($customer,$cash_balance,0);
_check_topup_log('set cash balance: ',[
{
outcome => 'ok',
amount => $cash_balance,
profile_before_id => $profile->{id},
profile_after_id => $profile->{id},
cash_balance_before => 0,
cash_balance_after => $cash_balance,
},
],'contract_id='.$customer->{id});
# patch free time balance only:
$req = HTTP::Request->new('PATCH', $uri.'/api/customerbalances/'.$customer->{id});
$req->header('Prefer' => 'return=representation');
$req->header('Content-Type' => 'application/json-patch+json');
$req->content(JSON::to_json(
[ { op => 'replace', path => '/free_time_balance', value => 0 } ]
, { allow_nonref => 1, allow_blessed => 1, convert_blessed => 1, pretty => 0 }));
$res = $ua->request($req);
is($res->code, 200, "patch customer balances free time balance only");
my $customerbalance = JSON::from_json($res->decoded_content, { allow_nonref => 1, });
_check_topup_log('patch free time balance: ',[
{
outcome => 'ok',
amount => $cash_balance,
profile_before_id => $profile->{id},
profile_after_id => $profile->{id},
cash_balance_before => 0,
cash_balance_after => $cash_balance,
},
{
outcome => 'ok',
amount => 0,
profile_before_id => $profile->{id},
profile_after_id => $profile->{id},
cash_balance_before => $cash_balance,
cash_balance_after => $cash_balance,
},
],'contract_id='.$customer->{id});
}
done_testing;
sub _check_topup_log {
my ($label,$expected_topup_log,$filter_query) = @_;
my $total_count = (scalar @$expected_topup_log);
my $i = 0;
my $nexturi = $uri.'/api/topuplogs/?page=1&rows=10&order_by_direction=asc&order_by=timestamp'.(defined $filter_query ? '&'.$filter_query : '');
do {
$req = HTTP::Request->new('GET',$nexturi);
$res = $ua->request($req);
$res = $ua->request($req);
#$res = $ua->get($nexturi);
is($res->code, 200, $label."fetch topup log collection page");
my $collection = JSON::from_json($res->decoded_content);
@ -272,28 +323,28 @@ sub _check_topup_log {
# TODO: I'd expect that to be an array ref in any case!
ok(ref $collection->{_embedded}->{'ngcp:topuplogs'} eq "ARRAY", $label."check if 'ngcp:topuplogs' is array");
#my $page_items = {};
foreach my $log_record (@{ $collection->{_embedded}->{'ngcp:topuplogs'} }) {
#$req = HTTP::Request->new('GET',$uri.$log_record->{_links}->{self}->{href});
#$res = $ua->request($req);
#$res = $ua->request($req);
#is($res->code, 200, $label."fetch topup log entry");
#my $got = JSON::from_json($res->decoded_content);
#is_deeply($got,$log_record,$label.'check topup log entry deeply');
_compare_log_record($label,$log_record,$expected_topup_log->[$i]);
$i++
}
} while($nexturi);
ok($i == $total_count,$label."check if all expected items are listed");
}
sub _compare_log_record {
my ($label,$got,$expected) = @_;
foreach my $field (keys %$expected) {
if ('message' eq $field) {
ok($got->{$field} =~ /$expected->{$field}/,$label."check log '" . $field . "': " . $got->{$field} . " =~ /" . $expected->{$field} . '/');
@ -305,7 +356,7 @@ sub _compare_log_record {
}
sub _create_customer {
my (@further_opts) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/customers/');
$req->header('Content-Type' => 'application/json');
@ -327,7 +378,7 @@ sub _create_customer {
my $customer = JSON::from_json($res->decoded_content);
$customer_map->{$customer->{id}} = $customer;
return $customer;
}
sub _create_profile_package {
@ -362,11 +413,11 @@ sub _create_profile_package {
}
sub _create_voucher {
my ($amount,$code,$customer,$package,@further_opts) = @_;
my $dtf = DateTime::Format::Strptime->new(
pattern => '%F %T',
);
pattern => '%F %T',
);
$req = HTTP::Request->new('POST', $uri.'/api/vouchers/');
$req->header('Content-Type' => 'application/json');
my $req_data = {
@ -390,7 +441,7 @@ sub _create_voucher {
my $voucher = JSON::from_json($res->decoded_content);
$voucher_map->{$voucher->{id}} = $voucher;
return $voucher;
}
sub _create_subscriber {
@ -418,7 +469,7 @@ sub _create_subscriber {
}
sub _perform_topup_voucher {
my ($subscriber,$voucher,$request_token,$error_code) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/topupvouchers/');
$req->header('Content-Type' => 'application/json');
@ -431,11 +482,11 @@ sub _perform_topup_voucher {
$res = $ua->request($req);
$error_code //= 204;
is($res->code, $error_code, ($error_code == 204 ? 'perform' : 'attempt')." perform topup with voucher " . $voucher->{code});
}
sub _perform_topup_cash {
my ($subscriber,$amount,$package,$request_token,$error_code) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/topupcash/');
$req->header('Content-Type' => 'application/json');
@ -449,7 +500,23 @@ sub _perform_topup_cash {
$res = $ua->request($req);
$error_code //= 204;
is($res->code, $error_code, ($error_code == 204 ? 'perform' : 'attempt')." topup with amount " . ( looks_like_number($amount) ? $amount * 100.0 . ' cents' : $amount) . ", " . ($package ? 'package id ' . $package->{id} : 'no package'));
}
sub _perform_set_balance {
my ($customer,$cash_balance,$free_time_balance,$error_code) = @_;
$req = HTTP::Request->new('PUT', $uri.'/api/customerbalances/' . $customer->{id});
$req->header('Content-Type' => 'application/json');
my $req_data = {
cash_balance => $cash_balance,
free_time_balance => $free_time_balance,
};
$req->content(JSON::to_json($req_data));
$res = $ua->request($req);
$error_code //= 204;
is($res->code, $error_code, ($error_code == 204 ? 'perform' : 'attempt')." setting cash_balance to " . $cash_balance . ", free_time_balance to $free_time_balance");
}
sub _create_billing_profile {

Loading…
Cancel
Save