diff --git a/htdocs/core/modules/modProduct.class.php b/htdocs/core/modules/modProduct.class.php
index f972c235802..2bd42d64e11 100644
--- a/htdocs/core/modules/modProduct.class.php
+++ b/htdocs/core/modules/modProduct.class.php
@@ -587,7 +587,7 @@ class modProduct extends DolibarrModules
));
$this->import_regex_array[$r] = array_merge($this->import_regex_array[$r], array(
- 'p.tobatch' => '^[0|1]$'
+ 'p.tobatch' => '^[0|1|2]$'
));
$this->import_convertvalue_array[$r] = array_merge($this->import_convertvalue_array[$r], array(
@@ -679,7 +679,7 @@ class modProduct extends DolibarrModules
//clauses copied from import_fields_array
if (!empty($conf->stock->enabled)) {
$import_sample = array_merge($import_sample, array(
- 'p.tobatch'=>"0 (don't use) / 1 (use batch/serial number)",
+ 'p.tobatch'=>"0 (don't use) / 1 (use batch) / 2 (use serial number)",
'p.seuil_stock_alerte' => '',
'p.pmp' => '0',
'p.desiredstock' => ''
diff --git a/htdocs/fourn/commande/dispatch.php b/htdocs/fourn/commande/dispatch.php
index 0ee9e87db4d..78e3884e9c4 100644
--- a/htdocs/fourn/commande/dispatch.php
+++ b/htdocs/fourn/commande/dispatch.php
@@ -855,7 +855,7 @@ if ($id > 0 || !empty($ref)) {
// Already dispatched
print '
'.$products_dispatched[$objp->rowid].' | ';
- if (!empty($conf->productbatch->enabled) && $objp->tobatch == 1) {
+ if (!empty($conf->productbatch->enabled) && $objp->tobatch > 0) {
$type = 'batch';
print '';
print ' | '; // Qty to dispatch
@@ -967,7 +967,7 @@ if ($id > 0 || !empty($ref)) {
print '';
print '';
- if (!empty($conf->productbatch->enabled) && $objp->tobatch == 1) {
+ if (!empty($conf->productbatch->enabled) && $objp->tobatch > 0) {
$type = 'batch';
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
} else {
diff --git a/htdocs/install/repair.php b/htdocs/install/repair.php
index 2a9c2bb11f2..80678abe24f 100644
--- a/htdocs/install/repair.php
+++ b/htdocs/install/repair.php
@@ -897,7 +897,7 @@ if ($ok && GETPOST('clean_product_stock_batch', 'alpha')) {
$sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
$sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps LEFT JOIN ".MAIN_DB_PREFIX."product_batch as pb ON ps.rowid = pb.fk_product_stock";
$sql .= " WHERE p.rowid = ps.fk_product";
- $sql .= " AND p.tobatch = 1";
+ $sql .= " AND p.tobatch > 0";
$sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
$sql .= " HAVING reel != SUM(pb.qty) or SUM(pb.qty) IS NULL";
print $sql;
@@ -981,7 +981,7 @@ if ($ok && GETPOST('clean_product_stock_negative_if_batch', 'alpha')) {
$sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
$sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps, ".MAIN_DB_PREFIX."product_batch as pb";
$sql .= " WHERE p.rowid = ps.fk_product AND ps.rowid = pb.fk_product_stock";
- $sql .= " AND p.tobatch = 1";
+ $sql .= " AND p.tobatch > 0";
$sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
$sql .= " HAVING reel != SUM(pb.qty)";
$resql = $db->query($sql);
diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang
index 3ab83908ff5..36adfd571fb 100644
--- a/htdocs/langs/en_US/productbatch.lang
+++ b/htdocs/langs/en_US/productbatch.lang
@@ -24,3 +24,5 @@ ProductLotSetup=Setup of module lot/serial
ShowCurrentStockOfLot=Show current stock for couple product/lot
ShowLogOfMovementIfLot=Show log of movements for couple product/lot
StockDetailPerBatch=Stock detail per lot
+SerialNumberAlreadyInUse=Serial number %s is already used for product %s
+TooManyQtyForSerialNumber=You can only have one product %s for serial number %S
diff --git a/htdocs/langs/fr_FR/productbatch.lang b/htdocs/langs/fr_FR/productbatch.lang
index 76cba909e2c..94ceb434bfd 100644
--- a/htdocs/langs/fr_FR/productbatch.lang
+++ b/htdocs/langs/fr_FR/productbatch.lang
@@ -24,3 +24,5 @@ ProductLotSetup=Configuration du module lot/série
ShowCurrentStockOfLot=Afficher le stock actuel pour le couple produit / lot
ShowLogOfMovementIfLot=Afficher l'historique des mouvements de couple produit / lot
StockDetailPerBatch=Stock détaillé par lot
+SerialNumberAlreadyInUse=Le numéro de série %s est déjà utilisé pour le produit %s
+TooManyQtyForSerialNumber=Vous ne pouvez avoir qu'un produit %s avec le numéro de série %s
diff --git a/htdocs/product/card.php b/htdocs/product/card.php
index 3eccae1e114..d7080e39417 100644
--- a/htdocs/product/card.php
+++ b/htdocs/product/card.php
@@ -1084,11 +1084,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
// Batch number management
if (!empty($conf->productbatch->enabled)) {
print ' | | '.$langs->trans("ManageLotSerial").' | ';
- if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
- $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"));
- } else {
- $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
- }
+ $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
print $form->selectarray('status_batch', $statutarray, GETPOST('status_batch'));
print ' |
';
}
@@ -1548,11 +1544,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
if ($conf->productbatch->enabled) {
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
print '| '.$langs->trans("ManageLotSerial").' | ';
- if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
- $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"));
- } else {
- $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
- }
+ $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
print $form->selectarray('status_batch', $statutarray, $object->status_batch);
print ' |
';
}
@@ -2040,11 +2032,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
if (!empty($conf->productbatch->enabled)) {
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
print '| '.$langs->trans("ManageLotSerial").' | ';
- if (!empty($conf->use_javascript_ajax) && $usercancreate && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE) && empty($conf->global->MAIN_ADVANCE_NUMLOT)) {
- print ajax_object_onoff($object, 'status_batch', 'tobatch', 'ProductStatusOnBatch', 'ProductStatusNotOnBatch');
- } else {
- print $object->getLibStatut(0, 2);
- }
+ print $object->getLibStatut(0, 2);
print ' |
';
}
}
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index 99e571fb8b7..072b113df7e 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -4747,10 +4747,10 @@ class Product extends CommonObject
if ($type == 2) {
switch ($mode) {
case 0:
- $label = ($status == 0 ? $langs->trans('ProductStatusNotOnBatch') : ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial')));
+ $label = ($status == 0 ? $langs->trans('ProductStatusNotOnBatch') : ($status == 1 ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial')));
return dolGetStatus($label);
case 1:
- $label = ($status == 0 ? $langs->trans('ProductStatusNotOnBatchShort') : ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatchShort') : $langs->trans('ProductStatusOnSerialShort')));
+ $label = ($status == 0 ? $langs->trans('ProductStatusNotOnBatchShort') : ($status == 1 ? $langs->trans('ProductStatusOnBatchShort') : $langs->trans('ProductStatusOnSerialShort')));
return dolGetStatus($label);
case 2:
return $this->LibStatut($status, 3, 2).' '.$this->LibStatut($status, 1, 2);
@@ -4788,10 +4788,10 @@ class Product extends CommonObject
$labelStatus = $langs->trans('ProductStatusOnBuyShort');
$labelStatusShort = $langs->trans('ProductStatusOnBuy');
} elseif ($type == 2) {
- $labelStatus = ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial'));
- $labelStatusShort = ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatchShort') : $langs->trans('ProductStatusOnSerialShort'));
+ $labelStatus = ($status == 1 ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial'));
+ $labelStatusShort = ($status == 1 ? $langs->trans('ProductStatusOnBatchShort') : $langs->trans('ProductStatusOnSerialShort'));
}
- } elseif (! empty($conf->global->MAIN_ADVANCE_NUMLOT) && $type == 2 && $status == 2) {
+ } elseif ( $type == 2 && $status == 2 ) {
$labelStatus = $langs->trans('ProductStatusOnSerial');
$labelStatusShort = $langs->trans('ProductStatusOnSerialShort');
}
diff --git a/htdocs/product/list.php b/htdocs/product/list.php
index 1e96e7d97f8..5bc973e56b7 100644
--- a/htdocs/product/list.php
+++ b/htdocs/product/list.php
@@ -971,18 +971,13 @@ if ($resql) {
// To batch
if (!empty($arrayfields['p.tobatch']['checked'])) {
print '';
-
- if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
- print $form->selectyesno('search_tobatch', $search_tobatch, 1, false, 1);
- } else {
- $statutarray = array(
- '-1' => '',
- '0' => $langs->trans("ProductStatusNotOnBatchShort"),
- '1' => $langs->trans("ProductStatusOnBatchShort"),
- '2' => $langs->trans("ProductStatusOnSerialShort")
- );
- print $form->selectarray('search_tobatch', $statutarray, $search_tobatch);
- }
+ $statutarray = array(
+ '-1' => '',
+ '0' => $langs->trans("ProductStatusNotOnBatchShort"),
+ '1' => $langs->trans("ProductStatusOnBatchShort"),
+ '2' => $langs->trans("ProductStatusOnSerialShort")
+ );
+ print $form->selectarray('search_tobatch', $statutarray, $search_tobatch);
print ' | ';
}
// Country
@@ -1672,11 +1667,7 @@ if ($resql) {
// Lot/Serial
if (!empty($arrayfields['p.tobatch']['checked'])) {
print '';
- if (empty($conf->global->MAIN_ADVANCE_NUMLOT)) {
- print yn($obj->tobatch);
- } else {
- print $product_static->getLibStatut(1, 2);
- }
+ print $product_static->getLibStatut(1, 2);
print ' | ';
if (!$i) {
$totalarray['nbfield']++;
diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php
index 17fe7193658..59bc60f342b 100644
--- a/htdocs/product/stock/class/mouvementstock.class.php
+++ b/htdocs/product/stock/class/mouvementstock.class.php
@@ -192,7 +192,7 @@ class MouvementStock extends CommonObject
}
}
// end hook at beginning
-
+
// Clean parameters
$price = price2num($price, 'MU'); // Clean value for the casse we receive a float zero value, to have it a real zero value.
if (empty($price)) $price = 0;
@@ -568,14 +568,32 @@ class MouvementStock extends CommonObject
// Update detail stock for batch product
if (!$error && !empty($conf->productbatch->enabled) && $product->hasbatch() && !$skip_batch)
{
- if ($id_product_batch > 0)
+ // check unicity for serial numbered equipments ( different for lots managed products)
+ if ( $product->status_batch == 2 && $qty > 0 )
{
- $result = $this->createBatch($id_product_batch, $qty);
- } else {
- $param_batch = array('fk_product_stock' =>$fk_product_stock, 'batchnumber'=>$batch);
- $result = $this->createBatch($param_batch, $qty);
+ if ( $this->getBatchCount($fk_product, $batch) > 0 )
+ {
+ $error++;
+ $this->errors[] = $langs->trans("SerialNumberAlreadyInUse", $batch, $product->ref);
+ }
+ elseif ( $qty > 1 )
+ {
+ $error++;
+ $this->errors[] = $langs->trans("TooManyQtyForSerialNumber", $product->ref, $batch);
+ }
+ }
+
+ if ( ! $error )
+ {
+ if ($id_product_batch > 0)
+ {
+ $result = $this->createBatch($id_product_batch, $qty);
+ } else {
+ $param_batch = array('fk_product_stock' =>$fk_product_stock, 'batchnumber'=>$batch);
+ $result = $this->createBatch($param_batch, $qty);
+ }
+ if ($result < 0) $error++;
}
- if ($result < 0) $error++;
}
// Update PMP and denormalized value of stock qty at product level
@@ -1208,4 +1226,39 @@ class MouvementStock extends CommonObject
return $this->deleteCommon($user, $notrigger);
//return $this->deleteCommon($user, $notrigger, 1);
}
+
+ /**
+ * Retrieve number of equipments for a product batch
+ *
+ * @param int $fk_product Product id
+ * @param varchar $batch batch number
+ * @return int <0 if KO, number of equipments if OK
+ */
+ private function getBatchCount($fk_product, $batch)
+ {
+ global $conf;
+
+ $cpt = 0;
+
+ $sql = "SELECT sum(pb.qty) as cpt";
+ $sql .= " FROM ".MAIN_DB_PREFIX."product_batch as pb";
+ $sql .= " INNER JOIN ".MAIN_DB_PREFIX."product_stock as ps ON ps.rowid = pb.fk_product_stock";
+ $sql .= " WHERE ps.fk_product = " . $fk_product;
+ $sql .= " AND pb.batch = '" . $this->db->escape($batch) . "'";
+
+ $result = $this->db->query($sql);
+ if ($result) {
+ if ($this->db->num_rows($result)) {
+ $obj = $this->db->fetch_object($result);
+ $cpt = $obj->cpt;
+ }
+
+ $this->db->free($result);
+ } else {
+ dol_print_error($this->db);
+ return -1;
+ }
+
+ return $cpt;
+ }
}