You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
17 KiB
438 lines
17 KiB
package NGCP::Panel::Controller::Customer;
|
|
use Sipwise::Base;
|
|
use namespace::sweep;
|
|
BEGIN { extends 'Catalyst::Controller'; }
|
|
use NGCP::Panel::Utils::Contract;
|
|
use NGCP::Panel::Form::CustomerMonthlyFraud;
|
|
use NGCP::Panel::Form::CustomerDailyFraud;
|
|
use NGCP::Panel::Form::CustomerBalance;
|
|
use NGCP::Panel::Form::Customer::Subscriber;
|
|
use NGCP::Panel::Form::Customer::PbxAdminSubscriber;
|
|
use NGCP::Panel::Form::Customer::PbxExtensionSubscriber;
|
|
use NGCP::Panel::Utils::Message;
|
|
use NGCP::Panel::Utils::Navigation;
|
|
use NGCP::Panel::Utils::DateTime;
|
|
use UUID qw/generate unparse/;
|
|
|
|
=head1 NAME
|
|
|
|
NGCP::Panel::Controller::Customer - Catalyst Controller
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Catalyst Controller.
|
|
|
|
=head1 METHODS
|
|
|
|
=cut
|
|
|
|
sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) {
|
|
my ($self, $c) = @_;
|
|
$c->log->debug(__PACKAGE__ . '::auto');
|
|
NGCP::Panel::Utils::Navigation::check_redirect_chain(c => $c);
|
|
return 1;
|
|
}
|
|
|
|
sub list_customer :Chained('/') :PathPart('customer') :CaptureArgs(0) {
|
|
my ($self, $c) = @_;
|
|
|
|
$c->stash->{contract_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
|
|
{ name => "id", search => 1, title => "#" },
|
|
{ name => "external_id", search => 1, title => "External #" },
|
|
{ name => "contact.reseller.name", search => 1, title => "Reseller" },
|
|
{ name => "contact.email", search => 1, title => "Contact Email" },
|
|
{ name => "billing_mappings.product.name", search => 1, title => "Product" },
|
|
{ name => "billing_mappings.billing_profile.name", search => 1, title => "Billing Profile" },
|
|
{ name => "status", search => 1, title => "Status" },
|
|
]);
|
|
|
|
$c->stash(
|
|
template => 'customer/list.tt'
|
|
);
|
|
}
|
|
|
|
sub root :Chained('list_customer') :PathPart('') :Args(0) {
|
|
my ($self, $c) = @_;
|
|
}
|
|
|
|
sub base :Chained('list_customer') :PathPart('') :CaptureArgs(1) {
|
|
my ($self, $c, $contract_id) = @_;
|
|
|
|
unless($contract_id && $contract_id->is_integer) {
|
|
$c->flash(messages => [{type => 'error', text => 'Invalid contract id detected!'}]);
|
|
$c->response->redirect($c->uri_for());
|
|
return;
|
|
}
|
|
|
|
my $contract = $c->model('DB')->resultset('contracts')
|
|
->search('me.id' => $contract_id);
|
|
unless($c->user->is_superuser) {
|
|
$contract = $contract->search({
|
|
'contact.reseller_id' => $c->user->reseller_id,
|
|
}, {
|
|
join => 'contact',
|
|
});
|
|
}
|
|
|
|
my $stime = NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month');
|
|
my $etime = $stime->clone->add(months => 1);
|
|
my $balance = $contract->first->contract_balances
|
|
->find({
|
|
start => { '>=' => $stime },
|
|
end => { '<' => $etime },
|
|
});
|
|
unless($balance) {
|
|
try {
|
|
NGCP::Panel::Utils::Contract::create_contract_balance(
|
|
c => $c,
|
|
profile => $contract->first->billing_mappings->search({
|
|
-and => [
|
|
-or => [
|
|
start_date => undef,
|
|
start_date => { '<=' => NGCP::Panel::Utils::DateTime::current_local },
|
|
],
|
|
-or => [
|
|
end_date => undef,
|
|
end_date => { '>=' => NGCP::Panel::Utils::DateTime::current_local },
|
|
]
|
|
],
|
|
},
|
|
{
|
|
order_by => { -desc => 'start_time', -desc => 'id' }
|
|
})->first->billing_profile,
|
|
contract => $contract->first,
|
|
);
|
|
} catch($e) {
|
|
NGCP::Panel::Utils::Message->error(
|
|
c => $c,
|
|
error => $e,
|
|
desc => "Failed to create contract balance.",
|
|
);
|
|
$c->response->redirect($c->uri_for());
|
|
return;
|
|
}
|
|
$balance = $contract->first->contract_balances
|
|
->find({
|
|
start => { '>=' => $stime },
|
|
end => { '<' => $etime },
|
|
});
|
|
}
|
|
|
|
my $contract_select_rs = NGCP::Panel::Utils::Contract::get_contract_rs(c => $c);
|
|
$contract_select_rs = $contract_select_rs->search({
|
|
'me.id' => $contract_id,
|
|
});
|
|
my $product_id = $contract_select_rs->search({'me.id' => $contract_id})->first->get_column('product_id');
|
|
NGCP::Panel::Utils::Message->error(
|
|
c => $c,
|
|
error => "No product for customer contract id $contract_id found",
|
|
desc => "No product for this customer contract found.",
|
|
) unless($product_id);
|
|
my $product = $c->model('DB')->resultset('products')->find($product_id);
|
|
NGCP::Panel::Utils::Message->error(
|
|
c => $c,
|
|
error => "No product with id $product_id for customer contract id $contract_id found",
|
|
desc => "Invalid product id for this customer contract.",
|
|
) unless($product);
|
|
|
|
my @subscribers = ();
|
|
foreach my $s($contract->first->voip_subscribers->search_rs({ status => 'active' })->all) {
|
|
my $sub = { $s->get_columns };
|
|
$sub->{domain} = $s->domain->domain;
|
|
$sub->{primary_number} = {$s->primary_number->get_columns} if(defined $s->primary_number);
|
|
$sub->{locations} = [ map { { $_->get_columns } } $c->model('DB')->resultset('location')->
|
|
search({
|
|
username => $s->username,
|
|
domain => $s->domain->domain,
|
|
})->all ];
|
|
push @subscribers, $sub;
|
|
}
|
|
$c->stash->{subscribers} = \@subscribers;
|
|
|
|
$c->stash(product => $product);
|
|
$c->stash(balance => $balance);
|
|
$c->stash(fraud => $contract->first->contract_fraud_preference);
|
|
$c->stash(template => 'customer/details.tt');
|
|
$c->stash(contract => $contract->first);
|
|
$c->stash(contract_rs => $contract);
|
|
}
|
|
|
|
sub details :Chained('base') :PathPart('details') :Args(0) {
|
|
my ($self, $c) = @_;
|
|
|
|
$c->stash->{contact_hash} = { $c->stash->{contract}->contact->get_inflated_columns };
|
|
}
|
|
|
|
sub subscriber_create :Chained('base') :PathPart('subscriber/create') :Args(0) {
|
|
my ($self, $c) = @_;
|
|
|
|
my $pbx;
|
|
$pbx = 1 if $c->stash->{product}->class eq 'pbxaccount';
|
|
my $form;
|
|
if($c->config->{features}->{cloudpbx} && $pbx) {
|
|
# we need to create an admin subscriber first
|
|
|
|
# TODO: should we check whether one of the existing subscribers has the admin flag set?
|
|
unless(@{ $c->stash->{subscribers} }) {
|
|
$form = NGCP::Panel::Form::Customer::PbxAdminSubscriber->new(ctx => $c);
|
|
} else {
|
|
$form = NGCP::Panel::Form::Customer::PbxExtensionSubscriber->new(ctx => $c);
|
|
}
|
|
} else {
|
|
$form = NGCP::Panel::Form::Customer::Subscriber->new;
|
|
}
|
|
|
|
my $params = {};
|
|
$params = $params->merge($c->session->{created_objects});
|
|
$form->process(
|
|
posted => ($c->request->method eq 'POST'),
|
|
params => $c->request->params,
|
|
item => $params,
|
|
);
|
|
NGCP::Panel::Utils::Navigation::check_form_buttons(
|
|
c => $c,
|
|
form => $form,
|
|
fields => [qw/domain.create/],
|
|
back_uri => $c->req->uri,
|
|
);
|
|
if($form->validated) {
|
|
my $schema = $c->model('DB');
|
|
my $contract = $c->stash->{contract};
|
|
my $reseller = $contract->contact->reseller;
|
|
my $billing_domain = $schema->resultset('domains')
|
|
->find($c->request->params->{'domain.id'});
|
|
my $prov_domain = $schema->resultset('voip_domains')
|
|
->find({domain => $billing_domain->domain});
|
|
try {
|
|
$schema->txn_do(sub {
|
|
my ($uuid_bin, $uuid_string);
|
|
UUID::generate($uuid_bin);
|
|
UUID::unparse($uuid_bin, $uuid_string);
|
|
|
|
# TODO: check if we find a reseller and contract and domains
|
|
|
|
my $number; my $cli = 0;
|
|
if(defined $c->request->params->{'e164.cc'} &&
|
|
$c->request->params->{'e164.cc'} ne '') {
|
|
$cli = $c->request->params->{'e164.cc'} .
|
|
($c->request->params->{'e164.ac'} || '') .
|
|
$c->request->params->{'e164.sn'};
|
|
|
|
$number = $reseller->voip_numbers->create({
|
|
cc => $c->request->params->{'e164.cc'},
|
|
ac => $c->request->params->{'e164.ac'} || '',
|
|
sn => $c->request->params->{'e164.sn'},
|
|
status => 'active',
|
|
});
|
|
}
|
|
my $billing_subscriber = $contract->voip_subscribers->create({
|
|
uuid => $uuid_string,
|
|
username => $c->request->params->{username},
|
|
domain_id => $billing_domain->id,
|
|
status => $c->request->params->{status},
|
|
primary_number_id => defined $number ? $number->id : undef,
|
|
});
|
|
if(defined $number) {
|
|
$number->update({ subscriber_id => $billing_subscriber->id });
|
|
}
|
|
|
|
my $prov_subscriber = $schema->resultset('provisioning_voip_subscribers')->create({
|
|
uuid => $uuid_string,
|
|
username => $c->request->params->{username},
|
|
password => $c->request->params->{password},
|
|
webusername => $c->request->params->{webusername} || $c->request->params->{username},
|
|
webpassword => $c->request->params->{webpassword},
|
|
admin => $c->request->params->{administrative} || 0,
|
|
account_id => $contract->id,
|
|
domain_id => $prov_domain->id,
|
|
create_timestamp => NGCP::Panel::Utils::DateTime::current_local,
|
|
});
|
|
|
|
my $voip_preferences = $schema->resultset('voip_preferences')->search({
|
|
'usr_pref' => 1,
|
|
});
|
|
$voip_preferences->find({ 'attribute' => 'account_id' })
|
|
->voip_usr_preferences->create({
|
|
'subscriber_id' => $prov_subscriber->id,
|
|
'value' => $contract->id,
|
|
});
|
|
$voip_preferences->find({ 'attribute' => 'ac' })
|
|
->voip_usr_preferences->create({
|
|
'subscriber_id' => $prov_subscriber->id,
|
|
'value' => $c->request->params->{'e164.ac'},
|
|
}) if (defined $c->request->params->{'e164.ac'} &&
|
|
length($c->request->params->{'e164.ac'}) > 0);
|
|
if(defined $c->request->params->{'e164.cc'} &&
|
|
length($c->request->params->{'e164.cc'}) > 0) {
|
|
|
|
$voip_preferences->find({ 'attribute' => 'cc' })
|
|
->voip_usr_preferences->create({
|
|
'subscriber_id' => $prov_subscriber->id,
|
|
'value' => $c->request->params->{'e164.cc'},
|
|
});
|
|
$cli = $c->request->params->{'e164.cc'} .
|
|
(defined $c->request->params->{'e164.ac'} &&
|
|
length($c->request->params->{'e164.ac'}) > 0 ?
|
|
$c->request->params->{'e164.ac'} : ''
|
|
) .
|
|
$c->request->params->{'e164.sn'};
|
|
$voip_preferences->find({ 'attribute' => 'cli' })
|
|
->voip_usr_preferences->create({
|
|
'subscriber_id' => $prov_subscriber->id,
|
|
'value' => $cli,
|
|
});
|
|
}
|
|
|
|
$schema->resultset('voicemail_users')->create({
|
|
customer_id => $uuid_string,
|
|
mailbox => $cli,
|
|
password => sprintf("%04d", int(rand 10000)),
|
|
email => '',
|
|
});
|
|
if($number) {
|
|
$schema->resultset('dbaliases')->create({
|
|
alias_username => $number->cc .
|
|
($number->ac || '').
|
|
$number->sn,
|
|
alias_domain => $prov_subscriber->domain->domain,
|
|
username => $prov_subscriber->username,
|
|
domain => $prov_subscriber->domain->domain,
|
|
});
|
|
}
|
|
|
|
delete $c->session->{created_objects}->{domain};
|
|
|
|
});
|
|
$c->flash(messages => [{type => 'success', text => 'Subscriber successfully created!'}]);
|
|
$c->response->redirect($c->uri_for_action('/customer/details', [$contract->id]));
|
|
return;
|
|
} catch($e) {
|
|
NGCP::Panel::Utils::Message->error(
|
|
c => $c,
|
|
error => $e,
|
|
desc => "Failed to create subscriber.",
|
|
);
|
|
$c->response->redirect($c->uri_for_action('/customer/details', [$contract->id]));
|
|
return;
|
|
}
|
|
}
|
|
|
|
$c->stash(close_target => $c->uri_for());
|
|
$c->stash(create_flag => 1);
|
|
$c->stash(form => $form)
|
|
}
|
|
|
|
sub edit_fraud :Chained('base') :PathPart('fraud/edit') :Args(1) {
|
|
my ($self, $c, $type) = @_;
|
|
|
|
my $posted = ($c->request->method eq 'POST');
|
|
my $form;
|
|
if($type eq "month") {
|
|
$form = NGCP::Panel::Form::CustomerMonthlyFraud->new;
|
|
} elsif($type eq "day") {
|
|
$form = NGCP::Panel::Form::CustomerDailyFraud->new;
|
|
} else {
|
|
$c->flash(messages => [{type => 'error', text => "Invalid fraud interval '$type'!"}]);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
|
|
my $fraud_prefs = $c->stash->{fraud} ||
|
|
$c->model('DB')->resultset('contract_fraud_preferences')
|
|
->new_result({ contract_id => $c->stash->{contract}->id});
|
|
$form->process(
|
|
posted => $posted,
|
|
params => $c->request->params,
|
|
action => $c->uri_for_action("/customer/edit_fraud", $c->stash->{contract}->id, $type),
|
|
item => $fraud_prefs,
|
|
);
|
|
if($posted && $form->validated) {
|
|
$c->flash(messages => [{type => 'success', text => 'Fraud settings successfully changed!'}]);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
|
|
$c->stash(close_target => $c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
$c->stash(form => $form);
|
|
$c->stash(edit_flag => 1);
|
|
}
|
|
|
|
sub delete_fraud :Chained('base') :PathPart('fraud/delete') :Args(1) {
|
|
my ($self, $c, $type) = @_;
|
|
|
|
if($type eq "month") {
|
|
$type = "interval";
|
|
} elsif($type eq "day") {
|
|
$type = "daily";
|
|
} else {
|
|
$c->flash(messages => [{type => 'error', text => "Invalid fraud interval '$type'!"}]);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
|
|
my $fraud_prefs = $c->stash->{fraud};
|
|
if($fraud_prefs) {
|
|
try {
|
|
$fraud_prefs->update({
|
|
"fraud_".$type."_limit" => undef,
|
|
"fraud_".$type."_lock" => undef,
|
|
"fraud_".$type."_notify" => undef,
|
|
});
|
|
} catch($e) {
|
|
NGCP::Panel::Utils::Message->error(
|
|
c => $c,
|
|
error => $e,
|
|
desc => "Failed to clear fraud interval.",
|
|
);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
}
|
|
$c->flash(messages => [{type => 'success', text => "Successfully cleared fraud interval!"}]);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
|
|
sub edit_balance :Chained('base') :PathPart('balance/edit') :Args(0) {
|
|
my ($self, $c) = @_;
|
|
|
|
my $posted = ($c->request->method eq 'POST');
|
|
my $form = NGCP::Panel::Form::CustomerBalance->new;
|
|
|
|
$form->process(
|
|
posted => $posted,
|
|
params => $c->request->params,
|
|
action => $c->uri_for_action("/customer/edit_balance", [$c->stash->{contract}->id]),
|
|
item => $c->stash->{balance},
|
|
);
|
|
if($posted && $form->validated) {
|
|
$c->flash(messages => [{type => 'success', text => 'Account balance successfully changed!'}]);
|
|
$c->response->redirect($c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
return;
|
|
}
|
|
|
|
$c->stash(close_target => $c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
|
|
$c->stash(form => $form);
|
|
$c->stash(edit_flag => 1);
|
|
}
|
|
|
|
sub pbx_group_ajax :Chained('base') :PathPart('pbx/group/ajax') :Args(0) {
|
|
}
|
|
|
|
=head1 AUTHOR
|
|
|
|
Andreas Granig,,,
|
|
|
|
=head1 LICENSE
|
|
|
|
This library is free software. You can redistribute it and/or modify
|
|
it under the same terms as Perl itself.
|
|
|
|
=cut
|
|
|
|
__PACKAGE__->meta->make_immutable;
|
|
|
|
1;
|
|
|
|
# vim: set tabstop=4 expandtab:
|