Grundstein für Gewicht in Auftrag/Lieferschein/Rechnung
[kivitendo-erp.git] / bin / mozilla / io.pl
index 9dd8e8c..ca3d050 100644 (file)
@@ -38,7 +38,7 @@
 
 use Carp;
 use CGI;
-use CGI::Ajax;
+use List::MoreUtils qw(any uniq);
 use List::Util qw(min max first);
 
 use SL::CVar;
@@ -47,6 +47,10 @@ use SL::CT;
 use SL::IC;
 use SL::IO;
 
+use SL::DB::Language;
+use SL::DB::Printer;
+use SL::Helper::Flash;
+
 require "bin/mozilla/common.pl";
 
 use strict;
@@ -113,11 +117,11 @@ sub display_row {
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
-  my $cgi      = $main::cgi;
+  my $cgi      = $::request->{cgi};
 
   my $numrows = shift;
 
-  my ($readonly, $stock_in_out, $stock_in_out_title);
+  my ($stock_in_out, $stock_in_out_title);
 
   my $is_purchase        = (first { $_ eq $form->{type} } qw(request_quotation purchase_order purchase_delivery_order)) || ($form->{script} eq 'ir.pl');
   my $show_min_order_qty =  first { $_ eq $form->{type} } qw(request_quotation purchase_order);
@@ -125,8 +129,6 @@ sub display_row {
   my $is_s_p_order       = (first { $_ eq $form->{type} } qw(sales_order purchase_order));
 
   if ($is_delivery_order) {
-    $readonly             = ' readonly' if ($form->{closed});
-
     if ($form->{type} eq 'sales_delivery_order') {
       $stock_in_out_title = $locale->text('Release From Stock');
       $stock_in_out       = 'out';
@@ -139,7 +141,7 @@ sub display_row {
   }
 
   # column_index
-  my @header_sort = qw(runningnumber partnumber description ship qty unit sellprice_pg sellprice discount linetotal);
+  my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice_pg sellprice discount linetotal);
   my @HEADER = (
     {  id => 'runningnumber', width => 5,     value => $locale->text('No.'),                  display => 1, },
     {  id => 'partnumber',    width => 8,     value => $locale->text('Number'),               display => 1, },
@@ -148,6 +150,7 @@ sub display_row {
     {  id => 'qty',           width => 5,     value => $locale->text('Qty'),                  display => 1, },
     {  id => 'price_factor',  width => 5,     value => $locale->text('Price Factor'),         display => !$is_delivery_order, },
     {  id => 'unit',          width => 5,     value => $locale->text('Unit'),                 display => 1, },
+    {  id => 'weight',        width => 5,     value => $locale->text('Weight'),               display => 1, },
     {  id => 'serialnr',      width => 10,    value => $locale->text('Serial No.'),           display => 0, },
     {  id => 'projectnr',     width => 10,    value => $locale->text('Project'),              display => 0, },
     {  id => 'sellprice',     width => 15,    value => $locale->text('Price'),                display => !$is_delivery_order, },
@@ -185,12 +188,13 @@ sub display_row {
   my $deliverydate  = $locale->text('Required by');
 
   # special alignings
-  my %align  = map { $_ => 'right' } qw(qty ship right sellprice_pg discount linetotal stock_in_out);
+  my %align  = map { $_ => 'right' } qw(qty ship right sellprice_pg discount linetotal stock_in_out weight);
   my %nowrap = map { $_ => 1 }       qw(description unit);
 
   $form->{marge_total}           = 0;
   $form->{sellprice_total}       = 0;
   $form->{lastcost_total}        = 0;
+  $form->{totalweight}           = 0;
   my %projectnumber_labels = ();
   my @projectnumber_values = ("");
 
@@ -203,6 +207,8 @@ sub display_row {
   _update_ship() if ($is_s_p_order);
   _update_custom_variables();
 
+  my $totalweight = 0;
+  my $defaults = AM->get_defaults();
   # rows
 
   my @ROWS;
@@ -225,6 +231,8 @@ sub display_row {
     # adjust prices by unit, ignore if pricegroup changed
     if ((!$form->{"prices_$i"}) || ($form->{"new_pricegroup_$i"} == $form->{"old_pricegroup_$i"})) {
         $form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
+        $form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
+        $form->{"weight_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
         $form->{"unit_old_$i"}   = $form->{"selected_unit_$i"};
     }
     my $this_unit = $form->{"unit_$i"};
@@ -247,7 +255,10 @@ sub display_row {
     $column_data{"unit"} = AM->unit_select_html($all_units, "unit_$i", $this_unit, $form->{"id_$i"} ? $form->{"unit_$i"} : undef);
 # / unit ending
 
+#count the max of decimalplaces of sellprice and lastcost, so the same number of decimalplaces
+#is shown for lastcost and sellprice.
     my $decimalplaces = ($form->{"sellprice_$i"} =~ /\.(\d+)/) ? max 2, length $1 : 2;
+    $decimalplaces = ($form->{"lastcost_$i"} =~ /\.(\d+)/) ? max $decimalplaces, length $1 : $decimalplaces;
 
     my $price_factor   = $price_factors{$form->{"price_factor_id_$i"}} || 1;
     my $discount       = $form->round_amount($form->{"qty_$i"} * $form->{"sellprice_$i"} *        $form->{"discount_$i"}  / 100 / $price_factor, 2);
@@ -278,12 +289,16 @@ sub display_row {
     }
 
     # build in drop down list for pricesgroups
+    # $sellprice_value setzt den Wert etwas unabhängiger von der Darstellung.
+    # Hintergrund: Preisgruppen werden hier überprüft und neu berechnet.
+    # Vorher wurde der ganze cgi->textfield Block zweimal identisch eingebaut, dass passiert
+    # jetzt nach der Abfrage.
+    my $sellprice_value;
     if ($form->{"prices_$i"}) {
       $column_data{sellprice_pg} = qq|<select name="sellprice_pg_$i" style="width: 8em">$form->{"prices_$i"}</select>|;
-      $column_data{sellprice}    = $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => 'check_right_number_format(this)', -value =>
-                                   (($form->{"new_pricegroup_$i"} != $form->{"old_pricegroup_$i"})
+      $sellprice_value           =($form->{"new_pricegroup_$i"} != $form->{"old_pricegroup_$i"})
                                       ? $form->format_amount(\%myconfig, $form->{"price_new_$i"}, $decimalplaces)
-                                      : $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces)));
+                                      : $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
     } else {
       # for last row and report
       # set pricegroup drop down list from report menu
@@ -296,14 +311,27 @@ sub display_row {
       } else {
         $column_data{sellprice_pg} = qq|&nbsp;|;
       }
-      $column_data{sellprice} = $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value =>
-                                                $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces));
+      $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
 
     }
-    $column_data{discount}    = $cgi->textfield(-name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}));
+    # Falls der Benutzer die Preise nicht anpassen sollte, wird das entsprechende
+    # Textfield auf readonly gesetzt. Anm. von Sven: Manipulation der Preise ist
+    # immer noch möglich, konsequenterweise sollten diese NUR aus der Datenbank
+    # geholt werden.
+    my $edit_prices = $main::auth->assert('edit_prices', 1);
+    $column_data{sellprice} = (!$edit_prices)
+                                ? $cgi->textfield(-readonly => "readonly",
+                                                  -name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value)
+                                : $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
+    $column_data{discount}    = (!$edit_prices)
+                                  ? $cgi->textfield(-readonly => "readonly",
+                                                    -name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}))
+                                  : $cgi->textfield(-name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}));
     $column_data{linetotal}   = $form->format_amount(\%myconfig, $linetotal, 2);
     $column_data{bin}         = $form->{"bin_$i"};
 
