diff --git a/lib/NGCP/Panel/Controller/API/CustomerZoneCosts.pm b/lib/NGCP/Panel/Controller/API/CustomerZoneCosts.pm index 2e4d86e9cc..fb19751bfe 100644 --- a/lib/NGCP/Panel/Controller/API/CustomerZoneCosts.pm +++ b/lib/NGCP/Panel/Controller/API/CustomerZoneCosts.pm @@ -38,6 +38,14 @@ class_has 'query_params' => ( }, }, }, + { + param => 'start', + description => 'Filter for a specific start time in format YYYY-MM-DDThhmmss.', + }, + { + param => 'end', + description => 'Filter for a specific end time in format YYYY-MM-DDThhmmss.', + }, ]}, ); diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 4e5782c564..a82d4e20d4 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -3,7 +3,6 @@ use Sipwise::Base; use namespace::sweep; BEGIN { extends 'Catalyst::Controller'; } use JSON qw(decode_json encode_json); -use NGCP::Panel::Utils::Contract; use NGCP::Panel::Form::CustomerMonthlyFraud; use NGCP::Panel::Form::CustomerDailyFraud; use NGCP::Panel::Form::CustomerBalance; @@ -21,6 +20,7 @@ use NGCP::Panel::Utils::Navigation; use NGCP::Panel::Utils::DateTime; use NGCP::Panel::Utils::Subscriber; use NGCP::Panel::Utils::Sounds; +use NGCP::Panel::Utils::Contract; use Template; =head1 NAME @@ -55,7 +55,7 @@ sub list_customer :Chained('/') :PathPart('customer') :CaptureArgs(0) { { name => "status", search => 1, title => $c->loc("Status") }, { name => "max_subscribers", search => 1, title => $c->loc("Max Number of Subscribers") }, ]); - my $rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( c => $c ); + my $rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); $c->stash( contract_select_rs => $rs, @@ -234,33 +234,24 @@ sub base :Chained('list_customer') :PathPart('') :CaptureArgs(1) { my $stime = NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month'); my $etime = $stime->clone->add(months => 1); - - my $balance = $contract_rs->first->contract_balances - ->find({ - start => { '>=' => $stime }, - end => { '<' => $etime }, - }); - unless($balance) { - try { - NGCP::Panel::Utils::Contract::create_contract_balance( - c => $c, - profile => $billing_mapping->billing_profile, - contract => $contract_rs->first, - ); - } catch($e) { - NGCP::Panel::Utils::Message->error( - c => $c, - error => $e, - desc => $c->loc('Failed to create contract balance.'), - ); - $c->response->redirect($c->uri_for()); - return; - } - $balance = $contract_rs->first->contract_balances - ->find({ - start => {'>=' => $stime}, - end => {'<' => $etime}, - }); + + my $balance; + try { + $balance = NGCP::Panel::Utils::Contract::get_contract_balance( + c => $c, + profile => $billing_mapping->billing_profile, + contract => $contract_rs->first, + stime => $stime, + etime => $etime + ); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to get contract balance.'), + ); + $c->response->redirect($c->uri_for()); + return; } my $product_id = $contract_rs->first->get_column('product_id'); diff --git a/lib/NGCP/Panel/Controller/Invoice.pm b/lib/NGCP/Panel/Controller/Invoice.pm new file mode 100644 index 0000000000..40f150ed35 --- /dev/null +++ b/lib/NGCP/Panel/Controller/Invoice.pm @@ -0,0 +1,300 @@ +package NGCP::Panel::Controller::Invoice; +use Sipwise::Base; +use namespace::sweep; +BEGIN { extends 'Catalyst::Controller'; } + +use NGCP::Panel::Utils::Message; +use NGCP::Panel::Utils::DateTime; +use NGCP::Panel::Utils::Contract; +use NGCP::Panel::Utils::InvoiceTemplate; +use NGCP::Panel::Form::Invoice::Invoice; + +sub auto :Private { + my ($self, $c) = @_; + $c->log->debug(__PACKAGE__ . '::auto'); + NGCP::Panel::Utils::Navigation::check_redirect_chain(c => $c); + return 1; +} + +sub inv_list :Chained('/') :PathPart('invoice') :CaptureArgs(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) :AllowedRole(subscriberadmin) { + my ( $self, $c ) = @_; + + $c->stash->{inv_rs} = $c->model('DB')->resultset('invoices'); + if($c->user->roles eq "admin") { + } elsif($c->user->roles eq "reseller") { + $c->stash->{inv_rs} = $c->stash->{inv_rs}->search({ + 'contact.reseller_id' => $c->user->reseller_id, + },{ + join => { contract => 'contact' }, + }); + } elsif($c->user->roles eq "subscriberadmin") { + $c->stash->{inv_rs} = $c->stash->{inv_rs}->search({ + contract_id => $c->user->account_id, + }); + }; + + $c->stash->{inv_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ + { name => 'id', search => 1, title => $c->loc('#') }, + { name => 'contract.id', search => 1, title => $c->loc('Customer #') }, + { name => 'contract.contact.email', search => 1, title => $c->loc('Customer Email') }, + { name => 'serial', search => 1, title => $c->loc('Serial') }, + ]); + + $c->stash(template => 'invoice/invoice_list.tt'); +} + +sub root :Chained('inv_list') :PathPart('') :Args(0) { + my ($self, $c) = @_; +} + +sub ajax :Chained('inv_list') :PathPart('ajax') :Args(0) { + my ($self, $c) = @_; + my $rs = $c->stash->{inv_rs}; + NGCP::Panel::Utils::Datatables::process($c, $rs, $c->stash->{inv_dt_columns}); + $c->detach( $c->view("JSON") ); +} + +sub base :Chained('inv_list') :PathPart('') :CaptureArgs(1) { + my ($self, $c, $inv_id) = @_; + + unless($inv_id && $inv_id->is_integer) { + NGCP::Panel::Utils::Message->error( + c => $c, + log => 'Invalid invoice id detected', + desc => $c->loc('Invalid invoice id detected'), + ); + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); + } + + my $res = $c->stash->{inv_rs}->find($inv_id); + unless(defined($res)) { + NGCP::Panel::Utils::Message->error( + c => $c, + log => 'Invoice does not exist', + desc => $c->loc('Invoice does not exist'), + ); + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); + } + $c->stash(inv => $res); +} + +sub create :Chained('inv_list') :PathPart('create') :Args() :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) { + my ($self, $c) = @_; + + my $posted = ($c->request->method eq 'POST'); + my $params = {}; + $params = $params->merge($c->session->{created_objects}); + + my $form; + $form = NGCP::Panel::Form::Invoice::Invoice->new(ctx => $c); + $form->process( + posted => $posted, + params => $c->request->params, + item => $params, + ); + NGCP::Panel::Utils::Navigation::check_form_buttons( + c => $c, + form => $form, + fields => { }, + back_uri => $c->req->uri, + ); + if($posted && $form->validated) { + try { + my $schema = $c->model('DB'); + $schema->txn_do(sub { + my $contract_id = $form->params->{contract}{id}; + my $customer_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); + my $customer = $customer_rs->find({ 'me.id' => $contract_id }); + unless($customer) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => "invalid contract_id $contract_id", + desc => $c->loc('Customer not found'), + ); + die; + } + delete $form->params->{contract}; + $form->params->{contract_id} = $contract_id; + + my $tmpl_id = $form->params->{template}{id}; + delete $form->params->{template}; + + my $tmpl = $schema->resultset('invoice_templates')->search({ + id => $tmpl_id, + }); + if($c->user->roles eq "admin") { + } elsif($c->user->roles eq "reseller") { + $tmpl = $tmpl->search({ + reseller_id => $c->user->reseller_id, + }); + } + $tmpl = $tmpl->first; + unless($tmpl) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => "invalid template id $tmpl_id", + desc => $c->loc('Invoice template not found'), + ); + die; + } + unless($tmpl->data) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => "invalid template id $tmpl_id, data is empty", + desc => $c->loc('Invoice template does not have an SVG stored yet'), + ); + die; + } + + unless($customer->contact->reseller_id == $tmpl->reseller_id) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => "template id ".$tmpl->id." has different reseller than contract id $contract_id", + desc => $c->loc('Template and customer must belong to same reseller'), + ); + die; + } + + my $stime = NGCP::Panel::Utils::DateTime::from_string( + delete $form->params->{period} + )->truncate(to => 'month'); + my $etime = $stime->clone->add(months => 1)->subtract(seconds => 1); + + my $zonecalls = NGCP::Panel::Utils::Contract::get_contract_zonesfees( + c => $c, + contract_id => $contract_id, + stime => $stime, + etime => $etime, + in => 0, + out => 1, + group_by_detail => 1, + ); + + my $billing_mapping = $customer->billing_mappings->find($customer->get_column('bmid')); + my $billing_profile = $billing_mapping->billing_profile; + + my $balance; + try { + $balance = NGCP::Panel::Utils::Contract::get_contract_balance( + c => $c, + profile => $billing_profile, + contract => $customer, + stime => $stime, + etime => $etime + ); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to get contract balance.'), + ); + die; + } + use Data::Printer; say ">>>>>>>>>>>>>>>>>>> balance\n"; p $balance; + + # TODO: generate pdf here, then insert as data + $form->params->{serial} = "test".time.int(rand(99999)); + + + $form->params->{amount_net} = $balance->cash_balance_interval; + $form->params->{amount_vat} = + $form->params->{amount_net} * ($billing_profile->vat_rate/100); # TODO: is it really in percent? + $form->params->{amount_total} = $form->params->{amount_net} + $form->params->{amount_vat}; + + + my $svg = $tmpl->data; + my $t = NGCP::Panel::Utils::InvoiceTemplate::get_tt(); + my $out = ''; + my $pdf = ''; + my $vars = {}; + + try { + NGCP::Panel::Utils::InvoiceTemplate::preprocess_svg(\$svg); + $t->process(\$svg, $vars, \$out) || do { + my $error = $t->error(); + my $msg = "error processing template, type=".$error->type.", info='".$error->info."'"; + NGCP::Panel::Utils::Message->error( + c => $c, + log => $msg, + desc => $c->loc('Failed to render template. Type is ' . $error->type . ', info is ' . $error->info), + ); + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); + return; + }; + + NGCP::Panel::Utils::InvoiceTemplate::svg_pdf($c, \$out, \$pdf); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + log => $e, + desc => $c->loc('Failed to preview template'), + ); + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); + return; + } + + $form->params->{data} = $pdf; + + # TODO: + #we are two hours off when converting back from epoch due to timezone + + $form->params->{period_start} = $stime->epoch; + $form->params->{period_end} = $etime->epoch; + + my $inv = $schema->resultset('invoices')->create($form->params); + }); + $c->flash(messages => [{type => 'success', text => $c->loc('Invoice successfully created')}]); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to create invoice .'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); + } + + $c->stash(form => $form); + $c->stash(create_flag => 1); +} + +sub delete :Chained('base') :PathPart('delete') { + my ($self, $c) = @_; + + try { + my $schema = $c->model('DB'); + $schema->txn_do(sub{ + $c->stash->{inv}->delete; + }); + $c->flash(messages => [{type => 'success', text => $c->loc('Invoice successfully deleted')}]); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to delete invoice .'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); +} + +sub download :Chained('base') :PathPart('download') { + my ($self, $c) = @_; + + try { + $c->response->content_type('application/pdf'); + $c->response->body($c->stash->{inv}->data); + return; + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to delete invoice .'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice')); +} + +__PACKAGE__->meta->make_immutable; +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 5ca742da5d..975c4ac7c2 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -153,14 +153,12 @@ sub create_list :Chained('sub_list') :PathPart('create') :Args(0) :Does(ACL) :AC try { $schema->txn_do(sub { my $preferences = {}; - my $contract_rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( c => $c ); - $contract_rs = $contract_rs->search({ + my $contract_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); + my $contract = $contract_rs->find({ 'me.id' => $form->params->{contract}{id}, - }, { - '+select' => 'billing_mappings.id', - '+as' => 'bmid', }); - my $contract = $contract_rs->first; + + my $billing_mapping = $contract->billing_mappings->find($contract->get_column('bmid')); if($contract->external_id) { @@ -225,14 +223,10 @@ sub base :Chained('sub_list') :PathPart('') :CaptureArgs(1) { $c->stash(subscriber => $res); $c->stash->{contract} = $c->stash->{subscriber}->contract; - my $contract_rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( c => $c ); - $contract_rs = $contract_rs->search({ + my $contract_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); + my $contract = $contract_rs->find({ 'me.id' => $c->stash->{contract}->id, - }, { - '+select' => 'billing_mappings.id', - '+as' => 'bmid', }); - my $contract = $contract_rs->first; my $billing_mapping = $contract->billing_mappings->find($contract->get_column('bmid')); $c->stash->{billing_mapping} = $billing_mapping; diff --git a/lib/NGCP/Panel/Field/DataTable.pm b/lib/NGCP/Panel/Field/DataTable.pm index 42bfa41c47..dd4bc0ff12 100644 --- a/lib/NGCP/Panel/Field/DataTable.pm +++ b/lib/NGCP/Panel/Field/DataTable.pm @@ -48,8 +48,6 @@ sub render_element { ], }); - use Data::Printer; p $vars; - $t->process($self->template, $vars, \$output) or die "Failed to process Datatables field template: ".$t->error(); diff --git a/lib/NGCP/Panel/Field/InvoiceTemplate.pm b/lib/NGCP/Panel/Field/InvoiceTemplate.pm new file mode 100644 index 0000000000..05ffb5140d --- /dev/null +++ b/lib/NGCP/Panel/Field/InvoiceTemplate.pm @@ -0,0 +1,17 @@ +package NGCP::Panel::Field::InvoiceTemplate; +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler::Field::Compound'; + +has_field 'id' => ( + type => '+NGCP::Panel::Field::DataTable', + label => 'Template', + do_label => 0, + do_wrapper => 0, + required => 1, + ajax_src => '/invoicetemplate/ajax', + template => 'helpers/datatables_field.tt', + table_titles => ['#', 'Reseller', 'Name', 'Active'], + table_fields => ['id', 'reseller_name', 'name', 'is_active'], +); + +1; diff --git a/lib/NGCP/Panel/Field/MonthPicker.pm b/lib/NGCP/Panel/Field/MonthPicker.pm new file mode 100644 index 0000000000..419bb306c8 --- /dev/null +++ b/lib/NGCP/Panel/Field/MonthPicker.pm @@ -0,0 +1,61 @@ +package NGCP::Panel::Field::MonthPicker; +use HTML::FormHandler::Moose; +use Template; +extends 'HTML::FormHandler::Field'; + +has '+widget' => (default => ''); # leave this empty, as there is no widget ... +has 'template' => ( isa => 'Str', + is => 'rw', + default => 'helpers/monthpicker.tt' ); +has 'language_file' => (isa => 'Str', is => 'rw', default => 'dataTables.default.js' ); +has 'date_format_js' => (isa => 'Str', is => 'rw', default => 'yy-mm' ); + +sub render_element { + my ($self) = @_; + my $output = ''; + + (my $fieldname = $self->html_name) =~ s!\.!!g; + + my $vars = { + label => $self->label, + field_name => $self->html_name, + field_id => $fieldname . "_datepicker", + value => $self->value, + date_format_js => $self->date_format_js, + errors => $self->errors, + language_file => $self->language_file, + }; + my $t = new Template({ + ABSOLUTE => 1, + INCLUDE_PATH => [ + '/usr/share/ngcp-panel/templates', + 'share/templates', + ], + }); + + $t->process($self->template, $vars, \$output) or + die "Failed to process Datepicker field template: ".$t->error(); + + return $output; +} + +sub render { + my ( $self, $result ) = @_; + $result ||= $self->result; + die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; + return $self->render_element( $result ); +} + +sub validate { + my ( $self ) = @_; + + return $self->add_error($self->label . " is invalid") + if($self->required and ( + !defined $self->value or !length($self->value) + )); + return 1; +} + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/Invoice/Invoice.pm b/lib/NGCP/Panel/Form/Invoice/Invoice.pm new file mode 100644 index 0000000000..9a9746f778 --- /dev/null +++ b/lib/NGCP/Panel/Form/Invoice/Invoice.pm @@ -0,0 +1,66 @@ +package NGCP::Panel::Form::Invoice::Invoice; + +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler'; +use Moose::Util::TypeConstraints; + +use HTML::FormHandler::Widget::Block::Bootstrap; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +has_field 'submitid' => ( type => 'Hidden' ); +sub build_render_list {[qw/submitid fields actions/]} +sub build_form_element_class {[qw(form-horizontal)]} + +has_field 'template' => ( + type => '+NGCP::Panel::Field::InvoiceTemplate', + label => 'Invoice Template', + validate_when_empty => 1, + element_attr => { + rel => ['tooltip'], + title => ['The invoice template to use for the invoice generation.'] + }, +); + +has_field 'contract' => ( + type => '+NGCP::Panel::Field::CustomerContract', + label => 'Customer', + validate_when_empty => 1, + element_attr => { + rel => ['tooltip'], + title => ['The contract to create the invoice for.'] + }, +); + +has_field 'period' => ( + #type => '+NGCP::Panel::Field::DateTime', + type => '+NGCP::Panel::Field::MonthPicker', + element_attr => { + rel => ['tooltip'], + title => ['YYYY-MM'] + }, + label => 'Invoice Period', + required => 1, +); + +has_field 'save' => ( + type => 'Submit', + value => 'Save', + element_class => [qw/btn btn-primary/], + label => '', +); + +has_block 'fields' => ( + tag => 'div', + class => [qw/modal-body/], + render_list => [qw/template contract period/], +); + +has_block 'actions' => ( + tag => 'div', + class => [qw/modal-footer/], + render_list => [qw/save/], +); + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/Subscriber/SubscriberAPI.pm b/lib/NGCP/Panel/Form/Subscriber/SubscriberAPI.pm index 97cd58a0e9..1b8ae7fa8f 100644 --- a/lib/NGCP/Panel/Form/Subscriber/SubscriberAPI.pm +++ b/lib/NGCP/Panel/Form/Subscriber/SubscriberAPI.pm @@ -119,7 +119,7 @@ has_field 'save' => ( has_block 'fields' => ( tag => 'div', class => [qw/modal-body/], - render_list => [qw/customer domain e164 alias_number email webusername webpassword username password status lock external_id administrative is_pbx_group pbx_group display_name profile_set profile/ ], + render_list => [qw/customer domain pbx_extension e164 alias_numbers email webusername webpassword username password status lock external_id administrative is_pbx_group pbx_group display_name profile_set profile/ ], ); has_block 'actions' => ( diff --git a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm deleted file mode 100644 index 48ae9dcedf..0000000000 --- a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm +++ /dev/null @@ -1,605 +0,0 @@ -package NGCP::Panel::Model::DB::InvoiceTemplate; -use base NGCP::Panel::Model::DB::Base; - -#use irka; -use Data::Dumper; - -sub getDefaultConditions{ - my $self = shift; - my ($params) = @_; - my ($provider_id,$tt_sourcestate,$tt_type,$tt_id) = @$params{qw/provider_id tt_sourcestate tt_type tt_id/}; - my $conditions = {}; - if($tt_id){ - $conditions = { id => $tt_id }; - }else{ - $conditions = { - reseller_id => $provider_id, - type => $tt_type, - is_active => 1, - }; - } - return $conditions; -} -sub getInvoiceTemplate{ - my $self = shift; - my (%params) = @_; - my ($provider_id,$tt_sourcestate,$tt_type,$tt_id) = @params{qw/provider_id tt_sourcestate tt_type tt_id/}; - - my $result = ''; - - #it is hard to express my disappointment by DBIx implementation. Where pure SQL is easy to automate, flexible and powerful, with DBIx you can't even get DB as aliases, only additional accessors, which aren't an option. What a poor "wonabe hibernate" idea and implementation. - my $tt_record = $self->schema->resultset('invoice_templates')->search( - { id => $tt_id }, { - } - )->first; - #here may be base64 decoding - - #here we will rely on form checking and defaults - #if('saved' eq $tt_sourcestate){ - if( $tt_record ){ - $tt_sourcestate and $result = $tt_record->get_column( 'base64_'.$tt_sourcestate ); - $tt_id = $tt_record->get_column( 'id' ); - } - if( $result && exists $params{result} ){ - ${$params{result}} = $result; - } - return ( $tt_id, \$result, $tt_record );#tt_record - sgorila hata, gori i saray -} - -sub storeInvoiceTemplateContent{ - my $self = shift; - my (%params) = @_; - my ($provider_id, $tt_sourcestate, $tt_type,$tt_string,$tt_id,$is_active,$name) = @params{qw/provider_id tt_sourcestate tt_type tt_string_sanitized tt_id is_active name/}; - - #my $tt_record = $self->resultset('invoice_templates')->search({ - $self->schema->txn_do(sub { -#reseller_id and is_active aren't unique key, because is_active can kepp some 0 values for one reseller, we shouldn't keep active and inactive in one table -# $self->schema->resultset('invoice_templates')->update_or_create({ -# reseller_id => $provider_id, -# type => $tt_type, -# is_active => 1, -# 'base64_'.$tt_sourcestate => $$tt_string, -# }); - - - my $tt_record_created; - my $tt_record_updated; - if( !$tt_id ){ - $tt_record_created = $self->schema->resultset('invoice_templates')->create({ - reseller_id => $provider_id, - type => $tt_type, -# is_active => $is_active, -# name => $name, - 'base64_'.$tt_sourcestate => $$tt_string, - }); - if($tt_record_created){ - $tt_id = $tt_record_created->id(); - } - }else{ - my $conditions = $self->getDefaultConditions(\%params); - $tt_record_updated = $self->schema->resultset('invoice_templates')->search($conditions); - $tt_record_updated->update({ -# is_active => $is_active, -# name => $name, - 'base64_'.$tt_sourcestate => $$tt_string, - }); - } -# if($is_active && $tt_id){ -# $self->deactivateOtherTemplates($provider_id,$tt_id); -# } - }); - return { tt_id => $tt_id }; -} -sub storeInvoiceTemplateInfo{ - my $self = shift; - my (%params) = @_; - my ($provider_id,$tt_id,$is_active,$name) = @params{qw/provider_id tt_id is_active name/}; - - $self->schema->txn_do(sub { - my $tt_record_created; - my $tt_record_updated; - if( !$tt_id ){ - $tt_record_created = $self->schema->resultset('invoice_templates')->create({ - reseller_id => $provider_id, - is_active => $is_active, - name => $name, - }); - if($tt_record_created){ - $tt_id = $tt_record_created->id(); - } - }else{ - $tt_record_updated = $self->schema->resultset('invoice_templates')->search({ id => $tt_id }); - $tt_record_updated->update({ - is_active => $is_active, - name => $name, - }); - } - if($is_active && $tt_id){ - $self->deactivateOtherTemplates($provider_id,$tt_id); - } - }); - return { tt_id => $tt_id }; -} -sub getInvoiceTemplateList{ - my $self = shift; - my (%params) = @_; - my ($provider_id,$tt_sourcestate,$tt_type, $tt_string, $tt_id) = @params{qw/provider_id tt_sourcestate tt_type tt_string_sanitized tt_id/}; - - return $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - }); -} -sub deleteInvoiceTemplate{ - my $self = shift; - my (%params) = @_; - my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; - return $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - id => $tt_id, - })->delete_all; -} -sub activateInvoiceTemplate{ - my $self = shift; - my (%params) = @_; - my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; - $self->schema->txn_do(sub { - $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - id => $tt_id, - })->update({ - is_active => 1, - }); - $self->deactivateOtherTemplates($provider_id,$tt_id); - }); -} -sub deactivateInvoiceTemplate{ - my $self = shift; - my (%params) = @_; - my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; - $self->schema->txn_do(sub { - $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - id => $tt_id, - })->update({ - is_active => 0, - }); - }); -} -sub deactivateOtherTemplates{ - my $self = shift; - my ($provider_id,$tt_id) = @_; - $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - id => {'!=' => $tt_id }, - is_active => 1, - })->update_all({ - is_active => 0, - }); -} -sub checkInvoiceTemplateProvider{ - my $self = shift; - my (%params) = @_; - my ($provider_id,$tt_id) = @params{qw/provider_id tt_id/}; - my $tt_record = $self->schema->resultset('invoice_templates')->search({ - reseller_id => $provider_id, - id => $tt_id, - }); - if($tt_record->get_column('id')){ - return 1; - } - return 0; -} - -sub getContractInfo{ - my $self = shift; - my (%params) = @_; - my ($contract_id) = @params{qw/contract_id/}; - return $self->schema->resultset('contracts')->search({ - id => $contract_id, - })->first; -} -sub getContactInfo{ - my $self = shift; - my (%params) = @_; - my ($contact_id) = @params{qw/contact_id/}; - return $self->schema->resultset('contacts')->search({ - id => $contact_id, - })->first; -} -sub getBillingProfile{ - my $self = shift; - my ($params) = @_; - my ($client_contract_id, $stime, $etime) = @$params{qw/client_contract_id stime etime/}; - #select distinct billing_profiles.* - #from billing_mappings - #inner join billing_profiles on billing_mappings.billing_profile_id=billing_profiles.id - #inner join contracts on contracts.id=billing_mappings.contract_id - #inner join products on billing_mappings.product_id=products.id and products.class in("sipaccount","pbxaccount") - #where - # contracts.status != "terminated" - # and contracts.contact_id=? - # and (billing_mappings.start_date <= ? OR billing_mappings.start_date IS NULL) - # and (billing_mappings.end_date >= ? OR billing_mappings.end_date IS NULL) - return $self->schema->resultset('billing_profiles')->search({ - 'contract.id' => $client_contract_id, - #'contract.status' => { '!=' => 'terminated' }, - 'product.class' => { '-in' => [qw/sipaccount pbxaccount/] }, - 'billing_mappings.start_date' => [ - { '<=' => $etime->epoch }, - { -is => undef }, - ], - 'billing_mappings.end_date' => [ - { '>=' => $stime->epoch }, - { -is => undef }, - ], - },{ - 'join' => [ { 'billing_mappings' => [ 'product', 'contract' ] } ], - })->first; -} -sub getContractBalance{ - my $self = shift; - my ($params) = @_; - my ($client_contract_id, $stime, $etime) = @$params{qw/client_contract_id stime etime/}; - #select distinct billing_profiles.* - #from billing_mappings - #inner join billing_profiles on billing_mappings.billing_profile_id=billing_profiles.id - #inner join contracts on contracts.id=billing_mappings.contract_id - #inner join products on billing_mappings.product_id=products.id and products.class in("sipaccount","pbxaccount") - #where - # contracts.status != "terminated" - # and contracts.contact_id=? - # and (billing_mappings.start_date <= ? OR billing_mappings.start_date IS NULL) - # and (billing_mappings.end_date >= ? OR billing_mappings.end_date IS NULL) - return $self->schema->resultset('contract_balances')->search({ - 'contract_id' => $client_contract_id, - #'contract.status' => { '!=' => 'terminated' }, - '-and' => [ - 'start' => [ - { '=' => $stime->datetime }, - { '-is' => undef }, - ], - 'end' => [ - { '=' => $etime->datetime }, - { '-is' => undef }, - ], - ], - },undef)->first; -} - -sub getProviderInvoiceList{ - my $self = shift; - my (%params) = @_; - my ($provider_reseller_id,$stime,$etime) = @params{qw/provider_id stime etime/}; - $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); - $etime ||= $stime->clone->add( months => 1); - $self->schema->resultset('invoices')->search({ - '-and' => - [ - 'contact.reseller_id' => $provider_reseller_id, #$client_contract_id - contract of the client - ], - },{ - 'select' => [ 'contract_balances.invoice_id','contract_balances.start','contract_balances.end','contract_balances.cash_balance','contract_balances.free_time_balance','reseller.id','reseller.name','contact.id',], - 'as' => [ 'invoice_id','contract_balance_start','contract_balance_end','cash_balance','free_time_balance', 'reseller_id', 'reseller_name','client_contact_id' ], - 'prefetch' => [ {'contract_balances' => { 'contract' => { 'contact' => 'reseller' } } } ], - #'collapse' => 1, - 'order_by' => [ { '-desc' => 'contract_balances.start' },{ '-asc' => 'contract_balances.end' }, ], - 'alias' => 'me', - }); -} -sub getProviderInvoiceListAjax{ - my $self = shift; - my (%params) = @_; - my ($provider_reseller_id,$client_contact_id,$stime,$etime) = @params{qw/provider_id client_contact_id stime etime/}; - $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); - $etime ||= $stime->clone->add( months => 1); - $self->schema->resultset('invoices')->search({ - '-and' => - [ - 'contact.reseller_id' => $provider_reseller_id, #$client_contract_id - contract of the client - $client_contact_id ? ('contact.id' => $client_contact_id) : (), #$client_contract_id - contract of the client - ], - },{ - #'select' => [ 'contract_balances.invoice_id','contract_balances.start','contract_balances.end','contract_balances.cash_balance','contract_balances.free_time_balance','reseller.id','reseller.name','contact.id',], - #'as' => [ 'invoice_id','contract_balance_start','contract_balance_end','cash_balance','free_time_balance', 'reseller_id', 'reseller_name','client_contact_id' ], - #'prefetch' => [ {'contract_balances' => { 'contract' => { 'contact' => 'reseller' } } } ], - ##'collapse' => 1, - #'order_by' => [ { '-desc' => 'contract_balances.start' },{ '-asc' => 'contract_balances.end' }, ], - #'alias' => 'me', - }); -} -sub createInvoice{ - my $self = shift; - my (%params) = @_; - my ($contract_balance,$data,$stime,$etime) = @params{qw/contract_balance data stime etime/}; - - # my($contract_id, $stime, $etime) = @_; - ##my $invoice_serial = $dbh->selectrow_array('select max(invoices.serial) from invoices inner join contract_balances on invoices.id=contract_balances.invoice_id where contract_balances.contract_id=?',undef,$contract_id ); - #my $invoice_serial = $dbh->selectrow_array('select max(invoices.serial) from invoices'); - #$invoice_serial += 1; - #$dbh->do('insert into invoices(year,month,serial)values(?,?,?)', undef, $stime->year, $stime->month,$invoice_serial ); - #my $invoice_id = $dbh->last_insert_id(undef,'billing','invoices','id'); - #$dbh->do('update contract_balances set invoice_id = ? where contract_id=? and start=? and end=?', undef, $invoice_id,$contract_id, $stime->datetime, $etime->datetime ); - #return $dbh->selectrow_hashref('select * from invoices where id=?',undef, $invoice_id); - my $invoice; - - if( $contract_balance->get_column('invoice_id')){ - $invoice = $self->schema->resultset('invoices')->search({ - id => $contract_balance->get_column('invoice_id'), - })->first; - }else{ - - my $invoice_serial = $self->schema->resultset('invoices')->search(undef, { - 'select' => { 'max' => 'me.serial', '-as' => 'serial_max'}, - })->first->get_column('serial_max'); - $invoice_serial +=1; - - my $invoice_record = $self->schema->resultset('invoices')->create({ - year => $stime->year, - month => $stime->month, - serial => $invoice_serial, - data => $data, - }); - if($invoice_record){ - $self->schema->resultset('contract_balances')->search({ - id => $contract_balance->get_column('id'), - })->update({ - invoice_id => $invoice_record->id() - }); - $invoice = $self->schema->resultset('invoices')->search({ - id => $invoice_record->id(), - })->first; - } - } - return $invoice; -} -sub storeInvoiceData{ - my $self = shift; - my (%params) = @_; - my ($invoice,$data_ref) = @params{qw/invoice data/}; - $self->schema->resultset('invoices')->search({ - id => $invoice->get_column('id'), - })->update({ - data => $$data_ref - }); -} -sub getInvoice{ - my $self = shift; - my (%params) = @_; - my ($invoice_id) = @params{qw/invoice_id/}; - return $self->schema->resultset('invoices')->search({ - 'me.id' => $invoice_id, - }, undef )->first; -} -sub getInvoiceContact{ - my $self = shift; - my (%params) = @_; - my ($invoice_id) = @params{qw/invoice_id/}; - return $self->schema->resultset('invoices')->search({ - 'me.id' => $invoice_id, - }, { - '+select' => ['contact.email'], - '+as' => ['email'], - 'join' => [ {'contract_balances' => { 'contract' => 'contact' } } ], - } )->first; -} -sub deleteInvoice{ - my $self = shift; - my (%params) = @_; - my ($invoice_id) = @params{qw/invoice_id/}; - - $self->schema->resultset('invoices')->search({ - 'id' => $invoice_id, - }, undef )->delete; - - $self->schema->resultset('contract_balances')->search({ - 'invoice_id' => $invoice_id, - }, undef )->update({ - 'invoice_id' => undef, - }); -} -sub getInvoiceProviderClients{ - my $self = shift; - my (%params) = @_; - my ($provider_contact_id,$stime,$etime) = @params{qw/provider_id stime etime/}; - $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); - $etime ||= $stime->clone->add( months => 1); - $self->schema->resultset('contacts')->search_rs({ - '-and' => - [ - 'me.reseller_id' => $provider_contact_id, #$client_contract_id - contract of the client - ], - '-exists' => $self->schema->resultset('billing_mappings')->search({ - #here rely on join generated by datatables - 'contract.id' => \' = contracts.id', - 'product.class' => [ "sipaccount", "pbxaccount" ], - #'-and' => [ - # 'start_date' => [ -or => - # { '<=' => $etime->epoch }, - # { -is => undef }, - # ], - # 'end_date' => [ -or => - # { '>=' => $stime->epoch }, - # { -is => undef }, - # ], - # ] - },{ - alias => 'billing_mappings_top', - join => ['product','contract'], - })->as_query - }); -} -sub call_owner_condition{ - my $self = shift; - my ($params) = @_; - (my($c,$provider_id,$client_contact_id,$client_contract_id)) = @$params{qw/c provider_id client_contact_id client_contract_id/}; - my %source_account_id_condition; - if($client_contract_id){ - %source_account_id_condition = ( 'source_account_id' => $client_contract_id ); - }elsif($client_contact_id){ - %source_account_id_condition = ( - 'source_account.contact_id' => $client_contact_id, - 'contact.reseller_id' => { '!=' => undef }, - ); - }elsif($provider_id){ - %source_account_id_condition = ( - 'contact.reseller_id' => $client_contact_id, - ); - } - return \%source_account_id_condition; -} -sub get_contract_calls_rs{ - my $self = shift; - my %params = @_; - (my($c,$provider_id,$client_contact_id,$client_contract_id,$stime,$etime)) = @params{qw/c provider_id client_contact_id client_contract_id stime etime/}; - - $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); - $etime ||= $stime->clone->add( months => 1 ); - - my %source_account_id_condition = %{$self->call_owner_condition(\%params)}; - - my $calls_rs = $self->schema->resultset('cdr')->search( { -# source_user_id => { 'in' => [ map {$_->uuid} @{$contract->{subscriber}} ] }, - 'call_status' => 'ok', - 'source_user_id' => { '!=' => '0' }, - 'contact.reseller_id' => { '!=' => undef }, - start_time => - [ -and => - { '>=' => $stime->epoch}, - { '<=' => $etime->epoch}, - ], - %source_account_id_condition - },{ - '+select' => [ - 'source_customer_billing_zones_history.zone', - 'source_customer_billing_zones_history.detail', - 'destination_user_in', - ], - '+as' => [qw/zone zone_detail destination/], - 'join' => [ - { - 'source_account' => 'contact', - }, - 'source_customer_billing_zones_history', - ], - 'order_by' => { '-desc' => 'start_time'}, - } ); - - return $calls_rs; -} -sub get_contract_zonesfees_rs { - my $self = shift; - my %params = @_; - (my($c,$provider_id,$client_contact_id,$client_contract_id,$stime,$etime)) = @params{qw/c provider_id client_contact_id client_contract_id stime etime/}; - - $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); - $etime ||= $stime->clone->add( months => 1 ); - - my %source_account_id_condition = %{$self->call_owner_condition(\%params)}; - # SELECT 'out' as direction, SUM(c.source_customer_cost) AS cost, b.zone, - # COUNT(*) AS number, SUM(c.duration) AS duration - # FROM accounting.cdr c - # LEFT JOIN billing.voip_subscribers v ON c.source_user_id = v.uuid - # LEFT JOIN billing.billing_zones_history b ON b.id = c.source_customer_billing_zone_id - # WHERE v.contract_id = ? - # AND c.call_status = 'ok' - # $start_time $end_time - # GROUP BY b.zone - - my $zonecalls_rs = $self->schema->resultset('cdr')->search( { -# source_user_id => { 'in' => [ map {$_->uuid} @{$contract->{subscriber}} ] }, - 'call_status' => 'ok', - 'source_user_id' => { '!=' => '0' }, - start_time => - [ -and => - { '>=' => $stime->epoch}, - { '<=' => $etime->epoch}, - ], - %source_account_id_condition - },{ - '+select' => [ - { sum => 'me.source_customer_cost', -as => 'cost', }, - { sum => 'me.source_customer_free_time', -as => 'free_time', } , - { sum => 'me.duration', -as => 'duration', } , - { count => '*', -as => 'number', } , - 'source_customer_billing_zones_history.zone', - 'source_customer_billing_zones_history.detail', - ], - '+as' => [qw/cost free_time duration number zone zone_detail/], - #alias => - join => 'source_customer_billing_zones_history', - group_by => 'source_customer_billing_zones_history.zone', - order_by => 'source_customer_billing_zones_history.zone', - } ); - - return $zonecalls_rs; -} - -sub checkResellerClientContract{ - my($self,$in) = @_; - my $res = 0; - - if($in->{client_contract_id} && $in->{provider_id}){ - if(my $contract = $self->schema->resultset('contracts')->search({ - 'contact.reseller_id' => $in->{provider_id}, - 'me.id' => $in->{client_contract_id}, - },{ - 'join' => 'contact', - })->first){ - $res = $contract->get_column('id'); - } - } - return $res; -} - -sub checkResellerClientContact{ - my($self,$in) = @_; - my $res = 0; - - if($in->{client_contact_id} && $in->{provider_id}){ - if(my $contact = $self->schema->resultset('contacts')->search({ - 'reseller_id' => $in->{provider_id}, - 'me.id' => $in->{client_contact_id}, - })->first){ - $res = $contact->get_column('id'); - } - } - return $res; -} - -sub checkResellerInvoice{ - my($self,$in) = @_; - my $res = 0; - - if($in->{invoice_id} && $in->{provider_id}){ - if(my $invoice = $self->schema->resultset('invoices')->search({ - 'contact.reseller_id' => $in->{provider_id}, - 'me.id' => $in->{invoice_id}, - },{ - 'join' => { 'contract_balances' => { 'contract' => 'contact' }}, - })->first){ - $res = $invoice->get_column('id'); - } - } - return $res; -} - -sub checkResellerInvoiceTemplate{ - my($self,$in) = @_; - my $res = 0; - #no warnings 'uninitialized'; - #$in->{c}->log->debug("checkResellerInvoiceTemplate: tt_id=".$in->{tt_id}.";provider_id=".$in->{provider_id}.";"); - - if($in->{tt_id} && $in->{provider_id}){ - #$in->{c}->log->debug("checkResellerInvoiceTemplate: tt_id=".$in->{tt_id}.";provider_id=".$in->{provider_id}.";"); - if(my $tt = $self->schema->resultset('invoice_templates')->search({ - 'reseller_id' => $in->{provider_id}, - 'me.id' => $in->{tt_id}, - })->first){ - $res = $tt->get_column('id'); - } - } - #$in->{c}->log->debug("checkResellerInvoiceTemplate: res=".$res.";"); - return $res; -} - -1; \ No newline at end of file diff --git a/lib/NGCP/Panel/Role/API.pm b/lib/NGCP/Panel/Role/API.pm index 7b48e9c95f..3c406a9e7d 100644 --- a/lib/NGCP/Panel/Role/API.pm +++ b/lib/NGCP/Panel/Role/API.pm @@ -470,6 +470,7 @@ around 'item_rs' => sub { my $c = $orig_params[0]; foreach my $param(keys $c->req->query_params) { my @p = grep { $_->{param} eq $param } @{ $self->query_params }; + next unless($p[0]->{query}); # skip "dummy" query parameters my $q = $c->req->query_params->{$param}; # TODO: arrayref? $q =~ s/\*/\%/g; if(@p) { diff --git a/lib/NGCP/Panel/Role/API/CustomerZoneCosts.pm b/lib/NGCP/Panel/Role/API/CustomerZoneCosts.pm index 55ef7aa0ac..7654956ec3 100644 --- a/lib/NGCP/Panel/Role/API/CustomerZoneCosts.pm +++ b/lib/NGCP/Panel/Role/API/CustomerZoneCosts.pm @@ -81,6 +81,9 @@ sub resource_from_item { stime => $stime, etime => $etime, subscriber_uuid => $subscriber_uuid, + in => 1, + out => 1, + group_by_detail => 0, ); $resource{customer_id} = int($item->id); @@ -132,16 +135,7 @@ sub query_param_string { sub item_rs { my ($self, $c) = @_; - my $item_rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( - c => $c, - ); - if($c->user->roles eq "admin") { - } elsif($c->user->roles eq "reseller") { - $item_rs = $item_rs->search({ - 'contact.reseller_id' => $c->user->reseller_id, - }); - } - + my $item_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); return $item_rs; } diff --git a/lib/NGCP/Panel/Role/API/Customers.pm b/lib/NGCP/Panel/Role/API/Customers.pm index 42e94acfc9..0d9585dd60 100644 --- a/lib/NGCP/Panel/Role/API/Customers.pm +++ b/lib/NGCP/Panel/Role/API/Customers.pm @@ -19,31 +19,8 @@ use NGCP::Panel::Form::Contract::ProductOptional; sub item_rs { my ($self, $c) = @_; - my $item_rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( - c => $c, - ); - $item_rs = $item_rs->search({ - 'contact.reseller_id' => { '-not' => undef }, - },{ - join => 'contact', - }); - $item_rs = $item_rs->search({ - '-or' => [ - 'product.class' => 'sipaccount', - 'product.class' => 'pbxaccount', - ], - },{ - join => {'billing_mappings' => 'product' }, - '+select' => 'billing_mappings.id', - '+as' => 'bmid', - }); - if($c->user->roles eq "admin") { - } elsif($c->user->roles eq "reseller") { - $item_rs = $item_rs->search({ - 'contact.reseller_id' => $c->user->reseller_id, - }); - } - + # returns a contracts rs filtered based on role + my $item_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); return $item_rs; } diff --git a/lib/NGCP/Panel/Role/API/SoundSets.pm b/lib/NGCP/Panel/Role/API/SoundSets.pm index 73bc3fd4ec..b1bf48aba1 100644 --- a/lib/NGCP/Panel/Role/API/SoundSets.pm +++ b/lib/NGCP/Panel/Role/API/SoundSets.pm @@ -133,8 +133,6 @@ sub update_item { $resource->{contract_default} //= 0; - use Data::Printer; p $item; - use Data::Printer; p $resource; $item->update($resource); if($item->contract_id && $item->contract_default && !$old_resource->{contract_default}) { diff --git a/lib/NGCP/Panel/Utils/Contract.pm b/lib/NGCP/Panel/Utils/Contract.pm index 1444b599a9..b04cff6b99 100644 --- a/lib/NGCP/Panel/Utils/Contract.pm +++ b/lib/NGCP/Panel/Utils/Contract.pm @@ -6,12 +6,44 @@ use Sipwise::Base; use DBIx::Class::Exception; use NGCP::Panel::Utils::DateTime; +sub get_contract_balance { + my (%params) = @_; + my $c = $params{c}; + my $contract = $params{contract}; + my $profile = $params{profile}; + my $stime = $params{stime}; + my $etime = $params{etime}; + my $schema = $params{schema} // $c->model('DB'); + + my $balance = $contract->contract_balances + ->find({ + start => { '>=' => $stime }, + end => { '<=' => $etime }, + }); + use Data::Printer; print ">>>>>>>>> get_balance\n"; p $balance; + unless($balance) { + use Data::Printer; print ">>>>>>>>> no balance\n"; p $balance; + $balance = create_contract_balance( + c => $c, + profile => $profile, + contract => $contract, + stime => $stime, + etime => $etime, + schema => $schema, + ); + use Data::Printer; print ">>>>>>>>> created balance\n"; p $balance; + } + return $balance; +} + + sub create_contract_balance { my %params = @_; my $c = $params{c}; my $contract = $params{contract}; my $profile = $params{profile}; + my $schema = $params{schema} // $c->model('DB'); # first, calculate start and end time of current billing profile @@ -28,10 +60,10 @@ sub create_contract_balance { etime => $etime, ); + my $balance; try { - my $schema = $c->model('DB'); $schema->txn_do(sub { - $schema->resultset('contract_balances')->create({ + $balance = $schema->resultset('contract_balances')->create({ contract_id => $contract->id, cash_balance => $cash_balance, cash_balance_interval => $cash_balance_interval, @@ -40,6 +72,7 @@ sub create_contract_balance { start => $stime, end => $etime, }); + use Data::Printer; print ">>>>>>>>> really created balance\n"; p $balance; }); } catch($e) { if ($e =~ /Duplicate entry/) { @@ -49,11 +82,13 @@ sub create_contract_balance { $e->rethrow; } }; - return; + use Data::Printer; print ">>>>>>>>> returning balance\n"; p $balance; + return $balance; } -sub get_contract_balance_values{ + +sub get_contract_balance_values { my %params = @_; - my($free_time,$free_cash,$stime,$etime) = @params{qw/interval_free_time interval_free_cash stime etime/}; + my($free_time, $free_cash, $stime, $etime) = @params{qw/interval_free_time interval_free_cash stime etime/}; my ($cash_balance, $cash_balance_interval, $free_time_balance, $free_time_balance_interval) = (0,0,0,0); if($free_time or $free_cash) { @@ -70,8 +105,9 @@ sub get_contract_balance_values{ } $etime->subtract(seconds => 1); } - return ($cash_balance,$cash_balance_interval,$free_time_balance,$free_time_balance_interval); + return ($cash_balance, $cash_balance_interval, $free_time_balance, $free_time_balance_interval); } + sub recursively_lock_contract { my %params = @_; @@ -169,6 +205,7 @@ sub get_contract_rs { my %params = @_; my $schema = $params{schema}; my $mapping_rs = $schema->resultset('billing_mappings'); + my $dtf = $schema->storage->datetime_parser; my $rs = $schema->resultset('contracts') ->search({ 'me.status' => { '!=' => 'terminated' }, @@ -176,11 +213,11 @@ sub get_contract_rs { '=' => $mapping_rs->search({ contract_id => { -ident => 'me.id' }, start_date => [ -or => - { '<=' => NGCP::Panel::Utils::DateTime::current_local }, + { '<=' => $dtf->format_datetime(NGCP::Panel::Utils::DateTime::current_local) }, { -is => undef }, ], end_date => [ -or => - { '>=' => NGCP::Panel::Utils::DateTime::current_local }, + { '>=' => $dtf->format_datetime(NGCP::Panel::Utils::DateTime::current_local) }, { -is => undef }, ], },{ @@ -207,36 +244,30 @@ sub get_contract_rs { return $rs; } -sub get_contracts_rs_sippbx{ +sub get_customer_rs { my %params = @_; - #pass here $c isn't very good idea, it doesn't allow "simple" call with really relevant information my $c = $params{c}; - # we only return customers, that is, contracts with contacts with a - # reseller + my $customers = get_contract_rs( schema => $c->model('DB'), ); - #really here we don't need role - we can pass only reseller_id, reseller_id should be tacken according to role in other method - #for all $customers = $customers->search({ 'contact.reseller_id' => { '-not' => undef }, },{ join => 'contact', }); - my $reseller_condition; - if($c->user->roles eq "reseller") { - $reseller_condition = $c->user->reseller_id; + if($c->user->roles eq "admin") { + } elsif($c->user->roles eq "reseller") { + $customers = $customers->search({ + 'contact.reseller_id' => $c->user->reseller_id, + }); } elsif($c->user->roles eq "subscriberadmin") { - $reseller_condition = $c->user->contract->contact->reseller_id; - } elsif($c->user->roles eq "admin") { - } - if ($reseller_condition){ $customers = $customers->search({ - 'contact.reseller_id' => $reseller_condition , + 'contact.reseller_id' => $c->user->contract->contact->reseller_id, }); - } + } $customers = $customers->search({ '-or' => [ @@ -252,57 +283,6 @@ sub get_contracts_rs_sippbx{ return $customers; } -sub get_contract_calls_rs{ - my %params = @_; - (my ($c,$contract_id,$stime,$etime)) = @params{qw/c contract_id stime etime/}; - -# SELECT 'out' as direction, SUM(c.source_customer_cost) AS cost, b.zone, -# COUNT(*) AS number, SUM(c.duration) AS duration -# FROM accounting.cdr c -# LEFT JOIN billing.voip_subscribers v ON c.source_user_id = v.uuid -# LEFT JOIN billing.billing_zones_history b ON b.id = c.source_customer_billing_zone_id -# WHERE v.contract_id = ? -# AND c.call_status = 'ok' -# $start_time $end_time -# GROUP BY b.zone - - my $zonecalls_rs = $c->model('DB')->resultset('cdr')->search( { -# source_user_id => { 'in' => [ map {$_->uuid} @{$contract->{subscriber}} ] }, - call_status => 'ok', - source_user_id => { '!=' => '0' }, - source_account_id => $contract_id, -# start_time => -# [ -and => -# { '>=' => $stime->epoch}, -# { '<=' => $etime->epoch}, -# ], - -# { -# '>=' => ["unix_timestamp(?)", $stime], -# '<=' => ["unix_timestamp(?)",$etime] }, -# { '>=' => \[ "unix_timestamp(?)", $stime ] }, -# { '<=' => \[ "unix_timestamp(?)", $etime ] }, -#requires fix: 757 #$self->_assert_bindval_matches_bindtype(@sub_bind); -#in SQL::Abstract, - - },{ - 'select' => [ - { sum => 'me.source_customer_cost', -as => 'cost' }, - { sum => 'me.source_customer_free_time', -as => 'free_time' }, - { sum => 'me.duration', -as => 'duration' }, - { count => '*', -as => 'number' }, - 'source_customer_billing_zones_history.zone', - 'source_customer_billing_zones_history.detail', - ], - '+as' => [qw/cost free_time duration number zone zone_detail/], - #alias => - join => 'source_customer_billing_zones_history', - group_by => 'source_customer_billing_zones_history.zone', - } ); - - return $zonecalls_rs; -} - sub get_contract_zonesfees_rs { my %params = @_; my $c = $params{c}; @@ -310,20 +290,7 @@ sub get_contract_zonesfees_rs { my $etime = $params{etime}; my $contract_id = $params{contract_id}; my $subscriber_uuid = $params{subscriber_uuid}; - - # should not be neccessary, done before -# $stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' ); -# $etime ||= $stime->clone->add( months => 1 ); - -# SELECT 'out' as direction, SUM(c.source_customer_cost) AS cost, b.zone, -# COUNT(*) AS number, SUM(c.duration) AS duration -# FROM accounting.cdr c -# LEFT JOIN billing.voip_subscribers v ON c.source_user_id = v.uuid -# LEFT JOIN billing.billing_zones_history b ON b.id = c.source_customer_billing_zone_id -# WHERE v.contract_id = ? -# AND c.call_status = 'ok' -# $start_time $end_time -# GROUP BY b.zone + my $group_detail = $params{group_by_detail}; my $zonecalls_rs_out = $c->model('DB')->resultset('cdr')->search( { 'call_status' => 'ok', @@ -343,10 +310,17 @@ sub get_contract_zonesfees_rs { { sum => 'me.duration', -as => 'duration' }, { count => '*', -as => 'number' }, 'source_customer_billing_zones_history.zone', + $group_detail ? 'source_customer_billing_zones_history.detail' : (), + ], + 'as' => [ + qw/customercost carriercost resellercost free_time duration number zone/, + $group_detail ? 'zone_detail' : (), ], - 'as' => [qw/customercost carriercost resellercost free_time duration number zone/], join => 'source_customer_billing_zones_history', - group_by => 'source_customer_billing_zones_history.zone', + group_by => [ + 'source_customer_billing_zones_history.zone', + $group_detail ? 'source_customer_billing_zones_history.detail' : (), + ], order_by => 'source_customer_billing_zones_history.zone', } ); @@ -368,10 +342,17 @@ sub get_contract_zonesfees_rs { { sum => 'me.duration', -as => 'duration' }, { count => '*', -as => 'number' }, 'destination_customer_billing_zones_history.zone', + $group_detail ? 'destination_customer_billing_zones_history.detail' : (), + ], + 'as' => [ + qw/customercost carriercost resellercost free_time duration number zone/, + $group_detail ? 'zone_detail' : (), ], - 'as' => [qw/customercost carriercost resellercost free_time duration number zone/], join => 'destination_customer_billing_zones_history', - group_by => 'destination_customer_billing_zones_history.zone', + group_by => [ + 'destination_customer_billing_zones_history.zone', + $group_detail ? 'destination_customer_billing_zones_history.detail' : (), + ], order_by => 'destination_customer_billing_zones_history.zone', } ); @@ -381,24 +362,38 @@ sub get_contract_zonesfees_rs { sub get_contract_zonesfees { my %params = @_; - my ($zonecalls_rs_in, $zonecalls_rs_out) = get_contract_zonesfees_rs(%params); + my $c = $params{c}; + my $in = delete $params{in}; + my $out = delete $params{out}; - my %zones; - for my $zone ($zonecalls_rs_in->all, $zonecalls_rs_out->all) { - my $zname = $zone->get_column('zone') // ''; + my ($zonecalls_rs_in, $zonecalls_rs_out) = get_contract_zonesfees_rs(%params); + my @zones = ( + $in ? $zonecalls_rs_in->all : (), + $out ? $zonecalls_rs_out->all : (), + ); + + my %allzones; + for my $zone (@zones) { + my $zname = $params{group_by_detail} ? + ($zone->get_column('zone')//'') . ' ' . ($zone->get_column('zone_detail')//'') : + ($zone->get_column('zone')//''); my %cols = $zone->get_inflated_columns; - $zones{$zname}{customercost} += $cols{customercost} || 0; - $zones{$zname}{carriercost} += $cols{carriercost} || 0; - $zones{$zname}{resellercost} += $cols{resellercost} || 0; - $zones{$zname}{duration} += $cols{duration} || 0; - $zones{$zname}{free_time} += $cols{free_time} || 0; - $zones{$zname}{number} += $cols{number} || 0; - - delete $zones{$zname}{zone}; + if($c->user->roles eq "admin") { + $allzones{$zname}{carriercost} += $cols{carriercost} || 0; + } + if($c->user->roles eq "admin" || $c->user->roles eq "reseller") { + $allzones{$zname}{resellercost} += $cols{resellercost} || 0; + } + $allzones{$zname}{customercost} += $cols{customercost} || 0; + $allzones{$zname}{duration} += $cols{duration} || 0; + $allzones{$zname}{free_time} += $cols{free_time} || 0; + $allzones{$zname}{number} += $cols{number} || 0; + + delete $allzones{$zname}{zone}; } - return \%zones; + return \%allzones; } 1; diff --git a/lib/NGCP/Panel/Utils/DateTime.pm b/lib/NGCP/Panel/Utils/DateTime.pm index c04946828e..d785810ef7 100644 --- a/lib/NGCP/Panel/Utils/DateTime.pm +++ b/lib/NGCP/Panel/Utils/DateTime.pm @@ -19,6 +19,10 @@ sub epoch_local { } sub from_string { my $s = shift; + + # if date is passed like xxxx-xx (as from monthpicker field), add a day + $s = $s . "-01" if($s =~ /^\d{4}\-\d{2}$/); + # just for convenience, if date is passed like xxxx-xx-xx xx:xx:xx, # convert it to xxxx-xx-xxTxx:xx:xx $s =~ s/^(\d{4}\-\d{2}\-\d{2})\s+(\d.+)$/$1T$2/; diff --git a/share/templates/helpers/monthpicker.tt b/share/templates/helpers/monthpicker.tt new file mode 100644 index 0000000000..eb127cdc75 --- /dev/null +++ b/share/templates/helpers/monthpicker.tt @@ -0,0 +1,30 @@ +