X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/ff159a4d47b9a2d10744dcfc23da2c63605c8a32..eeb5375ee7727c956cc357cc8f90b19d1bfe80b9:/SL/Template/OpenDocument.pm diff --git a/SL/Template/OpenDocument.pm b/SL/Template/OpenDocument.pm index 66d8f83ac..06dd6d973 100644 --- a/SL/Template/OpenDocument.pm +++ b/SL/Template/OpenDocument.pm @@ -7,20 +7,22 @@ use Encode; use HTML::Entities; use POSIX 'setsid'; use XML::LibXML; +use File::Basename; use SL::Iconv; use SL::Template::OpenDocument::Styles; -use SL::DB::BankAccount; use SL::Helper::QrBill; -use SL::Helper::ISO3166; +use SL::Helper::QrBillFunctions qw( + get_ref_number_formatted get_iban_formatted get_amount_formatted + get_street_name_from_address_line get_building_number_from_address_line +); use Cwd; # use File::Copy; # use File::Spec; # use File::Temp qw(:mktemp); use IO::File; -use List::Util qw(first); use strict; @@ -200,7 +202,7 @@ sub find_end { } elsif ((substr($text, $pos + 4, 4) eq 'else') && (1 == $depth)) { if (!$var) { - $self->{"error"} = '<%else%> outside of <%if%> / <%ifnot%>.'; + $self->{error} = '<%else%> outside of <%if%> / <%ifnot%>.'; return undef; } @@ -256,7 +258,7 @@ sub parse_block { ($table_row, $contents) = $self->find_end($contents, length($1)); if (!$table_row) { - $self->{"error"} = "Unclosed <\%foreachrow\%>." unless ($self->{"error"}); + $self->{error} = "Unclosed <\%foreachrow\%>." unless ($self->{"error"}); $main::lxdebug->leave_sub(); return undef; } @@ -311,7 +313,7 @@ sub parse_block { substr($contents, 0, $pos_foreach) = ""; if ($contents !~ m|^(\<\%foreach (.*?)\%\>)|) { - $self->{"error"} = "Malformed <\%foreach\%>."; + $self->{error} = "Malformed <\%foreach\%>."; $main::lxdebug->leave_sub(); return undef; } @@ -323,7 +325,7 @@ sub parse_block { my $block; ($block, $contents) = $self->find_end($contents); if (!$block) { - $self->{"error"} = "Unclosed <\%foreach\%>." unless ($self->{"error"}); + $self->{error} = "Unclosed <\%foreach\%>." unless ($self->{error}); $main::lxdebug->leave_sub(); return undef; } @@ -354,12 +356,18 @@ sub parse { my $self = $_[0]; local *OUT = $_[1]; - my $form = $self->{"form"}; + my $form = $self->{form}; close(OUT); + my $is_qr_bill = $::instance_conf->get_create_qrbill_invoices && + ($form->{formname} eq 'invoice' || + $form->{formname} eq 'invoice_for_advance_payment') && + $form->{template_meta}->{printer}->{template_code} =~ m/qr/ ? + 1 : 0; + my $qr_image_path; - if ($::instance_conf->get_create_qrbill_invoices && $form->{formname} eq 'invoice') { + if ($is_qr_bill) { # the biller account information, biller address and the reference number, # are needed in the template aswell as in the qr-code generation, therefore # assemble these and add to $::form @@ -367,22 +375,22 @@ sub parse { } my $file_name; - if ($form->{"IN"} =~ m|^/|) { - $file_name = $form->{"IN"}; + if ($form->{IN} =~ m|^/|) { + $file_name = $form->{IN}; } else { - $file_name = $form->{"templates"} . "/" . $form->{"IN"}; + $file_name = $form->{templates} . "/" . $form->{IN}; } my $zip = Archive::Zip->new(); if (Archive::Zip->AZ_OK != $zip->read($file_name)) { - $self->{"error"} = "File not found/is not a OpenDocument file."; + $self->{error} = "File not found/is not a OpenDocument file."; $main::lxdebug->leave_sub(); return 0; } my $contents = Encode::decode('utf-8-strict', $zip->contents("content.xml")); if (!$contents) { - $self->{"error"} = "File is not a OpenDocument file."; + $self->{error} = "File is not a OpenDocument file."; $main::lxdebug->leave_sub(); return 0; } @@ -435,7 +443,7 @@ sub parse { $zip->contents("styles.xml", Encode::encode('utf-8-strict', $new_styles)); } - if ($::instance_conf->get_create_qrbill_invoices && $form->{formname} eq 'invoice') { + if ($is_qr_bill) { # get placeholder path from odt XML my $qr_placeholder_path; my $dom = XML::LibXML->load_xml(string => $contents); @@ -457,10 +465,10 @@ sub parse { ); } - $zip->writeToFileNamed($form->{"tmpfile"}, 1); + $zip->writeToFileNamed($form->{tmpfile}, 1); my $res = 1; - if ($form->{"format"} =~ /pdf/) { + if ($form->{format} =~ /pdf/) { $res = $self->convert_to_pdf(); } @@ -468,256 +476,125 @@ sub parse { return $res; } -sub get_qrbill_account { - $main::lxdebug->enter_sub(); - my ($self) = @_; - - my $qr_account; - - my $bank_accounts = SL::DB::Manager::BankAccount->get_all; - $qr_account = scalar(@{ $bank_accounts }) == 1 ? - $bank_accounts->[0] : - first { $_->use_for_qrbill } @{ $bank_accounts }; - - if (!$qr_account) { - $::form->error($::locale->text('No bank account flagged for QRBill usage was found.')); - } - - $main::lxdebug->leave_sub(); - return $qr_account; -} - -sub remove_letters_prefix { - my $s = $_[0]; - $s =~ s/^[a-zA-Z]+//; - return $s; -} - -sub check_digits_and_max_length { - my $s = $_[0]; - my $length = $_[1]; - - return 0 if (!($s =~ /^\d*$/) || length($s) > $length); - return 1; -} - -sub calculate_check_digit { - # calculate ESR check digit using algorithm: "modulo 10, recursive" - my $ref_number_str = $_[0]; - - my @m = (0, 9, 4, 6, 8, 2, 7, 1, 3, 5); - my $carry = 0; - - my @ref_number_split = map int($_), split(//, $ref_number_str); - - for my $v (@ref_number_split) { - $carry = @m[($carry + $v) % 10]; - } - - return (10 - $carry) % 10; -} - -sub assemble_ref_number { - $main::lxdebug->enter_sub(); - - my $bank_id = $_[0]; - my $customer_number = $_[1]; - my $order_number = $_[2] // "0"; - my $invoice_number = $_[3] // "0"; - - # check values (analog to checks in makro) - # - bank_id - # input: 6 digits, only numbers - # output: 6 digits, only numbers - if (!($bank_id =~ /^\d*$/) || length($bank_id) != 6) { - $::form->error($::locale->text('Bank account id number invalid. Must be 6 digits.')); - } - - # - customer_number - # input: prefix (letters) + up to 6 digits (numbers) - # output: prefix removed, 6 digits, filled with leading zeros - $customer_number = remove_letters_prefix($customer_number); - if (!check_digits_and_max_length($customer_number, 6)) { - $::form->error($::locale->text('Customer number invalid. Must be less then or equal to 6 digits after prefix.')); - } - # fill with zeros - $customer_number = sprintf "%06d", $customer_number; - - # - order_number - # input: prefix (letters) + up to 7 digits, may be zero - # output: prefix removed, 7 digits, filled with leading zeros - $order_number = remove_letters_prefix($order_number); - if (!check_digits_and_max_length($order_number, 7)) { - $::form->error($::locale->text('Order number invalid. Must be less then or equal to 7 digits after prefix.')); - } - # fill with zeros - $order_number = sprintf "%07d", $order_number; - - # - invoice_number - # input: prefix (letters) + up to 7 digits, may be zero - # output: prefix removed, 7 digits, filled with leading zeros - $invoice_number = remove_letters_prefix($invoice_number); - if (!check_digits_and_max_length($invoice_number, 7)) { - $::form->error($::locale->text('Invoice number invalid. Must be less then or equal to 7 digits after prefix.')); - } - # fill with zeros - $invoice_number = sprintf "%07d", $invoice_number; - - # assemble ref. number - my $ref_number = $bank_id . $customer_number . $order_number . $invoice_number; - - # calculate check digit - my $ref_number_cpl = $ref_number . calculate_check_digit($ref_number); - - $main::lxdebug->leave_sub(); - return $ref_number_cpl; -} - -sub get_ref_number_formatted { - $main::lxdebug->enter_sub(); - - my $ref_number = $_[0]; - - # create ref. number in format: - # 'XX XXXXX XXXXX XXXXX XXXXX XXXXX' (2 digits + 5 x 5 digits) - my $ref_number_spaced = substr($ref_number, 0, 2) . ' ' . - substr($ref_number, 2, 5) . ' ' . - substr($ref_number, 7, 5) . ' ' . - substr($ref_number, 12, 5) . ' ' . - substr($ref_number, 17, 5) . ' ' . - substr($ref_number, 22, 5); - - $main::lxdebug->leave_sub(); - return $ref_number_spaced; -} - -sub get_iban_formatted { - $main::lxdebug->enter_sub(); - - my $iban = $_[0]; - - # create iban number in format: - # 'XXXX XXXX XXXX XXXX XXXX X' (5 x 4 + 1digits) - my $iban_spaced = substr($iban, 0, 4) . ' ' . - substr($iban, 4, 4) . ' ' . - substr($iban, 8, 4) . ' ' . - substr($iban, 12, 4) . ' ' . - substr($iban, 16, 4) . ' ' . - substr($iban, 20, 1); - - $main::lxdebug->leave_sub(); - return $iban_spaced; -} - -sub get_amount_formatted { - $main::lxdebug->enter_sub(); - - unless ($_[0] =~ /^\d+\.\d{2}$/) { - $::form->error($::locale->text('Amount has wrong format.')); - } - - local $_ = shift; - $_ = reverse split //; - m/^\d{2}\./g; - s/\G(\d{3})(?=\d)/$1 /g; - - $main::lxdebug->leave_sub(); - return scalar reverse split //; -} - sub generate_qr_code { $main::lxdebug->enter_sub(); my $self = $_[0]; - my $form = $self->{"form"}; + my $form = $self->{form}; # assemble data for QR-Code - # get qr-account data - my $qr_account = $self->get_qrbill_account(); + if (!$form->{qrbill_iban}) { + $::form->error($::locale->text('No bank account flagged for QRBill usage was found.')); + } my %biller_information = ( - 'iban' => $qr_account->{'iban'} + iban => $form->{qrbill_iban} ); - my $biller_countrycode = SL::Helper::ISO3166::map_name_to_alpha_2_code( - $::instance_conf->get_address_country() - ); - if (!$biller_countrycode) { + if (!$form->{qrbill_biller_countrycode}) { $::form->error($::locale->text('Error mapping biller countrycode.')); } my %biller_data = ( - 'address_type' => 'K', - 'company' => $::instance_conf->get_company(), - 'address_row1' => $::instance_conf->get_address_street1(), - 'address_row2' => $::instance_conf->get_address_zipcode() . ' ' . $::instance_conf->get_address_city(), - 'countrycode' => $biller_countrycode, + address_type => 'S', + company => $::instance_conf->get_company(), + street => get_street_name_from_address_line($::instance_conf->get_address_street1()), + street_no => get_building_number_from_address_line($::instance_conf->get_address_street1()), + postalcode => $::instance_conf->get_address_zipcode(), + city => $::instance_conf->get_address_city(), + countrycode => $form->{qrbill_biller_countrycode}, ); - my $amount; - if ($form->{'qrbill_without_amount'}) { + my ($amount, $amount_formatted); + if ($form->{qrbill_without_amount}) { $amount = ''; + $amount_formatted = ''; } else { - $amount = sprintf("%.2f", $form->parse_amount(\%::myconfig, $form->{'total'})); + $amount = $form->{qrbill_amount}; + + # format amount for template + $amount_formatted = get_amount_formatted($amount); + if (!$amount_formatted) { + $::form->error($::locale->text('Amount has wrong format.')); + } } my %payment_information = ( - 'amount' => $amount, - 'currency' => $form->{'currency'}, + amount => $amount, + currency => $form->{currency}, ); - my $customer_countrycode = SL::Helper::ISO3166::map_name_to_alpha_2_code($form->{'country'}); - if (!$customer_countrycode) { + # get address data from billing address if given, otherwise from invoice + my $street_name = get_street_name_from_address_line($form->{billing_address_id} ? + $form->{billing_address_street} : + $form->{street}); + my $building_number = get_building_number_from_address_line($form->{billing_address_id} ? + $form->{billing_address_street} : + $form->{street}); + my $postalcode = $form->{billing_address_id} ? + $form->{billing_address_zipcode} : + $form->{zipcode}; + my $city = $form->{billing_address_id} ? + $form->{billing_address_city} : + $form->{city}; + + # validate address data + if ($postalcode !~ m/^\d{4,}$/) { + $::form->error($::locale->text('Zipcode missing or wrong format.')); + } + if (!$city) { + $::form->error($::locale->text('No city given.')); + } + if (!$form->{qrbill_customer_countrycode}) { $::form->error($::locale->text('Error mapping customer countrycode.')); } + my %invoice_recipient_data = ( - 'address_type' => 'K', - 'name' => $form->{'name'}, - 'address_row1' => $form->{'street'}, - 'address_row2' => $form->{'zipcode'} . ' ' . $form->{'city'}, - 'countrycode' => $customer_countrycode, + address_type => 'S', + name => $form->{billing_address_id} ? + $form->{billing_address_name} : + $form->{name}, + street => $street_name, + street_no => $building_number, + postalcode => $postalcode, + city => $city, + countrycode => $form->{qrbill_customer_countrycode}, ); my %ref_nr_data; if ($::instance_conf->get_create_qrbill_invoices == 1) { - # generate ref.-no. with check digit - my $ref_number = assemble_ref_number( - $qr_account->{'bank_account_id'}, - $form->{'customernumber'}, - $form->{'ordnumber'}, - $form->{'invnumber'}, - ); + # fill reference number with zeros when printing preview (before booking) + my $reference_number = $form->{id} ? $form->{qr_reference} : '0' x 27; + %ref_nr_data = ( - 'type' => 'QRR', - 'ref_number' => $ref_number, + type => 'QRR', + ref_number => $reference_number, ); # get ref. number/iban formatted with spaces and set into form for template # processing - $form->{'ref_number'} = $ref_number; - $form->{'ref_number_formatted'} = get_ref_number_formatted($ref_number); + $form->{ref_number} = $reference_number; + $form->{ref_number_formatted} = get_ref_number_formatted($reference_number); } elsif ($::instance_conf->get_create_qrbill_invoices == 2) { %ref_nr_data = ( - 'type' => 'NON', - 'ref_number' => '', + type => 'NON', + ref_number => '', ); } else { $::form->error($::locale->text('Error getting QR-Bill type.')); } - # set into form for template processing - $form->{'biller_information'} = \%biller_information; - $form->{'biller_data'} = \%biller_data; - $form->{'iban_formatted'} = get_iban_formatted($qr_account->{'iban'}); - - # format amount for template - $form->{'amount_formatted'} = get_amount_formatted( - sprintf( - "%.2f", - $form->parse_amount(\%::myconfig, $form->{'total'}) - ) + my %additional_information = ( + unstructured_message => $form->{qr_unstructured_message} ); + # set into form for template processing + $form->{biller_information} = \%biller_information; + $form->{biller_data} = \%biller_data; + $form->{iban_formatted} = get_iban_formatted($form->{qrbill_iban}); + $form->{amount_formatted} = $amount_formatted; + $form->{unstructured_message} = $form->{qr_unstructured_message}; + # set outfile - my $outfile = $form->{"tmpdir"} . '/' . 'qr-code.png'; + my $outfile = $form->{tmpdir} . '/' . 'qr-code.png'; # generate QR-Code Image eval { @@ -727,6 +604,7 @@ sub generate_qr_code { \%payment_information, \%invoice_recipient_data, \%ref_nr_data, + \%additional_information ); $qr_image->generate($outfile); } or do { @@ -738,133 +616,6 @@ sub generate_qr_code { return $outfile; } -sub is_xvfb_running { - $main::lxdebug->enter_sub(); - - my ($self) = @_; - - local *IN; - my $dfname = $self->{"userspath"} . "/xvfb_display"; - my $display; - - $main::lxdebug->message(LXDebug->DEBUG2(), " Looking for $dfname\n"); - if ((-f $dfname) && open(IN, $dfname)) { - my $pid = ; - chomp($pid); - $display = ; - chomp($display); - my $xauthority = ; - chomp($xauthority); - close(IN); - - $main::lxdebug->message(LXDebug->DEBUG2(), " found with $pid and $display\n"); - - if ((! -d "/proc/$pid") || !open(IN, "/proc/$pid/cmdline")) { - $main::lxdebug->message(LXDebug->DEBUG2(), " no/wrong process #1\n"); - unlink($dfname, $xauthority); - $main::lxdebug->leave_sub(); - return undef; - } - my $line = ; - close(IN); - if ($line !~ /xvfb/i) { - $main::lxdebug->message(LXDebug->DEBUG2(), " no/wrong process #2\n"); - unlink($dfname, $xauthority); - $main::lxdebug->leave_sub(); - return undef; - } - - $ENV{"XAUTHORITY"} = $xauthority; - $ENV{"DISPLAY"} = $display; - } else { - $main::lxdebug->message(LXDebug->DEBUG2(), " not found\n"); - } - - $main::lxdebug->leave_sub(); - - return $display; -} - -sub spawn_xvfb { - $main::lxdebug->enter_sub(); - - my ($self) = @_; - - $main::lxdebug->message(LXDebug->DEBUG2, "spawn_xvfb()\n"); - - my $display = $self->is_xvfb_running(); - - if ($display) { - $main::lxdebug->leave_sub(); - return $display; - } - - $display = 99; - while ( -f "/tmp/.X${display}-lock") { - $display++; - } - $display = ":${display}"; - $main::lxdebug->message(LXDebug->DEBUG2(), " display $display\n"); - - my $mcookie = `mcookie`; - die("Installation error: mcookie not found.") if ($? != 0); - chomp($mcookie); - - $main::lxdebug->message(LXDebug->DEBUG2(), " mcookie $mcookie\n"); - - my $xauthority = "/tmp/.Xauthority-" . $$ . "-" . time() . "-" . int(rand(9999999)); - $ENV{"XAUTHORITY"} = $xauthority; - - $main::lxdebug->message(LXDebug->DEBUG2(), " xauthority $xauthority\n"); - - if (system("xauth add \"${display}\" . \"${mcookie}\"") == -1) { - die "system call to xauth failed: $!"; - } - if ($? != 0) { - $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started (xauth: $!)"; - $main::lxdebug->leave_sub(); - return undef; - } - - $main::lxdebug->message(LXDebug->DEBUG2(), " about to fork()\n"); - - my $pid = fork(); - if (0 == $pid) { - $main::lxdebug->message(LXDebug->DEBUG2(), " Child execing\n"); - exec($::lx_office_conf{applications}->{xvfb}, $display, "-screen", "0", "640x480x8", "-nolisten", "tcp"); - } - sleep(3); - $main::lxdebug->message(LXDebug->DEBUG2(), " parent dont sleeping\n"); - - local *OUT; - my $dfname = $self->{"userspath"} . "/xvfb_display"; - if (!open(OUT, ">", $dfname)) { - $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started ($dfname: $!)"; - unlink($xauthority); - kill($pid); - $main::lxdebug->leave_sub(); - return undef; - } - print(OUT "$pid\n$display\n$xauthority\n"); - close(OUT); - - $main::lxdebug->message(LXDebug->DEBUG2(), " parent re-testing\n"); - - if (!$self->is_xvfb_running()) { - $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started."; - unlink($xauthority, $dfname); - kill($pid); - $main::lxdebug->leave_sub(); - return undef; - } - - $main::lxdebug->message(LXDebug->DEBUG2(), " spawn OK\n"); - - $main::lxdebug->leave_sub(); - - return $display; -} - sub _run_python_uno { my ($self, @args) = @_; @@ -927,9 +678,9 @@ sub spawn_openoffice { my $ssres = setsid(); $main::lxdebug->message(LXDebug->DEBUG2(), " Child execing\n"); my @cmdline = ($::lx_office_conf{applications}->{openofficeorg_writer}, - "-minimized", "-norestore", "-nologo", "-nolockcheck", - "-headless", - "-accept=socket,host=localhost,port=" . + "--minimized", "--norestore", "--nologo", "--nolockcheck", + "--headless", + "--accept=socket,host=localhost,port=" . $::lx_office_conf{print_templates}->{openofficeorg_daemon_port} . ";urp;"); exec(@cmdline); } else { @@ -948,7 +699,7 @@ sub spawn_openoffice { } if (!$res) { - $self->{"error"} = "Conversion from OpenDocument to PDF failed because " . + $self->{error} = "Conversion from OpenDocument to PDF failed because " . "OpenOffice could not be started."; } @@ -962,30 +713,27 @@ sub convert_to_pdf { my ($self) = @_; - my $form = $self->{"form"}; + my $form = $self->{form}; - my $filename = $form->{"tmpfile"}; + my $filename = $form->{tmpfile}; $filename =~ s/.odt$//; if (substr($filename, 0, 1) ne "/") { $filename = getcwd() . "/${filename}"; } - if (substr($self->{"userspath"}, 0, 1) eq "/") { - $ENV{'HOME'} = $self->{"userspath"}; + if (substr($self->{userspath}, 0, 1) eq "/") { + $ENV{HOME} = $self->{userspath}; } else { - $ENV{'HOME'} = getcwd() . "/" . $self->{"userspath"}; + $ENV{HOME} = getcwd() . "/" . $self->{userspath}; } - if (!$self->spawn_xvfb()) { - $main::lxdebug->leave_sub(); - return 0; - } + my $outdir = dirname($filename); if (!$::lx_office_conf{print_templates}->{openofficeorg_daemon}) { if (system($::lx_office_conf{applications}->{openofficeorg_writer}, - "-minimized", "-norestore", "-nologo", "-nolockcheck", "-headless", - "file:${filename}.odt", - "macro://" . (split('/', $filename))[-1] . "/Standard.Conversion.ConvertSelfToPDF()") == -1) { + "--minimized", "--norestore", "--nologo", "--nolockcheck", "--headless", + "--convert-to", "pdf", "--outdir", $outdir, + "file:${filename}.odt") == -1) { die "system call to $::lx_office_conf{applications}->{openofficeorg_writer} failed: $!"; } } else { @@ -999,7 +747,7 @@ sub convert_to_pdf { my $res = $?; if ((0 == $?) || (-f "${filename}.pdf" && -s "${filename}.pdf")) { - $form->{"tmpfile"} =~ s/odt$/pdf/; + $form->{tmpfile} =~ s/odt$/pdf/; unlink($filename . ".odt"); @@ -1009,7 +757,7 @@ sub convert_to_pdf { } unlink($filename . ".odt", $filename . ".pdf"); - $self->{"error"} = "Conversion from OpenDocument to PDF failed. " . + $self->{error} = "Conversion from OpenDocument to PDF failed. " . "Exit code: $res"; $main::lxdebug->leave_sub(); @@ -1030,7 +778,7 @@ sub format_string { sub get_mime_type() { my ($self) = @_; - if ($self->{"form"}->{"format"} =~ /pdf/) { + if ($self->{form}->{format} =~ /pdf/) { return "application/pdf"; } else { return "application/vnd.oasis.opendocument.text";