From 79be93d998bee5295148dfe26501708bb8358d32 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur <eldy@destailleur.fr>
Date: Tue, 17 May 2016 14:02:18 +0200
Subject: [PATCH] FIX The table llx_product_lot is now filled/used to store
 eatby/sellby dates.

---
 dev/skeletons/build_class_from_table.php      |   2 +-
 .../install/mysql/migration/3.9.0-4.0.0.sql   |   7 ++
 htdocs/langs/en_US/productbatch.lang          |   1 -
 htdocs/product/class/product.class.php        |  42 +++++--
 htdocs/product/class/productbatch.class.php   |  58 +++++----
 htdocs/product/reassortlot.php                |  20 +--
 .../stock/class/mouvementstock.class.php      |  53 +++++++-
 .../product/stock/class/productlot.class.php  | 118 ++++++++++--------
 htdocs/product/stock/mouvement.php            |   5 +-
 htdocs/product/stock/product.php              |  68 +++++-----
 htdocs/product/stock/productlot_card.php      |  44 +++----
 htdocs/product/stock/productlot_list.php      |  49 ++++----
 .../product/stock/tpl/stocktransfer.tpl.php   |  11 +-
 13 files changed, 301 insertions(+), 177 deletions(-)

diff --git a/dev/skeletons/build_class_from_table.php b/dev/skeletons/build_class_from_table.php
index 800bafe6318..ecdbbd09dc4 100755
--- a/dev/skeletons/build_class_from_table.php
+++ b/dev/skeletons/build_class_from_table.php
@@ -259,7 +259,7 @@ foreach($property as $key => $prop)
 	if ($addfield)
 	{
 		$varprop.="\t\t\$sql.= '".$prop['field'];
-		if ($i <= count($property)-$no_output_field) $varprop.=",";
+		if ($i < (count($property)-$no_output_field)) $varprop.=",";
 		$varprop.="';";
 		$varprop.="\n";
 	}
diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql
index 93469ff5a8f..f34e52dafe6 100644
--- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql
+++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql
@@ -54,6 +54,8 @@ CREATE TABLE llx_product_lot (
   import_key    integer
 ) ENGINE=InnoDB;
 
+ALTER TABLE llx_product_lot ADD UNIQUE INDEX uk_product_lot(fk_product, batch);
+
 DROP TABLE llx_stock_serial; 
 
 ALTER TABLE llx_product ADD COLUMN note_public text;
@@ -460,6 +462,11 @@ CREATE TABLE llx_advtargetemailing
 ALTER TABLE llx_advtargetemailing ADD UNIQUE INDEX uk_advtargetemailing_name (name);
 
 
+update llx_product_batch set batch = '000000' where batch = 'Non d&eacute;fini';
+update llx_product_batch set batch = '000000' where batch = 'Non défini';
+update llx_product_batch set batch = '000000' where batch = 'Undefined';
+update llx_product_lot set batch = '000000' where batch = 'Undefined';
+update llx_stock_mouvement set batch = '000000' where batch = 'Undefined';
 
 -- At end
 ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch);
diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang
index 37ceaa49b38..c3d78f7ff7f 100644
--- a/htdocs/langs/en_US/productbatch.lang
+++ b/htdocs/langs/en_US/productbatch.lang
@@ -17,6 +17,5 @@ printEatby=Eat-by: %s
 printSellby=Sell-by: %s
 printQty=Qty: %d
 AddDispatchBatchLine=Add a line for Shelf Life dispatching
-BatchDefaultNumber=Undefined
 WhenProductBatchModuleOnOptionAreForced=When module Lot/Serial is on, increase/decrease stock mode is forced to last choice and can't be edited. Other options can be defined as you want.
 ProductDoesNotUseBatchSerial=This product does not use lot/serial number
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index e8b32f978cb..31aa35aba69 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -607,7 +607,8 @@ class Product extends CommonObject
     }
 
 	/**
-	 *	Update a record into database
+	 *	Update a record into database.
+	 *  If batch flag is set to on, we create records into llx_product_batch
 	 *
 	 *	@param	int		$id         Id of product
 	 *	@param  User	$user       Object user making update
@@ -672,25 +673,46 @@ class Product extends CommonObject
                 $org->fetch($this->id);
                 $this->oldcopy=$org;
             }
-            // test if batch management is activated on existing product
+            
+            // Test if batch management is activated on existing product
+            // If yes, we create missing entries into product_batch
             if ($this->hasbatch() && !$this->oldcopy->hasbatch())
             {
+                //$valueforundefinedlot = 'Undefined';  // In previous version, 39 and lower
+                $valueforundefinedlot = '000000';
+
+                dol_syslog("Flag batch of product id=".$this->id." is set to ON, so we will create missing records into product_batch");
+
                 $this->load_stock();
-                foreach ($this->stock_warehouse as $idW => $ObjW)
+                foreach ($this->stock_warehouse as $idW => $ObjW)   // For each warehouse where we have stocks defined for this product (for each lines in product_stock)
                 {
                     $qty_batch = 0;
-                    foreach ($ObjW->detail_batch as $detail)
+                    foreach ($ObjW->detail_batch as $detail)    // Each lines of detail in product_batch of the current $ObjW = product_stock
                     {
+                        if ($detail->batch == $valueforundefinedlot || $detail->batch == 'Undefined') 
+                        {
+                            // We discard this line, we will create it later
+                            $sqlclean="DELETE FROM ".MAIN_DB_PREFIX."product_batch WHERE batch in('Undefined', '".$valueforundefinedlot."') AND fk_product_stock = ".$ObjW->id;
+                            $result = $this->db->query($sqlclean);
+                            if (! $result)
+                            {
+                                dol_print_error($this->db);
+                                exit;
+                            }
+                            continue;
+                        }
+                    
                         $qty_batch += $detail->qty;
                     }
-                    // Quantities in batch details are not same same as stock quantity
-                    // So we add a default batch record
+                    // Quantities in batch details are not same as stock quantity,
+                    // so we add a default batch record to complete and get same qty in parent and child table
                     if ($ObjW->real <> $qty_batch)
                     {
                         $ObjBatch = new Productbatch($this->db);
-                        $ObjBatch->batch = $langs->trans('BatchDefaultNumber');
-                        $ObjBatch->qty = $ObjW->real - $qty_batch;
+                        $ObjBatch->batch = $valueforundefinedlot; 
+                        $ObjBatch->qty = ($ObjW->real - $qty_batch);
                         $ObjBatch->fk_product_stock = $ObjW->id;
+
                         if ($ObjBatch->create($user,1) < 0)
                         {
                             $error++;
@@ -699,6 +721,7 @@ class Product extends CommonObject
                     }
                 }
             }
+            
 	        // For automatic creation
 	        if ($this->barcode == -1) $this->barcode = $this->get_barcode($this,$this->barcode_type_code);
 
@@ -744,6 +767,7 @@ class Product extends CommonObject
 	        $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1);
 			$sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL');
 			$sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL');
+			// stock field is not here because it is a denormalized value from product_stock.
 			$sql.= " WHERE rowid = " . $id;
 
 			dol_syslog(get_class($this)."::update", LOG_DEBUG);
@@ -1597,7 +1621,7 @@ class Product extends CommonObject
      *  @param	int		$ignore_expression  Ignores the math expression for calculating price and uses the db value instead
 	 *  @return int     					<0 if KO, 0 if not found, >0 if OK
 	 */
