From 56a14da5d4f2d4bd0533564f8f331ca80e27b389 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur <eldy@destailleur.fr>
Date: Sun, 12 Apr 2015 04:01:28 +0200
Subject: [PATCH] FIXME that are not bug are replaced with TODO Add control on
 batch movement (lot/serial is mandatory, and control eatby and sellby are
 same for each lot/serial)

---
 dev/skeletons/skeleton_class.class.php        |  27 +--
 htdocs/accountancy/journal/sellsjournal.php   |   2 +-
 htdocs/adherents/class/adherent.class.php     |   2 +-
 htdocs/categories/class/categorie.class.php   |   4 +-
 htdocs/comm/action/class/actioncomm.class.php |   4 +-
 htdocs/comm/propal.php                        |   2 +-
 htdocs/comm/propal/class/propal.class.php     |  14 +-
 htdocs/commande/card.php                      |   2 +-
 htdocs/commande/class/commande.class.php      |   4 +-
 htdocs/compta/facture.php                     |   2 +-
 .../compta/localtax/class/localtax.class.php  |  37 ++--
 .../salaries/class/paymentsalary.class.php    |  15 +-
 htdocs/compta/tva/class/tva.class.php         |  44 +++--
 htdocs/core/class/commonobject.class.php      |   2 +-
 htdocs/core/db/pgsql.class.php                |   5 +-
 htdocs/core/db/sqlite.class.php               |   5 +-
 htdocs/core/db/sqlite3.class.php              |   5 +-
 htdocs/core/js/timesheet.js                   |   1 -
 htdocs/fichinter/card.php                     |   2 +-
 .../class/fournisseur.commande.class.php      |  44 +----
 .../fourn/class/fournisseur.facture.class.php |   2 +-
 .../fourn/class/fournisseur.product.class.php |   2 +-
 htdocs/fourn/commande/card.php                |   2 +-
 htdocs/fourn/facture/card.php                 |   2 +-
 .../install/mysql/migration/3.7.0-3.8.0.sql   |   2 +
 .../mysql/tables/llx_product_batch.key.sql    |   3 +-
 .../mysql/tables/llx_product_batch.sql        |   2 +-
 htdocs/install/upgrade2.php                   |   2 +-
 htdocs/langs/en_US/admin.lang                 |   8 +-
 htdocs/langs/en_US/errors.lang                |   2 +-
 htdocs/langs/en_US/productbatch.lang          |  22 +--
 htdocs/langs/en_US/stocks.lang                |   6 +-
 htdocs/product/class/product.class.php        |   4 +-
 htdocs/product/index.php                      |   1 -
 .../stock/class/mouvementstock.class.php      |  71 ++++++--
 htdocs/product/stock/massstockmove.php        | 166 ++++++++++++++----
 htdocs/product/stock/replenish.php            |  11 +-
 htdocs/societe/class/societe.class.php        |   2 +-
 htdocs/user/class/usergroup.class.php         |   4 +-
 39 files changed, 338 insertions(+), 199 deletions(-)

diff --git a/dev/skeletons/skeleton_class.class.php b/dev/skeletons/skeleton_class.class.php
index ff0c2f61200..1f5b56e661b 100644
--- a/dev/skeletons/skeleton_class.class.php
+++ b/dev/skeletons/skeleton_class.class.php
@@ -270,28 +270,20 @@ class Skeleton_Class extends CommonObject
         $resql = $this->db->query($sql);
     	if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
 
-		if (! $error)
+		if (! $error && ! $notrigger)
 		{
-			if (! $notrigger)
-			{
-	            // Uncomment this and change MYOBJECT to your own tag if you
-	            // want this action calls a trigger.
+            // Uncomment this and change MYOBJECT to your own tag if you
+            // want this action calls a trigger.
 
-	            //// Call triggers
-	            //$result=$this->call_trigger('MYOBJECT_MODIFY',$user);
-	            //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
-	            //// End call triggers
-			 }
+            //// Call triggers
+            //$result=$this->call_trigger('MYOBJECT_MODIFY',$user);
+            //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
+            //// End call triggers
 		}
 
         // Commit or rollback
 		if ($error)
 		{
-			foreach($this->errors as $errmsg)
-			{
-	            dol_syslog(__METHOD__." ".$errmsg, LOG_ERR);
-	            $this->error.=($this->error?', '.$errmsg:$errmsg);
-			}
 			$this->db->rollback();
 			return -1*$error;
 		}
@@ -344,11 +336,6 @@ class Skeleton_Class extends CommonObject
         // Commit or rollback
 		if ($error)
 		{
-			foreach($this->errors as $errmsg)
-			{
-	            dol_syslog(__METHOD__." ".$errmsg, LOG_ERR);
-	            $this->error.=($this->error?', '.$errmsg:$errmsg);
-			}
 			$this->db->rollback();
 			return -1*$error;
 		}
diff --git a/htdocs/accountancy/journal/sellsjournal.php b/htdocs/accountancy/journal/sellsjournal.php
index 7605d54825c..c2c51bacd42 100644
--- a/htdocs/accountancy/journal/sellsjournal.php
+++ b/htdocs/accountancy/journal/sellsjournal.php
@@ -178,7 +178,7 @@ if ($result) {
 
 /*
  * Action
- * FIXME Action must be set before any view part
+ * FIXME Action must be set before any view part to respect MVC
  */
 
 // Bookkeeping Write
diff --git a/htdocs/adherents/class/adherent.class.php b/htdocs/adherents/class/adherent.class.php
index 58593935a5f..1f37bc27c80 100644
--- a/htdocs/adherents/class/adherent.class.php
+++ b/htdocs/adherents/class/adherent.class.php
@@ -476,7 +476,7 @@ class Adherent extends CommonObject
 		    $action='update';
 
             // Actions on extra fields (by external module)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 		    $hookmanager->initHooks(array('memberdao'));
             $parameters=array('id'=>$this->id);
             $action='';
diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php
index 9a76211b828..3149d9ae4e2 100644
--- a/htdocs/categories/class/categorie.class.php
+++ b/htdocs/categories/class/categorie.class.php
@@ -201,7 +201,7 @@ class Categorie extends CommonObject
 				$action='create';
 
 				// Actions on extra fields (by external module or standard code)
-				// FIXME le hook fait double emploi avec le trigger !!
+				// TODO le hook fait double emploi avec le trigger !!
 				$hookmanager->initHooks(array('HookModuleNamedao'));
 				$parameters=array('socid'=>$this->id);
 				$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
@@ -294,7 +294,7 @@ class Categorie extends CommonObject
 			$action='update';
 
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('HookCategorydao'));
 			$parameters=array();
 			$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php
index 2e840abce76..283d9f95856 100644
--- a/htdocs/comm/action/class/actioncomm.class.php
+++ b/htdocs/comm/action/class/actioncomm.class.php
@@ -333,7 +333,7 @@ class ActionComm extends CommonObject
             	$action='create';
 
 	            // Actions on extra fields (by external module or standard code)
