Fixed bug. (from r1005)
[kivitendo-erp.git] / SL / Form.pm
index 5fb58af..1c03e10 100644 (file)
@@ -37,6 +37,8 @@
 
 package Form;
 
+use HTML::Template;
+
 sub _input_to_hash {
   $main::lxdebug->enter_sub();
 
@@ -140,8 +142,7 @@ sub new {
   $self->{action} = lc $self->{action};
   $self->{action} =~ s/( |-|,|#)/_/g;
 
-  $self->{version}   = "2.1.2";
-  $self->{dbversion} = "2.1.2";
+  $self->{version}   = "2.3.0";
 
   $main::lxdebug->leave_sub();
 
@@ -195,37 +196,43 @@ sub unescape {
 sub quote {
   my ($self, $str) = @_;
 
-  if ($str && ! ref($str)) {
-    $str =~ s/"/"/g;
+  if ($str && !ref($str)) {
+    $str =~ s/\"/"/g;
   }
 
   $str;
 
 }
 
-
 sub unquote {
   my ($self, $str) = @_;
 
-  if ($str && ! ref($str)) {
-    $str =~ s/"/"/g;
+  if ($str && !ref($str)) {
+    $str =~ s/"/\"/g;
   }
 
   $str;
 
 }
 
 sub hide_form {
   my $self = shift;
 
   if (@_) {
-    for (@_) { print qq|<input type=hidden name="$_" value="|.$self->quote($self->{$_}).qq|">\n| }
+    for (@_) {
+      print qq|<input type=hidden name="$_" value="|
+        . $self->quote($self->{$_})
+        . qq|">\n|;
+    }
   } else {
     delete $self->{header};
-    for (sort keys %$self) { print qq|<input type=hidden name="$_" value="|.$self->quote($self->{$_}).qq|">\n| }
+    for (sort keys %$self) {
+      print qq|<input type=hidden name="$_" value="|
+        . $self->quote($self->{$_})
+        . qq|">\n|;
+    }
   }
-  
+
 }
 
 sub error {
@@ -237,17 +244,7 @@ sub error {
     $msg =~ s/\n/<br>/g;
 
     $self->header;
-
-    print qq|
-    <body>
-
-    <h2 class=error>Error!</h2>
-
-    <p><b>$msg</b>
-
-    </body>
-    </html>
-    |;
+    $self->show_generic_error($msg);
 
     die "Error: $msg\n";
 
@@ -395,6 +392,7 @@ function fokus(){document.$self->{fokus}.focus();}
 
     print qq|Content-Type: text/html
 
+<html>
 <head>
   <title>$self->{titlebar}</title>
   $stylesheet
@@ -412,14 +410,92 @@ function fokus(){document.$self->{fokus}.focus();}
   $main::lxdebug->leave_sub();
 }
 
-# write Trigger JavaScript-Code ($qty = 1 - only one Trigger)
+use Data::Dumper;
+sub parse_html_template {
+  $main::lxdebug->enter_sub();
+
+  my ($self, $file, $additional_params) = @_;
+  my $language;
+
+  if (!defined($main::myconfig) || !defined($main::myconfig{"countrycode"})) {
+    $language = $main::language;
+  } else {
+    $language = $main::myconfig{"countrycode"};
+  }
+
+  if (-f "templates/webpages/${file}_${language}.html") {
+    if ((-f ".developer") &&
+        (-f "templates/webpages/${file}_master.html") &&
+        ((stat("templates/webpages/${file}_master.html"))[9] >
+         (stat("templates/webpages/${file}_${language}.html"))[9])) {
+      my $info = "Developper information: templates/webpages/${file}_master.html is newer than the localized version.\n" .
+        "Please re-run 'locales.pl' in 'locale/${language}'.";
+      print(qq|<pre>$info</pre>|);
+      die($info);
+    }
+
+    $file = "templates/webpages/${file}_${language}.html";
+  } elsif (-f "templates/webpages/${file}.html") {
+    $file = "templates/webpages/${file}.html";
+  } else {
+    my $info = "Web page template '${file}' not found.\n" .
+      "Please re-run 'locales.pl' in 'locale/${language}'.";
+    print(qq|<pre>$info</pre>|);
+    die($info);
+  }
+
+  my $template = HTML::Template->new("filename" => $file,
+                                     "die_on_bad_params" => 0,
+                                     "strict" => 0,
+                                     "case_sensitive" => 1,
+                                     "loop_context_vars" => 1,
+                                     "global_vars" => 1);
+
+  $additional_params = {} unless ($additional_params);
+  if ($self->{"DEBUG"}) {
+    $additional_params->{"DEBUG"} = $self->{"DEBUG"};
+  }
+
+  if ($additional_params->{"DEBUG"}) {
+    $additional_params->{"DEBUG"} =
+      "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
+  }
+
+  my @additional_param_names = keys(%{$additional_params});
+
+  foreach my $key ($template->param()) {
+    if (grep(/^${key}$/, @additional_param_names)) {
+      $template->param($key => $additional_params->{$key});
+    } else {
+      $template->param($key => $self->{$key});
+    }
+  }
+
+  my $output = $template->output();
+
+  $main::lxdebug->leave_sub();
+
+  return $output;
+}
+
+sub show_generic_error {
+  my ($self, $error, $title) = @_;
+
+  my $add_params = {};
+  $add_params->{"title"} = $title if ($title);
+  $self->{"label_error"} = $error;
+
+  print($self->parse_html_template("generic/error", $add_params));
+}
+
+# write Trigger JavaScript-Code ($qty = quantity of Triggers)
+# changed it to accept an arbitrary number of triggers - sschoeling
 sub write_trigger {
   $main::lxdebug->enter_sub();
 
-  my ($self,         $myconfig, $qty,
-      $inputField_1, $align_1,  $button_1,
-      $inputField_2, $align_2,  $button_2)
-    = @_;
+  my $self     = shift;
+  my $myconfig = shift;
+  my $qty      = shift;
 
   # set dateform for jsscript
   # default
@@ -448,35 +524,21 @@ sub write_trigger {
     }
   }
 
-  $trigger_1 = qq|
+  while ($#_ >= 2) {
+    push @triggers, qq|
        Calendar.setup(
       {
-      inputField : "$inputField_1",
+      inputField : "| . (shift) . qq|",
       ifFormat :"$ifFormat",
-      align : "$align_1", 
-      button : "$button_1"
+      align : "| .  (shift) . qq|", 
+      button : "| . (shift) . qq|"
       }
       );
        |;
-
-  if ($qty == 2) {
-    $trigger_2 = qq|
-       Calendar.setup(
-       {
-      inputField : "$inputField_2",
-      ifFormat :"$ifFormat",
-      align : "$align_2", 
-      button : "$button_2"
-      }
-      );
-        |;
   }
   $jsscript = qq|
        <script type="text/javascript">
-       <!--
-       $trigger_1
-       $trigger_2
-        //-->
+       <!--| . join("", @triggers) . qq|//-->
         </script>
         |;
 
@@ -520,13 +582,19 @@ sub format_amount {
 
   my ($self, $myconfig, $amount, $places, $dash) = @_;
 
+  #Workaround for $format_amount calls without $places
+  if (!defined $places) {
+    (my $dec) = ($amount =~ /\.(\d+)/);
+    $places = length $dec;
+  }
+
   if ($places =~ /\d/) {
     $amount = $self->round_amount($amount, $places);
   }
 
   # is the amount negative
   my $negative = ($amount < 0);
-  my $fillup = "";
+  my $fillup   = "";
 
   if ($amount != 0) {
     if ($myconfig->{numberformat} && ($myconfig->{numberformat} ne '1000.00'))
@@ -540,19 +608,19 @@ sub format_amount {
         $amount =~ s/\d{3,}?/$&,/g;
         $amount =~ s/,$//;
         $amount = join '', reverse split //, $amount;
-        $amount .= "\.$dec".$fillup if ($places ne '' && $places*1 != 0);
+        $amount .= "\.$dec" . $fillup if ($places ne '' && $places * 1 != 0);
       }
 
       if ($myconfig->{numberformat} eq '1.000,00') {
         $amount =~ s/\d{3,}?/$&./g;
         $amount =~ s/\.$//;
         $amount = join '', reverse split //, $amount;
-        $amount .= ",$dec".$fillup if ($places ne '' && $places*1 != 0);
+        $amount .= ",$dec" . $fillup if ($places ne '' && $places * 1 != 0);
       }
 
       if ($myconfig->{numberformat} eq '1000,00') {
         $amount = "$whole";
-        $amount .= ",$dec" .$fillup if ($places ne '' && $places*1 != 0);
+        $amount .= ",$dec" . $fillup if ($places ne '' && $places * 1 != 0);
       }
 
       if ($dash =~ /-/) {
@@ -584,17 +652,53 @@ sub parse_amount {
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig, $amount) = @_;
+  $main::lxdebug->message(LXDebug::DEBUG2, "Start amount: $amount");
 
-  if (!(substr($amount, -3, 1) eq ".")) {
-    if (   ($myconfig->{numberformat} eq '1.000,00')
-        || ($myconfig->{numberformat} eq '1000,00')) {
-      $amount =~ s/\.//g;
-      $amount =~ s/,/\./;
-    }
+  if ($myconfig->{in_numberformat} == 1) {
+
+    # Extra input number format 1000.00 or 1000,00
+    $main::lxdebug->message(LXDebug::DEBUG2,
+              "in_numberformat: " . $main::locale->text('1000,00 or 1000.00'));
+    $amount =~ s/,/\./g;
+
+    #$main::lxdebug->message(LXDebug::DEBUG2, "1.Parsed Number: $amount") if ($amount);
+    $amount = scalar reverse $amount;
+
+    #$main::lxdebug->message(LXDebug::DEBUG2, "2.Parsed Number: $amount") if ($amount);
+    $amount =~ s/\./DOT/;
+
+    #$main::lxdebug->message(LXDebug::DEBUG2, "3.Parsed Number: $amount") if ($amount);
+    $amount =~ s/\.//g;
+
+    #$main::lxdebug->message(LXDebug::DEBUG2, "4.Parsed Number: $amount") if ($amount);
+    $amount =~ s/DOT/\./;
+
+    #$main::lxdebug->message(LXDebug::DEBUG2, "5.Parsed Number:" . $amount) if ($amount);
+    $amount = scalar reverse $amount;
+    $main::lxdebug->message(LXDebug::DEBUG2,
+                            "Parsed amount:" . $amount . "\n");
 
-    $amount =~ s/,//g;
+    return ($amount * 1);
+
+  }
+  $main::lxdebug->message(LXDebug::DEBUG2,
+              "in_numberformat: " . $main::locale->text('equal Outputformat'));
+  $main::lxdebug->message(LXDebug::DEBUG2,
+                          " = numberformat: $myconfig->{numberformat}");
+  if (   ($myconfig->{numberformat} eq '1.000,00')
+      || ($myconfig->{numberformat} eq '1000,00')) {
+    $amount =~ s/\.//g;
+    $amount =~ s/,/\./;
+  }
+
+  if ($myconfig->{numberformat} eq "1'000.00") {
+    $amount =~ s/\'//g;
   }
 
+  $amount =~ s/,//g;
+
+  $main::lxdebug->message(LXDebug::DEBUG2, "Parsed amount:" . $amount . "\n")
+    if ($amount);
   $main::lxdebug->leave_sub();
 
   return ($amount * 1);
@@ -608,20 +712,18 @@ sub round_amount {
 
   # Rounding like "Kaufmannsrunden"
   # Descr. http://de.wikipedia.org/wiki/Rundung
-  # Inspired by 
+  # Inspired by
   # http://www.perl.com/doc/FAQs/FAQ/oldfaq-html/Q4.13.html
   # Solves Bug: 189
   # Udo Spallek
-  $amount       = $amount * (10 ** ($places));
-  $round_amount = int($amount + .5 * ($amount <=> 0))/(10**($places));
+  $amount = $amount * (10**($places));
+  $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
 
   $main::lxdebug->leave_sub();
 
   return $round_amount;
-  
-}
-
 
+}
 
 sub parse_template {
   $main::lxdebug->enter_sub();
@@ -681,7 +783,16 @@ sub parse_template {
 
     $par = "";
     $var = $_;
-
+    # Switch <%analyse%> for template checking
+    # If <%analyse%> is set in the template, you'll find the 
+    # parsed output in the user Directory for analysing
+    # Latex errors
+    # <%analyse%> is a switch (allways off, on if set), not a Variable
+    # Set $form->{analysing}="" for system state: never analyse.
+    # Set $form->{analysing}="1" for system state: ever analyse.
+    $self->{analysing} = "1" if (/<%analyse%>/ && !defined $self->{analysing});    
+    
     $two_passes = 1 if (/\\pageref/);
 
     # { Moritz Bunkus
@@ -737,8 +848,11 @@ sub parse_template {
           }
 
           # Yes we need a manual page break -- or the user has forced one
-          if ((($current_line + $lines) > $lpp) ||
-              ($self->{"_forced_pagebreaks"} && grep(/^${current_row}$/, @{$self->{"_forced_pagebreaks"}}))) {
+          if (
+             (($current_line + $lines) > $lpp)
+             || ($self->{"_forced_pagebreaks"}
+               && grep(/^${current_row}$/, @{ $self->{"_forced_pagebreaks"} }))
+            ) {
             my $pb = $pagebreak;
 
             # replace the special variables <%sumcarriedforward%>
@@ -825,12 +939,12 @@ sub parse_template {
     # check for <%include filename%>
     if (/\s*<%include /) {
 
-      # get the filename
+      # get the directory/filename
       chomp $var;
       $var =~ s/\s*<%include (.+?)%>/$1/;
 
-      # mangle filename
-      $var =~ s/(\/|\.\.)//g;
+      # mangle filename on basedir
+      $var =~ s/^(\/|\.\.)//g;
 
       # prevent the infinite loop!
       next if ($self->{"$var"});
@@ -846,6 +960,7 @@ sub parse_template {
     }
 
     s/<%(.+?)%>/$self->{$1}/g;
+    s/<nobr><\/nobr>/&nbsp;/g;
     print OUT;
   }
 
@@ -1002,12 +1117,14 @@ sub cleanup {
     close(FH);
   }
 
-  if ($self->{tmpfile}) {
+  if ($self->{analysing} eq "") {
+    if ($self->{tmpfile}) {
 
-    # strip extension
-    $self->{tmpfile} =~ s/\.\w+$//g;
-    my $tmpfile = $self->{tmpfile};
-    unlink(<$tmpfile.*>);
+      # strip extension
+      $self->{tmpfile} =~ s/\.\w+$//g;  
+      my $tmpfile = $self->{tmpfile};
+      unlink(<$tmpfile.*>);
+    }
   }
 
   chdir("$self->{cwd}");
@@ -1024,7 +1141,7 @@ sub format_string {
   my %unique_fields;
 
   %unique_fields = map({ $_ => 1 } @fields);
-  @fields = keys(%unique_fields);
+  @fields        = keys(%unique_fields);
 
   foreach my $field (@fields) {
     next unless ($self->{$field} =~ /\<pagebreak\>/);
@@ -1052,7 +1169,7 @@ sub format_string {
         '&', quotemeta('\n'), '
 ',
         '"', '\$', '%', '_', '#', quotemeta('^'),
-        '{', '}',  '<', '>', '£', "\r"
+        '{', '}',  '<', '>', '£', "\r", '²'
       ]
     },
     'html' => {
@@ -1078,6 +1195,7 @@ sub format_string {
       '
 '          => '\newline ',
       '£'  => '\pounds ',
+      '²'  => '$^2$',
       "\r" => ""
     });
 
@@ -1277,7 +1395,7 @@ sub get_exchangerate {
   $main::lxdebug->enter_sub();
 
   my ($self, $dbh, $curr, $transdate, $fld) = @_;
-  
+
   unless ($transdate) {
     $main::lxdebug->leave_sub();
     return "";
@@ -1330,8 +1448,9 @@ sub add_shipto {
   my ($self, $dbh, $id) = @_;
 ##LINET
   my $shipto;
-  foreach
-    my $item (qw(name department_1 department_2 street zipcode city country contact phone fax email)) {
+  foreach my $item (
+    qw(name department_1 department_2 street zipcode city country contact phone fax email)
+    ) {
     if ($self->{"shipto$item"}) {
       $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
     }
@@ -1339,7 +1458,8 @@ sub add_shipto {
   }
 
   if ($shipto) {
-    my $query = qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2, shiptostreet,
+    my $query =
+      qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2, shiptostreet,
                    shiptozipcode, shiptocity, shiptocountry, shiptocontact,
                   shiptophone, shiptofax, shiptoemail) VALUES ($id,
                   '$self->{shiptoname}', '$self->{shiptodepartment_1}', '$self->{shiptodepartment_2}', '$self->{shiptostreet}',
@@ -1598,8 +1718,7 @@ sub create_links {
   my %xkeyref = ();
 
   # now get the account numbers
-  $query =
-    qq|SELECT c.accno, SUBSTRING(c.description,1,50) as description, c.link, c.taxkey_id
+  $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id
               FROM chart c
              WHERE c.link LIKE '%$module%'
              ORDER BY c.accno|;
@@ -1672,7 +1791,7 @@ sub create_links {
                LEFT Join tax t ON (a.taxkey = t.taxkey)
                WHERE a.trans_id = $self->{id}
                AND a.fx_transaction = '0'
-               ORDER BY a.transdate|;
+               ORDER BY a.oid,a.transdate|;
     $sth = $dbh->prepare($query);
     $sth->execute || $self->dberror($query);
 
@@ -1682,12 +1801,17 @@ sub create_links {
     $self->{exchangerate} =
       $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate},
                               $fld);
+    my $index = 0;
 
     # store amounts in {acc_trans}{$key} for multiple accounts
     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
       $ref->{exchangerate} =
         $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate},
                                 $fld);
+      if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
+        $index++;
+      }
+      $ref->{index} = $index;
 
       push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
     }
@@ -1765,10 +1889,9 @@ sub lastname_used {
     $where = "quotation = '1'";
   }
 
-  my $query = qq|SELECT id FROM $arap
-                 WHERE id IN (SELECT MAX(id) FROM $arap
+  my $query = qq|SELECT MAX(id) FROM $arap
                              WHERE $where
-                             AND ${table}_id > 0)|;
+                             AND ${table}_id > 0|;
   my $sth = $dbh->prepare($query);
   $sth->execute || $self->dberror($query);
 
@@ -2121,7 +2244,6 @@ sub get_partsgroup {
   $main::lxdebug->leave_sub();
 }
 
-
 sub get_pricegroup {
   $main::lxdebug->enter_sub();
 
@@ -2153,92 +2275,95 @@ sub get_pricegroup {
   $main::lxdebug->leave_sub();
 }
 
-
 sub audittrail {
   my ($self, $dbh, $myconfig, $audittrail) = @_;
-  
-# table, $reference, $formname, $action, $id, $transdate) = @_;
+
+  # table, $reference, $formname, $action, $id, $transdate) = @_;
 
   my $query;
   my $rv;
   my $disconnect;
 
-  if (! $dbh) {
-    $dbh = $self->dbconnect($myconfig);
+  if (!$dbh) {
+    $dbh        = $self->dbconnect($myconfig);
     $disconnect = 1;
   }
-    
+
   # if we have an id add audittrail, otherwise get a new timestamp
-  
+
   if ($audittrail->{id}) {
-    
+
     $query = qq|SELECT audittrail FROM defaults|;
-    
+
     if ($dbh->selectrow_array($query)) {
       my ($null, $employee_id) = $self->get_employee($dbh);
 
       if ($self->{audittrail} && !$myconfig) {
-       chop $self->{audittrail};
-       
-       my @a = split /\|/, $self->{audittrail};
-       my %newtrail = ();
-       my $key;
-       my $i;
-       my @flds = qw(tablename reference formname action transdate);
-
-       # put into hash and remove dups
-       while (@a) {
-         $key = "$a[2]$a[3]";
-         $i = 0;
-         $newtrail{$key} = { map { $_ => $a[$i++] } @flds };
-         splice @a, 0, 5;
-       }
-       
-       $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+        chop $self->{audittrail};
+
+        my @a = split /\|/, $self->{audittrail};
+        my %newtrail = ();
+        my $key;
+        my $i;
+        my @flds = qw(tablename reference formname action transdate);
+
+        # put into hash and remove dups
+        while (@a) {
+          $key = "$a[2]$a[3]";
+          $i   = 0;
+          $newtrail{$key} = { map { $_ => $a[$i++] } @flds };
+          splice @a, 0, 5;
+        }
+
+        $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
                    formname, action, employee_id, transdate)
                    VALUES ($audittrail->{id}, ?, ?,
                    ?, ?, $employee_id, ?)|;
-       my $sth = $dbh->prepare($query) || $self->dberror($query);
-
-       foreach $key (sort { $newtrail{$a}{transdate} cmp $newtrail{$b}{transdate} } keys %newtrail) {
-         $i = 1;
-         for (@flds) { $sth->bind_param($i++, $newtrail{$key}{$_}) }
-
-         $sth->execute || $self->dberror;
-         $sth->finish;
-       }
+        my $sth = $dbh->prepare($query) || $self->dberror($query);
+
+        foreach $key (
+          sort {
+            $newtrail{$a}{transdate} cmp $newtrail{$b}{transdate}
+          } keys %newtrail
+          ) {
+          $i = 1;
+          for (@flds) { $sth->bind_param($i++, $newtrail{$key}{$_}) }
+
+          $sth->execute || $self->dberror;
+          $sth->finish;
+        }
       }
 
-     
       if ($audittrail->{transdate}) {
-       $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+        $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
                    formname, action, employee_id, transdate) VALUES (
                    $audittrail->{id}, '$audittrail->{tablename}', |
-                   .$dbh->quote($audittrail->{reference}).qq|,
+          . $dbh->quote($audittrail->{reference}) . qq|,
                    '$audittrail->{formname}', '$audittrail->{action}',
                    $employee_id, '$audittrail->{transdate}')|;
       } else {
-       $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+        $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
                    formname, action, employee_id) VALUES ($audittrail->{id},
                    '$audittrail->{tablename}', |
-                   .$dbh->quote($audittrail->{reference}).qq|,
+          . $dbh->quote($audittrail->{reference}) . qq|,
                    '$audittrail->{formname}', '$audittrail->{action}',
                    $employee_id)|;
       }
       $dbh->do($query);
     }
   } else {
-    
+
     $query = qq|SELECT current_timestamp FROM defaults|;
     my ($timestamp) = $dbh->selectrow_array($query);
 
-    $rv = "$audittrail->{tablename}|$audittrail->{reference}|$audittrail->{formname}|$audittrail->{action}|$timestamp|";
+    $rv =
+      "$audittrail->{tablename}|$audittrail->{reference}|$audittrail->{formname}|$audittrail->{action}|$timestamp|";
   }
 
   $dbh->disconnect if $disconnect;
-  
+
   $rv;
-  
+
 }
 
 package Locale;