-	function fetch($id='', $ref='', $ref_ext='', $ignore_expression = 0)
+	function fetch($id='', $ref='', $ref_ext='', $ignore_expression=0)
 	{
 	    include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
 
diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php
index d4440dac0f3..ed03c3e729e 100644
--- a/htdocs/product/class/productbatch.class.php
+++ b/htdocs/product/class/productbatch.class.php
@@ -40,7 +40,7 @@ class Productbatch extends CommonObject
 	var $batch='';
 	var $qty;
 	public $warehouseid;
-
+	public $fk_product;
 
 
 
@@ -75,7 +75,7 @@ class Productbatch extends CommonObject
 		// Put here code to add control on parameters values
 
         // Insert request
-		$sql = "INSERT INTO ".MAIN_DB_PREFIX.self::$_table_element." (";
+		$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_batch (";
 		$sql.= "fk_product_stock,";
 		$sql.= "sellby,";
 		$sql.= "eatby,";
@@ -143,15 +143,18 @@ class Productbatch extends CommonObject
 
 		$sql.= " t.tms,";
 		$sql.= " t.fk_product_stock,";
-		$sql.= " t.sellby,";
-		$sql.= " t.eatby,";
+		$sql.= " t.sellby as oldsellby,";
+		$sql.= " t.eatby as oldeatby,";
 		$sql.= " t.batch,";
 		$sql.= " t.qty,";
 		$sql.= " t.import_key,";
-		$sql.= " w.fk_entrepot";
+		$sql.= " w.fk_entrepot,";
+		$sql.= " w.fk_product,";
+		$sql.= " pl.eatby,";
+		$sql.= " pl.sellby";
 
-        $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t";
-        $sql.= " INNER JOIN ".MAIN_DB_PREFIX."product_stock w on t.fk_product_stock=w.rowid";
+        $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t INNER JOIN ".MAIN_DB_PREFIX."product_stock w on t.fk_product_stock = w.rowid";
+        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl on pl.fk_product = w.fk_product and pl.batch = t.batch"; 
         $sql.= " WHERE t.rowid = ".$id;
 
 		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
@@ -165,12 +168,13 @@ class Productbatch extends CommonObject
 				$this->id    = $obj->rowid;
 				$this->tms = $this->db->jdate($obj->tms);
 				$this->fk_product_stock = $obj->fk_product_stock;
-				$this->sellby = $this->db->jdate($obj->sellby);
-				$this->eatby = $this->db->jdate($obj->eatby);
+				$this->sellby = $this->db->jdate($obj->sellby?$obj->sellby:$obj->oldsellby);
+				$this->eatby = $this->db->jdate($obj->eatby?$obj->eatby:$obj->oldeatby);
 				$this->batch = $obj->batch;
 				$this->qty = $obj->qty;
 				$this->import_key = $obj->import_key;
 				$this->warehouseid= $obj->fk_entrepot;
+				$this->fk_product= $obj->fk_product;
 			}
 			$this->db->free($resql);
 
@@ -408,9 +412,9 @@ class Productbatch extends CommonObject
      *  Find first detail record that match eather eat-by or sell-by or batch within given warehouse
      *
      *  @param	int			$fk_product_stock   id product_stock for objet
-     *  @param	date		$eatby    			eat-by date for objet - deprecated: a search must be done on batch number
-     *  @param	date		$sellby   			sell-by date for objet - deprecated: a search must be done on batch number
-     *  @param	string		$batch_number   	batch number for objet
+     *  @param	date		$eatby    			eat-by date for object - deprecated: a search must be done on batch number
+     *  @param	date		$sellby   			sell-by date for object - deprecated: a search must be done on batch number
+     *  @param	string		$batch_number   	batch number for object
      *  @return int          					<0 if KO, >0 if OK
      */
     function find($fk_product_stock=0, $eatby='',$sellby='',$batch_number='')
@@ -421,8 +425,8 @@ class Productbatch extends CommonObject
 		$sql.= " t.rowid,";
 		$sql.= " t.tms,";
 		$sql.= " t.fk_product_stock,";
-		$sql.= " t.sellby,";
-		$sql.= " t.eatby,";
+		$sql.= " t.sellby,";              // deprecated
+		$sql.= " t.eatby,";               // deprecated
 		$sql.= " t.batch,";
 		$sql.= " t.qty,";
 		$sql.= " t.import_key";
@@ -431,6 +435,7 @@ class Productbatch extends CommonObject
 
 		if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'");            // deprecated
 		if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'");         // deprecated
+		
 		if (! empty($batch_number)) $sql.= " AND batch = '".$this->db->escape($batch_number)."'";
 
 		if (! empty($where)) $sql.= " AND (".implode(" OR ",$where).")";
@@ -468,10 +473,11 @@ class Productbatch extends CommonObject
      *
      *  @param	DoliDB		$db    				database object
      *  @param	int			$fk_product_stock	id product_stock for objet
-     *  @param	int			$with_qty    		doesn't return line with 0 quantity
+     *  @param	int			$with_qty    		1 = doesn't return line with 0 quantity
+     *  @param  int         $fk_product         If set to a product id, get eatby and sellby from table llx_product_lot
      *  @return array         					<0 if KO, array of batch
      */
-    public static function findAll($db,$fk_product_stock,$with_qty=0)
+    public static function findAll($db, $fk_product_stock, $with_qty=0, $fk_product=0)
     {
     	global $langs;
 		$ret = array();
@@ -480,14 +486,22 @@ class Productbatch extends CommonObject
 		$sql.= " t.rowid,";
 		$sql.= " t.tms,";
 		$sql.= " t.fk_product_stock,";
-		$sql.= " t.sellby,";
-		$sql.= " t.eatby,";
+		$sql.= " t.sellby as oldsellby,";     // deprecated but may not be migrated into new table
+		$sql.= " t.eatby as oldeatby,";       // deprecated but may not be migrated into new table
 		$sql.= " t.batch,";
 		$sql.= " t.qty,";
 		$sql.= " t.import_key";
+		if ($fk_product > 0)
+		{
+		    $sql.= ", pl.eatby as eatby, pl.sellby as sellby";
+		}
         $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t";
+        if ($fk_product > 0)
+        {
+            $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON pl.fk_product = ".$fk_product." AND pl.batch = t.batch";
+        }
 		$sql.= " WHERE fk_product_stock=".$fk_product_stock;
-		if ($with_qty) $sql.= " AND qty<>0";
+		if ($with_qty) $sql.= " AND t.qty <> 0";
 
 		dol_syslog("productbatch::findAll", LOG_DEBUG);
 		$resql=$db->query($sql);
@@ -499,12 +513,12 @@ class Productbatch extends CommonObject
             {
                 $obj = $db->fetch_object($resql);
 
-				$tmp = new productbatch($db);
+				$tmp = new Productbatch($db);
 				$tmp->id    = $obj->rowid;
 				$tmp->tms = $db->jdate($obj->tms);
 				$tmp->fk_product_stock = $obj->fk_product_stock;
-				$tmp->sellby = $db->jdate($obj->sellby);
-				$tmp->eatby = $db->jdate($obj->eatby);
+				$tmp->sellby = $db->jdate($obj->sellby ? $obj->sellby : $obj->oldsellby);
+				$tmp->eatby = $db->jdate($obj->eatby ? $obj->eatby : $obj->oldeatby);
 				$tmp->batch = $obj->batch;
 				$tmp->qty = $obj->qty;
 				$tmp->import_key = $obj->import_key;
diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php
index b23a1cbf25c..f7d5e60ca72 100644
--- a/htdocs/product/reassortlot.php
+++ b/htdocs/product/reassortlot.php
@@ -105,15 +105,17 @@ $htmlother=new FormOther($db);
 
 $title=$langs->trans("ProductsAndServices");
 
-$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,p.entity,';
+$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
 $sql.= ' p.fk_product_type, p.tms as datem,';
-$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock,';
+$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tobatch,';
 $sql.= ' s.fk_entrepot,';
-$sql.= ' pb.batch, pb.eatby, pb.sellby,';
-$sql.= ' SUM(pb.qty) as stock_physique';
+$sql.= ' pb.batch, pb.eatby as oldeatby, pb.sellby as oldsellby,';
+$sql.= ' pl.eatby, pl.sellby,';
+$sql.= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable';
 $sql.= ' FROM '.MAIN_DB_PREFIX.'product as p';
 $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s on p.rowid = s.fk_product';
 $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = s.rowid';
+$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch';
 // We'll need this table joined to the select in order to filter by categ
 if ($search_categ) $sql.= ", ".MAIN_DB_PREFIX."categorie_product as cp";
 $sql.= " WHERE p.entity IN (".getEntity('product', 1).")";
@@ -166,10 +168,12 @@ if ($search_batch)
 {
 	$sql .= " AND pb.batch LIKE '%".$db->escape($search_batch)."%'";
 }
-$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,";
-$sql.= " p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock,";
+$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,";
+$sql.= " p.fk_product_type, p.tms,";
+$sql.= " p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tobatch,";
 $sql.= " s.fk_entrepot,";
-$sql.= " pb.batch, pb.eatby, pb.sellby";
+$sql.= " pb.batch, pb.eatby, pb.sellby,";
+$sql.= " pl.eatby, pl.sellby";
 if ($toolowstock) $sql.= " HAVING SUM(".$db->ifsql('s.reel IS NULL', '0', 's.reel').") < p.seuil_stock_alerte";    // Not used yet
 $sql.= $db->order($sortfield,$sortorder);
 $sql.= $db->plimit($limit + 1, $offset);
@@ -364,7 +368,7 @@ if ($resql)
         //if ($objp->seuil_stock_alerte && ($objp->stock_physique < $objp->seuil_stock_alerte)) print img_warning($langs->trans("StockTooLow")).' ';
 		print $objp->stock_physique;
 		print '</td>';
-		print '<td align="right"><a href="'.DOL_URL_ROOT.'/product/stock/mouvement.php?idproduct='.$product_static->id.'&search_batch='.$objp->batch.'">'.$langs->trans("Movements").'</a></td>';
+		print '<td align="right"><a href="'.DOL_URL_ROOT.'/product/stock/mouvement.php?idproduct='.$product_static->id.'&search_warehouse='.$objp->fk_entrepot.'&search_batch='.($objp->batch != 'Undefined' ? $objp->batch : 'Undefined').'">'.$langs->trans("Movements").'</a></td>';
 		print '<td align="right" class="nowrap">'.$product_static->LibStatut($objp->statut,5,0).'</td>';
         print '<td align="right" class="nowrap">'.$product_static->LibStatut($objp->tobuy,5,1).'</td>';
 		print "</tr>\n";
diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php
index 9b6db71be9e..b9859f9ec9b 100644
--- a/htdocs/product/stock/class/mouvementstock.class.php
+++ b/htdocs/product/stock/class/mouvementstock.class.php
@@ -117,7 +117,6 @@ class MouvementStock extends CommonObject
 		// Test if product require batch data. If yes, and there is not, we throw an error.
 		if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch)
 		{
-			//if (empty($batch) && empty($eatby) && empty($sellby))
 			if (empty($batch))
 			{
 				$this->errors[]=$langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->name);
@@ -130,8 +129,9 @@ class MouvementStock extends CommonObject
 			// FIXME Code not complete to implement this
 			// Check table llx_product_lot from batchnumber for same product
 			// If found and eatby/sellby defined into table and provided and differs, return error
-			// If found and eatby/sellby not defined into table and provided, we update table
 			// If found and eatby/sellby defined into table and not provided, we take value from table
+			// If found and eatby/sellby not defined into table and provided, we update table
+			// If found and eatby/sellby not defined into table and not provided, we do nothing
 			// If not found, we add record
 			$sql = "SELECT pb.rowid, pb.batch, pb.eatby, pb.sellby FROM ".MAIN_DB_PREFIX."product_lot as pb";
 			$sql.= " WHERE pb.fk_product = ".$fk_product." AND pb.batch = '".$this->db->escape($batch)."'";
@@ -158,6 +158,27 @@ class MouvementStock extends CommonObject
                                     return -3;
                                 }
                             }
+                            else
+                            {
+                                $eatby = $obj->eatby; // If found and eatby/sellby defined into table and not provided, we take value from table
+                            }
+                        }
+                        else
+                        {
+                            if ($eatby) // If found and eatby/sellby not defined into table and provided, we update table
+                            {
+                                $productlot = new Productlot($this->db);
+                                $result = $productlot->fetch($obj->rowid);
+                                $productlot->eatby = $eatby;
+                                $result = $productlot->update($user);
+                                if ($result <= 0)
+                                {
+                                    $this->error = $productlot->error;
+                                    $this->errors = $productlot->errors;
+                                    $this->db->rollback();
+                                    return -5;
+                                }                            
+                            }
                         }
                         if ($obj->sellby)
                         {
@@ -171,8 +192,30 @@ class MouvementStock extends CommonObject
                         			return -3;
                         		}
                             }
