Merge branch 'master' of github.com:kivitendo/kivitendo-erp
[kivitendo-erp.git] / SL / DO.pm
index ef6a10b..14a499a 100644 (file)
--- a/SL/DO.pm
+++ b/SL/DO.pm
@@ -38,8 +38,12 @@ use YAML;
 
 use SL::AM;
 use SL::Common;
 
 use SL::AM;
 use SL::Common;
+use SL::CVar;
 use SL::DBUtils;
 use SL::RecordLinks;
 use SL::DBUtils;
 use SL::RecordLinks;
+use SL::IC;
+
+use strict;
 
 sub transactions {
   $main::lxdebug->enter_sub();
 
 sub transactions {
   $main::lxdebug->enter_sub();
@@ -56,33 +60,35 @@ sub transactions {
 
   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
 
   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
-  $query =
+  my $query =
     qq|SELECT dord.id, dord.donumber, dord.ordnumber, dord.transdate,
     qq|SELECT dord.id, dord.donumber, dord.ordnumber, dord.transdate,
-         ct.name, dord.${vc}_id, dord.globalproject_id,
+         ct.${vc}number, ct.name, dord.${vc}_id, dord.globalproject_id,
          dord.closed, dord.delivered, dord.shippingpoint, dord.shipvia,
          dord.transaction_description,
          pr.projectnumber AS globalprojectnumber,
          dord.closed, dord.delivered, dord.shippingpoint, dord.shipvia,
          dord.transaction_description,
          pr.projectnumber AS globalprojectnumber,
+         dep.description AS department,
          e.name AS employee,
          sm.name AS salesman
        FROM delivery_orders dord
        LEFT JOIN $vc ct ON (dord.${vc}_id = ct.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
        LEFT JOIN employee sm ON (dord.salesman_id = sm.id)
          e.name AS employee,
          sm.name AS salesman
        FROM delivery_orders dord
        LEFT JOIN $vc ct ON (dord.${vc}_id = ct.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
        LEFT JOIN employee sm ON (dord.salesman_id = sm.id)
-       LEFT JOIN project pr ON (dord.globalproject_id = pr.id)|;
+       LEFT JOIN project pr ON (dord.globalproject_id = pr.id)
+       LEFT JOIN department dep ON (dord.department_id = dep.id)
+|;
 
   push @where, ($form->{type} eq 'sales_delivery_order' ? '' : 'NOT ') . qq|COALESCE(dord.is_sales, FALSE)|;
 
 
   push @where, ($form->{type} eq 'sales_delivery_order' ? '' : 'NOT ') . qq|COALESCE(dord.is_sales, FALSE)|;
 
-  my $department_id = (split /--/, $form->{department})[1];
-  if ($department_id) {
+  if ($form->{department_id}) {
     push @where,  qq|dord.department_id = ?|;
     push @where,  qq|dord.department_id = ?|;
-    push @values, conv_i($department_id);
+    push @values, conv_i($form->{department_id});
   }
 
   if ($form->{project_id}) {
   }
 
   if ($form->{project_id}) {
-    $query .=
+    push @where,
       qq|(dord.globalproject_id = ?) OR EXISTS
           (SELECT * FROM delivery_order_items doi
       qq|(dord.globalproject_id = ?) OR EXISTS
           (SELECT * FROM delivery_order_items doi
-           WHERE (doi.project_id = ?) AND (oi.delivery_order_id = dord.id))|;
+           WHERE (doi.project_id = ?) AND (doi.delivery_order_id = dord.id))|;
     push @values, conv_i($form->{project_id}), conv_i($form->{project_id});
   }
 
     push @values, conv_i($form->{project_id}), conv_i($form->{project_id});
   }
 
@@ -100,6 +106,10 @@ sub transactions {
     push @where, "dord.$item = ?";
     push @values, conv_i($form->{$item});
   }
     push @where, "dord.$item = ?";
     push @values, conv_i($form->{$item});
   }
+  if (!$main::auth->assert('sales_all_edit', 1)) {
+    push @where, qq|dord.employee_id = (select id from employee where login= ?)|;
+    push @values, $form->{login};
+  }
 
   foreach my $item (qw(donumber ordnumber cusordnumber transaction_description)) {
     next unless ($form->{$item});
 
   foreach my $item (qw(donumber ordnumber cusordnumber transaction_description)) {
     next unless ($form->{$item});
@@ -107,7 +117,8 @@ sub transactions {
     push @values, '%' . $form->{$item} . '%';
   }
 
     push @values, '%' . $form->{$item} . '%';
   }
 
-  if (!($form->{open} && $form->{closed})) {
+  if (($form->{open} || $form->{closed}) &&
+      ($form->{open} ne $form->{closed})) {
     push @where, ($form->{open} ? "NOT " : "") . "COALESCE(dord.closed, FALSE)";
   }
 
     push @where, ($form->{open} ? "NOT " : "") . "COALESCE(dord.closed, FALSE)";
   }
 
@@ -139,7 +150,8 @@ sub transactions {
     "employee"                => "e.name",
     "salesman"                => "sm.name",
     "shipvia"                 => "dord.shipvia",
     "employee"                => "e.name",
     "salesman"                => "sm.name",
     "shipvia"                 => "dord.shipvia",
-    "transaction_description" => "dord.transaction_description"
+    "transaction_description" => "dord.transaction_description",
+    "department"              => "lower(dep.description)",
   );
 
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
   );
 
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
@@ -190,6 +202,9 @@ sub save {
   my $all_units = AM->retrieve_units($myconfig, $form);
   $form->{all_units} = $all_units;
 
   my $all_units = AM->retrieve_units($myconfig, $form);
   $form->{all_units} = $all_units;
 
+  my $ic_cvar_configs = CVar->get_configs(module => 'IC',
+                                          dbh    => $dbh);
+
   $form->{donumber}    = $form->update_defaults($myconfig, $form->{type} eq 'sales_delivery_order' ? 'sdonumber' : 'pdonumber', $dbh) unless $form->{donumber};
   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
   $form->get_employee($dbh) unless ($form->{employee_id});
   $form->{donumber}    = $form->update_defaults($myconfig, $form->{type} eq 'sales_delivery_order' ? 'sdonumber' : 'pdonumber', $dbh) unless $form->{donumber};
   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
   $form->get_employee($dbh) unless ($form->{employee_id});
@@ -217,7 +232,7 @@ sub save {
   }
 
   my $project_id;
   }
 
   my $project_id;
-  my $reqdate;
+  my $items_reqdate;
 
   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
@@ -240,14 +255,14 @@ sub save {
          id, delivery_order_id, parts_id, description, longdescription, qty, base_qty,
          sellprice, discount, unit, reqdate, project_id, serialnumber,
          ordnumber, transdate, cusordnumber,
          id, delivery_order_id, parts_id, description, longdescription, qty, base_qty,
          sellprice, discount, unit, reqdate, project_id, serialnumber,
          ordnumber, transdate, cusordnumber,
-         lastcost, price_factor_id, price_factor, marge_price_factor)
+         lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
-         (SELECT factor FROM price_factors WHERE id = ?), ?)|;
+         (SELECT factor FROM price_factors WHERE id = ?), ?, ?)|;
   my $h_item = prepare_query($form, $dbh, $q_item);
 
   my $q_item_stock =
   my $h_item = prepare_query($form, $dbh, $q_item);
 
   my $q_item_stock =
-    qq|INSERT INTO delivery_order_items_stock (delivery_order_item_id, qty, unit, warehouse_id, bin_id, chargenumber)
-       VALUES (?, ?, ?, ?, ?, ?)|;
+    qq|INSERT INTO delivery_order_items_stock (delivery_order_item_id, qty, unit, warehouse_id, bin_id, chargenumber, bestbefore)
+       VALUES (?, ?, ?, ?, ?, ?, ?)|;
   my $h_item_stock = prepare_query($form, $dbh, $q_item_stock);
 
   my $in_out       = $form->{type} =~ /^sales/ ? 'out' : 'in';
   my $h_item_stock = prepare_query($form, $dbh, $q_item_stock);
 
   my $in_out       = $form->{type} =~ /^sales/ ? 'out' : 'in';
@@ -265,49 +280,66 @@ sub save {
     }
     my $baseqty = $form->{"qty_$i"} * $basefactor;
 
     }
     my $baseqty = $form->{"qty_$i"} * $basefactor;
 
-    $form->{"lastcost_$i"} *= 1;
-
     # set values to 0 if nothing entered
     # set values to 0 if nothing entered
-    $form->{"discount_$i"}  = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
+    $form->{"discount_$i"}  = $form->parse_amount($myconfig, $form->{"discount_$i"});
     $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
     $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+    $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
 
     $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
     $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
-    $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
+    my $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 
-    $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
+    $items_reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
 
     do_statement($form, $h_item_id, $q_item_id);
     my ($item_id) = $h_item_id->fetchrow_array();
 
 
     do_statement($form, $h_item_id, $q_item_id);
     my ($item_id) = $h_item_id->fetchrow_array();
 
+    # Get pricegroup_id and save it. Unfortunately the interface
+    # also uses ID "0" for signalling that none is selected, but "0"
+    # must not be stored in the database. Therefore we cannot simply
+    # use conv_i().
+    my $pricegroup_id = $form->{"pricegroup_id_$i"} * 1;
+    $pricegroup_id    = undef if !$pricegroup_id;
+
     # save detail record in delivery_order_items table
     @values = (conv_i($item_id), conv_i($form->{id}), conv_i($form->{"id_$i"}),
                $form->{"description_$i"}, $form->{"longdescription_$i"},
                $form->{"qty_$i"}, $baseqty,
     # save detail record in delivery_order_items table
     @values = (conv_i($item_id), conv_i($form->{id}), conv_i($form->{"id_$i"}),
                $form->{"description_$i"}, $form->{"longdescription_$i"},
                $form->{"qty_$i"}, $baseqty,
-               $form->{"sellprice_$i"}, $form->{"discount_$i"},
-               $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
+               $form->{"sellprice_$i"}, $form->{"discount_$i"} / 100,
+               $form->{"unit_$i"}, conv_date($items_reqdate), conv_i($form->{"project_id_$i"}),
                $form->{"serialnumber_$i"},
                $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
                $form->{"cusordnumber_$i"},
                $form->{"lastcost_$i"},
                conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
                $form->{"serialnumber_$i"},
                $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
                $form->{"cusordnumber_$i"},
                $form->{"lastcost_$i"},
                conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
-               conv_i($form->{"marge_price_factor_$i"}));
+               conv_i($form->{"marge_price_factor_$i"}),
+               $pricegroup_id);
     do_statement($form, $h_item, $q_item, @values);
 
     my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"});
 
     foreach my $sinfo (@{ $stock_info }) {
       @values = ($item_id, $sinfo->{qty}, $sinfo->{unit}, conv_i($sinfo->{warehouse_id}),
     do_statement($form, $h_item, $q_item, @values);
 
     my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"});
 
     foreach my $sinfo (@{ $stock_info }) {
       @values = ($item_id, $sinfo->{qty}, $sinfo->{unit}, conv_i($sinfo->{warehouse_id}),
-                 conv_i($sinfo->{bin_id}), $sinfo->{chargenumber});
+                 conv_i($sinfo->{bin_id}), $sinfo->{chargenumber}, conv_date($sinfo->{bestbefore}));
       do_statement($form, $h_item_stock, $q_item_stock, @values);
     }
       do_statement($form, $h_item_stock, $q_item_stock, @values);
     }
+
+    CVar->save_custom_variables(module       => 'IC',
+                                sub_module   => 'delivery_order_items',
+                                trans_id     => $item_id,
+                                configs      => $ic_cvar_configs,
+                                variables    => $form,
+                                name_prefix  => 'ic_',
+                                name_postfix => "_$i",
+                                dbh          => $dbh);
   }
 
   $h_item_id->finish();
   $h_item->finish();
   $h_item_stock->finish();
 
   }
 
   $h_item_id->finish();
   $h_item->finish();
   $h_item_stock->finish();
 
