parent
a30237b89e
commit
17d98b9faa
@ -1,123 +1,248 @@
|
||||
package NGCP::Panel::Utils::InvoiceTemplate;
|
||||
#it should be part of real model, or subcontroller
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
#use Moose;
|
||||
use Sipwise::Base;
|
||||
use File::Temp qw/tempfile tempdir/;
|
||||
use File::Path qw/mkpath/;
|
||||
use File::Temp;
|
||||
use XML::XPath;
|
||||
use IPC::System::Simple qw/capturex/;
|
||||
use Template;
|
||||
|
||||
sub getDefaultInvoiceTemplate{
|
||||
my (%in) = @_;
|
||||
#in future may be we will store root default in Db too, but now it is convenient to edit template as file
|
||||
my $result = $in{c}->view('SVG')->getTemplateContent($in{c}, 'invoice/invoice_template_'.$in{type}.'.tt');
|
||||
|
||||
#$in{c}->log->debug("result=$result;");
|
||||
|
||||
if( $result && exists $in{result} ){
|
||||
${$in{result}} = $result;
|
||||
}
|
||||
return \$result;
|
||||
}
|
||||
sub convertSvg2Pdf{
|
||||
my($c,$svg_ref,$in,$out) = @_;
|
||||
sub svg_pdf {
|
||||
my ($c,$svg_ref,$pdf_ref) = @_;
|
||||
my $svg = $$svg_ref;
|
||||
my(@pages) = $svg=~/(<svg.*?(?:\/svg>))/sig;
|
||||
no warnings 'uninitialized';
|
||||
#$c->log->debug($svg);
|
||||
my ($tempdirbase,$tempdir );
|
||||
#my($fh, $tempfilename) = tempfile();
|
||||
$tempdirbase = join('/',File::Spec->tmpdir,@$in{qw/provider_id tt_type tt_sourcestate/}, $out->{tt_id});
|
||||
! -e $tempdirbase and mkpath( $tempdirbase, 0, 0777 );
|
||||
$tempdir = tempdir( DIR => $tempdirbase , CLEANUP => 0 );
|
||||
#print "tempdirbase=$tempdirbase; tempdir=$tempdir;$!;\n\n\n";
|
||||
$c and $c->log->debug("tempdirbase=$tempdirbase; tempdir=$tempdir;");
|
||||
#try{
|
||||
#} catch($e){
|
||||
# NGCP::Panel::Utils::Message->error(
|
||||
# c => $c,
|
||||
# error => "Can't create temporary directory at: $tempdirbase;" ,
|
||||
# desc => $c->loc("Can't create temporary directory."),
|
||||
# );
|
||||
#}
|
||||
|
||||
my $dir = File::Temp->newdir(undef, CLEANUP => 0);
|
||||
my $tempdir = $dir->dirname;
|
||||
my $pagenum = 1;
|
||||
my @pagefiles;
|
||||
foreach my $page (@pages){
|
||||
|
||||
# file consists of multiple svg tags (invald!), split them up:
|
||||
my(@pages) = $svg=~/(<svg.*?(?:\/svg>))/sig;
|
||||
|
||||
foreach my $page(@pages) {
|
||||
my $fh;
|
||||
|
||||
my $pagefile = "$tempdir/$pagenum.svg";
|
||||
push @pagefiles, $pagefile;
|
||||
open($fh,">",$pagefile);
|
||||
#try{
|
||||
#} catch($e){
|
||||
# NGCP::Panel::Utils::Message->error(
|
||||
# c => $c,
|
||||
# error => "Can't create temporary page file at: $tempdirbase/$page.svg;" ,
|
||||
# desc => $c->loc("Can't create temporary file."),
|
||||
# );
|
||||
#}
|
||||
|
||||
my $xp = XML::XPath->new($page);
|
||||
my $g = $xp->find('//g[contains(@class,"firsty-") and contains(@class,"lasty")]');
|
||||
foreach my $node($g->get_nodelist) {
|
||||
my $class = $node->getAttribute('class');
|
||||
my $firsty = $class; my $lasty = $class;
|
||||
|
||||
$firsty =~ s/^.+firsty\-(\d+).*$/$1/;
|
||||
$lasty =~ s/^.+lasty\-(\d+).*$/$1/;
|
||||
if(length($firsty) && length($lasty)) {
|
||||
process_child_nodes($node, $firsty, $lasty);
|
||||
}
|
||||
}
|
||||
$page = ($xp->findnodes('/'))[0]->toString();
|
||||
|
||||
|
||||
open($fh, ">", $pagefile);
|
||||
binmode($fh, ":utf8");
|
||||
print $fh $page;
|
||||
close $fh;
|
||||
|
||||
$pagenum++;
|
||||
}
|
||||
#no unit specification in documentation. Cool!
|
||||
my $cmd = "rsvg-convert -h 849 -w 600 -a -f pdf ".join(" ", @pagefiles);
|
||||
#print $cmd;
|
||||
#die();
|
||||
$c and $c->log->debug($cmd);
|
||||
#$cmd = "chmod ugo+rwx $filename";
|
||||
#binmode(STDIN);
|
||||
#$out->{tt_string_pdf} = `$cmd`;
|
||||
{
|
||||
#$cmd = "fc-list";
|
||||
open B, "$cmd |";
|
||||
binmode B;
|
||||
local $/ = undef;
|
||||
$out->{tt_string_pdf} = <B>;
|
||||
$c and $c->log->error("Pipe: close: !=$!; ?=$?;");
|
||||
close B or ($? == 0 ) or ( $c and $c->log->error("Error closing rsvg pipe: close: $!;"));
|
||||
}
|
||||
|
||||
# For whatever reason, the pdf looks ok with zoom of 1.0 when
|
||||
# generated via rsvg-convert, but the print result is too big,
|
||||
# so we need to scale it down by 0.8 to get a mediabox of 595,842
|
||||
# when using 90dpi.
|
||||
# (it doesn't happen with inkscape, no idea what rsvg does)
|
||||
my @cmd_args = (qw/-a -f pdf -z 0.8/, @pagefiles);
|
||||
$$pdf_ref = capturex([0], "/usr/bin/rsvg-convert", @cmd_args);
|
||||
|
||||
return 1;
|
||||
}
|
||||
sub preprocessInvoiceTemplateSvg{
|
||||
my($in,$svg_ref) = @_;
|
||||
|
||||
sub preprocess_svg {
|
||||
my($svg_ref) = @_;
|
||||
|
||||
my $xp = XML::XPath->new($$svg_ref);
|
||||
|
||||
my $g = $xp->find('//g[@id[contains(.,"page")]]');
|
||||
my $g = $xp->find('//g[@class="page"]');
|
||||
foreach my $node($g->get_nodelist) {
|
||||
if($node->getAttribute('display')) {
|
||||
$node->removeAttribute('display');
|
||||
}
|
||||
}
|
||||
|
||||
if($in->{no_fake_data}) {
|
||||
my $comment = $xp->find('/comment()[contains(.,"invoice_template_lorem.tt")]');
|
||||
foreach my $node($comment->get_nodelist) {
|
||||
$node->getParentNode->removeChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
my $comment = $xp->find('//comment()[normalize-space(.) = "{}" or normalize-space(.) = "{ }"]');
|
||||
foreach my $node($comment->get_nodelist) {
|
||||
$node->getParentNode->removeChild($node);
|
||||
}
|
||||
|
||||
$$svg_ref = ($xp->findnodes('/'))[0]->toString();
|
||||
source_user => 'user',
|
||||
source_domain => 'example.org',
|
||||
source_cli => '1234567890',
|
||||
destination_user_in => "1".$_."1234567890",
|
||||
|
||||
#no warnings 'uninitialized';
|
||||
##print "1.\n\n\n\n\nsvg=".$out->{tt_string_prepared}.";";
|
||||
$$svg_ref=~s/(?:{\s*)?<!--{|}-->(?:\s*})?//gs;
|
||||
$$svg_ref=~s/<(g .*?)(?:display\s*=\s*["']*none["'[:blank:]]+)(.*?id *=["' ]+page[^"' ]*["' ]+)([^>]*)>/<$1$2$3>/gs;
|
||||
$$svg_ref=~s/<(g .*?)(id *=["' ]+page[^"' ]*["' ]+.*?)(?:display\s*=\s*["']*none["'[:blank:]]+)([^>]*)>/<$1$2$3>/gs;
|
||||
#if($in->{no_fake_data}){
|
||||
# $$svg_ref=~s/\[%[^\[\%]+lorem.*?%\]//gs;
|
||||
#}
|
||||
##print "\n\n2.\n\n\n\nsvg=".$out->{tt_string_prepared}.";";
|
||||
}
|
||||
|
||||
1;
|
||||
sub sanitize_svg {
|
||||
my ($svg_ref) = @_;
|
||||
|
||||
my $xp = XML::XPath->new($$svg_ref);
|
||||
|
||||
my $s = $xp->find('//script');
|
||||
foreach my $node($s->get_nodelist) {
|
||||
if($node->getAttribute('display')) {
|
||||
$node->getParentNode->removeChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
$$svg_ref = ($xp->findnodes('/'))[0]->toString();
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub get_tt {
|
||||
my $tt = Template->new({
|
||||
ENCODING => 'UTF-8',
|
||||
RELATIVE => 1,
|
||||
INCLUDE_PATH => './share/templates:/usr/share/ngcp-panel/templates',
|
||||
});
|
||||
$tt->context->define_vmethod(
|
||||
hash => get_column => sub {
|
||||
my($item,$col) = @_;
|
||||
if('HASH' eq ref $item){
|
||||
return $item->{$col};
|
||||
}
|
||||
}
|
||||
);
|
||||
return $tt;
|
||||
}
|
||||
|
||||
sub process_child_nodes {
|
||||
my ($node, $firsty, $y) = @_;
|
||||
for my $attr (qw/y y1 y2/) {
|
||||
my $a = $node->getAttribute($attr);
|
||||
if($a) {
|
||||
$a =~ s/^(\d+)\w*$/$1/;
|
||||
my $delta = $a - $firsty;
|
||||
my $newy = $y + $delta;
|
||||
|
||||
$node->removeAttribute($attr);
|
||||
$node->appendAttribute(XML::XPath::Node::Attribute->new($attr, $newy."mm"));
|
||||
}
|
||||
}
|
||||
my @children = $node->getChildNodes();
|
||||
foreach my $node(@children) {
|
||||
process_child_nodes($node, $firsty, $y);
|
||||
}
|
||||
}
|
||||
|
||||
sub get_dummy_data {
|
||||
return {
|
||||
rescontact => {
|
||||
gender => 'male',
|
||||
firstname => 'Resellerfirst',
|
||||
lastname => 'Resellerlast',
|
||||
comregnum => 'COMREG1234567890',
|
||||
company => 'Resellercompany Inc.',
|
||||
street => 'Resellerstreet 12/3',
|
||||
postcode => '12345',
|
||||
city => 'Resellercity',
|
||||
country => 'Resellercountry',
|
||||
phonenumber => '+1234567890',
|
||||
mobilenumber => '+2234567890',
|
||||
faxnumber => '+3234567890',
|
||||
iban => 'RESIBAN1234567890',
|
||||
bic => 'RESBIC1234567890',
|
||||
bankname => 'Resellerbank',
|
||||
vatnum => 'RESVAT1234567890',
|
||||
gpp0 => 'RESGPP0',
|
||||
gpp1 => 'RESGPP1',
|
||||
gpp2 => 'RESGPP2',
|
||||
gpp3 => 'RESGPP3',
|
||||
gpp4 => 'RESGPP4',
|
||||
gpp5 => 'RESGPP5',
|
||||
gpp6 => 'RESGPP6',
|
||||
gpp7 => 'RESGPP7',
|
||||
gpp8 => 'RESGPP8',
|
||||
gpp9 => 'RESGPP9',
|
||||
},
|
||||
customer => {
|
||||
id => int(rand(10000))+10000,
|
||||
external_id => 'Resext1234567890',
|
||||
},
|
||||
custcontact => {
|
||||
gender => 'male',
|
||||
firstname => 'Customerfirst',
|
||||
lastname => 'Customerlast',
|
||||
comregnum => 'COMREG1234567890',
|
||||
company => 'Customercompany Inc.',
|
||||
street => 'Customerstreet 12/3',
|
||||
postcode => '12345',
|
||||
city => 'Customercity',
|
||||
country => 'Customercountry',
|
||||
phonenumber => '+4234567890',
|
||||
mobilenumber => '+5234567890',
|
||||
faxnumber => '+6234567890',
|
||||
iban => 'CUSTIBAN1234567890',
|
||||
bic => 'CUSTBIC1234567890',
|
||||
vatnum => 'CUSTVAT1234567890',
|
||||
bankname => 'Customerbank',
|
||||
gpp0 => 'CUSTGPP0',
|
||||
gpp1 => 'CUSTGPP1',
|
||||
gpp2 => 'CUSTGPP2',
|
||||
gpp3 => 'CUSTGPP3',
|
||||
gpp4 => 'CUSTGPP4',
|
||||
gpp5 => 'CUSTGPP5',
|
||||
gpp6 => 'CUSTGPP6',
|
||||
gpp7 => 'CUSTGPP7',
|
||||
gpp8 => 'CUSTGPP8',
|
||||
gpp9 => 'CUSTGPP9',
|
||||
},
|
||||
billprof => {
|
||||
handle => 'BILPROF12345',
|
||||
name => 'Test Billing Profile',
|
||||
prepaid => 0,
|
||||
interval_charge => 29.90,
|
||||
interval_free_time => 2000,
|
||||
interval_free_cash => 0,
|
||||
interval_unit => 'month',
|
||||
interval_count => 1,
|
||||
currency => 'EUR',
|
||||
vat_rate => 20,
|
||||
vat_included => 0,
|
||||
},
|
||||
invoice => {
|
||||
period_start => NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month'),
|
||||
period_end => NGCP::Panel::Utils::DateTime::current_local()->truncate(to => 'month')->add(months => 1)->subtract(seconds => 1),
|
||||
serial => '1234567',
|
||||
amount_net => 12345,
|
||||
amount_vat => 12345*0.2,
|
||||
amount_total => 12345+(12345*0.2),
|
||||
},
|
||||
calls => [
|
||||
map {{
|
||||
start_time => time,
|
||||
source_customer_cost => int(rand(100000)),
|
||||
duration => int(rand(7200)) + 10,
|
||||
destination_user_in => "1".$_."1234567890",
|
||||
call_type => (qw/cfu cfb cft cfna/)[int(rand 4)],
|
||||
zone => "Zone $_",
|
||||
zone_detail => "Detail $_",
|
||||
}}(1 .. 50)
|
||||
],
|
||||
zones => {
|
||||
totalcost => int(rand(10000))+10000,
|
||||
data => [
|
||||
map {{
|
||||
number => int(rand(200)),
|
||||
cost => int(rand(100000)),
|
||||
duration => int(rand(10000)),
|
||||
free_time => 0,
|
||||
zone => "Zone $_",
|
||||
zone_detail => "Detail $_",
|
||||
}}(1 .. 5)
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
1;
|
||||
# vim: set tabstop=4 expandtab:
|
||||
|
||||
Loading…
Reference in new issue