From bc0ce4bee097e190f91fdbf0aff8a55690e6f559 Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Thu, 27 Mar 2014 13:52:32 +0100 Subject: [PATCH] MT#5879 Loading of template data to form and to server in progress. Other left: confirm on deletion )href is not used, would be good to implement using datatables at least for invoice data, and use dynamic invoice data for online generation. svg-edit: would be very nice to implement editing of rows amount and text in text boxes, and as a very cool feature - applying styles. But next step will be cron. --- lib/NGCP/Panel/Controller/Customer.pm | 44 +++-- .../Panel/Form/Customer/InvoiceTemplate.pm | 18 +- lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm | 55 +++--- share/layout/body.tt | 5 +- share/static/js/jquery.loadJSON.js | 168 ++++++++++++++++++ share/templates/customer/invoice.tt | 151 ++++++++++------ .../customer/invoice_template_list.tt | 18 +- 7 files changed, 352 insertions(+), 107 deletions(-) create mode 100644 share/static/js/jquery.loadJSON.js diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 814f2ccee4..6d9a42544e 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -826,7 +826,7 @@ sub invoice_data :Chained('base') :PathPart('invoice') :CaptureArgs(0) { stime => $stime, etime => $etime, ); - #FAKE FAKE FAKE FAKE + #TODO: FAKE FAKE FAKE FAKE $invoice_details = [$invoice_details->all()]; my $i = 1; $invoice_details = [map{[$i++,$_]} (@$invoice_details) x 21]; @@ -928,8 +928,7 @@ sub invoice_template :Chained('invoice_data') :PathPart('template') :Args { $validator = NGCP::Panel::Form::Customer::InvoiceTemplate->new; # $form->schema( $c->model('DB::InvoiceTemplate')->schema ); #to common form package ? removing is necessary due to FormHandler param presence evaluation - it is based on key presence, not on defined/not defined value - foreach ( keys %$in) { if(!( defined $in->{$_} )){ delete $in->{$_}; } }; - + $validator->remove_undef_in($in); #really, we don't need a form here at all #just use as already implemented fields checking and defaults applying @@ -1016,6 +1015,8 @@ sub invoice_template :Chained('invoice_data') :PathPart('template') :Args { $c->response->content_type('application/pdf'); }elsif($in->{tt_output_type} eq 'html'){ $c->response->content_type('text/html'); + }elsif($in->{tt_output_type} eq 'json'){ + $c->response->content_type('application/json'); }elsif($in->{tt_output_type}=~m'zip'){ $c->response->content_type('application/zip'); } @@ -1035,32 +1036,43 @@ sub invoice_template :Chained('invoice_data') :PathPart('template') :Args { #preShowInvoice #even better - to template filters #also to model - $out->{tt_string}=~s/(?:{\s*)?(?:\s*})?//gs; - $out->{tt_string}=~s/()($2.*?>))/$1$3/gs; + $out->{tt_string_prepared}=$out->{tt_string_stored}=$out->{tt_string}; + $out->{tt_string_prepared}=~s/(?:{\s*)?(?:\s*})?//gs; + $out->{tt_string_prepared}=~s/()($2.*?>))/$1$3/gs; } if( ($in->{tt_output_type} eq 'svg') || ( $in->{tt_output_type} eq 'html') ){ #$c->response->content_type('image/svg+xml'); - $c->stash( template => \$out->{tt_string} ); + $c->stash( template => \$out->{tt_string_prepared} ); $c->detach( $c->view('SVG') ); }elsif($in->{tt_output_type} eq 'json'){ + #method + $c->log->debug('prepare json'); + my $aaData = { template =>{ - raw => $out->{tt_string}, - parsed => $c->view('SVG')->getTemplateProcessed($c,\$out->{tt_string}, $c->stash ), + raw => $out->{tt_string_stored}, + parsed => $c->view('SVG')->getTemplateProcessed($c, \$out->{tt_string_prepared}, $c->stash ), }, - form => { + }; + #can be empty if we just load default + if($out->{tt_data}){ + $aaData->{form} = { tt_id => $out->{tt_data}->get_column('id'), + }; + foreach(qw/name is_active/){ + $aaData->{form}->{$_} = $out->{tt_data}->get_column($_); } - }; - foreach(qw/name is_active/){ - $aaData->{form}->{$_} = $out->{tt_data}->get_column($_); + }else{ + #if we didn't have tt_data - then we have empty form fields with applied defaults + $aaData->{form} = $in; } - #$c->stash( aaData => $out ); + $c->stash( aaData => $aaData ); $c->detach( $c->view('JSON') ); }elsif($in->{tt_output_type} eq 'pdf'){ + #method $c->response->content_type('application/pdf'); - my $svg = $c->view('SVG')->getTemplateProcessed($c,\$out->{tt_string}, $c->stash ); + my $svg = $c->view('SVG')->getTemplateProcessed($c,\$out->{tt_string_prepared}, $c->stash ); my(@pages) = $svg=~/())/sig; #$c->log->debug($svg); @@ -1111,10 +1123,10 @@ sub invoice_template :Chained('invoice_data') :PathPart('template') :Args { open B, "$cmd |"; binmode B; local $/ = undef; - $out->{tt_string} = ; + $out->{tt_string_pdf} = ; close B; } - $c->response->body($out->{tt_string}); + $c->response->body($out->{tt_string_pdf}); return; #$out->{tt_string} = `cat $filename `; } diff --git a/lib/NGCP/Panel/Form/Customer/InvoiceTemplate.pm b/lib/NGCP/Panel/Form/Customer/InvoiceTemplate.pm index d665643274..c8f8a5bcb4 100644 --- a/lib/NGCP/Panel/Form/Customer/InvoiceTemplate.pm +++ b/lib/NGCP/Panel/Form/Customer/InvoiceTemplate.pm @@ -5,8 +5,8 @@ extends 'NGCP::Panel::Form::ValidatorBase'; use Moose::Util::TypeConstraints; enum 'TemplateType' => [ qw/svg html/ ];#html -enum 'TemplateTypeOutput' => [ qw/svg html pdf svgzip htmlzip pdfzip/ ];#html -enum 'TemplateViewMode' => [ qw/raw parsed/ ]; +enum 'TemplateTypeOutput' => [ qw/svg html pdf json svgzip htmlzip pdfzip/ ];#html +enum 'TemplateViewMode' => [ qw/raw parsed both/ ]; enum 'TemplateSourceState' => [ qw/saved previewed default/ ]; #no Moose::Util::TypeConstraints; @@ -61,11 +61,23 @@ has_field 'contract_id' => ( ); has_field 'tt_id' => ( - type => 'Text', + type => 'Hidden', #default => \& #apply => [ { check => \&validate_tt_string } ], required => 0, ); +has_field 'name' => ( + type => 'Text', + default => '', + #apply => [ { check => \&validate_tt_string } ], + required => 0, +); +has_field 'is_active' => ( + type => 'Checkbox', + default => '0', + #apply => [ { check => \&validate_tt_string } ], + required => 0, +); 1; diff --git a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm index 6c572a6a6f..e58cb46008 100644 --- a/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm +++ b/lib/NGCP/Panel/Model/DB/InvoiceTemplate.pm @@ -1,24 +1,39 @@ package NGCP::Panel::Model::DB::InvoiceTemplate; use base NGCP::Panel::Model::DB::Base; -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/}; +use irka; +use Data::Dumper; - my $result = ''; - +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 $conditions = {}; + irka::loglong("getDefaultConditions: tt_id=$tt_id;\n"); #my $tt_record = $self->resultset('invoice_template')->search({ if($tt_id){ $conditions = { id => $tt_id }; }else{ $conditions = { reseller_id => $contract_id, + type => $tt_type, is_active => 1, - type => $tt_type }; } + return $conditions; +} +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/}; + + irka::loglong("getCustomerInvoiceTemplate: tt_id=$tt_id;\n"); + irka::loglong(Dumper(\%params)); + my $result = ''; + + my $conditions = $self->getDefaultConditions(\%params); + #my $tt_record = $self->resultset('invoice_template')->search({ my $tt_record = $self->schema->resultset('invoice_template')->search($conditions)->first; #here may be base64 decoding @@ -31,13 +46,13 @@ sub getCustomerInvoiceTemplate{ if( $result && exists $params{result} ){ ${$params{result}} = $result; } - return ( $tt_id, \$result, $tt_record );#sgorila hata, gori i saray + return ( $tt_id, \$result, $tt_record );#tt_record - sgorila hata, gori i saray } sub storeCustomerInvoiceTemplate{ 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 ($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 $tt_record = $self->resultset('invoice_template')->search({ $self->schema->txn_do(sub { @@ -49,11 +64,7 @@ sub storeCustomerInvoiceTemplate{ # 'base64_'.$tt_sourcestate => $$tt_string, # }); - my $tt_record = $self->schema->resultset('invoice_template')->search({ - reseller_id => $contract_id, - type => $tt_type, - is_active => 1, - })->first; + #here may be base64 decoding # $self->schema->resultset('ivoice_template')->search({ @@ -63,22 +74,22 @@ sub storeCustomerInvoiceTemplate{ # is_active => 0, # }); - if( !$tt_record ){ + if( !$tt_id ){ $self->schema->resultset('invoice_template')->create({ reseller_id => $contract_id, type => $tt_type, is_active => 1, + name => $name, 'base64_'.$tt_sourcestate => $$tt_string, }); }else{ - $self->schema->resultset('invoice_template')->update({ - reseller_id => $contract_id, - type => $tt_type, - is_active => 1, + my $conditions = $self->getDefaultConditions(\%params); + my $tt_record = $self->schema->resultset('invoice_template')->search($conditions); + $tt_record->update({ + is_active => $is_active, + name => $name, 'base64_'.$tt_sourcestate => $$tt_string, - id => $tt_record->get_column( 'id' ), - }, - { key => 'id' }); + }); } }); } diff --git a/share/layout/body.tt b/share/layout/body.tt index 305b40748d..c86f5210be 100644 --- a/share/layout/body.tt +++ b/share/layout/body.tt @@ -92,7 +92,7 @@ [% # vim: set tabstop=4 syntax=html expandtab: -%] diff --git a/share/static/js/jquery.loadJSON.js b/share/static/js/jquery.loadJSON.js new file mode 100644 index 0000000000..cef2a70281 --- /dev/null +++ b/share/static/js/jquery.loadJSON.js @@ -0,0 +1,168 @@ +/* +* File: jquery.loadJSON.js +* Version: 1.0.0. +* Author: Jovan Popovic +* +* Copyright 2011 Jovan Popovic, all rights reserved. +* +* This source file is free software, under either the GPL v2 license or a +* BSD style license, as supplied with this software. +* +* This source file is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. +* +* This file contains implementation of the JQuery templating engine that load JSON +* objects into the HTML code. It is based on Alexandre Caprais notemplate plugin +* with several enchancements that are added to this plugin. +*/ + +(function ($) { + $.fn.loadJSON = function (obj, options) { + + function setElementValue(element, value, name) { + var type = element.type || element.tagName; + if (type == null) + return; + type = type.toLowerCase(); + switch (type) { + + case 'radio': + if (value.toString().toLowerCase() == element.value.toLowerCase()) + $(element).attr("checked", "checked"); + break; + + case 'checkbox': + if (value) + $(element).attr("checked", "checked"); + break; + + case 'select-multiple': + var values = value.constructor == Array ? value : [value]; + for (var i = 0; i < element.options.length; i++) { + for (var j = 0; j < values.length; j++) { + element.options[i].selected |= element.options[i].value == values[j]; + } + } + break; + + case 'select': + case 'select-one': + case 'text': + case 'hidden': + $(element).attr("value", value); + break; + case 'a': + var href = $(element).attr("href"); + var iPosition = href.indexOf('?'); + if (iPosition > 0) // if parameters in the URL exists add new pair using & + href = href.substring(0, iPosition) + '?' + name + '=' + value; + else//otherwise attach pair to URL + href = href + '?' + name + '=' + value; + $(element).attr("href", href); + break; + case 'img': //Assumption is that value is in the HREF$ALT format + var iPosition = value.indexOf('$'); + var src = ""; + var alt = ""; + if (iPosition > 0) { + src = value.substring(0, iPosition); + alt = value.substring(iPosition + 1); + } + else { + src = value; + var iPositionStart = value.lastIndexOf('/')+1; + var iPositionEnd = value.indexOf('.'); + alt = value.substring(iPositionStart, iPositionEnd); + } + $(element).attr("src", src); + $(element).attr("alt", alt); + break; + + case 'textarea': + case 'submit': + case 'button': + default: + try { + $(element).html(value); + } catch (exc) { } + } + + } + + function browseJSON(obj, element, name) { + + // no object + if (obj == undefined) { + } + // branch + else if (obj.constructor == Object) { + for (var prop in obj) { + if (prop == null) + continue; + //Find an element with class, id, name, or rel attribute that matches the propertu name + var child = jQuery.makeArray(jQuery("." + prop, element)).length > 0 ? jQuery("." + prop, element) : + jQuery("#" + prop, element).length > 0 ? jQuery("#" + prop, element) : + jQuery('[name="' + prop + '"]', element).length > 0 ? jQuery('[name="' + prop + '"]', element) : jQuery('[rel="' + prop + '"]'); + if (child.length != 0) + browseJSON(obj[prop], jQuery(child, element), prop); + } + } + // array + else if (obj.constructor == Array) { + if (element.length > 0 && element[0].tagName == "SELECT") { + setElementValue(element[0], obj, name); + } else { + var arr = jQuery.makeArray(element); + //how many duplicate + var nbToCreate = obj.length - arr.length; + var i = 0; + for (iExist = 0; iExist < arr.length; iExist++) { + if (i < obj.length) { + $(element).eq(iExist).loadJSON(obj[i]); + } + i++; + } + //fill started by last + i = obj.length - 1; + for (iCreate = 0; iCreate < nbToCreate; iCreate++) { + //duplicate the last + $(arr[arr.length - 1]).clone(true).insertAfter(arr[arr.length - 1]).loadJSON(obj[i]); + i--; + } + } + } + // data only + else { + var value = obj; + var type; + if (element.length > 0) { + for (i = 0; i < element.length; i++) + setElementValue(element[i], obj, name); + } + else { + setElementValue(element, obj, name); + } + } + } //function browseJSON end + + var defaults = { + }; + + properties = $.extend(defaults, options); + + return this.each(function () { + + if (obj.constructor == String) { + var element = $(this); + $.get(obj, function (data) { + element.loadJSON(data); + }); + } + + else { + browseJSON(obj, this); + } + }); + }; +})(jQuery); \ No newline at end of file diff --git a/share/templates/customer/invoice.tt b/share/templates/customer/invoice.tt index 8c314bb14f..6954c5c5a0 100644 --- a/share/templates/customer/invoice.tt +++ b/share/templates/customer/invoice.tt @@ -18,15 +18,16 @@ + -
+
+
+ +
+ +
+
+
+
+ +
+ +
+
[% c.loc('Refresh Preview')%] - [% c.loc('Save template')%] + [% c.loc('Save template')%]
+ [%initial = 'default'%]