X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FForm.pm;h=fd79b96286a1220710304750b1927f11a2a318af;hb=549f187d3a2b1d15f96c4556714666ed954447bb;hp=e5b1e745906d36873c7973d646a3a89c905a3f5f;hpb=b8fa93b5d2cfff453fd8a82206d1a288407a8be6;p=kivitendo-erp.git
diff --git a/SL/Form.pm b/SL/Form.pm
index e5b1e7459..fd79b9628 100644
--- a/SL/Form.pm
+++ b/SL/Form.pm
@@ -42,11 +42,11 @@ use Carp;
use Data::Dumper;
use Carp;
-use Config;
use CGI;
use Cwd;
use Encode;
use File::Copy;
+use File::Temp ();
use IO::File;
use Math::BigInt;
use POSIX qw(strftime);
@@ -59,7 +59,9 @@ use SL::CVar;
use SL::DB;
use SL::DBConnect;
use SL::DBUtils;
+use SL::DB::AdditionalBillingAddress;
use SL::DB::Customer;
+use SL::DB::CustomVariableConfig;
use SL::DB::Default;
use SL::DB::PaymentTerm;
use SL::DB::Vendor;
@@ -87,6 +89,7 @@ use List::Util qw(first max min sum);
use List::MoreUtils qw(all any apply);
use SL::DB::Tax;
use SL::Helper::File qw(:all);
+use SL::Helper::Number;
use SL::Helper::CreatePDF qw(merge_pdfs);
use strict;
@@ -115,11 +118,6 @@ sub new {
return $self;
}
-sub read_cgi_input {
- my ($self) = @_;
- SL::Request::read_cgi_input($self);
-}
-
sub _flatten_variables_rec {
$main::lxdebug->enter_sub(2);
@@ -386,10 +384,12 @@ sub create_http_response {
my $session_cookie_value = $main::auth->get_session_id();
if ($session_cookie_value) {
- $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
- '-value' => $session_cookie_value,
- '-path' => $uri->path,
- '-secure' => $::request->is_https);
+ $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
+ '-value' => $session_cookie_value,
+ '-path' => $uri->path,
+ '-expires' => '+' . $::auth->{session_timeout} . 'm',
+ '-secure' => $::request->is_https);
+ $session_cookie = "$session_cookie; SameSite=strict";
}
}
@@ -437,6 +437,8 @@ sub header {
common part_selection
), "jquery/ui/i18n/jquery.ui.datepicker-$::myconfig{countrycode}");
+ $layout->use_javascript("$_.js") for @{ $params{use_javascripts} // [] };
+
$self->{favicon} ||= "favicon.ico";
$self->{titlebar} = join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->read_version if $self->{title} || !$self->{titlebar};
@@ -453,6 +455,7 @@ sub header {
push @header, " " if $self->{landscape};
push @header, "" if -f $self->{favicon};
push @header, map { qq|| } $layout->javascripts;
+ push @header, '';
push @header, $self->{javascript} if $self->{javascript};
push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
@@ -552,8 +555,10 @@ sub _prepare_html_template {
}
$language = "de" unless ($language);
- if (-f "templates/webpages/${file}.html") {
- $file = "templates/webpages/${file}.html";
+ my $webpages_path = $::request->layout->webpages_path;
+
+ if (-f "${webpages_path}/${file}.html") {
+ $file = "${webpages_path}/${file}.html";
} elsif (ref $file eq 'SCALAR') {
# file is a scalarref, use inline mode
@@ -703,112 +708,10 @@ sub sort_columns {
return @columns;
}
#
-sub format_amount {
- $main::lxdebug->enter_sub(2);
+sub format_amount {
my ($self, $myconfig, $amount, $places, $dash) = @_;
- $amount ||= 0;
- $dash ||= '';
- my $neg = $amount < 0;
- my $force_places = defined $places && $places >= 0;
-
- $amount = $self->round_amount($amount, abs $places) if $force_places;
- $neg = 0 if $amount == 0; # don't show negative zero
- $amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa
-
- # before the sprintf amount was a number, afterwards it's a string. because of the dynamic nature of perl
- # this is easy to confuse, so keep in mind: before this comment no s///, m//, concat or other strong ops on
- # $amount. after this comment no +,-,*,/,abs. it will only introduce subtle bugs.
-
- $amount =~ s/0*$// unless defined $places && $places == 0; # cull trailing 0s
-
- my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
- my @p = split(/\./, $amount); # split amount at decimal point
-
- $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
- $amount = $p[0];
- if ($places || $p[1]) {
- $amount .= $d[0]
- . ( $p[1] || '' )
- . (0 x max(abs($places || 0) - length ($p[1]||''), 0)); # pad the fraction
- }
-
- $amount = do {
- ($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) :
- ($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) :
- ($neg ? "-$amount" : "$amount" ) ;
- };
-
- $main::lxdebug->leave_sub(2);
- return $amount;
-}
-
-sub format_amount_units {
- $main::lxdebug->enter_sub();
-
- my $self = shift;
- my %params = @_;
-
- my $myconfig = \%main::myconfig;
- my $amount = $params{amount} * 1;
- my $places = $params{places};
- my $part_unit_name = $params{part_unit};
- my $amount_unit_name = $params{amount_unit};
- my $conv_units = $params{conv_units};
- my $max_places = $params{max_places};
-
- if (!$part_unit_name) {
- $main::lxdebug->leave_sub();
- return '';
- }
-
- my $all_units = AM->retrieve_all_units;
-
- if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
- $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
- }
-
- if (!scalar @{ $conv_units }) {
- my $result = $self->format_amount($myconfig, $amount, $places, undef, $max_places) . " " . $part_unit_name;
- $main::lxdebug->leave_sub();
- return $result;
- }
-
- my $part_unit = $all_units->{$part_unit_name};
- my $conv_unit = ($amount_unit_name && ($amount_unit_name ne $part_unit_name)) ? $all_units->{$amount_unit_name} : $part_unit;
-
- $amount *= $conv_unit->{factor};
-
- my @values;
- my $num;
-
- foreach my $unit (@$conv_units) {
- my $last = $unit->{name} eq $part_unit->{name};
- if (!$last) {
- $num = int($amount / $unit->{factor});
- $amount -= $num * $unit->{factor};
- }
-
- if ($last ? $amount : $num) {
- push @values, { "unit" => $unit->{name},
- "amount" => $last ? $amount / $unit->{factor} : $num,
- "places" => $last ? $places : 0 };
- }
-
- last if $last;
- }
-
- if (!@values) {
- push @values, { "unit" => $part_unit_name,
- "amount" => 0,
- "places" => 0 };
- }
-
- my $result = join " ", map { $self->format_amount($myconfig, $_->{amount}, $_->{places}, undef, $max_places), $_->{unit} } @values;
-
- $main::lxdebug->leave_sub();
-
- return $result;
+ SL::Helper::Number::_format_number($amount, $places, %$myconfig, dash => $dash);
}
sub format_string {
@@ -829,82 +732,11 @@ sub format_string {
#
sub parse_amount {
- $main::lxdebug->enter_sub(2);
-
my ($self, $myconfig, $amount) = @_;
-
- if (!defined($amount) || ($amount eq '')) {
- $main::lxdebug->leave_sub(2);
- return 0;
- }
-
- if ( ($myconfig->{numberformat} eq '1.000,00')
- || ($myconfig->{numberformat} eq '1000,00')) {
- $amount =~ s/\.//g;
- $amount =~ s/,/\./g;
- }
-
- if ($myconfig->{numberformat} eq "1'000.00") {
- $amount =~ s/\'//g;
- }
-
- $amount =~ s/,//g;
-
- $main::lxdebug->leave_sub(2);
-
- # Make sure no code wich is not a math expression ends up in eval().
- return 0 unless $amount =~ /^ [\s \d \( \) \- \+ \* \/ \. ]* $/x;
-
- # Prevent numbers from being parsed as octals;
- $amount =~ s{ (?get_precision || 0.01;
- return $self->round_amount( $self->round_amount($amount / $precision, 0) * $precision, $places);
- }
-
- # We use Perl's knowledge of string representation for
- # rounding. First, convert the floating point number to a string
- # with a high number of places. Then split the string on the decimal
- # sign and use integer calculation for rounding the decimal places
- # part. If an overflow occurs then apply that overflow to the part
- # before the decimal sign as well using integer arithmetic again.
-
- my $int_amount = int(abs $amount);
- my $str_places = max(min(10, 16 - length("$int_amount") - $places), $places);
- my $amount_str = sprintf '%.*f', $places + $str_places, abs($amount);
-
- return $amount unless $amount_str =~ m{^(\d+)\.(\d+)$};
-
- my ($pre, $post) = ($1, $2);
- my $decimals = '1' . substr($post, 0, $places);
-
- my $propagation_limit = $Config{i32size} == 4 ? 7 : 18;
- my $add_for_rounding = substr($post, $places, 1) >= 5 ? 1 : 0;
-
- if ($places > $propagation_limit) {
- $decimals = Math::BigInt->new($decimals)->badd($add_for_rounding);
- $pre = Math::BigInt->new($decimals)->badd(1) if substr($decimals, 0, 1) eq '2';
-
- } else {
- $decimals += $add_for_rounding;
- $pre += 1 if substr($decimals, 0, 1) eq '2';
- }
-
- $amount = ("${pre}." . substr($decimals, 1)) * ($amount <=> 0);
-
- return $amount;
-}
+sub round_amount { shift; goto &SL::Helper::Number::_round_number; }
sub parse_template {
$main::lxdebug->enter_sub();
@@ -914,11 +746,18 @@ sub parse_template {
local (*IN, *OUT);
- my $defaults = SL::DB::Default->get;
- my $userspath = $::lx_office_conf{paths}->{userspath};
+ my $defaults = SL::DB::Default->get;
- $self->{"cwd"} = getcwd();
- $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
+ my $keep_temp_files = $::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files};
+ $self->{cwd} = getcwd();
+ my $temp_dir = File::Temp->newdir(
+ "kivitendo-print-XXXXXX",
+ DIR => $self->{cwd} . "/" . $::lx_office_conf{paths}->{userspath},
+ CLEANUP => !$keep_temp_files,
+ );
+
+ my $userspath = File::Spec->abs2rel($temp_dir->dirname);
+ $self->{tmpdir} = $temp_dir->dirname;
my $ext_for_format;
@@ -935,13 +774,6 @@ sub parse_template {
$template_type = 'HTML';
$ext_for_format = 'html';
- } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
- $template_type = 'XML';
- $ext_for_format = 'xml';
-
- } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
- $template_type = 'XML';
-
} elsif ( $self->{"format"} =~ /excel/i ) {
$template_type = 'Excel';
$ext_for_format = 'xls';
@@ -985,7 +817,6 @@ sub parse_template {
# OUT is used for the media, screen, printer, email
# for postscript we store a copy in a temporary file
- my $keep_temp_files = $::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files};
my ($temp_fh, $suffix);
$suffix = $self->{IN};
@@ -1029,6 +860,9 @@ sub parse_template {
# therefore copy to webdav, even if we do not have the webdav feature enabled (just archive)
my $copy_to_webdav = $::instance_conf->get_webdav_documents && !$self->{preview} && $self->{tmpdir} && $self->{tmpfile} && $self->{type}
&& $self->{type} ne 'statement';
+
+ $self->{attachment_filename} ||= $self->generate_attachment_filename;
+
if ( $ext_for_format eq 'pdf' && $self->doc_storage_enabled ) {
$self->append_general_pdf_attachments(filepath => $self->{tmpdir}."/".$self->{tmpfile},
type => $self->{type});
@@ -1043,9 +877,8 @@ sub parse_template {
}
}
- if (!$self->{preview} && $self->doc_storage_enabled)
+ if (!$self->{preview} && $self->{attachment_type} !~ m{^dunning} && $self->doc_storage_enabled)
{
- $self->{attachment_filename} ||= $self->generate_attachment_filename;
$self->store_pdf($self);
}
$self->cleanup;
@@ -1063,8 +896,7 @@ sub parse_template {
}
}
- if ( !$self->{preview} && $ext_for_format eq 'pdf' && $self->doc_storage_enabled) {
- $self->{attachment_filename} ||= $self->generate_attachment_filename;
+ if ( !$self->{preview} && $ext_for_format eq 'pdf' && $self->{attachment_type} !~ m{^dunning} && $self->doc_storage_enabled) {
my $file_obj = $self->store_pdf($self);
$self->{print_file_id} = $file_obj->id if $file_obj;
}
@@ -1111,21 +943,27 @@ sub send_email {
map { $mail->{$_} = $self->{$_} }
qw(cc subject message format);
+ if ($self->{cc_employee}) {
+ my ($user, $my_emp_cc);
+ $user = SL::DB::Manager::AuthUser->find_by(login => $self->{cc_employee});
+ $my_emp_cc = $user->get_config_value('email') if ref $user eq 'SL::DB::AuthUser';
+ $mail->{cc} .= ", " if $mail->{cc};
+ $mail->{cc} .= $my_emp_cc if $my_emp_cc;
+ }
+
$mail->{bcc} = $self->get_bcc_defaults($myconfig, $self->{bcc});
$mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
$mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
$mail->{fileid} = time() . '.' . $$ . '.';
+ $mail->{content_type} = "text/html";
my $full_signature = $self->create_email_signature();
- $full_signature =~ s/\r//g;
$mail->{attachments} = [];
my @attfiles;
# if we send html or plain text inline
if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
- $mail->{content_type} = "text/html";
$mail->{message} =~ s/\r//g;
- $mail->{message} =~ s/\n/
\n/g;
- $full_signature =~ s/\n/
\n/g;
+ $mail->{message} =~ s{\n}{
\n}g;
$mail->{message} .= $full_signature;
open(IN, "<", $self->{tmpfile})
@@ -1135,12 +973,13 @@ sub send_email {
} elsif (($self->{attachment_policy} // '') ne 'no_file') {
my $attachment_name = $self->{attachment_filename} || $self->{tmpfile};
- $attachment_name =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
+ $attachment_name =~ s{\.(.+?)$}{.${ext_for_format}} if ($ext_for_format);
if (($self->{attachment_policy} // '') eq 'old_file') {
- my ( $attfile ) = SL::File->get_all(object_id => $self->{id},
- object_type => $self->{formname},
- file_type => 'document');
+ my ( $attfile ) = SL::File->get_all(object_id => $self->{id},
+ object_type => $self->{type},
+ file_type => 'document',
+ print_variant => $self->{formname},);
if ($attfile) {
$attfile->{override_file_name} = $attachment_name if $attachment_name;
@@ -1249,42 +1088,61 @@ sub get_formname_translation {
local $::locale = Locale->new($self->{recipient_locale});
my %formname_translations = (
- bin_list => $main::locale->text('Bin List'),
- credit_note => $main::locale->text('Credit Note'),
- invoice => $main::locale->text('Invoice'),
- pick_list => $main::locale->text('Pick List'),
- proforma => $main::locale->text('Proforma Invoice'),
- purchase_order => $main::locale->text('Purchase Order'),
- request_quotation => $main::locale->text('RFQ'),
- sales_order => $main::locale->text('Confirmation'),
- sales_quotation => $main::locale->text('Quotation'),
- storno_invoice => $main::locale->text('Storno Invoice'),
- sales_delivery_order => $main::locale->text('Delivery Order'),
- purchase_delivery_order => $main::locale->text('Delivery Order'),
- dunning => $main::locale->text('Dunning'),
- dunning1 => $main::locale->text('Payment Reminder'),
- dunning2 => $main::locale->text('Dunning'),
- dunning3 => $main::locale->text('Last Dunning'),
- dunning_invoice => $main::locale->text('Dunning Invoice'),
- letter => $main::locale->text('Letter'),
- ic_supply => $main::locale->text('Intra-Community supply'),
- statement => $main::locale->text('Statement'),
+ bin_list => $main::locale->text('Bin List'),
+ credit_note => $main::locale->text('Credit Note'),
+ invoice => $main::locale->text('Invoice'),
+ invoice_copy => $main::locale->text('Invoice Copy'),
+ invoice_for_advance_payment => $main::locale->text('Invoice for Advance Payment'),
+ final_invoice => $main::locale->text('Final Invoice'),
+ pick_list => $main::locale->text('Pick List'),
+ proforma => $main::locale->text('Proforma Invoice'),
+ purchase_order => $main::locale->text('Purchase Order'),
+ request_quotation => $main::locale->text('RFQ'),
+ sales_order => $main::locale->text('Confirmation'),
+ sales_quotation => $main::locale->text('Quotation'),
+ storno_invoice => $main::locale->text('Storno Invoice'),
+ sales_delivery_order => $main::locale->text('Delivery Order'),
+ purchase_delivery_order => $main::locale->text('Delivery Order'),
+ supplier_delivery_order => $main::locale->text('Supplier Delivery Order'),
+ rma_delivery_order => $main::locale->text('RMA Delivery Order'),
+ dunning => $main::locale->text('Dunning'),
+ dunning1 => $main::locale->text('Payment Reminder'),
+ dunning2 => $main::locale->text('Dunning'),
+ dunning3 => $main::locale->text('Last Dunning'),
+ dunning_invoice => $main::locale->text('Dunning Invoice'),
+ letter => $main::locale->text('Letter'),
+ ic_supply => $main::locale->text('Intra-Community supply'),
+ statement => $main::locale->text('Statement'),
);
$main::lxdebug->leave_sub();
return $formname_translations{$formname};
}
+sub get_cusordnumber_translation {
+ $main::lxdebug->enter_sub();
+ my ($self, $formname) = @_;
+
+ $formname ||= $self->{formname};
+
+ $self->{recipient_locale} ||= Locale->lang_to_locale($self->{language});
+ local $::locale = Locale->new($self->{recipient_locale});
+
+
+ $main::lxdebug->leave_sub();
+ return $main::locale->text('Your Order');
+}
+
sub get_number_prefix_for_type {
$main::lxdebug->enter_sub();
my ($self) = @_;
my $prefix =
- (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
- : ($self->{type} =~ /_quotation$/) ? 'quo'
- : ($self->{type} =~ /_delivery_order$/) ? 'do'
- : ($self->{type} =~ /letter/) ? 'letter'
- : 'ord';
+ (first { $self->{type} eq $_ } qw(invoice invoice_for_advance_payment final_invoice credit_note)) ? 'inv'
+ : ($self->{type} =~ /_quotation$/) ? 'quo'
+ : ($self->{type} =~ /_delivery_order$/) ? 'do'
+ : ($self->{type} =~ /letter/) ? 'letter'
+ : 'ord';
# better default like this?
# : ($self->{type} =~ /(sales|purcharse)_order/ : 'ord';
@@ -1319,7 +1177,7 @@ sub generate_attachment_filename {
my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
my $prefix = $self->get_number_prefix_for_type();
- if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
+ if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice invoice_for_advance_payment final_invoice credit_note))) {
$attachment_filename .= ' (' . $recipient_locale->text('Preview') . ')' . $self->get_extension_for_format();
} elsif ($attachment_filename && $self->{"${prefix}number"}) {
@@ -1350,6 +1208,10 @@ sub generate_email_subject {
$subject .= " " . $self->{"${prefix}number"}
}
+ if ($self->{cusordnumber}) {
+ $subject = $self->get_cusordnumber_translation() . ' ' . $self->{cusordnumber} . ' / ' . $subject;
+ }
+
$main::lxdebug->leave_sub();
return $subject;
}
@@ -1377,8 +1239,13 @@ sub generate_email_body {
return undef unless $body;
- $body .= GenericTranslations->get(translation_type =>"salutation_punctuation_mark", language_id => $self->{language_id}) . "\n";
- $body .= GenericTranslations->get(translation_type =>"preset_text_$self->{formname}", language_id => $self->{language_id});
+ $body .= GenericTranslations->get(translation_type => "salutation_punctuation_mark", language_id => $self->{language_id});
+ $body = '
' . $::locale->quote_special_chars('HTML', $body) . '
'; + + my $translation_type = $params{translation_type} // "preset_text_$self->{formname}"; + my $main_body = GenericTranslations->get(translation_type => $translation_type, language_id => $self->{language_id}); + $main_body = GenericTranslations->get(translation_type => $params{fallback_translation_type}, language_id => $self->{language_id}) if !$main_body && $params{fallback_translation_type}; + $body .= $main_body; $body = $main::locale->unquote_special_chars('HTML', $body); @@ -1881,6 +1748,12 @@ sub add_shipto { return if !$shipto; + # shiptocp_gender only makes sense, if any other shipto attribute is set. + # Because shiptocp_gender is set to 'm' by default in forms + # it must not be considered above to decide if shiptos has to be added or + # updated, but must be inserted or updated as well in case. + push(@values, $self->{shiptocp_gender}); + my $shipto_id = $self->{shipto_id}; if ($self->{shipto_id}) { @@ -1894,10 +1767,10 @@ sub add_shipto { shiptocountry = ?, shiptogln = ?, shiptocontact = ?, - shiptocp_gender = ?, shiptophone = ?, shiptofax = ?, shiptoemail = ? + shiptocp_gender = ?, WHERE shipto_id = ?|; do_query($self, $dbh, $query, @values, $self->{shipto_id}); } else { @@ -1911,10 +1784,10 @@ sub add_shipto { shiptocountry = ? AND shiptogln = ? AND shiptocontact = ? AND - shiptocp_gender = ? AND shiptophone = ? AND shiptofax = ? AND shiptoemail = ? AND + shiptocp_gender = ? AND module = ? AND trans_id = ?|; my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values, $module, $id); @@ -1922,7 +1795,7 @@ sub add_shipto { my $insert_query = qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2, shiptostreet, shiptozipcode, shiptocity, shiptocountry, shiptogln, - shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module) + shiptocontact, shiptophone, shiptofax, shiptoemail, shiptocp_gender, module) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|; do_query($self, $dbh, $insert_query, $id, @values, $module); @@ -2070,26 +1943,6 @@ sub _get_projects { $main::lxdebug->leave_sub(); } -sub _get_shipto { - $main::lxdebug->enter_sub(); - - my ($self, $dbh, $vc_id, $key) = @_; - - $key = "all_shipto" unless ($key); - - if ($vc_id) { - # get shipping addresses - my $query = qq|SELECT * FROM shipto WHERE trans_id = ?|; - - $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id); - - } else { - $self->{$key} = []; - } - - $main::lxdebug->leave_sub(); -} - sub _get_printers { $main::lxdebug->enter_sub(); @@ -2129,36 +1982,6 @@ sub _get_charts { $main::lxdebug->leave_sub(); } -sub _get_taxcharts { - $main::lxdebug->enter_sub(); - - my ($self, $dbh, $params) = @_; - - my $key = "all_taxcharts"; - my @where; - - if (ref $params eq 'HASH') { - $key = $params->{key} if ($params->{key}); - if ($params->{module} eq 'AR') { - push @where, 'chart_categories ~ \'[ACILQ]\''; - - } elsif ($params->{module} eq 'AP') { - push @where, 'chart_categories ~ \'[ACELQ]\''; - } - - } elsif ($params) { - $key = $params; - } - - my $where = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : ''; - - my $query = qq|SELECT * FROM tax $where ORDER BY taxkey, rate|; - - $self->{$key} = selectall_hashref_query($self, $dbh, $query); - - $main::lxdebug->leave_sub(); -} - sub _get_taxzones { $main::lxdebug->enter_sub(); @@ -2372,31 +2195,19 @@ sub _get_simple { $main::lxdebug->leave_sub(); } -#sub _get_groups { -# $main::lxdebug->enter_sub(); -# -# my ($self, $dbh, $key) = @_; -# -# $key ||= "all_groups"; -# -# my $groups = $main::auth->read_groups(); -# -# $self->{$key} = selectall_hashref_query($self, $dbh, $query); -# -# $main::lxdebug->leave_sub(); -#} - sub get_lists { $main::lxdebug->enter_sub(); my $self = shift; my %params = @_; + croak "get_lists: shipto is no longer supported" if $params{shipto}; + my $dbh = $self->get_standard_dbh(\%main::myconfig); my ($sth, $query, $ref); my ($vc, $vc_id); - if ($params{contacts} || $params{shipto}) { + if ($params{contacts}) { $vc = 'customer' if $self->{"vc"} eq "customer"; $vc = 'vendor' if $self->{"vc"} eq "vendor"; die "invalid use of get_lists, need 'vc'" unless $vc; @@ -2407,10 +2218,6 @@ sub get_lists { $self->_get_contacts($dbh, $vc_id, $params{"contacts"}); } - if ($params{"shipto"}) { - $self->_get_shipto($dbh, $vc_id, $params{"shipto"}); - } - if ($params{"projects"} || $params{"all_projects"}) { $self->_get_projects($dbh, $params{"all_projects"} ? $params{"all_projects"} : $params{"projects"}, @@ -2429,10 +2236,6 @@ sub get_lists { $self->_get_charts($dbh, $params{"charts"}); } - if ($params{"taxcharts"}) { - $self->_get_taxcharts($dbh, $params{"taxcharts"}); - } - if ($params{"taxzones"}) { $self->_get_taxzones($dbh, $params{"taxzones"}); } @@ -2485,10 +2288,6 @@ sub get_lists { $self->_get_warehouses($dbh, $params{warehouses}); } -# if ($params{groups}) { -# $self->_get_groups($dbh, $params{groups}); -# } - if ($params{partsgroup}) { $self->get_partsgroup(\%main::myconfig, { all => 1, target => $params{partsgroup} }); } @@ -2722,12 +2521,12 @@ sub create_links { if ($self->{id}) { $query = qq|SELECT - a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid, - a.duedate, a.ordnumber, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.notes, + a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid, a.deliverydate, + a.duedate, a.tax_point, a.ordnumber, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.notes, a.mtime, a.itime, a.intnotes, a.department_id, a.amount AS oldinvtotal, a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type, - a.globalproject_id, ${extra_columns} + a.globalproject_id, a.transaction_description, ${extra_columns} c.name AS $table, d.description AS department, e.name AS employee @@ -2924,19 +2723,48 @@ sub lastname_used { } sub get_variable_content_types { - my %html_variables = ( - longdescription => 'html', - partnotes => 'html', - notes => 'html', - orignotes => 'html', - notes1 => 'html', - notes2 => 'html', - notes3 => 'html', - notes4 => 'html', - header_text => 'html', - footer_text => 'html', + my ($self) = @_; + + my %html_variables = ( + longdescription => 'html', + partnotes => 'html', + notes => 'html', + orignotes => 'html', + notes1 => 'html', + notes2 => 'html', + notes3 => 'html', + notes4 => 'html', + header_text => 'html', + footer_text => 'html', ); - return \%html_variables; + + return { + %html_variables, + $self->get_variable_content_types_for_cvars, + }; +} + +sub get_variable_content_types_for_cvars { + my ($self) = @_; + my $html_configs = SL::DB::Manager::CustomVariableConfig->get_all(where => [ type => 'htmlfield' ]); + my %types; + + if (@{ $html_configs }) { + my %prefix_by_module = ( + Contacts => 'cp_cvar_', + CT => 'vc_cvar_', + IC => 'ic_cvar_', + Projects => 'project_cvar_', + ShipTo => 'shiptocvar_', + ); + + foreach my $cfg (@{ $html_configs }) { + my $prefix = $prefix_by_module{$cfg->module}; + $types{$prefix . $cfg->name} = 'html' if $prefix; + } + } + + return %types; } sub current_date { @@ -3114,13 +2942,17 @@ sub save_status { # $main::locale->text('ELSE') # $main::locale->text('SAVED FOR DUNNING') # $main::locale->text('DUNNING STARTED') +# $main::locale->text('PREVIEWED') # $main::locale->text('PRINTED') # $main::locale->text('MAILED') # $main::locale->text('SCREENED') # $main::locale->text('CANCELED') # $main::locale->text('IMPORT') +# $main::locale->text('UNDO TRANSFER') # $main::locale->text('UNIMPORT') # $main::locale->text('invoice') +# $main::locale->text('invoice_for_advance_payment') +# $main::locale->text('final_invoice') # $main::locale->text('proforma') # $main::locale->text('sales_order') # $main::locale->text('pick_list') @@ -3165,7 +2997,7 @@ sub get_history { qq|SELECT h.employee_id, h.itime::timestamp(0) AS itime, h.addition, h.what_done, emp.name, h.snumbers, h.trans_id AS id | . qq|FROM history_erp h | . qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | . - qq|WHERE (trans_id = | . $trans_id . qq|) $restriction | . + qq|WHERE (trans_id = | . $dbh->quote($trans_id) . qq|) $restriction | . $order; my $sth = $dbh->prepare($query) || $self->dberror($query); @@ -3338,19 +3170,6 @@ sub prepare_for_printing { $self->{"employee_${_}"} = $defaults->$_ for qw(address businessnumber co_ustid company duns sepa_creditor_id taxnumber); } - # Load shipping address from database. If shipto_id is set then it's - # one from the customer's/vendor's master data. Otherwise look an a - # customized address linking back to the current record. - my $shipto_module = $self->{type} =~ /_delivery_order$/ ? 'DO' - : $self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/ ? 'OE' - : 'AR'; - my $shipto = $self->{shipto_id} ? SL::DB::Shipto->new(shipto_id => $self->{shipto_id})->load - : SL::DB::Manager::Shipto->get_first(where => [ module => $shipto_module, trans_id => $self->{id} ]); - if ($shipto) { - $self->{$_} = $shipto->$_ for grep { m{^shipto} } map { $_->name } @{ $shipto->meta->columns }; - $self->{"shiptocvar_" . $_->config->name} = $_->value_as_text for @{ $shipto->cvars_by_config }; - } - my $language = $self->{language} ? '_' . $self->{language} : ''; my ($language_tc, $output_numberformat, $output_dateformat, $output_longdates); @@ -3377,6 +3196,8 @@ sub prepare_for_printing { IS->invoice_details(\%::myconfig, $self, $::locale); } + $self->set_addition_billing_address_print_variables; + # Chose extension & set source file name my $extension = 'html'; if ($self->{format} eq 'postscript') { @@ -3399,7 +3220,7 @@ sub prepare_for_printing { # Format dates. $self->format_dates($output_dateformat, $output_longdates, - qw(invdate orddate quodate pldate duedate reqdate transdate shippingdate deliverydate validitydate paymentdate datepaid + qw(invdate orddate quodate pldate duedate reqdate transdate tax_point shippingdate deliverydate validitydate paymentdate datepaid transdate_oe deliverydate_oe employee_startdate employee_enddate), grep({ /^(?:datepaid|transdate_oe|reqdate|deliverydate|deliverydate_oe|transdate)_\d+$/ } keys(%{$self}))); @@ -3437,6 +3258,43 @@ sub prepare_for_printing { today => DateTime->today, }; + if ($defaults->print_interpolate_variables_in_positions) { + $self->substitute_placeholders_in_template_arrays({ field => 'description', type => 'text' }, { field => 'longdescription', type => 'html' }); + } + + return $self; +} + +sub set_addition_billing_address_print_variables { + my ($self) = @_; + + return if !$self->{billing_address_id}; + + my $address = SL::DB::Manager::AdditionalBillingAddress->find_by(id => $self->{billing_address_id}); + return if !$address; + + $self->{"billing_address_${_}"} = $address->$_ for map { $_->name } @{ $address->meta->columns }; +} + +sub substitute_placeholders_in_template_arrays { + my ($self, @fields) = @_; + + foreach my $spec (@fields) { + $spec = { field => $spec, type => 'text' } if !ref($spec); + my $field = $spec->{field}; + + next unless exists $self->{TEMPLATE_ARRAYS} && exists $self->{TEMPLATE_ARRAYS}->{$field}; + + my $tag_start = $spec->{type} eq 'html' ? '<%' : '<%'; + my $tag_end = $spec->{type} eq 'html' ? '%>' : '%>'; + my $formatter = $spec->{type} eq 'html' ? sub { $::locale->quote_special_chars('html', $_[0] // '') } : sub { $_[0] }; + + $self->{TEMPLATE_ARRAYS}->{$field} = [ + apply { s{${tag_start}(.+?)${tag_end}}{ $formatter->($self->{$1}) }eg } + @{ $self->{TEMPLATE_ARRAYS}->{$field} } + ]; + } + return $self; } @@ -3591,19 +3449,11 @@ sub reformat_numbers { } sub create_email_signature { - my $client_signature = $::instance_conf->get_signature; my $user_signature = $::myconfig{signature}; - my $signature = ''; - if ( $client_signature or $user_signature ) { - $signature = "\n\n-- \n"; - $signature .= $user_signature . "\n" if $user_signature; - $signature .= $client_signature . "\n" if $client_signature; - }; - return $signature; - -}; + return join '', grep { $_ } ($user_signature, $client_signature); +} sub calculate_tax { # this function calculates the net amount and tax for the lines in ar, ap and