-				// FIXME le hook fait double emploi avec le trigger !!
+				// TODO le hook fait double emploi avec le trigger !!
             	$hookmanager->initHooks(array('actioncommdao'));
 	            $parameters=array('actcomm'=>$this->id);
 	            $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
@@ -657,7 +657,7 @@ class ActionComm extends CommonObject
 			$action='update';
 
         	// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
         	$hookmanager->initHooks(array('actioncommdao'));
         	$parameters=array('actcomm'=>$this->id);
         	$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/comm/propal.php b/htdocs/comm/propal.php
index 1abd3bc4818..c59e20c8035 100644
--- a/htdocs/comm/propal.php
+++ b/htdocs/comm/propal.php
@@ -1144,7 +1144,7 @@ if (empty($reshook))
 		if (! $error)
 		{
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('propaldao'));
 			$parameters = array('id' => $object->id);
 			$reshook = $hookmanager->executeHooks('insertExtraFields', $parameters, $object, $action); // Note that $action and $object may have been
diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php
index c1442aa403e..6ff361f9205 100644
--- a/htdocs/comm/propal/class/propal.class.php
+++ b/htdocs/comm/propal/class/propal.class.php
@@ -173,7 +173,7 @@ class Propal extends CommonObject
     var $labelstatut_short=array();
 
     var $specimen;
-	
+
 	//Incorterms
 	var $fk_incoterms;
 	var $location_incoterms;
@@ -958,7 +958,7 @@ class Propal extends CommonObject
                     	$action='update';
 
                     	// Actions on extra fields (by external module or standard code)
-                    	// FIXME le hook fait double emploi avec le trigger !!
+                    	// TODO le hook fait double emploi avec le trigger !!
                     	$hookmanager->initHooks(array('propaldao'));
                     	$parameters=array('socid'=>$this->id);
                     	$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
@@ -1262,9 +1262,9 @@ class Propal extends CommonObject
 
 				//Incoterms
 				$this->fk_incoterms = $obj->fk_incoterms;
-				$this->location_incoterms = $obj->location_incoterms;									
+				$this->location_incoterms = $obj->location_incoterms;
 				$this->libelle_incoterms = $obj->libelle_incoterms;
