From 7ab5ab3c8542d24798009a09d7b9854576bd6737 Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Mon, 5 May 2014 21:26:05 +0300 Subject: [PATCH] MT#5879 Filter invoices by client. Generate form. Left to do: fix filtering by date, if field isn't a eppoch generation mark selected client in clients list. add big button to clear client selection --- lib/NGCP/Panel/Controller/Invoice.pm | 97 +++++++++++++++++++++ lib/NGCP/Panel/Form/Invoice/Generate.pm | 71 +++++++++++++++ lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm | 46 +++++----- lib/NGCP/Panel/Utils/Datatables.pm | 13 +-- share/templates/helpers/datatables.tt | 10 +++ share/templates/invoice/invoice_generate.tt | 2 + share/templates/invoice/list.tt | 69 +++++++++++---- 7 files changed, 263 insertions(+), 45 deletions(-) create mode 100644 lib/NGCP/Panel/Form/Invoice/Generate.pm create mode 100644 share/templates/invoice/invoice_generate.tt diff --git a/lib/NGCP/Panel/Controller/Invoice.pm b/lib/NGCP/Panel/Controller/Invoice.pm index 296b93a811..7ad1773ae5 100644 --- a/lib/NGCP/Panel/Controller/Invoice.pm +++ b/lib/NGCP/Panel/Controller/Invoice.pm @@ -174,8 +174,10 @@ sub invoice_list_data :Chained('invoice') :PathPart('list') :Args(0) { my $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); $c->log->debug('invoice_list_data'); my $provider_id = $c->stash->{provider}->id; + my $client_contact_id = $c->request->parameters->{client_contact_id}; my $invoice_list_ajax = $backend->getProviderInvoiceListAjax( provider_id => $provider_id, + $client_contact_id ? ( client_contact_id => $client_contact_id):(), ); $c->stash( invoice_list_data_ajax => $invoice_list_ajax, @@ -183,6 +185,19 @@ sub invoice_list_data :Chained('invoice') :PathPart('list') :Args(0) { #$c->detach( $c->view() ); } +sub provider_client_list :Chained('invoice') :PathPart('clients/list') :Args(0) { + my ($self, $c) = @_; + my $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); + $c->log->debug('provider_client_list'); + my $provider_id = $c->stash->{provider}->id; + my $provider_client_list_ajax = $backend->getInvoiceProviderClients( + provider_id => $provider_id, + ); + $c->stash( + provider_client_list_ajax => $provider_client_list_ajax, + ); + #$c->detach( $c->view() ); +} sub invoice_data :Chained('invoice') :PathPart('data') :Args(1) { my ($self, $c) = @_; @@ -195,6 +210,88 @@ sub invoice_data :Chained('invoice') :PathPart('data') :Args(1) { return; } +sub invoice_generate :Chained('base') :PathPart('generate') :Args(0) { + my ($self, $c) = @_; + $c->log->debug($c->action); + my($validator,$backend,$in,$out); + $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); + + #from parameters + $in = $c->request->parameters; + $in->{provider_id} = $c->stash->{provider}->id; + #(undef,undef,@$in{qw/tt_id/}) = @_; + + if($in->{tt_id}){ + #always was sure that i'm calm and even friendly person, but I would kill with pleasure author of dbix. + my $db_object; + ($out->{tt_id},undef,$db_object) = $backend->getInvoiceTemplate( %$in ); + $out->{tt_data}->{tt_id} = $db_object->get_column('id'); + $out->{tt_data}->{provider_id} = $db_object->get_column('reseller_id'); + foreach(qw/name is_active/){$out->{tt_data}->{$_} = $db_object->get_column($_);} + } + if(!$out->{tt_data}){ + $out->{tt_data} = $in; + } + $validator = NGCP::Panel::Form::Invoice::Template->new( backend => $backend ); + $validator->remove_undef_in($in); + #need to think how to automate it - maybe through form showing param through args? what about args for uri_for_action? + #join('/',$c->controller,$c->action) + $validator->action( $c->uri_for_action('invoice/generate',[$in->{provider_id}]) ); + $validator->name( 'invoice_generate' );#from parameters + #my $posted = 0; + my $posted = exists $in->{submitid}; + $c->log->debug("posted=$posted;"); + $validator->process( + posted => $posted, + params => $in, + #item => $in, + item => $out->{tt_data}, + #item => $out->{tt_data}, + ); + my $in_validated = $validator->fif; + if($posted){ + if($validator->validated) { + try { + $backend->storeInvoiceTemplateInfo(%$in_validated); + $c->flash(messages => [{type => 'success', text => $c->loc( + $in->{tt_id} + ?'Invoice template updated' + :'Invoice template created' + ) }]); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc( + $in->{tt_id} + ?'Failed to update invoice template.' + :'Failed to create invoice template.' + ), + ); + } + $c->stash( messages => $c->flash->{messages} ); + $c->stash( template => 'helpers/ajax_messages.tt' ); + }else{ + #$c->stash( m => {create_flag => !$in->{tt_id}} ); + #$c->stash( form => $validator ); + ##$c->stash( template => 'helpers/ajax_form_modal.tt' ); + #$c->stash( template => 'invoice/template_info_form.tt' ); + $c->response->headers->header( 'X-Form-Status' => 'error' ); + } + } + if(!$validator->validated){ + #$c->stash( in => $in ); + #$c->stash( out => $out ); + $c->stash( m => {create_flag => !$in->{tt_id}} ); + $c->stash( form => $validator ); + #$c->stash( template => 'helpers/ajax_form_modal.tt' ); + $c->stash( template => 'invoice/template_info_form.tt' ); + } + $c->detach( $c->view("SVG") );#to the sake of nowrapper +} + + + sub template_base :Chained('base') :PathPart('template') :CaptureArgs(0) { my ($self, $c) = @_; my($validator,$backend,$in,$out); diff --git a/lib/NGCP/Panel/Form/Invoice/Generate.pm b/lib/NGCP/Panel/Form/Invoice/Generate.pm new file mode 100644 index 0000000000..b5cd809b86 --- /dev/null +++ b/lib/NGCP/Panel/Form/Invoice/Generate.pm @@ -0,0 +1,71 @@ +package NGCP::Panel::Form::Invoice::Generate; + +use HTML::FormHandler::Moose; +extends 'NGCP::Panel::Form::ValidatorBase'; +extends 'HTML::FormHandler::Field::Compound'; + +use Moose::Util::TypeConstraints; +use HTML::FormHandler::Widget::Block::Bootstrap; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +has '+use_fields_for_input_without_param' => ( default => 1 ); +sub build_render_list {[qw/fields actions/]} +sub build_form_element_class { [qw/form-horizontal/] } + +has_field 'submitid' => ( type => 'Hidden' ); + +#has_field 'contract.id' => ( +# type => '+NGCP::Panel::Field::DataTable', +# label => 'Client', +# do_label => 0, +# do_wrapper => 0, +# required => 1, +# template => 'helpers/datatables_field.tt', +# ajax_src => '/contact/ajax_noreseller', +# table_titles => ['#', 'First Name', 'Last Name', 'Email'], +# table_fields => ['id', 'firstname', 'lastname', 'email'], +#); +has_field 'start' => ( + type => '+NGCP::Panel::Field::DateTime', + element_attr => { + rel => ['tooltip'], + title => ['YYYY-MM-DD HH:mm:ss'] + }, + label => 'Start Date/Time', + required => 1, +); + +has_field 'end' => ( + type => '+NGCP::Panel::Field::DateTime', + element_attr => { + rel => ['tooltip'], + title => ['YYYY-MM-DD HH:mm:ss'] + }, + label => 'End Date/Time', + required => 1, +); + +1; + +=head1 NAME + +NGCP::Panel::Form::InvoiceTemplate + +=head1 DESCRIPTION + +Form to modify an invoice template. + +=head1 METHODS + +=head1 AUTHOR + +Irina Peshinskaya + +=head1 LICENSE + +This library is free software. You can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm index be14eee15e..979b1e09a1 100644 --- a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm +++ b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm @@ -219,14 +219,14 @@ sub checkInvoiceTemplateProvider{ return 0; } -sub getInvoiceClientContactInfo{ - my $self = shift; - my (%params) = @_; - my ($client_id) = @params{qw/client_id/}; - return $tt_record = $self->schema->resultset('invoice_templates')->search({ - reseller_id => $client_id, - }); -} +#sub getInvoiceClientContactInfo{ +# my $self = shift; +# my (%params) = @_; +# my ($client_id) = @params{qw/client_id/}; +# return $tt_record = $self->schema->resultset('contacts')->search({ +# reseller_id => $client_id, +# }); +#} sub getProviderInvoiceList{ my $self = shift; my (%params) = @_; @@ -250,13 +250,14 @@ sub getProviderInvoiceList{ sub getProviderInvoiceListAjax{ my $self = shift; my (%params) = @_; - my ($provider_reseller_id,$stime,$etime) = @params{qw/provider_id stime etime/}; + 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',], @@ -279,28 +280,27 @@ sub getInvoice{ sub getInvoiceProviderClients{ my $self = shift; my (%params) = @_; - my ($provider_contact_id,$stime,$etime) = @params{qw/provider_contact_id stime etime/}; + 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' => [ - 'reseller_id' => $provider_contact_id, #$client_contract_id - contract of the client - 'id' => { '!=' => $provider_contact_id }, + 'me.reseller_id' => $provider_contact_id, #$client_contract_id - contract of the client ], '-exists' => $self->schema->resultset('billing_mappings')->search({ - 'contract.contact_id' => \' = contacts.id', + 'contract.contact_id' => \' = me.id', 'product.class' => [ "sipaccount", "pbxaccount" ], - '-and' => [ - 'start_date' => [ -or => - { '<=' => $etime->epoch }, - { -is => undef }, - ], - 'end_date' => [ -or => - { '>=' => $stime->epoch }, - { -is => undef }, - ], - ] + #'-and' => [ + # 'start_date' => [ -or => + # { '<=' => $etime->epoch }, + # { -is => undef }, + # ], + # 'end_date' => [ -or => + # { '>=' => $stime->epoch }, + # { -is => undef }, + # ], + # ] },{ alias => 'billing_mappings_top', join => ['product','contract'], diff --git a/lib/NGCP/Panel/Utils/Datatables.pm b/lib/NGCP/Panel/Utils/Datatables.pm index 276f0bc64d..8836f38d2d 100644 --- a/lib/NGCP/Panel/Utils/Datatables.pm +++ b/lib/NGCP/Panel/Utils/Datatables.pm @@ -52,17 +52,18 @@ sub process { } # data-range searching - my $from_date = $c_->request->params->{sSearch_0} // ""; - my $to_date = $c_->request->params->{sSearch_1} // ""; + my $from_date_in = $c_->request->params->{sSearch_0} // ""; + my $to_date_in = $c_->request->params->{sSearch_1} // ""; + my($from_date,$to_date); my $parser = DateTime::Format::Strptime->new( #pattern => '%Y-%m-%d %H:%M', pattern => '%Y-%m-%d', ); if($from_date) { - $from_date = $parser->parse_datetime($from_date); + $from_date = $parser->parse_datetime($from_date_in); } if($to_date) { - $to_date = $parser->parse_datetime($to_date); + $to_date = $parser->parse_datetime($to_date_in); } @searchColumns = (); foreach my $c(@{ $cols }) { @@ -71,12 +72,12 @@ sub process { if($c->{search_from_epoch} && $from_date) { $rs = $rs->search({ - $name => { '>=' => $from_date->epoch }, + $name => { '>=' => $c->{search_use_datetime} ? $from_date_in : $from_date->epoch }, }); } if($c->{search_to_epoch} && $to_date) { $rs = $rs->search({ - $name => { '<=' => $to_date->epoch }, + $name => { '<=' => $c->{search_use_datetime} ? $to_date_in : $to_date->epoch }, }); } } diff --git a/share/templates/helpers/datatables.tt b/share/templates/helpers/datatables.tt index f36f2ad8a8..2c78b115fa 100644 --- a/share/templates/helpers/datatables.tt +++ b/share/templates/helpers/datatables.tt @@ -61,6 +61,16 @@ $(document).ready(function() { "iDisplayLength": 5, 'iShowPages': 5, "sAjaxSource": "[% helper.ajax_uri %]", + "fnServerParams": function ( aoData ) { + var params = $('#[% helper.id_from_name %]_table').attr('paramsJSON'); + if(params){ + var paramsParsed = jQuery.parseJSON(params); + for(var key in paramsParsed){ + alert('key='+key+'; value='+paramsParsed[key]+';'); + aoData.push( {"name":key,"value":paramsParsed[key]} ); + } + } + }, [% IF helper.column_sort -%] "aaSorting": [ [% diff --git a/share/templates/invoice/invoice_generate.tt b/share/templates/invoice/invoice_generate.tt new file mode 100644 index 0000000000..c4c6a79fee --- /dev/null +++ b/share/templates/invoice/invoice_generate.tt @@ -0,0 +1,2 @@ +[%m.name = "Invoice Generation"%] +[%PROCESS 'helpers/ajax_form_modal.tt'-%] diff --git a/share/templates/invoice/list.tt b/share/templates/invoice/list.tt index 8a500b5162..29564616ca 100644 --- a/share/templates/invoice/list.tt +++ b/share/templates/invoice/list.tt @@ -6,6 +6,12 @@ [%PROCESS 'helpers/datatables_vars.tt' no_auto_helper = 1 -%] +[%PROCESS 'helpers/modal.tt' -%] +[%mf_helper = { + ajax_load => 1, + ajax_list_refresh => 'template', + }%] +[%modal_script( m = mf_helper )%] [% site_config.title = c.loc('Invoices for [_1]', provider.name ) -%] @@ -31,22 +37,50 @@
+ + +
+
+[%#Dumper.dump_html(provider_client_list_ajax)%] +[% + clearHelper(); + helper.name = c.loc('Clients'); + helper.name_single = c.loc('client'); + helper.dt_columns = [ + { name => 'id', title => c.loc('Client Contact Id'), search => 1}, + { name => 'contracts.id', title => c.loc('Client Contract Id'), search => 1}, + { name => 'contracts.external_id', title => c.loc('External #'), search => 1 }, + { name => 'email', title => c.loc('Contact Email'), search => 1 }, + { name => 'contracts.billing_mappings.billing_profile.name', title => c.loc('Billing Profile'), search => 1 }, + { name => 'contracts.status', title => c.loc('Status'), search => 1 }, + ]; + helper.dt_buttons = [ + { name = c.loc('View invoices'), uri = "javascript:alert('+full.id+');invoicesTable = \$(\\'#invoice_list_data_ajax_table\\');if(invoicesTable){invoicesTable.attr(\\'paramsJSON\\',JSON.stringify({\\'client_contact_id\\':'+full.id+'}));invoicesTable.dataTable().fnDraw();}void(0);", class = 'btn-small btn-primary', icon = 'icon-glass' }, + { name = c.loc('Generate invoice'), uri = "javascript:void(0);", class = 'btn-small btn-primary', icon = 'icon-star' }, + ]; + helper.identifier = 'provider_client_list_ajax'; + helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'provider_client_list' ] ) ; + initHelperAuto(); + PROCESS 'helpers/datatables.tt'; + datatables.clients = {helper => {}}; + datatables.clients.helper.import(helper); +-%] +[%#PROCESS 'invoice/invoice_list.tt' %] +
+
+
+ + +
-
+
-[%# [%IF 0 && c.user.roles == 'admin' %] -[%# { name => 'reseller_id', title => c.loc('Reseller Id')}, -[%# { name => 'reseller_name', title => c.loc('Reseller Name')}, -[%# [%END%] -[%# { name => 'client_contact_id', title => c.loc('Client')}, -[%# { name => 'contract_balance_start', title => c.loc('Period Start')}, -[%# { name => 'contract_balance_end', title => c.loc('Period End')}, -[%# { name => 'cash_balance', title => c.loc('Cash balance')}, -[%# { name => 'free_time_balance', title => c.loc('Free time balance')},%] [%#, title => c.loc('Reseller #')%] [%#, title => c.loc('Reseller name')%] [%#Dumper.dump_html(invoice_list_ajax)%] @@ -57,25 +91,27 @@ helper.dt_columns = [ { name => 'contract_balances.contract.contact.reseller_id'}, { name => 'contract_balances.contract.contact.reseller.name'}, - { name => 'contract_balances.contract.contact.id', title => c.loc('Client')}, - { name => 'contract_balances.invoice_id', title => c.loc('Invoice #')}, - { name => 'contract_balances.start', title => c.loc('Period Start')}, + { name => 'contract_balances.contract.contact.id', title => c.loc('Client'), search => 1}, + { name => 'contract_balances.invoice_id', title => c.loc('Invoice #'), search => 1}, + { name => 'contract_balances.start', title => c.loc('Period Start'), search_from_epoch => 1, search_to_epoch => 1, search_use_datetime => 1 }, { name => 'contract_balances.end', title => c.loc('Period End')}, { name => 'contract_balances.cash_balance', title => c.loc('Cash balance')}, { name => 'contract_balances.free_time_balance', title => c.loc('Free Time balance')}, ]; helper.dt_buttons = [ - { name = c.loc('View PDF'), uri = "javascript:window.open(\\'/invoice/data/' + full.contract_balances_invoice_id + '\\',\\'_blank\\');void(0);", class = 'btn-small btn-primary', icon = 'icon-edit' }, + { name = c.loc('View invoice PDF'), uri = "javascript:window.open(\\'/invoice/data/' + full.contract_balances_invoice_id + '\\',\\'_blank\\');void(0);", class = 'btn-small btn-primary', icon = 'icon-edit' }, ]; helper.identifier = 'invoice_list_data_ajax'; helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_list_data' ] ) ; initHelperAuto(); PROCESS 'helpers/datatables.tt'; + datatables.invoices = {helper => {}}; + datatables.invoices.helper.import(helper); -%] [%#PROCESS 'invoice/invoice_list.tt' %]
-
+
- +
+