+                            else
+                            {
+                                $sellby = $obj->sellby; // If found and eatby/sellby defined into table and not provided, we take value from table
+                            }
+                        }
+                	    else
+                        {
+                            if ($sellby) // If found and eatby/sellby not defined into table and provided, we update table
+                            {
+                                $productlot = new Productlot($this->db);
+                                $result = $productlot->fetch($obj->rowid);
+                                $productlot->sellby = $sellby;
+                                $result = $productlot->update($user);
+                                if ($result <= 0)
+                                {
+                                    $this->error = $productlot->error;
+                                    $this->errors = $productlot->errors;
+                                    $this->db->rollback();
+                                    return -5;
+                                }
+                            }
                         }
-                		$i++;
+                        
+                        $i++;
                 	}
             	}
             	else
@@ -181,7 +224,7 @@ class MouvementStock extends CommonObject
             	    $productlot->fk_product = $fk_product;
             	    $productlot->batch = $batch;
             	    $result = $productlot->create($user);
-            	    if (! $result)
+            	    if ($result <= 0)
             	    {
             	        $this->error = $productlot->error;
             	        $this->errors = $productlot->errors;
@@ -367,7 +410,7 @@ class MouvementStock extends CommonObject
 				$sql = "UPDATE ".MAIN_DB_PREFIX."product as p SET p.pmp = ".$newpmp.", ";
 				$sql.= " stock=(SELECT SUM(ps.reel) FROM ".MAIN_DB_PREFIX."product_stock ps WHERE ps.fk_product = p.rowid)";
 				$sql.= " WHERE rowid = ".$fk_product;
-				print $sql;
+				
 				dol_syslog(get_class($this)."::_create", LOG_DEBUG);
 				$resql=$this->db->query($sql);
 				if (! $resql)
diff --git a/htdocs/product/stock/class/productlot.class.php b/htdocs/product/stock/class/productlot.class.php
index 26970a68e85..9b15ed31fcc 100644
--- a/htdocs/product/stock/class/productlot.class.php
+++ b/htdocs/product/stock/class/productlot.class.php
@@ -56,14 +56,15 @@ class Productlot extends CommonObject
 	/**
 	 */
 	
-	public $tms = '';
+	public $entity;
 	public $fk_product;
 	public $batch;
 	public $eatby = '';
 	public $sellby = '';
-	public $note_public;
-	public $note_private;
-	public $qty;
+	public $datec = '';
+	public $tms = '';
+	public $fk_user_creat;
+	public $fk_user_modif;
 	public $import_key;
 
 	/**
@@ -96,20 +97,20 @@ class Productlot extends CommonObject
 
 		// Clean parameters
 		
+		if (isset($this->entity)) {
+			 $this->entity = trim($this->entity);
+		}
 		if (isset($this->fk_product)) {
 			 $this->fk_product = trim($this->fk_product);
 		}
 		if (isset($this->batch)) {
 			 $this->batch = trim($this->batch);
 		}
-		if (isset($this->note_public)) {
-			 $this->note_public = trim($this->note_public);
+		if (isset($this->fk_user_creat)) {
+			 $this->fk_user_creat = trim($this->fk_user_creat);
 		}
-		if (isset($this->note_private)) {
-			 $this->note_private = trim($this->note_private);
-		}
-		if (isset($this->qty)) {
-			 $this->qty = trim($this->qty);
+		if (isset($this->fk_user_modif)) {
+			 $this->fk_user_modif = trim($this->fk_user_modif);
 		}
 		if (isset($this->import_key)) {
 			 $this->import_key = trim($this->import_key);
@@ -123,26 +124,28 @@ class Productlot extends CommonObject
 		// Insert request
 		$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
 		
+		$sql.= 'entity,';
 		$sql.= 'fk_product,';
 		$sql.= 'batch,';
 		$sql.= 'eatby,';
 		$sql.= 'sellby,';
-		$sql.= 'note_public,';
-		$sql.= 'note_private,';
-		$sql.= 'qty';
+		$sql.= 'datec,';
+		$sql.= 'fk_user_creat,';
+		$sql.= 'fk_user_modif,';
 		$sql.= 'import_key';
 
 		
 		$sql .= ') VALUES (';
 		
+		$sql .= ' '.(! isset($this->entity)?'NULL':$this->entity).',';
 		$sql .= ' '.(! isset($this->fk_product)?'NULL':$this->fk_product).',';
 		$sql .= ' '.(! isset($this->batch)?'NULL':"'".$this->db->escape($this->batch)."'").',';
 		$sql .= ' '.(! isset($this->eatby) || dol_strlen($this->eatby)==0?'NULL':"'".$this->db->idate($this->eatby)."'").',';
 		$sql .= ' '.(! isset($this->sellby) || dol_strlen($this->sellby)==0?'NULL':"'".$this->db->idate($this->sellby)."'").',';
-		$sql .= ' '.(! isset($this->note_public)?'NULL':"'".$this->db->escape($this->note_public)."'").',';
-		$sql .= ' '.(! isset($this->note_private)?'NULL':"'".$this->db->escape($this->note_private)."'").',';
-		$sql .= ' '.(! isset($this->qty)?'NULL':"'".$this->qty."'").',';
-		$sql .= ' '.(! isset($this->import_key)?'NULL':"'".$this->db->escape($this->import_key)."'");
+		$sql .= ' '."'".$this->db->idate(dol_now())."'".',';
+		$sql .= ' '.(! isset($this->fk_user_creat)?'NULL':$this->fk_user_creat).',';
+		$sql .= ' '.(! isset($this->fk_user_modif)?'NULL':$this->fk_user_modif).',';
+		$sql .= ' '.(! isset($this->import_key)?'NULL':$this->import_key);
 
 		
 		$sql .= ')';
@@ -197,14 +200,15 @@ class Productlot extends CommonObject
 		$sql = 'SELECT';
 		$sql .= ' t.rowid,';
 		
-		$sql .= " t.tms,";
+		$sql .= " t.entity,";
 		$sql .= " t.fk_product,";
 		$sql .= " t.batch,";
 		$sql .= " t.eatby,";
 		$sql .= " t.sellby,";
-		$sql .= " t.note_public,";
-		$sql .= " t.note_private,";
-		$sql .= " t.qty,";
+		$sql .= " t.datec,";
+		$sql .= " t.tms,";
+		$sql .= " t.fk_user_creat,";
+		$sql .= " t.fk_user_modif,";
 		$sql .= " t.import_key";
 
 		
@@ -223,14 +227,15 @@ class Productlot extends CommonObject
 
 				$this->id = $obj->rowid;
 				
-				$this->tms = $this->db->jdate($obj->tms);
+				$this->entity = $obj->entity;
 				$this->fk_product = $obj->fk_product;
 				$this->batch = $obj->batch;
 				$this->eatby = $this->db->jdate($obj->eatby);
 				$this->sellby = $this->db->jdate($obj->sellby);
-				$this->note_public = $obj->note_public;
-				$this->note_private = $obj->note_private;
-				$this->qty = $obj->qty;
+				$this->datec = $this->db->jdate($obj->datec);
+				$this->tms = $this->db->jdate($obj->tms);
+				$this->fk_user_creat = $obj->fk_user_creat;
+				$this->fk_user_modif = $obj->fk_user_modif;
 				$this->import_key = $obj->import_key;
 
 				
@@ -269,14 +274,15 @@ class Productlot extends CommonObject
 		$sql = 'SELECT';
 		$sql .= ' t.rowid,';
 		
-		$sql .= " t.tms,";
+		$sql .= " t.entity,";
 		$sql .= " t.fk_product,";
 		$sql .= " t.batch,";
 		$sql .= " t.eatby,";
 		$sql .= " t.sellby,";
-		$sql .= " t.note_public,";
-		$sql .= " t.note_private,";
-		$sql .= " t.qty,";
+		$sql .= " t.datec,";
+		$sql .= " t.tms,";
+		$sql .= " t.fk_user_creat,";
+		$sql .= " t.fk_user_modif,";
 		$sql .= " t.import_key";
 
 		
@@ -310,14 +316,15 @@ class Productlot extends CommonObject
 
 				$line->id = $obj->rowid;
 				
-				$line->tms = $this->db->jdate($obj->tms);
+				$line->entity = $obj->entity;
 				$line->fk_product = $obj->fk_product;
 				$line->batch = $obj->batch;
 				$line->eatby = $this->db->jdate($obj->eatby);
 				$line->sellby = $this->db->jdate($obj->sellby);
-				$line->note_public = $obj->note_public;
-				$line->note_private = $obj->note_private;
-				$line->qty = $obj->qty;
+				$line->datec = $this->db->jdate($obj->datec);
+				$line->tms = $this->db->jdate($obj->tms);
+				$line->fk_user_creat = $obj->fk_user_creat;
+				$line->fk_user_modif = $obj->fk_user_modif;
 				$line->import_key = $obj->import_key;
 
 				
@@ -351,20 +358,20 @@ class Productlot extends CommonObject
 
 		// Clean parameters
 		
+		if (isset($this->entity)) {
+			 $this->entity = trim($this->entity);
+		}
 		if (isset($this->fk_product)) {
 			 $this->fk_product = trim($this->fk_product);
 		}
 		if (isset($this->batch)) {
 			 $this->batch = trim($this->batch);
 		}
-		if (isset($this->note_public)) {
-			 $this->note_public = trim($this->note_public);
+		if (isset($this->fk_user_creat)) {
+			 $this->fk_user_creat = trim($this->fk_user_creat);
 		}
-		if (isset($this->note_private)) {
-			 $this->note_private = trim($this->note_private);
-		}
-		if (isset($this->qty)) {
-			 $this->qty = trim($this->qty);
+		if (isset($this->fk_user_modif)) {
+			 $this->fk_user_modif = trim($this->fk_user_modif);
 		}
 		if (isset($this->import_key)) {
 			 $this->import_key = trim($this->import_key);
@@ -378,15 +385,16 @@ class Productlot extends CommonObject
 		// Update request
 		$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
 		
-		$sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'").',';
+		$sql .= ' entity = '.(isset($this->entity)?$this->entity:"null").',';
 		$sql .= ' fk_product = '.(isset($this->fk_product)?$this->fk_product:"null").',';
 		$sql .= ' batch = '.(isset($this->batch)?"'".$this->db->escape($this->batch)."'":"null").',';
 		$sql .= ' eatby = '.(! isset($this->eatby) || dol_strlen($this->eatby) != 0 ? "'".$this->db->idate($this->eatby)."'" : 'null').',';
 		$sql .= ' sellby = '.(! isset($this->sellby) || dol_strlen($this->sellby) != 0 ? "'".$this->db->idate($this->sellby)."'" : 'null').',';
-		$sql .= ' note_public = '.(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").',';
-		$sql .= ' note_private = '.(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").',';
-		$sql .= ' qty = '.(isset($this->qty)?$this->qty:"null").',';
-		$sql .= ' import_key = '.(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null");
+		$sql .= ' datec = '.(! isset($this->datec) || dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').',';
+		$sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'").',';
+		$sql .= ' fk_user_creat = '.(isset($this->fk_user_creat)?$this->fk_user_creat:"null").',';
+		$sql .= ' fk_user_modif = '.(isset($this->fk_user_modif)?$this->fk_user_modif:"null").',';
+		$sql .= ' import_key = '.(isset($this->import_key)?$this->import_key:"null");
 
         
 		$sql .= ' WHERE rowid=' . $this->id;
@@ -626,14 +634,15 @@ class Productlot extends CommonObject
 	{
 		$this->id = 0;
 		
-		$this->tms = '';
+		$this->entity = '';
 		$this->fk_product = '';
 		$this->batch = '';
 		$this->eatby = '';
 		$this->sellby = '';
-		$this->note_public = '';
-		$this->note_private = '';
-		$this->qty = '';
+		$this->datec = '';
+		$this->tms = '';
+		$this->fk_user_creat = '';
+		$this->fk_user_modif = '';
 		$this->import_key = '';
 
 		
@@ -654,14 +663,15 @@ class ProductlotLine
 	 * @var mixed Sample line property 1
 	 */
 	
-	public $tms = '';
+	public $entity;
 	public $fk_product;
 	public $batch;
 	public $eatby = '';
 	public $sellby = '';
-	public $note_public;
-	public $note_private;
-	public $qty;
+	public $datec = '';
+	public $tms = '';
+	public $fk_user_creat;
+	public $fk_user_modif;
 	public $import_key;
 
 	/**
diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php
index 29ee33c56eb..b18ca146a25 100644
--- a/htdocs/product/stock/mouvement.php
+++ b/htdocs/product/stock/mouvement.php
@@ -367,7 +367,7 @@ if (! empty($search_movement))      $sql.= " AND m.label LIKE '%".$db->escape($s
 if (! empty($search_inventorycode)) $sql.= " AND m.inventorycode LIKE '%".$db->escape($search_inventorycode)."%'";
 if (! empty($search_product_ref))   $sql.= " AND p.ref LIKE '%".$db->escape($search_product_ref)."%'";
 if (! empty($search_product))       $sql.= " AND p.label LIKE '%".$db->escape($search_product)."%'";
-if (! empty($search_warehouse))     $sql.= " AND e.label LIKE '%".$db->escape($search_warehouse)."%'";
+if (! empty($search_warehouse))     $sql.= " AND e.rowid = '".$db->escape($search_warehouse)."'";
 if (! empty($search_user))          $sql.= " AND u.login LIKE '%".$db->escape($search_user)."%'";
 if (! empty($search_batch))         $sql.= " AND m.batch LIKE '%".$db->escape($search_batch)."%'";
 
@@ -702,7 +702,8 @@ if ($resql)
     if (! $id > 0) 
     {
         print '<td class="liste_titre" align="left">';
-        print '<input class="flat" type="text" size="8" name="search_warehouse" value="'.($search_warehouse).'">';
+        //print '<input class="flat" type="text" size="8" name="search_warehouse" value="'.($search_warehouse).'">';
+        print $formproduct->selectWarehouses($search_warehouse, 'search_warehouse', '', 1);
         print '</td>';
     }
     // Author
diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php
index 499efd29847..1b4c159b1d6 100644
--- a/htdocs/product/stock/product.php
+++ b/htdocs/product/stock/product.php
@@ -273,6 +273,10 @@ if ($action == "transfert_stock" && ! $cancel)
 						$eatby,$sellby,$batch,
 						GETPOST('inventorycode')
 					);
+					if ($result1 < 0) $error++;
+				}
+				if (! $error)
+				{
 					// Add stock
 					$result2=$object->correct_stock_batch(
 						$user,
@@ -284,31 +288,39 @@ if ($action == "transfert_stock" && ! $cancel)
 						$eatby,$sellby,$batch,
 						GETPOST('inventorycode')
 					);
+					if ($result2 < 0) $error++;
 				}
 			}
 			else
 			{
-				// Remove stock
-				$result1=$object->correct_stock(
-					$user,
-					GETPOST("id_entrepot"),
-					GETPOST("nbpiece"),
-					1,
-					GETPOST("label"),
-					$pricesrc,
-					GETPOST('inventorycode')
-				);
-
-				// Add stock
-				$result2=$object->correct_stock(
-					$user,
-					GETPOST("id_entrepot_destination"),
-					GETPOST("nbpiece"),
-					0,
-					GETPOST("label"),
-					$pricedest,
-					GETPOST('inventorycode')
-				);
+				if (! $error)
+				{
+    			    // Remove stock
+    				$result1=$object->correct_stock(
+    					$user,
+    					GETPOST("id_entrepot"),
+    					GETPOST("nbpiece"),
+    					1,
+    					GETPOST("label"),
+    					$pricesrc,
+    					GETPOST('inventorycode')
+    				);
+    				if ($result1 < 0) $error++;
+				}
+				if (! $error)
+				{
+    				// Add stock
+    				$result2=$object->correct_stock(
+    					$user,
+    					GETPOST("id_entrepot_destination"),
+    					GETPOST("nbpiece"),
+    					0,
+    					GETPOST("label"),
+    					$pricedest,
+    					GETPOST('inventorycode')
+    				);
+    				if ($result2 < 0) $error++;
+				}
 			}
 			if (! $error && $result1 >= 0 && $result2 >= 0)
 			{
@@ -589,7 +601,7 @@ if ($id > 0 || $ref)
 	if ($action == "correction")
 	{
 		include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stockcorrection.tpl.php';
-		print '<br>';
+		print '<br><br>';
 	}
 
 	/*
@@ -598,7 +610,7 @@ if ($id > 0 || $ref)
 	if ($action == "transfert")
 	{
 		include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stocktransfer.tpl.php';
-		print '<br>';
+		print '<br><br>';
 	}
 
 	/*
@@ -657,10 +669,10 @@ if (empty($action) && $object->id)
 
 
 /*
- * Stock detail (by warehouse). Do not go down into batch.
+ * Stock detail (by warehouse). May go down into batch details.
  */
 
-print '<br><table class="noborder" width="100%">';
+print '<table class="noborder" width="100%">';
 print '<tr class="liste_titre"><td width="40%" colspan="4">'.$langs->trans("Warehouse").'</td>';
 print '<td align="right">'.$langs->trans("NumberOfUnit").'</td>';
 print '<td align="right">'.$langs->trans("AverageUnitPricePMPShort").'</td>';
@@ -726,14 +738,14 @@ if ($resql)
 		if (price2num($object->pmp)) $totalwithpmp += $obj->reel;
 		$totalvalue = $totalvalue + ($object->pmp*$obj->reel);
         $totalvaluesell = $totalvaluesell + ($object->price*$obj->reel);
-		//Batch Detail
+		// Batch Detail
 		if ((! empty($conf->productbatch->enabled)) && $object->hasbatch())
 		{
-			$details=Productbatch::findAll($db,$obj->product_stock_id);
+			$details=Productbatch::findAll($db, $obj->product_stock_id, 0, $object->id);
 			if ($details<0) dol_print_error($db);
 			foreach ($details as $pdluo)
 			{
-			    if ( $action == 'editline' && GETPOST('lineid','int')==$pdluo->id )
+			    if ($action == 'editline' && GETPOST('lineid','int') == $pdluo->id)
 			    { //Current line edit
 			        print "\n".'<tr><td colspan="9">';
 			        print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST"><input type="hidden" name="pdluoid" value="'.$pdluo->id.'"><input type="hidden" name="action" value="updateline"><input type="hidden" name="id" value="'.$id.'"><table class="noborder" width="100%"><tr><td width="10%"></td>';
diff --git a/htdocs/product/stock/productlot_card.php b/htdocs/product/stock/productlot_card.php
index 2e8620797c7..4af5b8a4d59 100644
--- a/htdocs/product/stock/productlot_card.php
+++ b/htdocs/product/stock/productlot_card.php
@@ -20,7 +20,7 @@
  *   	\file       stock/productlot_card.php
  *		\ingroup    stock
  *		\brief      This file is an example of a php page
- *					Initialy built by build_class_from_table on 2016-05-17 10:33
+ *					Initialy built by build_class_from_table on 2016-05-17 12:22
  */
 
 //if (! defined('NOREQUIREUSER'))  define('NOREQUIREUSER','1');
@@ -57,12 +57,12 @@ $backtopage = GETPOST('backtopage');
 $myparam	= GETPOST('myparam','alpha');
 
 
+$search_entity=GETPOST('search_entity','int');
 $search_fk_product=GETPOST('search_fk_product','int');
 $search_batch=GETPOST('search_batch','alpha');
-$search_note_public=GETPOST('search_note_public','alpha');
-$search_note_private=GETPOST('search_note_private','alpha');
-$search_qty=GETPOST('search_qty','alpha');
-$search_import_key=GETPOST('search_import_key','alpha');
+$search_fk_user_creat=GETPOST('search_fk_user_creat','int');
+$search_fk_user_modif=GETPOST('search_fk_user_modif','int');
+$search_import_key=GETPOST('search_import_key','int');
 
 
 
@@ -114,12 +114,12 @@ if (empty($reshook))
 
 		/* object_prop_getpost_prop */
 		
+	$object->entity=GETPOST('entity','int');
 	$object->fk_product=GETPOST('fk_product','int');
 	$object->batch=GETPOST('batch','alpha');
-	$object->note_public=GETPOST('note_public','alpha');
-	$object->note_private=GETPOST('note_private','alpha');
-	$object->qty=GETPOST('qty','alpha');
-	$object->import_key=GETPOST('import_key','alpha');
+	$object->fk_user_creat=GETPOST('fk_user_creat','int');
+	$object->fk_user_modif=GETPOST('fk_user_modif','int');
+	$object->import_key=GETPOST('import_key','int');
 
 		
 
@@ -161,12 +161,12 @@ if (empty($reshook))
 		$error=0;
 
 		
+	$object->entity=GETPOST('entity','int');
 	$object->fk_product=GETPOST('fk_product','int');
 	$object->batch=GETPOST('batch','alpha');
-	$object->note_public=GETPOST('note_public','alpha');
-	$object->note_private=GETPOST('note_private','alpha');
-	$object->qty=GETPOST('qty','alpha');
-	$object->import_key=GETPOST('import_key','alpha');
+	$object->fk_user_creat=GETPOST('fk_user_creat','int');
+	$object->fk_user_modif=GETPOST('fk_user_modif','int');
+	$object->import_key=GETPOST('import_key','int');
 
 		
 
@@ -262,11 +262,11 @@ if ($action == 'create')
 	print '<table class="border centpercent">'."\n";
 	// print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td><input class="flat" type="text" size="36" name="label" value="'.$label.'"></td></tr>';
 	// 
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldentity").'</td><td><input class="flat" type="text" name="entity" value="'.GETPOST('entity').'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_product").'</td><td><input class="flat" type="text" name="fk_product" value="'.GETPOST('fk_product').'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldbatch").'</td><td><input class="flat" type="text" name="batch" value="'.GETPOST('batch').'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_public").'</td><td><input class="flat" type="text" name="note_public" value="'.GETPOST('note_public').'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_private").'</td><td><input class="flat" type="text" name="note_private" value="'.GETPOST('note_private').'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldqty").'</td><td><input class="flat" type="text" name="qty" value="'.GETPOST('qty').'"></td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_creat").'</td><td><input class="flat" type="text" name="fk_user_creat" value="'.GETPOST('fk_user_creat').'"></td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_modif").'</td><td><input class="flat" type="text" name="fk_user_modif" value="'.GETPOST('fk_user_modif').'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldimport_key").'</td><td><input class="flat" type="text" name="import_key" value="'.GETPOST('import_key').'"></td></tr>';
 
 	print '</table>'."\n";
@@ -295,11 +295,11 @@ if (($id || $ref) && $action == 'edit')
 	print '<table class="border centpercent">'."\n";
 	// print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td><input class="flat" type="text" size="36" name="label" value="'.$label.'"></td></tr>';
 	// 
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldentity").'</td><td><input class="flat" type="text" name="entity" value="'.$object->entity.'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_product").'</td><td><input class="flat" type="text" name="fk_product" value="'.$object->fk_product.'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldbatch").'</td><td><input class="flat" type="text" name="batch" value="'.$object->batch.'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_public").'</td><td><input class="flat" type="text" name="note_public" value="'.$object->note_public.'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_private").'</td><td><input class="flat" type="text" name="note_private" value="'.$object->note_private.'"></td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldqty").'</td><td><input class="flat" type="text" name="qty" value="'.$object->qty.'"></td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_creat").'</td><td><input class="flat" type="text" name="fk_user_creat" value="'.$object->fk_user_creat.'"></td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_modif").'</td><td><input class="flat" type="text" name="fk_user_modif" value="'.$object->fk_user_modif.'"></td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldimport_key").'</td><td><input class="flat" type="text" name="import_key" value="'.$object->import_key.'"></td></tr>';
 
 	print '</table>';