-				
+
                 if ($obj->fk_statut == self::STATUS_DRAFT)
                 {
                     $this->brouillon = 1;
@@ -1300,7 +1300,7 @@ class Propal extends CommonObject
                 	$extrafieldsline=new ExtraFields($this->db);
                 	$line = new PropaleLigne($this->db);
                 	$extralabelsline=$extrafieldsline->fetch_name_optionals_label($line->table_element,true);
-                	
+
                     $num = $this->db->num_rows($result);
                     $i = 0;
 
@@ -1353,7 +1353,7 @@ class Propal extends CommonObject
                         $line->date_end  		= $objp->date_end;
 
                         $line->fetch_optionals($line->id,$extralabelsline);
-                        
+
                         $this->lines[$i]        = $line;
                         //dol_syslog("1 ".$line->fk_product);
                         //print "xx $i ".$this->lines[$i]->fk_product;
@@ -1392,7 +1392,7 @@ class Propal extends CommonObject
     	$action='update';
 
     	// Actions on extra fields (by external module or standard code)
-    	// FIXME le hook fait double emploi avec le trigger !!
+    	// TODO le hook fait double emploi avec le trigger !!
     	$hookmanager->initHooks(array('propaldao'));
     	$parameters=array('id'=>$this->id);
     	$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php
index 5c33fdaaf7e..294445705ed 100644
--- a/htdocs/commande/card.php
+++ b/htdocs/commande/card.php
@@ -1117,7 +1117,7 @@ if (empty($reshook))
 		if (! $error)
 		{
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('orderdao'));
 			$parameters = array('id' => $object->id);
 			$reshook = $hookmanager->executeHooks('insertExtraFields', $parameters, $object, $action); // Note that $action and $object may have been modified by
diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php
index f95482f5e06..6896c49db3e 100644
--- a/htdocs/commande/class/commande.class.php
+++ b/htdocs/commande/class/commande.class.php
@@ -877,7 +877,7 @@ class Commande extends CommonOrder
                     	//$action='create';
 
 	                    // Actions on extra fields (by external module or standard code)
-	                    // FIXME le hook fait double emploi avec le trigger !!
+	                    // TODO le hook fait double emploi avec le trigger !!
 	                    /*$hookmanager->initHooks(array('orderdao'));
 	                    $parameters=array('socid'=>$this->id);
 	                    $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
@@ -2663,7 +2663,7 @@ class Commande extends CommonOrder
     	$action='create';
 
     	// Actions on extra fields (by external module or standard code)
-    	// FIXME le hook fait double emploi avec le trigger !!
+    	// TODO le hook fait double emploi avec le trigger !!
     	$hookmanager->initHooks(array('orderdao'));
     	$parameters=array('id'=>$this->id);
     	$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php
index f1f92d01f3f..f8186bf6e9b 100644
--- a/htdocs/compta/facture.php
+++ b/htdocs/compta/facture.php
@@ -1809,7 +1809,7 @@ if (empty($reshook))
 
 		if (! $error) {
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('invoicedao'));
 			$parameters = array('id' => $object->id);
 			$reshook = $hookmanager->executeHooks('insertExtraFields', $parameters, $object, $action); // Note that $action and $object may have been modified by
diff --git a/htdocs/compta/localtax/class/localtax.class.php b/htdocs/compta/localtax/class/localtax.class.php
index 994e3cca40c..04461117271 100644
--- a/htdocs/compta/localtax/class/localtax.class.php
+++ b/htdocs/compta/localtax/class/localtax.class.php
@@ -109,13 +109,21 @@ class Localtax extends CommonObject
             if ($result < 0) $error++;
             // End call triggers
 
-			//FIXME: Add rollback if trigger fail
-
-            return $this->id;
+            if (! $error)
+            {
+            	$this->db->commit();
+            	return $this->id;
+            }
+            else
+			{
+				$this->db->rollback();
+				return -1;
+            }
         }
         else
-        {
+		{
             $this->error="Error ".$this->db->lasterror();
+            $this->db->rollback();
             return -1;
         }
     }
@@ -141,7 +149,9 @@ class Localtax extends CommonObject
 		$this->fk_user_creat=trim($this->fk_user_creat);
 		$this->fk_user_modif=trim($this->fk_user_modif);
 
-        // Update request
+		$this->db->begin();
+
+		// Update request
         $sql = "UPDATE ".MAIN_DB_PREFIX."localtax SET";
         $sql.= " localtaxtype=".$this->ltt.",";
 		$sql.= " tms=".$this->db->idate($this->tms).",";
@@ -160,20 +170,27 @@ class Localtax extends CommonObject
         if (! $resql)
         {
             $this->error="Error ".$this->db->lasterror();
-            return -1;
+            $error++;
         }
 
-		if (! $notrigger)
+		if (! $error && ! $notrigger)
 		{
             // Call trigger
             $result=$this->call_trigger('LOCALTAX_MODIFY',$user);
             if ($result < 0) $error++;
             // End call triggers
-
-            //FIXME: Add rollback if trigger fail
     	}
 
-        return 1;
+    	if (! $error)
+    	{
+    		$this->db->commit();
+    		return 1;
+    	}
+    	else
+    	{
+    		$this->db->rollback();
+    		return -1;
+    	}
     }
 
 
diff --git a/htdocs/compta/salaries/class/paymentsalary.class.php b/htdocs/compta/salaries/class/paymentsalary.class.php
index 0db00c10c7f..f7da679196b 100644
--- a/htdocs/compta/salaries/class/paymentsalary.class.php
+++ b/htdocs/compta/salaries/class/paymentsalary.class.php
@@ -95,6 +95,8 @@ class PaymentSalary extends CommonObject
 			return -1;
 		}
 
+		$this->db->begin();
+
 		// Update request
 		$sql = "UPDATE ".MAIN_DB_PREFIX."payment_salary SET";
 
@@ -129,11 +131,18 @@ class PaymentSalary extends CommonObject
             $result=$this->call_trigger('PAYMENT_SALARY_MODIFY',$user);
             if ($result < 0) $error++;
             // End call triggers
-
-			//FIXME: Add rollback if trigger fail
 		}
 
-		return 1;
+		if (! $error)
+		{
+			$this->db->commit();
+			return 1;
+		}
+		else
+		{
+			$this->db->rollback();
+			return -1;
+		}
 	}
 
 
diff --git a/htdocs/compta/tva/class/tva.class.php b/htdocs/compta/tva/class/tva.class.php
index 948b5096fa3..fa8a71a24e4 100644
--- a/htdocs/compta/tva/class/tva.class.php
+++ b/htdocs/compta/tva/class/tva.class.php
@@ -89,7 +89,9 @@ class Tva extends CommonObject
 		// Check parameters
 		// Put here code to add control on parameters values
 
-        // Insert request
+		$this->db->begin();
+
+		// Insert request
 		$sql = "INSERT INTO ".MAIN_DB_PREFIX."tva(";
 		$sql.= "tms,";
 		$sql.= "datep,";
@@ -126,13 +128,22 @@ class Tva extends CommonObject
             if ($result < 0) $error++;
             // End call triggers
 
-            //FIXME: Add rollback if trigger fail
-            return $this->id;
+            if (! $error)
+            {
+            	$this->db->commit();
+            	return $this->id;
+            }
+            else
+			{
+				$this->db->rollback();
+				return -1;
+            }
         }
         else
-        {
-            $this->error="Error ".$this->db->lasterror();
-            return -1;
+		{
+			$this->error="Error ".$this->db->lasterror();
+			$this->db->rollback();
+			return -1;
         }
     }
 
@@ -160,7 +171,9 @@ class Tva extends CommonObject
 		// Check parameters
 		// Put here code to add control on parameters values
 
-        // Update request
+		$this->db->begin();
+
+		// Update request
         $sql = "UPDATE ".MAIN_DB_PREFIX."tva SET";
 
 		$sql.= " tms=".$this->db->idate($this->tms).",";
@@ -181,20 +194,27 @@ class Tva extends CommonObject
         if (! $resql)
         {
             $this->error="Error ".$this->db->lasterror();
-            return -1;
+            $error++;
         }
 
-		if (! $notrigger)
+		if (! $error && ! $notrigger)
 		{
             // Call trigger
             $result=$this->call_trigger('TVA_MODIFY',$user);
             if ($result < 0) $error++;
             // End call triggers
-
-            //FIXME: Add rollback if trigger fail
     	}
 
-        return 1;
+        if (! $error)
+    	{
+    		$this->db->commit();
+    		return 1;
+    	}
+    	else
+    	{
+    		$this->db->rollback();
+    		return -1;
+    	}
     }
 
 
diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php
index db4c1fc5a03..f2e4980ea47 100644
--- a/htdocs/core/class/commonobject.class.php
+++ b/htdocs/core/class/commonobject.class.php
@@ -3215,7 +3215,7 @@ abstract class CommonObject
 
 		$marginInfo = $this->getMarginInfos($force_price);
 
-		if (! empty($conf->global->MARGIN_ADD_SHOWHIDE_BUTTON))	// FIXME Warning this feature rely on an external js file that may be removed. Using native js function document.cookie should be better
+		if (! empty($conf->global->MARGIN_ADD_SHOWHIDE_BUTTON))	// TODO Warning this feature rely on an external js file that may be removed. Using native js function document.cookie should be better
 		{
 			print $langs->trans('ShowMarginInfos').' : ';
 	        $hidemargininfos = $_COOKIE['DOLUSER_MARGININFO_HIDE_SHOW'];
diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php
index 285bdc2cdef..e946d39e143 100644
--- a/htdocs/core/db/pgsql.class.php
+++ b/htdocs/core/db/pgsql.class.php
@@ -437,10 +437,7 @@ class DoliDBPgsql extends DoliDB
 	 */
 	function getDriverInfo()
 	{
-		// FIXME: Dummy method
-		// TODO: Implement
-
-		return '';
+		return 'pgsql php driver';
 	}
 
     /**
diff --git a/htdocs/core/db/sqlite.class.php b/htdocs/core/db/sqlite.class.php
index d09ce11ca43..c1819151a35 100644
--- a/htdocs/core/db/sqlite.class.php
+++ b/htdocs/core/db/sqlite.class.php
@@ -345,10 +345,7 @@ class DoliDBSqlite extends DoliDB
      */
     function getDriverInfo()
     {
-	    // FIXME: Dummy method
-	    // TODO: Implement
-
-    	return '';
+	    return 'sqlite php driver';
     }
 
 
diff --git a/htdocs/core/db/sqlite3.class.php b/htdocs/core/db/sqlite3.class.php
index 973a6147bdd..1024dafd0cc 100644
--- a/htdocs/core/db/sqlite3.class.php
+++ b/htdocs/core/db/sqlite3.class.php
@@ -365,10 +365,7 @@ class DoliDBSqlite3 extends DoliDB
      */
     function getDriverInfo()
     {
-        // FIXME: Dummy method
-        // TODO: Implement
-
-        return '';
+        return 'sqlite3 php driver';
     }
 
 
diff --git a/htdocs/core/js/timesheet.js b/htdocs/core/js/timesheet.js
index c36c03f55ff..da350162c1b 100644
--- a/htdocs/core/js/timesheet.js
+++ b/htdocs/core/js/timesheet.js
@@ -1,4 +1,3 @@
-//FIXME total not working    
 /* Copyright (C) 2014 delcroip <delcroip@gmail.com>
  * Laurent Destailleur 2015 <eldy@users.sourceforge.net>
  *
diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php
index 9eb9b243830..e95b14398a2 100644
--- a/htdocs/fichinter/card.php
+++ b/htdocs/fichinter/card.php
@@ -847,7 +847,7 @@ else if ($action == 'update_extras')
 	if (! $error)
 	{
 		// Actions on extra fields (by external module or standard code)
-		// FIXME le hook fait double emploi avec le trigger !!
+		// TODO le hook fait double emploi avec le trigger !!
 		$hookmanager->initHooks(array('interventiondao'));
 		$parameters=array('id'=>$object->id);
 		$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$object,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php
index f6575eb2140..440a46c1c62 100644
--- a/htdocs/fourn/class/fournisseur.commande.class.php
+++ b/htdocs/fourn/class/fournisseur.commande.class.php
@@ -1413,40 +1413,6 @@ class CommandeFournisseur extends CommonOrder
 
         $now=dol_now();
 
-        // If a serial number is provided, we check that sellby and eatby match already existing serial
-        if ($batch)
-        {
-			$sql = "SELECT rowid, batch, eatby, sellby FROM ".MAIN_DB_PREFIX."product_batch WHERE batch = '".$this->db->escape($batch)."'";
-            dol_syslog(get_class($this)."::dispatchProduct scan serial to check if eatby and sellby match", LOG_DEBUG);
-            $resql = $this->db->query($sql);
-            if ($resql)
-            {
-            	$num = $this->db->num_rows($resql);
-            	$i=0;
-            	while ($i < $num)
-            	{
-            		$obj = $this->db->fetch_object($resql);
-            		if ($obj->eatby != $eatby)
-            		{
-						$this->error=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->eatby);
-            			return -1;
-            		}
-            		if ($obj->sellby != $sellby)
-            		{
-						$this->error=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->sellby);
-            			return -1;
-            		}
-            		$i++;
-            	}
-            }
-            else
-			{
-            	dol_print_error($this->db);
-            	return -1;
-			}
-        }
-
-
         if (($this->statut == 3 || $this->statut == 4 || $this->statut == 5))
         {
             $this->db->begin();
@@ -1468,13 +1434,11 @@ class CommandeFournisseur extends CommonOrder
 					$result=$this->call_trigger('LINEORDER_SUPPLIER_DISPATCH',$user);
 					if ($result < 0)
                     {
-                        $this->db->rollback();
+                        $error++;
                         return -1;
                     }
 					// End call triggers
                 }
-
-                $this->db->commit();
             }
             else
 			{
@@ -1483,7 +1447,7 @@ class CommandeFournisseur extends CommonOrder
             }
 
             // Si module stock gere et que incrementation faite depuis un dispatching en stock
-            if (!$error && $entrepot > 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER))
+            if (! $error && $entrepot > 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER))
             {
 
                 $mouv = new MouvementStock($this->db);
@@ -1495,13 +1459,13 @@ class CommandeFournisseur extends CommonOrder
                     if ($result < 0)
                     {
                         $this->error=$mouv->error;
-                        dol_syslog(get_class($this)."::dispatchProduct ".$this->error, LOG_ERR);
+                        $this->errors=$mouv->errors;
+                        dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',',$this->errors), LOG_ERR);
                         $error++;
                     }
                 }
             }
 
-            //TODO: Check if there is a current transaction in DB but seems not.
             if ($error == 0)
             {
                 $this->db->commit();
diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php
index a76f55567a8..a1988b5c3ac 100644
--- a/htdocs/fourn/class/fournisseur.facture.class.php
+++ b/htdocs/fourn/class/fournisseur.facture.class.php
@@ -253,7 +253,7 @@ class FactureFournisseur extends CommonInvoice
             	$action='create';
 
 				// Actions on extra fields (by external module or standard code)
-				// FIXME le hook fait double emploi avec le trigger !!
+				// TODO le hook fait double emploi avec le trigger !!
 				$hookmanager->initHooks(array('supplierinvoicedao'));
 				$parameters=array('socid'=>$this->id);
 				$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php
index 280ea8cec2e..0f740a5367c 100755
--- a/htdocs/fourn/class/fournisseur.product.class.php
+++ b/htdocs/fourn/class/fournisseur.product.class.php
@@ -360,7 +360,7 @@ class ProductFournisseur extends Product
             	$this->fk_product				= $obj->fk_product;
             	$this->fk_availability			= $obj->fk_availability;
 				$this->delivery_time_days		= $obj->delivery_time_days;
-            	//$this->fourn_tva_npr			= $obj->fourn_tva_npr; // FIXME this field not exist in llx_product_fournisseur_price
+            	//$this->fourn_tva_npr			= $obj->fourn_tva_npr; // TODO this field not exist in llx_product_fournisseur_price. We should add it ?
                 $this->fk_supplier_price_expression      = $obj->fk_supplier_price_expression;
 
                 if (empty($ignore_expression) && !empty($this->fk_supplier_price_expression)) {
diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php
index caac210bbb2..90505dfa341 100644
--- a/htdocs/fourn/commande/card.php
+++ b/htdocs/fourn/commande/card.php
@@ -835,7 +835,7 @@ if (empty($reshook))
 		if (! $error)
 		{
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('supplierorderdao'));
 			$parameters=array('id'=>$object->id);
 
diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php
index 135592b9c7c..1691efc78a5 100644
--- a/htdocs/fourn/facture/card.php
+++ b/htdocs/fourn/facture/card.php
@@ -1139,7 +1139,7 @@ if (empty($reshook))
 		if (!$error)
 		{
 			// Actions on extra fields (by external module or standard code)
-			// FIXME le hook fait double emploi avec le trigger !!
+			// TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('supplierinvoicedao'));
 			$parameters=array('id'=>$object->id);
 
diff --git a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql
index 6af1a0fe647..6c70b73030f 100644
--- a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql
+++ b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql
@@ -181,6 +181,8 @@ ALTER TABLE llx_stock_mouvement ADD COLUMN batch varchar(30) DEFAULT NULL;
 ALTER TABLE llx_stock_mouvement ADD COLUMN eatby date DEFAULT NULL;
 ALTER TABLE llx_stock_mouvement ADD COLUMN sellby date DEFAULT NULL;
 
+UPDATE llx_product_batch SET batch = 'unknown' WHERE batch IS NULL;
+ALTER TABLE llx_product_batch MODIFY COLUMN batch varchar(30) NOT NULL;
 
 
 CREATE TABLE llx_expensereport (
diff --git a/htdocs/install/mysql/tables/llx_product_batch.key.sql b/htdocs/install/mysql/tables/llx_product_batch.key.sql
index f1f24c8b81a..d022beac743 100644
--- a/htdocs/install/mysql/tables/llx_product_batch.key.sql
+++ b/htdocs/install/mysql/tables/llx_product_batch.key.sql
@@ -16,5 +16,6 @@
 --
 -- ============================================================================
 
-ALTER TABLE llx_product_batch ADD INDEX idx_fk_product_stock (fk_product_stock);
+ALTER TABLE llx_product_batch ADD INDEX idx_fk_product_stock(fk_product_stock);
+ALTER TABLE llx_product_batch ADD INDEX idx_batch(batch);
 ALTER TABLE llx_product_batch ADD CONSTRAINT fk_product_batch_fk_product_stock FOREIGN KEY (fk_product_stock) REFERENCES llx_product_stock (rowid);
diff --git a/htdocs/install/mysql/tables/llx_product_batch.sql b/htdocs/install/mysql/tables/llx_product_batch.sql
index cbcca4ecacd..1cf72151304 100644
--- a/htdocs/install/mysql/tables/llx_product_batch.sql
+++ b/htdocs/install/mysql/tables/llx_product_batch.sql
@@ -21,7 +21,7 @@ CREATE TABLE llx_product_batch (
   fk_product_stock integer NOT NULL,
   eatby datetime DEFAULT NULL,
   sellby datetime DEFAULT NULL,
-  batch varchar(30) DEFAULT NULL,
+  batch varchar(30) NOT NULL,
   qty double NOT NULL DEFAULT 0,
   import_key varchar(14) DEFAULT NULL
 ) ENGINE=InnoDB;
diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php
index e75b7d8389c..8d63f02ddc8 100644
--- a/htdocs/install/upgrade2.php
+++ b/htdocs/install/upgrade2.php
@@ -3391,7 +3391,7 @@ function migrate_mode_reglement($db,$langs,$conf)
 
 				if ($resqla && $resql)
 				{
-					foreach($elements['tables'] as $table)		// FIXME We must not update tables if oldid is not renamed
+					foreach($elements['tables'] as $table)
 					{
 						$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
 						$sql.= "fk_mode_reglement = ".$elements['new_id'][$key];
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index 753ebc4073c..b2ba5c06585 100755
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -541,8 +541,8 @@ Module6000Name=Workflow
 Module6000Desc=Workflow management
 Module20000Name=Leave Requests management
 Module20000Desc=Declare and follow employees leaves requests
-Module39000Name=Product batch
-Module39000Desc=Batch or serial number, eat-by and sell-by date management on products
+Module39000Name=Product lot
+Module39000Desc=Lot or serial number, eat-by and sell-by date management on products
 Module50000Name=PayBox
 Module50000Desc=Module to offer an online payment page by credit card with PayBox
 Module50100Name=Point of sales
@@ -559,8 +559,6 @@ Module59000Name=Margins
 Module59000Desc=Module to manage margins
 Module60000Name=Commissions
 Module60000Desc=Module to manage commissions
-Module150010Name=Batch number, eat-by date and sell-by date
-Module150010Desc=batch number, eat-by date and sell-by date management for product
 Permission11=Read customer invoices
 Permission12=Create/modify customer invoices
 Permission13=Unvalidate customer invoices
@@ -1543,7 +1541,7 @@ CashDeskBankAccountForCB= Default account to use to receive payments by credit c
 CashDeskDoNotDecreaseStock=Disable stock decrease when a sell is done from Point of Sale (if "no", stock decrease is done for each sell done from POS, whatever is option set into module Stock).
 CashDeskIdWareHouse=Force and restrict warehouse to use for stock decrease
 StockDecreaseForPointOfSaleDisabled=Stock decrease from Point Of Sale disabled
-StockDecreaseForPointOfSaleDisabledbyBatch=Stock decrease in POS is not compatible with batch management
+StockDecreaseForPointOfSaleDisabledbyBatch=Stock decrease in POS is not compatible with lot management
 CashDeskYouDidNotDisableStockDecease=You did not disable stock decrease when making a sell from Point Of Sale. So a warehouse is required.
 ##### Bookmark #####
 BookmarkSetup=Bookmark module setup
diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang
index 32a66564b07..4266107d8a3 100755
--- a/htdocs/langs/en_US/errors.lang
+++ b/htdocs/langs/en_US/errors.lang
@@ -159,7 +159,7 @@ ErrorPriceExpression22=Negative result '%s'
 ErrorPriceExpressionInternal=Internal error '%s'
 ErrorPriceExpressionUnknown=Unknown error '%s'
 ErrorSrcAndTargetWarehouseMustDiffers=Source and target warehouses must differs
-ErrorTryToMakeMoveOnProductRequiringBatchData=Error, trying to make a stock movement without batch/serial information, on a product requiring batch/serial information
+ErrorTryToMakeMoveOnProductRequiringBatchData=Error, trying to make a stock movement without lot/serial information, on a product requiring lot/serial information
 ErrorCantSetReceptionToTotalDoneWithReceptionToApprove=All recorded receptions must first be verified (approved or denied) before being allowed to do this action
 ErrorCantSetReceptionToTotalDoneWithReceptionDenied=All recorded receptions must first be verified (approved) before being allowed to do this action
 ErrorGlobalVariableUpdater0=HTTP request failed with error '%s'
diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang
index 8508a26e96d..85b1d27f3a6 100644
--- a/htdocs/langs/en_US/productbatch.lang
+++ b/htdocs/langs/en_US/productbatch.lang
@@ -1,22 +1,22 @@
 # ProductBATCH language file - en_US - ProductBATCH
-ManageLotSerial=Use batch/serial number
-ProductStatusOnBatch=Yes (Batch/serial required)
-ProductStatusNotOnBatch=No (Batch/serial not used)
+ManageLotSerial=Use lot/serial number
+ProductStatusOnBatch=Yes (lot/serial required)
+ProductStatusNotOnBatch=No (lot/serial not used)
 ProductStatusOnBatchShort=Yes
 ProductStatusNotOnBatchShort=No
-Batch=Batch/Serial
-atleast1batchfield=Eat-by date or Sell-by date or Batch number
-batch_number=Batch/Serial number
-BatchNumberShort=Batch/Serial
+Batch=Lot/Serial
+atleast1batchfield=Eat-by date or Sell-by date or Lot/Serial number
+batch_number=Lot/Serial number
+BatchNumberShort=Lot/Serial
 l_eatby=Eat-by date
 l_sellby=Sell-by date
-DetailBatchNumber=Batch/Serial details
-DetailBatchFormat=Batch/Serial: %s - Eat by: %s - Sell by: %s (Qty : %d)
-printBatch=Batch: %s
+DetailBatchNumber=Lot/Serial details
+DetailBatchFormat=Lot/Serial: %s - Eat by: %s - Sell by: %s (Qty : %d)
+printBatch=Lot/Serial: %s
 printEatby=Eat-by: %s
 printSellby=Sell-by: %s
 printQty=Qty: %d
 AddDispatchBatchLine=Add a line for Shelf Life dispatching
 BatchDefaultNumber=Undefined
-WhenProductBatchModuleOnOptionAreForced=When module Batch/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.
+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 batch/serial number
diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang
index ad06fd79e26..7b963497d82 100644
--- a/htdocs/langs/en_US/stocks.lang
+++ b/htdocs/langs/en_US/stocks.lang
@@ -78,7 +78,7 @@ IdWarehouse=Id warehouse
 DescWareHouse=Description warehouse
 LieuWareHouse=Localisation warehouse
 WarehousesAndProducts=Warehouses and products
-WarehousesAndProductsBatchDetail=Warehouses and products (with detail per batch/serial)
+WarehousesAndProductsBatchDetail=Warehouses and products (with detail per lot/serial)
 AverageUnitPricePMPShort=Weighted average input price
 AverageUnitPricePMP=Weighted average input price
 SellPriceMin=Selling Unit Price
@@ -132,5 +132,7 @@ IsInPackage=Contained into package
 ShowWarehouse=Show warehouse
 MovementCorrectStock=Stock content correction for product %s
 MovementTransferStock=Stock transfer of product %s into another warehouse
-WarehouseMustBeSelectedAtFirstStepWhenProductBatchModuleOn=Source warehouse must be defined here when batch module is on. It will be used to list wich lot/serial is available for product that required lot/serial data for movement. If you want to send products from different warehouses, just make the shipment into several steps.
+WarehouseMustBeSelectedAtFirstStepWhenProductBatchModuleOn=Source warehouse must be defined here when "product lot" module is on. It will be used to list wich lot/serial is available for product that required lot/serial data for movement. If you want to send products from different warehouses, just make the shipment into several steps.
 InventoryCodeShort=Inv./Mov. code
+NoPendingReceptionOnSupplierOrder=No pending reception due to opened supplier order
+ThisSerialAlreadyExistWithDifferentDate=This lot/serial number (<strong>%s</strong>) already exists but with different eatby or sellby date (found <strong>%s</strong> but you enter <strong>%s</strong>).
\ No newline at end of file
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index c3f8cf5994d..8e861056ce4 100755
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -3146,6 +3146,8 @@ class Product extends CommonObject
 			else
 			{
 			    $this->error=$movementstock->error;
+			    $this->errors=$movementstock->errors;
+
 				$this->db->rollback();
 				return -1;
 			}
@@ -3198,7 +3200,7 @@ class Product extends CommonObject
 	}
 
 	/**
-	 *    Load information about stock of a product into stock_warehouse[] and stock_reel
+	 *    Load information about stock of a product into stock_reel, stock_warehouse[] (including stock_warehouse[idwarehouse]->detail_batch for batch products)
 	 *
 	 *    @return     int             < 0 if KO, > 0 if OK
 	 */
diff --git a/htdocs/product/index.php b/htdocs/product/index.php
index 90ec2e76fd1..3142e041262 100644
--- a/htdocs/product/index.php
+++ b/htdocs/product/index.php
@@ -383,7 +383,6 @@ function activitytrim($product_type)
 	$result = $db->query($sql);
 	if ($result)
 	{
-		//$tmpyear=$beginyear; // FIXME $beginyear is not defined
 		$tmpyear=0;
 		$trim1=0;
 		$trim2=0;
diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php
index c41fe590d18..3f8b2362386 100644
--- a/htdocs/product/stock/class/mouvementstock.class.php
+++ b/htdocs/product/stock/class/mouvementstock.class.php
@@ -67,10 +67,10 @@ class MouvementStock extends CommonObject
 	 *	@param		date	$eatby			eat-by date
 	 *	@param		date	$sellby			sell-by date
 	 *	@param		string	$batch			batch number
-	 *	@param		boolean	$skip_sellby	If set to true, stock mouvement is done without impacting batch record
+	 *	@param		boolean	$skip_batch		If set to true, stock movement is done without impacting batch record
 	 *	@return		int						<0 if KO, 0 if fk_product is null, >0 if OK
 	 */
-	function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $inventorycode='', $datem='',$eatby='',$sellby='',$batch='',$skip_sellby=false)
+	function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $inventorycode='', $datem='',$eatby='',$sellby='',$batch='',$skip_batch=false)
 	{
 		global $conf, $langs;
 
@@ -84,6 +84,16 @@ class MouvementStock extends CommonObject
 
 		// Check parameters
 		if (empty($fk_product)) return 0;
+		if ($eatby < 0)
+		{
+			$this->errors[]='ErrorBadValueForParameterEatBy';
+			return -1;
+		}
+		if ($sellby < 0)
+		{
+			$this->errors[]='ErrorBadValueForParameterEatBy';
+			return -1;
+		}
 
 		// Set properties of movement
 		$this->product_id = $fk_product;
@@ -103,16 +113,53 @@ class MouvementStock extends CommonObject
 		$product->load_stock();
 
 		// Test if product require batch data. If yes, and there is not, we throw an error.
-		if ($product->hasbatch() && ! $skip_sellby)
+		if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch)
 		{
-			if (empty($batch) && empty($eatby) && empty($sellby))
+			//if (empty($batch) && empty($eatby) && empty($sellby))
+			if (empty($batch))
 			{
-				$this->errors[]="ErrorTryToMakeMoveOnProductRequiringBatchData";
+				$this->errors[]=$langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->name);
 				dol_syslog("Try to make a movement of a product with status_batch on without any batch data");
 
 				$this->db->rollback();
 				return -2;
 			}
