Revert "Probleme bei Mwst-Wechsel"
[kivitendo-erp.git] / SL / Form.pm
index 392cd66..c6fd574 100644 (file)
@@ -56,6 +56,7 @@ use SL::DBUtils;
 use SL::DO;
 use SL::IC;
 use SL::IS;
+use SL::Layout::Dispatcher;
 use SL::Locale;
 use SL::Mailer;
 use SL::Menu;
@@ -447,47 +448,38 @@ sub create_http_response {
   return $output;
 }
 
-sub use_stylesheet {
-  my $self = shift;
-
-  $self->{stylesheet} = [ $self->{stylesheet} ] unless ref $self->{stylesheet} eq 'ARRAY';
-  $self->{stylesheet} = [ grep { -f                       }
-                          map  { m:^css/: ? $_ : "css/$_" }
-                          grep { $_                       }
-                               (@{ $self->{stylesheet} }, @_)
-                        ];
-
-  return @{ $self->{stylesheet} };
-}
-
-sub get_stylesheet_for_user {
-  my $css_path = 'css';
-  if (my $user_style = $::myconfig{stylesheet}) {
-    $user_style =~ s/\.css$//; # nuke trailing .css, this is a remnand of pre 2.7.0 stylesheet handling
-    $css_path = "$css_path/$user_style" if -d "$css_path/$user_style";
-  } else {
-    $css_path = "$css_path/lx-office-erp";
-  }
-  $::myconfig{css_path} = $css_path; # needed for menunew, FIXME: don't do this here
-
-  return $css_path;
-}
-
 sub header {
   $::lxdebug->enter_sub;
 
-  # extra code is currently only used by menuv3 and menuv4 to set their css.
-  # it is strongly deprecated, and will be changed in a future version.
   my ($self, %params) = @_;
   my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
   my @header;
 
   $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
 
-  my $css_path = $self->get_stylesheet_for_user;
+  if ($params{no_layout}) {
+    $::request->{layout} = SL::Layout::Dispatcher->new(style => 'none');
+  }
+
+  my $layout = $::request->{layout};
+
+  # standard css for all
+  # this should gradually move to the layouts that need it
+  $layout->use_stylesheet("$_.css") for qw(
+    main menu tabcontent list_accounts jquery.autocomplete
+    jquery.multiselect2side frame_header/header
+    ui-lightness/jquery-ui-1.8.12.custom
+    js/jscalendar/calendar-win2k-1
+  );
+
+  $layout->use_javascript("$_.js") for qw(
+    jquery common jscalendar/calendar jscalendar/lang/calendar-de
+    jscalendar/calendar-setup part_selection jquery-ui jquery.cookie jqModal
+    switchmenuframe
+  );
 
   $self->{favicon} ||= "favicon.ico";
-  $self->{titlebar}  = "$self->{title} - $self->{titlebar}" if $self->{title};
+  $self->{titlebar} = join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
 
   # build includes
   if ($self->{refresh_url} || $self->{refresh_time}) {
@@ -496,37 +488,18 @@ sub header {
     push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
   }
 
-  push @header, map { qq|<link rel="stylesheet" href="$_" type="text/css" title="Lx-Office stylesheet">| } $self->use_stylesheet;
-
-  push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
-  push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
-  push @header, map { qq|<script type="text/javascript" src="js/$_.js"></script>| }
-       qw(jquery common jscalendar/calendar jscalendar/lang/calendar-de jscalendar/calendar-setup part_selection jquery-ui jqModal switchmenuframe);
-  push @header, map { qq|<link rel="stylesheet" type="text/css" href="$css_path/$_.css">| }
-       qw(main menu tabcontent list_accounts jquery.autocomplete jquery.multiselect2side frame_header/header ui-lightness/jquery-ui-1.8.12.custom);
-  push @header, map { qq|<link rel="stylesheet" type="text/css" href="js/jscalendar/calendar-win2k-1.css">| }
+  push @header, map { qq|<link rel="stylesheet" href="$_" type="text/css" title="Stylesheet">| } $layout->stylesheets;
+  push @header, "<style type='text/css'>\@page { size:landscape; }</style> "                     if $self->{landscape};
+  push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>"         if -f $self->{favicon};
+  push @header, map { qq|<script type="text/javascript" src="$_"></script>| }                    $layout->javascripts;
+  push @header, $self->{javascript} if $self->{javascript};
   push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
-  push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
-  push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
-    join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
-
-  # if there is a title, we put some JavaScript in to the page, wich writes a
-  # meaningful title-tag for our frameset.
-  my $title_hack = '';
-  if ($self->{title}) {
-    $title_hack = qq|
-    <script type="text/javascript">
-    <!--
-      // Write a meaningful title-tag for our frameset.
-      top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
-    //-->
-    </script>|;
-  }
 
   my  %doctypes = (
     strict       => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|,
     transitional => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|,
     frameset     => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|,
+    html5        => qq|<!DOCTYPE html>|,
   );
 
   # output
@@ -550,15 +523,34 @@ EOT
    ***********************************************/
 
   </script>
-  $params{extra_code}
-  $title_hack
  </head>
+ <body>
 
 EOT
+  print $::request->{layout}->pre_content;
+  print $::request->{layout}->start_content;
+
+  $layout->header_done;
 
   $::lxdebug->leave_sub;
 }
 
+sub footer {
+  return unless $::request->{layout}->need_footer;
+
+  print $::request->{layout}->end_content;
+  print $::request->{layout}->post_content;
+
+  if (my @inline_scripts = $::request->{layout}->javascripts_inline) {
+    print "<script type='text/javascript'>@inline_scripts</script>\n";
+  }
+
+  print <<EOL
+ </body>
+</html>
+EOL
+}
+
 sub ajax_response_header {
   $main::lxdebug->enter_sub();
 
@@ -589,7 +581,7 @@ sub set_standard_title {
   $::lxdebug->enter_sub;
   my $self = shift;
 
-  $self->{titlebar}  = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
+  $self->{titlebar}  = "kivitendo " . $::locale->text('Version') . " $self->{version}";
   $self->{titlebar} .= "- $::myconfig{name}"   if $::myconfig{name};
   $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
 
@@ -642,11 +634,9 @@ sub _prepare_html_template {
   $additional_params->{"conf_latex_templates"}        = $::lx_office_conf{print_templates}->{latex};
   $additional_params->{"conf_opendocument_templates"} = $::lx_office_conf{print_templates}->{opendocument};
   $additional_params->{"conf_vertreter"}              = $::lx_office_conf{features}->{vertreter};
-  $additional_params->{"conf_show_best_before"}       = $::lx_office_conf{features}->{show_best_before};
   $additional_params->{"conf_parts_image_css"}        = $::lx_office_conf{features}->{parts_image_css};
   $additional_params->{"conf_parts_listing_images"}   = $::lx_office_conf{features}->{parts_listing_images};
   $additional_params->{"conf_parts_show_image"}       = $::lx_office_conf{features}->{parts_show_image};
-  $additional_params->{"conf_payments_changeable"}    = $::lx_office_conf{features}->{payments_changeable};
   $additional_params->{"INSTANCE_CONF"}               = $::instance_conf;
 
   if (my $debug_options = $::lx_office_conf{debug}{options}) {
@@ -689,6 +679,8 @@ sub init_template {
 
   return $self->template if $self->template;
 
+  # Force scripts/locales.pl to pick up the exception handling template.
+  # parse_html_template('generic/exception')
   return $self->template(Template->new({
      'INTERPOLATE'  => 0,
      'EVAL_PERL'    => 0,
@@ -698,6 +690,7 @@ sub init_template {
      'INCLUDE_PATH' => '.:templates/webpages',
      'COMPILE_EXT'  => '.tcc',
      'COMPILE_DIR'  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
+     'ERROR'        => 'templates/webpages/generic/exception.html',
   })) || die;
 }
 
@@ -782,10 +775,8 @@ sub write_trigger {
   # default
   my %dateformats = (
     "dd.mm.yy" => "%d.%m.%Y",
-    "dd-mm-yy" => "%d-%m-%Y",
     "dd/mm/yy" => "%d/%m/%Y",
     "mm/dd/yy" => "%m/%d/%Y",
-    "mm-dd-yy" => "%m-%d-%Y",
     "yyyy-mm-dd" => "%Y-%m-%d",
     );
 
@@ -859,39 +850,30 @@ sub format_amount {
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig, $amount, $places, $dash) = @_;
-  $dash ||= '';
+  $amount ||= 0;
+  $dash   ||= '';
+  my $neg = $amount < 0;
+  my $force_places = defined $places && $places >= 0;
 
-  if ($amount eq "") {
-    $amount = 0;
-  }
-
-  # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
+  $amount = $self->round_amount($amount, abs $places) if $force_places;
+  $amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa
 
-  my $neg = ($amount =~ s/^-//);
-  my $exp = ($amount =~ m/[e]/) ? 1 : 0;
+  # before the sprintf amount was a number, afterwards it's a string. because of the dynamic nature of perl
+  # this is easy to confuse, so keep in mind: before this comment no s///, m//, concat or other strong ops on
+  # $amount. after this comment no +,-,*,/,abs. it will only introduce subtle bugs.
 
-  if (defined($places) && ($places ne '')) {
-    if (not $exp) {
-      if ($places < 0) {
-        $amount *= 1;
-        $places *= -1;
-
-        if ($amount =~ /\.(\d+)/) {
-          my $actual_places = length $1;
-          $places = $actual_places if $actual_places > $places;
-        }
-      }
-    }
-    $amount = $self->round_amount($amount, $places);
-  }
+  $amount =~ s/0*$// unless defined $places && $places == 0;             # cull trailing 0s
 
   my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
-  my @p = split(/\./, $amount); # split amount at decimal point
-
-  $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
+  my @p = split(/\./, $amount);                                          # split amount at decimal point
 
+  $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1];                             # add 1,000 delimiters
   $amount = $p[0];
-  $amount .= $d[0].($p[1]||'').(0 x ($places - length ($p[1]||''))) if ($places || $p[1] ne '');
+  if ($places || $p[1]) {
+    $amount .= $d[0]
+            .  ( $p[1] || '' )
+            .  (0 x (abs($places || 0) - length ($p[1]||'')));           # pad the fraction
+  }
 
   $amount = do {
     ($dash =~ /-/)    ? ($neg ? "($amount)"                            : "$amount" )                              :
@@ -899,7 +881,6 @@ sub format_amount {
                         ($neg ? "-$amount"                             : "$amount" )                              ;
   };
 
-
   $main::lxdebug->leave_sub(2);
   return $amount;
 }
@@ -1111,9 +1092,10 @@ sub parse_template {
     'kivitendo-printXXXXXX',
     SUFFIX => '.' . ($suffix || 'tex'),
     DIR    => $userspath,
-    UNLINK => 1,
+    UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
   );
   close $temp_fh;
+  (undef, undef, $self->{template_meta}{tmpfile}) = File::Spec->splitpath( $self->{tmpfile} );
 
   if ($template->uses_temp_file() || $self->{media} eq 'email') {
     $out              = $self->{OUT};
@@ -1625,7 +1607,7 @@ sub get_exchangerate {
   my ($self, $dbh, $curr, $transdate, $fld) = @_;
   my ($query);
 
-  unless ($transdate) {
+  unless ($transdate && $curr) {
     $main::lxdebug->leave_sub();
     return 1;
   }
@@ -1758,10 +1740,9 @@ sub set_payment_options {
     $amounts{invtotal} = $self->{invtotal};
     $amounts{total}    = $self->{total};
   }
-  $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
-
   map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
 
+  $amounts{skonto_in_percent}  = 100.0 * $self->{percent_skonto};
   $amounts{skonto_amount}      = $amounts{invtotal} * $self->{percent_skonto};
   $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
   $amounts{total_wo_skonto}    = $amounts{total}    * (1 - $self->{percent_skonto});
@@ -1989,8 +1970,19 @@ sub get_duedate {
   $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
 
   my $dbh         = $self->get_standard_dbh($myconfig);
-  my $query       = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
-  my ($duedate)   = selectrow_query($self, $dbh, $query, $self->{payment_id});
+  my ($payment_id, $duedate);
+
+  if($self->{payment_id}) {
+    $payment_id = $self->{payment_id};
+  } elsif($self->{vendor_id}) {
+    my $query = 'SELECT payment_id FROM vendor WHERE id = ?';
+    ($payment_id) = selectrow_query($self, $dbh, $query, $self->{vendor_id});
+  }
+
+  if ($payment_id) {
+    my $query  = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
+    ($duedate) = selectrow_query($self, $dbh, $query, $payment_id);
+  }
 
   $main::lxdebug->leave_sub();
 
@@ -2540,13 +2532,17 @@ sub all_vc {
 
   $table = $table eq "customer" ? "customer" : "vendor";
 
-  my $query = qq|SELECT count(*) FROM $table WHERE NOT obsolete|;
+  # build selection list
+  # Hotfix für Bug 1837 - Besser wäre es alte Buchungsbelege
+  # OHNE Auswahlliste (reines Textfeld) zu laden. Hilft aber auch
+  # nicht für veränderbare Belege (oe, do, ...)
+  my $obsolete = "WHERE NOT obsolete" unless $self->{id};
+  my $query = qq|SELECT count(*) FROM $table $obsolete|;
   my ($count) = selectrow_query($self, $dbh, $query);
 
-  # build selection list
-  if ($count <= $myconfig->{vclimit}) {
+  if ($count < $myconfig->{vclimit}) {
     $query = qq|SELECT id, name, salesman_id
-                FROM $table WHERE NOT obsolete
+                FROM $table $obsolete
                 ORDER BY name|;
     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
   }
@@ -2557,7 +2553,8 @@ sub all_vc {
   # setup sales contacts
   $query = qq|SELECT e.id, e.name
               FROM employee e
-              WHERE (e.sales = '1') AND (NOT e.id = ?)|;
+              WHERE (e.sales = '1') AND (NOT e.id = ?)
+              ORDER BY name|;
   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
 
   # this is for self
@@ -2565,11 +2562,6 @@ sub all_vc {
        { id   => $self->{employee_id},
          name => $self->{employee} });
 
-  # sort the whole thing
-  @{ $self->{all_employees} } =
-    sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
-
-
     # prepare query for departments
     $query = qq|SELECT id, description
                 FROM department
@@ -2744,6 +2736,9 @@ sub create_links {
     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
   }
 
+  my $extra_columns = '';
+  $extra_columns   .= 'a.direct_debit, ' if $module eq 'AR';
+
   if ($self->{id}) {
     $query =
       qq|SELECT
@@ -2751,7 +2746,7 @@ sub create_links {
            a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
            a.intnotes, a.department_id, a.amount AS oldinvtotal,
            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
-           a.globalproject_id,
+           a.globalproject_id, ${extra_columns}
            c.name AS $table,
            d.description AS department,
            e.name AS employee
@@ -3584,6 +3579,29 @@ sub reformat_numbers {
   $::myconfig{numberformat} = $saved_numberformat;
 }
 
+sub layout {
+  my ($self) = @_;
+  $::lxdebug->enter_sub;
+
+  my %style_to_script_map = (
+    v3  => 'v3',
+    neu => 'new',
+  );
+
+  my $menu_script = $style_to_script_map{$::myconfig{menustyle}} || '';
+
+  package main;
+  require "bin/mozilla/menu$menu_script.pl";
+  package Form;
+  require SL::Controller::FrameHeader;
+
+
+  my $layout = SL::Controller::FrameHeader->new->action_header . ::render();
+
+  $::lxdebug->leave_sub;
+  return $layout;
+}
+
 1;
 
 __END__
@@ -3594,7 +3612,7 @@ SL::Form.pm - main data object.
 
 =head1 SYNOPSIS
 
-This is the main data object of Lx-Office.
+This is the main data object of kivitendo.
 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
 Points of interest for a beginner are:
 
@@ -3619,7 +3637,7 @@ will in this case not increase the value, and return undef.
 
 Generates a HTTP redirection header for the new C<$url>. Constructs an
 absolute URL including scheme, host name and port. If C<$url> is a
-relative URL then it is considered relative to Lx-Office base URL.
+relative URL then it is considered relative to kivitendo base URL.
 
 This function C<die>s if headers have already been created with
 C<$::form-E<gt>header>.