+    $column_data{weight}      = $form->format_amount(\%myconfig, $form->{"qty_$i"} * $form->{"weight_$i"}, 3) . ' ' . $defaults->{weightunit};
+
     if ($is_delivery_order) {
       $column_data{stock_in_out} =  calculate_stock_in_out($i);
     }
@@ -326,8 +354,20 @@ sub display_row {
     $form->{"marge_percent_$i"} = 0;
 
     my $marge_color;
-    my $real_sellprice           = $linetotal;
-    my $real_lastcost            = $form->{"lastcost_$i"} * $form->{"qty_$i"} / ( $form->{"marge_price_factor_$i"} || 1 );
+    my $real_sellprice;
+    if ( $form->{taxincluded} and $form->{"qty_$i"} * 1  and $form->{$form->{"taxaccounts_$i"} . "_rate"} * 1) {
+      # if we use taxincluded we need to calculate the marge from the net_value
+      # all the marge calculations are based on linetotal which we need to
+      # convert to net first
+
+      # there is no direct form value for the tax_rate of the item, but
+      # form->{taxaccounts_$i} gives the tax account (e.g. 3806) and 3806_rate
+      # gives the tax percentage (e.g. 0.19)
+      $real_sellprice = $linetotal / (1 + $form->{$form->{"taxaccounts_$i"} . "_rate"});
+    } else {
+      $real_sellprice            = $linetotal;
+    };
+    my $real_lastcost            = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / $price_factor, 2);
     my $marge_percent_warn       = $myconfig{marge_percent_warn} * 1 || 15;
     my $marge_adjust_credit_note = $form->{type} eq 'credit_note' ? -1 : 1;
 