+
+			// If a serial number is provided, we check that sellby and eatby match already existing serial
+			$sql = "SELECT pb.rowid, pb.batch, pb.eatby, pb.sellby FROM ".MAIN_DB_PREFIX."product_batch as pb, ".MAIN_DB_PREFIX."product_stock as ps";
+			$sql.= " WHERE pb.fk_product_stock = ps.rowid AND ps.fk_product = ".$fk_product." AND pb.batch = '".$this->db->escape($batch)."'";
+            dol_syslog(get_class($this)."::_create scan serial for this product to check if eatby and sellby match", LOG_DEBUG);
+            $resql = $this->db->query($sql);
+            if ($resql)
+            {
+            	$num = $this->db->num_rows($resql);
+            	$i=0;
+            	while ($i < $num)
+            	{
+            		$obj = $this->db->fetch_object($resql);
+            		if ($obj->eatby != $eatby)
+            		{
+						$this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->eatby, $eatby);
+						dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->eatby, $eatby));
+						$this->db->rollback();
+            			return -3;
+            		}
+            		if ($obj->sellby != $sellby)
+            		{
+						$this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->sellby, $sellby);
+						dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->sellby, $sellby));
+						$this->db->rollback();
+            			return -3;
+            		}
+            		$i++;
+            	}
+            }
+            else
+			{
+            	dol_print_error($this->db);
+            	$this->db->rollback();
+            	return -1;
+			}
 		}
 
 		// Define if we must make the stock change (If product type is a service or if stock is used also for services)
