1 #====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 1998-2002
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
15 # Contributors: Thomas Bayen <bayen@gmx.de>
16 # Antti Kaihola <akaihola@siba.fi>
17 # Moritz Bunkus (tex code)
19 # This program is free software; you can redistribute it and/or modify
20 # it under the terms of the GNU General Public License as published by
21 # the Free Software Foundation; either version 2 of the License, or
22 # (at your option) any later version.
24 # This program is distributed in the hope that it will be useful,
25 # but WITHOUT ANY WARRANTY; without even the implied warranty of
26 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 # GNU General Public License for more details.
28 # You should have received a copy of the GNU General Public License
29 # along with this program; if not, write to the Free Software
30 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #======================================================================
32 # Utilities for parsing forms
33 # and supporting routines for linking account numbers
34 # used in AR, AP and IS, IR modules
36 #======================================================================
68 use List::Util qw(first max min sum);
69 use List::MoreUtils qw(all any apply);
76 disconnect_standard_dbh();
79 sub disconnect_standard_dbh {
80 return unless $standard_dbh;
81 $standard_dbh->disconnect();
86 $main::lxdebug->enter_sub(2);
92 my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
97 $curr = \ $self->{ shift @tokens };
101 my $sep = shift @tokens;
102 my $key = shift @tokens;
104 $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
105 $curr = \ $$curr->[max 0, $#$$curr] if $sep eq '[].';
106 $curr = \ $$curr->[++$#$$curr] if $sep eq '[+].';
107 $curr = \ $$curr->{$key}
112 $main::lxdebug->leave_sub(2);
118 $main::lxdebug->enter_sub(2);
123 my @pairs = split(/&/, $input);
126 my ($key, $value) = split(/=/, $_, 2);
127 $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
130 $main::lxdebug->leave_sub(2);
133 sub _request_to_hash {
134 $main::lxdebug->enter_sub(2);
140 if (!$ENV{'CONTENT_TYPE'}
141 || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
143 $self->_input_to_hash($input);
145 $main::lxdebug->leave_sub(2);
149 my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
151 my $boundary = '--' . $1;
153 foreach my $line (split m/\n/, $input) {
154 last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
156 if (($line eq $boundary) || ($line eq "$boundary\r")) {
157 ${ $previous } =~ s|\r?\n$|| if $previous;
163 $content_type = "text/plain";
170 next unless $boundary_found;
172 if (!$headers_done) {
173 $line =~ s/[\r\n]*$//;
180 if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
181 if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
183 substr $line, $-[0], $+[0] - $-[0], "";
186 if ($line =~ m|name\s*=\s*"(.*?)"|i) {
188 substr $line, $-[0], $+[0] - $-[0], "";
191 $previous = _store_value($uploads, $name, '') if ($name);
192 $self->{FILENAME} = $filename if ($filename);
197 if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
204 next unless $previous;
206 ${ $previous } .= "${line}\n";
209 ${ $previous } =~ s|\r?\n$|| if $previous;
211 $main::lxdebug->leave_sub(2);
216 sub _recode_recursively {
217 $main::lxdebug->enter_sub();
218 my ($iconv, $param) = @_;
220 if (any { ref $param eq $_ } qw(Form HASH)) {
221 foreach my $key (keys %{ $param }) {
222 if (!ref $param->{$key}) {
223 # Workaround for a bug: converting $param->{$key} directly
224 # leads to 'undef'. I don't know why. Converting a copy works,
226 $param->{$key} = $iconv->convert("" . $param->{$key});
228 _recode_recursively($iconv, $param->{$key});
232 } elsif (ref $param eq 'ARRAY') {
233 foreach my $idx (0 .. scalar(@{ $param }) - 1) {
234 if (!ref $param->[$idx]) {
235 # Workaround for a bug: converting $param->[$idx] directly
236 # leads to 'undef'. I don't know why. Converting a copy works,
238 $param->[$idx] = $iconv->convert("" . $param->[$idx]);
240 _recode_recursively($iconv, $param->[$idx]);
244 $main::lxdebug->leave_sub();
248 $main::lxdebug->enter_sub();
254 if ($LXDebug::watch_form) {
255 require SL::Watchdog;
256 tie %{ $self }, 'SL::Watchdog';
261 $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
262 $self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
265 if ($ENV{CONTENT_LENGTH}) {
267 read STDIN, $content, $ENV{CONTENT_LENGTH};
268 $uploads = $self->_request_to_hash($content);
271 my $db_charset = $::lx_office_conf{system}->{dbcharset};
272 $db_charset ||= Common::DEFAULT_CHARSET;
274 my $encoding = $self->{INPUT_ENCODING} || $db_charset;
275 delete $self->{INPUT_ENCODING};
277 _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
279 map { $self->{$_} = $uploads->{$_} } keys %{ $uploads } if $uploads;
281 #$self->{version} = "2.6.1"; # Old hardcoded but secure style
282 open VERSION_FILE, "VERSION"; # New but flexible code reads version from VERSION-file
283 $self->{version} = <VERSION_FILE>;
285 $self->{version} =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
287 $main::lxdebug->leave_sub();
292 sub _flatten_variables_rec {
293 $main::lxdebug->enter_sub(2);
302 if ('' eq ref $curr->{$key}) {
303 @result = ({ 'key' => $prefix . $key, 'value' => $curr->{$key} });
305 } elsif ('HASH' eq ref $curr->{$key}) {
306 foreach my $hash_key (sort keys %{ $curr->{$key} }) {
307 push @result, $self->_flatten_variables_rec($curr->{$key}, $prefix . $key . '.', $hash_key);
311 foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
312 my $first_array_entry = 1;
314 foreach my $hash_key (sort keys %{ $curr->{$key}->[$idx] }) {
315 push @result, $self->_flatten_variables_rec($curr->{$key}->[$idx], $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
316 $first_array_entry = 0;
321 $main::lxdebug->leave_sub(2);
326 sub flatten_variables {
327 $main::lxdebug->enter_sub(2);
335 push @variables, $self->_flatten_variables_rec($self, '', $_);
338 $main::lxdebug->leave_sub(2);
343 sub flatten_standard_variables {
344 $main::lxdebug->enter_sub(2);
347 my %skip_keys = map { $_ => 1 } (qw(login password header stylesheet titlebar version), @_);
351 foreach (grep { ! $skip_keys{$_} } keys %{ $self }) {
352 push @variables, $self->_flatten_variables_rec($self, '', $_);
355 $main::lxdebug->leave_sub(2);
361 $main::lxdebug->enter_sub();
367 map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
369 $main::lxdebug->leave_sub();
373 $main::lxdebug->enter_sub(2);
376 my $password = $self->{password};
378 $self->{password} = 'X' x 8;
380 local $Data::Dumper::Sortkeys = 1;
381 my $output = Dumper($self);
383 $self->{password} = $password;
385 $main::lxdebug->leave_sub(2);
391 $main::lxdebug->enter_sub(2);
393 my ($self, $str) = @_;
395 $str = Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
396 $str =~ s/([^a-zA-Z0-9_.:-])/sprintf("%%%02x", ord($1))/ge;
398 $main::lxdebug->leave_sub(2);
404 $main::lxdebug->enter_sub(2);
406 my ($self, $str) = @_;
411 $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
412 $str = Encode::decode('utf-8-strict', $str) if $::locale->is_utf8;
414 $main::lxdebug->leave_sub(2);
420 $main::lxdebug->enter_sub();
421 my ($self, $str) = @_;
423 if ($str && !ref($str)) {
424 $str =~ s/\"/"/g;
427 $main::lxdebug->leave_sub();
433 $main::lxdebug->enter_sub();
434 my ($self, $str) = @_;
436 if ($str && !ref($str)) {
437 $str =~ s/"/\"/g;
440 $main::lxdebug->leave_sub();
446 $main::lxdebug->enter_sub();
450 map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
452 for (sort keys %$self) {
453 next if (($_ eq "header") || (ref($self->{$_}) ne ""));
454 print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
457 $main::lxdebug->leave_sub();
461 my ($self, $code) = @_;
462 local $self->{__ERROR_HANDLER} = sub { die SL::X::FormError->new($_[0]) };
467 $main::lxdebug->enter_sub();
469 $main::lxdebug->show_backtrace();
471 my ($self, $msg) = @_;
473 if ($self->{__ERROR_HANDLER}) {
474 $self->{__ERROR_HANDLER}->($msg);
476 } elsif ($ENV{HTTP_USER_AGENT}) {
478 $self->show_generic_error($msg);
481 print STDERR "Error: $msg\n";
485 $main::lxdebug->leave_sub();
489 $main::lxdebug->enter_sub();
491 my ($self, $msg) = @_;
493 if ($ENV{HTTP_USER_AGENT}) {
496 if (!$self->{header}) {
502 <p class="message_ok"><b>$msg</b></p>
504 <script type="text/javascript">
506 // If JavaScript is enabled, the whole thing will be reloaded.
507 // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
508 // it now loads the correct code into the browser instead of do nothing.
509 setTimeout("top.frames.location.href='login.pl'",500);
518 if ($self->{info_function}) {
519 &{ $self->{info_function} }($msg);
525 $main::lxdebug->leave_sub();
528 # calculates the number of rows in a textarea based on the content and column number
529 # can be capped with maxrows
531 $main::lxdebug->enter_sub();
532 my ($self, $str, $cols, $maxrows, $minrows) = @_;
536 my $rows = sum map { int((length() - 2) / $cols) + 1 } split /\r/, $str;
539 $main::lxdebug->leave_sub();
541 return max(min($rows, $maxrows), $minrows);
545 $main::lxdebug->enter_sub();
547 my ($self, $msg) = @_;
549 $self->error("$msg\n" . $DBI::errstr);
551 $main::lxdebug->leave_sub();
555 $main::lxdebug->enter_sub();
557 my ($self, $name, $msg) = @_;
560 foreach my $part (split m/\./, $name) {
561 if (!$curr->{$part} || ($curr->{$part} =~ /^\s*$/)) {
564 $curr = $curr->{$part};
567 $main::lxdebug->leave_sub();
570 sub _get_request_uri {
573 return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
575 my $scheme = $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
576 my $port = $ENV{SERVER_PORT} || '';
577 $port = undef if (($scheme eq 'http' ) && ($port == 80))
578 || (($scheme eq 'https') && ($port == 443));
580 my $uri = URI->new("${scheme}://");
581 $uri->scheme($scheme);
583 $uri->host($ENV{HTTP_HOST} || $ENV{SERVER_ADDR});
584 $uri->path_query($ENV{REQUEST_URI});
590 sub _add_to_request_uri {
593 my $relative_new_path = shift;
594 my $request_uri = shift || $self->_get_request_uri;
595 my $relative_new_uri = URI->new($relative_new_path);
596 my @request_segments = $request_uri->path_segments;
598 my $new_uri = $request_uri->clone;
599 $new_uri->path_segments(@request_segments[0..scalar(@request_segments) - 2], $relative_new_uri->path_segments);
604 sub create_http_response {
605 $main::lxdebug->enter_sub();
610 my $cgi = $main::cgi;
611 $cgi ||= CGI->new('');
614 if (defined $main::auth) {
615 my $uri = $self->_get_request_uri;
616 my @segments = $uri->path_segments;
618 $uri->path_segments(@segments);
620 my $session_cookie_value = $main::auth->get_session_id();
622 if ($session_cookie_value) {
623 $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
624 '-value' => $session_cookie_value,
625 '-path' => $uri->path,
626 '-secure' => $ENV{HTTPS});
630 my %cgi_params = ('-type' => $params{content_type});
631 $cgi_params{'-charset'} = $params{charset} if ($params{charset});
632 $cgi_params{'-cookie'} = $session_cookie if ($session_cookie);
634 map { $cgi_params{'-' . $_} = $params{$_} if exists $params{$_} } qw(content_disposition content_length);
636 my $output = $cgi->header(%cgi_params);
638 $main::lxdebug->leave_sub();
645 $::lxdebug->enter_sub;
647 # extra code is currently only used by menuv3 and menuv4 to set their css.
648 # it is strongly deprecated, and will be changed in a future version.
649 my ($self, $extra_code) = @_;
650 my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
653 $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
655 $self->{favicon} ||= "favicon.ico";
656 $self->{titlebar} = "$self->{title} - $self->{titlebar}" if $self->{title};
659 if ($self->{refresh_url} || $self->{refresh_time}) {
660 my $refresh_time = $self->{refresh_time} || 3;
661 my $refresh_url = $self->{refresh_url} || $ENV{REFERER};
662 push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
665 push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
666 for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
668 push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
669 push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
670 push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
671 '<script type="text/javascript" src="js/common.js"></script>',
672 '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
673 '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
674 '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
675 '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
676 '<script type="text/javascript" src="js/part_selection.js"></script>';
677 push @header, $self->{javascript} if $self->{javascript};
678 push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
679 push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
680 push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
681 join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
683 # if there is a title, we put some JavaScript in to the page, wich writes a
684 # meaningful title-tag for our frameset.
686 if ($self->{title}) {
688 <script type="text/javascript">
690 // Write a meaningful title-tag for our frameset.
691 top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
697 print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
698 print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
699 if $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
703 <meta http-equiv="Content-Type" content="text/html; charset=$db_charset">
704 <title>$self->{titlebar}</title>
706 print " $_\n" for @header;
708 <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
709 <meta name="robots" content="noindex,nofollow" />
710 <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
711 <script type="text/javascript" src="js/tabcontent.js">
713 /***********************************************
714 * Tab Content script v2.2- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
715 * This notice MUST stay intact for legal use
716 * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
717 ***********************************************/
726 $::lxdebug->leave_sub;
729 sub ajax_response_header {
730 $main::lxdebug->enter_sub();
734 my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
735 my $cgi = $main::cgi || CGI->new('');
736 my $output = $cgi->header('-charset' => $db_charset);
738 $main::lxdebug->leave_sub();
743 sub redirect_header {
747 my $base_uri = $self->_get_request_uri;
748 my $new_uri = URI->new_abs($new_url, $base_uri);
750 die "Headers already sent" if $self->{header};
753 my $cgi = $main::cgi || CGI->new('');
754 return $cgi->redirect($new_uri);
757 sub set_standard_title {
758 $::lxdebug->enter_sub;
761 $self->{titlebar} = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
762 $self->{titlebar} .= "- $::myconfig{name}" if $::myconfig{name};
763 $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
765 $::lxdebug->leave_sub;
768 sub _prepare_html_template {
769 $main::lxdebug->enter_sub();
771 my ($self, $file, $additional_params) = @_;
774 if (!%::myconfig || !$::myconfig{"countrycode"}) {
775 $language = $::lx_office_conf{system}->{language};
777 $language = $main::myconfig{"countrycode"};
779 $language = "de" unless ($language);
781 if (-f "templates/webpages/${file}.html") {
782 $file = "templates/webpages/${file}.html";
785 my $info = "Web page template '${file}' not found.\n";
786 print qq|<pre>$info</pre>|;
790 if ($self->{"DEBUG"}) {
791 $additional_params->{"DEBUG"} = $self->{"DEBUG"};
794 if ($additional_params->{"DEBUG"}) {
795 $additional_params->{"DEBUG"} =
796 "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
799 if (%main::myconfig) {
800 $::myconfig{jsc_dateformat} = apply {
804 } $::myconfig{"dateformat"};
805 $additional_params->{"myconfig"} ||= \%::myconfig;
806 map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
809 $additional_params->{"conf_dbcharset"} = $::lx_office_conf{system}->{dbcharset};
810 $additional_params->{"conf_webdav"} = $::lx_office_conf{features}->{webdav};
811 $additional_params->{"conf_lizenzen"} = $::lx_office_conf{features}->{lizenzen};
812 $additional_params->{"conf_latex_templates"} = $::lx_office_conf{print_templates}->{latex};
813 $additional_params->{"conf_opendocument_templates"} = $::lx_office_conf{print_templates}->{opendocument};
814 $additional_params->{"conf_vertreter"} = $::lx_office_conf{features}->{vertreter};
815 $additional_params->{"conf_show_best_before"} = $::lx_office_conf{features}->{show_best_before};
816 $additional_params->{"conf_parts_image_css"} = $::lx_office_conf{features}->{parts_image_css};
817 $additional_params->{"conf_parts_listing_images"} = $::lx_office_conf{features}->{parts_listing_images};
818 $additional_params->{"conf_parts_show_image"} = $::lx_office_conf{features}->{parts_show_image};
820 if (%main::debug_options) {
821 map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
824 if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
825 while (my ($key, $value) = each %{ $main::auth->{RIGHTS}->{$self->{login}} }) {
826 $additional_params->{"AUTH_RIGHTS_" . uc($key)} = $value;
830 $main::lxdebug->leave_sub();
835 sub parse_html_template {
836 $main::lxdebug->enter_sub();
838 my ($self, $file, $additional_params) = @_;
840 $additional_params ||= { };
842 my $real_file = $self->_prepare_html_template($file, $additional_params);
843 my $template = $self->template || $self->init_template;
845 map { $additional_params->{$_} ||= $self->{$_} } keys %{ $self };
848 $template->process($real_file, $additional_params, \$output) || die $template->error;
850 $main::lxdebug->leave_sub();
858 return if $self->template;
860 return $self->template(Template->new({
865 'PLUGIN_BASE' => 'SL::Template::Plugin',
866 'INCLUDE_PATH' => '.:templates/webpages',
867 'COMPILE_EXT' => '.tcc',
868 'COMPILE_DIR' => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
874 $self->{template_object} = shift if @_;
875 return $self->{template_object};
878 sub show_generic_error {
879 $main::lxdebug->enter_sub();
881 my ($self, $error, %params) = @_;
883 if ($self->{__ERROR_HANDLER}) {
884 $self->{__ERROR_HANDLER}->($error);
885 $main::lxdebug->leave_sub();
890 'title_error' => $params{title},
891 'label_error' => $error,
894 if ($params{action}) {
897 map { delete($self->{$_}); } qw(action);
898 map { push @vars, { "name" => $_, "value" => $self->{$_} } if (!ref($self->{$_})); } keys %{ $self };
900 $add_params->{SHOW_BUTTON} = 1;
901 $add_params->{BUTTON_LABEL} = $params{label} || $params{action};
902 $add_params->{VARIABLES} = \@vars;
904 } elsif ($params{back_button}) {
905 $add_params->{SHOW_BACK_BUTTON} = 1;
908 $self->{title} = $params{title} if $params{title};
911 print $self->parse_html_template("generic/error", $add_params);
913 print STDERR "Error: $error\n";
915 $main::lxdebug->leave_sub();
920 sub show_generic_information {
921 $main::lxdebug->enter_sub();
923 my ($self, $text, $title) = @_;
926 'title_information' => $title,
927 'label_information' => $text,
930 $self->{title} = $title if ($title);
933 print $self->parse_html_template("generic/information", $add_params);
935 $main::lxdebug->leave_sub();
940 # write Trigger JavaScript-Code ($qty = quantity of Triggers)
941 # changed it to accept an arbitrary number of triggers - sschoeling
943 $main::lxdebug->enter_sub();
946 my $myconfig = shift;
949 # set dateform for jsscript
952 "dd.mm.yy" => "%d.%m.%Y",
953 "dd-mm-yy" => "%d-%m-%Y",
954 "dd/mm/yy" => "%d/%m/%Y",
955 "mm/dd/yy" => "%m/%d/%Y",
956 "mm-dd-yy" => "%m-%d-%Y",
957 "yyyy-mm-dd" => "%Y-%m-%d",
960 my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
961 $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
968 inputField : "| . (shift) . qq|",
969 ifFormat :"$ifFormat",
970 align : "| . (shift) . qq|",
971 button : "| . (shift) . qq|"
977 <script type="text/javascript">
978 <!--| . join("", @triggers) . qq|//-->
982 $main::lxdebug->leave_sub();
985 } #end sub write_trigger
988 $main::lxdebug->enter_sub();
990 my ($self, $msg) = @_;
992 if (!$self->{callback}) {
996 print $::form->redirect_header($self->{callback});
1001 $main::lxdebug->leave_sub();
1004 # sort of columns removed - empty sub
1006 $main::lxdebug->enter_sub();
1008 my ($self, @columns) = @_;
1010 $main::lxdebug->leave_sub();
1016 $main::lxdebug->enter_sub(2);
1018 my ($self, $myconfig, $amount, $places, $dash) = @_;
1020 if ($amount eq "") {
1024 # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
1026 my $neg = ($amount =~ s/^-//);
1027 my $exp = ($amount =~ m/[e]/) ? 1 : 0;
1029 if (defined($places) && ($places ne '')) {
1035 my ($actual_places) = ($amount =~ /\.(\d+)/);
1036 $actual_places = length($actual_places);
1037 $places = $actual_places > $places ? $actual_places : $places;
1040 $amount = $self->round_amount($amount, $places);
1043 my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
1044 my @p = split(/\./, $amount); # split amount at decimal point
1046 $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
1049 $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
1052 ($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) :
1053 ($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) :
1054 ($neg ? "-$amount" : "$amount" ) ;
1058 $main::lxdebug->leave_sub(2);
1062 sub format_amount_units {
1063 $main::lxdebug->enter_sub();
1068 my $myconfig = \%main::myconfig;
1069 my $amount = $params{amount} * 1;
1070 my $places = $params{places};
1071 my $part_unit_name = $params{part_unit};
1072 my $amount_unit_name = $params{amount_unit};
1073 my $conv_units = $params{conv_units};
1074 my $max_places = $params{max_places};
1076 if (!$part_unit_name) {
1077 $main::lxdebug->leave_sub();
1081 AM->retrieve_all_units();
1082 my $all_units = $main::all_units;
1084 if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
1085 $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
1088 if (!scalar @{ $conv_units }) {
1089 my $result = $self->format_amount($myconfig, $amount, $places, undef, $max_places) . " " . $part_unit_name;
1090 $main::lxdebug->leave_sub();
1094 my $part_unit = $all_units->{$part_unit_name};
1095 my $conv_unit = ($amount_unit_name && ($amount_unit_name ne $part_unit_name)) ? $all_units->{$amount_unit_name} : $part_unit;
1097 $amount *= $conv_unit->{factor};
1102 foreach my $unit (@$conv_units) {
1103 my $last = $unit->{name} eq $part_unit->{name};
1105 $num = int($amount / $unit->{factor});
1106 $amount -= $num * $unit->{factor};
1109 if ($last ? $amount : $num) {
1110 push @values, { "unit" => $unit->{name},
1111 "amount" => $last ? $amount / $unit->{factor} : $num,
1112 "places" => $last ? $places : 0 };
1119 push @values, { "unit" => $part_unit_name,
1124 my $result = join " ", map { $self->format_amount($myconfig, $_->{amount}, $_->{places}, undef, $max_places), $_->{unit} } @values;
1126 $main::lxdebug->leave_sub();
1132 $main::lxdebug->enter_sub(2);
1137 $input =~ s/(^|[^\#]) \# (\d+) /$1$_[$2 - 1]/gx;
1138 $input =~ s/(^|[^\#]) \#\{(\d+)\}/$1$_[$2 - 1]/gx;
1139 $input =~ s/\#\#/\#/g;
1141 $main::lxdebug->leave_sub(2);
1149 $main::lxdebug->enter_sub(2);
1151 my ($self, $myconfig, $amount) = @_;
1153 if ( ($myconfig->{numberformat} eq '1.000,00')
1154 || ($myconfig->{numberformat} eq '1000,00')) {
1159 if ($myconfig->{numberformat} eq "1'000.00") {
1165 $main::lxdebug->leave_sub(2);
1167 return ($amount * 1);
1171 $main::lxdebug->enter_sub(2);
1173 my ($self, $amount, $places) = @_;
1176 # Rounding like "Kaufmannsrunden" (see http://de.wikipedia.org/wiki/Rundung )
1178 # Round amounts to eight places before rounding to the requested
1179 # number of places. This gets rid of errors due to internal floating
1180 # point representation.
1181 $amount = $self->round_amount($amount, 8) if $places < 8;
1182 $amount = $amount * (10**($places));
1183 $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
1185 $main::lxdebug->leave_sub(2);
1187 return $round_amount;
1191 sub parse_template {
1192 $main::lxdebug->enter_sub();
1194 my ($self, $myconfig) = @_;
1199 my $userspath = $::lx_office_conf{paths}->{userspath};
1201 $self->{"cwd"} = getcwd();
1202 $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
1207 if ($self->{"format"} =~ /(opendocument|oasis)/i) {
1208 $template_type = 'OpenDocument';
1209 $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
1211 } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
1212 $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
1213 $template_type = 'LaTeX';
1214 $ext_for_format = 'pdf';
1216 } elsif (($self->{"format"} =~ /html/i) || (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
1217 $template_type = 'HTML';
1218 $ext_for_format = 'html';
1220 } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
1221 $template_type = 'XML';
1222 $ext_for_format = 'xml';
1224 } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
1225 $template_type = 'XML';
1227 } elsif ( $self->{"format"} =~ /excel/i ) {
1228 $template_type = 'Excel';
1229 $ext_for_format = 'xls';
1231 } elsif ( defined $self->{'format'}) {
1232 $self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
1234 } elsif ( $self->{'format'} eq '' ) {
1235 $self->error("No Outputformat given: $self->{'format'}");
1237 } else { #Catch the rest
1238 $self->error("Outputformat not defined: $self->{'format'}");
1241 my $template = SL::Template::create(type => $template_type,
1242 file_name => $self->{IN},
1244 myconfig => $myconfig,
1245 userspath => $userspath);
1247 # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
1248 $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
1250 if (!$self->{employee_id}) {
1251 map { $self->{"employee_${_}"} = $myconfig->{$_}; } qw(email tel fax name signature company address businessnumber co_ustid taxnumber duns);
1254 map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
1255 map { $self->{"myconfig_${_}"} = $myconfig->{$_} } grep { $_ ne 'dbpasswd' } keys %{ $myconfig };
1257 $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
1259 # OUT is used for the media, screen, printer, email
1260 # for postscript we store a copy in a temporary file
1262 my $prepend_userspath;
1264 if (!$self->{tmpfile}) {
1265 $self->{tmpfile} = "${fileid}.$self->{IN}";
1266 $prepend_userspath = 1;
1269 $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
1271 $self->{tmpfile} =~ s|.*/||;
1272 $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
1273 $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
1275 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1276 $out = $self->{OUT};
1277 $self->{OUT} = ">$self->{tmpfile}";
1283 open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
1284 $result = $template->parse(*OUT);
1289 $result = $template->parse(*STDOUT);
1294 $self->error("$self->{IN} : " . $template->get_error());
1297 if ($self->{media} eq 'file') {
1298 copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
1300 chdir("$self->{cwd}");
1302 $::lxdebug->leave_sub();
1307 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1309 if ($self->{media} eq 'email') {
1311 my $mail = new Mailer;
1313 map { $mail->{$_} = $self->{$_} }
1314 qw(cc bcc subject message version format);
1315 $mail->{charset} = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
1316 $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
1317 $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
1318 $mail->{fileid} = "$fileid.";
1319 $myconfig->{signature} =~ s/\r//g;
1321 # if we send html or plain text inline
1322 if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
1323 $mail->{contenttype} = "text/html";
1325 $mail->{message} =~ s/\r//g;
1326 $mail->{message} =~ s/\n/<br>\n/g;
1327 $myconfig->{signature} =~ s/\n/<br>\n/g;
1328 $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
1330 open(IN, $self->{tmpfile})
1331 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1333 $mail->{message} .= $_;
1340 if (!$self->{"do_not_attach"}) {
1341 my $attachment_name = $self->{attachment_filename} || $self->{tmpfile};
1342 $attachment_name =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
1343 $mail->{attachments} = [{ "filename" => $self->{tmpfile},
1344 "name" => $attachment_name }];
1347 $mail->{message} =~ s/\r//g;
1348 $mail->{message} .= "\n-- \n$myconfig->{signature}";
1352 my $err = $mail->send();
1353 $self->error($self->cleanup . "$err") if ($err);
1357 $self->{OUT} = $out;
1359 my $numbytes = (-s $self->{tmpfile});
1360 open(IN, $self->{tmpfile})
1361 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1364 $self->{copies} = 1 unless $self->{media} eq 'printer';
1366 chdir("$self->{cwd}");
1367 #print(STDERR "Kopien $self->{copies}\n");
1368 #print(STDERR "OUT $self->{OUT}\n");
1369 for my $i (1 .. $self->{copies}) {
1371 open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
1372 print OUT while <IN>;
1377 $self->{attachment_filename} = ($self->{attachment_filename})
1378 ? $self->{attachment_filename}
1379 : $self->generate_attachment_filename();
1381 # launch application
1382 print qq|Content-Type: | . $template->get_mime_type() . qq|
1383 Content-Disposition: attachment; filename="$self->{attachment_filename}"
1384 Content-Length: $numbytes
1388 $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
1399 chdir("$self->{cwd}");
1400 $main::lxdebug->leave_sub();
1403 sub get_formname_translation {
1404 $main::lxdebug->enter_sub();
1405 my ($self, $formname) = @_;
1407 $formname ||= $self->{formname};
1409 my %formname_translations = (
1410 bin_list => $main::locale->text('Bin List'),
1411 credit_note => $main::locale->text('Credit Note'),
1412 invoice => $main::locale->text('Invoice'),
1413 pick_list => $main::locale->text('Pick List'),
1414 proforma => $main::locale->text('Proforma Invoice'),
1415 purchase_order => $main::locale->text('Purchase Order'),
1416 request_quotation => $main::locale->text('RFQ'),
1417 sales_order => $main::locale->text('Confirmation'),
1418 sales_quotation => $main::locale->text('Quotation'),
1419 storno_invoice => $main::locale->text('Storno Invoice'),
1420 sales_delivery_order => $main::locale->text('Delivery Order'),
1421 purchase_delivery_order => $main::locale->text('Delivery Order'),
1422 dunning => $main::locale->text('Dunning'),
1425 $main::lxdebug->leave_sub();
1426 return $formname_translations{$formname}
1429 sub get_number_prefix_for_type {
1430 $main::lxdebug->enter_sub();
1434 (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
1435 : ($self->{type} =~ /_quotation$/) ? 'quo'
1436 : ($self->{type} =~ /_delivery_order$/) ? 'do'
1439 $main::lxdebug->leave_sub();
1443 sub get_extension_for_format {
1444 $main::lxdebug->enter_sub();
1447 my $extension = $self->{format} =~ /pdf/i ? ".pdf"
1448 : $self->{format} =~ /postscript/i ? ".ps"
1449 : $self->{format} =~ /opendocument/i ? ".odt"
1450 : $self->{format} =~ /excel/i ? ".xls"
1451 : $self->{format} =~ /html/i ? ".html"
1454 $main::lxdebug->leave_sub();
1458 sub generate_attachment_filename {
1459 $main::lxdebug->enter_sub();
1462 my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1463 my $prefix = $self->get_number_prefix_for_type();
1465 if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
1466 $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
1468 } elsif ($attachment_filename && $self->{"${prefix}number"}) {
1469 $attachment_filename .= "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
1472 $attachment_filename = "";
1475 $attachment_filename = $main::locale->quote_special_chars('filenames', $attachment_filename);
1476 $attachment_filename =~ s|[\s/\\]+|_|g;
1478 $main::lxdebug->leave_sub();
1479 return $attachment_filename;
1482 sub generate_email_subject {
1483 $main::lxdebug->enter_sub();
1486 my $subject = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1487 my $prefix = $self->get_number_prefix_for_type();
1489 if ($subject && $self->{"${prefix}number"}) {
1490 $subject .= " " . $self->{"${prefix}number"}
1493 $main::lxdebug->leave_sub();
1498 $main::lxdebug->enter_sub();
1502 chdir("$self->{tmpdir}");
1505 if (-f "$self->{tmpfile}.err") {
1506 open(FH, "$self->{tmpfile}.err");
1511 if ($self->{tmpfile} && !($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})) {
1512 $self->{tmpfile} =~ s|.*/||g;
1514 $self->{tmpfile} =~ s/\.\w+$//g;
1515 my $tmpfile = $self->{tmpfile};
1516 unlink(<$tmpfile.*>);
1519 chdir("$self->{cwd}");
1521 $main::lxdebug->leave_sub();
1527 $main::lxdebug->enter_sub();
1529 my ($self, $date, $myconfig) = @_;
1532 if ($date && $date =~ /\D/) {
1534 if ($myconfig->{dateformat} =~ /^yy/) {
1535 ($yy, $mm, $dd) = split /\D/, $date;
1537 if ($myconfig->{dateformat} =~ /^mm/) {
1538 ($mm, $dd, $yy) = split /\D/, $date;
1540 if ($myconfig->{dateformat} =~ /^dd/) {
1541 ($dd, $mm, $yy) = split /\D/, $date;
1546 $yy = ($yy < 70) ? $yy + 2000 : $yy;
1547 $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
1549 $dd = "0$dd" if ($dd < 10);
1550 $mm = "0$mm" if ($mm < 10);
1552 $date = "$yy$mm$dd";
1555 $main::lxdebug->leave_sub();
1560 # Database routines used throughout
1562 sub _dbconnect_options {
1564 my $options = { pg_enable_utf8 => $::locale->is_utf8,
1571 $main::lxdebug->enter_sub(2);
1573 my ($self, $myconfig) = @_;
1575 # connect to database
1576 my $dbh = SL::DBConnect->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
1580 if ($myconfig->{dboptions}) {
1581 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1584 $main::lxdebug->leave_sub(2);
1589 sub dbconnect_noauto {
1590 $main::lxdebug->enter_sub();
1592 my ($self, $myconfig) = @_;
1594 # connect to database
1595 my $dbh = SL::DBConnect->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
1599 if ($myconfig->{dboptions}) {
1600 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1603 $main::lxdebug->leave_sub();
1608 sub get_standard_dbh {
1609 $main::lxdebug->enter_sub(2);
1612 my $myconfig = shift || \%::myconfig;
1614 if ($standard_dbh && !$standard_dbh->{Active}) {
1615 $main::lxdebug->message(LXDebug->INFO(), "get_standard_dbh: \$standard_dbh is defined but not Active anymore");
1616 undef $standard_dbh;
1619 $standard_dbh ||= $self->dbconnect_noauto($myconfig);
1621 $main::lxdebug->leave_sub(2);
1623 return $standard_dbh;
1627 $main::lxdebug->enter_sub();
1629 my ($self, $date, $myconfig) = @_;
1630 my $dbh = $self->dbconnect($myconfig);
1632 my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
1633 my $sth = prepare_execute_query($self, $dbh, $query, $date);
1634 my ($closed) = $sth->fetchrow_array;
1636 $main::lxdebug->leave_sub();
1641 sub update_balance {
1642 $main::lxdebug->enter_sub();
1644 my ($self, $dbh, $table, $field, $where, $value, @values) = @_;
1646 # if we have a value, go do it
1649 # retrieve balance from table
1650 my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
1651 my $sth = prepare_execute_query($self, $dbh, $query, @values);
1652 my ($balance) = $sth->fetchrow_array;
1658 $query = "UPDATE $table SET $field = $balance WHERE $where";
1659 do_query($self, $dbh, $query, @values);
1661 $main::lxdebug->leave_sub();
1664 sub update_exchangerate {
1665 $main::lxdebug->enter_sub();
1667 my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
1669 # some sanity check for currency
1671 $main::lxdebug->leave_sub();
1674 $query = qq|SELECT curr FROM defaults|;
1676 my ($currency) = selectrow_query($self, $dbh, $query);
1677 my ($defaultcurrency) = split m/:/, $currency;
1680 if ($curr eq $defaultcurrency) {
1681 $main::lxdebug->leave_sub();
1685 $query = qq|SELECT e.curr FROM exchangerate e
1686 WHERE e.curr = ? AND e.transdate = ?
1688 my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
1697 $buy = conv_i($buy, "NULL");
1698 $sell = conv_i($sell, "NULL");
1701 if ($buy != 0 && $sell != 0) {
1702 $set = "buy = $buy, sell = $sell";
1703 } elsif ($buy != 0) {
1704 $set = "buy = $buy";
1705 } elsif ($sell != 0) {
1706 $set = "sell = $sell";
1709 if ($sth->fetchrow_array) {
1710 $query = qq|UPDATE exchangerate
1716 $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
1717 VALUES (?, $buy, $sell, ?)|;
1720 do_query($self, $dbh, $query, $curr, $transdate);
1722 $main::lxdebug->leave_sub();
1725 sub save_exchangerate {
1726 $main::lxdebug->enter_sub();
1728 my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
1730 my $dbh = $self->dbconnect($myconfig);
1734 $buy = $rate if $fld eq 'buy';
1735 $sell = $rate if $fld eq 'sell';
1738 $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
1743 $main::lxdebug->leave_sub();
1746 sub get_exchangerate {
1747 $main::lxdebug->enter_sub();
1749 my ($self, $dbh, $curr, $transdate, $fld) = @_;
1752 unless ($transdate) {
1753 $main::lxdebug->leave_sub();
1757 $query = qq|SELECT curr FROM defaults|;
1759 my ($currency) = selectrow_query($self, $dbh, $query);
1760 my ($defaultcurrency) = split m/:/, $currency;
1762 if ($currency eq $defaultcurrency) {
1763 $main::lxdebug->leave_sub();
1767 $query = qq|SELECT e.$fld FROM exchangerate e
1768 WHERE e.curr = ? AND e.transdate = ?|;
1769 my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
1773 $main::lxdebug->leave_sub();
1775 return $exchangerate;
1778 sub check_exchangerate {
1779 $main::lxdebug->enter_sub();
1781 my ($self, $myconfig, $currency, $transdate, $fld) = @_;
1783 if ($fld !~/^buy|sell$/) {
1784 $self->error('Fatal: check_exchangerate called with invalid buy/sell argument');
1787 unless ($transdate) {
1788 $main::lxdebug->leave_sub();
1792 my ($defaultcurrency) = $self->get_default_currency($myconfig);
1794 if ($currency eq $defaultcurrency) {
1795 $main::lxdebug->leave_sub();
1799 my $dbh = $self->get_standard_dbh($myconfig);
1800 my $query = qq|SELECT e.$fld FROM exchangerate e
1801 WHERE e.curr = ? AND e.transdate = ?|;
1803 my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
1805 $main::lxdebug->leave_sub();
1807 return $exchangerate;
1810 sub get_all_currencies {
1811 $main::lxdebug->enter_sub();
1814 my $myconfig = shift || \%::myconfig;
1815 my $dbh = $self->get_standard_dbh($myconfig);
1817 my $query = qq|SELECT curr FROM defaults|;
1819 my ($curr) = selectrow_query($self, $dbh, $query);
1820 my @currencies = grep { $_ } map { s/\s//g; $_ } split m/:/, $curr;
1822 $main::lxdebug->leave_sub();
1827 sub get_default_currency {
1828 $main::lxdebug->enter_sub();
1830 my ($self, $myconfig) = @_;
1831 my @currencies = $self->get_all_currencies($myconfig);
1833 $main::lxdebug->leave_sub();
1835 return $currencies[0];
1838 sub set_payment_options {
1839 $main::lxdebug->enter_sub();
1841 my ($self, $myconfig, $transdate) = @_;
1843 return $main::lxdebug->leave_sub() unless ($self->{payment_id});
1845 my $dbh = $self->get_standard_dbh($myconfig);
1848 qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
1849 qq|FROM payment_terms p | .
1852 ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
1853 $self->{payment_terms}) =
1854 selectrow_query($self, $dbh, $query, $self->{payment_id});
1856 if ($transdate eq "") {
1857 if ($self->{invdate}) {
1858 $transdate = $self->{invdate};
1860 $transdate = $self->{transdate};
1865 qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
1866 qq|FROM payment_terms|;
1867 ($self->{netto_date}, $self->{skonto_date}) =
1868 selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
1870 my ($invtotal, $total);
1871 my (%amounts, %formatted_amounts);
1873 if ($self->{type} =~ /_order$/) {
1874 $amounts{invtotal} = $self->{ordtotal};
1875 $amounts{total} = $self->{ordtotal};
1877 } elsif ($self->{type} =~ /_quotation$/) {
1878 $amounts{invtotal} = $self->{quototal};
1879 $amounts{total} = $self->{quototal};
1882 $amounts{invtotal} = $self->{invtotal};
1883 $amounts{total} = $self->{total};
1885 $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
1887 map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
1889 $amounts{skonto_amount} = $amounts{invtotal} * $self->{percent_skonto};
1890 $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
1891 $amounts{total_wo_skonto} = $amounts{total} * (1 - $self->{percent_skonto});
1893 foreach (keys %amounts) {
1894 $amounts{$_} = $self->round_amount($amounts{$_}, 2);
1895 $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}, 2);
1898 if ($self->{"language_id"}) {
1900 qq|SELECT t.translation, l.output_numberformat, l.output_dateformat, l.output_longdates | .
1901 qq|FROM generic_translations t | .
1902 qq|LEFT JOIN language l ON t.language_id = l.id | .
1903 qq|WHERE (t.language_id = ?)
1904 AND (t.translation_id = ?)
1905 AND (t.translation_type = 'SL::DB::PaymentTerm/description_long')|;
1906 my ($description_long, $output_numberformat, $output_dateformat,
1907 $output_longdates) =
1908 selectrow_query($self, $dbh, $query,
1909 $self->{"language_id"}, $self->{"payment_id"});
1911 $self->{payment_terms} = $description_long if ($description_long);
1913 if ($output_dateformat) {
1914 foreach my $key (qw(netto_date skonto_date)) {
1916 $main::locale->reformat_date($myconfig, $self->{$key},
1922 if ($output_numberformat &&
1923 ($output_numberformat ne $myconfig->{"numberformat"})) {
1924 my $saved_numberformat = $myconfig->{"numberformat"};
1925 $myconfig->{"numberformat"} = $output_numberformat;
1926 map { $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}) } keys %amounts;
1927 $myconfig->{"numberformat"} = $saved_numberformat;
1931 $self->{payment_terms} =~ s/<%netto_date%>/$self->{netto_date}/g;
1932 $self->{payment_terms} =~ s/<%skonto_date%>/$self->{skonto_date}/g;
1933 $self->{payment_terms} =~ s/<%currency%>/$self->{currency}/g;
1934 $self->{payment_terms} =~ s/<%terms_netto%>/$self->{terms_netto}/g;
1935 $self->{payment_terms} =~ s/<%account_number%>/$self->{account_number}/g;
1936 $self->{payment_terms} =~ s/<%bank%>/$self->{bank}/g;
1937 $self->{payment_terms} =~ s/<%bank_code%>/$self->{bank_code}/g;
1939 map { $self->{payment_terms} =~ s/<%${_}%>/$formatted_amounts{$_}/g; } keys %formatted_amounts;
1941 $self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
1943 $main::lxdebug->leave_sub();
1947 sub get_template_language {
1948 $main::lxdebug->enter_sub();
1950 my ($self, $myconfig) = @_;
1952 my $template_code = "";
1954 if ($self->{language_id}) {
1955 my $dbh = $self->get_standard_dbh($myconfig);
1956 my $query = qq|SELECT template_code FROM language WHERE id = ?|;
1957 ($template_code) = selectrow_query($self, $dbh, $query, $self->{language_id});
1960 $main::lxdebug->leave_sub();
1962 return $template_code;
1965 sub get_printer_code {
1966 $main::lxdebug->enter_sub();
1968 my ($self, $myconfig) = @_;
1970 my $template_code = "";
1972 if ($self->{printer_id}) {
1973 my $dbh = $self->get_standard_dbh($myconfig);
1974 my $query = qq|SELECT template_code, printer_command FROM printers WHERE id = ?|;
1975 ($template_code, $self->{printer_command}) = selectrow_query($self, $dbh, $query, $self->{printer_id});
1978 $main::lxdebug->leave_sub();
1980 return $template_code;
1984 $main::lxdebug->enter_sub();
1986 my ($self, $myconfig) = @_;
1988 my $template_code = "";
1990 if ($self->{shipto_id}) {
1991 my $dbh = $self->get_standard_dbh($myconfig);
1992 my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
1993 my $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{shipto_id});
1994 map({ $self->{$_} = $ref->{$_} } keys(%$ref));
1997 $main::lxdebug->leave_sub();
2001 $main::lxdebug->enter_sub();
2003 my ($self, $dbh, $id, $module) = @_;
2008 foreach my $item (qw(name department_1 department_2 street zipcode city country
2009 contact cp_gender phone fax email)) {
2010 if ($self->{"shipto$item"}) {
2011 $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
2013 push(@values, $self->{"shipto${item}"});
2017 if ($self->{shipto_id}) {
2018 my $query = qq|UPDATE shipto set
2020 shiptodepartment_1 = ?,
2021 shiptodepartment_2 = ?,
2027 shiptocp_gender = ?,
2031 WHERE shipto_id = ?|;
2032 do_query($self, $dbh, $query, @values, $self->{shipto_id});
2034 my $query = qq|SELECT * FROM shipto
2035 WHERE shiptoname = ? AND
2036 shiptodepartment_1 = ? AND
2037 shiptodepartment_2 = ? AND
2038 shiptostreet = ? AND
2039 shiptozipcode = ? AND
2041 shiptocountry = ? AND
2042 shiptocontact = ? AND
2043 shiptocp_gender = ? AND
2049 my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values, $module, $id);
2052 qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
2053 shiptostreet, shiptozipcode, shiptocity, shiptocountry,
2054 shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module)
2055 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
2056 do_query($self, $dbh, $query, $id, @values, $module);
2061 $main::lxdebug->leave_sub();
2065 $main::lxdebug->enter_sub();
2067 my ($self, $dbh) = @_;
2069 $dbh ||= $self->get_standard_dbh(\%main::myconfig);
2071 my $query = qq|SELECT id, name FROM employee WHERE login = ?|;
2072 ($self->{"employee_id"}, $self->{"employee"}) = selectrow_query($self, $dbh, $query, $self->{login});
2073 $self->{"employee_id"} *= 1;
2075 $main::lxdebug->leave_sub();
2078 sub get_employee_data {
2079 $main::lxdebug->enter_sub();
2084 Common::check_params(\%params, qw(prefix));
2085 Common::check_params_x(\%params, qw(id));
2088 $main::lxdebug->leave_sub();
2092 my $myconfig = \%main::myconfig;
2093 my $dbh = $params{dbh} || $self->get_standard_dbh($myconfig);
2095 my ($login) = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
2098 my $user = User->new($login);
2099 map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
2101 $self->{$params{prefix} . '_login'} = $login;
2102 $self->{$params{prefix} . '_name'} ||= $login;
2105 $main::lxdebug->leave_sub();
2109 $main::lxdebug->enter_sub();
2111 my ($self, $myconfig, $reference_date) = @_;
2113 $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
2115 my $dbh = $self->get_standard_dbh($myconfig);
2116 my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
2117 my ($duedate) = selectrow_query($self, $dbh, $query, $self->{payment_id});
2119 $main::lxdebug->leave_sub();
2125 $main::lxdebug->enter_sub();
2127 my ($self, $dbh, $id, $key) = @_;
2129 $key = "all_contacts" unless ($key);
2133 $main::lxdebug->leave_sub();
2138 qq|SELECT cp_id, cp_cv_id, cp_name, cp_givenname, cp_abteilung | .
2139 qq|FROM contacts | .
2140 qq|WHERE cp_cv_id = ? | .
2141 qq|ORDER BY lower(cp_name)|;
2143 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $id);
2145 $main::lxdebug->leave_sub();
2149 $main::lxdebug->enter_sub();
2151 my ($self, $dbh, $key) = @_;
2153 my ($all, $old_id, $where, @values);
2155 if (ref($key) eq "HASH") {
2158 $key = "ALL_PROJECTS";
2160 foreach my $p (keys(%{$params})) {
2162 $all = $params->{$p};
2163 } elsif ($p eq "old_id") {
2164 $old_id = $params->{$p};
2165 } elsif ($p eq "key") {
2166 $key = $params->{$p};
2172 $where = "WHERE active ";
2174 if (ref($old_id) eq "ARRAY") {
2175 my @ids = grep({ $_ } @{$old_id});
2177 $where .= " OR id IN (" . join(",", map({ "?" } @ids)) . ") ";
2178 push(@values, @ids);
2181 $where .= " OR (id = ?) ";
2182 push(@values, $old_id);
2188 qq|SELECT id, projectnumber, description, active | .
2191 qq|ORDER BY lower(projectnumber)|;
2193 $self->{$key} = selectall_hashref_query($self, $dbh, $query, @values);
2195 $main::lxdebug->leave_sub();
2199 $main::lxdebug->enter_sub();
2201 my ($self, $dbh, $vc_id, $key) = @_;
2203 $key = "all_shipto" unless ($key);
2206 # get shipping addresses
2207 my $query = qq|SELECT * FROM shipto WHERE trans_id = ?|;
2209 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id);
2215 $main::lxdebug->leave_sub();
2219 $main::lxdebug->enter_sub();
2221 my ($self, $dbh, $key) = @_;
2223 $key = "all_printers" unless ($key);
2225 my $query = qq|SELECT id, printer_description, printer_command, template_code FROM printers|;
2227 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2229 $main::lxdebug->leave_sub();
2233 $main::lxdebug->enter_sub();
2235 my ($self, $dbh, $params) = @_;
2238 $key = $params->{key};
2239 $key = "all_charts" unless ($key);
2241 my $transdate = quote_db_date($params->{transdate});
2244 qq|SELECT c.id, c.accno, c.description, c.link, c.charttype, tk.taxkey_id, tk.tax_id | .
2246 qq|LEFT JOIN taxkeys tk ON | .
2247 qq|(tk.id = (SELECT id FROM taxkeys | .
2248 qq| WHERE taxkeys.chart_id = c.id AND startdate <= $transdate | .
2249 qq| ORDER BY startdate DESC LIMIT 1)) | .
2250 qq|ORDER BY c.accno|;
2252 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2254 $main::lxdebug->leave_sub();
2257 sub _get_taxcharts {
2258 $main::lxdebug->enter_sub();
2260 my ($self, $dbh, $params) = @_;
2262 my $key = "all_taxcharts";
2265 if (ref $params eq 'HASH') {
2266 $key = $params->{key} if ($params->{key});
2267 if ($params->{module} eq 'AR') {
2268 push @where, 'taxkey NOT IN (8, 9, 18, 19)';
2270 } elsif ($params->{module} eq 'AP') {
2271 push @where, 'taxkey NOT IN (1, 2, 3, 12, 13)';
2278 my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
2280 my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
2282 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2284 $main::lxdebug->leave_sub();
2288 $main::lxdebug->enter_sub();
2290 my ($self, $dbh, $key) = @_;
2292 $key = "all_taxzones" unless ($key);
2294 my $query = qq|SELECT * FROM tax_zones ORDER BY id|;
2296 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2298 $main::lxdebug->leave_sub();
2301 sub _get_employees {
2302 $main::lxdebug->enter_sub();
2304 my ($self, $dbh, $default_key, $key) = @_;
2306 $key = $default_key unless ($key);
2307 $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee ORDER BY lower(name)|);
2309 $main::lxdebug->leave_sub();
2312 sub _get_business_types {
2313 $main::lxdebug->enter_sub();
2315 my ($self, $dbh, $key) = @_;
2317 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2318 $options->{key} ||= "all_business_types";
2321 if (exists $options->{salesman}) {
2322 $where = 'WHERE ' . ($options->{salesman} ? '' : 'NOT ') . 'COALESCE(salesman)';
2325 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, qq|SELECT * FROM business $where ORDER BY lower(description)|);
2327 $main::lxdebug->leave_sub();
2330 sub _get_languages {
2331 $main::lxdebug->enter_sub();
2333 my ($self, $dbh, $key) = @_;
2335 $key = "all_languages" unless ($key);
2337 my $query = qq|SELECT * FROM language ORDER BY id|;
2339 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2341 $main::lxdebug->leave_sub();
2344 sub _get_dunning_configs {
2345 $main::lxdebug->enter_sub();
2347 my ($self, $dbh, $key) = @_;
2349 $key = "all_dunning_configs" unless ($key);
2351 my $query = qq|SELECT * FROM dunning_config ORDER BY dunning_level|;
2353 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2355 $main::lxdebug->leave_sub();
2358 sub _get_currencies {
2359 $main::lxdebug->enter_sub();
2361 my ($self, $dbh, $key) = @_;
2363 $key = "all_currencies" unless ($key);
2365 my $query = qq|SELECT curr AS currency FROM defaults|;
2367 $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
2369 $main::lxdebug->leave_sub();
2373 $main::lxdebug->enter_sub();
2375 my ($self, $dbh, $key) = @_;
2377 $key = "all_payments" unless ($key);
2379 my $query = qq|SELECT * FROM payment_terms ORDER BY sortkey|;
2381 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2383 $main::lxdebug->leave_sub();
2386 sub _get_customers {
2387 $main::lxdebug->enter_sub();
2389 my ($self, $dbh, $key) = @_;
2391 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2392 $options->{key} ||= "all_customers";
2393 my $limit_clause = "LIMIT $options->{limit}" if $options->{limit};
2396 push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if $options->{business_is_salesman};
2397 push @where, qq|NOT obsolete| if !$options->{with_obsolete};
2398 my $where_str = @where ? "WHERE " . join(" AND ", map { "($_)" } @where) : '';
2400 my $query = qq|SELECT * FROM customer $where_str ORDER BY name $limit_clause|;
2401 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, $query);
2403 $main::lxdebug->leave_sub();
2407 $main::lxdebug->enter_sub();
2409 my ($self, $dbh, $key) = @_;
2411 $key = "all_vendors" unless ($key);
2413 my $query = qq|SELECT * FROM vendor WHERE NOT obsolete ORDER BY name|;
2415 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2417 $main::lxdebug->leave_sub();
2420 sub _get_departments {
2421 $main::lxdebug->enter_sub();
2423 my ($self, $dbh, $key) = @_;
2425 $key = "all_departments" unless ($key);
2427 my $query = qq|SELECT * FROM department ORDER BY description|;
2429 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2431 $main::lxdebug->leave_sub();
2434 sub _get_warehouses {
2435 $main::lxdebug->enter_sub();
2437 my ($self, $dbh, $param) = @_;
2439 my ($key, $bins_key);
2441 if ('' eq ref $param) {
2445 $key = $param->{key};
2446 $bins_key = $param->{bins};
2449 my $query = qq|SELECT w.* FROM warehouse w
2450 WHERE (NOT w.invalid) AND
2451 ((SELECT COUNT(b.*) FROM bin b WHERE b.warehouse_id = w.id) > 0)
2452 ORDER BY w.sortkey|;
2454 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2457 $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?
2458 ORDER BY description|;
2459 my $sth = prepare_query($self, $dbh, $query);
2461 foreach my $warehouse (@{ $self->{$key} }) {
2462 do_statement($self, $sth, $query, $warehouse->{id});
2463 $warehouse->{$bins_key} = [];
2465 while (my $ref = $sth->fetchrow_hashref()) {
2466 push @{ $warehouse->{$bins_key} }, $ref;
2472 $main::lxdebug->leave_sub();
2476 $main::lxdebug->enter_sub();
2478 my ($self, $dbh, $table, $key, $sortkey) = @_;
2480 my $query = qq|SELECT * FROM $table|;
2481 $query .= qq| ORDER BY $sortkey| if ($sortkey);
2483 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2485 $main::lxdebug->leave_sub();
2489 # $main::lxdebug->enter_sub();
2491 # my ($self, $dbh, $key) = @_;
2493 # $key ||= "all_groups";
2495 # my $groups = $main::auth->read_groups();
2497 # $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2499 # $main::lxdebug->leave_sub();
2503 $main::lxdebug->enter_sub();
2508 my $dbh = $self->get_standard_dbh(\%main::myconfig);
2509 my ($sth, $query, $ref);
2511 my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
2512 my $vc_id = $self->{"${vc}_id"};
2514 if ($params{"contacts"}) {
2515 $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
2518 if ($params{"shipto"}) {
2519 $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
2522 if ($params{"projects"} || $params{"all_projects"}) {
2523 $self->_get_projects($dbh, $params{"all_projects"} ?
2524 $params{"all_projects"} : $params{"projects"},
2525 $params{"all_projects"} ? 1 : 0);
2528 if ($params{"printers"}) {
2529 $self->_get_printers($dbh, $params{"printers"});
2532 if ($params{"languages"}) {
2533 $self->_get_languages($dbh, $params{"languages"});
2536 if ($params{"charts"}) {
2537 $self->_get_charts($dbh, $params{"charts"});
2540 if ($params{"taxcharts"}) {
2541 $self->_get_taxcharts($dbh, $params{"taxcharts"});
2544 if ($params{"taxzones"}) {
2545 $self->_get_taxzones($dbh, $params{"taxzones"});
2548 if ($params{"employees"}) {
2549 $self->_get_employees($dbh, "all_employees", $params{"employees"});
2552 if ($params{"salesmen"}) {
2553 $self->_get_employees($dbh, "all_salesmen", $params{"salesmen"});
2556 if ($params{"business_types"}) {
2557 $self->_get_business_types($dbh, $params{"business_types"});
2560 if ($params{"dunning_configs"}) {
2561 $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
2564 if($params{"currencies"}) {
2565 $self->_get_currencies($dbh, $params{"currencies"});
2568 if($params{"customers"}) {
2569 $self->_get_customers($dbh, $params{"customers"});
2572 if($params{"vendors"}) {
2573 if (ref $params{"vendors"} eq 'HASH') {
2574 $self->_get_vendors($dbh, $params{"vendors"}{key}, $params{"vendors"}{limit});
2576 $self->_get_vendors($dbh, $params{"vendors"});
2580 if($params{"payments"}) {
2581 $self->_get_payments($dbh, $params{"payments"});
2584 if($params{"departments"}) {
2585 $self->_get_departments($dbh, $params{"departments"});
2588 if ($params{price_factors}) {
2589 $self->_get_simple($dbh, 'price_factors', $params{price_factors}, 'sortkey');
2592 if ($params{warehouses}) {
2593 $self->_get_warehouses($dbh, $params{warehouses});
2596 # if ($params{groups}) {
2597 # $self->_get_groups($dbh, $params{groups});
2600 if ($params{partsgroup}) {
2601 $self->get_partsgroup(\%main::myconfig, { all => 1, target => $params{partsgroup} });
2604 $main::lxdebug->leave_sub();
2607 # this sub gets the id and name from $table
2609 $main::lxdebug->enter_sub();
2611 my ($self, $myconfig, $table) = @_;
2613 # connect to database
2614 my $dbh = $self->get_standard_dbh($myconfig);
2616 $table = $table eq "customer" ? "customer" : "vendor";
2617 my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
2619 my ($query, @values);
2621 if (!$self->{openinvoices}) {
2623 if ($self->{customernumber} ne "") {
2624 $where = qq|(vc.customernumber ILIKE ?)|;
2625 push(@values, '%' . $self->{customernumber} . '%');
2627 $where = qq|(vc.name ILIKE ?)|;
2628 push(@values, '%' . $self->{$table} . '%');
2632 qq~SELECT vc.id, vc.name,
2633 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2635 WHERE $where AND (NOT vc.obsolete)
2639 qq~SELECT DISTINCT vc.id, vc.name,
2640 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2642 JOIN $table vc ON (a.${table}_id = vc.id)
2643 WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
2645 push(@values, '%' . $self->{$table} . '%');
2648 $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
2650 $main::lxdebug->leave_sub();
2652 return scalar(@{ $self->{name_list} });
2655 # the selection sub is used in the AR, AP, IS, IR and OE module
2658 $main::lxdebug->enter_sub();
2660 my ($self, $myconfig, $table, $module) = @_;
2663 my $dbh = $self->get_standard_dbh;
2665 $table = $table eq "customer" ? "customer" : "vendor";
2667 my $query = qq|SELECT count(*) FROM $table|;
2668 my ($count) = selectrow_query($self, $dbh, $query);
2670 # build selection list
2671 if ($count <= $myconfig->{vclimit}) {
2672 $query = qq|SELECT id, name, salesman_id
2673 FROM $table WHERE NOT obsolete
2675 $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
2679 $self->get_employee($dbh);
2681 # setup sales contacts
2682 $query = qq|SELECT e.id, e.name
2684 WHERE (e.sales = '1') AND (NOT e.id = ?)|;
2685 $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
2688 push(@{ $self->{all_employees} },
2689 { id => $self->{employee_id},
2690 name => $self->{employee} });
2692 # sort the whole thing
2693 @{ $self->{all_employees} } =
2694 sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
2696 if ($module eq 'AR') {
2698 # prepare query for departments
2699 $query = qq|SELECT id, description
2702 ORDER BY description|;
2705 $query = qq|SELECT id, description
2707 ORDER BY description|;
2710 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2713 $query = qq|SELECT id, description
2717 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2720 $query = qq|SELECT printer_description, id
2722 ORDER BY printer_description|;
2724 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2727 $query = qq|SELECT id, description
2731 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2733 $main::lxdebug->leave_sub();
2736 sub language_payment {
2737 $main::lxdebug->enter_sub();
2739 my ($self, $myconfig) = @_;
2741 my $dbh = $self->get_standard_dbh($myconfig);
2743 my $query = qq|SELECT id, description
2747 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2750 $query = qq|SELECT printer_description, id
2752 ORDER BY printer_description|;
2754 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2757 $query = qq|SELECT id, description
2761 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2763 # get buchungsgruppen
2764 $query = qq|SELECT id, description
2765 FROM buchungsgruppen|;
2767 $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
2769 $main::lxdebug->leave_sub();
2772 # this is only used for reports
2773 sub all_departments {
2774 $main::lxdebug->enter_sub();
2776 my ($self, $myconfig, $table) = @_;
2778 my $dbh = $self->get_standard_dbh($myconfig);
2781 if ($table eq 'customer') {
2782 $where = "WHERE role = 'P' ";
2785 my $query = qq|SELECT id, description
2788 ORDER BY description|;
2789 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2791 delete($self->{all_departments}) unless (@{ $self->{all_departments} || [] });
2793 $main::lxdebug->leave_sub();
2797 $main::lxdebug->enter_sub();
2799 my ($self, $module, $myconfig, $table, $provided_dbh) = @_;
2802 if ($table eq "customer") {
2811 $self->all_vc($myconfig, $table, $module);
2813 # get last customers or vendors
2814 my ($query, $sth, $ref);
2816 my $dbh = $provided_dbh ? $provided_dbh : $self->get_standard_dbh($myconfig);
2821 my $transdate = "current_date";
2822 if ($self->{transdate}) {
2823 $transdate = $dbh->quote($self->{transdate});
2826 # now get the account numbers
2827 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2828 FROM chart c, taxkeys tk
2829 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
2830 (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
2833 $sth = $dbh->prepare($query);
2835 do_statement($self, $sth, $query, '%' . $module . '%');
2837 $self->{accounts} = "";
2838 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2840 foreach my $key (split(/:/, $ref->{link})) {
2841 if ($key =~ /\Q$module\E/) {
2843 # cross reference for keys
2844 $xkeyref{ $ref->{accno} } = $key;
2846 push @{ $self->{"${module}_links"}{$key} },
2847 { accno => $ref->{accno},
2848 description => $ref->{description},
2849 taxkey => $ref->{taxkey_id},
2850 tax_id => $ref->{tax_id} };
2852 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2858 # get taxkeys and description
2859 $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
2860 $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
2862 if (($module eq "AP") || ($module eq "AR")) {
2863 # get tax rates and description
2864 $query = qq|SELECT * FROM tax|;
2865 $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
2871 a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
2872 a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
2873 a.intnotes, a.department_id, a.amount AS oldinvtotal,
2874 a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
2876 d.description AS department,
2879 JOIN $table c ON (a.${table}_id = c.id)
2880 LEFT JOIN employee e ON (e.id = a.employee_id)
2881 LEFT JOIN department d ON (d.id = a.department_id)
2883 $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2885 foreach my $key (keys %$ref) {
2886 $self->{$key} = $ref->{$key};
2889 my $transdate = "current_date";
2890 if ($self->{transdate}) {
2891 $transdate = $dbh->quote($self->{transdate});
2894 # now get the account numbers
2895 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2897 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
2899 AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
2900 OR c.link LIKE '%_tax%' OR c.taxkey_id IS NULL)
2903 $sth = $dbh->prepare($query);
2904 do_statement($self, $sth, $query, "%$module%");
2906 $self->{accounts} = "";
2907 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2909 foreach my $key (split(/:/, $ref->{link})) {
2910 if ($key =~ /\Q$module\E/) {
2912 # cross reference for keys
2913 $xkeyref{ $ref->{accno} } = $key;
2915 push @{ $self->{"${module}_links"}{$key} },
2916 { accno => $ref->{accno},
2917 description => $ref->{description},
2918 taxkey => $ref->{taxkey_id},
2919 tax_id => $ref->{tax_id} };
2921 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2927 # get amounts from individual entries
2930 c.accno, c.description,
2931 a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
2935 LEFT JOIN chart c ON (c.id = a.chart_id)
2936 LEFT JOIN project p ON (p.id = a.project_id)
2937 LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
2938 WHERE (tk.taxkey_id=a.taxkey) AND
2939 ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
2940 THEN tk.chart_id = a.chart_id
2943 OR (c.link='%tax%')) AND
2944 (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
2945 WHERE a.trans_id = ?
2946 AND a.fx_transaction = '0'
2947 ORDER BY a.acc_trans_id, a.transdate|;
2948 $sth = $dbh->prepare($query);
2949 do_statement($self, $sth, $query, $self->{id});
2951 # get exchangerate for currency
2952 $self->{exchangerate} =
2953 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2956 # store amounts in {acc_trans}{$key} for multiple accounts
2957 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
2958 $ref->{exchangerate} =
2959 $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
2960 if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
2963 if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
2964 $ref->{amount} *= -1;
2966 $ref->{index} = $index;
2968 push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
2974 d.curr AS currencies, d.closedto, d.revtrans,
2975 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2976 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2978 $ref = selectfirst_hashref_query($self, $dbh, $query);
2979 map { $self->{$_} = $ref->{$_} } keys %$ref;
2986 current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
2987 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2988 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2990 $ref = selectfirst_hashref_query($self, $dbh, $query);
2991 map { $self->{$_} = $ref->{$_} } keys %$ref;
2993 if ($self->{"$self->{vc}_id"}) {
2995 # only setup currency
2996 ($self->{currency}) = split(/:/, $self->{currencies});
3000 $self->lastname_used($dbh, $myconfig, $table, $module);
3002 # get exchangerate for currency
3003 $self->{exchangerate} =
3004 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
3010 $main::lxdebug->leave_sub();
3014 $main::lxdebug->enter_sub();
3016 my ($self, $dbh, $myconfig, $table, $module) = @_;
3020 $table = $table eq "customer" ? "customer" : "vendor";
3021 my %column_map = ("a.curr" => "currency",
3022 "a.${table}_id" => "${table}_id",
3023 "a.department_id" => "department_id",
3024 "d.description" => "department",
3025 "ct.name" => $table,
3026 "current_date + ct.terms" => "duedate",
3029 if ($self->{type} =~ /delivery_order/) {
3030 $arap = 'delivery_orders';
3031 delete $column_map{"a.curr"};
3033 } elsif ($self->{type} =~ /_order/) {
3035 $where = "quotation = '0'";
3037 } elsif ($self->{type} =~ /_quotation/) {
3039 $where = "quotation = '1'";
3041 } elsif ($table eq 'customer') {
3049 $where = "($where) AND" if ($where);
3050 my $query = qq|SELECT MAX(id) FROM $arap
3051 WHERE $where ${table}_id > 0|;
3052 my ($trans_id) = selectrow_query($self, $dbh, $query);
3055 my $column_spec = join(', ', map { "${_} AS $column_map{$_}" } keys %column_map);
3056 $query = qq|SELECT $column_spec
3058 LEFT JOIN $table ct ON (a.${table}_id = ct.id)
3059 LEFT JOIN department d ON (a.department_id = d.id)
3061 my $ref = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
3063 map { $self->{$_} = $ref->{$_} } values %column_map;
3065 $main::lxdebug->leave_sub();
3069 $main::lxdebug->enter_sub();
3072 my $myconfig = shift || \%::myconfig;
3073 my ($thisdate, $days) = @_;
3075 my $dbh = $self->get_standard_dbh($myconfig);
3080 my $dateformat = $myconfig->{dateformat};
3081 $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
3082 $thisdate = $dbh->quote($thisdate);
3083 $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
3085 $query = qq|SELECT current_date AS thisdate|;
3088 ($thisdate) = selectrow_query($self, $dbh, $query);
3090 $main::lxdebug->leave_sub();
3096 $main::lxdebug->enter_sub();
3098 my ($self, $string) = @_;
3100 if ($string !~ /%/) {
3101 $string = "%$string%";
3104 $string =~ s/\'/\'\'/g;
3106 $main::lxdebug->leave_sub();
3112 $main::lxdebug->enter_sub();
3114 my ($self, $flds, $new, $count, $numrows) = @_;
3118 map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } } 1 .. $count;
3123 foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
3125 my $j = $item->{ndx} - 1;
3126 map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
3130 for $i ($count + 1 .. $numrows) {
3131 map { delete $self->{"${_}_$i"} } @{$flds};
3134 $main::lxdebug->leave_sub();
3138 $main::lxdebug->enter_sub();
3140 my ($self, $myconfig) = @_;
3144 my $dbh = $self->dbconnect_noauto($myconfig);
3146 my $query = qq|DELETE FROM status
3147 WHERE (formname = ?) AND (trans_id = ?)|;
3148 my $sth = prepare_query($self, $dbh, $query);
3150 if ($self->{formname} =~ /(check|receipt)/) {
3151 for $i (1 .. $self->{rowcount}) {
3152 do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
3155 do_statement($self, $sth, $query, $self->{formname}, $self->{id});
3159 my $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3160 my $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3162 my %queued = split / /, $self->{queued};
3165 if ($self->{formname} =~ /(check|receipt)/) {
3167 # this is a check or receipt, add one entry for each lineitem
3168 my ($accno) = split /--/, $self->{account};
3169 $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
3170 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
3171 @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
3172 $sth = prepare_query($self, $dbh, $query);
3174 for $i (1 .. $self->{rowcount}) {
3175 if ($self->{"checked_$i"}) {
3176 do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
3182 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3183 VALUES (?, ?, ?, ?, ?)|;
3184 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
3185 $queued{$self->{formname}}, $self->{formname});
3191 $main::lxdebug->leave_sub();
3195 $main::lxdebug->enter_sub();
3197 my ($self, $dbh) = @_;
3199 my ($query, $printed, $emailed);
3201 my $formnames = $self->{printed};
3202 my $emailforms = $self->{emailed};
3204 $query = qq|DELETE FROM status
3205 WHERE (formname = ?) AND (trans_id = ?)|;
3206 do_query($self, $dbh, $query, $self->{formname}, $self->{id});
3208 # this only applies to the forms
3209 # checks and receipts are posted when printed or queued
3211 if ($self->{queued}) {
3212 my %queued = split / /, $self->{queued};
3214 foreach my $formname (keys %queued) {
3215 $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3216 $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3218 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3219 VALUES (?, ?, ?, ?, ?)|;
3220 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
3222 $formnames =~ s/\Q$self->{formname}\E//;
3223 $emailforms =~ s/\Q$self->{formname}\E//;
3228 # save printed, emailed info
3229 $formnames =~ s/^ +//g;
3230 $emailforms =~ s/^ +//g;
3233 map { $status{$_}{printed} = 1 } split / +/, $formnames;
3234 map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
3236 foreach my $formname (keys %status) {
3237 $printed = ($formnames =~ /\Q$self->{formname}\E/) ? "1" : "0";
3238 $emailed = ($emailforms =~ /\Q$self->{formname}\E/) ? "1" : "0";
3240 $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
3241 VALUES (?, ?, ?, ?)|;
3242 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
3245 $main::lxdebug->leave_sub();
3249 # $main::locale->text('SAVED')
3250 # $main::locale->text('DELETED')
3251 # $main::locale->text('ADDED')
3252 # $main::locale->text('PAYMENT POSTED')
3253 # $main::locale->text('POSTED')
3254 # $main::locale->text('POSTED AS NEW')
3255 # $main::locale->text('ELSE')
3256 # $main::locale->text('SAVED FOR DUNNING')
3257 # $main::locale->text('DUNNING STARTED')
3258 # $main::locale->text('PRINTED')
3259 # $main::locale->text('MAILED')
3260 # $main::locale->text('SCREENED')
3261 # $main::locale->text('CANCELED')
3262 # $main::locale->text('invoice')
3263 # $main::locale->text('proforma')
3264 # $main::locale->text('sales_order')
3265 # $main::locale->text('pick_list')
3266 # $main::locale->text('purchase_order')
3267 # $main::locale->text('bin_list')
3268 # $main::locale->text('sales_quotation')
3269 # $main::locale->text('request_quotation')
3272 $main::lxdebug->enter_sub();
3275 my $dbh = shift || $self->get_standard_dbh;
3277 if(!exists $self->{employee_id}) {
3278 &get_employee($self, $dbh);
3282 qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
3283 qq|VALUES (?, (SELECT id FROM employee WHERE login = ?), ?, ?, ?)|;
3284 my @values = (conv_i($self->{id}), $self->{login},
3285 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
3286 do_query($self, $dbh, $query, @values);
3290 $main::lxdebug->leave_sub();
3294 $main::lxdebug->enter_sub();
3296 my ($self, $dbh, $trans_id, $restriction, $order) = @_;
3297 my ($orderBy, $desc) = split(/\-\-/, $order);
3298 $order = " ORDER BY " . ($order eq "" ? " h.itime " : ($desc == 1 ? $orderBy . " DESC " : $orderBy . " "));
3301 if ($trans_id ne "") {
3303 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 | .
3304 qq|FROM history_erp h | .
3305 qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
3306 qq|WHERE (trans_id = | . $trans_id . qq|) $restriction | .
3309 my $sth = $dbh->prepare($query) || $self->dberror($query);
3311 $sth->execute() || $self->dberror("$query");
3313 while(my $hash_ref = $sth->fetchrow_hashref()) {
3314 $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
3315 $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
3316 $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
3317 $tempArray[$i++] = $hash_ref;
3319 $main::lxdebug->leave_sub() and return \@tempArray
3320 if ($i > 0 && $tempArray[0] ne "");
3322 $main::lxdebug->leave_sub();
3326 sub update_defaults {
3327 $main::lxdebug->enter_sub();
3329 my ($self, $myconfig, $fld, $provided_dbh) = @_;
3332 if ($provided_dbh) {
3333 $dbh = $provided_dbh;
3335 $dbh = $self->dbconnect_noauto($myconfig);
3337 my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
3338 my $sth = $dbh->prepare($query);
3340 $sth->execute || $self->dberror($query);
3341 my ($var) = $sth->fetchrow_array;
3344 if ($var =~ m/\d+$/) {
3345 my $new_var = (substr $var, $-[0]) * 1 + 1;
3346 my $len_diff = length($var) - $-[0] - length($new_var);
3347 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3353 $query = qq|UPDATE defaults SET $fld = ?|;
3354 do_query($self, $dbh, $query, $var);
3356 if (!$provided_dbh) {
3361 $main::lxdebug->leave_sub();
3366 sub update_business {
3367 $main::lxdebug->enter_sub();
3369 my ($self, $myconfig, $business_id, $provided_dbh) = @_;
3372 if ($provided_dbh) {
3373 $dbh = $provided_dbh;
3375 $dbh = $self->dbconnect_noauto($myconfig);
3378 qq|SELECT customernumberinit FROM business
3379 WHERE id = ? FOR UPDATE|;
3380 my ($var) = selectrow_query($self, $dbh, $query, $business_id);
3382 return undef unless $var;
3384 if ($var =~ m/\d+$/) {
3385 my $new_var = (substr $var, $-[0]) * 1 + 1;
3386 my $len_diff = length($var) - $-[0] - length($new_var);
3387 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3393 $query = qq|UPDATE business
3394 SET customernumberinit = ?
3396 do_query($self, $dbh, $query, $var, $business_id);
3398 if (!$provided_dbh) {
3403 $main::lxdebug->leave_sub();
3408 sub get_partsgroup {
3409 $main::lxdebug->enter_sub();
3411 my ($self, $myconfig, $p) = @_;
3412 my $target = $p->{target} || 'all_partsgroup';
3414 my $dbh = $self->get_standard_dbh($myconfig);
3416 my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
3418 JOIN parts p ON (p.partsgroup_id = pg.id) |;
3421 if ($p->{searchitems} eq 'part') {
3422 $query .= qq|WHERE p.inventory_accno_id > 0|;
3424 if ($p->{searchitems} eq 'service') {
3425 $query .= qq|WHERE p.inventory_accno_id IS NULL|;
3427 if ($p->{searchitems} eq 'assembly') {
3428 $query .= qq|WHERE p.assembly = '1'|;
3430 if ($p->{searchitems} eq 'labor') {
3431 $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
3434 $query .= qq|ORDER BY partsgroup|;
3437 $query = qq|SELECT id, partsgroup FROM partsgroup
3438 ORDER BY partsgroup|;
3441 if ($p->{language_code}) {
3442 $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
3443 t.description AS translation
3445 JOIN parts p ON (p.partsgroup_id = pg.id)
3446 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
3447 ORDER BY translation|;
3448 @values = ($p->{language_code});
3451 $self->{$target} = selectall_hashref_query($self, $dbh, $query, @values);
3453 $main::lxdebug->leave_sub();
3456 sub get_pricegroup {
3457 $main::lxdebug->enter_sub();
3459 my ($self, $myconfig, $p) = @_;
3461 my $dbh = $self->get_standard_dbh($myconfig);
3463 my $query = qq|SELECT p.id, p.pricegroup
3466 $query .= qq| ORDER BY pricegroup|;
3469 $query = qq|SELECT id, pricegroup FROM pricegroup
3470 ORDER BY pricegroup|;
3473 $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
3475 $main::lxdebug->leave_sub();
3479 # usage $form->all_years($myconfig, [$dbh])
3480 # return list of all years where bookings found
3483 $main::lxdebug->enter_sub();
3485 my ($self, $myconfig, $dbh) = @_;
3487 $dbh ||= $self->get_standard_dbh($myconfig);
3490 my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
3491 (SELECT MAX(transdate) FROM acc_trans)|;
3492 my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
3494 if ($myconfig->{dateformat} =~ /^yy/) {
3495 ($startdate) = split /\W/, $startdate;
3496 ($enddate) = split /\W/, $enddate;
3498 (@_) = split /\W/, $startdate;
3500 (@_) = split /\W/, $enddate;
3505 $startdate = substr($startdate,0,4);
3506 $enddate = substr($enddate,0,4);
3508 while ($enddate >= $startdate) {
3509 push @all_years, $enddate--;
3514 $main::lxdebug->leave_sub();
3518 $main::lxdebug->enter_sub();
3522 map { $self->{_VAR_BACKUP}->{$_} = $self->{$_} if exists $self->{$_} } @vars;
3524 $main::lxdebug->leave_sub();
3528 $main::lxdebug->enter_sub();
3533 map { $self->{$_} = $self->{_VAR_BACKUP}->{$_} if exists $self->{_VAR_BACKUP}->{$_} } @vars;
3535 $main::lxdebug->leave_sub();
3538 sub prepare_for_printing {
3541 $self->{templates} ||= $::myconfig{templates};
3542 $self->{formname} ||= $self->{type};
3543 $self->{media} ||= 'email';
3545 die "'media' other than 'email', 'file', 'printer' is not supported yet" unless $self->{media} =~ m/^(?:email|file|printer)$/;
3547 # set shipto from billto unless set
3548 my $has_shipto = any { $self->{"shipto$_"} } qw(name street zipcode city country contact);
3549 if (!$has_shipto && ($self->{type} =~ m/^(?:purchase_order|request_quotation)$/)) {
3550 $self->{shiptoname} = $::myconfig{company};
3551 $self->{shiptostreet} = $::myconfig{address};
3554 my $language = $self->{language} ? '_' . $self->{language} : '';
3556 my ($language_tc, $output_numberformat, $output_dateformat, $output_longdates);
3557 if ($self->{language_id}) {
3558 ($language_tc, $output_numberformat, $output_dateformat, $output_longdates) = AM->get_language_details(\%::myconfig, $self, $self->{language_id});
3560 $output_dateformat = $::myconfig{dateformat};
3561 $output_numberformat = $::myconfig{numberformat};
3562 $output_longdates = 1;
3565 # Retrieve accounts for tax calculation.
3566 IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
3568 if ($self->{type} =~ /_delivery_order$/) {
3569 DO->order_details();
3570 } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
3571 OE->order_details(\%::myconfig, $self);
3573 IS->invoice_details(\%::myconfig, $self, $::locale);
3576 # Chose extension & set source file name
3577 my $extension = 'html';
3578 if ($self->{format} eq 'postscript') {
3579 $self->{postscript} = 1;
3581 } elsif ($self->{"format"} =~ /pdf/) {
3583 $extension = $self->{'format'} =~ m/opendocument/i ? 'odt' : 'tex';
3584 } elsif ($self->{"format"} =~ /opendocument/) {
3585 $self->{opendocument} = 1;
3587 } elsif ($self->{"format"} =~ /excel/) {
3592 my $printer_code = '_' . $self->{printer_code} if $self->{printer_code};
3593 my $email_extension = '_email' if -f "$self->{templates}/$self->{formname}_email${language}${printer_code}.${extension}";
3594 $self->{IN} = "$self->{formname}${email_extension}${language}${printer_code}.${extension}";
3597 $self->format_dates($output_dateformat, $output_longdates,
3598 qw(invdate orddate quodate pldate duedate reqdate transdate shippingdate deliverydate validitydate paymentdate datepaid
3599 transdate_oe deliverydate_oe employee_startdate employee_enddate),
3600 grep({ /^(?:datepaid|transdate_oe|reqdate|deliverydate|deliverydate_oe|transdate)_\d+$/ } keys(%{$self})));
3602 $self->reformat_numbers($output_numberformat, 2,
3603 qw(invtotal ordtotal quototal subtotal linetotal listprice sellprice netprice discount tax taxbase total paid),
3604 grep({ /^(?:linetotal|listprice|sellprice|netprice|taxbase|discount|paid|subtotal|total|tax)_\d+$/ } keys(%{$self})));
3606 $self->reformat_numbers($output_numberformat, undef, qw(qty price_factor), grep({ /^qty_\d+$/} keys(%{$self})));
3608 my ($cvar_date_fields, $cvar_number_fields) = CVar->get_field_format_list('module' => 'CT', 'prefix' => 'vc_');
3610 if (scalar @{ $cvar_date_fields }) {
3611 $self->format_dates($output_dateformat, $output_longdates, @{ $cvar_date_fields });
3614 while (my ($precision, $field_list) = each %{ $cvar_number_fields }) {
3615 $self->reformat_numbers($output_numberformat, $precision, @{ $field_list });
3622 my ($self, $dateformat, $longformat, @indices) = @_;
3624 $dateformat ||= $::myconfig{dateformat};
3626 foreach my $idx (@indices) {
3627 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3628 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3629 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $::locale->reformat_date(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i], $dateformat, $longformat);
3633 next unless defined $self->{$idx};
3635 if (!ref($self->{$idx})) {
3636 $self->{$idx} = $::locale->reformat_date(\%::myconfig, $self->{$idx}, $dateformat, $longformat);
3638 } elsif (ref($self->{$idx}) eq "ARRAY") {
3639 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3640 $self->{$idx}->[$i] = $::locale->reformat_date(\%::myconfig, $self->{$idx}->[$i], $dateformat, $longformat);
3646 sub reformat_numbers {
3647 my ($self, $numberformat, $places, @indices) = @_;
3649 return if !$numberformat || ($numberformat eq $::myconfig{numberformat});
3651 foreach my $idx (@indices) {
3652 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3653 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3654 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $self->parse_amount(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i]);
3658 next unless defined $self->{$idx};
3660 if (!ref($self->{$idx})) {
3661 $self->{$idx} = $self->parse_amount(\%::myconfig, $self->{$idx});
3663 } elsif (ref($self->{$idx}) eq "ARRAY") {
3664 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3665 $self->{$idx}->[$i] = $self->parse_amount(\%::myconfig, $self->{$idx}->[$i]);
3670 my $saved_numberformat = $::myconfig{numberformat};
3671 $::myconfig{numberformat} = $numberformat;
3673 foreach my $idx (@indices) {
3674 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3675 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3676 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $self->format_amount(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i], $places);
3680 next unless defined $self->{$idx};
3682 if (!ref($self->{$idx})) {
3683 $self->{$idx} = $self->format_amount(\%::myconfig, $self->{$idx}, $places);
3685 } elsif (ref($self->{$idx}) eq "ARRAY") {
3686 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3687 $self->{$idx}->[$i] = $self->format_amount(\%::myconfig, $self->{$idx}->[$i], $places);
3692 $::myconfig{numberformat} = $saved_numberformat;
3701 SL::Form.pm - main data object.
3705 This is the main data object of Lx-Office.
3706 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
3707 Points of interest for a beginner are:
3709 - $form->error - renders a generic error in html. accepts an error message
3710 - $form->get_standard_dbh - returns a database connection for the
3712 =head1 SPECIAL FUNCTIONS
3714 =head2 C<_store_value()>
3716 parses a complex var name, and stores it in the form.
3719 $form->_store_value($key, $value);
3721 keys must start with a string, and can contain various tokens.
3722 supported key structures are:
3725 simple key strings work as expected
3730 separating two keys by a dot (.) will result in a hash lookup for the inner value
3731 this is similar to the behaviour of java and templating mechanisms.
3733 filter.description => $form->{filter}->{description}
3735 3. array+hashref access
3737 adding brackets ([]) before the dot will cause the next hash to be put into an array.
3738 using [+] instead of [] will force a new array index. this is useful for recurring
3739 data structures like part lists. put a [+] into the first varname, and use [] on the
3742 repeating these names in your template:
3745 invoice.items[].parts_id
3749 $form->{invoice}->{items}->[
3763 using brackets at the end of a name will result in a pure array to be created.
3764 note that you mustn't use [+], which is reserved for array+hash access and will
3765 result in undefined behaviour in array context.
3767 filter.status[] => $form->{status}->[ val1, val2, ... ]
3769 =head2 C<update_business> PARAMS
3772 \%config, - config hashref
3773 $business_id, - business id
3774 $dbh - optional database handle
3776 handles business (thats customer/vendor types) sequences.
3778 special behaviour for empty strings in customerinitnumber field:
3779 will in this case not increase the value, and return undef.
3781 =head2 C<redirect_header> $url
3783 Generates a HTTP redirection header for the new C<$url>. Constructs an
3784 absolute URL including scheme, host name and port. If C<$url> is a
3785 relative URL then it is considered relative to Lx-Office base URL.
3787 This function C<die>s if headers have already been created with
3788 C<$::form-E<gt>header>.
3792 print $::form->redirect_header('oe.pl?action=edit&id=1234');
3793 print $::form->redirect_header('http://www.lx-office.org/');
3797 Generates a general purpose http/html header and includes most of the scripts
3798 ans stylesheets needed.
3800 Only one header will be generated. If the method was already called in this
3801 request it will not output anything and return undef. Also if no
3802 HTTP_USER_AGENT is found, no header is generated.
3804 Although header does not accept parameters itself, it will honor special
3805 hashkeys of its Form instance:
3813 If one of these is set, a http-equiv refresh is generated. Missing parameters
3814 default to 3 seconds and the refering url.
3820 If these are arrayrefs the contents will be inlined into the header.
3824 If true, a css snippet will be generated that sets the page in landscape mode.
3828 Used to override the default favicon.
3832 A html page title will be generated from this