@@ -349,17 +389,20 @@ sub display_row {
         &nbsp;<b>%s</b> <input size="5" name="lastcost_$i" value="%s">|,
                    $marge_color, $locale->text('Ertrag'),$form->{"marge_absolut_$i"}, $form->{"marge_percent_$i"},
                    $locale->text('LP'), $form->format_amount(\%myconfig, $form->{"listprice_$i"}, 2),
-                   $locale->text('EK'), $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, 2) }
-      if $form->{"id_$i"} && ($form->{type} =~ /^sales_/ ||  $form->{type} =~ /invoice/) && !$is_delivery_order;
+                   $locale->text('EK'), $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces) }
+      if $form->{"id_$i"} && ($form->{type} =~ /^sales_/ ||  $form->{type} =~ /invoice/ || $form->{type} =~ /^credit_note$/ ) && !$is_delivery_order;
 
     $form->{"listprice_$i"} = $form->format_amount(\%myconfig, $form->{"listprice_$i"}, 2)
       if $form->{"id_$i"} && ($form->{type} =~ /^sales_/ ||  $form->{type} =~ /invoice/) ;
 # / marge calculations ending
 
+# Calculate total weight
+    $totalweight += ($form->{"qty_$i"} * $form->{"weight_$i"});
+
 # calculate onhand
     if ($form->{"id_$i"}) {
       my $part         = IC->get_basic_part_info(id => $form->{"id_$i"});
-      my $onhand_color = 'color="#ff0000"' if  $part->{onhand} < $part->{rop};
+      my $onhand_color = $part->{onhand} < $part->{rop} ? 'color="#ff0000"' : '';
       push @ROW2, { value => sprintf "<b>%s</b> <font %s>%s %s</font>",
                       $locale->text('On Hand'),
                       $onhand_color,
@@ -373,7 +416,7 @@ sub display_row {
 
     if ($is_delivery_order) {
       map { $form->{"${_}_${i}"} = $form->format_amount(\%myconfig, $form->{"${_}_${i}"}) } qw(sellprice discount lastcost);
-      push @hidden_vars, qw(sellprice discount not_discountable price_factor_id lastcost);
+      push @hidden_vars, qw(sellprice discount not_discountable price_factor_id lastcost pricegroup_id);
       push @hidden_vars, "stock_${stock_in_out}_sum_qty", "stock_${stock_in_out}";
     }
 
@@ -383,7 +426,7 @@ sub display_row {
           map { ($cgi->hidden("-name" => $_, "-value" => $form->{$_})); } map { $_."_$i" }
             (qw(orderitems_id bo pricegroup_old price_old id inventory_accno bin partsgroup partnotes
                 income_accno expense_accno listprice assembly taxaccounts ordnumber transdate cusordnumber
-                longdescription basefactor marge_absolut marge_percent marge_price_factor), @hidden_vars)
+                longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars)
     );
 
     map { $form->{"${_}_base"} += $linetotal } (split(/ /, $form->{"taxaccounts_$i"}));
@@ -396,6 +439,8 @@ sub display_row {
     push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, };
   }
 
+  $form->{totalweight} = $totalweight;
+
   print $form->parse_html_template('oe/sales_order', { ROWS   => \@ROWS,
                                                        HEADER => \@HEADER,
                                                      });
@@ -415,7 +460,7 @@ sub set_pricegroup {
 
   my $form     = $main::form;
   my $locale   = $main::locale;
-  my $cgi      = $main::cgi;
+  my $cgi      = $::request->{cgi};
 
   _check_io_auth();
 
@@ -456,7 +501,7 @@ sub select_item {
   } @{ $::form->{item_list} };
 
   # delete action variable
-  delete @{$::form}{qw(action item_list header)};
+  delete @{$::form}{qw(action item_list)};
 
   print $::form->parse_html_template('io/select_item', { PREVIOUS_FORM => $previous_form,
                                                          MODE          => $mode,
@@ -528,12 +573,16 @@ sub item_selected {
       $form->{"sellprice_$i"} =
         $form->round_amount($form->{"sellprice_$i"}, $decimalplaces);
     }
+
+    # tradediscount
+    if ($::form->{tradediscount}) {
+      $::form->{"sellprice_$i"} *= 1 - $::form->{tradediscount};
+    }
   }
 
   map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
     qw(sellprice listprice weight);
 
-  $form->{sellprice} += ($form->{"sellprice_$i"} * $form->{"qty_$i"});
   $form->{weight}    += ($form->{"weight_$i"} * $form->{"qty_$i"});
 
   if ($form->{"not_discountable_$i"}) {
@@ -765,6 +814,7 @@ sub validate_items {
 
   # check if items are valid
   if ($form->{rowcount} == 1) {
+    flash('warning', $::locale->text('The action you\'ve chosen has not been executed because the document does not contain any item yet.'));
     &update;
     ::end_of_request();
   }
@@ -930,6 +980,9 @@ sub edit_e_mail {
     $form->{"email"} = $form->{"cp_email"} if $form->{"cp_email"};
   }
 
+  $form->{language} = $form->get_template_language(\%myconfig);
+  $form->{language} = "_" . $form->{language} if $form->{language};
+
   my $title = $locale->text('E-mail') . " " . $form->get_formname_translation();
 
   $form->{oldmedia} = $form->{media};
@@ -938,7 +991,7 @@ sub edit_e_mail {
   my $attachment_filename = $form->generate_attachment_filename();
   my $subject             = $form->{subject} || $form->generate_email_subject();
 
-  $form->{"fokus"} = $form->{"email"} ? "Form.subject" : "Form.email";
+  $::request->{layout}->focus($form->{"email"} ? "#subject" : "#email");
   $form->header;
 
   my (@dont_hide_key_list, %dont_hide_key, @hidden_keys);
@@ -952,7 +1005,7 @@ sub edit_e_mail {
                                      subject       => $subject,
                                      print_options => print_options('inline' => 1),
                                      HIDDEN        => [ map +{ name => $_, value => $form->{$_} }, @hidden_keys ],
-                                     SHOW_BCC      => $myconfig{role} eq 'admin' });
+                                     SHOW_BCC      => $::auth->assert('email_bcc', 'may fail') });
 
   $main::lxdebug->leave_sub();
 }
@@ -1047,11 +1100,12 @@ sub print_options {
     opthash("inline",                $form->{SM}{inline},              $locale->text('In-line'))
       if ($form->{media} eq 'email');
 
+  my $printable_templates = any { $::lx_office_conf{print_templates}->{$_} } qw(latex opendocument);
   push @MEDIA, grep $_,
       opthash("screen",              $form->{OP}{screen},              $locale->text('Screen')),
-    ($form->{printers} && scalar @{ $form->{printers} } && $::lx_office_conf{print_templates}->{latex}) ?
+    ($printable_templates && $form->{printers} && scalar @{ $form->{printers} }) ?
       opthash("printer",             $form->{OP}{printer},             $locale->text('Printer')) : undef,
-    ($::lx_office_conf{print_templates}->{latex} && !$options{no_queue}) ?
+    ($printable_templates && !$options{no_queue}) ?
       opthash("queue",               $form->{OP}{queue},               $locale->text('Queue')) : undef
         if ($form->{media} ne 'email');
 
@@ -1293,7 +1347,7 @@ sub print_form {
       call_sub($display_form);
       # saving the history
       if(!exists $form->{addition}) {
-        $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
+        $form->{snumbers} = "${inv}number" . "_" . $form->{"${inv}number"};
         $form->{addition} = "PRINTED";
         $form->save_history;
       }
@@ -1312,11 +1366,15 @@ sub print_form {
   my $payment_id_saved = $form->{payment_id};
   my $salesman_id_saved = $form->{salesman_id};
   my $cp_id_saved = $form->{cp_id};
+  my $taxzone_id_saved = $form->{taxzone_id};
+  my $currency_saved = $form->{currency};
 
   call_sub("$form->{vc}_details") if ($form->{vc});
 
   $form->{language_id} = $language_saved;
   $form->{payment_id} = $payment_id_saved;
+  $form->{taxzone_id} = $taxzone_id_saved;
+  $form->{currency} = $currency_saved;
 
   $form->{"email"} = $saved_email if ($saved_email);
   $form->{"cc"}    = $saved_cc    if ($saved_cc);
@@ -1339,11 +1397,15 @@ sub print_form {
     $output_longdates = 1;
   }
 
+  # Store the output number format so that the template modules know
+  # how to parse the amounts back if requested.
+  $myconfig{output_numberformat} = $output_numberformat || $myconfig{numberformat};
+
   ($form->{employee}) = split /--/, $form->{employee};
 
   # create the form variables
   if ($form->{type} =~ /_delivery_order$/) {
-    DO->order_details();
+    DO->order_details(\%myconfig, \%$form);
   } elsif ($order) {
     OE->order_details(\%myconfig, \%$form);
   } else {
@@ -1357,7 +1419,7 @@ sub print_form {
     $form->get_shipto(\%myconfig);
   }
 
-  my @a = qw(name street zipcode city country contact);
+  my @a = qw(name department_1 department_2 street zipcode city country contact phone fax email);
 
   my $shipto = 1;
 
@@ -1406,7 +1468,7 @@ sub print_form {
   format_dates($output_dateformat, $output_longdates,
                qw(invdate orddate quodate pldate duedate reqdate transdate
                   shippingdate deliverydate validitydate paymentdate
-                  datepaid transdate_oe deliverydate_oe
+                  datepaid transdate_oe deliverydate_oe dodate
                   employee_startdate employee_enddate
                   ),
                grep({ /^datepaid_\d+$/ ||
@@ -1420,7 +1482,7 @@ sub print_form {
   reformat_numbers($output_numberformat, 2,
                    qw(invtotal ordtotal quototal subtotal linetotal
                       listprice sellprice netprice discount
-                      tax taxbase total paid),
+                      tax taxbase total paid payment),
                    grep({ /^(?:linetotal|nodiscount_linetotal|listprice|sellprice|netprice|taxbase|discount|p_discount|discount_sub|nodiscount_sub|paid|subtotal|total|tax)_\d+$/ } keys(%{$form})));
 
   reformat_numbers($output_numberformat, undef,
@@ -1455,17 +1517,26 @@ sub print_form {
     $extension            = 'xls';
   }
 
-  my $email_extension = '_email' if (($form->{media} eq 'email') && (-f "$myconfig{templates}/$form->{formname}_email$form->{language}${printer_code}.${extension}"));
+  # search for the template
+  my @template_files;
+  push @template_files, "$form->{formname}_email$form->{language}$printer_code.$extension" if $form->{media} eq 'email';
+  push @template_files, "$form->{formname}$form->{language}$printer_code.$extension";
+  push @template_files, "$form->{formname}.$extension";
+  push @template_files, "default.$extension";
+  @template_files = uniq @template_files;
+  $form->{IN}     = first { -f "$myconfig{templates}/$_" } @template_files;
 
-  $form->{IN}         = "$form->{formname}${email_extension}$form->{language}${printer_code}.${extension}";
+  if (!defined $form->{IN}) {
+    $::form->error($::locale->text('Cannot find matching template for this print request. Please contact your template maintainer. I tried these: #1.', join ', ', map { "'$_'"} @template_files));
+  }
 
   delete $form->{OUT};
 
   if ($form->{media} eq 'printer') {
-    #$form->{OUT} = "| $form->{printer_command} &>/dev/null";
-    $form->{OUT} = "| $form->{printer_command} ";
+    $form->{OUT}      = $form->{printer_command};
+    $form->{OUT_MODE} = '|-';
     $form->{printed} .= " $form->{formname}";
-    $form->{printed} =~ s/^ //;
+    $form->{printed}  =~ s/^ //;
   }
   my $printed = $form->{printed};
 
@@ -1479,31 +1550,45 @@ sub print_form {
   my $emailed = $form->{emailed};
 
   if ($form->{media} eq 'queue') {
-    my %queued = map { s|.*/|| } split / /, $form->{queued};
+    my %queued = map { s|.*[/\\]||; $_ } split / /, $form->{queued};
 
     my $filename;
+    my $suffix = ($form->{postscript}) ? '.ps' : '.pdf';
     if ($filename = $queued{ $form->{formname} }) {
-      $form->{queued} =~ s/\Q$form->{formname} $filename\E//;
       unlink $::lx_office_conf{paths}->{spool} . "/$filename";
-      $filename =~ s/\..*$//g;
+      delete $queued{ $form->{formname} };
+
+      $form->{queued}    =  join ' ', %queued;
+      $filename          =~ s/\..*$//g;
+      $filename         .=  $suffix;
+      $form->{OUT}       =  $::lx_office_conf{paths}->{spool} . "/$filename";
+      $form->{OUT_MODE}  =  '>';
+
     } else {
-      $filename = time;
-      $filename .= $$;
+      my $temp_fh;
+      ($temp_fh, $filename) = File::Temp::tempfile(
+        'kivitendo-spoolXXXXXX',
+        SUFFIX => "$suffix",
+        DIR    => $::lx_office_conf{paths}->{spool},
+        UNLINK => 0,
+      );
+      close $temp_fh;
+      $form->{OUT} = "$filename";
+      # use >> for OUT_MODE because file is already created by File::Temp
+      $form->{OUT_MODE} = '>>';
+      # strip directory so that only filename is stored in table status
+      ($filename) = $filename =~ /^$::lx_office_conf{paths}->{spool}\/(.*)/;
     }
 
-    $filename .= ($form->{postscript}) ? '.ps' : '.pdf';
-    $form->{OUT} = ">" . $::lx_office_conf{paths}->{spool} . "/$filename";
-
     # add type
     $form->{queued} .= " $form->{formname} $filename";
-
     $form->{queued} =~ s/^ //;
   }
   my $queued = $form->{queued};
 
 # saving the history
   if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
+    $form->{snumbers} = "${inv}number" . "_" . $form->{"${inv}number"};
     if($form->{media} =~ /printer/) {
       $form->{addition} = "PRINTED";
     }
@@ -1520,6 +1605,17 @@ sub print_form {
   }
   # /saving the history
 
+  # prepare meta information for template introspection
+  $form->{template_meta} = {
+    formname  => $form->{formname},
+    language  => SL::DB::Manager::Language->find_by_or_create(id => $form->{language_id} || undef),
+    format    => $form->{format},
+    media     => $form->{media},
+    extension => $extension,
+    printer   => SL::DB::Manager::Printer->find_by_or_create(id => $form->{printer_id} || undef),
+    today     => DateTime->today,
+  };
+
   $form->parse_template(\%myconfig);
 
   $form->{callback} = "";
@@ -1685,7 +1781,7 @@ sub set_duedate {
   my $invdate = $form->{invdate} eq 'undefined' ? undef : $form->{invdate};
   my $duedate = $form->get_duedate(\%myconfig, $invdate);
 
-  print $form->ajax_response_header() . $duedate;
+  print $form->ajax_response_header() . ($duedate || $invdate);
 
   $main::lxdebug->leave_sub();
 }
@@ -1705,6 +1801,7 @@ sub _update_part_information {
 
     my $info                 = $form->{PART_INFORMATION}->{$form->{"id_${i}"}} || { };
     $form->{"partunit_${i}"} = $info->{unit};
+    $form->{"weight_$i"}     = $info->{weight};
   }
 
   $main::lxdebug->leave_sub();
@@ -1794,6 +1891,8 @@ sub _render_custom_variables_inputs {
       $description = $cvar->{description} . ' ';
     }
 
+    my $form_key = "ic_cvar_" . $cvar->{name} . "_$params{row}";
+
     push @{ $params{ROW2} }, {
       line_break     => $num_visible_cvars == 1,
       description    => $description,
@@ -1804,10 +1903,43 @@ sub _render_custom_variables_inputs {
          name_prefix       => 'ic_',
          name_postfix      => "_$params{row}",
          valid             => $cvar->{valid},
-         value             => $form->{"ic_cvar_" . $cvar->{name} . "_$params{row}"},
+         value             => CVar->parse($::form->{$form_key}, $cvar),
       }
     };
   }
 
   $main::lxdebug->leave_sub(2);
 }
+
+sub _remove_billed_or_delivered_rows {
+  my (%params) = @_;
+
+  croak "Missing parameter 'quantities'" if !$params{quantities};
+
+  my @fields = map { s/_1$//; $_ } grep { m/_1$/ } keys %{ $::form };
+  my @new_rows;
+
+  my $removed_rows = 0;
+  my $row          = 0;
+  while ($row < $::form->{rowcount}) {
+    $row++;
+    next unless $::form->{"id_$row"};
+
+    my $parts_id                      = $::form->{"id_$row"};
+    my $base_qty                      = $::form->parse_amount(\%::myconfig, $::form->{"qty_$row"}) * SL::DB::Manager::Unit->find_by(name => $::form->{"unit_$row"})->base_factor;
+
+    my $sub_qty                       = min($base_qty, $params{quantities}->{$parts_id});
+    $params{quantities}->{$parts_id} -= $sub_qty;
+
+    if (!$sub_qty || ($sub_qty != $base_qty)) {
+      $::form->{"qty_${row}"} = $::form->format_amount(\%::myconfig, ($base_qty - $sub_qty) / SL::DB::Manager::Unit->find_by(name => $::form->{"unit_$row"})->base_factor);
+      push @new_rows, { map { $_ => $::form->{"${_}_${row}"} } @fields };
+
+    } else {
+      $removed_rows++;
+    }
+  }
+
+  $::form->redo_rows(\@fields, \@new_rows, scalar(@new_rows), $::form->{rowcount});
+  $::form->{rowcount} -= $removed_rows;
+}