From 7720d53bb13f3f6933c8746c61e8a3adc8a5874a Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Sun, 20 Apr 2014 20:45:36 +0300 Subject: [PATCH] MT#5879 Separate invoice and invoice template interfaces. --- lib/NGCP/Panel/Controller/Customer.pm | 1 - lib/NGCP/Panel/Controller/Invoice.pm | 165 +++++++++--------- lib/NGCP/Panel/Form/InvoiceTemplate/Basic.pm | 4 +- lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm | 58 +++--- share/static/js/invoice_template.js | 37 ++-- .../extensions/ext-server_opensave.js | 2 +- .../invoice/invoice_template_list.tt | 63 ------- share/templates/invoice/list.tt | 116 +++++++++++- .../{invoice_template.tt => template.tt} | 56 +++--- ...e.tt => template_editor_aux_embedimage.tt} | 0 ...editor_form.tt => template_editor_form.tt} | 34 ++-- ...ate_info_form.tt => template_info_form.tt} | 0 share/templates/invoice/template_list.tt | 63 +++++++ 13 files changed, 355 insertions(+), 244 deletions(-) delete mode 100644 share/templates/invoice/invoice_template_list.tt rename share/templates/invoice/{invoice_template.tt => template.tt} (67%) rename share/templates/invoice/{invoice_template_aux_embedimage.tt => template_editor_aux_embedimage.tt} (100%) rename share/templates/invoice/{invoice_template_editor_form.tt => template_editor_form.tt} (75%) rename share/templates/invoice/{invoice_template_info_form.tt => template_info_form.tt} (100%) create mode 100644 share/templates/invoice/template_list.tt diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 26aa1d5950..12ed117bab 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -1,6 +1,5 @@ package NGCP::Panel::Controller::Customer; use Sipwise::Base; -use MIME::Base64 qw(encode_base64); use namespace::sweep; BEGIN { extends 'Catalyst::Controller'; } use JSON qw(decode_json encode_json); diff --git a/lib/NGCP/Panel/Controller/Invoice.pm b/lib/NGCP/Panel/Controller/Invoice.pm index 541e710b84..82b377c768 100644 --- a/lib/NGCP/Panel/Controller/Invoice.pm +++ b/lib/NGCP/Panel/Controller/Invoice.pm @@ -5,6 +5,9 @@ BEGIN { extends 'Catalyst::Controller'; } use DateTime qw(); use HTTP::Status qw(HTTP_SEE_OTHER); use File::Type; +use MIME::Base64 qw(encode_base64); + + use NGCP::Panel::Utils::Contract; use NGCP::Panel::Utils::Message; @@ -22,24 +25,16 @@ sub auto { return 1; } -sub list_invoice_template :Chained('/') :PathPart('invoice') :CaptureArgs(0) { +sub invoice :Chained('/') :PathPart('invoice') :CaptureArgs(0) { my ($self, $c) = @_; - - $c->stash( - resellers => $c->model('DB') - ->resultset('resellers')->search({ - status => { '!=' => 'terminated' } - }), - template => 'invoice/invoice.tt' - ); } -sub root :Chained('list_invoice_template') :PathPart('') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) +sub root :Chained('invoice') :PathPart('') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) { my ($self, $c) = @_; } -sub messages :Chained('list_invoice_template') :PathPart('messages') :Args(0) { +sub messages :Chained('invoice') :PathPart('messages') :Args(0) { my ($self, $c) = @_; $c->log->debug('messages'); $c->stash( messages => $c->flash->{messages} ); @@ -47,7 +42,7 @@ sub messages :Chained('list_invoice_template') :PathPart('messages') :Args(0) { $c->detach( $c->view('SVG') );#no wrapper view } -sub ajax_allmighty :Chained('base') :PathPart('ajaxall') :Args(1) { +sub ajax_datatables_data :Chained('base') :PathPart('ajax') :Args(1) { my ($self, $c, $item ) = @_; my $dt_columns_json = $c->request->parameters->{dt_columns}; $c->forward( $item ); @@ -56,9 +51,10 @@ sub ajax_allmighty :Chained('base') :PathPart('ajaxall') :Args(1) { $c->detach( $c->view("JSON") ); } -sub base :Chained('list_invoice_template') :PathPart('') :CaptureArgs(1) { +sub base :Chained('invoice') :PathPart('') :CaptureArgs(1) { my ($self, $c, $reseller_id) = @_; - + $c->log->debug('base'); + unless($reseller_id && $reseller_id->is_int) { NGCP::Panel::Utils::Message->error( c => $c, @@ -70,9 +66,13 @@ sub base :Chained('list_invoice_template') :PathPart('') :CaptureArgs(1) { } $c->detach('/denied_page') if($c->user->roles eq "reseller" && $c->user->reseller_id != $reseller_id); + + my $reseller = $c->model('DB')->resultset('resellers')->search({ + status => { '!=' => 'terminated' }, + id => $reseller_id + }); - $c->stash(reseller => $c->stash->{resellers}->search_rs({ id => $reseller_id })); - unless($c->stash->{reseller}->first) { + unless($reseller->first) { NGCP::Panel::Utils::Message->error( c => $c, log => 'Reseller not found', @@ -80,39 +80,18 @@ sub base :Chained('list_invoice_template') :PathPart('') :CaptureArgs(1) { ); NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/reseller')); } - $c->stash(contract => $c->stash->{resellers}->search_rs({ id => $reseller_id })->first->contract ); - $c->stash(provider => $c->stash->{resellers}->first ); -} -sub details :Chained('base') :PathPart('') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) -{ - my ($self, $c) = @_; - $c->forward('invoice_details_zones'); - $c->forward('invoice_details_calls'); - $c->forward('invoice_template_list_data'); - #$self->invoice_details_zones($c); - #$self->invoice_details_calles($c); + $c->stash( + provider => $reseller->first, + provider_rs => $reseller, + contract => $reseller->search_rs({ id => $reseller_id })->first->contract + ); } -sub invoice_base :Chained('base') :PathPart('') :CaptureArgs(0) { - my ($self, $c) = @_; - my($validator,$backend,$in,$out); - $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); - $c->log->debug('invoice_base'); - my $client_id = $c->stash->{client} ? $c->stash->{client}->id : undef ; - my $client; - if($client_id){ - $client = $backend->getClient($client_id); - }else{ - #$c->stash->{provider}->id; - } - $c->stash( provider => $c->stash->{reseller}->first ); -} -sub invoice_details_zones :Chained('invoice_base') :PathPart('') :CaptureArgs(0) { +sub invoice_details_zones :Chained('base') :PathPart('') :CaptureArgs(0) { my ($self, $c) = @_; $c->log->debug('invoice_details_zones'); - $c->forward( 'invoice_base' ); - my $contract_id = $c->stash->{provider}->id; + my $provider_id = $c->stash->{provider}->id; my $stime = NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month'); my $etime = $stime->clone->add(months => 1); @@ -120,7 +99,7 @@ sub invoice_details_zones :Chained('invoice_base') :PathPart('') :CaptureArgs(0) #my $form = NGCP::Panel::Form::InvoiceTemplate::Basic->new( ); my $invoice_details_zones = NGCP::Panel::Utils::Contract::get_contract_zonesfees_rs( c => $c, - contract_id => $contract_id, + provider_id => $provider_id, stime => $stime, etime => $etime, ); @@ -132,11 +111,11 @@ sub invoice_details_zones :Chained('invoice_base') :PathPart('') :CaptureArgs(0) $c->stash( invoice_details_zones => $invoice_details_zones ); $c->stash( invoice_details_zones_ajax => $invoice_details_zones_ajax ); } + sub invoice_details_calls :Chained('invoice_details_zones') :PathPart('') :CaptureArgs(0) { my ($self, $c) = @_; $c->log->debug('invoice_details_calls'); - $c->forward( 'invoice_base' ); - my $contract_id = $c->stash->{provider}->id; + my $provider_id = $c->stash->{provider}->id; my $stime = NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month'); my $etime = $stime->clone->add(months => 1); @@ -144,7 +123,7 @@ sub invoice_details_calls :Chained('invoice_details_zones') :PathPart('') :Captu #my $form = NGCP::Panel::Form::InvoiceTemplate::Basic->new( ); my $invoice_details_calls = NGCP::Panel::Utils::Contract::get_contract_calls_rs( c => $c, - contract_id => $contract_id, + provider_id => $provider_id, stime => $stime, etime => $etime, ); @@ -165,7 +144,29 @@ sub invoice_details_calls :Chained('invoice_details_zones') :PathPart('') :Captu $c->stash( invoice_details_calls => $invoice_details_calls ); $c->stash( invoice_details_calls_ajax => $invoice_details_calls_ajax ); } -sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/info') :Args(0) { + +sub invoice_list :Chained('invoice_details_calls') :PathPart('list') :Args(0) { + my ($self, $c) = @_; + $c->stash( template => 'invoice/list.tt' ); +} + +sub template_base :Chained('base') :PathPart('template') :CaptureArgs(0) { + my ($self, $c) = @_; + my($validator,$backend,$in,$out); + $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); + $c->log->debug('template_base'); + $c->forward( 'template_list_data' ); + my $client_id = $c->stash->{client} ? $c->stash->{client}->id : undef ; + my $client; + if($client_id){ + $client = $backend->getClient($client_id); + }else{ + #$c->stash->{provider}->id; + } + #$c->stash( provider => $c->stash->{reseller}->first ); +} + +sub template_info :Chained('template_base') :PathPart('info') :Args(0) { my ($self, $c) = @_; $c->log->debug($c->action); my($validator,$backend,$in,$out); @@ -173,7 +174,7 @@ sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/i #from parameters $in = $c->request->parameters; - $in->{contract_id} = $c->stash->{provider}->id; + $in->{provider_id} = $c->stash->{provider}->id; #(undef,undef,@$in{qw/tt_id/}) = @_; if($in->{tt_id}){ @@ -181,7 +182,9 @@ sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/i my $db_object; ($out->{tt_id},undef,$db_object) = $backend->getCustomerInvoiceTemplate( %$in ); $out->{tt_data}->{tt_id} = $db_object->get_column('id'); - $out->{tt_data}->{contract_id} = $db_object->get_column('reseller_id'); + if(!$c->stash->{provider}){ + + } foreach(qw/name is_active/){$out->{tt_data}->{$_} = $db_object->get_column($_);} } if(!$out->{tt_data}){ @@ -191,8 +194,8 @@ sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/i $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/invoice_template_info',[$in->{contract_id}]) ); - $validator->name( 'invoice_template_info' );#from parameters + $validator->action( $c->uri_for_action('invoice/template_info',[$in->{provider_id}]) ); + $validator->name( 'template_info' );#from parameters #my $posted = 0; my $posted = exists $in->{submitid}; $c->log->debug("posted=$posted;"); @@ -230,7 +233,7 @@ sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/i $c->stash( m => {create_flag => !$in->{tt_id}} ); $c->stash( form => $validator ); #$c->stash( template => 'helpers/ajax_form_modal.tt' ); - $c->stash( template => 'invoice/invoice_template_info_form.tt' ); + $c->stash( template => 'invoice/template_info_form.tt' ); $c->response->headers->header( 'X-Form-Status' => 'error' ); } }else{ @@ -239,15 +242,14 @@ sub invoice_template_info :Chained('invoice_base') :PathPart('invoice/template/i $c->stash( m => {create_flag => !$in->{tt_id}} ); $c->stash( form => $validator ); #$c->stash( template => 'helpers/ajax_form_modal.tt' ); - $c->stash( template => 'invoice/invoice_template_info_form.tt' ); + $c->stash( template => 'invoice/template_info_form.tt' ); } $c->detach( $c->view("SVG") );#to the sake of nowrapper } - -sub invoice_template_activate :Chained('invoice_base') :PathPart('invoice/template/activate') :Args(2) { +sub template_activate :Chained('template_base') :PathPart('activate') :Args(2) { my ($self, $c) = @_; - $c->log->debug('invoice_template_activate'); + $c->log->debug('template_activate'); my($validator,$backend,$in,$out); (undef,undef,@$in{qw/tt_id is_active/}) = @_; @@ -256,7 +258,7 @@ sub invoice_template_activate :Chained('invoice_base') :PathPart('invoice/templa #this is just copy-paste from method above #of course we are chained and we can put in and out to stash #input - $in->{contract_id} = $c->stash->{provider}->id; + $in->{provider_id} = $c->stash->{provider}->id; #output $out={}; @@ -302,11 +304,11 @@ sub invoice_template_activate :Chained('invoice_base') :PathPart('invoice/templa ? 'Invoice template deactivated' :'Invoice template activated' ) }]); - $c->forward( 'invoice_template_list' ); + $c->forward( 'template_list' ); } -sub invoice_template_delete :Chained('invoice_base') :PathPart('invoice/template/delete') :Args(1) { +sub template_delete :Chained('template_base') :PathPart('delete') :Args(1) { my ($self, $c) = @_; - $c->log->debug('invoice_template_delete'); + $c->log->debug('template_delete'); my($validator,$backend,$in,$out); (undef,undef,@$in{qw/tt_id/}) = @_; @@ -315,7 +317,7 @@ sub invoice_template_delete :Chained('invoice_base') :PathPart('invoice/template #this is just copy-paste from method above #of course we are chained and we can put in and out to stash #input - $in->{contract_id} = $c->stash->{provider}->id; + $in->{provider_id} = $c->stash->{provider}->id; #output $out={}; @@ -356,41 +358,40 @@ sub invoice_template_delete :Chained('invoice_base') :PathPart('invoice/template $c->flash(messages => [{type => 'success', text => $c->loc( 'Invoice template deleted' ) }]); - $c->forward( 'invoice_template_list' ); + $c->forward( 'template_list' ); } -sub invoice_template_list_data :Chained('invoice_details_calls') :PathPart('') :CaptureArgs(0) { +sub template_list_data :Chained('base') :PathPart('') :CaptureArgs(0) { my ($self, $c) = @_; - $c->log->debug('invoice_template_list_data'); + $c->log->debug('template_list_data'); my($validator,$backend,$in,$out); - $in->{contract_id} = $c->stash->{provider}->id; + $in->{provider_id} = $c->stash->{provider}->id; $backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); my $records = $backend->getCustomerInvoiceTemplateList( %$in ); - $c->stash( invoice_template_list => $records ); + $c->stash( template_list => $records ); } -sub invoice_template_list :Chained('invoice_base') :PathPart('invoice/template') :Args(0) { +sub template_list :Chained('template_base') :PathPart('list') :Args(0) { my ($self, $c) = @_; - $c->log->debug('invoice_template_list'); - $c->stash( template => 'invoice/invoice_template_list.tt' ); - $c->forward( 'invoice_template_list_data' ); + $c->log->debug('template_list'); + $c->stash( template => 'invoice/template_list.tt' ); $c->detach($c->view('SVG'));#just no wrapper - maybe there is some other way? } -sub invoice :Chained('invoice_template_list_data') :PathPart('invoice') :Args(0) { +sub template :Chained('template_base') :PathPart('') :Args(0) { my ($self, $c) = @_; - $c->stash(template => 'invoice/invoice.tt'); + $c->stash(template => 'invoice/template.tt'); } -sub invoice_template :Chained('invoice_details_calls') :PathPart('template') :Args { +sub template_view :Chained('template_base') :PathPart('view') :Args { my ($self, $c) = @_; - $c->log->debug('invoice_template'); + $c->log->debug('template_view'); no warnings 'uninitialized'; my($validator,$backend,$in,$out); #input (undef,undef,@$in{qw/tt_type tt_viewmode tt_sourcestate tt_output_type tt_id/}) = @_ ; - $in->{contract_id} = $c->stash->{provider}->id; + $in->{provider_id} = $c->stash->{provider}->id; #$in->{client_id} = ; $in->{tt_string} = $c->request->body_parameters->{template} || ''; foreach(qw/name is_active/){$in->{$_} = $c->request->parameters->{$_};} @@ -529,7 +530,7 @@ sub invoice_template :Chained('invoice_details_calls') :PathPart('template') :Ar return; }else{#parsed - my $contacts = $c->model('DB')->resultset('contacts')->search({ id => $in->{contract_id} }); + my $contacts = $c->model('DB')->resultset('contacts')->search({ id => $in->{provider_id} }); $c->stash( provider => $contacts->first ); #some preprocessing should be done only before showing. So, there will be: @@ -578,7 +579,7 @@ sub invoice_template :Chained('invoice_details_calls') :PathPart('template') :Ar my ($tempdirbase,$tempdir ); use File::Temp qw/tempfile tempdir/; #my($fh, $tempfilename) = tempfile(); - $tempdirbase = join('/',File::Spec->tmpdir,@$in{qw/contract_id tt_type tt_sourcestate/}, $out->{tt_id}); + $tempdirbase = join('/',File::Spec->tmpdir,@$in{qw/provider_id tt_type tt_sourcestate/}, $out->{tt_id}); use File::Path qw( mkpath ); ! -e $tempdirbase and mkpath( $tempdirbase, 0, 0777 ); $tempdir = tempdir( DIR => $tempdirbase , CLEANUP => 1 ); @@ -633,11 +634,11 @@ sub invoice_template :Chained('invoice_details_calls') :PathPart('template') :Ar } } -sub invoice_template_aux_embedImage :Chained('list_reseller') :PathPart('auxembedimage') :Args(0) { +sub template_aux_embedImage :Chained('invoice') :PathPart('auxembedimage') :Args(0) { my ($self, $c) = @_; #I know somewhere is logging of all visited methods - $c->log->debug('invoice_template_aux_handleImageUpload'); + $c->log->debug('template_aux_embedImage'); my($validator,$backend,$in,$out); #todo @@ -654,7 +655,7 @@ sub invoice_template_aux_embedImage :Chained('list_reseller') :PathPart('auxembe $c->log->debug('mime-type '.$out->{image_content_mimetype}); $c->stash(out => $out); $c->stash(in => $in); - $c->stash(template => 'invoice/invoice_template_aux_embedimage.tt'); + $c->stash(template => 'invoice/template_editor_aux_embedimage.tt'); $c->detach( $c->view('SVG') ); } diff --git a/lib/NGCP/Panel/Form/InvoiceTemplate/Basic.pm b/lib/NGCP/Panel/Form/InvoiceTemplate/Basic.pm index 3b21b610c2..25c1be3d0a 100644 --- a/lib/NGCP/Panel/Form/InvoiceTemplate/Basic.pm +++ b/lib/NGCP/Panel/Form/InvoiceTemplate/Basic.pm @@ -58,7 +58,7 @@ has_field 'tt_string' => ( required => 0, ); -has_field 'contract_id' => ( +has_field 'provider_id' => ( type => 'Hidden', #default => \& #apply => [ { check => \&validate_tt_string } ], @@ -94,7 +94,7 @@ has_field 'save' => ( has_block 'fields' => ( tag => 'div', class => [qw/modal-body/], - render_list => [qw/name tt_id is_active submitid contract_id/], + render_list => [qw/name tt_id is_active submitid provider_id/], ); has_block 'actions' => ( diff --git a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm index ada350c078..a9e3f6dcc1 100644 --- a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm +++ b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm @@ -8,14 +8,14 @@ sub getDefaultConditions{ my $self = shift; my ($params) = @_; #irka::loglong(Dumper($params)); - my ($contract_id,$tt_sourcestate,$tt_type,$tt_id) = @$params{qw/contract_id tt_sourcestate tt_type tt_id/}; + my ($provider_id,$tt_sourcestate,$tt_type,$tt_id) = @$params{qw/provider_id tt_sourcestate tt_type tt_id/}; my $conditions = {}; irka::loglong("getDefaultConditions: tt_id=$tt_id;\n"); if($tt_id){ $conditions = { id => $tt_id }; }else{ $conditions = { - reseller_id => $contract_id, + reseller_id => $provider_id, type => $tt_type, is_active => 1, }; @@ -25,7 +25,7 @@ sub getDefaultConditions{ sub getCustomerInvoiceTemplate{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_sourcestate,$tt_type,$tt_id) = @params{qw/contract_id tt_sourcestate tt_type tt_id/}; + my ($provider_id,$tt_sourcestate,$tt_type,$tt_id) = @params{qw/provider_id tt_sourcestate tt_type tt_id/}; #irka::loglong("getCustomerInvoiceTemplate: tt_id=$tt_id;\n"); #irka::loglong(Dumper(\%params)); @@ -36,14 +36,14 @@ sub getCustomerInvoiceTemplate{ #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 a option. What a poor "wonabe hibernate" idea and implementation. my $tt_record = $self->schema->resultset('invoice_templates')->search( { id => $tt_id }, { - #'+select' => [{'reseller_id' =>'contract_id','-as'=>'contract_id'},{'id' => 'tt_id','-as'=>'tt_id'}] + #'+select' => [{'reseller_id' =>'provider_id','-as'=>'provider_id'},{'id' => 'tt_id','-as'=>'tt_id'}] #'+select' => [ - # [ 'reseller_id', {'-as'=>'contract_id'}], + # [ 'reseller_id', {'-as'=>'provider_id'}], # [ 'id', {'-as'=>'tt_id'}], #], #'+select' => [qw/reseller_id id/], - #'+as' => [qw/contract_id tt_id/] - #'-as' => [{reseller_id=>contract_id},{ id=>tt_id}] + #'+as' => [qw/provider_id tt_id/] + #'-as' => [{reseller_id=>provider_id},{ id=>tt_id}] } )->first; #here may be base64 decoding @@ -52,7 +52,7 @@ sub getCustomerInvoiceTemplate{ #if('saved' eq $tt_sourcestate){ if( $tt_record ){ $tt_sourcestate and $result = $tt_record->get_column( 'base64_'.$tt_sourcestate ); - #$tt_record->contract_id + #$tt_record->reseller_id $tt_id = $tt_record->get_column( 'id' ); } if( $result && exists $params{result} ){ @@ -64,13 +64,13 @@ sub getCustomerInvoiceTemplate{ sub storeCustomerInvoiceTemplate{ my $self = shift; my (%params) = @_; - my ($contract_id, $tt_sourcestate, $tt_type,$tt_string,$tt_id,$is_active,$name) = @params{qw/contract_id tt_sourcestate tt_type tt_string_sanitized tt_id is_active name/}; + 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 => $contract_id, +# reseller_id => $provider_id, # type => $tt_type, # is_active => 1, # 'base64_'.$tt_sourcestate => $$tt_string, @@ -80,7 +80,7 @@ sub storeCustomerInvoiceTemplate{ #here may be base64 decoding # $self->schema->resultset('ivoice_template')->search({ -# reseller_id => $contract_id, +# reseller_id => $provider_id, # type => $tt_type, # })->update_all({ # is_active => 0, @@ -90,7 +90,7 @@ sub storeCustomerInvoiceTemplate{ my $tt_record_updated; if( !$tt_id ){ $tt_record_created = $self->schema->resultset('invoice_templates')->create({ - reseller_id => $contract_id, + reseller_id => $provider_id, type => $tt_type, # is_active => $is_active, # name => $name, @@ -109,7 +109,7 @@ sub storeCustomerInvoiceTemplate{ }); } # if($is_active && $tt_id){ -# $self->deactivateOtherTemplates($contract_id,$tt_id); +# $self->deactivateOtherTemplates($provider_id,$tt_id); # } }); return { tt_id => $tt_id }; @@ -117,7 +117,7 @@ sub storeCustomerInvoiceTemplate{ sub storeInvoiceTemplateInfo{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_id,$is_active,$name) = @params{qw/contract_id tt_id is_active name/}; + my ($provider_id,$tt_id,$is_active,$name) = @params{qw/provider_id tt_id is_active name/}; #my $tt_record = $self->resultset('invoice_templates')->search({ $self->schema->txn_do(sub { @@ -125,7 +125,7 @@ sub storeInvoiceTemplateInfo{ my $tt_record_updated; if( !$tt_id ){ $tt_record_created = $self->schema->resultset('invoice_templates')->create({ - reseller_id => $contract_id, + reseller_id => $provider_id, is_active => $is_active, name => $name, }); @@ -140,7 +140,7 @@ sub storeInvoiceTemplateInfo{ }); } if($is_active && $tt_id){ - $self->deactivateOtherTemplates($contract_id,$tt_id); + $self->deactivateOtherTemplates($provider_id,$tt_id); } }); return { tt_id => $tt_id }; @@ -148,46 +148,46 @@ sub storeInvoiceTemplateInfo{ sub getCustomerInvoiceTemplateList{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_sourcestate,$tt_type, $tt_string, $tt_id) = @params{qw/contract_id tt_sourcestate tt_type tt_string_sanitized tt_id/}; + 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_template_fake')->find(\'select * from invoice_templates')->all #$self->schema->resultset('invoice_templates')->name(\'(select * from invoice_templates)')->all #]; return [ $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, })->all ]; } sub deleteCustomerInvoiceTemplate{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_id) = @params{qw/contract_id tt_id/}; + my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; return $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, id => $tt_id, })->delete_all; } sub activateCustomerInvoiceTemplate{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_id) = @params{qw/contract_id tt_id/}; + my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; $self->schema->txn_do(sub { $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, id => $tt_id, })->update({ is_active => 1, }); - $self->deactivateOtherTemplates($contract_id,$tt_id); + $self->deactivateOtherTemplates($provider_id,$tt_id); }); } sub deactivateCustomerInvoiceTemplate{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_id) = @params{qw/contract_id tt_id/}; + my ($provider_id, $tt_id) = @params{qw/provider_id tt_id/}; $self->schema->txn_do(sub { $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, id => $tt_id, })->update({ is_active => 0, @@ -196,9 +196,9 @@ sub deactivateCustomerInvoiceTemplate{ } sub deactivateOtherTemplates{ my $self = shift; - my ($contract_id,$tt_id) = @_; + my ($provider_id,$tt_id) = @_; $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, id => {'!=' => $tt_id }, is_active => 1, })->update_all({ @@ -208,9 +208,9 @@ sub deactivateOtherTemplates{ sub checkCustomerInvoiceTemplateContract{ my $self = shift; my (%params) = @_; - my ($contract_id,$tt_id) = @params{qw/contract_id tt_id/}; + my ($provider_id,$tt_id) = @params{qw/provider_id tt_id/}; my $tt_record = $self->schema->resultset('invoice_templates')->search({ - reseller_id => $contract_id, + reseller_id => $provider_id, id => $tt_id, }); if($tt_record->get_column('id')){ diff --git a/share/static/js/invoice_template.js b/share/static/js/invoice_template.js index b95ed8469b..2ae3d2cf55 100644 --- a/share/static/js/invoice_template.js +++ b/share/static/js/invoice_template.js @@ -39,7 +39,7 @@ function setSvgStringToPreview( svgParsedString, q, data ) { dataPreview.tt_type = 'svg'; dataPreview.tt_output_type = 'svg'; dataPreview.tt_sourcestate = dataPreview.tt_sourcestate || 'saved'; - q = uriForAction( dataPreview, 'invoice_template' ); + q = uriForAction( dataPreview, 'template' ); //alert('setSvgStringToPreview: q='+q+';'); } previewIframe.src = q; @@ -48,7 +48,7 @@ function setSvgStringToPreview( svgParsedString, q, data ) { } } function fetchSvgToEditor( data ) { - var q = uriForAction( data, 'invoice_template' ); + var q = uriForAction( data, 'template' ); //alert('fetchSvgToEditor: q='+q+';'); $.ajax({ url: q, @@ -61,7 +61,7 @@ function fetchInvoiceTemplateData( data, noshowform ){ //params spec: tt_type=[svg|html]/tt_viewmode[parsed|raw]/tt_sourcestate[saved|previewed|default]/tt_output_type[svg|pdf|html|json|svgzip|pdfzip|htmlzip]/tt_id //tt_output_type=svg really outputs text/html mimetype. But it will be couple of tags ( per page). data.tt_output_type = 'json'; - var q = uriForAction( data, 'invoice_template' ); + var q = uriForAction( data, 'template' ); //alert('fetchInvoiceTemplateData: q='+q+';'); var queryObj = { url: q, @@ -86,22 +86,23 @@ function fetchInvoiceTemplateData( data, noshowform ){ setSvgStringToEditor( templatedata.aaData.template.raw ); setSvgStringToPreview( templatedata.aaData.template.parsed, '', data ); } - $('#load_previewed_control').css('visibility', 'visible' ); +// $('#load_previewed_control').css('visibility', 'visible' ); +// $('#load_saved_control').css('visibility', 'visible' ); if( templatedata.aaData.form ){ - $('form[name=invoice_template_editor]').loadJSON(templatedata.aaData.form); - if(templatedata.aaData.form.base64_previewed){ - $('#load_previewed_control').css('display', 'inline' ); - } + $('form[name=template_editor]').loadJSON(templatedata.aaData.form); + $('#load_previewed_control').css('display', 'inline' ); + $('#load_saved_control').css('display', 'inline' ); } if( !noshowform ){ - $('#invoice_template_editor_form').css('visibility','visible'); + $('#template_editor_form').css('visibility','visible'); } } }); } function clearTemplateForm(data){ - $('#invoice_template_editor_form').css('visibility','hidden'); + $('#template_editor_form').css('visibility','hidden'); $('#load_previewed_control').css('display', 'none' ); + $('#load_saved_control').css('display', 'none' ); if(!data){ data = {}; } @@ -110,7 +111,7 @@ function clearTemplateForm(data){ } function savePreviewedAndShowParsed( data ){ var svgString = getSvgString(); - var q = uriForAction( data, 'invoice_template_previewed' ); + var q = uriForAction( data, 'template_previewed' ); //alert('savePreviewedAndShowParsed: svgString='+svgString+'; q='+q+';'); //alert('savePreviewedAndShowParsed: q='+q+';'); //save @@ -120,15 +121,16 @@ function savePreviewedAndShowParsed( data ){ // & show template //alert('savePreviewedAndShowParsed: httpResponse='+httpResponse+';'); setSvgStringToPreview( httpResponse, q, data ) - //refresh list after saving - refreshAjaxList( 'invoice_template', data ); + $('#load_previewed_control').css('display', 'inline' ); + //refresh list after saving - there is nothin that can be cahnged in templates list after preview refresh + //refreshAjaxList( 'template', data ); } ); } -function saveTemplate( data ) { +function saveTemplate( data ) { var svgString = getSvgString(); data.tt_sourcestate='saved'; data.tt_output_type = 'json'; - var q = uriForAction( data, 'invoice_template_saved' ); + var q = uriForAction( data, 'template_saved' ); q=formToUri(q); //alert('saveTemplate: q='+q+';'); $.ajax( { @@ -138,9 +140,10 @@ function saveTemplate( data ) { data: { template: svgString }, } ).done( function( jsonResponse ) { if(jsonResponse.aaData && jsonResponse.aaData.form){ - $('form[name=invoice_template_editor]').loadJSON(jsonResponse.aaData.form); + $('form[name=template_editor]').loadJSON(jsonResponse.aaData.form); } - refreshAjaxList( 'invoice_template', data ); + $('#load_saved_control').css('display', 'inline' ); + //refreshAjaxList( 'template', data ); }); } diff --git a/share/static/js/libs/svg-edit/extensions/ext-server_opensave.js b/share/static/js/libs/svg-edit/extensions/ext-server_opensave.js index 8e6019a4ce..13aed26098 100644 --- a/share/static/js/libs/svg-edit/extensions/ext-server_opensave.js +++ b/share/static/js/libs/svg-edit/extensions/ext-server_opensave.js @@ -113,7 +113,7 @@ svgEditor.addExtension("server_opensave", { open_svg_action = svgEditor.curConfig.extPath + 'fileopen.php?type=load_svg'; import_svg_action = svgEditor.curConfig.extPath + 'fileopen.php?type=import_svg'; //import_img_action = svgEditor.curConfig.extPath + 'fileopen.php?type=import_img'; - import_img_action = '/customer/auxembedimage?type=import_img'; + import_img_action = '/invoice/auxembedimage?type=import_img'; // Set up function for PHP uploader to use svgEditor.processFile = function(str64, type) { diff --git a/share/templates/invoice/invoice_template_list.tt b/share/templates/invoice/invoice_template_list.tt deleted file mode 100644 index 0d2d82f257..0000000000 --- a/share/templates/invoice/invoice_template_list.tt +++ /dev/null @@ -1,63 +0,0 @@ -[% USE Dumper %] -[% USE format %] -[% money_format = format('%.2f') %] -[% IF !c.user.read_only && (c.user.roles == 'admin' || c.user.roles == 'reseller') -%] -[% write_access = 1 %] -[%END%] - - [%IF invoice_template_list.size %] - - - - - - - - - - - - [% FOR template IN invoice_template_list -%] - [%# Dumper.dump_html(invoice_template_list.as_query)%] - [%# Dumper.dump_html(template)%] - - - - - - - - [%END%] - -
[% c.loc('Active') %][% c.loc('Id') %][% c.loc('Name') %][% c.loc('Type') %]
[%IF template.get_column('is_active') > 0; '✓'; END%][% template.get_column('id') %][% template.get_column('name') %][% template.get_column('type') %] - -
- [%END%] - - \ No newline at end of file diff --git a/share/templates/invoice/list.tt b/share/templates/invoice/list.tt index 514b158642..18f9bbf74d 100644 --- a/share/templates/invoice/list.tt +++ b/share/templates/invoice/list.tt @@ -1,11 +1,111 @@ -[% site_config.title = c.loc('InvoicePeriods') -%] +[% USE Dumper %] +[% USE format %] +[% money_format = format('%.2f') %] +[% write_access = 1 %] + +[%PROCESS 'helpers/datatables_vars.tt' + no_auto_helper = 1 +-%] + +[% site_config.title = c.loc('Invoices for [_1]', provider.name ) -%] + +
+ + [% c.loc('Back') %] + +
+ +[% IF reseller.first.status != "active" -%] + [%messages.unshift( c.loc('Reseller is [_1]', reseller.first.status) ); %] +[% END -%] +
+[%PROCESS 'helpers/ajax_messages.tt' -%] +
+ + +
+[% back_created = 1 -%] + +
+ +
+ + +
+
+ + +[% + clearHelper(); + helper.name = c.loc('Invoice Connection Fees'); + helper.dt_columns = [ + { name => 'source_customer_billing_zones_history.zone', title => c.loc('Zone'), search=> 1 }, + { name => 'number', title => c.loc('Calls') }, + { name => 'duration', title => c.loc('Usage') }, + { name => 'free_time', title => c.loc('Free time') }, + { name => 'cost', title => c.loc('Amount EUR') }, + ]; + helper.name_single = c.loc('Invoice Connections Records'); + helper.identifier = 'invoice_details_zones_ajax'; + helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_details_zones' ] ) ; + initHelperAuto(); + PROCESS 'helpers/datatables.tt'; +-%] + [%PROCESS 'invoice/invoice_details_zones_list.tt' %] + +
+
+
+ +
+ + +
+
+ + [% - helper.name = c.loc('InvoicePeriods'); - helper.name_single = c.loc('InvoicePeriod'); - helper.identifier = 'InvoicePeriod'; - helper.top_buttons = ""; - - PROCESS 'helpers/datatables_vars.tt'; + clearHelper(); + helper.name = c.loc('Invoice Calls'); + helper.dt_columns = [ + { name => 'start_time', title => c.loc('Start time'), search_from_epoch => 1, search_to_epoch=>1 }, + { name => 'duration', title => c.loc('Duration') }, + { name => 'destination_user', title => c.loc('Destination'), search => 1 }, + { name => 'call_type', title => c.loc('Type'), search => 1 }, + { name => 'source_customer_billing_zones_history.zone', title => c.loc('Zone'), search=> 1 }, + { name => 'source_customer_cost', title => c.loc('Amount EUR') }, + ]; + helper.name_single = c.loc('Invoice calls'); + helper.identifier = 'invoice_details_calls_ajax'; + helper.id_from_name = 'start_time'; + helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_details_calls' ] ) ; + initHelperAuto(); PROCESS 'helpers/datatables.tt'; -%] -[% # vim: set tabstop=4 syntax=html expandtab: -%] + [%PROCESS 'invoice/invoice_details_calls_list.tt' %] + +
+
+
+
+ + + diff --git a/share/templates/invoice/invoice_template.tt b/share/templates/invoice/template.tt similarity index 67% rename from share/templates/invoice/invoice_template.tt rename to share/templates/invoice/template.tt index ef355f8477..882fc81179 100644 --- a/share/templates/invoice/invoice_template.tt +++ b/share/templates/invoice/template.tt @@ -9,7 +9,7 @@ [%PROCESS 'helpers/modal.tt' -%] [%mf_helper = { ajax_load => 1, - ajax_list_refresh => 'invoice_template', + ajax_list_refresh => 'template', }%] [%modal_script( m = mf_helper )%] @@ -27,31 +27,31 @@ var uriForAction = function( data, type ){ //also we can think about getting URIs via ajax ) var q_template; switch(type){ - case 'invoice_template_previewed': - //q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ['contract_id','svg','parsed','previewed','svg','tt_id']) -%]'; - q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ["contract_id"]) -%]'+'/svg/parsed/previewed/svg/tt_id'; + case 'template_previewed': + //q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id','svg','parsed','previewed','svg','tt_id']) -%]'; + q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id']) -%]'+'/svg/parsed/previewed/svg/tt_id'; break; - case 'invoice_template_saved': - //q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ['contract_id','svg','parsed','saved','svg','tt_id']) -%]'; - q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ["contract_id"]) -%]'+'/svg/parsed/saved/tt_output_type/tt_id'; + case 'template_saved': + //q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id','svg','parsed','saved','svg','tt_id']) -%]'; + q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id']) -%]'+'/svg/parsed/saved/tt_output_type/tt_id'; break; - case 'invoice_template_list': - q_template = '[%- c.uri_for_action("/invoice/invoice_template_list", ['contract_id']) -%]'; + case 'template_list': + q_template = '[%- c.uri_for_action("/invoice/template_list", ['provider_id']) -%]'; break; case 'messages': q_template = '[%- c.uri_for_action("/invoice/messages") -%]'; break; - case 'invoice_template': + case 'template': default: - //q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ['contract_id','tt_type','tt_viewmode','tt_sourcestate','tt_output_type','tt_id']) -%]'; - q_template = '[%- c.uri_for_action("/invoice/invoice_template_view", ["contract_id"]) -%]'+'/tt_type/tt_viewmode/tt_sourcestate/tt_output_type/tt_id'; + //q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id','tt_type','tt_viewmode','tt_sourcestate','tt_output_type','tt_id']) -%]'; + q_template = '[%- c.uri_for_action("/invoice/template_view", ['provider_id']) -%]'+'/tt_type/tt_viewmode/tt_sourcestate/tt_output_type/tt_id'; break; } if(!data.tt_id){data.tt_id = '';} if(!data.tt_type){data.tt_type = 'svg';} if(!data.tt_output_type){data.tt_output_type = 'svg';} if(!data.tt_viewmode){data.tt_viewmode = 'both';} - var params = ['contract_id','tt_id','tt_type','tt_output_type','tt_viewmode','tt_sourcestate']; + var params = ['provider_id','tt_id','tt_type','tt_output_type','tt_viewmode','tt_sourcestate']; params.forEach(function(key){ q_template=q_template.replace(key,data[key]); }); @@ -65,7 +65,10 @@ var uriForAction = function( data, type ){ [% c.loc('Back') %] - [% c.loc('Create invoices template')%] + [% c.loc('Create invoices template')%] + + + [% c.loc('Create invoices template')%] @@ -80,11 +83,11 @@ var uriForAction = function( data, type ){
[% back_created = 1 -%] -
+
@@ -103,7 +106,7 @@ var uriForAction = function( data, type ){ ]; helper.name_single = c.loc('Invoice Connections Records'); helper.identifier = 'invoice_details_zones_ajax'; - helper.ajax_uri = c.uri_for_action( '/invoice/ajax_allmighty', [ provider.id, 'invoice_details_zones' ] ) ; + helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_details_zones' ] ) ; initHelperAuto(); PROCESS 'helpers/datatables.tt'; -%] @@ -115,7 +118,7 @@ var uriForAction = function( data, type ){
@@ -149,8 +152,7 @@ oTable.fnClose(); ]; helper.name_single = c.loc('Invoice calls'); helper.identifier = 'invoice_details_calls_ajax'; - helper.id_from_name = 'start_time'; - helper.ajax_uri = c.uri_for_action( '/invoice/ajax_allmighty', [ provider.id, 'invoice_details_calls' ] ) ; + helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_details_calls' ] ) ; initHelperAuto(); PROCESS 'helpers/datatables.tt'; -%] @@ -164,23 +166,23 @@ oTable.fnClose();
-
-
- [%PROCESS 'invoice/invoice_template_list.tt' %] +
+
+ [%PROCESS 'invoice/template_list.tt' %]
- -
+
diff --git a/share/templates/invoice/invoice_template_aux_embedimage.tt b/share/templates/invoice/template_editor_aux_embedimage.tt similarity index 100% rename from share/templates/invoice/invoice_template_aux_embedimage.tt rename to share/templates/invoice/template_editor_aux_embedimage.tt diff --git a/share/templates/invoice/invoice_template_editor_form.tt b/share/templates/invoice/template_editor_form.tt similarity index 75% rename from share/templates/invoice/invoice_template_editor_form.tt rename to share/templates/invoice/template_editor_form.tt index 9676292f34..382d78fdc0 100644 --- a/share/templates/invoice/invoice_template_editor_form.tt +++ b/share/templates/invoice/template_editor_form.tt @@ -1,16 +1,22 @@ [%# USE FillInForm %] -
+ - + [% c.loc('Refresh Preview')%] @@ -18,7 +24,7 @@ function getTtIdVal(){ [% c.loc('Save template')%] @@ -27,21 +33,21 @@ function getTtIdVal(){ [% c.loc('Load default')%] [% c.loc('Load saved')%]
@@ -50,7 +56,7 @@ function getTtIdVal(){ tt_sourcestate: 'previewed', tt_output_type : 'svg', tt_id: getTtIdVal(), - contract_id: '[%provider.id%]', + provider_id: '[%provider.id%]', }),'_blank');void(0);">[% c.loc('Previewed SVG')%] @@ -58,7 +64,7 @@ function getTtIdVal(){ tt_sourcestate: 'previewed', tt_output_type : 'pdf', tt_id: getTtIdVal(), - contract_id: '[%provider.id%]', + provider_id: '[%provider.id%]', }),'_blank');void(0);">[% c.loc('Previewed PDF')%] @@ -66,7 +72,7 @@ function getTtIdVal(){ tt_sourcestate: 'saved', tt_output_type : 'svg', tt_id: getTtIdVal(), - contract_id: '[%provider.id%]', + provider_id: '[%provider.id%]', }),'_blank');void(0);">[% c.loc('Saved SVG')%] @@ -74,7 +80,7 @@ function getTtIdVal(){ tt_sourcestate: 'saved', tt_output_type : 'pdf', tt_id: getTtIdVal(), - contract_id: '[%provider.id%]', + provider_id: '[%provider.id%]', }),'_blank');void(0);">[% c.loc('Saved PDF')%] [%initial = 'default'%] @@ -86,10 +92,10 @@ function getTtIdVal(){ fetchSvgToEditor({ tt_viewmode: 'raw', tt_sourcestate: '[%initial%]', - contract_id: '[%provider.id%]', + provider_id: '[%provider.id%]', tt_id: getTtIdVal(), });" width="550px" height="750px" style="border-width:0px;">
\ No newline at end of file diff --git a/share/templates/invoice/invoice_template_info_form.tt b/share/templates/invoice/template_info_form.tt similarity index 100% rename from share/templates/invoice/invoice_template_info_form.tt rename to share/templates/invoice/template_info_form.tt diff --git a/share/templates/invoice/template_list.tt b/share/templates/invoice/template_list.tt new file mode 100644 index 0000000000..ddb0270c91 --- /dev/null +++ b/share/templates/invoice/template_list.tt @@ -0,0 +1,63 @@ +[% USE Dumper %] +[% USE format %] +[% money_format = format('%.2f') %] +[% IF !c.user.read_only && (c.user.roles == 'admin' || c.user.roles == 'reseller') -%] +[% write_access = 1 %] +[%END%] + +[%IF template_list.size %] + + + + + + + + + + + + [% FOR template IN template_list -%] + [%# Dumper.dump_html(template_list.as_query)%] + [%# Dumper.dump_html(template)%] + + + + + + + + [%END%] + +
[% c.loc('Active') %][% c.loc('Id') %][% c.loc('Name') %][% c.loc('Type') %]
[%IF template.get_column('is_active') > 0; '✓'; END%][% template.get_column('id') %][% template.get_column('name') %][% template.get_column('type') %] + +
+[%END%] + + \ No newline at end of file