@@ -157,7 +204,7 @@ class MouvementStock extends CommonObject
 			}
 			else
 			{
-				$this->error=$this->db->lasterror();
+				$this->errors[]=$this->db->lasterror();
 				$error = -1;
 			}
 
@@ -190,7 +237,7 @@ class MouvementStock extends CommonObject
 				}
 				else
 				{
-					$this->error=$this->db->lasterror();
+					$this->errors[]=$this->db->lasterror();
 					$error = -2;
 				}
 			}
@@ -212,7 +259,7 @@ class MouvementStock extends CommonObject
 					$resql=$this->db->query($sql);
 					if (! $resql)
 					{
-						$this->error=$this->db->lasterror();
+						$this->errors[]=$this->db->lasterror();
 						$error = -4;
 					}
 			*/
@@ -266,7 +313,7 @@ class MouvementStock extends CommonObject
 				$resql=$this->db->query($sql);
 				if (! $resql)
 				{
-					$this->error=$this->db->lasterror();
+					$this->errors[]=$this->db->lasterror();
 					$error = -3;
 				}
 				else if(empty($fk_product_stock))
@@ -276,8 +323,8 @@ class MouvementStock extends CommonObject
 
 			}
 
-			// Update detail stock for sell-by date
-			if (($product->hasbatch()) && (! $error) && (! $skip_sellby))
+			// Update detail stock for batch product
+			if (! $error && ! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch)
 			{
 				$param_batch=array('fk_product_stock' =>$fk_product_stock, 'eatby'=>$eatby, 'sellby'=>$sellby, 'batchnumber'=>$batch);
 				$result=$this->_create_batch($param_batch, $qty);
@@ -296,7 +343,7 @@ class MouvementStock extends CommonObject
 				$resql=$this->db->query($sql);
 				if (! $resql)
 				{
-					$this->error=$this->db->lasterror();
+					$this->errors[]=$this->db->lasterror();
 					$error = -4;
 				}
 			}
diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php
index 8a0b7297e2f..386f8ef2a2b 100644
--- a/htdocs/product/stock/massstockmove.php
+++ b/htdocs/product/stock/massstockmove.php
@@ -1,6 +1,6 @@
 <?php
-/* Copyright (C) 2013	Laurent Destaileur	<ely@users.sourceforge.net>
- * Copyright (C) 2014	Regis Houssin		<regis.houssin@capnetworks.com>
+/* Copyright (C) 2013-2015 Laurent Destaileur	<ely@users.sourceforge.net>
+ * Copyright (C) 2014	   Regis Houssin		<regis.houssin@capnetworks.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -47,6 +47,7 @@ $action = GETPOST('action','alpha');
 $id_product = GETPOST('productid', 'int');
 $id_sw = GETPOST('id_sw', 'int');
 $id_tw = GETPOST('id_tw', 'int');
+$batch = GETPOST('batch');
 $qty = GETPOST('qty');
 $idline = GETPOST('idline');
 
@@ -79,11 +80,6 @@ if ($action == 'addline')
 		$error++;
 		setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Product")),'errors');
 	}
-	if (! $qty)
-	{
-		$error++;
-	    setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Qty")),'errors');
-	}
 	if (! ($id_sw > 0))
 	{
 		$error++;
@@ -100,12 +96,32 @@ if ($action == 'addline')
 		$langs->load("errors");
 		setEventMessage($langs->trans("ErrorWarehouseMustDiffers"),'errors');
 	}
+	if (! $qty)
+	{
+		$error++;
+	    setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Qty")),'errors');
+	}
+
+	// Check a batch number is provided if product need it
+	if (! $error)
+	{
+		$producttmp=new Product($db);
+		$producttmp->fetch($id_product);
+		if ($producttmp->status_batch)
+		{
+			if (empty($batch))
+			{
+				$error++;
+				setEventMessage($langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData"), 'errors');
+			}
+		}
+	}
 
 	if (! $error)
 	{
 		if (count(array_keys($listofdata)) > 0) $id=max(array_keys($listofdata)) + 1;
 		else $id=1;
-		$listofdata[$id]=array('id'=>$id, 'id_product'=>$id_product, 'qty'=>$qty, 'id_sw'=>$id_sw, 'id_tw'=>$id_tw);
+		$listofdata[$id]=array('id'=>$id, 'id_product'=>$id_product, 'qty'=>$qty, 'id_sw'=>$id_sw, 'id_tw'=>$id_tw, 'batch'=>$batch);
 		$_SESSION['massstockmove']=json_encode($listofdata);
 
 		unset($id_product);
@@ -145,6 +161,9 @@ if ($action == 'createmovements')
 			$id_sw=$val['id_sw'];
 			$id_tw=$val['id_tw'];
 			$qty=price2num($val['qty']);
+			$batch=$val['batch'];
+			$dlc=-1;		// They are loaded later from serial
+			$dluo=-1;		// They are loaded later from serial
 
 			if (! $error && $id_sw <> $id_tw && is_numeric($qty) && $id_product)
 			{
@@ -154,39 +173,91 @@ if ($action == 'createmovements')
 
 				// Define value of products moved
 				$pricesrc=0;
-				if (isset($product->stock_warehouse[$id_sw]->pmp)) $pricesrc=$product->stock_warehouse[$id_sw]->pmp;
+				if (! empty($product->pmp)) $pricesrc=$product->pmp;
 				$pricedest=$pricesrc;
 
 				//print 'price src='.$pricesrc.', price dest='.$pricedest;exit;
 
-				// Remove stock
-				$result1=$product->correct_stock(
-	    			$user,
-	    			$id_sw,
-	    			$qty,
-	    			1,
-	    			GETPOST("label"),
-	    			$pricesrc
-				);
-				if ($result1 < 0)
+				if (empty($conf->productbatch->enabled) || ! $product->hasbatch())		// If product does not need lot/serial
 				{
-					$error++;
-					setEventMessage($product->errors,'errors');
+					// Remove stock
+					$result1=$product->correct_stock(
+		    			$user,
+		    			$id_sw,
+		    			$qty,
+		    			1,
+		    			GETPOST("label"),
+		    			$pricesrc,
+						GETPOST("codemove")
+					);
+					if ($result1 < 0)
+					{
+						$error++;
+						setEventMessage($product->errors,'errors');
+					}
+
+					// Add stock
+					$result2=$product->correct_stock(
+		    			$user,
+		    			$id_tw,
+		    			$qty,
+		    			0,
+		    			GETPOST("label"),
+		    			$pricedest,
+						GETPOST("codemove")
+					);
+					if ($result2 < 0)
+					{
+						$error++;
+						setEventMessage($product->errors,'errors');
+					}
 				}
-
-				// Add stock
-				$result2=$product->correct_stock(
-	    			$user,
-	    			$id_tw,
-	    			$qty,
-	    			0,
-	    			GETPOST("label"),
-	    			$pricedest
-				);
-				if ($result2 < 0)
+				else
 				{
-					$error++;
-					setEventMessage($product->errors,'errors');
+					// FIXME Seach record into product_batch table from serial to use same value for dlc and dluo
+					// FIXME MAke field batch lot required.
+
+					/*var_dump($batch);
+					var_dump($product->stock_warehouse);
+					exit;*/
+
+					// Remove stock
+					$result1=$product->correct_stock_batch(
+		    			$user,
+		    			$id_sw,
+		    			$qty,
+		    			1,
+		    			GETPOST("label"),
+		    			$pricesrc,
+						$dlc,
+						$dluo,
+						$batch,
+						GETPOST("codemove")
+					);
+					if ($result1 < 0)
+					{
+						$error++;
+						setEventMessage($product->errors,'errors');
+					}
+
+					// Add stock
+					$result2=$product->correct_stock_batch(
+		    			$user,
+		    			$id_tw,
+		    			$qty,
+		    			0,
+		    			GETPOST("label"),
+		    			$pricedest,
+						$dlc,
+						$dluo,
+						$batch,
+						GETPOST("codemove")
+					);
+					if ($result2 < 0)
+					{
+						$error++;
+						setEventMessage($product->errors,'errors');
+					}
 				}
 			}
 			else
