diff --git a/lib/NGCP/Panel/Controller/Reseller.pm b/lib/NGCP/Panel/Controller/Reseller.pm index d8a49773da..7a5982dabf 100644 --- a/lib/NGCP/Panel/Controller/Reseller.pm +++ b/lib/NGCP/Panel/Controller/Reseller.pm @@ -318,8 +318,10 @@ sub _handle_reseller_status_change { sub details :Chained('base') :PathPart('details') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) { my ($self, $c) = @_; #didn't find a way to make it correct with chain - $c->forward('invoice_details'); - #$self->invoice_details($c); + $c->forward('invoice_details_zones'); + $c->forward('invoice_details_calls'); + #$self->invoice_details_zones($c); + #$self->invoice_details_calles($c); $c->stash(template => 'reseller/details.tt'); return; } @@ -429,9 +431,9 @@ sub messages :Chained('list_reseller') :PathPart('messages') :Args(0) { $c->log->debug('messages'); $c->stash( messages => $c->flash->{messages} ); $c->stash( template => 'helpers/ajax_messages.tt' ); - $c->detach( $c->view('SVG') ); + $c->detach( $c->view('SVG') );#no wrapper view } -sub invoice_details :Chained('base') :PathPart('invoice') :CaptureArgs(0) { +sub invoice_details_zones :Chained('base') :PathPart('invoice') :CaptureArgs(0) { my ($self, $c) = @_; $c->log->debug('invoice_details'); my $contract_id = $c->stash->{contract}->id; @@ -440,29 +442,63 @@ sub invoice_details :Chained('base') :PathPart('invoice') :CaptureArgs(0) { #look, NGCP::Panel::Utils::Contract - it is kind of backend separation here #my $form = NGCP::Panel::Form::InvoiceTemplate::Basic->new( ); - my $invoice_details = NGCP::Panel::Utils::Contract::get_contract_calls_rs( + my $invoice_details_zones = NGCP::Panel::Utils::Contract::get_contract_zonesfees_rs( + c => $c, + contract_id => $contract_id, + stime => $stime, + etime => $etime, + ); + #TODO: FAKE FAKE FAKE FAKE + my $invoice_details_zones_raw = $invoice_details_zones; + $invoice_details_zones = [$invoice_details_zones_raw->all()]; + my $i = 1; + $invoice_details_zones = [map{[$i++,$_]} (@$invoice_details_zones) x 21]; + $c->stash( invoice_details_zones => $invoice_details_zones ); + $c->stash( invoice_details_zones_raw => $invoice_details_zones_raw ); +} +sub invoice_details_calls :Chained('invoice_details_zones') :PathPart('') :CaptureArgs(0) { + my ($self, $c) = @_; + $c->log->debug('invoice_details_calls'); + my $contract_id = $c->stash->{contract}->id; + my $stime = NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month'); + my $etime = $stime->clone->add(months => 1); + + #look, NGCP::Panel::Utils::Contract - it is kind of backend separation here + #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, stime => $stime, etime => $etime, ); #TODO: FAKE FAKE FAKE FAKE - my $invoice_details_raw = $invoice_details; - $invoice_details = [$invoice_details_raw->all()]; + my $invoice_details_calls_raw = $invoice_details_calls; + $invoice_details_calls = [$invoice_details_calls_raw->all()]; my $i = 1; - $invoice_details = [map{[$i++,$_]} (@$invoice_details) x 21]; - $c->stash( invoice_details => $invoice_details ); - $c->stash( invoice_details_raw => $invoice_details_raw ); + $invoice_details_calls = [map{[$i++,$_]} (@$invoice_details_calls) x 21]; + $c->stash( invoice_details_calls => $invoice_details_calls ); + $c->stash( invoice_details_calls_raw => $invoice_details_calls_raw ); +} +sub invoice_details_zones_ajax :Chained('base') :PathPart('invoice/details/zones/ajax') :Args(0) { + my ($self, $c) = @_; + my $dt_columns_json = $c->request->parameters->{dt_columns}; + #use irka; + #use Data::Dumper; + #irka::loglong(Dumper($dt_columns)); + $c->forward( 'invoice_details_zones' ); + my $dt_columns = from_json($dt_columns_json); + NGCP::Panel::Utils::Datatables::process($c, $c->stash->{invoice_details_zones_raw}, $dt_columns ); + $c->detach( $c->view("JSON") ); } -sub invoice_details_ajax :Chained('base') :PathPart('invoice/details/ajax') :Args(0) { +sub invoice_details_calls_ajax :Chained('base') :PathPart('invoice/details/calls/ajax') :Args(0) { my ($self, $c) = @_; my $dt_columns_json = $c->request->parameters->{dt_columns}; #use irka; #use Data::Dumper; #irka::loglong(Dumper($dt_columns)); - $c->forward( 'invoice_details' ); + $c->forward( 'invoice_details_calls' ); my $dt_columns = from_json($dt_columns_json); - NGCP::Panel::Utils::Datatables::process($c, $c->stash->{invoice_details_raw}, $dt_columns ); + NGCP::Panel::Utils::Datatables::process($c, $c->stash->{invoice_details_calls_raw}, $dt_columns ); $c->detach( $c->view("JSON") ); } sub invoice_template_info :Chained('base') :PathPart('invoice/template/info') :Args(0) { @@ -659,7 +695,7 @@ sub invoice_template_delete :Chained('base') :PathPart('invoice_template/delete' $c->forward( 'invoice_template_list' ); } -sub invoice_template_list_data :Chained('invoice_details') :PathPart('') :CaptureArgs(0) { +sub invoice_template_list_data :Chained('invoice_details_zones') :PathPart('') :CaptureArgs(0) { my ($self, $c) = @_; $c->log->debug('invoice_template_list_data'); my($validator,$backend,$in,$out); @@ -681,7 +717,7 @@ sub invoice :Chained('invoice_template_list_data') :PathPart('') :Args(0) { $c->stash(template => 'invoice/invoice.tt'); } -sub invoice_template :Chained('invoice_details') :PathPart('template') :Args { +sub invoice_template :Chained('invoice_details_calls') :PathPart('template') :Args { my ($self, $c) = @_; $c->log->debug('invoice_template'); no warnings 'uninitialized'; diff --git a/lib/NGCP/Panel/Utils/Contract.pm b/lib/NGCP/Panel/Utils/Contract.pm index 2f6dd247cc..2212e45a72 100644 --- a/lib/NGCP/Panel/Utils/Contract.pm +++ b/lib/NGCP/Panel/Utils/Contract.pm @@ -260,7 +260,7 @@ sub get_contract_calls_rs{ # source_user_id => { 'in' => [ map {$_->uuid} @{$contract->{subscriber}} ] }, call_status => 'ok', source_user_id => { '!=' => '0' }, - source_account_id => $contract_id, +# source_account_id => $contract_id, # start_time => # [ -and => # { '>=' => $stime->epoch}, @@ -282,8 +282,10 @@ sub get_contract_calls_rs{ { 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/], + '+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', } ); diff --git a/share/static/js/libs/svg-edit/extensions/allowedMimeTypes.php b/share/static/js/libs/svg-edit/extensions/allowedMimeTypes.php deleted file mode 100644 index 1e932b841b..0000000000 --- a/share/static/js/libs/svg-edit/extensions/allowedMimeTypes.php +++ /dev/null @@ -1,11 +0,0 @@ - 'image/svg+xml;charset=utf-8', - 'png' => 'image/png', - 'jpeg' => 'image/jpeg', - 'bmp' => 'image/bmp', - 'webp' => 'image/webp' -); - -?> \ No newline at end of file diff --git a/share/static/js/libs/svg-edit/extensions/fileopen.php b/share/static/js/libs/svg-edit/extensions/fileopen.php deleted file mode 100644 index f0ae937ba5..0000000000 --- a/share/static/js/libs/svg-edit/extensions/fileopen.php +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - diff --git a/share/static/js/libs/svg-edit/extensions/filesave.php b/share/static/js/libs/svg-edit/extensions/filesave.php deleted file mode 100644 index 84d7300a40..0000000000 --- a/share/static/js/libs/svg-edit/extensions/filesave.php +++ /dev/null @@ -1,60 +0,0 @@ - 0) { - $file = $_POST['filename'] . $suffix; -} else { - $file = 'image' . $suffix; -} - -if ($suffix == '.svg') { - $contents = $_POST['output_svg']; -} else { - $contents = $_POST['output_img']; - $pos = (strpos($contents, 'base64,') + 7); - $contents = base64_decode(substr($contents, $pos)); -} - -header("Cache-Control: public"); -header("Content-Description: File Transfer"); - -// See http://tools.ietf.org/html/rfc6266#section-4.1 -header("Content-Disposition: attachment; filename*=UTF-8''" . encodeRFC5987ValueChars( - // preg_replace('@[\\\\/:*?"<>|]@', '', $file) // If we wanted to strip Windows-disallowed characters server-side (but not a security issue, so we can strip client-side instead) - $file -)); -header("Content-Type: " . $mime); -header("Content-Transfer-Encoding: binary"); - -echo $contents; - -?> \ No newline at end of file diff --git a/share/static/js/libs/svg-edit/extensions/savefile.php b/share/static/js/libs/svg-edit/extensions/savefile.php deleted file mode 100644 index 2efd90b459..0000000000 --- a/share/static/js/libs/svg-edit/extensions/savefile.php +++ /dev/null @@ -1,16 +0,0 @@ -|]@u', '_', $_POST['filename']) : 'saved') . '.svg'; // These characters are indicated as prohibited by Windows - - $fh = fopen($filename, 'w') or die("Can't open file"); - fwrite($fh, $svg); - fclose($fh); -?> diff --git a/share/templates/invoice/invoice.tt b/share/templates/invoice/invoice.tt index 1d068ba6b5..236adfbb4f 100644 --- a/share/templates/invoice/invoice.tt +++ b/share/templates/invoice/invoice.tt @@ -85,10 +85,41 @@ var uriForAction = function( data, type ){
- [% c.loc('Invoice details') %] + [% c.loc('Invoice details') %]
-
+
+
+ + +[% + 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_raw'; + helper.ajax_uri = c.uri_for_action( '/reseller/invoice_details_zones_ajax', [ contract.id ] ) ; + initHelperAuto(); + PROCESS 'helpers/datatables.tt'; +-%] + [%PROCESS 'invoice/invoice_details_zones_list.tt' %] + +
+
+
+ +
+ + +
@@ -96,19 +127,19 @@ var uriForAction = function( data, type ){ clearHelper(); helper.name = c.loc('Invoice Details'); helper.dt_columns = [ - { name => 'zone', title => c.loc('Zone'), search=> 1 }, - { name => 'number', title => c.loc('Calls amount') }, - { name => 'duration', title => c.loc('Duration') }, + { 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('Cash') }, + { name => 'cost', title => c.loc('Amount EUR') }, ]; - helper.name_single = c.loc('Invoice Record'); - helper.identifier = 'invoice_details_raw'; - helper.ajax_uri = c.uri_for_action( '/reseller/invoice_details_ajax', [ contract.id ] ) ; + helper.name_single = c.loc('Invoice Connections Records'); + helper.identifier = 'invoice_details_zones_raw'; + helper.ajax_uri = c.uri_for_action( '/reseller/invoice_details_zones_ajax', [ contract.id ] ) ; initHelperAuto(); PROCESS 'helpers/datatables.tt'; -%] - [%PROCESS 'invoice/invoice_details_list.tt' %] + [%PROCESS 'invoice/invoice_details_zones_list.tt' %]
diff --git a/share/templates/invoice/invoice_details_calls_list.tt b/share/templates/invoice/invoice_details_calls_list.tt new file mode 100644 index 0000000000..4fd1088431 --- /dev/null +++ b/share/templates/invoice/invoice_details_calls_list.tt @@ -0,0 +1,42 @@ +[%# USE Dumper %] +[%# Dumper.dump(invoice_details_zones)%] +[%# invoice_details_zones.size%] + + + + + + + + + + + + + + + + [%# Dumper.dump_html(invoice_details_zones.as_query)%] + [% FOR call IN invoice_details_zones -%] + [%IF call.1; row_number = call.0; call = call.1; END%] + [% total_duration = total_duration + call.get_column('duration') %] + [% total_cost = total_cost + call.get_column('cost') %] + + + + + + + + + + [%END%] + + + + + + + + +
[% c.loc('Num') %][% c.loc('Start time') %][% c.loc('Duration') %][% c.loc('Destination') %][% c.loc('Type') %][% c.loc('Zone/Details') %][% c.loc('Cash') %]
[% row_number %]
[% call.get_column('start_time') %]
[% call.get_column('duration')|format('%.3f') %]
[% call.get_column('destination')%][%#to mscro or view .replace('(.*?)\d{4}$','$1****') %]
[% call.get_column('call_type') %]
[% call.get_column('zone') _ '/' _ call.get_column('detail') %]
[% money_format( call.get_column('cost') / 100 ) %]
[% c.loc('Total') %]
[% total_number %]
[% total_duration | format('%.3f') %]
[% total_free_time | format('%d')%]
[% money_format( total_cost / 100 ) %]
diff --git a/share/templates/invoice/invoice_details_list.tt b/share/templates/invoice/invoice_details_zones_list.tt similarity index 79% rename from share/templates/invoice/invoice_details_list.tt rename to share/templates/invoice/invoice_details_zones_list.tt index 11ae6c2d67..afa3354a25 100644 --- a/share/templates/invoice/invoice_details_list.tt +++ b/share/templates/invoice/invoice_details_zones_list.tt @@ -1,22 +1,22 @@ [%# USE Dumper %] -[%# Dumper.dump(invoice_details)%] -[%# invoice_details.size%] +[%# Dumper.dump(invoice_details_zones)%] +[%# invoice_details_zones.size%] - - - + + + - [%# Dumper.dump_html(invoice_details.as_query)%] - [% FOR call IN invoice_details -%] + [%# Dumper.dump_html(invoice_details_zones.as_query)%] + [% FOR call IN invoice_details_zones -%] [%IF call.1; row_number = call.0; call = call.1; END%] [% total_number = total_number + call.get_column('number') %] [% total_duration = total_duration + call.get_column('duration') %] @@ -24,7 +24,7 @@ [% total_cost = total_cost + call.get_column('cost') %] - + diff --git a/share/templates/invoice/invoice_template_aux.tt b/share/templates/invoice/invoice_template_aux.tt index b1b339c86c..9b72309054 100644 --- a/share/templates/invoice/invoice_template_aux.tt +++ b/share/templates/invoice/invoice_template_aux.tt @@ -50,10 +50,10 @@ [%#data can be empty, if we just need y - it doesn't depend on data %] [%IF pagetype == 'titlepage' %] [%page = titlepage()%] - [%ELSIF pagetype == 'midpage' %] - [%page = midpage()%] - [%ELSIF pagetype == 'lastpage' %] - [%page = lastpage()%] + [%ELSIF pagetype == 'zonepage' %] + [%page = zonepage()%] + [%ELSIF pagetype == 'callpage' %] + [%page = callpage()%] [%END%] [%page%] [%END%] @@ -87,66 +87,112 @@ [%END -%] [%END -%] -[%MACRO get_page_rows_number(pagetype, tt_type, row_vertical_interval) BLOCK-%] +[%MACRO get_page_zonerows_number(pagetype, tt_type, row_vertical_interval) BLOCK-%] [%#doesn't work %] - [% page_rows_re = page_rows_re(tt_type) %] + [% page_zonerows_re = page_zonerows_re(tt_type) %] [% IF tt_type == 'svg' -%] [%# page_rows_re = '(?si)]*(?:\s+rows\s*=.*?(\d+))?[^>]*\s+id\s*=.*?(?:page_invoicedetails)[^>]*(?:\s+rows\s*=.*?(\d+))?[^>]*>' -%] - [% page_rows_re = 'rows="([0-9]+)"' -%] + [% page_rows_re = 'zonerows="([0-9]+)"' -%] [%END%] [% page = get_page(pagetype) %] [% matches = page.match( page_rows_re ) -%] - [%#page=Dumper.dump(page)%] - [%#page_rows_re=Dumper.dump(page_rows_re)%] - [%#matches=Dumper.dump(matches)%] - - [% rows = matches.0 || matches.1 %] - [%#IF !rows%] - [%# all_g_y_re = page_y_re(tt_type) %] - [%# matches = page.match( page_rows_re ) -%] - [%#END%] - [%#IF ! rows %][%# rows = 10%][%#END%] + [%rows = matches.0 || matches.1 %] [%rows = Math.int(rows)%] [%rows%] [%END -%] -[%MACRO show_pages(invoice_details, pagetype, pagenum_in) BLOCK-%] - [% allrowsnumber = invoice_details.size() %] - [% titlerows = get_page_rows_number('titlepage','svg') %] - [%#IF titlerows== 0 ; titlerows = 10; END%] - [% midrows = get_page_rows_number('midpage','svg') %] - [%IF midrows == 0 ; midrows = 30 ; END%] - [% allmidpages = ( (allrowsnumber - titlerows) / midrows )|format('%d') %] - [% allmidrows = allmidpages * midrows %] - [% lastrows = allrowsnumber - allmidrows - titlerows %] +[%MACRO get_page_callrows_number(pagetype, tt_type, row_vertical_interval) BLOCK-%] + [%#doesn't work %] + [% page_rows_re = page_rows_re(tt_type) %] + [% IF tt_type == 'svg' -%] + [%# page_rows_re = '(?si)]*(?:\s+rows\s*=.*?(\d+))?[^>]*\s+id\s*=.*?(?:page_invoicedetails)[^>]*(?:\s+rows\s*=.*?(\d+))?[^>]*>' -%] + [% page_rows_re = 'callrows="([0-9]+)"' -%] + [%END%] + + [% page = get_page(pagetype) %] + [% matches = page.match( page_rows_re ) -%] + + [%rows = matches.0 || matches.1 %] + [%rows = Math.int(rows)%] + [%rows%] +[%END -%] + +[%MACRO show_pages(invoice_details_zones, invoice_details_calls, pagetype, pagenum_in) BLOCK-%] +[%#todo: remove copypast with some macro, later%] + [% allzonerowsnumber = invoice_details_zones.size() %] + [% titlezonerows = get_page_zonerows_number('titlepage','svg') %] + [% midzonerows = get_page_zonerows_number('zonepage','svg') %] + [% midzonerows = ( midzonerows == 0 ) ? 30 : midzonerows %] + + [% allzonepages = ( (allzonerowsnumber - titlezonerows) / midzonerows )|format('%d') %] + [% allmidzonerows = allzonepages * midzonerows %] + [% lastzonerows = allzonerowsnumber - allmidzonerows - titlezonerows %] + + + [% allcallrowsnumber = invoice_details_calls.size() %] + [% titlecallrows = get_page_callrows_number('titlepage','svg') %] + [% midcallrows = get_page_callrows_number('callpage','svg') %] + [% midcallrows = ( midcallrows < 1 ) ? 30 : midcallrows %] + [% allcallpages = ( (allcallrowsnumber - titlecallrows) / midcallrows )|format('%d') %] + [% allmidcallrows = allcallpages * midcallrows %] + [% lastcallrows = allcallrowsnumber - allmidcallrows - titlecallrows %] + + [%IF ( pagetype == 'title' || pagetype=='all') %] [% pagenum = 1%] [% document_header()%] - [% titlepage( titlerows > 0 ? invoice_details.slice(0, titlerows - 1 ) : [], pagenum) -%] + + [% titlepage( + ( titlezonerows > 0 && invoice_details_zones.size > 0 ) ? invoice_details_zones.slice(0, titlezonerows - 1 ) : [], + + ( titlecallrows > 0 && invoice_details_calls.size > 0 ) ? invoice_details_calls.slice(0, titlecallrows - 1 ) : [], + pagenum + ) -%] [% bgpage(pagenum) -%] [% document_footer()%] [%END-%] - [%IF ( pagetype == 'mid' || pagetype=='all' ) && allmidrows %] - [% pages = pagenum_in ? [ pagenum_in ] : [ 1 .. allmidpages ] %] + [%IF ( pagetype == 'zone' || pagetype=='all' ) && allmidzonerows %] + [% pages = pagenum_in ? [ pagenum_in ] : [ 1 .. allzonepages ] %] [%FOREACH pagenum IN pages %] - [% pagerowsstart = titlerows + midrows * ( pagenum - 1 )%] - [% pagerowsend = titlerows + midrows * pagenum - 1 %] + [% pagerowsstart = titlezonerows + midzonerows * ( pagenum - 1 )%] + [% pagerowsend = titlezonerows + midzonerows * pagenum - 1 %] + [% document_header()%] + [% zonepage( invoice_details_zones.slice( pagerowsstart, pagerowsend ), pagenum + 1 ) -%] + [%#+1 because of 1 for titlepage %] + [% bgpage(pagenum + 1) -%] + [% document_footer()%] + [%END-%] + [%IF lastzonerows > 0 %] + [%#2 because zonepages started from 1, not from 0, and we need add 1 for titlepage %] + [% pagenum = 2 + allzonepages %] [% document_header()%] - [% midpage( invoice_details.slice( pagerowsstart, pagerowsend ), pagenum + 1 ) -%] - [% bgpage(pagenum + 1s) -%] + [% zonepage( invoice_details_zones.slice( allzonerowsnumber - lastzonerows, allzonerowsnumber ), pagenum ) -%] + [% bgpage(pagenum) -%] [% document_footer()%] [%END-%] [%END-%] - [%IF ( pagetype == 'last' || pagetype=='all' )%] - [%#2 because midpages started from 1, and we need add 1 for titlepage %] - [% pagenum = 2 + allmidpages %] - [% document_header()%] - [% lastpage( invoice_details.slice( allrowsnumber - lastrows, allrowsnumber ), pagenum ) -%] - [% bgpage(pagenum) -%] - [% document_footer()%] + [%IF ( pagetype == 'call' || pagetype=='all' )%] + [% pages = pagenum_in ? [ pagenum_in ] : [ 1 .. allcallpages ] %] + [%FOREACH pagenum IN pages %] + [% pagerowsstart = titlecallrows + midcallrows * ( pagenum - 1 )%] + [% pagerowsend = titlecallrows + midcallrows * pagenum - 1 %] + [% document_header()%] + [% callpage( invoice_details_calls.slice( pagerowsstart, pagerowsend ), pagenum + 1 ) -%] + [%#+1 because of 1 for titlepage %] + [% bgpage(pagenum + allzonepages + 1) -%] + [% document_footer()%] + [%END-%] + [%IF lastcallrows > 0 %] + [%#2 because callpages started from 1, not from 0, and we need add 1 for titlepage %] + [% pagenum = 2 + allcallpages %] + [% document_header()%] + [% callpage( invoice_details_calls.slice( allcallrowsnumber - lastcallrows, allcallrowsnumber ), pagenum ) -%] + [% bgpage(pagenum) -%] + [% document_footer()%] + [%END-%] [%END-%] [%END-%] diff --git a/share/templates/invoice/invoice_template_editor_form.tt b/share/templates/invoice/invoice_template_editor_form.tt index 13caeef721..447c55350c 100644 --- a/share/templates/invoice/invoice_template_editor_form.tt +++ b/share/templates/invoice/invoice_template_editor_form.tt @@ -1,5 +1,3 @@ -[%#It can be separated later to form template and loaded with ajax and fillinform template::toolkit plugin. %] -[%#It is reliable and easy to implement solution, but requires additional modules. For now populate small form with jquery.%] [%# USE FillInForm %]
[% c.loc('Num') %][% c.loc('Zone') %][% c.loc('Calls amount') %][% c.loc('Duration') %][% c.loc('Zone/Details') %][% c.loc('Calls') %][% c.loc('Usage') %] [% c.loc('Free time') %] [% c.loc('Cash') %]
[% row_number %][% call.get_column('zone') %][% call.get_column('zone') _ '/' _ call.get_column('zone_detail') %]
[% call.get_column('number') %]
[% call.get_column('duration')|format('%.3f') %]
[% call.get_column('free_time')|format('%d') %]