@@ -330,11 +330,11 @@ if ($id && (empty($action) || $action == 'view' || $action == 'delete'))
 	print '<table class="border centpercent">'."\n";
 	// print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td><input class="flat" type="text" size="36" name="label" value="'.$label.'"></td></tr>';
 	// 
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldentity").'</td><td>$object->entity</td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_product").'</td><td>$object->fk_product</td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldbatch").'</td><td>$object->batch</td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_public").'</td><td>$object->note_public</td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldnote_private").'</td><td>$object->note_private</td></tr>';
-print '<tr><td class="fieldrequired">'.$langs->trans("Fieldqty").'</td><td>$object->qty</td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_creat").'</td><td>$object->fk_user_creat</td></tr>';
+print '<tr><td class="fieldrequired">'.$langs->trans("Fieldfk_user_modif").'</td><td>$object->fk_user_modif</td></tr>';
 print '<tr><td class="fieldrequired">'.$langs->trans("Fieldimport_key").'</td><td>$object->import_key</td></tr>';
 
 	print '</table>';
diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php
index ded98827e31..a8fd68f6a34 100644
--- a/htdocs/product/stock/productlot_list.php
+++ b/htdocs/product/stock/productlot_list.php
@@ -20,7 +20,7 @@
  *   	\file       stock/productlot_list.php
  *		\ingroup    stock
  *		\brief      This file is an example of a php page