@@ -254,6 +325,11 @@ $param='';
 
 print '<tr class="liste_titre">';
 print getTitleFieldOfList($langs->trans('ProductRef'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder);
+print getTitleFieldOfList('');
+if ($conf->productbatch->enabled)
+{
+	print getTitleFieldOfList($langs->trans('Batch'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder);
+}
 print getTitleFieldOfList($langs->trans('WarehouseSource'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder);
 print getTitleFieldOfList($langs->trans('WarehouseTarget'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder);
 print getTitleFieldOfList($langs->trans('Qty'),0,$_SERVER["PHP_SELF"],'',$param,'','align="center" class="tagtd"',$sortfield,$sortorder);
@@ -276,6 +352,15 @@ else
 }
 print $form->select_produits($id_product,'productid',$filtertype,$limit);
 print '</td>';
+print '<td>';
+print '</td>';
+// Batch number
+if ($conf->productbatch->enabled)
+{
+	print '<td>';
+	print '<input type="text" name="batch" value="'.$batch.'">';
+	print '</td>';
+}
 // In warehouse
 print '<td>';
 print $formproduct->selectWarehouses($id_sw,'id_sw','',1);
@@ -308,6 +393,12 @@ foreach($listofdata as $key => $val)
 	print $productstatic->getNomUrl(1);
 	$productstatic->ref=$oldref;
 	print '</td>';
+	if ($conf->productbatch->enabled)
+	{
+		print '<td>';
+		print $val['batch'];
+		print '</td>';
+	}
 	print '<td>';
 	print $warehousestatics->getNomUrl(1);
 	print '</td>';
@@ -333,9 +424,16 @@ print '<input type="hidden" name="token" value="' .$_SESSION['newtoken'] . '">';
 print '<input type="hidden" name="action" value="createmovements">';
 
 // Button to record mass movement
+$codemove=GETPOST('codemove');
 $labelmovement=GETPOST("label")?GETPOST('label'):$langs->trans("StockTransfer").' '.dol_print_date($now,'%Y-%m-%d %H:%M');
 
 print '<table class="border" width="100%">';
+	print '<tr>';
+	print '<td width="20%">'.$langs->trans("InventoryCode").'</td>';
+	print '<td colspan="5">';
+	print '<input type="text" name="codemove" size="10" value="'.dol_escape_htmltag($codemove).'">';
+	print '</td>';
+	print '</tr>';
 	print '<tr>';
 	print '<td width="20%">'.$langs->trans("LabelMovement").'</td>';
 	print '<td colspan="5">';
diff --git a/htdocs/product/stock/replenish.php b/htdocs/product/stock/replenish.php
index 289c3c31457..0f935c0154c 100644
--- a/htdocs/product/stock/replenish.php
+++ b/htdocs/product/stock/replenish.php
@@ -536,9 +536,11 @@ while ($i < ($limit ? min($num, $limit) : $num))
 		//virtual stock to compute the stock to buy value
 		$stocktobuy = max(max($objp->desiredstock, $objp->alertstock) - $stock - $ordered, 0);
 		$disabled = '';
-		if($ordered > 0) {
-			$compare = $usevirtualstock ? $stock : $stock + $ordered;
-			if($compare >= $objp->desiredstock) {
+		if ($ordered > 0)
+		{
+			$stockforcompare = $usevirtualstock ? $stock : $stock + $ordered;
+			if ($stockforcompare >= $objp->desiredstock)
+			{
 				$picto = img_picto('', './img/yes', '', 1);
 				$disabled = 'disabled="disabled"';
 			}
@@ -546,7 +548,8 @@ while ($i < ($limit ? min($num, $limit) : $num))
 				$picto = img_picto('', './img/no', '', 1);
 			}
 		} else {
-			$picto = img_picto('', './img/no', '', 1);
+			//$picto = img_help('',$langs->trans("NoPendingReceptionOnSupplierOrder"));
+			$picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), './img/no', '', 1);
 		}
 
 		print '<tr '.$bc[$var].'>';
diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php
index 7f8df5ed810..a3cb5b44dc6 100644
--- a/htdocs/societe/class/societe.class.php
+++ b/htdocs/societe/class/societe.class.php
@@ -895,7 +895,7 @@ class Societe extends CommonObject
             	$action='update';
 
                 // Actions on extra fields (by external module or standard code)
-                // FIXME le hook fait double emploi avec le trigger !!
+                // TODO le hook fait double emploi avec le trigger !!
                 $hookmanager->initHooks(array('thirdpartydao'));
                 $parameters=array('socid'=>$this->id);
                 $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
diff --git a/htdocs/user/class/usergroup.class.php b/htdocs/user/class/usergroup.class.php
index 599172c028e..35663f05978 100644
--- a/htdocs/user/class/usergroup.class.php
+++ b/htdocs/user/class/usergroup.class.php
@@ -623,7 +623,7 @@ class UserGroup extends CommonObject
 			$action='create';
 
 			// Actions on extra fields (by external module or standard code)
-            // FIXME le hook fait double emploi avec le trigger !!
+            // TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('groupdao'));
 			$parameters=array();
 			$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
@@ -694,7 +694,7 @@ class UserGroup extends CommonObject
 			$action='update';
 
 			// Actions on extra fields (by external module or standard code)
-            // FIXME le hook fait double emploi avec le trigger !!
+            // TODO le hook fait double emploi avec le trigger !!
 			$hookmanager->initHooks(array('groupdao'));
 			$parameters=array();
 			$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
-- 
GitLab