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 #======================================================================
59 use List::Util qw(first max min sum);
60 use List::MoreUtils qw(any apply);
67 disconnect_standard_dbh();
70 sub disconnect_standard_dbh {
71 return unless $standard_dbh;
72 $standard_dbh->disconnect();
77 $main::lxdebug->enter_sub(2);
83 my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
88 $curr = \ $self->{ shift @tokens };
92 my $sep = shift @tokens;
93 my $key = shift @tokens;
95 $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
96 $curr = \ $$curr->[max 0, $#$$curr] if $sep eq '[].';
97 $curr = \ $$curr->[++$#$$curr] if $sep eq '[+].';
98 $curr = \ $$curr->{$key}
103 $main::lxdebug->leave_sub(2);
109 $main::lxdebug->enter_sub(2);
114 my @pairs = split(/&/, $input);
117 my ($key, $value) = split(/=/, $_, 2);
118 $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
121 $main::lxdebug->leave_sub(2);
124 sub _request_to_hash {
125 $main::lxdebug->enter_sub(2);
130 if (!$ENV{'CONTENT_TYPE'}
131 || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
133 $self->_input_to_hash($input);
135 $main::lxdebug->leave_sub(2);
139 my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
141 my $boundary = '--' . $1;
143 foreach my $line (split m/\n/, $input) {
144 last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
146 if (($line eq $boundary) || ($line eq "$boundary\r")) {
147 ${ $previous } =~ s|\r?\n$|| if $previous;
153 $content_type = "text/plain";
160 next unless $boundary_found;
162 if (!$headers_done) {
163 $line =~ s/[\r\n]*$//;
170 if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
171 if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
173 substr $line, $-[0], $+[0] - $-[0], "";
176 if ($line =~ m|name\s*=\s*"(.*?)"|i) {
178 substr $line, $-[0], $+[0] - $-[0], "";
181 $previous = $self->_store_value($name, '') if ($name);
182 $self->{FILENAME} = $filename if ($filename);
187 if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
194 next unless $previous;
196 ${ $previous } .= "${line}\n";
199 ${ $previous } =~ s|\r?\n$|| if $previous;
201 $main::lxdebug->leave_sub(2);
204 sub _recode_recursively {
205 $main::lxdebug->enter_sub();
206 my ($iconv, $param) = @_;
208 if (any { ref $param eq $_ } qw(Form HASH)) {
209 foreach my $key (keys %{ $param }) {
210 if (!ref $param->{$key}) {
211 # Workaround for a bug: converting $param->{$key} directly
212 # leads to 'undef'. I don't know why. Converting a copy works,
214 $param->{$key} = $iconv->convert("" . $param->{$key});
216 _recode_recursively($iconv, $param->{$key});
220 } elsif (ref $param eq 'ARRAY') {
221 foreach my $idx (0 .. scalar(@{ $param }) - 1) {
222 if (!ref $param->[$idx]) {
223 # Workaround for a bug: converting $param->[$idx] directly
224 # leads to 'undef'. I don't know why. Converting a copy works,
226 $param->[$idx] = $iconv->convert("" . $param->[$idx]);
228 _recode_recursively($iconv, $param->[$idx]);
232 $main::lxdebug->leave_sub();
236 $main::lxdebug->enter_sub();
242 if ($LXDebug::watch_form) {
243 require SL::Watchdog;
244 tie %{ $self }, 'SL::Watchdog';
249 $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
250 $self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
252 if ($ENV{CONTENT_LENGTH}) {
254 read STDIN, $content, $ENV{CONTENT_LENGTH};
255 $self->_request_to_hash($content);
258 my $db_charset = $main::dbcharset;
259 $db_charset ||= Common::DEFAULT_CHARSET;
261 my $encoding = $self->{INPUT_ENCODING} || $db_charset;
262 delete $self->{INPUT_ENCODING};
264 _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
266 $self->{action} = lc $self->{action};
267 $self->{action} =~ s/( |-|,|\#)/_/g;
269 #$self->{version} = "2.6.1"; # Old hardcoded but secure style
270 open VERSION_FILE, "VERSION"; # New but flexible code reads version from VERSION-file
271 $self->{version} = <VERSION_FILE>;
273 $self->{version} =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
275 $main::lxdebug->leave_sub();
280 sub _flatten_variables_rec {
281 $main::lxdebug->enter_sub(2);
290 if ('' eq ref $curr->{$key}) {
291 @result = ({ 'key' => $prefix . $key, 'value' => $curr->{$key} });
293 } elsif ('HASH' eq ref $curr->{$key}) {
294 foreach my $hash_key (sort keys %{ $curr->{$key} }) {
295 push @result, $self->_flatten_variables_rec($curr->{$key}, $prefix . $key . '.', $hash_key);
299 foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
300 my $first_array_entry = 1;
302 foreach my $hash_key (sort keys %{ $curr->{$key}->[$idx] }) {
303 push @result, $self->_flatten_variables_rec($curr->{$key}->[$idx], $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
304 $first_array_entry = 0;
309 $main::lxdebug->leave_sub(2);
314 sub flatten_variables {
315 $main::lxdebug->enter_sub(2);
323 push @variables, $self->_flatten_variables_rec($self, '', $_);
326 $main::lxdebug->leave_sub(2);
331 sub flatten_standard_variables {
332 $main::lxdebug->enter_sub(2);
335 my %skip_keys = map { $_ => 1 } (qw(login password header stylesheet titlebar version), @_);
339 foreach (grep { ! $skip_keys{$_} } keys %{ $self }) {
340 push @variables, $self->_flatten_variables_rec($self, '', $_);
343 $main::lxdebug->leave_sub(2);
349 $main::lxdebug->enter_sub();
355 map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
357 $main::lxdebug->leave_sub();
361 $main::lxdebug->enter_sub(2);
364 my $password = $self->{password};
366 $self->{password} = 'X' x 8;
368 local $Data::Dumper::Sortkeys = 1;
369 my $output = Dumper($self);
371 $self->{password} = $password;
373 $main::lxdebug->leave_sub(2);
379 $main::lxdebug->enter_sub(2);
381 my ($self, $str) = @_;
383 $str = Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
384 $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
386 $main::lxdebug->leave_sub(2);
392 $main::lxdebug->enter_sub(2);
394 my ($self, $str) = @_;
399 $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
401 $main::lxdebug->leave_sub(2);
407 $main::lxdebug->enter_sub();
408 my ($self, $str) = @_;
410 if ($str && !ref($str)) {
411 $str =~ s/\"/"/g;
414 $main::lxdebug->leave_sub();
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();
437 map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
439 for (sort keys %$self) {
440 next if (($_ eq "header") || (ref($self->{$_}) ne ""));
441 print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
444 $main::lxdebug->leave_sub();
448 $main::lxdebug->enter_sub();
450 $main::lxdebug->show_backtrace();
452 my ($self, $msg) = @_;
453 if ($ENV{HTTP_USER_AGENT}) {
455 $self->show_generic_error($msg);
458 print STDERR "Error: $msg\n";
462 $main::lxdebug->leave_sub();
466 $main::lxdebug->enter_sub();
468 my ($self, $msg) = @_;
470 if ($ENV{HTTP_USER_AGENT}) {
473 if (!$self->{header}) {
479 <p class="message_ok"><b>$msg</b></p>
481 <script type="text/javascript">
483 // If JavaScript is enabled, the whole thing will be reloaded.
484 // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
485 // it now loads the correct code into the browser instead of do nothing.
486 setTimeout("top.frames.location.href='login.pl'",500);
495 if ($self->{info_function}) {
496 &{ $self->{info_function} }($msg);
502 $main::lxdebug->leave_sub();
505 # calculates the number of rows in a textarea based on the content and column number
506 # can be capped with maxrows
508 $main::lxdebug->enter_sub();
509 my ($self, $str, $cols, $maxrows, $minrows) = @_;
513 my $rows = sum map { int((length() - 2) / $cols) + 1 } split /\r/, $str;
516 $main::lxdebug->leave_sub();
518 return max(min($rows, $maxrows), $minrows);
522 $main::lxdebug->enter_sub();
524 my ($self, $msg) = @_;
526 $self->error("$msg\n" . $DBI::errstr);
528 $main::lxdebug->leave_sub();
532 $main::lxdebug->enter_sub();
534 my ($self, $name, $msg) = @_;
537 foreach my $part (split m/\./, $name) {
538 if (!$curr->{$part} || ($curr->{$part} =~ /^\s*$/)) {
541 $curr = $curr->{$part};
544 $main::lxdebug->leave_sub();
547 sub _get_request_uri {
550 return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
552 my $scheme = $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
553 my $port = $ENV{SERVER_PORT} || '';
554 $port = undef if (($scheme eq 'http' ) && ($port == 80))
555 || (($scheme eq 'https') && ($port == 443));
557 my $uri = URI->new("${scheme}://");
558 $uri->scheme($scheme);
560 $uri->host($ENV{HTTP_HOST} || $ENV{SERVER_ADDR});
561 $uri->path_query($ENV{REQUEST_URI});
567 sub _add_to_request_uri {
570 my $relative_new_path = shift;
571 my $request_uri = shift || $self->_get_request_uri;
572 my $relative_new_uri = URI->new($relative_new_path);
573 my @request_segments = $request_uri->path_segments;
575 my $new_uri = $request_uri->clone;
576 $new_uri->path_segments(@request_segments[0..scalar(@request_segments) - 2], $relative_new_uri->path_segments);
581 sub create_http_response {
582 $main::lxdebug->enter_sub();
587 my $cgi = $main::cgi;
588 $cgi ||= CGI->new('');
591 if (defined $main::auth) {
592 my $uri = $self->_get_request_uri;
593 my @segments = $uri->path_segments;
595 $uri->path_segments(@segments);
597 my $session_cookie_value = $main::auth->get_session_id();
599 if ($session_cookie_value) {
600 $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
601 '-value' => $session_cookie_value,
602 '-path' => $uri->path,
603 '-secure' => $ENV{HTTPS});
607 my %cgi_params = ('-type' => $params{content_type});
608 $cgi_params{'-charset'} = $params{charset} if ($params{charset});
609 $cgi_params{'-cookie'} = $session_cookie if ($session_cookie);
611 my $output = $cgi->header(%cgi_params);
613 $main::lxdebug->leave_sub();
620 $::lxdebug->enter_sub;
622 # extra code is currently only used by menuv3 and menuv4 to set their css.
623 # it is strongly deprecated, and will be changed in a future version.
624 my ($self, $extra_code) = @_;
625 my $db_charset = $::dbcharset || Common::DEFAULT_CHARSET;
628 $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
630 $self->{favicon} ||= "favicon.ico";
631 $self->{titlebar} = "$self->{title} - $self->{titlebar}" if $self->{title};
634 if ($self->{refresh_url} || $self->{refresh_time}) {
635 my $refresh_time = $self->{refresh_time} || 3;
636 my $refresh_url = $self->{refresh_url} || $ENV{REFERER};
637 push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
640 push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
641 for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
643 push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
644 push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
645 push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
646 '<script type="text/javascript" src="js/common.js"></script>',
647 '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
648 '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
649 '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
650 '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
651 '<script type="text/javascript" src="js/part_selection.js"></script>';
652 push @header, $self->{javascript} if $self->{javascript};
653 push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
654 push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
655 push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
656 join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
658 # if there is a title, we put some JavaScript in to the page, wich writes a
659 # meaningful title-tag for our frameset.
661 if ($self->{title}) {
663 <script type="text/javascript">
665 // Write a meaningful title-tag for our frameset.
666 top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
672 print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
673 print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
674 if $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
678 <meta http-equiv="Content-Type" content="text/html; charset=$db_charset">
679 <title>$self->{titlebar}</title>
681 print " $_\n" for @header;
683 <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
684 <meta name="robots" content="noindex,nofollow" />
685 <script type="text/javascript" src="js/highlight_input.js"></script>
686 <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
687 <script type="text/javascript" src="js/tabcontent.js">
689 /***********************************************
690 * Tab Content script v2.2- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
691 * This notice MUST stay intact for legal use
692 * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
693 ***********************************************/
702 $::lxdebug->leave_sub;
705 sub ajax_response_header {
706 $main::lxdebug->enter_sub();
710 my $db_charset = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
711 my $cgi = $main::cgi || CGI->new('');
712 my $output = $cgi->header('-charset' => $db_charset);
714 $main::lxdebug->leave_sub();
719 sub redirect_header {
723 my $base_uri = $self->_get_request_uri;
724 my $new_uri = URI->new_abs($new_url, $base_uri);
726 die "Headers already sent" if $::self->{header};
729 my $cgi = $main::cgi || CGI->new('');
730 return $cgi->redirect($new_uri);
733 sub set_standard_title {
734 $::lxdebug->enter_sub;
737 $self->{titlebar} = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
738 $self->{titlebar} .= "- $::myconfig{name}" if $::myconfig{name};
739 $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
741 $::lxdebug->leave_sub;
744 sub _prepare_html_template {
745 $main::lxdebug->enter_sub();
747 my ($self, $file, $additional_params) = @_;
750 if (!%::myconfig || !$::myconfig{"countrycode"}) {
751 $language = $main::language;
753 $language = $main::myconfig{"countrycode"};
755 $language = "de" unless ($language);
757 if (-f "templates/webpages/${file}.html") {
758 if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
759 my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
760 "Please re-run 'locales.pl' in 'locale/${language}'.";
761 print(qq|<pre>$info</pre>|);
765 $file = "templates/webpages/${file}.html";
768 my $info = "Web page template '${file}' not found.\n" .
769 "Please re-run 'locales.pl' in 'locale/${language}'.";
770 print(qq|<pre>$info</pre>|);
774 if ($self->{"DEBUG"}) {
775 $additional_params->{"DEBUG"} = $self->{"DEBUG"};
778 if ($additional_params->{"DEBUG"}) {
779 $additional_params->{"DEBUG"} =
780 "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
783 if (%main::myconfig) {
784 $::myconfig{jsc_dateformat} = apply {
788 } $::myconfig{"dateformat"};
789 $additional_params->{"myconfig"} ||= \%::myconfig;
790 map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
793 $additional_params->{"conf_dbcharset"} = $::dbcharset;
794 $additional_params->{"conf_webdav"} = $::webdav;
795 $additional_params->{"conf_lizenzen"} = $::lizenzen;
796 $additional_params->{"conf_latex_templates"} = $::latex;
797 $additional_params->{"conf_opendocument_templates"} = $::opendocument_templates;
798 $additional_params->{"conf_vertreter"} = $::vertreter;
799 $additional_params->{"conf_show_best_before"} = $::show_best_before;
800 $additional_params->{"conf_parts_image_css"} = $::parts_image_css;
801 $additional_params->{"conf_parts_listing_images"} = $::parts_listing_images;
802 $additional_params->{"conf_parts_show_image"} = $::parts_show_image;
804 if (%main::debug_options) {
805 map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
808 if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
809 while (my ($key, $value) = each %{ $main::auth->{RIGHTS}->{$self->{login}} }) {
810 $additional_params->{"AUTH_RIGHTS_" . uc($key)} = $value;
814 $main::lxdebug->leave_sub();
819 sub parse_html_template {
820 $main::lxdebug->enter_sub();
822 my ($self, $file, $additional_params) = @_;
824 $additional_params ||= { };
826 my $real_file = $self->_prepare_html_template($file, $additional_params);
827 my $template = $self->template || $self->init_template;
829 map { $additional_params->{$_} ||= $self->{$_} } keys %{ $self };
832 $template->process($real_file, $additional_params, \$output) || die $template->error;
834 $main::lxdebug->leave_sub();
842 return if $self->template;
844 return $self->template(Template->new({
849 'PLUGIN_BASE' => 'SL::Template::Plugin',
850 'INCLUDE_PATH' => '.:templates/webpages',
851 'COMPILE_EXT' => '.tcc',
852 'COMPILE_DIR' => $::userspath . '/templates-cache',
858 $self->{template_object} = shift if @_;
859 return $self->{template_object};
862 sub show_generic_error {
863 $main::lxdebug->enter_sub();
865 my ($self, $error, %params) = @_;
868 'title_error' => $params{title},
869 'label_error' => $error,
872 if ($params{action}) {
875 map { delete($self->{$_}); } qw(action);
876 map { push @vars, { "name" => $_, "value" => $self->{$_} } if (!ref($self->{$_})); } keys %{ $self };
878 $add_params->{SHOW_BUTTON} = 1;
879 $add_params->{BUTTON_LABEL} = $params{label} || $params{action};
880 $add_params->{VARIABLES} = \@vars;
882 } elsif ($params{back_button}) {
883 $add_params->{SHOW_BACK_BUTTON} = 1;
886 $self->{title} = $params{title} if $params{title};
889 print $self->parse_html_template("generic/error", $add_params);
891 print STDERR "Error: $error\n";
893 $main::lxdebug->leave_sub();
898 sub show_generic_information {
899 $main::lxdebug->enter_sub();
901 my ($self, $text, $title) = @_;
904 'title_information' => $title,
905 'label_information' => $text,
908 $self->{title} = $title if ($title);
911 print $self->parse_html_template("generic/information", $add_params);
913 $main::lxdebug->leave_sub();
918 # write Trigger JavaScript-Code ($qty = quantity of Triggers)
919 # changed it to accept an arbitrary number of triggers - sschoeling
921 $main::lxdebug->enter_sub();
924 my $myconfig = shift;
927 # set dateform for jsscript
930 "dd.mm.yy" => "%d.%m.%Y",
931 "dd-mm-yy" => "%d-%m-%Y",
932 "dd/mm/yy" => "%d/%m/%Y",
933 "mm/dd/yy" => "%m/%d/%Y",
934 "mm-dd-yy" => "%m-%d-%Y",
935 "yyyy-mm-dd" => "%Y-%m-%d",
938 my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
939 $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
946 inputField : "| . (shift) . qq|",
947 ifFormat :"$ifFormat",
948 align : "| . (shift) . qq|",
949 button : "| . (shift) . qq|"
955 <script type="text/javascript">
956 <!--| . join("", @triggers) . qq|//-->
960 $main::lxdebug->leave_sub();
963 } #end sub write_trigger
966 $main::lxdebug->enter_sub();
968 my ($self, $msg) = @_;
970 if (!$self->{callback}) {
976 # my ($script, $argv) = split(/\?/, $self->{callback}, 2);
977 # $script =~ s|.*/||;
978 # $script =~ s|[^a-zA-Z0-9_\.]||g;
979 # exec("perl", "$script", $argv);
981 print $::form->redirect_header($self->{callback});
983 $main::lxdebug->leave_sub();
986 # sort of columns removed - empty sub
988 $main::lxdebug->enter_sub();
990 my ($self, @columns) = @_;
992 $main::lxdebug->leave_sub();
998 $main::lxdebug->enter_sub(2);
1000 my ($self, $myconfig, $amount, $places, $dash) = @_;
1002 if ($amount eq "") {
1006 # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
1008 my $neg = ($amount =~ s/^-//);
1009 my $exp = ($amount =~ m/[e]/) ? 1 : 0;
1011 if (defined($places) && ($places ne '')) {
1017 my ($actual_places) = ($amount =~ /\.(\d+)/);
1018 $actual_places = length($actual_places);
1019 $places = $actual_places > $places ? $actual_places : $places;
1022 $amount = $self->round_amount($amount, $places);
1025 my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
1026 my @p = split(/\./, $amount); # split amount at decimal point
1028 $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
1031 $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
1034 ($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) :
1035 ($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) :
1036 ($neg ? "-$amount" : "$amount" ) ;
1040 $main::lxdebug->leave_sub(2);
1044 sub format_amount_units {
1045 $main::lxdebug->enter_sub();
1050 my $myconfig = \%main::myconfig;
1051 my $amount = $params{amount} * 1;
1052 my $places = $params{places};
1053 my $part_unit_name = $params{part_unit};
1054 my $amount_unit_name = $params{amount_unit};
1055 my $conv_units = $params{conv_units};
1056 my $max_places = $params{max_places};
1058 if (!$part_unit_name) {
1059 $main::lxdebug->leave_sub();
1063 AM->retrieve_all_units();
1064 my $all_units = $main::all_units;
1066 if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
1067 $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
1070 if (!scalar @{ $conv_units }) {
1071 my $result = $self->format_amount($myconfig, $amount, $places, undef, $max_places) . " " . $part_unit_name;
1072 $main::lxdebug->leave_sub();
1076 my $part_unit = $all_units->{$part_unit_name};
1077 my $conv_unit = ($amount_unit_name && ($amount_unit_name ne $part_unit_name)) ? $all_units->{$amount_unit_name} : $part_unit;
1079 $amount *= $conv_unit->{factor};
1084 foreach my $unit (@$conv_units) {
1085 my $last = $unit->{name} eq $part_unit->{name};
1087 $num = int($amount / $unit->{factor});
1088 $amount -= $num * $unit->{factor};
1091 if ($last ? $amount : $num) {
1092 push @values, { "unit" => $unit->{name},
1093 "amount" => $last ? $amount / $unit->{factor} : $num,
1094 "places" => $last ? $places : 0 };
1101 push @values, { "unit" => $part_unit_name,
1106 my $result = join " ", map { $self->format_amount($myconfig, $_->{amount}, $_->{places}, undef, $max_places), $_->{unit} } @values;
1108 $main::lxdebug->leave_sub();
1114 $main::lxdebug->enter_sub(2);
1119 $input =~ s/(^|[^\#]) \# (\d+) /$1$_[$2 - 1]/gx;
1120 $input =~ s/(^|[^\#]) \#\{(\d+)\}/$1$_[$2 - 1]/gx;
1121 $input =~ s/\#\#/\#/g;
1123 $main::lxdebug->leave_sub(2);
1131 $main::lxdebug->enter_sub(2);
1133 my ($self, $myconfig, $amount) = @_;
1135 if ( ($myconfig->{numberformat} eq '1.000,00')
1136 || ($myconfig->{numberformat} eq '1000,00')) {
1141 if ($myconfig->{numberformat} eq "1'000.00") {
1147 $main::lxdebug->leave_sub(2);
1149 return ($amount * 1);
1153 $main::lxdebug->enter_sub(2);
1155 my ($self, $amount, $places) = @_;
1158 # Rounding like "Kaufmannsrunden" (see http://de.wikipedia.org/wiki/Rundung )
1160 # Round amounts to eight places before rounding to the requested
1161 # number of places. This gets rid of errors due to internal floating
1162 # point representation.
1163 $amount = $self->round_amount($amount, 8) if $places < 8;
1164 $amount = $amount * (10**($places));
1165 $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
1167 $main::lxdebug->leave_sub(2);
1169 return $round_amount;
1173 sub parse_template {
1174 $main::lxdebug->enter_sub();
1176 my ($self, $myconfig, $userspath) = @_;
1181 $self->{"cwd"} = getcwd();
1182 $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
1187 if ($self->{"format"} =~ /(opendocument|oasis)/i) {
1188 $template_type = 'OpenDocument';
1189 $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
1191 } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
1192 $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
1193 $template_type = 'LaTeX';
1194 $ext_for_format = 'pdf';
1196 } elsif (($self->{"format"} =~ /html/i) || (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
1197 $template_type = 'HTML';
1198 $ext_for_format = 'html';
1200 } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
1201 $template_type = 'XML';
1202 $ext_for_format = 'xml';
1204 } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
1205 $template_type = 'XML';
1207 } elsif ( $self->{"format"} =~ /excel/i ) {
1208 $template_type = 'Excel';
1209 $ext_for_format = 'xls';
1211 } elsif ( defined $self->{'format'}) {
1212 $self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
1214 } elsif ( $self->{'format'} eq '' ) {
1215 $self->error("No Outputformat given: $self->{'format'}");
1217 } else { #Catch the rest
1218 $self->error("Outputformat not defined: $self->{'format'}");
1221 my $template = SL::Template::create(type => $template_type,
1222 file_name => $self->{IN},
1224 myconfig => $myconfig,
1225 userspath => $userspath);
1227 # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
1228 $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
1230 if (!$self->{employee_id}) {
1231 map { $self->{"employee_${_}"} = $myconfig->{$_}; } qw(email tel fax name signature company address businessnumber co_ustid taxnumber duns);
1234 map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
1236 $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
1238 # OUT is used for the media, screen, printer, email
1239 # for postscript we store a copy in a temporary file
1241 my $prepend_userspath;
1243 if (!$self->{tmpfile}) {
1244 $self->{tmpfile} = "${fileid}.$self->{IN}";
1245 $prepend_userspath = 1;
1248 $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
1250 $self->{tmpfile} =~ s|.*/||;
1251 $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
1252 $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
1254 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1255 $out = $self->{OUT};
1256 $self->{OUT} = ">$self->{tmpfile}";
1262 open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
1263 $result = $template->parse(*OUT);
1268 $result = $template->parse(*STDOUT);
1273 $self->error("$self->{IN} : " . $template->get_error());
1276 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1278 if ($self->{media} eq 'email') {
1280 my $mail = new Mailer;
1282 map { $mail->{$_} = $self->{$_} }
1283 qw(cc bcc subject message version format);
1284 $mail->{charset} = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
1285 $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
1286 $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
1287 $mail->{fileid} = "$fileid.";
1288 $myconfig->{signature} =~ s/\r//g;
1290 # if we send html or plain text inline
1291 if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
1292 $mail->{contenttype} = "text/html";
1294 $mail->{message} =~ s/\r//g;
1295 $mail->{message} =~ s/\n/<br>\n/g;
1296 $myconfig->{signature} =~ s/\n/<br>\n/g;
1297 $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
1299 open(IN, $self->{tmpfile})
1300 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1302 $mail->{message} .= $_;
1309 if (!$self->{"do_not_attach"}) {
1310 my $attachment_name = $self->{attachment_filename} || $self->{tmpfile};
1311 $attachment_name =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
1312 $mail->{attachments} = [{ "filename" => $self->{tmpfile},
1313 "name" => $attachment_name }];
1316 $mail->{message} =~ s/\r//g;
1317 $mail->{message} .= "\n-- \n$myconfig->{signature}";
1321 my $err = $mail->send();
1322 $self->error($self->cleanup . "$err") if ($err);
1326 $self->{OUT} = $out;
1328 my $numbytes = (-s $self->{tmpfile});
1329 open(IN, $self->{tmpfile})
1330 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1332 $self->{copies} = 1 unless $self->{media} eq 'printer';
1334 chdir("$self->{cwd}");
1335 #print(STDERR "Kopien $self->{copies}\n");
1336 #print(STDERR "OUT $self->{OUT}\n");
1337 for my $i (1 .. $self->{copies}) {
1339 open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
1340 print OUT while <IN>;
1345 $self->{attachment_filename} = ($self->{attachment_filename})
1346 ? $self->{attachment_filename}
1347 : $self->generate_attachment_filename();
1349 # launch application
1350 print qq|Content-Type: | . $template->get_mime_type() . qq|
1351 Content-Disposition: attachment; filename="$self->{attachment_filename}"
1352 Content-Length: $numbytes
1356 $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
1367 chdir("$self->{cwd}");
1368 $main::lxdebug->leave_sub();
1371 sub get_formname_translation {
1372 $main::lxdebug->enter_sub();
1373 my ($self, $formname) = @_;
1375 $formname ||= $self->{formname};
1377 my %formname_translations = (
1378 bin_list => $main::locale->text('Bin List'),
1379 credit_note => $main::locale->text('Credit Note'),
1380 invoice => $main::locale->text('Invoice'),
1381 pick_list => $main::locale->text('Pick List'),
1382 proforma => $main::locale->text('Proforma Invoice'),
1383 purchase_order => $main::locale->text('Purchase Order'),
1384 request_quotation => $main::locale->text('RFQ'),
1385 sales_order => $main::locale->text('Confirmation'),
1386 sales_quotation => $main::locale->text('Quotation'),
1387 storno_invoice => $main::locale->text('Storno Invoice'),
1388 sales_delivery_order => $main::locale->text('Delivery Order'),
1389 purchase_delivery_order => $main::locale->text('Delivery Order'),
1390 dunning => $main::locale->text('Dunning'),
1393 $main::lxdebug->leave_sub();
1394 return $formname_translations{$formname}
1397 sub get_number_prefix_for_type {
1398 $main::lxdebug->enter_sub();
1402 (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
1403 : ($self->{type} =~ /_quotation$/) ? 'quo'
1404 : ($self->{type} =~ /_delivery_order$/) ? 'do'
1407 $main::lxdebug->leave_sub();
1411 sub get_extension_for_format {
1412 $main::lxdebug->enter_sub();
1415 my $extension = $self->{format} =~ /pdf/i ? ".pdf"
1416 : $self->{format} =~ /postscript/i ? ".ps"
1417 : $self->{format} =~ /opendocument/i ? ".odt"
1418 : $self->{format} =~ /excel/i ? ".xls"
1419 : $self->{format} =~ /html/i ? ".html"
1422 $main::lxdebug->leave_sub();
1426 sub generate_attachment_filename {
1427 $main::lxdebug->enter_sub();
1430 my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1431 my $prefix = $self->get_number_prefix_for_type();
1433 if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
1434 $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
1436 } elsif ($attachment_filename && $self->{"${prefix}number"}) {
1437 $attachment_filename .= "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
1440 $attachment_filename = "";
1443 $attachment_filename = $main::locale->quote_special_chars('filenames', $attachment_filename);
1444 $attachment_filename =~ s|[\s/\\]+|_|g;
1446 $main::lxdebug->leave_sub();
1447 return $attachment_filename;
1450 sub generate_email_subject {
1451 $main::lxdebug->enter_sub();
1454 my $subject = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1455 my $prefix = $self->get_number_prefix_for_type();
1457 if ($subject && $self->{"${prefix}number"}) {
1458 $subject .= " " . $self->{"${prefix}number"}
1461 $main::lxdebug->leave_sub();
1466 $main::lxdebug->enter_sub();
1470 chdir("$self->{tmpdir}");
1473 if (-f "$self->{tmpfile}.err") {
1474 open(FH, "$self->{tmpfile}.err");
1479 if ($self->{tmpfile} && ! $::keep_temp_files) {
1480 $self->{tmpfile} =~ s|.*/||g;
1482 $self->{tmpfile} =~ s/\.\w+$//g;
1483 my $tmpfile = $self->{tmpfile};
1484 unlink(<$tmpfile.*>);
1487 chdir("$self->{cwd}");
1489 $main::lxdebug->leave_sub();
1495 $main::lxdebug->enter_sub();
1497 my ($self, $date, $myconfig) = @_;
1500 if ($date && $date =~ /\D/) {
1502 if ($myconfig->{dateformat} =~ /^yy/) {
1503 ($yy, $mm, $dd) = split /\D/, $date;
1505 if ($myconfig->{dateformat} =~ /^mm/) {
1506 ($mm, $dd, $yy) = split /\D/, $date;
1508 if ($myconfig->{dateformat} =~ /^dd/) {
1509 ($dd, $mm, $yy) = split /\D/, $date;
1514 $yy = ($yy < 70) ? $yy + 2000 : $yy;
1515 $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
1517 $dd = "0$dd" if ($dd < 10);
1518 $mm = "0$mm" if ($mm < 10);
1520 $date = "$yy$mm$dd";
1523 $main::lxdebug->leave_sub();
1528 # Database routines used throughout
1530 sub _dbconnect_options {
1532 my $options = { pg_enable_utf8 => $::locale->is_utf8,
1539 $main::lxdebug->enter_sub(2);
1541 my ($self, $myconfig) = @_;
1543 # connect to database
1544 my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
1548 if ($myconfig->{dboptions}) {
1549 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1552 $main::lxdebug->leave_sub(2);
1557 sub dbconnect_noauto {
1558 $main::lxdebug->enter_sub();
1560 my ($self, $myconfig) = @_;
1562 # connect to database
1563 my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
1567 if ($myconfig->{dboptions}) {
1568 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1571 $main::lxdebug->leave_sub();
1576 sub get_standard_dbh {
1577 $main::lxdebug->enter_sub(2);
1580 my $myconfig = shift || \%::myconfig;
1582 if ($standard_dbh && !$standard_dbh->{Active}) {
1583 $main::lxdebug->message(LXDebug->INFO(), "get_standard_dbh: \$standard_dbh is defined but not Active anymore");
1584 undef $standard_dbh;
1587 $standard_dbh ||= $self->dbconnect_noauto($myconfig);
1589 $main::lxdebug->leave_sub(2);
1591 return $standard_dbh;
1595 $main::lxdebug->enter_sub();
1597 my ($self, $date, $myconfig) = @_;
1598 my $dbh = $self->dbconnect($myconfig);
1600 my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
1601 my $sth = prepare_execute_query($self, $dbh, $query, $date);
1602 my ($closed) = $sth->fetchrow_array;
1604 $main::lxdebug->leave_sub();
1609 sub update_balance {
1610 $main::lxdebug->enter_sub();
1612 my ($self, $dbh, $table, $field, $where, $value, @values) = @_;
1614 # if we have a value, go do it
1617 # retrieve balance from table
1618 my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
1619 my $sth = prepare_execute_query($self, $dbh, $query, @values);
1620 my ($balance) = $sth->fetchrow_array;
1626 $query = "UPDATE $table SET $field = $balance WHERE $where";
1627 do_query($self, $dbh, $query, @values);
1629 $main::lxdebug->leave_sub();
1632 sub update_exchangerate {
1633 $main::lxdebug->enter_sub();
1635 my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
1637 # some sanity check for currency
1639 $main::lxdebug->leave_sub();
1642 $query = qq|SELECT curr FROM defaults|;
1644 my ($currency) = selectrow_query($self, $dbh, $query);
1645 my ($defaultcurrency) = split m/:/, $currency;
1648 if ($curr eq $defaultcurrency) {
1649 $main::lxdebug->leave_sub();
1653 $query = qq|SELECT e.curr FROM exchangerate e
1654 WHERE e.curr = ? AND e.transdate = ?
1656 my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
1665 $buy = conv_i($buy, "NULL");
1666 $sell = conv_i($sell, "NULL");
1669 if ($buy != 0 && $sell != 0) {
1670 $set = "buy = $buy, sell = $sell";
1671 } elsif ($buy != 0) {
1672 $set = "buy = $buy";
1673 } elsif ($sell != 0) {
1674 $set = "sell = $sell";
1677 if ($sth->fetchrow_array) {
1678 $query = qq|UPDATE exchangerate
1684 $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
1685 VALUES (?, $buy, $sell, ?)|;
1688 do_query($self, $dbh, $query, $curr, $transdate);
1690 $main::lxdebug->leave_sub();
1693 sub save_exchangerate {
1694 $main::lxdebug->enter_sub();
1696 my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
1698 my $dbh = $self->dbconnect($myconfig);
1702 $buy = $rate if $fld eq 'buy';
1703 $sell = $rate if $fld eq 'sell';
1706 $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
1711 $main::lxdebug->leave_sub();
1714 sub get_exchangerate {
1715 $main::lxdebug->enter_sub();
1717 my ($self, $dbh, $curr, $transdate, $fld) = @_;
1720 unless ($transdate) {
1721 $main::lxdebug->leave_sub();
1725 $query = qq|SELECT curr FROM defaults|;
1727 my ($currency) = selectrow_query($self, $dbh, $query);
1728 my ($defaultcurrency) = split m/:/, $currency;
1730 if ($currency eq $defaultcurrency) {
1731 $main::lxdebug->leave_sub();
1735 $query = qq|SELECT e.$fld FROM exchangerate e
1736 WHERE e.curr = ? AND e.transdate = ?|;
1737 my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
1741 $main::lxdebug->leave_sub();
1743 return $exchangerate;
1746 sub check_exchangerate {
1747 $main::lxdebug->enter_sub();
1749 my ($self, $myconfig, $currency, $transdate, $fld) = @_;
1751 if ($fld !~/^buy|sell$/) {
1752 $self->error('Fatal: check_exchangerate called with invalid buy/sell argument');
1755 unless ($transdate) {
1756 $main::lxdebug->leave_sub();
1760 my ($defaultcurrency) = $self->get_default_currency($myconfig);
1762 if ($currency eq $defaultcurrency) {
1763 $main::lxdebug->leave_sub();
1767 my $dbh = $self->get_standard_dbh($myconfig);
1768 my $query = qq|SELECT e.$fld FROM exchangerate e
1769 WHERE e.curr = ? AND e.transdate = ?|;
1771 my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
1773 $main::lxdebug->leave_sub();
1775 return $exchangerate;
1778 sub get_all_currencies {
1779 $main::lxdebug->enter_sub();
1782 my $myconfig = shift || \%::myconfig;
1783 my $dbh = $self->get_standard_dbh($myconfig);
1785 my $query = qq|SELECT curr FROM defaults|;
1787 my ($curr) = selectrow_query($self, $dbh, $query);
1788 my @currencies = grep { $_ } map { s/\s//g; $_ } split m/:/, $curr;
1790 $main::lxdebug->leave_sub();
1795 sub get_default_currency {
1796 $main::lxdebug->enter_sub();
1798 my ($self, $myconfig) = @_;
1799 my @currencies = $self->get_all_currencies($myconfig);
1801 $main::lxdebug->leave_sub();
1803 return $currencies[0];
1806 sub set_payment_options {
1807 $main::lxdebug->enter_sub();
1809 my ($self, $myconfig, $transdate) = @_;
1811 return $main::lxdebug->leave_sub() unless ($self->{payment_id});
1813 my $dbh = $self->get_standard_dbh($myconfig);
1816 qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
1817 qq|FROM payment_terms p | .
1820 ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
1821 $self->{payment_terms}) =
1822 selectrow_query($self, $dbh, $query, $self->{payment_id});
1824 if ($transdate eq "") {
1825 if ($self->{invdate}) {
1826 $transdate = $self->{invdate};
1828 $transdate = $self->{transdate};
1833 qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
1834 qq|FROM payment_terms|;
1835 ($self->{netto_date}, $self->{skonto_date}) =
1836 selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
1838 my ($invtotal, $total);
1839 my (%amounts, %formatted_amounts);
1841 if ($self->{type} =~ /_order$/) {
1842 $amounts{invtotal} = $self->{ordtotal};
1843 $amounts{total} = $self->{ordtotal};
1845 } elsif ($self->{type} =~ /_quotation$/) {
1846 $amounts{invtotal} = $self->{quototal};
1847 $amounts{total} = $self->{quototal};
1850 $amounts{invtotal} = $self->{invtotal};
1851 $amounts{total} = $self->{total};
1853 $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
1855 map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
1857 $amounts{skonto_amount} = $amounts{invtotal} * $self->{percent_skonto};
1858 $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
1859 $amounts{total_wo_skonto} = $amounts{total} * (1 - $self->{percent_skonto});
1861 foreach (keys %amounts) {
1862 $amounts{$_} = $self->round_amount($amounts{$_}, 2);
1863 $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}, 2);
1866 if ($self->{"language_id"}) {
1868 qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
1869 qq|FROM translation_payment_terms t | .
1870 qq|LEFT JOIN language l ON t.language_id = l.id | .
1871 qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
1872 my ($description_long, $output_numberformat, $output_dateformat,
1873 $output_longdates) =
1874 selectrow_query($self, $dbh, $query,
1875 $self->{"language_id"}, $self->{"payment_id"});
1877 $self->{payment_terms} = $description_long if ($description_long);
1879 if ($output_dateformat) {
1880 foreach my $key (qw(netto_date skonto_date)) {
1882 $main::locale->reformat_date($myconfig, $self->{$key},
1888 if ($output_numberformat &&
1889 ($output_numberformat ne $myconfig->{"numberformat"})) {
1890 my $saved_numberformat = $myconfig->{"numberformat"};
1891 $myconfig->{"numberformat"} = $output_numberformat;
1892 map { $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}) } keys %amounts;
1893 $myconfig->{"numberformat"} = $saved_numberformat;
1897 $self->{payment_terms} =~ s/<%netto_date%>/$self->{netto_date}/g;
1898 $self->{payment_terms} =~ s/<%skonto_date%>/$self->{skonto_date}/g;
1899 $self->{payment_terms} =~ s/<%currency%>/$self->{currency}/g;
1900 $self->{payment_terms} =~ s/<%terms_netto%>/$self->{terms_netto}/g;
1901 $self->{payment_terms} =~ s/<%account_number%>/$self->{account_number}/g;
1902 $self->{payment_terms} =~ s/<%bank%>/$self->{bank}/g;
1903 $self->{payment_terms} =~ s/<%bank_code%>/$self->{bank_code}/g;
1905 map { $self->{payment_terms} =~ s/<%${_}%>/$formatted_amounts{$_}/g; } keys %formatted_amounts;
1907 $self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
1909 $main::lxdebug->leave_sub();
1913 sub get_template_language {
1914 $main::lxdebug->enter_sub();
1916 my ($self, $myconfig) = @_;
1918 my $template_code = "";
1920 if ($self->{language_id}) {
1921 my $dbh = $self->get_standard_dbh($myconfig);
1922 my $query = qq|SELECT template_code FROM language WHERE id = ?|;
1923 ($template_code) = selectrow_query($self, $dbh, $query, $self->{language_id});
1926 $main::lxdebug->leave_sub();
1928 return $template_code;
1931 sub get_printer_code {
1932 $main::lxdebug->enter_sub();
1934 my ($self, $myconfig) = @_;
1936 my $template_code = "";
1938 if ($self->{printer_id}) {
1939 my $dbh = $self->get_standard_dbh($myconfig);
1940 my $query = qq|SELECT template_code, printer_command FROM printers WHERE id = ?|;
1941 ($template_code, $self->{printer_command}) = selectrow_query($self, $dbh, $query, $self->{printer_id});
1944 $main::lxdebug->leave_sub();
1946 return $template_code;
1950 $main::lxdebug->enter_sub();
1952 my ($self, $myconfig) = @_;
1954 my $template_code = "";
1956 if ($self->{shipto_id}) {
1957 my $dbh = $self->get_standard_dbh($myconfig);
1958 my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
1959 my $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{shipto_id});
1960 map({ $self->{$_} = $ref->{$_} } keys(%$ref));
1963 $main::lxdebug->leave_sub();
1967 $main::lxdebug->enter_sub();
1969 my ($self, $dbh, $id, $module) = @_;
1974 foreach my $item (qw(name department_1 department_2 street zipcode city country
1975 contact cp_gender phone fax email)) {
1976 if ($self->{"shipto$item"}) {
1977 $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
1979 push(@values, $self->{"shipto${item}"});
1983 if ($self->{shipto_id}) {
1984 my $query = qq|UPDATE shipto set
1986 shiptodepartment_1 = ?,
1987 shiptodepartment_2 = ?,
1993 shiptocp_gender = ?,
1997 WHERE shipto_id = ?|;
1998 do_query($self, $dbh, $query, @values, $self->{shipto_id});
2000 my $query = qq|SELECT * FROM shipto
2001 WHERE shiptoname = ? AND
2002 shiptodepartment_1 = ? AND
2003 shiptodepartment_2 = ? AND
2004 shiptostreet = ? AND
2005 shiptozipcode = ? AND
2007 shiptocountry = ? AND
2008 shiptocontact = ? AND
2009 shiptocp_gender = ? AND
2015 my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values, $module, $id);
2018 qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
2019 shiptostreet, shiptozipcode, shiptocity, shiptocountry,
2020 shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module)
2021 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
2022 do_query($self, $dbh, $query, $id, @values, $module);
2027 $main::lxdebug->leave_sub();
2031 $main::lxdebug->enter_sub();
2033 my ($self, $dbh) = @_;
2035 $dbh ||= $self->get_standard_dbh(\%main::myconfig);
2037 my $query = qq|SELECT id, name FROM employee WHERE login = ?|;
2038 ($self->{"employee_id"}, $self->{"employee"}) = selectrow_query($self, $dbh, $query, $self->{login});
2039 $self->{"employee_id"} *= 1;
2041 $main::lxdebug->leave_sub();
2044 sub get_employee_data {
2045 $main::lxdebug->enter_sub();
2050 Common::check_params(\%params, qw(prefix));
2051 Common::check_params_x(\%params, qw(id));
2054 $main::lxdebug->leave_sub();
2058 my $myconfig = \%main::myconfig;
2059 my $dbh = $params{dbh} || $self->get_standard_dbh($myconfig);
2061 my ($login) = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
2064 my $user = User->new($login);
2065 map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
2067 $self->{$params{prefix} . '_login'} = $login;
2068 $self->{$params{prefix} . '_name'} ||= $login;
2071 $main::lxdebug->leave_sub();
2075 $main::lxdebug->enter_sub();
2077 my ($self, $myconfig, $reference_date) = @_;
2079 $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
2081 my $dbh = $self->get_standard_dbh($myconfig);
2082 my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
2083 my ($duedate) = selectrow_query($self, $dbh, $query, $self->{payment_id});
2085 $main::lxdebug->leave_sub();
2091 $main::lxdebug->enter_sub();
2093 my ($self, $dbh, $id, $key) = @_;
2095 $key = "all_contacts" unless ($key);
2099 $main::lxdebug->leave_sub();
2104 qq|SELECT cp_id, cp_cv_id, cp_name, cp_givenname, cp_abteilung | .
2105 qq|FROM contacts | .
2106 qq|WHERE cp_cv_id = ? | .
2107 qq|ORDER BY lower(cp_name)|;
2109 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $id);
2111 $main::lxdebug->leave_sub();
2115 $main::lxdebug->enter_sub();
2117 my ($self, $dbh, $key) = @_;
2119 my ($all, $old_id, $where, @values);
2121 if (ref($key) eq "HASH") {
2124 $key = "ALL_PROJECTS";
2126 foreach my $p (keys(%{$params})) {
2128 $all = $params->{$p};
2129 } elsif ($p eq "old_id") {
2130 $old_id = $params->{$p};
2131 } elsif ($p eq "key") {
2132 $key = $params->{$p};
2138 $where = "WHERE active ";
2140 if (ref($old_id) eq "ARRAY") {
2141 my @ids = grep({ $_ } @{$old_id});
2143 $where .= " OR id IN (" . join(",", map({ "?" } @ids)) . ") ";
2144 push(@values, @ids);
2147 $where .= " OR (id = ?) ";
2148 push(@values, $old_id);
2154 qq|SELECT id, projectnumber, description, active | .
2157 qq|ORDER BY lower(projectnumber)|;
2159 $self->{$key} = selectall_hashref_query($self, $dbh, $query, @values);
2161 $main::lxdebug->leave_sub();
2165 $main::lxdebug->enter_sub();
2167 my ($self, $dbh, $vc_id, $key) = @_;
2169 $key = "all_shipto" unless ($key);
2172 # get shipping addresses
2173 my $query = qq|SELECT * FROM shipto WHERE trans_id = ?|;
2175 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id);
2181 $main::lxdebug->leave_sub();
2185 $main::lxdebug->enter_sub();
2187 my ($self, $dbh, $key) = @_;
2189 $key = "all_printers" unless ($key);
2191 my $query = qq|SELECT id, printer_description, printer_command, template_code FROM printers|;
2193 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2195 $main::lxdebug->leave_sub();
2199 $main::lxdebug->enter_sub();
2201 my ($self, $dbh, $params) = @_;
2204 $key = $params->{key};
2205 $key = "all_charts" unless ($key);
2207 my $transdate = quote_db_date($params->{transdate});
2210 qq|SELECT c.id, c.accno, c.description, c.link, c.charttype, tk.taxkey_id, tk.tax_id | .
2212 qq|LEFT JOIN taxkeys tk ON | .
2213 qq|(tk.id = (SELECT id FROM taxkeys | .
2214 qq| WHERE taxkeys.chart_id = c.id AND startdate <= $transdate | .
2215 qq| ORDER BY startdate DESC LIMIT 1)) | .
2216 qq|ORDER BY c.accno|;
2218 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2220 $main::lxdebug->leave_sub();
2223 sub _get_taxcharts {
2224 $main::lxdebug->enter_sub();
2226 my ($self, $dbh, $params) = @_;
2228 my $key = "all_taxcharts";
2231 if (ref $params eq 'HASH') {
2232 $key = $params->{key} if ($params->{key});
2233 if ($params->{module} eq 'AR') {
2234 push @where, 'taxkey NOT IN (8, 9, 18, 19)';
2236 } elsif ($params->{module} eq 'AP') {
2237 push @where, 'taxkey NOT IN (1, 2, 3, 12, 13)';
2244 my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
2246 my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
2248 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2250 $main::lxdebug->leave_sub();
2254 $main::lxdebug->enter_sub();
2256 my ($self, $dbh, $key) = @_;
2258 $key = "all_taxzones" unless ($key);
2260 my $query = qq|SELECT * FROM tax_zones ORDER BY id|;
2262 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2264 $main::lxdebug->leave_sub();
2267 sub _get_employees {
2268 $main::lxdebug->enter_sub();
2270 my ($self, $dbh, $default_key, $key) = @_;
2272 $key = $default_key unless ($key);
2273 $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee ORDER BY lower(name)|);
2275 $main::lxdebug->leave_sub();
2278 sub _get_business_types {
2279 $main::lxdebug->enter_sub();
2281 my ($self, $dbh, $key) = @_;
2283 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2284 $options->{key} ||= "all_business_types";
2287 if (exists $options->{salesman}) {
2288 $where = 'WHERE ' . ($options->{salesman} ? '' : 'NOT ') . 'COALESCE(salesman)';
2291 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, qq|SELECT * FROM business $where ORDER BY lower(description)|);
2293 $main::lxdebug->leave_sub();
2296 sub _get_languages {
2297 $main::lxdebug->enter_sub();
2299 my ($self, $dbh, $key) = @_;
2301 $key = "all_languages" unless ($key);
2303 my $query = qq|SELECT * FROM language ORDER BY id|;
2305 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2307 $main::lxdebug->leave_sub();
2310 sub _get_dunning_configs {
2311 $main::lxdebug->enter_sub();
2313 my ($self, $dbh, $key) = @_;
2315 $key = "all_dunning_configs" unless ($key);
2317 my $query = qq|SELECT * FROM dunning_config ORDER BY dunning_level|;
2319 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2321 $main::lxdebug->leave_sub();
2324 sub _get_currencies {
2325 $main::lxdebug->enter_sub();
2327 my ($self, $dbh, $key) = @_;
2329 $key = "all_currencies" unless ($key);
2331 my $query = qq|SELECT curr AS currency FROM defaults|;
2333 $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
2335 $main::lxdebug->leave_sub();
2339 $main::lxdebug->enter_sub();
2341 my ($self, $dbh, $key) = @_;
2343 $key = "all_payments" unless ($key);
2345 my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
2347 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2349 $main::lxdebug->leave_sub();
2352 sub _get_customers {
2353 $main::lxdebug->enter_sub();
2355 my ($self, $dbh, $key) = @_;
2357 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2358 $options->{key} ||= "all_customers";
2359 my $limit_clause = "LIMIT $options->{limit}" if $options->{limit};
2362 push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if $options->{business_is_salesman};
2363 push @where, qq|NOT obsolete| if !$options->{with_obsolete};
2364 my $where_str = @where ? "WHERE " . join(" AND ", map { "($_)" } @where) : '';
2366 my $query = qq|SELECT * FROM customer $where_str ORDER BY name $limit_clause|;
2367 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, $query);
2369 $main::lxdebug->leave_sub();
2373 $main::lxdebug->enter_sub();
2375 my ($self, $dbh, $key) = @_;
2377 $key = "all_vendors" unless ($key);
2379 my $query = qq|SELECT * FROM vendor WHERE NOT obsolete ORDER BY name|;
2381 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2383 $main::lxdebug->leave_sub();
2386 sub _get_departments {
2387 $main::lxdebug->enter_sub();
2389 my ($self, $dbh, $key) = @_;
2391 $key = "all_departments" unless ($key);
2393 my $query = qq|SELECT * FROM department ORDER BY description|;
2395 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2397 $main::lxdebug->leave_sub();
2400 sub _get_warehouses {
2401 $main::lxdebug->enter_sub();
2403 my ($self, $dbh, $param) = @_;
2405 my ($key, $bins_key);
2407 if ('' eq ref $param) {
2411 $key = $param->{key};
2412 $bins_key = $param->{bins};
2415 my $query = qq|SELECT w.* FROM warehouse w
2416 WHERE (NOT w.invalid) AND
2417 ((SELECT COUNT(b.*) FROM bin b WHERE b.warehouse_id = w.id) > 0)
2418 ORDER BY w.sortkey|;
2420 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2423 $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?|;
2424 my $sth = prepare_query($self, $dbh, $query);
2426 foreach my $warehouse (@{ $self->{$key} }) {
2427 do_statement($self, $sth, $query, $warehouse->{id});
2428 $warehouse->{$bins_key} = [];
2430 while (my $ref = $sth->fetchrow_hashref()) {
2431 push @{ $warehouse->{$bins_key} }, $ref;
2437 $main::lxdebug->leave_sub();
2441 $main::lxdebug->enter_sub();
2443 my ($self, $dbh, $table, $key, $sortkey) = @_;
2445 my $query = qq|SELECT * FROM $table|;
2446 $query .= qq| ORDER BY $sortkey| if ($sortkey);
2448 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2450 $main::lxdebug->leave_sub();
2454 # $main::lxdebug->enter_sub();
2456 # my ($self, $dbh, $key) = @_;
2458 # $key ||= "all_groups";
2460 # my $groups = $main::auth->read_groups();
2462 # $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2464 # $main::lxdebug->leave_sub();
2468 $main::lxdebug->enter_sub();
2473 my $dbh = $self->get_standard_dbh(\%main::myconfig);
2474 my ($sth, $query, $ref);
2476 my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
2477 my $vc_id = $self->{"${vc}_id"};
2479 if ($params{"contacts"}) {
2480 $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
2483 if ($params{"shipto"}) {
2484 $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
2487 if ($params{"projects"} || $params{"all_projects"}) {
2488 $self->_get_projects($dbh, $params{"all_projects"} ?
2489 $params{"all_projects"} : $params{"projects"},
2490 $params{"all_projects"} ? 1 : 0);
2493 if ($params{"printers"}) {
2494 $self->_get_printers($dbh, $params{"printers"});
2497 if ($params{"languages"}) {
2498 $self->_get_languages($dbh, $params{"languages"});
2501 if ($params{"charts"}) {
2502 $self->_get_charts($dbh, $params{"charts"});
2505 if ($params{"taxcharts"}) {
2506 $self->_get_taxcharts($dbh, $params{"taxcharts"});
2509 if ($params{"taxzones"}) {
2510 $self->_get_taxzones($dbh, $params{"taxzones"});
2513 if ($params{"employees"}) {
2514 $self->_get_employees($dbh, "all_employees", $params{"employees"});
2517 if ($params{"salesmen"}) {
2518 $self->_get_employees($dbh, "all_salesmen", $params{"salesmen"});
2521 if ($params{"business_types"}) {
2522 $self->_get_business_types($dbh, $params{"business_types"});
2525 if ($params{"dunning_configs"}) {
2526 $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
2529 if($params{"currencies"}) {
2530 $self->_get_currencies($dbh, $params{"currencies"});
2533 if($params{"customers"}) {
2534 $self->_get_customers($dbh, $params{"customers"});
2537 if($params{"vendors"}) {
2538 if (ref $params{"vendors"} eq 'HASH') {
2539 $self->_get_vendors($dbh, $params{"vendors"}{key}, $params{"vendors"}{limit});
2541 $self->_get_vendors($dbh, $params{"vendors"});
2545 if($params{"payments"}) {
2546 $self->_get_payments($dbh, $params{"payments"});
2549 if($params{"departments"}) {
2550 $self->_get_departments($dbh, $params{"departments"});
2553 if ($params{price_factors}) {
2554 $self->_get_simple($dbh, 'price_factors', $params{price_factors}, 'sortkey');
2557 if ($params{warehouses}) {
2558 $self->_get_warehouses($dbh, $params{warehouses});
2561 # if ($params{groups}) {
2562 # $self->_get_groups($dbh, $params{groups});
2565 if ($params{partsgroup}) {
2566 $self->get_partsgroup(\%main::myconfig, { all => 1, target => $params{partsgroup} });
2569 $main::lxdebug->leave_sub();
2572 # this sub gets the id and name from $table
2574 $main::lxdebug->enter_sub();
2576 my ($self, $myconfig, $table) = @_;
2578 # connect to database
2579 my $dbh = $self->get_standard_dbh($myconfig);
2581 $table = $table eq "customer" ? "customer" : "vendor";
2582 my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
2584 my ($query, @values);
2586 if (!$self->{openinvoices}) {
2588 if ($self->{customernumber} ne "") {
2589 $where = qq|(vc.customernumber ILIKE ?)|;
2590 push(@values, '%' . $self->{customernumber} . '%');
2592 $where = qq|(vc.name ILIKE ?)|;
2593 push(@values, '%' . $self->{$table} . '%');
2597 qq~SELECT vc.id, vc.name,
2598 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2600 WHERE $where AND (NOT vc.obsolete)
2604 qq~SELECT DISTINCT vc.id, vc.name,
2605 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2607 JOIN $table vc ON (a.${table}_id = vc.id)
2608 WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
2610 push(@values, '%' . $self->{$table} . '%');
2613 $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
2615 $main::lxdebug->leave_sub();
2617 return scalar(@{ $self->{name_list} });
2620 # the selection sub is used in the AR, AP, IS, IR and OE module
2623 $main::lxdebug->enter_sub();
2625 my ($self, $myconfig, $table, $module) = @_;
2628 my $dbh = $self->get_standard_dbh;
2630 $table = $table eq "customer" ? "customer" : "vendor";
2632 my $query = qq|SELECT count(*) FROM $table|;
2633 my ($count) = selectrow_query($self, $dbh, $query);
2635 # build selection list
2636 if ($count <= $myconfig->{vclimit}) {
2637 $query = qq|SELECT id, name, salesman_id
2638 FROM $table WHERE NOT obsolete
2640 $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
2644 $self->get_employee($dbh);
2646 # setup sales contacts
2647 $query = qq|SELECT e.id, e.name
2649 WHERE (e.sales = '1') AND (NOT e.id = ?)|;
2650 $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
2653 push(@{ $self->{all_employees} },
2654 { id => $self->{employee_id},
2655 name => $self->{employee} });
2657 # sort the whole thing
2658 @{ $self->{all_employees} } =
2659 sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
2661 if ($module eq 'AR') {
2663 # prepare query for departments
2664 $query = qq|SELECT id, description
2667 ORDER BY description|;
2670 $query = qq|SELECT id, description
2672 ORDER BY description|;
2675 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2678 $query = qq|SELECT id, description
2682 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2685 $query = qq|SELECT printer_description, id
2687 ORDER BY printer_description|;
2689 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2692 $query = qq|SELECT id, description
2696 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2698 $main::lxdebug->leave_sub();
2701 sub language_payment {
2702 $main::lxdebug->enter_sub();
2704 my ($self, $myconfig) = @_;
2706 my $dbh = $self->get_standard_dbh($myconfig);
2708 my $query = qq|SELECT id, description
2712 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2715 $query = qq|SELECT printer_description, id
2717 ORDER BY printer_description|;
2719 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2722 $query = qq|SELECT id, description
2726 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2728 # get buchungsgruppen
2729 $query = qq|SELECT id, description
2730 FROM buchungsgruppen|;
2732 $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
2734 $main::lxdebug->leave_sub();
2737 # this is only used for reports
2738 sub all_departments {
2739 $main::lxdebug->enter_sub();
2741 my ($self, $myconfig, $table) = @_;
2743 my $dbh = $self->get_standard_dbh($myconfig);
2746 if ($table eq 'customer') {
2747 $where = "WHERE role = 'P' ";
2750 my $query = qq|SELECT id, description
2753 ORDER BY description|;
2754 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2756 delete($self->{all_departments}) unless (@{ $self->{all_departments} || [] });
2758 $main::lxdebug->leave_sub();
2762 $main::lxdebug->enter_sub();
2764 my ($self, $module, $myconfig, $table, $provided_dbh) = @_;
2767 if ($table eq "customer") {
2776 $self->all_vc($myconfig, $table, $module);
2778 # get last customers or vendors
2779 my ($query, $sth, $ref);
2781 my $dbh = $provided_dbh ? $provided_dbh : $self->get_standard_dbh($myconfig);
2786 my $transdate = "current_date";
2787 if ($self->{transdate}) {
2788 $transdate = $dbh->quote($self->{transdate});
2791 # now get the account numbers
2792 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2793 FROM chart c, taxkeys tk
2794 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
2795 (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
2798 $sth = $dbh->prepare($query);
2800 do_statement($self, $sth, $query, '%' . $module . '%');
2802 $self->{accounts} = "";
2803 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2805 foreach my $key (split(/:/, $ref->{link})) {
2806 if ($key =~ /\Q$module\E/) {
2808 # cross reference for keys
2809 $xkeyref{ $ref->{accno} } = $key;
2811 push @{ $self->{"${module}_links"}{$key} },
2812 { accno => $ref->{accno},
2813 description => $ref->{description},
2814 taxkey => $ref->{taxkey_id},
2815 tax_id => $ref->{tax_id} };
2817 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2823 # get taxkeys and description
2824 $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
2825 $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
2827 if (($module eq "AP") || ($module eq "AR")) {
2828 # get tax rates and description
2829 $query = qq|SELECT * FROM tax|;
2830 $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
2836 a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
2837 a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
2838 a.intnotes, a.department_id, a.amount AS oldinvtotal,
2839 a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
2841 d.description AS department,
2844 JOIN $table c ON (a.${table}_id = c.id)
2845 LEFT JOIN employee e ON (e.id = a.employee_id)
2846 LEFT JOIN department d ON (d.id = a.department_id)
2848 $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2850 foreach my $key (keys %$ref) {
2851 $self->{$key} = $ref->{$key};
2854 my $transdate = "current_date";
2855 if ($self->{transdate}) {
2856 $transdate = $dbh->quote($self->{transdate});
2859 # now get the account numbers
2860 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2862 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
2864 AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
2865 OR c.link LIKE '%_tax%' OR c.taxkey_id IS NULL)
2868 $sth = $dbh->prepare($query);
2869 do_statement($self, $sth, $query, "%$module%");
2871 $self->{accounts} = "";
2872 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2874 foreach my $key (split(/:/, $ref->{link})) {
2875 if ($key =~ /\Q$module\E/) {
2877 # cross reference for keys
2878 $xkeyref{ $ref->{accno} } = $key;
2880 push @{ $self->{"${module}_links"}{$key} },
2881 { accno => $ref->{accno},
2882 description => $ref->{description},
2883 taxkey => $ref->{taxkey_id},
2884 tax_id => $ref->{tax_id} };
2886 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2892 # get amounts from individual entries
2895 c.accno, c.description,
2896 a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
2900 LEFT JOIN chart c ON (c.id = a.chart_id)
2901 LEFT JOIN project p ON (p.id = a.project_id)
2902 LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
2903 WHERE (tk.taxkey_id=a.taxkey) AND
2904 ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
2905 THEN tk.chart_id = a.chart_id
2908 OR (c.link='%tax%')) AND
2909 (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
2910 WHERE a.trans_id = ?
2911 AND a.fx_transaction = '0'
2912 ORDER BY a.acc_trans_id, a.transdate|;
2913 $sth = $dbh->prepare($query);
2914 do_statement($self, $sth, $query, $self->{id});
2916 # get exchangerate for currency
2917 $self->{exchangerate} =
2918 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2921 # store amounts in {acc_trans}{$key} for multiple accounts
2922 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
2923 $ref->{exchangerate} =
2924 $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
2925 if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
2928 if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
2929 $ref->{amount} *= -1;
2931 $ref->{index} = $index;
2933 push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
2939 d.curr AS currencies, d.closedto, d.revtrans,
2940 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2941 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2943 $ref = selectfirst_hashref_query($self, $dbh, $query);
2944 map { $self->{$_} = $ref->{$_} } keys %$ref;
2951 current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
2952 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2953 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2955 $ref = selectfirst_hashref_query($self, $dbh, $query);
2956 map { $self->{$_} = $ref->{$_} } keys %$ref;
2958 if ($self->{"$self->{vc}_id"}) {
2960 # only setup currency
2961 ($self->{currency}) = split(/:/, $self->{currencies});
2965 $self->lastname_used($dbh, $myconfig, $table, $module);
2967 # get exchangerate for currency
2968 $self->{exchangerate} =
2969 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2975 $main::lxdebug->leave_sub();
2979 $main::lxdebug->enter_sub();
2981 my ($self, $dbh, $myconfig, $table, $module) = @_;
2985 $table = $table eq "customer" ? "customer" : "vendor";
2986 my %column_map = ("a.curr" => "currency",
2987 "a.${table}_id" => "${table}_id",
2988 "a.department_id" => "department_id",
2989 "d.description" => "department",
2990 "ct.name" => $table,
2991 "current_date + ct.terms" => "duedate",
2994 if ($self->{type} =~ /delivery_order/) {
2995 $arap = 'delivery_orders';
2996 delete $column_map{"a.curr"};
2998 } elsif ($self->{type} =~ /_order/) {
3000 $where = "quotation = '0'";
3002 } elsif ($self->{type} =~ /_quotation/) {
3004 $where = "quotation = '1'";
3006 } elsif ($table eq 'customer') {
3014 $where = "($where) AND" if ($where);
3015 my $query = qq|SELECT MAX(id) FROM $arap
3016 WHERE $where ${table}_id > 0|;
3017 my ($trans_id) = selectrow_query($self, $dbh, $query);
3020 my $column_spec = join(', ', map { "${_} AS $column_map{$_}" } keys %column_map);
3021 $query = qq|SELECT $column_spec
3023 LEFT JOIN $table ct ON (a.${table}_id = ct.id)
3024 LEFT JOIN department d ON (a.department_id = d.id)
3026 my $ref = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
3028 map { $self->{$_} = $ref->{$_} } values %column_map;
3030 $main::lxdebug->leave_sub();
3034 $main::lxdebug->enter_sub();
3037 my $myconfig = shift || \%::myconfig;
3038 my ($thisdate, $days) = @_;
3040 my $dbh = $self->get_standard_dbh($myconfig);
3045 my $dateformat = $myconfig->{dateformat};
3046 $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
3047 $thisdate = $dbh->quote($thisdate);
3048 $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
3050 $query = qq|SELECT current_date AS thisdate|;
3053 ($thisdate) = selectrow_query($self, $dbh, $query);
3055 $main::lxdebug->leave_sub();
3061 $main::lxdebug->enter_sub();
3063 my ($self, $string) = @_;
3065 if ($string !~ /%/) {
3066 $string = "%$string%";
3069 $string =~ s/\'/\'\'/g;
3071 $main::lxdebug->leave_sub();
3077 $main::lxdebug->enter_sub();
3079 my ($self, $flds, $new, $count, $numrows) = @_;
3083 map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } } 1 .. $count;
3088 foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
3090 my $j = $item->{ndx} - 1;
3091 map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
3095 for $i ($count + 1 .. $numrows) {
3096 map { delete $self->{"${_}_$i"} } @{$flds};
3099 $main::lxdebug->leave_sub();
3103 $main::lxdebug->enter_sub();
3105 my ($self, $myconfig) = @_;
3109 my $dbh = $self->dbconnect_noauto($myconfig);
3111 my $query = qq|DELETE FROM status
3112 WHERE (formname = ?) AND (trans_id = ?)|;
3113 my $sth = prepare_query($self, $dbh, $query);
3115 if ($self->{formname} =~ /(check|receipt)/) {
3116 for $i (1 .. $self->{rowcount}) {
3117 do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
3120 do_statement($self, $sth, $query, $self->{formname}, $self->{id});
3124 my $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3125 my $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3127 my %queued = split / /, $self->{queued};
3130 if ($self->{formname} =~ /(check|receipt)/) {
3132 # this is a check or receipt, add one entry for each lineitem
3133 my ($accno) = split /--/, $self->{account};
3134 $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
3135 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
3136 @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
3137 $sth = prepare_query($self, $dbh, $query);
3139 for $i (1 .. $self->{rowcount}) {
3140 if ($self->{"checked_$i"}) {
3141 do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
3147 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3148 VALUES (?, ?, ?, ?, ?)|;
3149 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
3150 $queued{$self->{formname}}, $self->{formname});
3156 $main::lxdebug->leave_sub();
3160 $main::lxdebug->enter_sub();
3162 my ($self, $dbh) = @_;
3164 my ($query, $printed, $emailed);
3166 my $formnames = $self->{printed};
3167 my $emailforms = $self->{emailed};
3169 $query = qq|DELETE FROM status
3170 WHERE (formname = ?) AND (trans_id = ?)|;
3171 do_query($self, $dbh, $query, $self->{formname}, $self->{id});
3173 # this only applies to the forms
3174 # checks and receipts are posted when printed or queued
3176 if ($self->{queued}) {
3177 my %queued = split / /, $self->{queued};
3179 foreach my $formname (keys %queued) {
3180 $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3181 $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3183 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3184 VALUES (?, ?, ?, ?, ?)|;
3185 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
3187 $formnames =~ s/\Q$self->{formname}\E//;
3188 $emailforms =~ s/\Q$self->{formname}\E//;
3193 # save printed, emailed info
3194 $formnames =~ s/^ +//g;
3195 $emailforms =~ s/^ +//g;
3198 map { $status{$_}{printed} = 1 } split / +/, $formnames;
3199 map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
3201 foreach my $formname (keys %status) {
3202 $printed = ($formnames =~ /\Q$self->{formname}\E/) ? "1" : "0";
3203 $emailed = ($emailforms =~ /\Q$self->{formname}\E/) ? "1" : "0";
3205 $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
3206 VALUES (?, ?, ?, ?)|;
3207 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
3210 $main::lxdebug->leave_sub();
3214 # $main::locale->text('SAVED')
3215 # $main::locale->text('DELETED')
3216 # $main::locale->text('ADDED')
3217 # $main::locale->text('PAYMENT POSTED')
3218 # $main::locale->text('POSTED')
3219 # $main::locale->text('POSTED AS NEW')
3220 # $main::locale->text('ELSE')
3221 # $main::locale->text('SAVED FOR DUNNING')
3222 # $main::locale->text('DUNNING STARTED')
3223 # $main::locale->text('PRINTED')
3224 # $main::locale->text('MAILED')
3225 # $main::locale->text('SCREENED')
3226 # $main::locale->text('CANCELED')
3227 # $main::locale->text('invoice')
3228 # $main::locale->text('proforma')
3229 # $main::locale->text('sales_order')
3230 # $main::locale->text('pick_list')
3231 # $main::locale->text('purchase_order')
3232 # $main::locale->text('bin_list')
3233 # $main::locale->text('sales_quotation')
3234 # $main::locale->text('request_quotation')
3237 $main::lxdebug->enter_sub();
3240 my $dbh = shift || $self->get_standard_dbh;
3242 if(!exists $self->{employee_id}) {
3243 &get_employee($self, $dbh);
3247 qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
3248 qq|VALUES (?, (SELECT id FROM employee WHERE login = ?), ?, ?, ?)|;
3249 my @values = (conv_i($self->{id}), $self->{login},
3250 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
3251 do_query($self, $dbh, $query, @values);
3255 $main::lxdebug->leave_sub();
3259 $main::lxdebug->enter_sub();
3261 my ($self, $dbh, $trans_id, $restriction, $order) = @_;
3262 my ($orderBy, $desc) = split(/\-\-/, $order);
3263 $order = " ORDER BY " . ($order eq "" ? " h.itime " : ($desc == 1 ? $orderBy . " DESC " : $orderBy . " "));
3266 if ($trans_id ne "") {
3268 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 | .
3269 qq|FROM history_erp h | .
3270 qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
3271 qq|WHERE (trans_id = | . $trans_id . qq|) $restriction | .
3274 my $sth = $dbh->prepare($query) || $self->dberror($query);
3276 $sth->execute() || $self->dberror("$query");
3278 while(my $hash_ref = $sth->fetchrow_hashref()) {
3279 $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
3280 $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
3281 $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
3282 $tempArray[$i++] = $hash_ref;
3284 $main::lxdebug->leave_sub() and return \@tempArray
3285 if ($i > 0 && $tempArray[0] ne "");
3287 $main::lxdebug->leave_sub();
3291 sub update_defaults {
3292 $main::lxdebug->enter_sub();
3294 my ($self, $myconfig, $fld, $provided_dbh) = @_;
3297 if ($provided_dbh) {
3298 $dbh = $provided_dbh;
3300 $dbh = $self->dbconnect_noauto($myconfig);
3302 my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
3303 my $sth = $dbh->prepare($query);
3305 $sth->execute || $self->dberror($query);
3306 my ($var) = $sth->fetchrow_array;
3309 if ($var =~ m/\d+$/) {
3310 my $new_var = (substr $var, $-[0]) * 1 + 1;
3311 my $len_diff = length($var) - $-[0] - length($new_var);
3312 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3318 $query = qq|UPDATE defaults SET $fld = ?|;
3319 do_query($self, $dbh, $query, $var);
3321 if (!$provided_dbh) {
3326 $main::lxdebug->leave_sub();
3331 sub update_business {
3332 $main::lxdebug->enter_sub();
3334 my ($self, $myconfig, $business_id, $provided_dbh) = @_;
3337 if ($provided_dbh) {
3338 $dbh = $provided_dbh;
3340 $dbh = $self->dbconnect_noauto($myconfig);
3343 qq|SELECT customernumberinit FROM business
3344 WHERE id = ? FOR UPDATE|;
3345 my ($var) = selectrow_query($self, $dbh, $query, $business_id);
3347 return undef unless $var;
3349 if ($var =~ m/\d+$/) {
3350 my $new_var = (substr $var, $-[0]) * 1 + 1;
3351 my $len_diff = length($var) - $-[0] - length($new_var);
3352 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3358 $query = qq|UPDATE business
3359 SET customernumberinit = ?
3361 do_query($self, $dbh, $query, $var, $business_id);
3363 if (!$provided_dbh) {
3368 $main::lxdebug->leave_sub();
3373 sub get_partsgroup {
3374 $main::lxdebug->enter_sub();
3376 my ($self, $myconfig, $p) = @_;
3377 my $target = $p->{target} || 'all_partsgroup';
3379 my $dbh = $self->get_standard_dbh($myconfig);
3381 my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
3383 JOIN parts p ON (p.partsgroup_id = pg.id) |;
3386 if ($p->{searchitems} eq 'part') {
3387 $query .= qq|WHERE p.inventory_accno_id > 0|;
3389 if ($p->{searchitems} eq 'service') {
3390 $query .= qq|WHERE p.inventory_accno_id IS NULL|;
3392 if ($p->{searchitems} eq 'assembly') {
3393 $query .= qq|WHERE p.assembly = '1'|;
3395 if ($p->{searchitems} eq 'labor') {
3396 $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
3399 $query .= qq|ORDER BY partsgroup|;
3402 $query = qq|SELECT id, partsgroup FROM partsgroup
3403 ORDER BY partsgroup|;
3406 if ($p->{language_code}) {
3407 $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
3408 t.description AS translation
3410 JOIN parts p ON (p.partsgroup_id = pg.id)
3411 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
3412 ORDER BY translation|;
3413 @values = ($p->{language_code});
3416 $self->{$target} = selectall_hashref_query($self, $dbh, $query, @values);
3418 $main::lxdebug->leave_sub();
3421 sub get_pricegroup {
3422 $main::lxdebug->enter_sub();
3424 my ($self, $myconfig, $p) = @_;
3426 my $dbh = $self->get_standard_dbh($myconfig);
3428 my $query = qq|SELECT p.id, p.pricegroup
3431 $query .= qq| ORDER BY pricegroup|;
3434 $query = qq|SELECT id, pricegroup FROM pricegroup
3435 ORDER BY pricegroup|;
3438 $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
3440 $main::lxdebug->leave_sub();
3444 # usage $form->all_years($myconfig, [$dbh])
3445 # return list of all years where bookings found
3448 $main::lxdebug->enter_sub();
3450 my ($self, $myconfig, $dbh) = @_;
3452 $dbh ||= $self->get_standard_dbh($myconfig);
3455 my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
3456 (SELECT MAX(transdate) FROM acc_trans)|;
3457 my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
3459 if ($myconfig->{dateformat} =~ /^yy/) {
3460 ($startdate) = split /\W/, $startdate;
3461 ($enddate) = split /\W/, $enddate;
3463 (@_) = split /\W/, $startdate;
3465 (@_) = split /\W/, $enddate;
3470 $startdate = substr($startdate,0,4);
3471 $enddate = substr($enddate,0,4);
3473 while ($enddate >= $startdate) {
3474 push @all_years, $enddate--;
3479 $main::lxdebug->leave_sub();
3483 $main::lxdebug->enter_sub();
3487 map { $self->{_VAR_BACKUP}->{$_} = $self->{$_} if exists $self->{$_} } @vars;
3489 $main::lxdebug->leave_sub();
3493 $main::lxdebug->enter_sub();
3498 map { $self->{$_} = $self->{_VAR_BACKUP}->{$_} if exists $self->{_VAR_BACKUP}->{$_} } @vars;
3500 $main::lxdebug->leave_sub();
3509 SL::Form.pm - main data object.
3513 This is the main data object of Lx-Office.
3514 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
3515 Points of interest for a beginner are:
3517 - $form->error - renders a generic error in html. accepts an error message
3518 - $form->get_standard_dbh - returns a database connection for the
3520 =head1 SPECIAL FUNCTIONS
3522 =head2 C<_store_value()>
3524 parses a complex var name, and stores it in the form.
3527 $form->_store_value($key, $value);
3529 keys must start with a string, and can contain various tokens.
3530 supported key structures are:
3533 simple key strings work as expected
3538 separating two keys by a dot (.) will result in a hash lookup for the inner value
3539 this is similar to the behaviour of java and templating mechanisms.
3541 filter.description => $form->{filter}->{description}
3543 3. array+hashref access
3545 adding brackets ([]) before the dot will cause the next hash to be put into an array.
3546 using [+] instead of [] will force a new array index. this is useful for recurring
3547 data structures like part lists. put a [+] into the first varname, and use [] on the
3550 repeating these names in your template:
3553 invoice.items[].parts_id
3557 $form->{invoice}->{items}->[
3571 using brackets at the end of a name will result in a pure array to be created.
3572 note that you mustn't use [+], which is reserved for array+hash access and will
3573 result in undefined behaviour in array context.
3575 filter.status[] => $form->{status}->[ val1, val2, ... ]
3577 =head2 C<update_business> PARAMS
3580 \%config, - config hashref
3581 $business_id, - business id
3582 $dbh - optional database handle
3584 handles business (thats customer/vendor types) sequences.
3586 special behaviour for empty strings in customerinitnumber field:
3587 will in this case not increase the value, and return undef.
3589 =head2 C<redirect_header> $url
3591 Generates a HTTP redirection header for the new C<$url>. Constructs an
3592 absolute URL including scheme, host name and port. If C<$url> is a
3593 relative URL then it is considered relative to Lx-Office base URL.
3595 This function C<die>s if headers have already been created with
3596 C<$::form-E<gt>header>.
3600 print $::form->redirect_header('oe.pl?action=edit&id=1234');
3601 print $::form->redirect_header('http://www.lx-office.org/');
3605 Generates a general purpose http/html header and includes most of the scripts
3606 ans stylesheets needed.
3608 Only one header will be generated. If the method was already called in this
3609 request it will not output anything and return undef. Also if no
3610 HTTP_USER_AGENT is found, no header is generated.
3612 Although header does not accept parameters itself, it will honor special
3613 hashkeys of its Form instance:
3621 If one of these is set, a http-equiv refresh is generated. Missing parameters
3622 default to 3 seconds and the refering url.
3628 If these are arrayrefs the contents will be inlined into the header.
3632 If true, a css snippet will be generated that sets the page in landscape mode.
3636 Used to override the default favicon.
3640 A html page title will be generated from this