-  ($null, $form->{department_id}) = split(/--/, $form->{department});
 
 
+  # reqdate is last items reqdate (?: old behaviour) if not already set
+  $form->{reqdate} ||= $items_reqdate;
   # save DO record
   $query =
     qq|UPDATE delivery_orders SET
   # save DO record
   $query =
     qq|UPDATE delivery_orders SET
@@ -316,13 +348,13 @@ sub save {
          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, closed = ?,
          delivered = ?, department_id = ?, language_id = ?, shipto_id = ?,
          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?,
          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, closed = ?,
          delivered = ?, department_id = ?, language_id = ?, shipto_id = ?,
          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?,
-         is_sales = ?
+         is_sales = ?, taxzone_id = ?, taxincluded = ?, terms = ?, curr = ?
        WHERE id = ?|;
 
   @values = ($form->{donumber}, $form->{ordnumber},
              $form->{cusordnumber}, conv_date($form->{transdate}),
              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
        WHERE id = ?|;
 
   @values = ($form->{donumber}, $form->{ordnumber},
              $form->{cusordnumber}, conv_date($form->{transdate}),
              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
-             conv_date($reqdate), $form->{shippingpoint}, $form->{shipvia},
+             conv_date($form->{reqdate}), $form->{shippingpoint}, $form->{shipvia},
              $form->{notes}, $form->{intnotes},
              $form->{closed} ? 't' : 'f', $form->{delivered} ? "t" : "f",
              conv_i($form->{department_id}), conv_i($form->{language_id}), conv_i($form->{shipto_id}),
              $form->{notes}, $form->{intnotes},
              $form->{closed} ? 't' : 'f', $form->{delivered} ? "t" : "f",
              conv_i($form->{department_id}), conv_i($form->{language_id}), conv_i($form->{shipto_id}),
@@ -330,6 +362,7 @@ sub save {
              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
              $form->{transaction_description},
              $form->{type} =~ /^sales/ ? 't' : 'f',
              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
              $form->{transaction_description},
              $form->{type} =~ /^sales/ ? 't' : 'f',
+             conv_i($form->{taxzone_id}), $form->{taxincluded} ? 't' : 'f', conv_i($form->{terms}), substr($form->{currency}, 0, 3),
              conv_i($form->{id}));
   do_query($form, $dbh, $query, @values);
 
              conv_i($form->{id}));
   do_query($form, $dbh, $query, @values);
 
@@ -362,7 +395,7 @@ sub save {
 
   $form->{saved_donumber} = $form->{donumber};
 
 
   $form->{saved_donumber} = $form->{donumber};
 
-  Common::webdav_folder($form) if ($main::webdav);
+  Common::webdav_folder($form);
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
@@ -387,7 +420,7 @@ sub mark_orders_if_delivered {
                                         'to_table'   => 'delivery_orders',
                                         'to_id'      => $params{do_id});
 
                                         'to_table'   => 'delivery_orders',
                                         'to_id'      => $params{do_id});
 
-  my ($oe_id)  = $links[0]->{from_id} if (scalar @links);
+  my $oe_id  = @links ? $links[0]->{from_id} : undef;
 
   return $main::lxdebug->leave_sub() if (!$oe_id);
 
 
   return $main::lxdebug->leave_sub() if (!$oe_id);
 
@@ -403,8 +436,6 @@ sub mark_orders_if_delivered {
                                          'oe_id' => $oe_id,);
   my %ordered   = ();
 
                                          'oe_id' => $oe_id,);
   my %ordered   = ();
 
-  do_statement($form, $sth, $query, $oe_id);
-
   while (my $ref = $sth->fetchrow_hashref()) {
     $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
 
   while (my $ref = $sth->fetchrow_hashref()) {
     $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
 
@@ -472,7 +503,7 @@ sub delete {
 
   my $myconfig = \%main::myconfig;
   my $form     = $main::form;
 
   my $myconfig = \%main::myconfig;
   my $form     = $main::form;
-  my $spool    = $main::spool;
+  my $spool    = $::lx_office_conf{paths}->{spool};
 
   # connect to database
   my $dbh = $form->get_standard_dbh($myconfig);
 
   # connect to database
   my $dbh = $form->get_standard_dbh($myconfig);
@@ -483,6 +514,7 @@ sub delete {
 
   my $spoolfile;
   my @spoolfiles = ();
 
   my $spoolfile;
   my @spoolfiles = ();
+  my @values;
 
   while (($spoolfile) = $sth->fetchrow_array) {
     push @spoolfiles, $spoolfile;
 
   while (($spoolfile) = $sth->fetchrow_array) {
     push @spoolfiles, $spoolfile;
@@ -546,14 +578,20 @@ sub retrieve {
 
   my ($query, $query_add, @values, $sth, $ref);
 
 
   my ($query, $query_add, @values, $sth, $ref);
 
+  my $ic_cvar_configs = CVar->get_configs(module => 'IC',
+                                          dbh    => $dbh);
+
   my $vc   = $params{vc} eq 'customer' ? 'customer' : 'vendor';
 
   my $mode = !$params{ids} ? 'default' : ref $params{ids} eq 'ARRAY' ? 'multi' : 'single';
 
   if ($mode eq 'default') {
   my $vc   = $params{vc} eq 'customer' ? 'customer' : 'vendor';
 
   my $mode = !$params{ids} ? 'default' : ref $params{ids} eq 'ARRAY' ? 'multi' : 'single';
 
   if ($mode eq 'default') {
-    $ref = selectfirst_hashref_query($form, $dbh, qq|SELECT current_date AS transdate, current_date AS reqdate|);
+    $ref = selectfirst_hashref_query($form, $dbh, qq|SELECT current_date AS transdate|);
     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
+    # if reqdate is not set from oe-workflow, set it to transdate (which is current date)
+    $form->{reqdate} ||= $form->{transdate};
+
     # get last name used
     $form->lastname_used($dbh, $myconfig, $vc) unless $form->{"${vc}_id"};
 
     # get last name used
     $form->lastname_used($dbh, $myconfig, $vc) unless $form->{"${vc}_id"};
 
@@ -577,7 +615,8 @@ sub retrieve {
          dord.closed, dord.reqdate, dord.department_id, dord.cusordnumber,
          d.description AS department, dord.language_id,
          dord.shipto_id,
          dord.closed, dord.reqdate, dord.department_id, dord.cusordnumber,
          d.description AS department, dord.language_id,
          dord.shipto_id,
-         dord.globalproject_id, dord.delivered, dord.transaction_description
+         dord.globalproject_id, dord.delivered, dord.transaction_description,
+         dord.taxzone_id, dord.taxincluded, dord.terms, dord.curr AS currency
        FROM delivery_orders dord
        JOIN ${vc} cv ON (dord.${vc}_id = cv.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
        FROM delivery_orders dord
        JOIN ${vc} cv ON (dord.${vc}_id = cv.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
@@ -586,7 +625,7 @@ sub retrieve {
   $sth = prepare_execute_query($form, $dbh, $query, @do_ids);
 
   delete $form->{"${vc}_id"};
   $sth = prepare_execute_query($form, $dbh, $query, @do_ids);
 
   delete $form->{"${vc}_id"};
-  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+  while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
     if ($form->{"${vc}_id"} && ($ref->{"${vc}_id"} != $form->{"${vc}_id"})) {
       $sth->finish();
       $main::lxdebug->leave_sub();
     if ($form->{"${vc}_id"} && ($ref->{"${vc}_id"} != $form->{"${vc}_id"})) {
       $sth->finish();
       $main::lxdebug->leave_sub();
@@ -595,9 +634,15 @@ sub retrieve {
     }
 
     map { $form->{$_} = $ref->{$_} } keys %$ref if ($ref);
     }
 
     map { $form->{$_} = $ref->{$_} } keys %$ref if ($ref);
+    $form->{donumber_array} .= $form->{donumber} . ' ';
   }
   $sth->finish();
 
   }
   $sth->finish();
 
+  # remove any trailing whitespace
+  $form->{currency} =~ s/\s*$//;
+
+  $form->{donumber_array} =~ s/\s*$//g;
+
   $form->{saved_donumber} = $form->{donumber};
 
   # if not given, fill transdate with current_date
   $form->{saved_donumber} = $form->{donumber};
 
   # if not given, fill transdate with current_date
@@ -607,7 +652,7 @@ sub retrieve {
     $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'DO'|;
     $sth   = prepare_execute_query($form, $dbh, $query, $form->{id});
 
     $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'DO'|;
     $sth   = prepare_execute_query($form, $dbh, $query, $form->{id});
 
-    $ref   = $sth->fetchrow_hashref(NAME_lc);
+    $ref   = $sth->fetchrow_hashref("NAME_lc");
     delete $ref->{id};
     map { $form->{$_} = $ref->{$_} } keys %$ref;
     $sth->finish();
     delete $ref->{id};
     map { $form->{$_} = $ref->{$_} } keys %$ref;
     $sth->finish();
@@ -616,7 +661,7 @@ sub retrieve {
     $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
     $sth   = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
     $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
     $sth   = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
-    while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+    while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
       $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
       $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
@@ -636,12 +681,12 @@ sub retrieve {
   # stuff different from the whole will not be overwritten, but saved with a suffix.
   $query =
     qq|SELECT doi.id AS delivery_order_items_id,
   # stuff different from the whole will not be overwritten, but saved with a suffix.
   $query =
     qq|SELECT doi.id AS delivery_order_items_id,
-         p.partnumber, p.assembly, doi.description, doi.qty,
+         p.partnumber, p.assembly, p.listprice, doi.description, doi.qty,
          doi.sellprice, doi.parts_id AS id, doi.unit, doi.discount, p.bin, p.notes AS partnotes,
          doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
          doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
          doi.sellprice, doi.parts_id AS id, doi.unit, doi.discount, p.bin, p.notes AS partnotes,
          doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
          doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
-         doi.price_factor_id, doi.price_factor, doi.marge_price_factor,
-         pr.projectnumber,
+         doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id,
+         pr.projectnumber, dord.transdate AS dord_transdate,
          pg.partsgroup
        FROM delivery_order_items doi
        JOIN parts p ON (doi.parts_id = p.id)
          pg.partsgroup
        FROM delivery_order_items doi
        JOIN parts p ON (doi.parts_id = p.id)
@@ -653,11 +698,21 @@ sub retrieve {
 
   $form->{form_details} = selectall_hashref_query($form, $dbh, $query, @do_ids);
 
 
   $form->{form_details} = selectall_hashref_query($form, $dbh, $query, @do_ids);
 
+  # Retrieve custom variables.
+  foreach my $doi (@{ $form->{form_details} }) {
+    my $cvars = CVar->get_custom_variables(dbh        => $dbh,
+                                           module     => 'IC',
+                                           sub_module => 'delivery_order_items',
+                                           trans_id   => $doi->{delivery_order_items_id},
+                                          );
+    map { $doi->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
+  }
+
   if ($mode eq 'single') {
     my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
     $query =
   if ($mode eq 'single') {
     my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
     $query =
-      qq|SELECT qty, unit, bin_id, warehouse_id, chargenumber
+      qq|SELECT qty, unit, bin_id, warehouse_id, chargenumber, bestbefore
          FROM delivery_order_items_stock
          WHERE delivery_order_item_id = ?|;
     my $sth = prepare_query($form, $dbh, $query);
          FROM delivery_order_items_stock
          WHERE delivery_order_item_id = ?|;
     my $sth = prepare_query($form, $dbh, $query);
@@ -675,7 +730,7 @@ sub retrieve {
     $sth->finish();
   }
 
     $sth->finish();
   }
 
-  Common::webdav_folder($form) if ($main::webdav);
+  Common::webdav_folder($form);
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
@@ -704,7 +759,7 @@ sub order_details {
   my %oid = ('Pg'     => 'oid',
              'Oracle' => 'rowid');
 
   my %oid = ('Pg'     => 'oid',
              'Oracle' => 'rowid');
 
-  my (@project_ids, %projectnumbers);
+  my (@project_ids, %projectnumbers, %projectdescriptions);
 
   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
 
   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
@@ -719,24 +774,27 @@ sub order_details {
   }
 
   if (@project_ids) {
   }
 
   if (@project_ids) {
-    $query = "SELECT id, projectnumber FROM project WHERE id IN (" .
+    $query = "SELECT id, projectnumber, description FROM project WHERE id IN (" .
       join(", ", map("?", @project_ids)) . ")";
     $sth = prepare_execute_query($form, $dbh, $query, @project_ids);
     while (my $ref = $sth->fetchrow_hashref()) {
       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
       join(", ", map("?", @project_ids)) . ")";
     $sth = prepare_execute_query($form, $dbh, $query, @project_ids);
     while (my $ref = $sth->fetchrow_hashref()) {
       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
+      $projectdescriptions{$ref->{id}} = $ref->{description};
     }
     $sth->finish();
   }
 
   $form->{"globalprojectnumber"} =
     $projectnumbers{$form->{"globalproject_id"}};
     }
     $sth->finish();
   }
 
   $form->{"globalprojectnumber"} =
     $projectnumbers{$form->{"globalproject_id"}};
+  $form->{"globalprojectdescription"} =
+      $projectdescriptions{$form->{"globalproject_id"}};
 
   my $q_pg     = qq|SELECT p.partnumber, p.description, p.unit, a.qty, pg.partsgroup
                     FROM assembly a
                     JOIN parts p ON (a.parts_id = p.id)
                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
                     WHERE a.bom = '1'
 
   my $q_pg     = qq|SELECT p.partnumber, p.description, p.unit, a.qty, pg.partsgroup
                     FROM assembly a
                     JOIN parts p ON (a.parts_id = p.id)
                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
                     WHERE a.bom = '1'
-                      AND a.id = ? $sortorder|;
+                      AND a.id = ?|;
   my $h_pg     = prepare_query($form, $dbh, $q_pg);
 
   my $q_bin_wh = qq|SELECT (SELECT description FROM bin       WHERE id = ?) AS bin,
   my $h_pg     = prepare_query($form, $dbh, $q_pg);
 
   my $q_bin_wh = qq|SELECT (SELECT description FROM bin       WHERE id = ?) AS bin,
@@ -747,11 +805,23 @@ sub order_details {
 
   my $num_si   = 0;
 
 
   my $num_si   = 0;
 
+  my $ic_cvar_configs = CVar->get_configs(module => 'IC');
+
+  $form->{TEMPLATE_ARRAYS} = { };
+  IC->prepare_parts_for_printing();
+
   my @arrays =
     qw(runningnumber number description longdescription qty unit
   my @arrays =
     qw(runningnumber number description longdescription qty unit
-       partnotes serialnumber reqdate projectnumber
+       partnotes serialnumber reqdate projectnumber projectdescription
        si_runningnumber si_number si_description
        si_runningnumber si_number si_description
-       si_warehouse si_bin si_chargenumber si_qty si_unit);
+       si_warehouse si_bin si_chargenumber si_bestbefore si_qty si_unit);
+
+  map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays);
+
+  push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
+
+  $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
+  my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
   my $sameitem = "";
   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
   my $sameitem = "";
   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
@@ -774,16 +844,19 @@ sub order_details {
 
     my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
 
     my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
-    push @{ $form->{runningnumber} },   $position;
-    push @{ $form->{number} },          $form->{"partnumber_$i"};
-    push @{ $form->{description} },     $form->{"description_$i"};
-    push @{ $form->{longdescription} }, $form->{"longdescription_$i"};
-    push @{ $form->{qty} },             $form->format_amount($myconfig, $form->{"qty_$i"});
-    push @{ $form->{unit} },            $form->{"unit_$i"};
-    push @{ $form->{partnotes} },       $form->{"partnotes_$i"};
-    push @{ $form->{serialnumber} },    $form->{"serialnumber_$i"};
-    push @{ $form->{reqdate} },         $form->{"reqdate_$i"};
-    push @{ $form->{projectnumber} },   $projectnumbers{$form->{"project_id_$i"}};
+    push @{ $form->{TEMPLATE_ARRAYS}{runningnumber} },   $position;
+    push @{ $form->{TEMPLATE_ARRAYS}{number} },          $form->{"partnumber_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{description} },     $form->{"description_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{longdescription} }, $form->{"longdescription_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{qty} },             $form->format_amount($myconfig, $form->{"qty_$i"});
+    push @{ $form->{TEMPLATE_ARRAYS}{qty_nofmt} },       $form->{"qty_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{unit} },            $form->{"unit_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{partnotes} },       $form->{"partnotes_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{serialnumber} },    $form->{"serialnumber_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{reqdate} },         $form->{"reqdate_$i"};
+    push @{ $form->{TEMPLATE_ARRAYS}{projectnumber} },   $projectnumbers{$form->{"project_id_$i"}};
+    push @{ $form->{TEMPLATE_ARRAYS}{projectdescription} },
+      $projectdescriptions{$form->{"project_id_$i"}};
 
     if ($form->{"assembly_$i"}) {
       $sameitem = "";
 
     if ($form->{"assembly_$i"}) {
       $sameitem = "";
@@ -799,16 +872,15 @@ sub order_details {
 
       do_statement($form, $h_pg, $q_pg, conv_i($form->{"id_$i"}));
 
 
       do_statement($form, $h_pg, $q_pg, conv_i($form->{"id_$i"}));
 
-      while (my $ref = $h_pg->fetchrow_hashref(NAME_lc)) {
+      while (my $ref = $h_pg->fetchrow_hashref("NAME_lc")) {
         if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
         if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
-          map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays));
+          map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
           $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
           $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
-          push(@{ $form->{description} }, $sameitem);
+          push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
         }
         }
+        push(@{ $form->{TEMPLATE_ARRAYS}->{"description"} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq| -- $ref->{partnumber}, $ref->{description}|);
 
 
-        push(@{ $form->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
-
-        map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays));
+        map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
       }
     }
 
       }
     }
 
@@ -821,16 +893,22 @@ sub order_details {
         do_statement($form, $h_bin_wh, $q_bin_wh, conv_i($si->{bin_id}), conv_i($si->{warehouse_id}));
         my $bin_wh = $h_bin_wh->fetchrow_hashref();
 
         do_statement($form, $h_bin_wh, $q_bin_wh, conv_i($si->{bin_id}), conv_i($si->{warehouse_id}));
         my $bin_wh = $h_bin_wh->fetchrow_hashref();
 
-        push @{ $form->{si_runningnumber} }, $num_si;
-        push @{ $form->{si_number} },        $form->{"partnumber_$i"};
-        push @{ $form->{si_description} },   $form->{"description_$i"};
-        push @{ $form->{si_warehouse} },     $bin_wh->{warehouse};
-        push @{ $form->{si_bin} },           $bin_wh->{bin};
-        push @{ $form->{si_chargenumber} },  $si->{chargenumber};
-        push @{ $form->{si_qty} },           $form->format_amount($myconfig, $si->{qty} * 1);
-        push @{ $form->{si_unit} },          $si->{unit};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_runningnumber}[$position-1] }, $num_si;
+        push @{ $form->{TEMPLATE_ARRAYS}{si_number}[$position-1] },        $form->{"partnumber_$i"};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_description}[$position-1] },   $form->{"description_$i"};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_warehouse}[$position-1] },     $bin_wh->{warehouse};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_bin}[$position-1] },           $bin_wh->{bin};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_chargenumber}[$position-1] },  $si->{chargenumber};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_bestbefore}[$position-1] },    $si->{bestbefore};
+        push @{ $form->{TEMPLATE_ARRAYS}{si_qty}[$position-1] },           $form->format_amount($myconfig, $si->{qty} * 1);
+        push @{ $form->{TEMPLATE_ARRAYS}{si_qty_nofmt}[$position-1] },     $si->{qty} * 1;
+        push @{ $form->{TEMPLATE_ARRAYS}{si_unit}[$position-1] },          $si->{unit};
       }
     }
       }
     }
+
+    push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
+      CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
+        for @{ $ic_cvar_configs };
   }
 
   $h_pg->finish();
   }
 
   $h_pg->finish();
@@ -846,6 +924,8 @@ sub project_description {
 
   my ($self, $dbh, $id) = @_;
 
 
   my ($self, $dbh, $id) = @_;
 
+  my $form     =  $main::form;
+
   my $query = qq|SELECT description FROM project WHERE id = ?|;
   my ($value) = selectrow_query($form, $dbh, $query, $id);
 
   my $query = qq|SELECT description FROM project WHERE id = ?|;
   my ($value) = selectrow_query($form, $dbh, $query, $id);
 
@@ -880,7 +960,7 @@ sub unpack_stock_information {
 }
 
 sub get_item_availability {
 }
 
 sub get_item_availability {
-  $main::lxdebug->enter_sub();
+  $::lxdebug->enter_sub;
 
   my $self     = shift;
   my %params   = @_;
 
   my $self     = shift;
   my %params   = @_;
@@ -888,23 +968,22 @@ sub get_item_availability {
   Common::check_params(\%params, qw(parts_id));
 
   my @parts_ids = 'ARRAY' eq ref $params{parts_id} ? @{ $params{parts_id} } : ($params{parts_id});
   Common::check_params(\%params, qw(parts_id));
 
   my @parts_ids = 'ARRAY' eq ref $params{parts_id} ? @{ $params{parts_id} } : ($params{parts_id});
-  my $form      = $main::form;
 
   my $query     =
 
   my $query     =
-    qq|SELECT i.warehouse_id, i.bin_id, i.chargenumber, SUM(qty) AS qty, i.parts_id,
+    qq|SELECT i.warehouse_id, i.bin_id, i.chargenumber, i.bestbefore, SUM(qty) AS qty, i.parts_id,
          w.description AS warehousedescription,
          b.description AS bindescription
        FROM inventory i
        LEFT JOIN warehouse w ON (i.warehouse_id = w.id)
        LEFT JOIN bin b       ON (i.bin_id       = b.id)
        WHERE (i.parts_id IN (| . join(', ', ('?') x scalar(@parts_ids)) . qq|))
          w.description AS warehousedescription,
          b.description AS bindescription
        FROM inventory i
        LEFT JOIN warehouse w ON (i.warehouse_id = w.id)
        LEFT JOIN bin b       ON (i.bin_id       = b.id)
        WHERE (i.parts_id IN (| . join(', ', ('?') x scalar(@parts_ids)) . qq|))
-         AND qty > 0
-       GROUP BY i.warehouse_id, i.bin_id, i.chargenumber, i.parts_id, w.description, b.description
-       ORDER BY LOWER(w.description), LOWER(b.description), LOWER(i.chargenumber)|;
-
-  my $contents = selectall_hashref_query($form, $form->get_standard_dbh($myconfig), $query, @parts_ids);
+       GROUP BY i.warehouse_id, i.bin_id, i.chargenumber, i.bestbefore, i.parts_id, w.description, b.description
+       HAVING SUM(qty) > 0
+       ORDER BY LOWER(w.description), LOWER(b.description), LOWER(i.chargenumber), i.bestbefore
+|;
+  my $contents = selectall_hashref_query($::form, $::form->get_standard_dbh, $query, @parts_ids);
 
 
-  $main::lxdebug->leave_sub();
+  $::lxdebug->leave_sub;
 
   return @{ $contents };
 }
 
   return @{ $contents };
 }
@@ -938,7 +1017,8 @@ sub check_stock_availability {
     foreach my $row (@contents) {
       next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
                ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
     foreach my $row (@contents) {
       next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
                ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
-               ($row->{chargenumber} ne $sinfo->{chargenumber}));
+               ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
+               ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
       $found       = 1;
 
 
       $found       = 1;
 
@@ -986,11 +1066,13 @@ sub transfer_in_out {
       "${prefix}_warehouse_id" => $request->{warehouse_id},
       "${prefix}_bin_id"       => $request->{bin_id},
       'chargenumber'           => $request->{chargenumber},
       "${prefix}_warehouse_id" => $request->{warehouse_id},
       "${prefix}_bin_id"       => $request->{bin_id},
       'chargenumber'           => $request->{chargenumber},
+      'bestbefore'             => $request->{bestbefore},
       'qty'                    => $request->{qty},
       'unit'                   => $request->{unit},
       'oe_id'                  => $form->{id},
       'shippingdate'           => 'current_date',
       'transfer_type'          => $params{direction} eq 'in' ? 'stock' : 'shipped',
       'qty'                    => $request->{qty},
       'unit'                   => $request->{unit},
       'oe_id'                  => $form->{id},
       'shippingdate'           => 'current_date',
       'transfer_type'          => $params{direction} eq 'in' ? 'stock' : 'shipped',
+      'project_id'             => $request->{project_id},
     };
   }
 
     };
   }
 
@@ -1035,7 +1117,7 @@ sub get_shipped_qty {
   my $all_units = AM->retrieve_all_units();
 
   foreach my $entry (@{ $entries }) {
   my $all_units = AM->retrieve_all_units();
 
   foreach my $entry (@{ $entries }) {
-    $entry->{qty} *= $all_units->{$entry->{unit}}->{factor} / $all_units->{$entry->{partunit}}->{factor};
+    $entry->{qty} *= AM->convert_unit($entry->{unit}, $entry->{partunit}, $all_units);
 
     if (!$ship{$entry->{parts_id}}) {
       $ship{$entry->{parts_id}} = $entry;
 
     if (!$ship{$entry->{parts_id}}) {
       $ship{$entry->{parts_id}} = $entry;