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 #======================================================================
66 use List::Util qw(first max min sum);
67 use List::MoreUtils qw(all any apply);
74 disconnect_standard_dbh();
77 sub disconnect_standard_dbh {
78 return unless $standard_dbh;
79 $standard_dbh->disconnect();
84 $main::lxdebug->enter_sub(2);
90 my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
95 $curr = \ $self->{ shift @tokens };
99 my $sep = shift @tokens;
100 my $key = shift @tokens;
102 $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
103 $curr = \ $$curr->[max 0, $#$$curr] if $sep eq '[].';
104 $curr = \ $$curr->[++$#$$curr] if $sep eq '[+].';
105 $curr = \ $$curr->{$key}
110 $main::lxdebug->leave_sub(2);
116 $main::lxdebug->enter_sub(2);
121 my @pairs = split(/&/, $input);
124 my ($key, $value) = split(/=/, $_, 2);
125 $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
128 $main::lxdebug->leave_sub(2);
131 sub _request_to_hash {
132 $main::lxdebug->enter_sub(2);
137 if (!$ENV{'CONTENT_TYPE'}
138 || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
140 $self->_input_to_hash($input);
142 $main::lxdebug->leave_sub(2);
146 my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
148 my $boundary = '--' . $1;
150 foreach my $line (split m/\n/, $input) {
151 last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
153 if (($line eq $boundary) || ($line eq "$boundary\r")) {
154 ${ $previous } =~ s|\r?\n$|| if $previous;
160 $content_type = "text/plain";
167 next unless $boundary_found;
169 if (!$headers_done) {
170 $line =~ s/[\r\n]*$//;
177 if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
178 if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
180 substr $line, $-[0], $+[0] - $-[0], "";
183 if ($line =~ m|name\s*=\s*"(.*?)"|i) {
185 substr $line, $-[0], $+[0] - $-[0], "";
188 $previous = $self->_store_value($name, '') if ($name);
189 $self->{FILENAME} = $filename if ($filename);
194 if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
201 next unless $previous;
203 ${ $previous } .= "${line}\n";
206 ${ $previous } =~ s|\r?\n$|| if $previous;
208 $main::lxdebug->leave_sub(2);
211 sub _recode_recursively {
212 $main::lxdebug->enter_sub();
213 my ($iconv, $param) = @_;
215 if (any { ref $param eq $_ } qw(Form HASH)) {
216 foreach my $key (keys %{ $param }) {
217 if (!ref $param->{$key}) {
218 # Workaround for a bug: converting $param->{$key} directly
219 # leads to 'undef'. I don't know why. Converting a copy works,
221 $param->{$key} = $iconv->convert("" . $param->{$key});
223 _recode_recursively($iconv, $param->{$key});
227 } elsif (ref $param eq 'ARRAY') {
228 foreach my $idx (0 .. scalar(@{ $param }) - 1) {
229 if (!ref $param->[$idx]) {
230 # Workaround for a bug: converting $param->[$idx] directly
231 # leads to 'undef'. I don't know why. Converting a copy works,
233 $param->[$idx] = $iconv->convert("" . $param->[$idx]);
235 _recode_recursively($iconv, $param->[$idx]);
239 $main::lxdebug->leave_sub();
243 $main::lxdebug->enter_sub();
249 if ($LXDebug::watch_form) {
250 require SL::Watchdog;
251 tie %{ $self }, 'SL::Watchdog';
256 $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
257 $self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
259 if ($ENV{CONTENT_LENGTH}) {
261 read STDIN, $content, $ENV{CONTENT_LENGTH};
262 $self->_request_to_hash($content);
265 my $db_charset = $main::dbcharset;
266 $db_charset ||= Common::DEFAULT_CHARSET;
268 my $encoding = $self->{INPUT_ENCODING} || $db_charset;
269 delete $self->{INPUT_ENCODING};
271 _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
273 #$self->{version} = "2.6.1"; # Old hardcoded but secure style
274 open VERSION_FILE, "VERSION"; # New but flexible code reads version from VERSION-file
275 $self->{version} = <VERSION_FILE>;
277 $self->{version} =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
279 $main::lxdebug->leave_sub();
284 sub _flatten_variables_rec {
285 $main::lxdebug->enter_sub(2);
294 if ('' eq ref $curr->{$key}) {
295 @result = ({ 'key' => $prefix . $key, 'value' => $curr->{$key} });
297 } elsif ('HASH' eq ref $curr->{$key}) {
298 foreach my $hash_key (sort keys %{ $curr->{$key} }) {
299 push @result, $self->_flatten_variables_rec($curr->{$key}, $prefix . $key . '.', $hash_key);
303 foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
304 my $first_array_entry = 1;
306 foreach my $hash_key (sort keys %{ $curr->{$key}->[$idx] }) {
307 push @result, $self->_flatten_variables_rec($curr->{$key}->[$idx], $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
308 $first_array_entry = 0;
313 $main::lxdebug->leave_sub(2);
318 sub flatten_variables {
319 $main::lxdebug->enter_sub(2);
327 push @variables, $self->_flatten_variables_rec($self, '', $_);
330 $main::lxdebug->leave_sub(2);
335 sub flatten_standard_variables {
336 $main::lxdebug->enter_sub(2);
339 my %skip_keys = map { $_ => 1 } (qw(login password header stylesheet titlebar version), @_);
343 foreach (grep { ! $skip_keys{$_} } keys %{ $self }) {
344 push @variables, $self->_flatten_variables_rec($self, '', $_);
347 $main::lxdebug->leave_sub(2);
353 $main::lxdebug->enter_sub();
359 map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
361 $main::lxdebug->leave_sub();
365 $main::lxdebug->enter_sub(2);
368 my $password = $self->{password};
370 $self->{password} = 'X' x 8;
372 local $Data::Dumper::Sortkeys = 1;
373 my $output = Dumper($self);
375 $self->{password} = $password;
377 $main::lxdebug->leave_sub(2);
383 $main::lxdebug->enter_sub(2);
385 my ($self, $str) = @_;
387 $str = Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
388 $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
390 $main::lxdebug->leave_sub(2);
396 $main::lxdebug->enter_sub(2);
398 my ($self, $str) = @_;
403 $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
405 $main::lxdebug->leave_sub(2);
411 $main::lxdebug->enter_sub();
412 my ($self, $str) = @_;
414 if ($str && !ref($str)) {
415 $str =~ s/\"/"/g;
418 $main::lxdebug->leave_sub();
424 $main::lxdebug->enter_sub();
425 my ($self, $str) = @_;
427 if ($str && !ref($str)) {
428 $str =~ s/"/\"/g;
431 $main::lxdebug->leave_sub();
437 $main::lxdebug->enter_sub();
441 map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
443 for (sort keys %$self) {
444 next if (($_ eq "header") || (ref($self->{$_}) ne ""));
445 print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
448 $main::lxdebug->leave_sub();
452 $main::lxdebug->enter_sub();
454 $main::lxdebug->show_backtrace();
456 my ($self, $msg) = @_;
457 if ($ENV{HTTP_USER_AGENT}) {
459 $self->show_generic_error($msg);
462 print STDERR "Error: $msg\n";
466 $main::lxdebug->leave_sub();
470 $main::lxdebug->enter_sub();
472 my ($self, $msg) = @_;
474 if ($ENV{HTTP_USER_AGENT}) {
477 if (!$self->{header}) {
483 <p class="message_ok"><b>$msg</b></p>
485 <script type="text/javascript">
487 // If JavaScript is enabled, the whole thing will be reloaded.
488 // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
489 // it now loads the correct code into the browser instead of do nothing.
490 setTimeout("top.frames.location.href='login.pl'",500);
499 if ($self->{info_function}) {
500 &{ $self->{info_function} }($msg);
506 $main::lxdebug->leave_sub();
509 # calculates the number of rows in a textarea based on the content and column number
510 # can be capped with maxrows
512 $main::lxdebug->enter_sub();
513 my ($self, $str, $cols, $maxrows, $minrows) = @_;
517 my $rows = sum map { int((length() - 2) / $cols) + 1 } split /\r/, $str;
520 $main::lxdebug->leave_sub();
522 return max(min($rows, $maxrows), $minrows);
526 $main::lxdebug->enter_sub();
528 my ($self, $msg) = @_;
530 $self->error("$msg\n" . $DBI::errstr);
532 $main::lxdebug->leave_sub();
536 $main::lxdebug->enter_sub();
538 my ($self, $name, $msg) = @_;
541 foreach my $part (split m/\./, $name) {
542 if (!$curr->{$part} || ($curr->{$part} =~ /^\s*$/)) {
545 $curr = $curr->{$part};
548 $main::lxdebug->leave_sub();
551 sub _get_request_uri {
554 return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
556 my $scheme = $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
557 my $port = $ENV{SERVER_PORT} || '';
558 $port = undef if (($scheme eq 'http' ) && ($port == 80))
559 || (($scheme eq 'https') && ($port == 443));
561 my $uri = URI->new("${scheme}://");
562 $uri->scheme($scheme);
564 $uri->host($ENV{HTTP_HOST} || $ENV{SERVER_ADDR});
565 $uri->path_query($ENV{REQUEST_URI});
571 sub _add_to_request_uri {
574 my $relative_new_path = shift;
575 my $request_uri = shift || $self->_get_request_uri;
576 my $relative_new_uri = URI->new($relative_new_path);
577 my @request_segments = $request_uri->path_segments;
579 my $new_uri = $request_uri->clone;
580 $new_uri->path_segments(@request_segments[0..scalar(@request_segments) - 2], $relative_new_uri->path_segments);
585 sub create_http_response {
586 $main::lxdebug->enter_sub();
591 my $cgi = $main::cgi;
592 $cgi ||= CGI->new('');
595 if (defined $main::auth) {
596 my $uri = $self->_get_request_uri;
597 my @segments = $uri->path_segments;
599 $uri->path_segments(@segments);
601 my $session_cookie_value = $main::auth->get_session_id();
603 if ($session_cookie_value) {
604 $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
605 '-value' => $session_cookie_value,
606 '-path' => $uri->path,
607 '-secure' => $ENV{HTTPS});
611 my %cgi_params = ('-type' => $params{content_type});
612 $cgi_params{'-charset'} = $params{charset} if ($params{charset});
613 $cgi_params{'-cookie'} = $session_cookie if ($session_cookie);
615 my $output = $cgi->header(%cgi_params);
617 $main::lxdebug->leave_sub();
624 $::lxdebug->enter_sub;
626 # extra code is currently only used by menuv3 and menuv4 to set their css.
627 # it is strongly deprecated, and will be changed in a future version.
628 my ($self, $extra_code) = @_;
629 my $db_charset = $::dbcharset || Common::DEFAULT_CHARSET;
632 $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
634 $self->{favicon} ||= "favicon.ico";
635 $self->{titlebar} = "$self->{title} - $self->{titlebar}" if $self->{title};
638 if ($self->{refresh_url} || $self->{refresh_time}) {
639 my $refresh_time = $self->{refresh_time} || 3;
640 my $refresh_url = $self->{refresh_url} || $ENV{REFERER};
641 push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
644 push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
645 for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
647 push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
648 push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
649 push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
650 '<script type="text/javascript" src="js/common.js"></script>',
651 '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
652 '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
653 '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
654 '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
655 '<script type="text/javascript" src="js/part_selection.js"></script>';
656 push @header, $self->{javascript} if $self->{javascript};
657 push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
658 push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
659 push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
660 join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
662 # if there is a title, we put some JavaScript in to the page, wich writes a
663 # meaningful title-tag for our frameset.
665 if ($self->{title}) {
667 <script type="text/javascript">
669 // Write a meaningful title-tag for our frameset.
670 top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
676 print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
677 print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
678 if $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
682 <meta http-equiv="Content-Type" content="text/html; charset=$db_charset">
683 <title>$self->{titlebar}</title>
685 print " $_\n" for @header;
687 <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
688 <meta name="robots" content="noindex,nofollow" />
689 <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
690 <script type="text/javascript" src="js/tabcontent.js">
692 /***********************************************
693 * Tab Content script v2.2- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
694 * This notice MUST stay intact for legal use
695 * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
696 ***********************************************/
705 $::lxdebug->leave_sub;
708 sub ajax_response_header {
709 $main::lxdebug->enter_sub();
713 my $db_charset = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
714 my $cgi = $main::cgi || CGI->new('');
715 my $output = $cgi->header('-charset' => $db_charset);
717 $main::lxdebug->leave_sub();
722 sub redirect_header {
726 my $base_uri = $self->_get_request_uri;
727 my $new_uri = URI->new_abs($new_url, $base_uri);
729 die "Headers already sent" if $::self->{header};
732 my $cgi = $main::cgi || CGI->new('');
733 return $cgi->redirect($new_uri);
736 sub set_standard_title {
737 $::lxdebug->enter_sub;
740 $self->{titlebar} = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
741 $self->{titlebar} .= "- $::myconfig{name}" if $::myconfig{name};
742 $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
744 $::lxdebug->leave_sub;
747 sub _prepare_html_template {
748 $main::lxdebug->enter_sub();
750 my ($self, $file, $additional_params) = @_;
753 if (!%::myconfig || !$::myconfig{"countrycode"}) {
754 $language = $main::language;
756 $language = $main::myconfig{"countrycode"};
758 $language = "de" unless ($language);
760 if (-f "templates/webpages/${file}.html") {
761 if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
762 my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
763 "Please re-run 'locales.pl' in 'locale/${language}'.";
764 print(qq|<pre>$info</pre>|);
768 $file = "templates/webpages/${file}.html";
771 my $info = "Web page template '${file}' not found.\n";
772 print qq|<pre>$info</pre>|;
776 if ($self->{"DEBUG"}) {
777 $additional_params->{"DEBUG"} = $self->{"DEBUG"};
780 if ($additional_params->{"DEBUG"}) {
781 $additional_params->{"DEBUG"} =
782 "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
785 if (%main::myconfig) {
786 $::myconfig{jsc_dateformat} = apply {
790 } $::myconfig{"dateformat"};
791 $additional_params->{"myconfig"} ||= \%::myconfig;
792 map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
795 $additional_params->{"conf_dbcharset"} = $::dbcharset;
796 $additional_params->{"conf_webdav"} = $::webdav;
797 $additional_params->{"conf_lizenzen"} = $::lizenzen;
798 $additional_params->{"conf_latex_templates"} = $::latex;
799 $additional_params->{"conf_opendocument_templates"} = $::opendocument_templates;
800 $additional_params->{"conf_vertreter"} = $::vertreter;
801 $additional_params->{"conf_show_best_before"} = $::show_best_before;
802 $additional_params->{"conf_parts_image_css"} = $::parts_image_css;
803 $additional_params->{"conf_parts_listing_images"} = $::parts_listing_images;
804 $additional_params->{"conf_parts_show_image"} = $::parts_show_image;
806 if (%main::debug_options) {
807 map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
810 if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
811 while (my ($key, $value) = each %{ $main::auth->{RIGHTS}->{$self->{login}} }) {
812 $additional_params->{"AUTH_RIGHTS_" . uc($key)} = $value;
816 $main::lxdebug->leave_sub();
821 sub parse_html_template {
822 $main::lxdebug->enter_sub();
824 my ($self, $file, $additional_params) = @_;
826 $additional_params ||= { };
828 my $real_file = $self->_prepare_html_template($file, $additional_params);
829 my $template = $self->template || $self->init_template;
831 map { $additional_params->{$_} ||= $self->{$_} } keys %{ $self };
834 $template->process($real_file, $additional_params, \$output) || die $template->error;
836 $main::lxdebug->leave_sub();
844 return if $self->template;
846 return $self->template(Template->new({
851 'PLUGIN_BASE' => 'SL::Template::Plugin',
852 'INCLUDE_PATH' => '.:templates/webpages',
853 'COMPILE_EXT' => '.tcc',
854 'COMPILE_DIR' => $::userspath . '/templates-cache',
860 $self->{template_object} = shift if @_;
861 return $self->{template_object};
864 sub show_generic_error {
865 $main::lxdebug->enter_sub();
867 my ($self, $error, %params) = @_;
870 'title_error' => $params{title},
871 'label_error' => $error,
874 if ($params{action}) {
877 map { delete($self->{$_}); } qw(action);
878 map { push @vars, { "name" => $_, "value" => $self->{$_} } if (!ref($self->{$_})); } keys %{ $self };
880 $add_params->{SHOW_BUTTON} = 1;
881 $add_params->{BUTTON_LABEL} = $params{label} || $params{action};
882 $add_params->{VARIABLES} = \@vars;
884 } elsif ($params{back_button}) {
885 $add_params->{SHOW_BACK_BUTTON} = 1;
888 $self->{title} = $params{title} if $params{title};
891 print $self->parse_html_template("generic/error", $add_params);
893 print STDERR "Error: $error\n";
895 $main::lxdebug->leave_sub();
900 sub show_generic_information {
901 $main::lxdebug->enter_sub();
903 my ($self, $text, $title) = @_;
906 'title_information' => $title,
907 'label_information' => $text,
910 $self->{title} = $title if ($title);
913 print $self->parse_html_template("generic/information", $add_params);
915 $main::lxdebug->leave_sub();
920 # write Trigger JavaScript-Code ($qty = quantity of Triggers)
921 # changed it to accept an arbitrary number of triggers - sschoeling
923 $main::lxdebug->enter_sub();
926 my $myconfig = shift;
929 # set dateform for jsscript
932 "dd.mm.yy" => "%d.%m.%Y",
933 "dd-mm-yy" => "%d-%m-%Y",
934 "dd/mm/yy" => "%d/%m/%Y",
935 "mm/dd/yy" => "%m/%d/%Y",
936 "mm-dd-yy" => "%m-%d-%Y",
937 "yyyy-mm-dd" => "%Y-%m-%d",
940 my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
941 $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
948 inputField : "| . (shift) . qq|",
949 ifFormat :"$ifFormat",
950 align : "| . (shift) . qq|",
951 button : "| . (shift) . qq|"
957 <script type="text/javascript">
958 <!--| . join("", @triggers) . qq|//-->
962 $main::lxdebug->leave_sub();
965 } #end sub write_trigger
968 $main::lxdebug->enter_sub();
970 my ($self, $msg) = @_;
972 if (!$self->{callback}) {
978 # my ($script, $argv) = split(/\?/, $self->{callback}, 2);
979 # $script =~ s|.*/||;
980 # $script =~ s|[^a-zA-Z0-9_\.]||g;
981 # exec("perl", "$script", $argv);
983 print $::form->redirect_header($self->{callback});
985 $main::lxdebug->leave_sub();
988 # sort of columns removed - empty sub
990 $main::lxdebug->enter_sub();
992 my ($self, @columns) = @_;
994 $main::lxdebug->leave_sub();
1000 $main::lxdebug->enter_sub(2);
1002 my ($self, $myconfig, $amount, $places, $dash) = @_;
1004 if ($amount eq "") {
1008 # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
1010 my $neg = ($amount =~ s/^-//);
1011 my $exp = ($amount =~ m/[e]/) ? 1 : 0;
1013 if (defined($places) && ($places ne '')) {
1019 my ($actual_places) = ($amount =~ /\.(\d+)/);
1020 $actual_places = length($actual_places);
1021 $places = $actual_places > $places ? $actual_places : $places;
1024 $amount = $self->round_amount($amount, $places);
1027 my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
1028 my @p = split(/\./, $amount); # split amount at decimal point
1030 $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
1033 $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
1036 ($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) :
1037 ($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) :
1038 ($neg ? "-$amount" : "$amount" ) ;
1042 $main::lxdebug->leave_sub(2);
1046 sub format_amount_units {
1047 $main::lxdebug->enter_sub();
1052 my $myconfig = \%main::myconfig;
1053 my $amount = $params{amount} * 1;
1054 my $places = $params{places};
1055 my $part_unit_name = $params{part_unit};
1056 my $amount_unit_name = $params{amount_unit};
1057 my $conv_units = $params{conv_units};
1058 my $max_places = $params{max_places};
1060 if (!$part_unit_name) {
1061 $main::lxdebug->leave_sub();
1065 AM->retrieve_all_units();
1066 my $all_units = $main::all_units;
1068 if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
1069 $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
1072 if (!scalar @{ $conv_units }) {
1073 my $result = $self->format_amount($myconfig, $amount, $places, undef, $max_places) . " " . $part_unit_name;
1074 $main::lxdebug->leave_sub();
1078 my $part_unit = $all_units->{$part_unit_name};
1079 my $conv_unit = ($amount_unit_name && ($amount_unit_name ne $part_unit_name)) ? $all_units->{$amount_unit_name} : $part_unit;
1081 $amount *= $conv_unit->{factor};
1086 foreach my $unit (@$conv_units) {
1087 my $last = $unit->{name} eq $part_unit->{name};
1089 $num = int($amount / $unit->{factor});
1090 $amount -= $num * $unit->{factor};
1093 if ($last ? $amount : $num) {
1094 push @values, { "unit" => $unit->{name},
1095 "amount" => $last ? $amount / $unit->{factor} : $num,
1096 "places" => $last ? $places : 0 };
1103 push @values, { "unit" => $part_unit_name,
1108 my $result = join " ", map { $self->format_amount($myconfig, $_->{amount}, $_->{places}, undef, $max_places), $_->{unit} } @values;
1110 $main::lxdebug->leave_sub();
1116 $main::lxdebug->enter_sub(2);
1121 $input =~ s/(^|[^\#]) \# (\d+) /$1$_[$2 - 1]/gx;
1122 $input =~ s/(^|[^\#]) \#\{(\d+)\}/$1$_[$2 - 1]/gx;
1123 $input =~ s/\#\#/\#/g;
1125 $main::lxdebug->leave_sub(2);
1133 $main::lxdebug->enter_sub(2);
1135 my ($self, $myconfig, $amount) = @_;
1137 if ( ($myconfig->{numberformat} eq '1.000,00')
1138 || ($myconfig->{numberformat} eq '1000,00')) {
1143 if ($myconfig->{numberformat} eq "1'000.00") {
1149 $main::lxdebug->leave_sub(2);
1151 return ($amount * 1);
1155 $main::lxdebug->enter_sub(2);
1157 my ($self, $amount, $places) = @_;
1160 # Rounding like "Kaufmannsrunden" (see http://de.wikipedia.org/wiki/Rundung )
1162 # Round amounts to eight places before rounding to the requested
1163 # number of places. This gets rid of errors due to internal floating
1164 # point representation.
1165 $amount = $self->round_amount($amount, 8) if $places < 8;
1166 $amount = $amount * (10**($places));
1167 $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
1169 $main::lxdebug->leave_sub(2);
1171 return $round_amount;
1175 sub parse_template {
1176 $main::lxdebug->enter_sub();
1178 my ($self, $myconfig, $userspath) = @_;
1183 $self->{"cwd"} = getcwd();
1184 $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
1189 if ($self->{"format"} =~ /(opendocument|oasis)/i) {
1190 $template_type = 'OpenDocument';
1191 $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
1193 } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
1194 $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
1195 $template_type = 'LaTeX';
1196 $ext_for_format = 'pdf';
1198 } elsif (($self->{"format"} =~ /html/i) || (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
1199 $template_type = 'HTML';
1200 $ext_for_format = 'html';
1202 } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
1203 $template_type = 'XML';
1204 $ext_for_format = 'xml';
1206 } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
1207 $template_type = 'XML';
1209 } elsif ( $self->{"format"} =~ /excel/i ) {
1210 $template_type = 'Excel';
1211 $ext_for_format = 'xls';
1213 } elsif ( defined $self->{'format'}) {
1214 $self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
1216 } elsif ( $self->{'format'} eq '' ) {
1217 $self->error("No Outputformat given: $self->{'format'}");
1219 } else { #Catch the rest
1220 $self->error("Outputformat not defined: $self->{'format'}");
1223 my $template = SL::Template::create(type => $template_type,
1224 file_name => $self->{IN},
1226 myconfig => $myconfig,
1227 userspath => $userspath);
1229 # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
1230 $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
1232 if (!$self->{employee_id}) {
1233 map { $self->{"employee_${_}"} = $myconfig->{$_}; } qw(email tel fax name signature company address businessnumber co_ustid taxnumber duns);
1236 map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
1238 $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
1240 # OUT is used for the media, screen, printer, email
1241 # for postscript we store a copy in a temporary file
1243 my $prepend_userspath;
1245 if (!$self->{tmpfile}) {
1246 $self->{tmpfile} = "${fileid}.$self->{IN}";
1247 $prepend_userspath = 1;
1250 $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
1252 $self->{tmpfile} =~ s|.*/||;
1253 $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
1254 $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
1256 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1257 $out = $self->{OUT};
1258 $self->{OUT} = ">$self->{tmpfile}";
1264 open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
1265 $result = $template->parse(*OUT);
1270 $result = $template->parse(*STDOUT);
1275 $self->error("$self->{IN} : " . $template->get_error());
1278 if ($self->{media} eq 'file') {
1279 copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
1281 chdir("$self->{cwd}");
1283 $::lxdebug->leave_sub();
1288 if ($template->uses_temp_file() || $self->{media} eq 'email') {
1290 if ($self->{media} eq 'email') {
1292 my $mail = new Mailer;
1294 map { $mail->{$_} = $self->{$_} }
1295 qw(cc bcc subject message version format);
1296 $mail->{charset} = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
1297 $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
1298 $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
1299 $mail->{fileid} = "$fileid.";
1300 $myconfig->{signature} =~ s/\r//g;
1302 # if we send html or plain text inline
1303 if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
1304 $mail->{contenttype} = "text/html";
1306 $mail->{message} =~ s/\r//g;
1307 $mail->{message} =~ s/\n/<br>\n/g;
1308 $myconfig->{signature} =~ s/\n/<br>\n/g;
1309 $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
1311 open(IN, $self->{tmpfile})
1312 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1314 $mail->{message} .= $_;
1321 if (!$self->{"do_not_attach"}) {
1322 my $attachment_name = $self->{attachment_filename} || $self->{tmpfile};
1323 $attachment_name =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
1324 $mail->{attachments} = [{ "filename" => $self->{tmpfile},
1325 "name" => $attachment_name }];
1328 $mail->{message} =~ s/\r//g;
1329 $mail->{message} .= "\n-- \n$myconfig->{signature}";
1333 my $err = $mail->send();
1334 $self->error($self->cleanup . "$err") if ($err);
1338 $self->{OUT} = $out;
1340 my $numbytes = (-s $self->{tmpfile});
1341 open(IN, $self->{tmpfile})
1342 or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1344 $self->{copies} = 1 unless $self->{media} eq 'printer';
1346 chdir("$self->{cwd}");
1347 #print(STDERR "Kopien $self->{copies}\n");
1348 #print(STDERR "OUT $self->{OUT}\n");
1349 for my $i (1 .. $self->{copies}) {
1351 open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
1352 print OUT while <IN>;
1357 $self->{attachment_filename} = ($self->{attachment_filename})
1358 ? $self->{attachment_filename}
1359 : $self->generate_attachment_filename();
1361 # launch application
1362 print qq|Content-Type: | . $template->get_mime_type() . qq|
1363 Content-Disposition: attachment; filename="$self->{attachment_filename}"
1364 Content-Length: $numbytes
1368 $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
1379 chdir("$self->{cwd}");
1380 $main::lxdebug->leave_sub();
1383 sub get_formname_translation {
1384 $main::lxdebug->enter_sub();
1385 my ($self, $formname) = @_;
1387 $formname ||= $self->{formname};
1389 my %formname_translations = (
1390 bin_list => $main::locale->text('Bin List'),
1391 credit_note => $main::locale->text('Credit Note'),
1392 invoice => $main::locale->text('Invoice'),
1393 pick_list => $main::locale->text('Pick List'),
1394 proforma => $main::locale->text('Proforma Invoice'),
1395 purchase_order => $main::locale->text('Purchase Order'),
1396 request_quotation => $main::locale->text('RFQ'),
1397 sales_order => $main::locale->text('Confirmation'),
1398 sales_quotation => $main::locale->text('Quotation'),
1399 storno_invoice => $main::locale->text('Storno Invoice'),
1400 sales_delivery_order => $main::locale->text('Delivery Order'),
1401 purchase_delivery_order => $main::locale->text('Delivery Order'),
1402 dunning => $main::locale->text('Dunning'),
1405 $main::lxdebug->leave_sub();
1406 return $formname_translations{$formname}
1409 sub get_number_prefix_for_type {
1410 $main::lxdebug->enter_sub();
1414 (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
1415 : ($self->{type} =~ /_quotation$/) ? 'quo'
1416 : ($self->{type} =~ /_delivery_order$/) ? 'do'
1419 $main::lxdebug->leave_sub();
1423 sub get_extension_for_format {
1424 $main::lxdebug->enter_sub();
1427 my $extension = $self->{format} =~ /pdf/i ? ".pdf"
1428 : $self->{format} =~ /postscript/i ? ".ps"
1429 : $self->{format} =~ /opendocument/i ? ".odt"
1430 : $self->{format} =~ /excel/i ? ".xls"
1431 : $self->{format} =~ /html/i ? ".html"
1434 $main::lxdebug->leave_sub();
1438 sub generate_attachment_filename {
1439 $main::lxdebug->enter_sub();
1442 my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1443 my $prefix = $self->get_number_prefix_for_type();
1445 if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
1446 $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
1448 } elsif ($attachment_filename && $self->{"${prefix}number"}) {
1449 $attachment_filename .= "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
1452 $attachment_filename = "";
1455 $attachment_filename = $main::locale->quote_special_chars('filenames', $attachment_filename);
1456 $attachment_filename =~ s|[\s/\\]+|_|g;
1458 $main::lxdebug->leave_sub();
1459 return $attachment_filename;
1462 sub generate_email_subject {
1463 $main::lxdebug->enter_sub();
1466 my $subject = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
1467 my $prefix = $self->get_number_prefix_for_type();
1469 if ($subject && $self->{"${prefix}number"}) {
1470 $subject .= " " . $self->{"${prefix}number"}
1473 $main::lxdebug->leave_sub();
1478 $main::lxdebug->enter_sub();
1482 chdir("$self->{tmpdir}");
1485 if (-f "$self->{tmpfile}.err") {
1486 open(FH, "$self->{tmpfile}.err");
1491 if ($self->{tmpfile} && ! $::keep_temp_files) {
1492 $self->{tmpfile} =~ s|.*/||g;
1494 $self->{tmpfile} =~ s/\.\w+$//g;
1495 my $tmpfile = $self->{tmpfile};
1496 unlink(<$tmpfile.*>);
1499 chdir("$self->{cwd}");
1501 $main::lxdebug->leave_sub();
1507 $main::lxdebug->enter_sub();
1509 my ($self, $date, $myconfig) = @_;
1512 if ($date && $date =~ /\D/) {
1514 if ($myconfig->{dateformat} =~ /^yy/) {
1515 ($yy, $mm, $dd) = split /\D/, $date;
1517 if ($myconfig->{dateformat} =~ /^mm/) {
1518 ($mm, $dd, $yy) = split /\D/, $date;
1520 if ($myconfig->{dateformat} =~ /^dd/) {
1521 ($dd, $mm, $yy) = split /\D/, $date;
1526 $yy = ($yy < 70) ? $yy + 2000 : $yy;
1527 $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
1529 $dd = "0$dd" if ($dd < 10);
1530 $mm = "0$mm" if ($mm < 10);
1532 $date = "$yy$mm$dd";
1535 $main::lxdebug->leave_sub();
1540 # Database routines used throughout
1542 sub _dbconnect_options {
1544 my $options = { pg_enable_utf8 => $::locale->is_utf8,
1551 $main::lxdebug->enter_sub(2);
1553 my ($self, $myconfig) = @_;
1555 # connect to database
1556 my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
1560 if ($myconfig->{dboptions}) {
1561 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1564 $main::lxdebug->leave_sub(2);
1569 sub dbconnect_noauto {
1570 $main::lxdebug->enter_sub();
1572 my ($self, $myconfig) = @_;
1574 # connect to database
1575 my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
1579 if ($myconfig->{dboptions}) {
1580 $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1583 $main::lxdebug->leave_sub();
1588 sub get_standard_dbh {
1589 $main::lxdebug->enter_sub(2);
1592 my $myconfig = shift || \%::myconfig;
1594 if ($standard_dbh && !$standard_dbh->{Active}) {
1595 $main::lxdebug->message(LXDebug->INFO(), "get_standard_dbh: \$standard_dbh is defined but not Active anymore");
1596 undef $standard_dbh;
1599 $standard_dbh ||= SL::DB::create->dbh;
1601 $main::lxdebug->leave_sub(2);
1603 return $standard_dbh;
1607 $main::lxdebug->enter_sub();
1609 my ($self, $date, $myconfig) = @_;
1610 my $dbh = $self->dbconnect($myconfig);
1612 my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
1613 my $sth = prepare_execute_query($self, $dbh, $query, $date);
1614 my ($closed) = $sth->fetchrow_array;
1616 $main::lxdebug->leave_sub();
1621 sub update_balance {
1622 $main::lxdebug->enter_sub();
1624 my ($self, $dbh, $table, $field, $where, $value, @values) = @_;
1626 # if we have a value, go do it
1629 # retrieve balance from table
1630 my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
1631 my $sth = prepare_execute_query($self, $dbh, $query, @values);
1632 my ($balance) = $sth->fetchrow_array;
1638 $query = "UPDATE $table SET $field = $balance WHERE $where";
1639 do_query($self, $dbh, $query, @values);
1641 $main::lxdebug->leave_sub();
1644 sub update_exchangerate {
1645 $main::lxdebug->enter_sub();
1647 my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
1649 # some sanity check for currency
1651 $main::lxdebug->leave_sub();
1654 $query = qq|SELECT curr FROM defaults|;
1656 my ($currency) = selectrow_query($self, $dbh, $query);
1657 my ($defaultcurrency) = split m/:/, $currency;
1660 if ($curr eq $defaultcurrency) {
1661 $main::lxdebug->leave_sub();
1665 $query = qq|SELECT e.curr FROM exchangerate e
1666 WHERE e.curr = ? AND e.transdate = ?
1668 my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
1677 $buy = conv_i($buy, "NULL");
1678 $sell = conv_i($sell, "NULL");
1681 if ($buy != 0 && $sell != 0) {
1682 $set = "buy = $buy, sell = $sell";
1683 } elsif ($buy != 0) {
1684 $set = "buy = $buy";
1685 } elsif ($sell != 0) {
1686 $set = "sell = $sell";
1689 if ($sth->fetchrow_array) {
1690 $query = qq|UPDATE exchangerate
1696 $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
1697 VALUES (?, $buy, $sell, ?)|;
1700 do_query($self, $dbh, $query, $curr, $transdate);
1702 $main::lxdebug->leave_sub();
1705 sub save_exchangerate {
1706 $main::lxdebug->enter_sub();
1708 my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
1710 my $dbh = $self->dbconnect($myconfig);
1714 $buy = $rate if $fld eq 'buy';
1715 $sell = $rate if $fld eq 'sell';
1718 $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
1723 $main::lxdebug->leave_sub();
1726 sub get_exchangerate {
1727 $main::lxdebug->enter_sub();
1729 my ($self, $dbh, $curr, $transdate, $fld) = @_;
1732 unless ($transdate) {
1733 $main::lxdebug->leave_sub();
1737 $query = qq|SELECT curr FROM defaults|;
1739 my ($currency) = selectrow_query($self, $dbh, $query);
1740 my ($defaultcurrency) = split m/:/, $currency;
1742 if ($currency eq $defaultcurrency) {
1743 $main::lxdebug->leave_sub();
1747 $query = qq|SELECT e.$fld FROM exchangerate e
1748 WHERE e.curr = ? AND e.transdate = ?|;
1749 my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
1753 $main::lxdebug->leave_sub();
1755 return $exchangerate;
1758 sub check_exchangerate {
1759 $main::lxdebug->enter_sub();
1761 my ($self, $myconfig, $currency, $transdate, $fld) = @_;
1763 if ($fld !~/^buy|sell$/) {
1764 $self->error('Fatal: check_exchangerate called with invalid buy/sell argument');
1767 unless ($transdate) {
1768 $main::lxdebug->leave_sub();
1772 my ($defaultcurrency) = $self->get_default_currency($myconfig);
1774 if ($currency eq $defaultcurrency) {
1775 $main::lxdebug->leave_sub();
1779 my $dbh = $self->get_standard_dbh($myconfig);
1780 my $query = qq|SELECT e.$fld FROM exchangerate e
1781 WHERE e.curr = ? AND e.transdate = ?|;
1783 my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
1785 $main::lxdebug->leave_sub();
1787 return $exchangerate;
1790 sub get_all_currencies {
1791 $main::lxdebug->enter_sub();
1794 my $myconfig = shift || \%::myconfig;
1795 my $dbh = $self->get_standard_dbh($myconfig);
1797 my $query = qq|SELECT curr FROM defaults|;
1799 my ($curr) = selectrow_query($self, $dbh, $query);
1800 my @currencies = grep { $_ } map { s/\s//g; $_ } split m/:/, $curr;
1802 $main::lxdebug->leave_sub();
1807 sub get_default_currency {
1808 $main::lxdebug->enter_sub();
1810 my ($self, $myconfig) = @_;
1811 my @currencies = $self->get_all_currencies($myconfig);
1813 $main::lxdebug->leave_sub();
1815 return $currencies[0];
1818 sub set_payment_options {
1819 $main::lxdebug->enter_sub();
1821 my ($self, $myconfig, $transdate) = @_;
1823 return $main::lxdebug->leave_sub() unless ($self->{payment_id});
1825 my $dbh = $self->get_standard_dbh($myconfig);
1828 qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
1829 qq|FROM payment_terms p | .
1832 ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
1833 $self->{payment_terms}) =
1834 selectrow_query($self, $dbh, $query, $self->{payment_id});
1836 if ($transdate eq "") {
1837 if ($self->{invdate}) {
1838 $transdate = $self->{invdate};
1840 $transdate = $self->{transdate};
1845 qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
1846 qq|FROM payment_terms|;
1847 ($self->{netto_date}, $self->{skonto_date}) =
1848 selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
1850 my ($invtotal, $total);
1851 my (%amounts, %formatted_amounts);
1853 if ($self->{type} =~ /_order$/) {
1854 $amounts{invtotal} = $self->{ordtotal};
1855 $amounts{total} = $self->{ordtotal};
1857 } elsif ($self->{type} =~ /_quotation$/) {
1858 $amounts{invtotal} = $self->{quototal};
1859 $amounts{total} = $self->{quototal};
1862 $amounts{invtotal} = $self->{invtotal};
1863 $amounts{total} = $self->{total};
1865 $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
1867 map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
1869 $amounts{skonto_amount} = $amounts{invtotal} * $self->{percent_skonto};
1870 $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
1871 $amounts{total_wo_skonto} = $amounts{total} * (1 - $self->{percent_skonto});
1873 foreach (keys %amounts) {
1874 $amounts{$_} = $self->round_amount($amounts{$_}, 2);
1875 $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}, 2);
1878 if ($self->{"language_id"}) {
1880 qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
1881 qq|FROM translation_payment_terms t | .
1882 qq|LEFT JOIN language l ON t.language_id = l.id | .
1883 qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
1884 my ($description_long, $output_numberformat, $output_dateformat,
1885 $output_longdates) =
1886 selectrow_query($self, $dbh, $query,
1887 $self->{"language_id"}, $self->{"payment_id"});
1889 $self->{payment_terms} = $description_long if ($description_long);
1891 if ($output_dateformat) {
1892 foreach my $key (qw(netto_date skonto_date)) {
1894 $main::locale->reformat_date($myconfig, $self->{$key},
1900 if ($output_numberformat &&
1901 ($output_numberformat ne $myconfig->{"numberformat"})) {
1902 my $saved_numberformat = $myconfig->{"numberformat"};
1903 $myconfig->{"numberformat"} = $output_numberformat;
1904 map { $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}) } keys %amounts;
1905 $myconfig->{"numberformat"} = $saved_numberformat;
1909 $self->{payment_terms} =~ s/<%netto_date%>/$self->{netto_date}/g;
1910 $self->{payment_terms} =~ s/<%skonto_date%>/$self->{skonto_date}/g;
1911 $self->{payment_terms} =~ s/<%currency%>/$self->{currency}/g;
1912 $self->{payment_terms} =~ s/<%terms_netto%>/$self->{terms_netto}/g;
1913 $self->{payment_terms} =~ s/<%account_number%>/$self->{account_number}/g;
1914 $self->{payment_terms} =~ s/<%bank%>/$self->{bank}/g;
1915 $self->{payment_terms} =~ s/<%bank_code%>/$self->{bank_code}/g;
1917 map { $self->{payment_terms} =~ s/<%${_}%>/$formatted_amounts{$_}/g; } keys %formatted_amounts;
1919 $self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
1921 $main::lxdebug->leave_sub();
1925 sub get_template_language {
1926 $main::lxdebug->enter_sub();
1928 my ($self, $myconfig) = @_;
1930 my $template_code = "";
1932 if ($self->{language_id}) {
1933 my $dbh = $self->get_standard_dbh($myconfig);
1934 my $query = qq|SELECT template_code FROM language WHERE id = ?|;
1935 ($template_code) = selectrow_query($self, $dbh, $query, $self->{language_id});
1938 $main::lxdebug->leave_sub();
1940 return $template_code;
1943 sub get_printer_code {
1944 $main::lxdebug->enter_sub();
1946 my ($self, $myconfig) = @_;
1948 my $template_code = "";
1950 if ($self->{printer_id}) {
1951 my $dbh = $self->get_standard_dbh($myconfig);
1952 my $query = qq|SELECT template_code, printer_command FROM printers WHERE id = ?|;
1953 ($template_code, $self->{printer_command}) = selectrow_query($self, $dbh, $query, $self->{printer_id});
1956 $main::lxdebug->leave_sub();
1958 return $template_code;
1962 $main::lxdebug->enter_sub();
1964 my ($self, $myconfig) = @_;
1966 my $template_code = "";
1968 if ($self->{shipto_id}) {
1969 my $dbh = $self->get_standard_dbh($myconfig);
1970 my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
1971 my $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{shipto_id});
1972 map({ $self->{$_} = $ref->{$_} } keys(%$ref));
1975 $main::lxdebug->leave_sub();
1979 $main::lxdebug->enter_sub();
1981 my ($self, $dbh, $id, $module) = @_;
1986 foreach my $item (qw(name department_1 department_2 street zipcode city country
1987 contact cp_gender phone fax email)) {
1988 if ($self->{"shipto$item"}) {
1989 $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
1991 push(@values, $self->{"shipto${item}"});
1995 if ($self->{shipto_id}) {
1996 my $query = qq|UPDATE shipto set
1998 shiptodepartment_1 = ?,
1999 shiptodepartment_2 = ?,
2005 shiptocp_gender = ?,
2009 WHERE shipto_id = ?|;
2010 do_query($self, $dbh, $query, @values, $self->{shipto_id});
2012 my $query = qq|SELECT * FROM shipto
2013 WHERE shiptoname = ? AND
2014 shiptodepartment_1 = ? AND
2015 shiptodepartment_2 = ? AND
2016 shiptostreet = ? AND
2017 shiptozipcode = ? AND
2019 shiptocountry = ? AND
2020 shiptocontact = ? AND
2021 shiptocp_gender = ? AND
2027 my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values, $module, $id);
2030 qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
2031 shiptostreet, shiptozipcode, shiptocity, shiptocountry,
2032 shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module)
2033 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
2034 do_query($self, $dbh, $query, $id, @values, $module);
2039 $main::lxdebug->leave_sub();
2043 $main::lxdebug->enter_sub();
2045 my ($self, $dbh) = @_;
2047 $dbh ||= $self->get_standard_dbh(\%main::myconfig);
2049 my $query = qq|SELECT id, name FROM employee WHERE login = ?|;
2050 ($self->{"employee_id"}, $self->{"employee"}) = selectrow_query($self, $dbh, $query, $self->{login});
2051 $self->{"employee_id"} *= 1;
2053 $main::lxdebug->leave_sub();
2056 sub get_employee_data {
2057 $main::lxdebug->enter_sub();
2062 Common::check_params(\%params, qw(prefix));
2063 Common::check_params_x(\%params, qw(id));
2066 $main::lxdebug->leave_sub();
2070 my $myconfig = \%main::myconfig;
2071 my $dbh = $params{dbh} || $self->get_standard_dbh($myconfig);
2073 my ($login) = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
2076 my $user = User->new($login);
2077 map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
2079 $self->{$params{prefix} . '_login'} = $login;
2080 $self->{$params{prefix} . '_name'} ||= $login;
2083 $main::lxdebug->leave_sub();
2087 $main::lxdebug->enter_sub();
2089 my ($self, $myconfig, $reference_date) = @_;
2091 $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
2093 my $dbh = $self->get_standard_dbh($myconfig);
2094 my $query = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
2095 my ($duedate) = selectrow_query($self, $dbh, $query, $self->{payment_id});
2097 $main::lxdebug->leave_sub();
2103 $main::lxdebug->enter_sub();
2105 my ($self, $dbh, $id, $key) = @_;
2107 $key = "all_contacts" unless ($key);
2111 $main::lxdebug->leave_sub();
2116 qq|SELECT cp_id, cp_cv_id, cp_name, cp_givenname, cp_abteilung | .
2117 qq|FROM contacts | .
2118 qq|WHERE cp_cv_id = ? | .
2119 qq|ORDER BY lower(cp_name)|;
2121 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $id);
2123 $main::lxdebug->leave_sub();
2127 $main::lxdebug->enter_sub();
2129 my ($self, $dbh, $key) = @_;
2131 my ($all, $old_id, $where, @values);
2133 if (ref($key) eq "HASH") {
2136 $key = "ALL_PROJECTS";
2138 foreach my $p (keys(%{$params})) {
2140 $all = $params->{$p};
2141 } elsif ($p eq "old_id") {
2142 $old_id = $params->{$p};
2143 } elsif ($p eq "key") {
2144 $key = $params->{$p};
2150 $where = "WHERE active ";
2152 if (ref($old_id) eq "ARRAY") {
2153 my @ids = grep({ $_ } @{$old_id});
2155 $where .= " OR id IN (" . join(",", map({ "?" } @ids)) . ") ";
2156 push(@values, @ids);
2159 $where .= " OR (id = ?) ";
2160 push(@values, $old_id);
2166 qq|SELECT id, projectnumber, description, active | .
2169 qq|ORDER BY lower(projectnumber)|;
2171 $self->{$key} = selectall_hashref_query($self, $dbh, $query, @values);
2173 $main::lxdebug->leave_sub();
2177 $main::lxdebug->enter_sub();
2179 my ($self, $dbh, $vc_id, $key) = @_;
2181 $key = "all_shipto" unless ($key);
2184 # get shipping addresses
2185 my $query = qq|SELECT * FROM shipto WHERE trans_id = ?|;
2187 $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id);
2193 $main::lxdebug->leave_sub();
2197 $main::lxdebug->enter_sub();
2199 my ($self, $dbh, $key) = @_;
2201 $key = "all_printers" unless ($key);
2203 my $query = qq|SELECT id, printer_description, printer_command, template_code FROM printers|;
2205 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2207 $main::lxdebug->leave_sub();
2211 $main::lxdebug->enter_sub();
2213 my ($self, $dbh, $params) = @_;
2216 $key = $params->{key};
2217 $key = "all_charts" unless ($key);
2219 my $transdate = quote_db_date($params->{transdate});
2222 qq|SELECT c.id, c.accno, c.description, c.link, c.charttype, tk.taxkey_id, tk.tax_id | .
2224 qq|LEFT JOIN taxkeys tk ON | .
2225 qq|(tk.id = (SELECT id FROM taxkeys | .
2226 qq| WHERE taxkeys.chart_id = c.id AND startdate <= $transdate | .
2227 qq| ORDER BY startdate DESC LIMIT 1)) | .
2228 qq|ORDER BY c.accno|;
2230 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2232 $main::lxdebug->leave_sub();
2235 sub _get_taxcharts {
2236 $main::lxdebug->enter_sub();
2238 my ($self, $dbh, $params) = @_;
2240 my $key = "all_taxcharts";
2243 if (ref $params eq 'HASH') {
2244 $key = $params->{key} if ($params->{key});
2245 if ($params->{module} eq 'AR') {
2246 push @where, 'taxkey NOT IN (8, 9, 18, 19)';
2248 } elsif ($params->{module} eq 'AP') {
2249 push @where, 'taxkey NOT IN (1, 2, 3, 12, 13)';
2256 my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
2258 my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
2260 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2262 $main::lxdebug->leave_sub();
2266 $main::lxdebug->enter_sub();
2268 my ($self, $dbh, $key) = @_;
2270 $key = "all_taxzones" unless ($key);
2272 my $query = qq|SELECT * FROM tax_zones ORDER BY id|;
2274 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2276 $main::lxdebug->leave_sub();
2279 sub _get_employees {
2280 $main::lxdebug->enter_sub();
2282 my ($self, $dbh, $default_key, $key) = @_;
2284 $key = $default_key unless ($key);
2285 $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee ORDER BY lower(name)|);
2287 $main::lxdebug->leave_sub();
2290 sub _get_business_types {
2291 $main::lxdebug->enter_sub();
2293 my ($self, $dbh, $key) = @_;
2295 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2296 $options->{key} ||= "all_business_types";
2299 if (exists $options->{salesman}) {
2300 $where = 'WHERE ' . ($options->{salesman} ? '' : 'NOT ') . 'COALESCE(salesman)';
2303 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, qq|SELECT * FROM business $where ORDER BY lower(description)|);
2305 $main::lxdebug->leave_sub();
2308 sub _get_languages {
2309 $main::lxdebug->enter_sub();
2311 my ($self, $dbh, $key) = @_;
2313 $key = "all_languages" unless ($key);
2315 my $query = qq|SELECT * FROM language ORDER BY id|;
2317 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2319 $main::lxdebug->leave_sub();
2322 sub _get_dunning_configs {
2323 $main::lxdebug->enter_sub();
2325 my ($self, $dbh, $key) = @_;
2327 $key = "all_dunning_configs" unless ($key);
2329 my $query = qq|SELECT * FROM dunning_config ORDER BY dunning_level|;
2331 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2333 $main::lxdebug->leave_sub();
2336 sub _get_currencies {
2337 $main::lxdebug->enter_sub();
2339 my ($self, $dbh, $key) = @_;
2341 $key = "all_currencies" unless ($key);
2343 my $query = qq|SELECT curr AS currency FROM defaults|;
2345 $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
2347 $main::lxdebug->leave_sub();
2351 $main::lxdebug->enter_sub();
2353 my ($self, $dbh, $key) = @_;
2355 $key = "all_payments" unless ($key);
2357 my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
2359 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2361 $main::lxdebug->leave_sub();
2364 sub _get_customers {
2365 $main::lxdebug->enter_sub();
2367 my ($self, $dbh, $key) = @_;
2369 my $options = ref $key eq 'HASH' ? $key : { key => $key };
2370 $options->{key} ||= "all_customers";
2371 my $limit_clause = "LIMIT $options->{limit}" if $options->{limit};
2374 push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if $options->{business_is_salesman};
2375 push @where, qq|NOT obsolete| if !$options->{with_obsolete};
2376 my $where_str = @where ? "WHERE " . join(" AND ", map { "($_)" } @where) : '';
2378 my $query = qq|SELECT * FROM customer $where_str ORDER BY name $limit_clause|;
2379 $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, $query);
2381 $main::lxdebug->leave_sub();
2385 $main::lxdebug->enter_sub();
2387 my ($self, $dbh, $key) = @_;
2389 $key = "all_vendors" unless ($key);
2391 my $query = qq|SELECT * FROM vendor WHERE NOT obsolete ORDER BY name|;
2393 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2395 $main::lxdebug->leave_sub();
2398 sub _get_departments {
2399 $main::lxdebug->enter_sub();
2401 my ($self, $dbh, $key) = @_;
2403 $key = "all_departments" unless ($key);
2405 my $query = qq|SELECT * FROM department ORDER BY description|;
2407 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2409 $main::lxdebug->leave_sub();
2412 sub _get_warehouses {
2413 $main::lxdebug->enter_sub();
2415 my ($self, $dbh, $param) = @_;
2417 my ($key, $bins_key);
2419 if ('' eq ref $param) {
2423 $key = $param->{key};
2424 $bins_key = $param->{bins};
2427 my $query = qq|SELECT w.* FROM warehouse w
2428 WHERE (NOT w.invalid) AND
2429 ((SELECT COUNT(b.*) FROM bin b WHERE b.warehouse_id = w.id) > 0)
2430 ORDER BY w.sortkey|;
2432 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2435 $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?|;
2436 my $sth = prepare_query($self, $dbh, $query);
2438 foreach my $warehouse (@{ $self->{$key} }) {
2439 do_statement($self, $sth, $query, $warehouse->{id});
2440 $warehouse->{$bins_key} = [];
2442 while (my $ref = $sth->fetchrow_hashref()) {
2443 push @{ $warehouse->{$bins_key} }, $ref;
2449 $main::lxdebug->leave_sub();
2453 $main::lxdebug->enter_sub();
2455 my ($self, $dbh, $table, $key, $sortkey) = @_;
2457 my $query = qq|SELECT * FROM $table|;
2458 $query .= qq| ORDER BY $sortkey| if ($sortkey);
2460 $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2462 $main::lxdebug->leave_sub();
2466 # $main::lxdebug->enter_sub();
2468 # my ($self, $dbh, $key) = @_;
2470 # $key ||= "all_groups";
2472 # my $groups = $main::auth->read_groups();
2474 # $self->{$key} = selectall_hashref_query($self, $dbh, $query);
2476 # $main::lxdebug->leave_sub();
2480 $main::lxdebug->enter_sub();
2485 my $dbh = $self->get_standard_dbh(\%main::myconfig);
2486 my ($sth, $query, $ref);
2488 my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
2489 my $vc_id = $self->{"${vc}_id"};
2491 if ($params{"contacts"}) {
2492 $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
2495 if ($params{"shipto"}) {
2496 $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
2499 if ($params{"projects"} || $params{"all_projects"}) {
2500 $self->_get_projects($dbh, $params{"all_projects"} ?
2501 $params{"all_projects"} : $params{"projects"},
2502 $params{"all_projects"} ? 1 : 0);
2505 if ($params{"printers"}) {
2506 $self->_get_printers($dbh, $params{"printers"});
2509 if ($params{"languages"}) {
2510 $self->_get_languages($dbh, $params{"languages"});
2513 if ($params{"charts"}) {
2514 $self->_get_charts($dbh, $params{"charts"});
2517 if ($params{"taxcharts"}) {
2518 $self->_get_taxcharts($dbh, $params{"taxcharts"});
2521 if ($params{"taxzones"}) {
2522 $self->_get_taxzones($dbh, $params{"taxzones"});
2525 if ($params{"employees"}) {
2526 $self->_get_employees($dbh, "all_employees", $params{"employees"});
2529 if ($params{"salesmen"}) {
2530 $self->_get_employees($dbh, "all_salesmen", $params{"salesmen"});
2533 if ($params{"business_types"}) {
2534 $self->_get_business_types($dbh, $params{"business_types"});
2537 if ($params{"dunning_configs"}) {
2538 $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
2541 if($params{"currencies"}) {
2542 $self->_get_currencies($dbh, $params{"currencies"});
2545 if($params{"customers"}) {
2546 $self->_get_customers($dbh, $params{"customers"});
2549 if($params{"vendors"}) {
2550 if (ref $params{"vendors"} eq 'HASH') {
2551 $self->_get_vendors($dbh, $params{"vendors"}{key}, $params{"vendors"}{limit});
2553 $self->_get_vendors($dbh, $params{"vendors"});
2557 if($params{"payments"}) {
2558 $self->_get_payments($dbh, $params{"payments"});
2561 if($params{"departments"}) {
2562 $self->_get_departments($dbh, $params{"departments"});
2565 if ($params{price_factors}) {
2566 $self->_get_simple($dbh, 'price_factors', $params{price_factors}, 'sortkey');
2569 if ($params{warehouses}) {
2570 $self->_get_warehouses($dbh, $params{warehouses});
2573 # if ($params{groups}) {
2574 # $self->_get_groups($dbh, $params{groups});
2577 if ($params{partsgroup}) {
2578 $self->get_partsgroup(\%main::myconfig, { all => 1, target => $params{partsgroup} });
2581 $main::lxdebug->leave_sub();
2584 # this sub gets the id and name from $table
2586 $main::lxdebug->enter_sub();
2588 my ($self, $myconfig, $table) = @_;
2590 # connect to database
2591 my $dbh = $self->get_standard_dbh($myconfig);
2593 $table = $table eq "customer" ? "customer" : "vendor";
2594 my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
2596 my ($query, @values);
2598 if (!$self->{openinvoices}) {
2600 if ($self->{customernumber} ne "") {
2601 $where = qq|(vc.customernumber ILIKE ?)|;
2602 push(@values, '%' . $self->{customernumber} . '%');
2604 $where = qq|(vc.name ILIKE ?)|;
2605 push(@values, '%' . $self->{$table} . '%');
2609 qq~SELECT vc.id, vc.name,
2610 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2612 WHERE $where AND (NOT vc.obsolete)
2616 qq~SELECT DISTINCT vc.id, vc.name,
2617 vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
2619 JOIN $table vc ON (a.${table}_id = vc.id)
2620 WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
2622 push(@values, '%' . $self->{$table} . '%');
2625 $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
2627 $main::lxdebug->leave_sub();
2629 return scalar(@{ $self->{name_list} });
2632 # the selection sub is used in the AR, AP, IS, IR and OE module
2635 $main::lxdebug->enter_sub();
2637 my ($self, $myconfig, $table, $module) = @_;
2640 my $dbh = $self->get_standard_dbh;
2642 $table = $table eq "customer" ? "customer" : "vendor";
2644 my $query = qq|SELECT count(*) FROM $table|;
2645 my ($count) = selectrow_query($self, $dbh, $query);
2647 # build selection list
2648 if ($count <= $myconfig->{vclimit}) {
2649 $query = qq|SELECT id, name, salesman_id
2650 FROM $table WHERE NOT obsolete
2652 $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
2656 $self->get_employee($dbh);
2658 # setup sales contacts
2659 $query = qq|SELECT e.id, e.name
2661 WHERE (e.sales = '1') AND (NOT e.id = ?)|;
2662 $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
2665 push(@{ $self->{all_employees} },
2666 { id => $self->{employee_id},
2667 name => $self->{employee} });
2669 # sort the whole thing
2670 @{ $self->{all_employees} } =
2671 sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
2673 if ($module eq 'AR') {
2675 # prepare query for departments
2676 $query = qq|SELECT id, description
2679 ORDER BY description|;
2682 $query = qq|SELECT id, description
2684 ORDER BY description|;
2687 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2690 $query = qq|SELECT id, description
2694 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2697 $query = qq|SELECT printer_description, id
2699 ORDER BY printer_description|;
2701 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2704 $query = qq|SELECT id, description
2708 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2710 $main::lxdebug->leave_sub();
2713 sub language_payment {
2714 $main::lxdebug->enter_sub();
2716 my ($self, $myconfig) = @_;
2718 my $dbh = $self->get_standard_dbh($myconfig);
2720 my $query = qq|SELECT id, description
2724 $self->{languages} = selectall_hashref_query($self, $dbh, $query);
2727 $query = qq|SELECT printer_description, id
2729 ORDER BY printer_description|;
2731 $self->{printers} = selectall_hashref_query($self, $dbh, $query);
2734 $query = qq|SELECT id, description
2738 $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
2740 # get buchungsgruppen
2741 $query = qq|SELECT id, description
2742 FROM buchungsgruppen|;
2744 $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
2746 $main::lxdebug->leave_sub();
2749 # this is only used for reports
2750 sub all_departments {
2751 $main::lxdebug->enter_sub();
2753 my ($self, $myconfig, $table) = @_;
2755 my $dbh = $self->get_standard_dbh($myconfig);
2758 if ($table eq 'customer') {
2759 $where = "WHERE role = 'P' ";
2762 my $query = qq|SELECT id, description
2765 ORDER BY description|;
2766 $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
2768 delete($self->{all_departments}) unless (@{ $self->{all_departments} || [] });
2770 $main::lxdebug->leave_sub();
2774 $main::lxdebug->enter_sub();
2776 my ($self, $module, $myconfig, $table, $provided_dbh) = @_;
2779 if ($table eq "customer") {
2788 $self->all_vc($myconfig, $table, $module);
2790 # get last customers or vendors
2791 my ($query, $sth, $ref);
2793 my $dbh = $provided_dbh ? $provided_dbh : $self->get_standard_dbh($myconfig);
2798 my $transdate = "current_date";
2799 if ($self->{transdate}) {
2800 $transdate = $dbh->quote($self->{transdate});
2803 # now get the account numbers
2804 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2805 FROM chart c, taxkeys tk
2806 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
2807 (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
2810 $sth = $dbh->prepare($query);
2812 do_statement($self, $sth, $query, '%' . $module . '%');
2814 $self->{accounts} = "";
2815 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2817 foreach my $key (split(/:/, $ref->{link})) {
2818 if ($key =~ /\Q$module\E/) {
2820 # cross reference for keys
2821 $xkeyref{ $ref->{accno} } = $key;
2823 push @{ $self->{"${module}_links"}{$key} },
2824 { accno => $ref->{accno},
2825 description => $ref->{description},
2826 taxkey => $ref->{taxkey_id},
2827 tax_id => $ref->{tax_id} };
2829 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2835 # get taxkeys and description
2836 $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
2837 $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
2839 if (($module eq "AP") || ($module eq "AR")) {
2840 # get tax rates and description
2841 $query = qq|SELECT * FROM tax|;
2842 $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
2848 a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
2849 a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
2850 a.intnotes, a.department_id, a.amount AS oldinvtotal,
2851 a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
2853 d.description AS department,
2856 JOIN $table c ON (a.${table}_id = c.id)
2857 LEFT JOIN employee e ON (e.id = a.employee_id)
2858 LEFT JOIN department d ON (d.id = a.department_id)
2860 $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2862 foreach my $key (keys %$ref) {
2863 $self->{$key} = $ref->{$key};
2866 my $transdate = "current_date";
2867 if ($self->{transdate}) {
2868 $transdate = $dbh->quote($self->{transdate});
2871 # now get the account numbers
2872 $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2874 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
2876 AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
2877 OR c.link LIKE '%_tax%' OR c.taxkey_id IS NULL)
2880 $sth = $dbh->prepare($query);
2881 do_statement($self, $sth, $query, "%$module%");
2883 $self->{accounts} = "";
2884 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
2886 foreach my $key (split(/:/, $ref->{link})) {
2887 if ($key =~ /\Q$module\E/) {
2889 # cross reference for keys
2890 $xkeyref{ $ref->{accno} } = $key;
2892 push @{ $self->{"${module}_links"}{$key} },
2893 { accno => $ref->{accno},
2894 description => $ref->{description},
2895 taxkey => $ref->{taxkey_id},
2896 tax_id => $ref->{tax_id} };
2898 $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2904 # get amounts from individual entries
2907 c.accno, c.description,
2908 a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
2912 LEFT JOIN chart c ON (c.id = a.chart_id)
2913 LEFT JOIN project p ON (p.id = a.project_id)
2914 LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
2915 WHERE (tk.taxkey_id=a.taxkey) AND
2916 ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
2917 THEN tk.chart_id = a.chart_id
2920 OR (c.link='%tax%')) AND
2921 (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
2922 WHERE a.trans_id = ?
2923 AND a.fx_transaction = '0'
2924 ORDER BY a.acc_trans_id, a.transdate|;
2925 $sth = $dbh->prepare($query);
2926 do_statement($self, $sth, $query, $self->{id});
2928 # get exchangerate for currency
2929 $self->{exchangerate} =
2930 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2933 # store amounts in {acc_trans}{$key} for multiple accounts
2934 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
2935 $ref->{exchangerate} =
2936 $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
2937 if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
2940 if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
2941 $ref->{amount} *= -1;
2943 $ref->{index} = $index;
2945 push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
2951 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;
2963 current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
2964 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2965 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2967 $ref = selectfirst_hashref_query($self, $dbh, $query);
2968 map { $self->{$_} = $ref->{$_} } keys %$ref;
2970 if ($self->{"$self->{vc}_id"}) {
2972 # only setup currency
2973 ($self->{currency}) = split(/:/, $self->{currencies});
2977 $self->lastname_used($dbh, $myconfig, $table, $module);
2979 # get exchangerate for currency
2980 $self->{exchangerate} =
2981 $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2987 $main::lxdebug->leave_sub();
2991 $main::lxdebug->enter_sub();
2993 my ($self, $dbh, $myconfig, $table, $module) = @_;
2997 $table = $table eq "customer" ? "customer" : "vendor";
2998 my %column_map = ("a.curr" => "currency",
2999 "a.${table}_id" => "${table}_id",
3000 "a.department_id" => "department_id",
3001 "d.description" => "department",
3002 "ct.name" => $table,
3003 "current_date + ct.terms" => "duedate",
3006 if ($self->{type} =~ /delivery_order/) {
3007 $arap = 'delivery_orders';
3008 delete $column_map{"a.curr"};
3010 } elsif ($self->{type} =~ /_order/) {
3012 $where = "quotation = '0'";
3014 } elsif ($self->{type} =~ /_quotation/) {
3016 $where = "quotation = '1'";
3018 } elsif ($table eq 'customer') {
3026 $where = "($where) AND" if ($where);
3027 my $query = qq|SELECT MAX(id) FROM $arap
3028 WHERE $where ${table}_id > 0|;
3029 my ($trans_id) = selectrow_query($self, $dbh, $query);
3032 my $column_spec = join(', ', map { "${_} AS $column_map{$_}" } keys %column_map);
3033 $query = qq|SELECT $column_spec
3035 LEFT JOIN $table ct ON (a.${table}_id = ct.id)
3036 LEFT JOIN department d ON (a.department_id = d.id)
3038 my $ref = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
3040 map { $self->{$_} = $ref->{$_} } values %column_map;
3042 $main::lxdebug->leave_sub();
3046 $main::lxdebug->enter_sub();
3049 my $myconfig = shift || \%::myconfig;
3050 my ($thisdate, $days) = @_;
3052 my $dbh = $self->get_standard_dbh($myconfig);
3057 my $dateformat = $myconfig->{dateformat};
3058 $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
3059 $thisdate = $dbh->quote($thisdate);
3060 $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
3062 $query = qq|SELECT current_date AS thisdate|;
3065 ($thisdate) = selectrow_query($self, $dbh, $query);
3067 $main::lxdebug->leave_sub();
3073 $main::lxdebug->enter_sub();
3075 my ($self, $string) = @_;
3077 if ($string !~ /%/) {
3078 $string = "%$string%";
3081 $string =~ s/\'/\'\'/g;
3083 $main::lxdebug->leave_sub();
3089 $main::lxdebug->enter_sub();
3091 my ($self, $flds, $new, $count, $numrows) = @_;
3095 map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } } 1 .. $count;
3100 foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
3102 my $j = $item->{ndx} - 1;
3103 map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
3107 for $i ($count + 1 .. $numrows) {
3108 map { delete $self->{"${_}_$i"} } @{$flds};
3111 $main::lxdebug->leave_sub();
3115 $main::lxdebug->enter_sub();
3117 my ($self, $myconfig) = @_;
3121 my $dbh = $self->dbconnect_noauto($myconfig);
3123 my $query = qq|DELETE FROM status
3124 WHERE (formname = ?) AND (trans_id = ?)|;
3125 my $sth = prepare_query($self, $dbh, $query);
3127 if ($self->{formname} =~ /(check|receipt)/) {
3128 for $i (1 .. $self->{rowcount}) {
3129 do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
3132 do_statement($self, $sth, $query, $self->{formname}, $self->{id});
3136 my $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3137 my $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3139 my %queued = split / /, $self->{queued};
3142 if ($self->{formname} =~ /(check|receipt)/) {
3144 # this is a check or receipt, add one entry for each lineitem
3145 my ($accno) = split /--/, $self->{account};
3146 $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
3147 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
3148 @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
3149 $sth = prepare_query($self, $dbh, $query);
3151 for $i (1 .. $self->{rowcount}) {
3152 if ($self->{"checked_$i"}) {
3153 do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
3159 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3160 VALUES (?, ?, ?, ?, ?)|;
3161 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
3162 $queued{$self->{formname}}, $self->{formname});
3168 $main::lxdebug->leave_sub();
3172 $main::lxdebug->enter_sub();
3174 my ($self, $dbh) = @_;
3176 my ($query, $printed, $emailed);
3178 my $formnames = $self->{printed};
3179 my $emailforms = $self->{emailed};
3181 $query = qq|DELETE FROM status
3182 WHERE (formname = ?) AND (trans_id = ?)|;
3183 do_query($self, $dbh, $query, $self->{formname}, $self->{id});
3185 # this only applies to the forms
3186 # checks and receipts are posted when printed or queued
3188 if ($self->{queued}) {
3189 my %queued = split / /, $self->{queued};
3191 foreach my $formname (keys %queued) {
3192 $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3193 $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
3195 $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
3196 VALUES (?, ?, ?, ?, ?)|;
3197 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
3199 $formnames =~ s/\Q$self->{formname}\E//;
3200 $emailforms =~ s/\Q$self->{formname}\E//;
3205 # save printed, emailed info
3206 $formnames =~ s/^ +//g;
3207 $emailforms =~ s/^ +//g;
3210 map { $status{$_}{printed} = 1 } split / +/, $formnames;
3211 map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
3213 foreach my $formname (keys %status) {
3214 $printed = ($formnames =~ /\Q$self->{formname}\E/) ? "1" : "0";
3215 $emailed = ($emailforms =~ /\Q$self->{formname}\E/) ? "1" : "0";
3217 $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
3218 VALUES (?, ?, ?, ?)|;
3219 do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
3222 $main::lxdebug->leave_sub();
3226 # $main::locale->text('SAVED')
3227 # $main::locale->text('DELETED')
3228 # $main::locale->text('ADDED')
3229 # $main::locale->text('PAYMENT POSTED')
3230 # $main::locale->text('POSTED')
3231 # $main::locale->text('POSTED AS NEW')
3232 # $main::locale->text('ELSE')
3233 # $main::locale->text('SAVED FOR DUNNING')
3234 # $main::locale->text('DUNNING STARTED')
3235 # $main::locale->text('PRINTED')
3236 # $main::locale->text('MAILED')
3237 # $main::locale->text('SCREENED')
3238 # $main::locale->text('CANCELED')
3239 # $main::locale->text('invoice')
3240 # $main::locale->text('proforma')
3241 # $main::locale->text('sales_order')
3242 # $main::locale->text('pick_list')
3243 # $main::locale->text('purchase_order')
3244 # $main::locale->text('bin_list')
3245 # $main::locale->text('sales_quotation')
3246 # $main::locale->text('request_quotation')
3249 $main::lxdebug->enter_sub();
3252 my $dbh = shift || $self->get_standard_dbh;
3254 if(!exists $self->{employee_id}) {
3255 &get_employee($self, $dbh);
3259 qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
3260 qq|VALUES (?, (SELECT id FROM employee WHERE login = ?), ?, ?, ?)|;
3261 my @values = (conv_i($self->{id}), $self->{login},
3262 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
3263 do_query($self, $dbh, $query, @values);
3267 $main::lxdebug->leave_sub();
3271 $main::lxdebug->enter_sub();
3273 my ($self, $dbh, $trans_id, $restriction, $order) = @_;
3274 my ($orderBy, $desc) = split(/\-\-/, $order);
3275 $order = " ORDER BY " . ($order eq "" ? " h.itime " : ($desc == 1 ? $orderBy . " DESC " : $orderBy . " "));
3278 if ($trans_id ne "") {
3280 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 | .
3281 qq|FROM history_erp h | .
3282 qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
3283 qq|WHERE (trans_id = | . $trans_id . qq|) $restriction | .
3286 my $sth = $dbh->prepare($query) || $self->dberror($query);
3288 $sth->execute() || $self->dberror("$query");
3290 while(my $hash_ref = $sth->fetchrow_hashref()) {
3291 $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
3292 $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
3293 $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
3294 $tempArray[$i++] = $hash_ref;
3296 $main::lxdebug->leave_sub() and return \@tempArray
3297 if ($i > 0 && $tempArray[0] ne "");
3299 $main::lxdebug->leave_sub();
3303 sub update_defaults {
3304 $main::lxdebug->enter_sub();
3306 my ($self, $myconfig, $fld, $provided_dbh) = @_;
3309 if ($provided_dbh) {
3310 $dbh = $provided_dbh;
3312 $dbh = $self->dbconnect_noauto($myconfig);
3314 my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
3315 my $sth = $dbh->prepare($query);
3317 $sth->execute || $self->dberror($query);
3318 my ($var) = $sth->fetchrow_array;
3321 if ($var =~ m/\d+$/) {
3322 my $new_var = (substr $var, $-[0]) * 1 + 1;
3323 my $len_diff = length($var) - $-[0] - length($new_var);
3324 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3330 $query = qq|UPDATE defaults SET $fld = ?|;
3331 do_query($self, $dbh, $query, $var);
3333 if (!$provided_dbh) {
3338 $main::lxdebug->leave_sub();
3343 sub update_business {
3344 $main::lxdebug->enter_sub();
3346 my ($self, $myconfig, $business_id, $provided_dbh) = @_;
3349 if ($provided_dbh) {
3350 $dbh = $provided_dbh;
3352 $dbh = $self->dbconnect_noauto($myconfig);
3355 qq|SELECT customernumberinit FROM business
3356 WHERE id = ? FOR UPDATE|;
3357 my ($var) = selectrow_query($self, $dbh, $query, $business_id);
3359 return undef unless $var;
3361 if ($var =~ m/\d+$/) {
3362 my $new_var = (substr $var, $-[0]) * 1 + 1;
3363 my $len_diff = length($var) - $-[0] - length($new_var);
3364 $var = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
3370 $query = qq|UPDATE business
3371 SET customernumberinit = ?
3373 do_query($self, $dbh, $query, $var, $business_id);
3375 if (!$provided_dbh) {
3380 $main::lxdebug->leave_sub();
3385 sub get_partsgroup {
3386 $main::lxdebug->enter_sub();
3388 my ($self, $myconfig, $p) = @_;
3389 my $target = $p->{target} || 'all_partsgroup';
3391 my $dbh = $self->get_standard_dbh($myconfig);
3393 my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
3395 JOIN parts p ON (p.partsgroup_id = pg.id) |;
3398 if ($p->{searchitems} eq 'part') {
3399 $query .= qq|WHERE p.inventory_accno_id > 0|;
3401 if ($p->{searchitems} eq 'service') {
3402 $query .= qq|WHERE p.inventory_accno_id IS NULL|;
3404 if ($p->{searchitems} eq 'assembly') {
3405 $query .= qq|WHERE p.assembly = '1'|;
3407 if ($p->{searchitems} eq 'labor') {
3408 $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
3411 $query .= qq|ORDER BY partsgroup|;
3414 $query = qq|SELECT id, partsgroup FROM partsgroup
3415 ORDER BY partsgroup|;
3418 if ($p->{language_code}) {
3419 $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
3420 t.description AS translation
3422 JOIN parts p ON (p.partsgroup_id = pg.id)
3423 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
3424 ORDER BY translation|;
3425 @values = ($p->{language_code});
3428 $self->{$target} = selectall_hashref_query($self, $dbh, $query, @values);
3430 $main::lxdebug->leave_sub();
3433 sub get_pricegroup {
3434 $main::lxdebug->enter_sub();
3436 my ($self, $myconfig, $p) = @_;
3438 my $dbh = $self->get_standard_dbh($myconfig);
3440 my $query = qq|SELECT p.id, p.pricegroup
3443 $query .= qq| ORDER BY pricegroup|;
3446 $query = qq|SELECT id, pricegroup FROM pricegroup
3447 ORDER BY pricegroup|;
3450 $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
3452 $main::lxdebug->leave_sub();
3456 # usage $form->all_years($myconfig, [$dbh])
3457 # return list of all years where bookings found
3460 $main::lxdebug->enter_sub();
3462 my ($self, $myconfig, $dbh) = @_;
3464 $dbh ||= $self->get_standard_dbh($myconfig);
3467 my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
3468 (SELECT MAX(transdate) FROM acc_trans)|;
3469 my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
3471 if ($myconfig->{dateformat} =~ /^yy/) {
3472 ($startdate) = split /\W/, $startdate;
3473 ($enddate) = split /\W/, $enddate;
3475 (@_) = split /\W/, $startdate;
3477 (@_) = split /\W/, $enddate;
3482 $startdate = substr($startdate,0,4);
3483 $enddate = substr($enddate,0,4);
3485 while ($enddate >= $startdate) {
3486 push @all_years, $enddate--;
3491 $main::lxdebug->leave_sub();
3495 $main::lxdebug->enter_sub();
3499 map { $self->{_VAR_BACKUP}->{$_} = $self->{$_} if exists $self->{$_} } @vars;
3501 $main::lxdebug->leave_sub();
3505 $main::lxdebug->enter_sub();
3510 map { $self->{$_} = $self->{_VAR_BACKUP}->{$_} if exists $self->{_VAR_BACKUP}->{$_} } @vars;
3512 $main::lxdebug->leave_sub();
3515 sub prepare_for_printing {
3518 $self->{templates} ||= $::myconfig{templates};
3519 $self->{formname} ||= $self->{type};
3520 $self->{media} ||= 'email';
3522 die "'media' other than 'email' or 'file' is not supported yet" unless $self->{media} =~ m/^(?:email|file)$/;
3524 # set shipto from billto unless set
3525 my $has_shipto = any { $self->{"shipto$_"} } qw(name street zipcode city country contact);
3526 if (!$has_shipto && ($self->{type} =~ m/^(?:purchase_order|request_quotation)$/)) {
3527 $self->{shiptoname} = $::myconfig{company};
3528 $self->{shiptostreet} = $::myconfig{address};
3531 my $language = $self->{language} ? '_' . $self->{language} : '';
3533 my ($language_tc, $output_numberformat, $output_dateformat, $output_longdates);
3534 if ($self->{language_id}) {
3535 ($language_tc, $output_numberformat, $output_dateformat, $output_longdates) = AM->get_language_details(\%::myconfig, $self, $self->{language_id});
3537 $output_dateformat = $::myconfig{dateformat};
3538 $output_numberformat = $::myconfig{numberformat};
3539 $output_longdates = 1;
3542 # Retrieve accounts for tax calculation.
3543 IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
3545 if ($self->{type} =~ /_delivery_order$/) {
3546 DO->order_details();
3547 } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
3548 OE->order_details(\%::myconfig, $self);
3550 IS->invoice_details(\%::myconfig, $self, $::locale);
3553 # Chose extension & set source file name
3554 my $extension = 'html';
3555 if ($self->{format} eq 'postscript') {
3556 $self->{postscript} = 1;
3558 } elsif ($self->{"format"} =~ /pdf/) {
3560 $extension = $self->{'format'} =~ m/opendocument/i ? 'odt' : 'tex';
3561 } elsif ($self->{"format"} =~ /opendocument/) {
3562 $self->{opendocument} = 1;
3564 } elsif ($self->{"format"} =~ /excel/) {
3569 my $email_extension = '_email' if -f "$::myconfig{templates}/$self->{formname}_email${language}.${extension}";
3570 $self->{IN} = "$self->{formname}${email_extension}${language}.${extension}";
3573 $self->format_dates($output_dateformat, $output_longdates,
3574 qw(invdate orddate quodate pldate duedate reqdate transdate shippingdate deliverydate validitydate paymentdate datepaid
3575 transdate_oe deliverydate_oe employee_startdate employee_enddate),
3576 grep({ /^(?:datepaid|transdate_oe|reqdate|deliverydate|deliverydate_oe|transdate)_\d+$/ } keys(%{$self})));
3578 $self->reformat_numbers($output_numberformat, 2,
3579 qw(invtotal ordtotal quototal subtotal linetotal listprice sellprice netprice discount tax taxbase total paid),
3580 grep({ /^(?:linetotal|listprice|sellprice|netprice|taxbase|discount|paid|subtotal|total|tax)_\d+$/ } keys(%{$self})));
3582 $self->reformat_numbers($output_numberformat, undef, qw(qty price_factor), grep({ /^qty_\d+$/} keys(%{$self})));
3584 my ($cvar_date_fields, $cvar_number_fields) = CVar->get_field_format_list('module' => 'CT', 'prefix' => 'vc_');
3586 if (scalar @{ $cvar_date_fields }) {
3587 $self->format_dates($output_dateformat, $output_longdates, @{ $cvar_date_fields });
3590 while (my ($precision, $field_list) = each %{ $cvar_number_fields }) {
3591 $self->reformat_numbers($output_numberformat, $precision, @{ $field_list });
3598 my ($self, $dateformat, $longformat, @indices) = @_;
3600 $dateformat ||= $::myconfig{dateformat};
3602 foreach my $idx (@indices) {
3603 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3604 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3605 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $::locale->reformat_date(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i], $dateformat, $longformat);
3609 next unless defined $self->{$idx};
3611 if (!ref($self->{$idx})) {
3612 $self->{$idx} = $::locale->reformat_date(\%::myconfig, $self->{$idx}, $dateformat, $longformat);
3614 } elsif (ref($self->{$idx}) eq "ARRAY") {
3615 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3616 $self->{$idx}->[$i] = $::locale->reformat_date(\%::myconfig, $self->{$idx}->[$i], $dateformat, $longformat);
3622 sub reformat_numbers {
3623 my ($self, $numberformat, $places, @indices) = @_;
3625 return if !$numberformat || ($numberformat eq $::myconfig{numberformat});
3627 foreach my $idx (@indices) {
3628 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3629 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3630 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $self->parse_amount(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i]);
3634 next unless defined $self->{$idx};
3636 if (!ref($self->{$idx})) {
3637 $self->{$idx} = $self->parse_amount(\%::myconfig, $self->{$idx});
3639 } elsif (ref($self->{$idx}) eq "ARRAY") {
3640 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3641 $self->{$idx}->[$i] = $self->parse_amount(\%::myconfig, $self->{$idx}->[$i]);
3646 my $saved_numberformat = $::myconfig{numberformat};
3647 $::myconfig{numberformat} = $numberformat;
3649 foreach my $idx (@indices) {
3650 if ($self->{TEMPLATE_ARRAYS} && (ref($self->{TEMPLATE_ARRAYS}->{$idx}) eq "ARRAY")) {
3651 for (my $i = 0; $i < scalar(@{ $self->{TEMPLATE_ARRAYS}->{$idx} }); $i++) {
3652 $self->{TEMPLATE_ARRAYS}->{$idx}->[$i] = $self->format_amount(\%::myconfig, $self->{TEMPLATE_ARRAYS}->{$idx}->[$i], $places);
3656 next unless defined $self->{$idx};
3658 if (!ref($self->{$idx})) {
3659 $self->{$idx} = $self->format_amount(\%::myconfig, $self->{$idx}, $places);
3661 } elsif (ref($self->{$idx}) eq "ARRAY") {
3662 for (my $i = 0; $i < scalar(@{ $self->{$idx} }); $i++) {
3663 $self->{$idx}->[$i] = $self->format_amount(\%::myconfig, $self->{$idx}->[$i], $places);
3668 $::myconfig{numberformat} = $saved_numberformat;
3677 SL::Form.pm - main data object.
3681 This is the main data object of Lx-Office.
3682 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
3683 Points of interest for a beginner are:
3685 - $form->error - renders a generic error in html. accepts an error message
3686 - $form->get_standard_dbh - returns a database connection for the
3688 =head1 SPECIAL FUNCTIONS
3690 =head2 C<_store_value()>
3692 parses a complex var name, and stores it in the form.
3695 $form->_store_value($key, $value);
3697 keys must start with a string, and can contain various tokens.
3698 supported key structures are:
3701 simple key strings work as expected
3706 separating two keys by a dot (.) will result in a hash lookup for the inner value
3707 this is similar to the behaviour of java and templating mechanisms.
3709 filter.description => $form->{filter}->{description}
3711 3. array+hashref access
3713 adding brackets ([]) before the dot will cause the next hash to be put into an array.
3714 using [+] instead of [] will force a new array index. this is useful for recurring
3715 data structures like part lists. put a [+] into the first varname, and use [] on the
3718 repeating these names in your template:
3721 invoice.items[].parts_id
3725 $form->{invoice}->{items}->[
3739 using brackets at the end of a name will result in a pure array to be created.
3740 note that you mustn't use [+], which is reserved for array+hash access and will
3741 result in undefined behaviour in array context.
3743 filter.status[] => $form->{status}->[ val1, val2, ... ]
3745 =head2 C<update_business> PARAMS
3748 \%config, - config hashref
3749 $business_id, - business id
3750 $dbh - optional database handle
3752 handles business (thats customer/vendor types) sequences.
3754 special behaviour for empty strings in customerinitnumber field:
3755 will in this case not increase the value, and return undef.
3757 =head2 C<redirect_header> $url
3759 Generates a HTTP redirection header for the new C<$url>. Constructs an
3760 absolute URL including scheme, host name and port. If C<$url> is a
3761 relative URL then it is considered relative to Lx-Office base URL.
3763 This function C<die>s if headers have already been created with
3764 C<$::form-E<gt>header>.
3768 print $::form->redirect_header('oe.pl?action=edit&id=1234');
3769 print $::form->redirect_header('http://www.lx-office.org/');
3773 Generates a general purpose http/html header and includes most of the scripts
3774 ans stylesheets needed.
3776 Only one header will be generated. If the method was already called in this
3777 request it will not output anything and return undef. Also if no
3778 HTTP_USER_AGENT is found, no header is generated.
3780 Although header does not accept parameters itself, it will honor special
3781 hashkeys of its Form instance:
3789 If one of these is set, a http-equiv refresh is generated. Missing parameters
3790 default to 3 seconds and the refering url.
3796 If these are arrayrefs the contents will be inlined into the header.
3800 If true, a css snippet will be generated that sets the page in landscape mode.
3804 Used to override the default favicon.
3808 A html page title will be generated from this