- *					Initialy built by build_class_from_table on 2016-05-17 10:33
+ *					Initialy built by build_class_from_table on 2016-05-17 12:22
  */
 
 //if (! defined('NOREQUIREUSER'))  define('NOREQUIREUSER','1');
@@ -59,12 +59,12 @@ $backtopage = GETPOST('backtopage');
 $myparam	= GETPOST('myparam','alpha');
 
 
+$search_entity=GETPOST('search_entity','int');
 $search_fk_product=GETPOST('search_fk_product','int');
 $search_batch=GETPOST('search_batch','alpha');
-$search_note_public=GETPOST('search_note_public','alpha');
-$search_note_private=GETPOST('search_note_private','alpha');
-$search_qty=GETPOST('search_qty','alpha');
-$search_import_key=GETPOST('search_import_key','alpha');
+$search_fk_user_creat=GETPOST('search_fk_user_creat','int');
+$search_fk_user_modif=GETPOST('search_fk_user_modif','int');
+$search_import_key=GETPOST('search_import_key','int');
 
 
 $search_myfield=GETPOST('search_myfield');
@@ -109,11 +109,11 @@ if (($id > 0 || ! empty($ref)) && $action != 'add')
 // Definition of fields for list
 $arrayfields=array(
     
+'t.entity'=>array('label'=>$langs->trans("Fieldentity"), 'checked'=>1),
 't.fk_product'=>array('label'=>$langs->trans("Fieldfk_product"), 'checked'=>1),
 't.batch'=>array('label'=>$langs->trans("Fieldbatch"), 'checked'=>1),
-'t.note_public'=>array('label'=>$langs->trans("Fieldnote_public"), 'checked'=>1),
-'t.note_private'=>array('label'=>$langs->trans("Fieldnote_private"), 'checked'=>1),
-'t.qty'=>array('label'=>$langs->trans("Fieldqty"), 'checked'=>1),
+'t.fk_user_creat'=>array('label'=>$langs->trans("Fieldfk_user_creat"), 'checked'=>1),
+'t.fk_user_modif'=>array('label'=>$langs->trans("Fieldfk_user_modif"), 'checked'=>1),
 't.import_key'=>array('label'=>$langs->trans("Fieldimport_key"), 'checked'=>1),
 
     
@@ -152,11 +152,11 @@ include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
 if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers
 {
 	
+$search_entity='';
 $search_fk_product='';
 $search_batch='';
-$search_note_public='';
-$search_note_private='';
-$search_qty='';
+$search_fk_user_creat='';
+$search_fk_user_modif='';
 $search_import_key='';
 
 	
@@ -235,14 +235,15 @@ jQuery(document).ready(function() {
 $sql = "SELECT";
 $sql.= " t.rowid,";
 
-		$sql .= " t.tms,";
+		$sql .= " t.entity,";
 		$sql .= " t.fk_product,";
 		$sql .= " t.batch,";
 		$sql .= " t.eatby,";
 		$sql .= " t.sellby,";
-		$sql .= " t.note_public,";
-		$sql .= " t.note_private,";
-		$sql .= " t.qty,";
+		$sql .= " t.datec,";
+		$sql .= " t.tms,";
+		$sql .= " t.fk_user_creat,";
+		$sql .= " t.fk_user_modif,";
 		$sql .= " t.import_key";
 
 
@@ -257,11 +258,11 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
 $sql.= " WHERE 1 = 1";
 //$sql.= " WHERE u.entity IN (".getEntity('mytable',1).")";
 
+if ($search_entity) $sql.= natural_search("entity",$search_entity);
 if ($search_fk_product) $sql.= natural_search("fk_product",$search_fk_product);
 if ($search_batch) $sql.= natural_search("batch",$search_batch);
-if ($search_note_public) $sql.= natural_search("note_public",$search_note_public);
-if ($search_note_private) $sql.= natural_search("note_private",$search_note_private);
-if ($search_qty) $sql.= natural_search("qty",$search_qty);
+if ($search_fk_user_creat) $sql.= natural_search("fk_user_creat",$search_fk_user_creat);
+if ($search_fk_user_modif) $sql.= natural_search("fk_user_modif",$search_fk_user_modif);
 if ($search_import_key) $sql.= natural_search("import_key",$search_import_key);
 
 
@@ -306,11 +307,11 @@ if ($resql)
     $params='';
     if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
     
+if ($search_entity != '') $params.= '&amp;search_entity='.urlencode($search_entity);
 if ($search_fk_product != '') $params.= '&amp;search_fk_product='.urlencode($search_fk_product);
 if ($search_batch != '') $params.= '&amp;search_batch='.urlencode($search_batch);
-if ($search_note_public != '') $params.= '&amp;search_note_public='.urlencode($search_note_public);
-if ($search_note_private != '') $params.= '&amp;search_note_private='.urlencode($search_note_private);
-if ($search_qty != '') $params.= '&amp;search_qty='.urlencode($search_qty);
+if ($search_fk_user_creat != '') $params.= '&amp;search_fk_user_creat='.urlencode($search_fk_user_creat);
+if ($search_fk_user_modif != '') $params.= '&amp;search_fk_user_modif='.urlencode($search_fk_user_modif);
 if ($search_import_key != '') $params.= '&amp;search_import_key='.urlencode($search_import_key);
 
 	
@@ -389,11 +390,11 @@ if ($search_import_key != '') $params.= '&amp;search_import_key='.urlencode($sea
     // Fields title search
 	print '<tr class="liste_titre">';
 	
+if (! empty($arrayfields['t.entity']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_entity" value="'.$search_entity.'" size="10"></td>';
 if (! empty($arrayfields['t.fk_product']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_fk_product" value="'.$search_fk_product.'" size="10"></td>';
 if (! empty($arrayfields['t.batch']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_batch" value="'.$search_batch.'" size="10"></td>';
-if (! empty($arrayfields['t.note_public']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_note_public" value="'.$search_note_public.'" size="10"></td>';
-if (! empty($arrayfields['t.note_private']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_note_private" value="'.$search_note_private.'" size="10"></td>';
-if (! empty($arrayfields['t.qty']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_qty" value="'.$search_qty.'" size="10"></td>';
+if (! empty($arrayfields['t.fk_user_creat']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_fk_user_creat" value="'.$search_fk_user_creat.'" size="10"></td>';
+if (! empty($arrayfields['t.fk_user_modif']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_fk_user_modif" value="'.$search_fk_user_modif.'" size="10"></td>';
 if (! empty($arrayfields['t.import_key']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_import_key" value="'.$search_import_key.'" size="10"></td>';
 
 	
diff --git a/htdocs/product/stock/tpl/stocktransfer.tpl.php b/htdocs/product/stock/tpl/stocktransfer.tpl.php
index a50fafca278..824eda16d30 100644
--- a/htdocs/product/stock/tpl/stocktransfer.tpl.php
+++ b/htdocs/product/stock/tpl/stocktransfer.tpl.php
@@ -88,7 +88,16 @@
 		{
 			print '<tr>';
 			print '<td'.($object->element == 'stock'?'': ' class="fieldrequired"').'>'.$langs->trans("batch_number").'</td><td colspan="5">';
-			print '<input type="text" name="batch_number" size="40"'.($pdluoid > 0 ? ' disabled':'').' value="'.(GETPOST('batch_number')?GETPOST('batch_number'):$pdluo->batch).'">';			// If form was opened for a specific pdluoid, field is disabled
+			if ($pdluoid > 0)
+			{
+                // If form was opened for a specific pdluoid, field is disabled
+                print '<input type="text" name="batch_number_bis" size="40" disabled="disabled" value="'.(GETPOST('batch_number')?GETPOST('batch_number'):$pdluo->batch).'">';
+			    print '<input type="hidden" name="batch_number" value="'.(GETPOST('batch_number')?GETPOST('batch_number'):$pdluo->batch).'">';
+			}
+			else
+			{
+			    print '<input type="text" name="batch_number" size="40" value="'.(GETPOST('batch_number')?GETPOST('batch_number'):$pdluo->batch).'">';
+			}
 			print '</td>';
 			print '</tr><tr>';
 			print '<td>'.$langs->trans("l_eatby").'</td><td>';
-- 
GitLab