use Data::Dumper;
use CGI;
-use CGI::Ajax;
use Cwd;
use Encode;
use File::Copy;
use SL::Common;
use SL::CVar;
use SL::DB;
+use SL::DBConnect;
use SL::DBUtils;
use SL::DO;
use SL::IC;
use SL::IS;
+use SL::Layout::Dispatcher;
+use SL::Locale;
use SL::Mailer;
use SL::Menu;
+use SL::MoreCommon qw(uri_encode uri_decode);
use SL::OE;
+use SL::PrefixedNumber;
+use SL::Request;
use SL::Template;
use SL::User;
+use SL::X;
use Template;
use URI;
use List::Util qw(first max min sum);
undef $standard_dbh;
}
-sub _store_value {
- $main::lxdebug->enter_sub(2);
-
- my $self = shift;
- my $key = shift;
- my $value = shift;
-
- my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
-
- my $curr;
-
- if (scalar @tokens) {
- $curr = \ $self->{ shift @tokens };
- }
-
- while (@tokens) {
- my $sep = shift @tokens;
- my $key = shift @tokens;
-
- $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
- $curr = \ $$curr->[max 0, $#$$curr] if $sep eq '[].';
- $curr = \ $$curr->[++$#$$curr] if $sep eq '[+].';
- $curr = \ $$curr->{$key}
- }
-
- $$curr = $value;
-
- $main::lxdebug->leave_sub(2);
-
- return $curr;
-}
-
-sub _input_to_hash {
- $main::lxdebug->enter_sub(2);
-
- my $self = shift;
- my $input = shift;
-
- my @pairs = split(/&/, $input);
-
- foreach (@pairs) {
- my ($key, $value) = split(/=/, $_, 2);
- $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
- }
-
- $main::lxdebug->leave_sub(2);
-}
-
-sub _request_to_hash {
- $main::lxdebug->enter_sub(2);
-
- my $self = shift;
- my $input = shift;
-
- if (!$ENV{'CONTENT_TYPE'}
- || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
-
- $self->_input_to_hash($input);
-
- $main::lxdebug->leave_sub(2);
- return;
- }
-
- my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
-
- my $boundary = '--' . $1;
-
- foreach my $line (split m/\n/, $input) {
- last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
-
- if (($line eq $boundary) || ($line eq "$boundary\r")) {
- ${ $previous } =~ s|\r?\n$|| if $previous;
-
- undef $previous;
- undef $filename;
-
- $headers_done = 0;
- $content_type = "text/plain";
- $boundary_found = 1;
- $need_cr = 0;
-
- next;
- }
-
- next unless $boundary_found;
-
- if (!$headers_done) {
- $line =~ s/[\r\n]*$//;
-
- if (!$line) {
- $headers_done = 1;
- next;
- }
-
- if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
- if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
- $filename = $1;
- substr $line, $-[0], $+[0] - $-[0], "";
- }
-
- if ($line =~ m|name\s*=\s*"(.*?)"|i) {
- $name = $1;
- substr $line, $-[0], $+[0] - $-[0], "";
- }
-
- $previous = $self->_store_value($name, '') if ($name);
- $self->{FILENAME} = $filename if ($filename);
-
- next;
- }
-
- if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
- $content_type = $1;
- }
-
- next;
- }
-
- next unless $previous;
-
- ${ $previous } .= "${line}\n";
- }
-
- ${ $previous } =~ s|\r?\n$|| if $previous;
-
- $main::lxdebug->leave_sub(2);
-}
-
-sub _recode_recursively {
- $main::lxdebug->enter_sub();
- my ($iconv, $param) = @_;
-
- if (any { ref $param eq $_ } qw(Form HASH)) {
- foreach my $key (keys %{ $param }) {
- if (!ref $param->{$key}) {
- # Workaround for a bug: converting $param->{$key} directly
- # leads to 'undef'. I don't know why. Converting a copy works,
- # though.
- $param->{$key} = $iconv->convert("" . $param->{$key});
- } else {
- _recode_recursively($iconv, $param->{$key});
- }
- }
-
- } elsif (ref $param eq 'ARRAY') {
- foreach my $idx (0 .. scalar(@{ $param }) - 1) {
- if (!ref $param->[$idx]) {
- # Workaround for a bug: converting $param->[$idx] directly
- # leads to 'undef'. I don't know why. Converting a copy works,
- # though.
- $param->[$idx] = $iconv->convert("" . $param->[$idx]);
- } else {
- _recode_recursively($iconv, $param->[$idx]);
- }
- }
- }
- $main::lxdebug->leave_sub();
-}
-
sub new {
$main::lxdebug->enter_sub();
my $self = {};
+ no warnings 'once';
if ($LXDebug::watch_form) {
require SL::Watchdog;
tie %{ $self }, 'SL::Watchdog';
bless $self, $type;
- $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
- $self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
-
- if ($ENV{CONTENT_LENGTH}) {
- my $content;
- read STDIN, $content, $ENV{CONTENT_LENGTH};
- $self->_request_to_hash($content);
- }
-
- my $db_charset = $main::dbcharset;
- $db_charset ||= Common::DEFAULT_CHARSET;
-
- my $encoding = $self->{INPUT_ENCODING} || $db_charset;
- delete $self->{INPUT_ENCODING};
-
- _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
-
- #$self->{version} = "2.6.1"; # Old hardcoded but secure style
open VERSION_FILE, "VERSION"; # New but flexible code reads version from VERSION-file
$self->{version} = <VERSION_FILE>;
close VERSION_FILE;
return $self;
}
+sub read_cgi_input {
+ my ($self) = @_;
+ SL::Request::read_cgi_input($self);
+}
+
sub _flatten_variables_rec {
$main::lxdebug->enter_sub(2);
}
sub escape {
- $main::lxdebug->enter_sub(2);
-
my ($self, $str) = @_;
- $str = Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
- $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
-
- $main::lxdebug->leave_sub(2);
-
- return $str;
+ return uri_encode($str);
}
sub unescape {
- $main::lxdebug->enter_sub(2);
-
my ($self, $str) = @_;
- $str =~ tr/+/ /;
- $str =~ s/\\$//;
-
- $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
-
- $main::lxdebug->leave_sub(2);
-
- return $str;
+ return uri_decode($str);
}
sub quote {
my $self = shift;
if (@_) {
- map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
+ map({ print($::request->{cgi}->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
} else {
for (sort keys %$self) {
next if (($_ eq "header") || (ref($self->{$_}) ne ""));
- print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
+ print($::request->{cgi}->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
}
}
$main::lxdebug->leave_sub();
}
+sub throw_on_error {
+ my ($self, $code) = @_;
+ local $self->{__ERROR_HANDLER} = sub { die SL::X::FormError->new($_[0]) };
+ $code->();
+}
+
sub error {
$main::lxdebug->enter_sub();
$main::lxdebug->show_backtrace();
my ($self, $msg) = @_;
- if ($ENV{HTTP_USER_AGENT}) {
+
+ if ($self->{__ERROR_HANDLER}) {
+ $self->{__ERROR_HANDLER}->($msg);
+
+ } elsif ($ENV{HTTP_USER_AGENT}) {
$msg =~ s/\n/<br>/g;
$self->show_generic_error($msg);
<script type="text/javascript">
<!--
// If JavaScript is enabled, the whole thing will be reloaded.
- // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
+ // The reason is: When one changes his menu setup (HTML / CSS ...)
// it now loads the correct code into the browser instead of do nothing.
setTimeout("top.frames.location.href='login.pl'",500);
//-->
my $self = shift;
return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
+ return URI->new if !$ENV{REQUEST_URI}; # for testing
my $scheme = $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
- my $port = $ENV{SERVER_PORT} || '';
+ my $port = $ENV{SERVER_PORT};
$port = undef if (($scheme eq 'http' ) && ($port == 80))
|| (($scheme eq 'https') && ($port == 443));
my $self = shift;
my %params = @_;
- my $cgi = $main::cgi;
- $cgi ||= CGI->new('');
+ my $cgi = $::request->{cgi};
my $session_cookie;
if (defined $main::auth) {
$cgi_params{'-charset'} = $params{charset} if ($params{charset});
$cgi_params{'-cookie'} = $session_cookie if ($session_cookie);
+ map { $cgi_params{'-' . $_} = $params{$_} if exists $params{$_} } qw(content_disposition content_length);
+
my $output = $cgi->header(%cgi_params);
$main::lxdebug->leave_sub();
return $output;
}
-
sub header {
$::lxdebug->enter_sub;
- # extra code is currently only used by menuv3 and menuv4 to set their css.
- # it is strongly deprecated, and will be changed in a future version.
- my ($self, $extra_code) = @_;
- my $db_charset = $::dbcharset || Common::DEFAULT_CHARSET;
+ my ($self, %params) = @_;
+ my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
my @header;
$::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
+ if ($params{no_layout}) {
+ $::request->{layout} = SL::Layout::Dispatcher->new(style => 'none');
+ }
+
+ my $layout = $::request->{layout};
+
+ # standard css for all
+ # this should gradually move to the layouts that need it
+ $layout->use_stylesheet("$_.css") for qw(
+ main menu list_accounts jquery.autocomplete
+ jquery.multiselect2side frame_header/header
+ ui-lightness/jquery-ui
+ jquery-ui.custom
+ );
+
+ $layout->use_javascript("$_.js") for (qw(
+ jquery jquery-ui jquery.cookie jqModal jquery.checkall
+ common part_selection switchmenuframe
+ ), "jquery/ui/i18n/jquery.ui.datepicker-$::myconfig{countrycode}");
+
$self->{favicon} ||= "favicon.ico";
- $self->{titlebar} = "$self->{title} - $self->{titlebar}" if $self->{title};
+ $self->{titlebar} = join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title} || !$self->{titlebar};
# build includes
if ($self->{refresh_url} || $self->{refresh_time}) {
push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
}
- push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
- for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
+ my $auto_reload_resources_param = $layout->auto_reload_resources_param;
- push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
- push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
- push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
- '<script type="text/javascript" src="js/common.js"></script>',
- '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
- '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
- '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
- '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
- '<script type="text/javascript" src="js/part_selection.js"></script>';
+ push @header, map { qq|<link rel="stylesheet" href="${_}${auto_reload_resources_param}" type="text/css" title="Stylesheet">| } $layout->stylesheets;
+ push @header, "<style type='text/css'>\@page { size:landscape; }</style> " if $self->{landscape};
+ push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
+ push @header, map { qq|<script type="text/javascript" src="${_}${auto_reload_resources_param}"></script>| } $layout->javascripts;
push @header, $self->{javascript} if $self->{javascript};
push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
- push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
- push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
- join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
-
- # if there is a title, we put some JavaScript in to the page, wich writes a
- # meaningful title-tag for our frameset.
- my $title_hack = '';
- if ($self->{title}) {
- $title_hack = qq|
- <script type="text/javascript">
- <!--
- // Write a meaningful title-tag for our frameset.
- top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
- //-->
- </script>|;
- }
+
+ my %doctypes = (
+ strict => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|,
+ transitional => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|,
+ frameset => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|,
+ html5 => qq|<!DOCTYPE html>|,
+ );
# output
print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
- print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
- if $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
+ print $doctypes{$params{doctype} || 'transitional'}, $/;
print <<EOT;
<html>
<head>
EOT
print " $_\n" for @header;
print <<EOT;
- <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
- <meta name="robots" content="noindex,nofollow" />
- <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
- <script type="text/javascript" src="js/tabcontent.js">
-
- /***********************************************
- * Tab Content script v2.2- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
- * This notice MUST stay intact for legal use
- * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
- ***********************************************/
-
- </script>
- $extra_code
- $title_hack
+ <meta name="robots" content="noindex,nofollow">
</head>
+ <body>
EOT
+ print $::request->{layout}->pre_content;
+ print $::request->{layout}->start_content;
+
+ $layout->header_done;
$::lxdebug->leave_sub;
}
+sub footer {
+ return unless $::request->{layout}->need_footer;
+
+ print $::request->{layout}->end_content;
+ print $::request->{layout}->post_content;
+
+ if (my @inline_scripts = $::request->{layout}->javascripts_inline) {
+ print "<script type='text/javascript'>@inline_scripts</script>\n";
+ }
+
+ print <<EOL
+ </body>
+</html>
+EOL
+}
+
sub ajax_response_header {
$main::lxdebug->enter_sub();
my ($self) = @_;
- my $db_charset = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
- my $cgi = $main::cgi || CGI->new('');
- my $output = $cgi->header('-charset' => $db_charset);
+ my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
+ my $output = $::request->{cgi}->header('-charset' => $db_charset);
$main::lxdebug->leave_sub();
my $base_uri = $self->_get_request_uri;
my $new_uri = URI->new_abs($new_url, $base_uri);
- die "Headers already sent" if $::self->{header};
+ die "Headers already sent" if $self->{header};
$self->{header} = 1;
- my $cgi = $main::cgi || CGI->new('');
- return $cgi->redirect($new_uri);
+ return $::request->{cgi}->redirect($new_uri);
}
sub set_standard_title {
$::lxdebug->enter_sub;
my $self = shift;
- $self->{titlebar} = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
+ $self->{titlebar} = "kivitendo " . $::locale->text('Version') . " $self->{version}";
$self->{titlebar} .= "- $::myconfig{name}" if $::myconfig{name};
$self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
my $language;
if (!%::myconfig || !$::myconfig{"countrycode"}) {
- $language = $main::language;
+ $language = $::lx_office_conf{system}->{language};
} else {
$language = $main::myconfig{"countrycode"};
}
$language = "de" unless ($language);
if (-f "templates/webpages/${file}.html") {
- if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
- my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
- "Please re-run 'locales.pl' in 'locale/${language}'.";
- print(qq|<pre>$info</pre>|);
- ::end_of_request();
- }
-
$file = "templates/webpages/${file}.html";
} else {
map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
}
- $additional_params->{"conf_dbcharset"} = $::dbcharset;
- $additional_params->{"conf_webdav"} = $::webdav;
- $additional_params->{"conf_lizenzen"} = $::lizenzen;
- $additional_params->{"conf_latex_templates"} = $::latex;
- $additional_params->{"conf_opendocument_templates"} = $::opendocument_templates;
- $additional_params->{"conf_vertreter"} = $::vertreter;
- $additional_params->{"conf_show_best_before"} = $::show_best_before;
- $additional_params->{"conf_parts_image_css"} = $::parts_image_css;
- $additional_params->{"conf_parts_listing_images"} = $::parts_listing_images;
- $additional_params->{"conf_parts_show_image"} = $::parts_show_image;
+ $additional_params->{"conf_dbcharset"} = $::lx_office_conf{system}->{dbcharset};
+ $additional_params->{"conf_webdav"} = $::lx_office_conf{features}->{webdav};
+ $additional_params->{"conf_latex_templates"} = $::lx_office_conf{print_templates}->{latex};
+ $additional_params->{"conf_opendocument_templates"} = $::lx_office_conf{print_templates}->{opendocument};
+ $additional_params->{"conf_vertreter"} = $::lx_office_conf{features}->{vertreter};
+ $additional_params->{"conf_parts_image_css"} = $::lx_office_conf{features}->{parts_image_css};
+ $additional_params->{"conf_parts_listing_images"} = $::lx_office_conf{features}->{parts_listing_images};
+ $additional_params->{"conf_parts_show_image"} = $::lx_office_conf{features}->{parts_show_image};
+ $additional_params->{"INSTANCE_CONF"} = $::instance_conf;
- if (%main::debug_options) {
- map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
+ if (my $debug_options = $::lx_office_conf{debug}{options}) {
+ map { $additional_params->{'DEBUG_' . uc($_)} = $debug_options->{$_} } keys %$debug_options;
}
if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
sub init_template {
my $self = shift;
- return if $self->template;
+ return $self->template if $self->template;
+ # Force scripts/locales.pl to pick up the exception handling template.
+ # parse_html_template('generic/exception')
return $self->template(Template->new({
'INTERPOLATE' => 0,
'EVAL_PERL' => 0,
'PLUGIN_BASE' => 'SL::Template::Plugin',
'INCLUDE_PATH' => '.:templates/webpages',
'COMPILE_EXT' => '.tcc',
- 'COMPILE_DIR' => $::userspath . '/templates-cache',
+ 'COMPILE_DIR' => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
+ 'ERROR' => 'templates/webpages/generic/exception.html',
})) || die;
}
my ($self, $error, %params) = @_;
+ if ($self->{__ERROR_HANDLER}) {
+ $self->{__ERROR_HANDLER}->($error);
+ $main::lxdebug->leave_sub();
+ return;
+ }
+
+ if ($::request->is_ajax) {
+ $::lxdebug->message(0, "trying to render AJAX response...");
+ SL::ClientJS->new
+ ->error($error)
+ ->render(SL::Controller::Base->new);
+ ::end_of_request();
+ }
+
my $add_params = {
'title_error' => $params{title},
'label_error' => $error,
::end_of_request();
}
-# write Trigger JavaScript-Code ($qty = quantity of Triggers)
-# changed it to accept an arbitrary number of triggers - sschoeling
-sub write_trigger {
- $main::lxdebug->enter_sub();
-
- my $self = shift;
- my $myconfig = shift;
- my $qty = shift;
-
- # set dateform for jsscript
- # default
- my %dateformats = (
- "dd.mm.yy" => "%d.%m.%Y",
- "dd-mm-yy" => "%d-%m-%Y",
- "dd/mm/yy" => "%d/%m/%Y",
- "mm/dd/yy" => "%m/%d/%Y",
- "mm-dd-yy" => "%m-%d-%Y",
- "yyyy-mm-dd" => "%Y-%m-%d",
- );
-
- my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
- $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
-
- my @triggers;
- while ($#_ >= 2) {
- push @triggers, qq|
- Calendar.setup(
- {
- inputField : "| . (shift) . qq|",
- ifFormat :"$ifFormat",
- align : "| . (shift) . qq|",
- button : "| . (shift) . qq|"
- }
- );
- |;
- }
- my $jsscript = qq|
- <script type="text/javascript">
- <!--| . join("", @triggers) . qq|//-->
- </script>
- |;
+sub _store_redirect_info_in_session {
+ my ($self) = @_;
- $main::lxdebug->leave_sub();
+ return unless $self->{callback} =~ m:^ ( [^\?/]+ \.pl ) \? (.+) :x;
- return $jsscript;
-} #end sub write_trigger
+ my ($controller, $params) = ($1, $2);
+ my $form = { map { map { $self->unescape($_) } split /=/, $_, 2 } split m/\&/, $params };
+ $self->{callback} = "${controller}?RESTORE_FORM_FROM_SESSION_ID=" . $::auth->save_form_in_session(form => $form);
+}
sub redirect {
$main::lxdebug->enter_sub();
my ($self, $msg) = @_;
if (!$self->{callback}) {
-
$self->info($msg);
- ::end_of_request();
- }
-# my ($script, $argv) = split(/\?/, $self->{callback}, 2);
-# $script =~ s|.*/||;
-# $script =~ s|[^a-zA-Z0-9_\.]||g;
-# exec("perl", "$script", $argv);
+ } else {
+ $self->_store_redirect_info_in_session;
+ print $::form->redirect_header($self->{callback});
+ }
- print $::form->redirect_header($self->{callback});
+ ::end_of_request();
$main::lxdebug->leave_sub();
}
$main::lxdebug->enter_sub(2);
my ($self, $myconfig, $amount, $places, $dash) = @_;
+ $amount ||= 0;
+ $dash ||= '';
+ my $neg = $amount < 0;
+ my $force_places = defined $places && $places >= 0;
- if ($amount eq "") {
- $amount = 0;
- }
-
- # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
+ $amount = $self->round_amount($amount, abs $places) if $force_places;
+ $amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa
- my $neg = ($amount =~ s/^-//);
- my $exp = ($amount =~ m/[e]/) ? 1 : 0;
+ # 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.
- if (defined($places) && ($places ne '')) {
- if (not $exp) {
- if ($places < 0) {
- $amount *= 1;
- $places *= -1;
-
- my ($actual_places) = ($amount =~ /\.(\d+)/);
- $actual_places = length($actual_places);
- $places = $actual_places > $places ? $actual_places : $places;
- }
- }
- $amount = $self->round_amount($amount, $places);
- }
+ $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
+ 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];
- $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
+ if ($places || $p[1]) {
+ $amount .= $d[0]
+ . ( $p[1] || '' )
+ . (0 x (abs($places || 0) - length ($p[1]||''))); # pad the fraction
+ }
$amount = do {
($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) :
($neg ? "-$amount" : "$amount" ) ;
};
-
$main::lxdebug->leave_sub(2);
return $amount;
}
return '';
}
- AM->retrieve_all_units();
- my $all_units = $main::all_units;
+ 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');
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/,/\./;
+ $amount =~ s/,/\./g;
}
if ($myconfig->{numberformat} eq "1'000.00") {
$main::lxdebug->leave_sub(2);
- return ($amount * 1);
+ # Make sure no code wich is not a math expression ends up in eval().
+ return 0 unless $amount =~ /^ [\s \d \( \) \- \+ \* \/ \. ]* $/x;
+ return scalar(eval($amount)) * 1 ;
}
sub round_amount {
sub parse_template {
$main::lxdebug->enter_sub();
- my ($self, $myconfig, $userspath) = @_;
- my $out;
+ my ($self, $myconfig) = @_;
+ my ($out, $out_mode);
local (*IN, *OUT);
+ my $userspath = $::lx_office_conf{paths}->{userspath};
+
$self->{"cwd"} = getcwd();
$self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
}
map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
+ map { $self->{"myconfig_${_}"} = $myconfig->{$_} } grep { $_ ne 'dbpasswd' } keys %{ $myconfig };
$self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
# OUT is used for the media, screen, printer, email
# for postscript we store a copy in a temporary file
- my $fileid = time;
- my $prepend_userspath;
-
- if (!$self->{tmpfile}) {
- $self->{tmpfile} = "${fileid}.$self->{IN}";
- $prepend_userspath = 1;
- }
-
- $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
-
- $self->{tmpfile} =~ s|.*/||;
- $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
- $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
+ my ($temp_fh, $suffix);
+ $suffix = $self->{IN};
+ $suffix =~ s/.*\.//;
+ ($temp_fh, $self->{tmpfile}) = File::Temp::tempfile(
+ 'kivitendo-printXXXXXX',
+ SUFFIX => '.' . ($suffix || 'tex'),
+ DIR => $userspath,
+ UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
+ );
+ close $temp_fh;
+ (undef, undef, $self->{template_meta}{tmpfile}) = File::Spec->splitpath( $self->{tmpfile} );
if ($template->uses_temp_file() || $self->{media} eq 'email') {
- $out = $self->{OUT};
- $self->{OUT} = ">$self->{tmpfile}";
+ $out = $self->{OUT};
+ $out_mode = $self->{OUT_MODE} || '>';
+ $self->{OUT} = "$self->{tmpfile}";
+ $self->{OUT_MODE} = '>';
}
my $result;
+ my $command_formatter = sub {
+ my ($out_mode, $out) = @_;
+ return $out_mode eq '|-' ? SL::Template::create(type => 'ShellCommand', form => $self)->parse($out) : $out;
+ };
if ($self->{OUT}) {
- open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
- $result = $template->parse(*OUT);
- close OUT;
-
+ $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
+ open(OUT, $self->{OUT_MODE}, $self->{OUT}) or $self->error("error on opening $self->{OUT} with mode $self->{OUT_MODE} : $!");
} else {
+ *OUT = ($::dispatcher->get_standard_filehandles)[1];
$self->header;
- $result = $template->parse(*STDOUT);
}
- if (!$result) {
+ if (!$template->parse(*OUT)) {
$self->cleanup();
$self->error("$self->{IN} : " . $template->get_error());
}
+ close OUT if $self->{OUT};
+
if ($self->{media} eq 'file') {
copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
$self->cleanup;
map { $mail->{$_} = $self->{$_} }
qw(cc bcc subject message version format);
- $mail->{charset} = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
+ $mail->{charset} = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
$mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
$mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
- $mail->{fileid} = "$fileid.";
+ $mail->{fileid} = time() . '.' . $$ . '.';
$myconfig->{signature} =~ s/\r//g;
# if we send html or plain text inline
if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
- $mail->{contenttype} = "text/html";
+ $mail->{contenttype} = "text/html";
+ $mail->{message} =~ s/\r//g;
+ $mail->{message} =~ s/\n/<br>\n/g;
+ $myconfig->{signature} =~ s/\n/<br>\n/g;
+ $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
- $mail->{message} =~ s/\r//g;
- $mail->{message} =~ s/\n/<br>\n/g;
- $myconfig->{signature} =~ s/\n/<br>\n/g;
- $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
-
- open(IN, $self->{tmpfile})
+ open(IN, "<", $self->{tmpfile})
or $self->error($self->cleanup . "$self->{tmpfile} : $!");
- while (<IN>) {
- $mail->{message} .= $_;
- }
-
+ $mail->{message} .= $_ while <IN>;
close(IN);
} else {
} else {
- $self->{OUT} = $out;
+ $self->{OUT} = $out;
+ $self->{OUT_MODE} = $out_mode;
my $numbytes = (-s $self->{tmpfile});
- open(IN, $self->{tmpfile})
+ open(IN, "<", $self->{tmpfile})
or $self->error($self->cleanup . "$self->{tmpfile} : $!");
+ binmode IN;
$self->{copies} = 1 unless $self->{media} eq 'printer';
#print(STDERR "OUT $self->{OUT}\n");
for my $i (1 .. $self->{copies}) {
if ($self->{OUT}) {
- open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
- print OUT while <IN>;
+ $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
+
+ open OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
+ print OUT $_ while <IN>;
close OUT;
- seek IN, 0, 0;
+ seek IN, 0, 0;
} else {
$self->{attachment_filename} = ($self->{attachment_filename})
$formname ||= $self->{formname};
+ $self->{recipient_locale} ||= Locale->lang_to_locale($self->{language});
+ local $::locale = Locale->new($self->{recipient_locale});
+
my %formname_translations = (
bin_list => $main::locale->text('Bin List'),
credit_note => $main::locale->text('Credit Note'),
);
$main::lxdebug->leave_sub();
- return $formname_translations{$formname}
+ return $formname_translations{$formname};
}
sub get_number_prefix_for_type {
$main::lxdebug->enter_sub();
my ($self) = @_;
+ $self->{recipient_locale} ||= Locale->lang_to_locale($self->{language});
+ my $recipient_locale = Locale->new($self->{recipient_locale});
+
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))) {
- $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
+ $attachment_filename .= ' (' . $recipient_locale->text('Preview') . ')' . $self->get_extension_for_format();
} elsif ($attachment_filename && $self->{"${prefix}number"}) {
$attachment_filename .= "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
sub cleanup {
$main::lxdebug->enter_sub();
- my $self = shift;
+ my ($self, $application) = @_;
+
+ my $error_code = $?;
chdir("$self->{tmpdir}");
my @err = ();
- if (-f "$self->{tmpfile}.err") {
+ if ((-1 == $error_code) || (127 == (($error_code) >> 8))) {
+ push @err, $::locale->text('The application "#1" was not found on the system.', $application || 'pdflatex') . ' ' . $::locale->text('Please contact your administrator.');
+
+ } elsif (-f "$self->{tmpfile}.err") {
open(FH, "$self->{tmpfile}.err");
@err = <FH>;
close(FH);
}
- if ($self->{tmpfile} && ! $::keep_temp_files) {
+ if ($self->{tmpfile} && !($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})) {
$self->{tmpfile} =~ s|.*/||g;
# strip extension
$self->{tmpfile} =~ s/\.\w+$//g;
my ($self, $myconfig) = @_;
# connect to database
- my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
+ my $dbh = SL::DBConnect->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
or $self->dberror;
# set db options
my ($self, $myconfig) = @_;
# connect to database
- my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
+ my $dbh = SL::DBConnect->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
or $self->dberror;
# set db options
undef $standard_dbh;
}
- $standard_dbh ||= SL::DB::create->dbh;
+ $standard_dbh ||= $self->dbconnect_noauto($myconfig);
$main::lxdebug->leave_sub(2);
my $dbh = $self->dbconnect($myconfig);
my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
- my $sth = prepare_execute_query($self, $dbh, $query, $date);
+ my $sth = prepare_execute_query($self, $dbh, $query, conv_date($date));
+
+ # Falls $date = '' - Fehlermeldung aus der Datenbank. Ich denke,
+ # es ist sicher ein conv_date vorher IMMER auszuführen.
+ # Testfälle ohne definiertes closedto:
+ # Leere Datumseingabe i.O.
+ # SELECT 1 FROM defaults WHERE '' < closedto
+ # normale Zahlungsbuchung über Rechnungsmaske i.O.
+ # SELECT 1 FROM defaults WHERE '10.05.2011' < closedto
+ # Testfälle mit definiertem closedto (30.04.2011):
+ # Leere Datumseingabe i.O.
+ # SELECT 1 FROM defaults WHERE '' < closedto
+ # normale Buchung im geschloßenem Zeitraum i.O.
+ # SELECT 1 FROM defaults WHERE '21.04.2011' < closedto
+ # Fehlermeldung: Es können keine Zahlungen für abgeschlossene Bücher gebucht werden!
+ # normale Buchung in aktiver Buchungsperiode i.O.
+ # SELECT 1 FROM defaults WHERE '01.05.2011' < closedto
+
my ($closed) = $sth->fetchrow_array;
$main::lxdebug->leave_sub();
my ($self, $dbh, $curr, $transdate, $fld) = @_;
my ($query);
- unless ($transdate) {
+ unless ($transdate && $curr) {
$main::lxdebug->leave_sub();
return 1;
}
my $dbh = $self->get_standard_dbh($myconfig);
my $query =
- qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
+ qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long , p.description | .
qq|FROM payment_terms p | .
qq|WHERE p.id = ?|;
($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
- $self->{payment_terms}) =
+ $self->{payment_terms}, $self->{payment_description}) =
selectrow_query($self, $dbh, $query, $self->{payment_id});
if ($transdate eq "") {
$amounts{invtotal} = $self->{invtotal};
$amounts{total} = $self->{total};
}
- $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
-
map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
+ $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
$amounts{skonto_amount} = $amounts{invtotal} * $self->{percent_skonto};
$amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
$amounts{total_wo_skonto} = $amounts{total} * (1 - $self->{percent_skonto});
if ($self->{"language_id"}) {
$query =
- qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
- qq|FROM translation_payment_terms t | .
+ qq|SELECT t.translation, l.output_numberformat, l.output_dateformat, l.output_longdates | .
+ qq|FROM generic_translations t | .
qq|LEFT JOIN language l ON t.language_id = l.id | .
- qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
+ qq|WHERE (t.language_id = ?)
+ AND (t.translation_id = ?)
+ AND (t.translation_type = 'SL::DB::PaymentTerm/description_long')|;
my ($description_long, $output_numberformat, $output_dateformat,
$output_longdates) =
selectrow_query($self, $dbh, $query,
my ($login) = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
if ($login) {
- my $user = User->new($login);
+ my $user = User->new(login => $login);
map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
$self->{$params{prefix} . '_login'} = $login;
$reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
my $dbh = $self->get_standard_dbh($myconfig);
- my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
- my ($duedate) = selectrow_query($self, $dbh, $query, $self->{payment_id});
+ my ($payment_id, $duedate);
+
+ if($self->{payment_id}) {
+ $payment_id = $self->{payment_id};
+ } elsif($self->{vendor_id}) {
+ my $query = 'SELECT payment_id FROM vendor WHERE id = ?';
+ ($payment_id) = selectrow_query($self, $dbh, $query, $self->{vendor_id});
+ }
+
+ if ($payment_id) {
+ my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
+ ($duedate) = selectrow_query($self, $dbh, $query, $payment_id);
+ }
$main::lxdebug->leave_sub();
$key = $params;
}
- my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
+ my $where = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : '';
my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
$key = "all_payments" unless ($key);
- my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
+ my $query = qq|SELECT * FROM payment_terms ORDER BY sortkey|;
$self->{$key} = selectall_hashref_query($self, $dbh, $query);
my $options = ref $key eq 'HASH' ? $key : { key => $key };
$options->{key} ||= "all_customers";
- my $limit_clause = "LIMIT $options->{limit}" if $options->{limit};
+ my $limit_clause = $options->{limit} ? "LIMIT $options->{limit}" : '';
my @where;
push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if $options->{business_is_salesman};
$self->{$key} = selectall_hashref_query($self, $dbh, $query);
if ($bins_key) {
- $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?|;
+ $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?
+ ORDER BY description|;
my $sth = prepare_query($self, $dbh, $query);
foreach my $warehouse (@{ $self->{$key} }) {
return scalar(@{ $self->{name_list} });
}
-# the selection sub is used in the AR, AP, IS, IR and OE module
+# the selection sub is used in the AR, AP, IS, IR, DO and OE module
#
sub all_vc {
$main::lxdebug->enter_sub();
$table = $table eq "customer" ? "customer" : "vendor";
- my $query = qq|SELECT count(*) FROM $table|;
+ # build selection list
+ # Hotfix für Bug 1837 - Besser wäre es alte Buchungsbelege
+ # OHNE Auswahlliste (reines Textfeld) zu laden. Hilft aber auch
+ # nicht für veränderbare Belege (oe, do, ...)
+ my $obsolete = $self->{id} ? '' : "WHERE NOT obsolete";
+ my $query = qq|SELECT count(*) FROM $table $obsolete|;
my ($count) = selectrow_query($self, $dbh, $query);
- # build selection list
- if ($count <= $myconfig->{vclimit}) {
+ if ($count < $myconfig->{vclimit}) {
$query = qq|SELECT id, name, salesman_id
- FROM $table WHERE NOT obsolete
+ FROM $table $obsolete
ORDER BY name|;
$self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
}
# setup sales contacts
$query = qq|SELECT e.id, e.name
FROM employee e
- WHERE (e.sales = '1') AND (NOT e.id = ?)|;
+ WHERE (e.sales = '1') AND (NOT e.id = ?)
+ ORDER BY name|;
$self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
# this is for self
{ id => $self->{employee_id},
name => $self->{employee} });
- # sort the whole thing
- @{ $self->{all_employees} } =
- sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
-
- if ($module eq 'AR') {
-
# prepare query for departments
$query = qq|SELECT id, description
FROM department
- WHERE role = 'P'
ORDER BY description|;
- } else {
- $query = qq|SELECT id, description
- FROM department
- ORDER BY description|;
- }
-
$self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
# get languages
my ($self, $myconfig, $table) = @_;
my $dbh = $self->get_standard_dbh($myconfig);
- my $where;
-
- if ($table eq 'customer') {
- $where = "WHERE role = 'P' ";
- }
my $query = qq|SELECT id, description
FROM department
- $where
ORDER BY description|;
$self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
}
# now get the account numbers
- $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
- FROM chart c, taxkeys tk
- WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
- (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
- ORDER BY c.accno|;
+# $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
+# FROM chart c, taxkeys tk
+# WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
+# (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
+# ORDER BY c.accno|;
+
+# same query as above, but without expensive subquery for each row. about 80% faster
+ $query = qq|
+ SELECT c.accno, c.description, c.link, c.taxkey_id, tk2.tax_id
+ FROM chart c
+ -- find newest entries in taxkeys
+ INNER JOIN (
+ SELECT chart_id, MAX(startdate) AS startdate
+ FROM taxkeys
+ WHERE (startdate <= $transdate)
+ GROUP BY chart_id
+ ) tk ON (c.id = tk.chart_id)
+ -- and load all of those entries
+ INNER JOIN taxkeys tk2
+ ON (tk.chart_id = tk2.chart_id AND tk.startdate = tk2.startdate)
+ WHERE (c.link LIKE ?)
+ ORDER BY c.accno|;
$sth = $dbh->prepare($query);
$self->{TAX} = selectall_hashref_query($self, $dbh, $query);
}
+ my $extra_columns = '';
+ $extra_columns .= 'a.direct_debit, ' if ($module eq 'AR') || ($module eq 'AP');
+
if ($self->{id}) {
$query =
qq|SELECT
a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
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}
c.name AS $table,
d.description AS department,
e.name AS employee
$self->{$key} = $ref->{$key};
}
+ # remove any trailing whitespace
+ $self->{currency} =~ s/\s*$//;
+
my $transdate = "current_date";
if ($self->{transdate}) {
$transdate = $dbh->quote($self->{transdate});
$query =
qq|SELECT
c.accno, c.description,
- a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
+ a.acc_trans_id, a.source, a.amount, a.memo, a.transdate, a.gldate, a.cleared, a.project_id, a.taxkey,
p.projectnumber,
t.rate, t.id
FROM acc_trans a
if ($self->{"$self->{vc}_id"}) {
# only setup currency
- ($self->{currency}) = split(/:/, $self->{currencies});
+ ($self->{currency}) = split(/:/, $self->{currencies}) if !$self->{currency};
} else {
"a.department_id" => "department_id",
"d.description" => "department",
"ct.name" => $table,
+ "ct.curr" => "cv_curr",
"current_date + ct.terms" => "duedate",
);
if ($self->{type} =~ /delivery_order/) {
$arap = 'delivery_orders';
delete $column_map{"a.curr"};
+ delete $column_map{"ct.curr"};
} elsif ($self->{type} =~ /_order/) {
$arap = 'oe';
map { $self->{$_} = $ref->{$_} } values %column_map;
+ # remove any trailing whitespace
+ $self->{currency} =~ s/\s*$// if $self->{currency};
+ $self->{cv_curr} =~ s/\s*$// if $self->{cv_curr};
+
+ # if customer/vendor currency is set use this
+ $self->{currency} = $self->{cv_curr} if $self->{cv_curr};
+
$main::lxdebug->leave_sub();
}
my ($var) = $sth->fetchrow_array;
$sth->finish;
- if ($var =~ m/\d+$/) {
- my $new_var = (substr $var, $-[0]) * 1 + 1;
- my $len_diff = length($var) - $-[0] - length($new_var);
- $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
-
- } else {
- $var = $var . '1';
- }
-
+ $var = SL::PrefixedNumber->new(number => $var)->get_next;
$query = qq|UPDATE defaults SET $fld = ?|;
do_query($self, $dbh, $query, $var);
$self->{formname} ||= $self->{type};
$self->{media} ||= 'email';
- die "'media' other than 'email' or 'file' is not supported yet" unless $self->{media} =~ m/^(?:email|file)$/;
+ die "'media' other than 'email', 'file', 'printer' is not supported yet" unless $self->{media} =~ m/^(?:email|file|printer)$/;
# set shipto from billto unless set
my $has_shipto = any { $self->{"shipto$_"} } qw(name street zipcode city country contact);
$extension = 'xls';
}
- my $email_extension = '_email' if -f "$::myconfig{templates}/$self->{formname}_email$self->{language}.${extension}";
- $self->{IN} = "$self->{formname}${email_extension}$self->{language}.${extension}";
+ my $printer_code = $self->{printer_code} ? '_' . $self->{printer_code} : '';
+ my $email_extension = -f "$::myconfig{templates}/$self->{formname}_email${language}.${extension}" ? '_email' : '';
+ $self->{IN} = "$self->{formname}${email_extension}${language}${printer_code}.${extension}";
# Format dates.
$self->format_dates($output_dateformat, $output_longdates,
$::myconfig{numberformat} = $saved_numberformat;
}
+sub layout {
+ my ($self) = @_;
+ $::lxdebug->enter_sub;
+
+ my %style_to_script_map = (
+ v3 => 'v3',
+ neu => 'new',
+ );
+
+ my $menu_script = $style_to_script_map{$::myconfig{menustyle}} || '';
+
+ package main;
+ require "bin/mozilla/menu$menu_script.pl";
+ package Form;
+ require SL::Controller::FrameHeader;
+
+
+ my $layout = SL::Controller::FrameHeader->new->action_header . ::render();
+
+ $::lxdebug->leave_sub;
+ return $layout;
+}
+
1;
__END__
=head1 SYNOPSIS
-This is the main data object of Lx-Office.
+This is the main data object of kivitendo.
Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
Points of interest for a beginner are:
=head1 SPECIAL FUNCTIONS
-=head2 C<_store_value()>
-
-parses a complex var name, and stores it in the form.
-
-syntax:
- $form->_store_value($key, $value);
-
-keys must start with a string, and can contain various tokens.
-supported key structures are:
-
-1. simple access
- simple key strings work as expected
-
- id => $form->{id}
-
-2. hash access.
- separating two keys by a dot (.) will result in a hash lookup for the inner value
- this is similar to the behaviour of java and templating mechanisms.
-
- filter.description => $form->{filter}->{description}
-
-3. array+hashref access
-
- adding brackets ([]) before the dot will cause the next hash to be put into an array.
- using [+] instead of [] will force a new array index. this is useful for recurring
- data structures like part lists. put a [+] into the first varname, and use [] on the
- following ones.
-
- repeating these names in your template:
-
- invoice.items[+].id
- invoice.items[].parts_id
-
- will result in:
-
- $form->{invoice}->{items}->[
- {
- id => ...
- parts_id => ...
- },
- {
- id => ...
- parts_id => ...
- }
- ...
- ]
-
-4. arrays
-
- using brackets at the end of a name will result in a pure array to be created.
- note that you mustn't use [+], which is reserved for array+hash access and will
- result in undefined behaviour in array context.
-
- filter.status[] => $form->{status}->[ val1, val2, ... ]
-
=head2 C<update_business> PARAMS
PARAMS (not named):
Generates a HTTP redirection header for the new C<$url>. Constructs an
absolute URL including scheme, host name and port. If C<$url> is a
-relative URL then it is considered relative to Lx-Office base URL.
+relative URL then it is considered relative to kivitendo base URL.
This function C<die>s if headers have already been created with
C<$::form-E<gt>header>.
=head2 C<header>
Generates a general purpose http/html header and includes most of the scripts
-ans stylesheets needed.
+and stylesheets needed. Stylesheets can be added with L<use_stylesheet>.
Only one header will be generated. If the method was already called in this
request it will not output anything and return undef. Also if no
=item stylesheet
-=item stylesheets
-
-If these are arrayrefs the contents will be inlined into the header.
+Either a scalar or an array ref. Will be inlined into the header. Add
+stylesheets with the L<use_stylesheet> function.
=item landscape