forked from Wavyzz/dolibarr
Merge pull request #16537 from altairisfr/pr_sn_unicity
Unique serial number management
This commit is contained in:
@@ -587,7 +587,7 @@ class modProduct extends DolibarrModules
|
|||||||
));
|
));
|
||||||
|
|
||||||
$this->import_regex_array[$r] = array_merge($this->import_regex_array[$r], array(
|
$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(
|
$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
|
//clauses copied from import_fields_array
|
||||||
if (!empty($conf->stock->enabled)) {
|
if (!empty($conf->stock->enabled)) {
|
||||||
$import_sample = array_merge($import_sample, array(
|
$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.seuil_stock_alerte' => '',
|
||||||
'p.pmp' => '0',
|
'p.pmp' => '0',
|
||||||
'p.desiredstock' => ''
|
'p.desiredstock' => ''
|
||||||
|
|||||||
@@ -855,7 +855,7 @@ if ($id > 0 || !empty($ref)) {
|
|||||||
// Already dispatched
|
// Already dispatched
|
||||||
print '<td class="right">'.$products_dispatched[$objp->rowid].'</td>';
|
print '<td class="right">'.$products_dispatched[$objp->rowid].'</td>';
|
||||||
|
|
||||||
if (!empty($conf->productbatch->enabled) && $objp->tobatch == 1) {
|
if (!empty($conf->productbatch->enabled) && $objp->tobatch > 0) {
|
||||||
$type = 'batch';
|
$type = 'batch';
|
||||||
print '<td class="right">';
|
print '<td class="right">';
|
||||||
print '</td>'; // Qty to dispatch
|
print '</td>'; // Qty to dispatch
|
||||||
@@ -967,7 +967,7 @@ if ($id > 0 || !empty($ref)) {
|
|||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
print '<td>';
|
print '<td>';
|
||||||
if (!empty($conf->productbatch->enabled) && $objp->tobatch == 1) {
|
if (!empty($conf->productbatch->enabled) && $objp->tobatch > 0) {
|
||||||
$type = 'batch';
|
$type = 'batch';
|
||||||
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
|
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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 = "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 .= " 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 .= " 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 .= " 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";
|
$sql .= " HAVING reel != SUM(pb.qty) or SUM(pb.qty) IS NULL";
|
||||||
print $sql;
|
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 = "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 .= " 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 .= " 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 .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
|
||||||
$sql .= " HAVING reel != SUM(pb.qty)";
|
$sql .= " HAVING reel != SUM(pb.qty)";
|
||||||
$resql = $db->query($sql);
|
$resql = $db->query($sql);
|
||||||
|
|||||||
@@ -24,3 +24,5 @@ ProductLotSetup=Setup of module lot/serial
|
|||||||
ShowCurrentStockOfLot=Show current stock for couple product/lot
|
ShowCurrentStockOfLot=Show current stock for couple product/lot
|
||||||
ShowLogOfMovementIfLot=Show log of movements for couple product/lot
|
ShowLogOfMovementIfLot=Show log of movements for couple product/lot
|
||||||
StockDetailPerBatch=Stock detail per 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
|
||||||
|
|||||||
@@ -24,3 +24,5 @@ ProductLotSetup=Configuration du module lot/série
|
|||||||
ShowCurrentStockOfLot=Afficher le stock actuel pour le couple produit / lot
|
ShowCurrentStockOfLot=Afficher le stock actuel pour le couple produit / lot
|
||||||
ShowLogOfMovementIfLot=Afficher l'historique des mouvements de couple produit / lot
|
ShowLogOfMovementIfLot=Afficher l'historique des mouvements de couple produit / lot
|
||||||
StockDetailPerBatch=Stock détaillé par 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
|
||||||
|
|||||||
@@ -1084,11 +1084,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
|||||||
// Batch number management
|
// Batch number management
|
||||||
if (!empty($conf->productbatch->enabled)) {
|
if (!empty($conf->productbatch->enabled)) {
|
||||||
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="3">';
|
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="3">';
|
||||||
if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
|
$statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
|
||||||
$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"));
|
|
||||||
}
|
|
||||||
print $form->selectarray('status_batch', $statutarray, GETPOST('status_batch'));
|
print $form->selectarray('status_batch', $statutarray, GETPOST('status_batch'));
|
||||||
print '</td></tr>';
|
print '</td></tr>';
|
||||||
}
|
}
|
||||||
@@ -1548,11 +1544,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
|||||||
if ($conf->productbatch->enabled) {
|
if ($conf->productbatch->enabled) {
|
||||||
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
|
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
|
||||||
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="3">';
|
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="3">';
|
||||||
if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
|
$statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
|
||||||
$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"));
|
|
||||||
}
|
|
||||||
print $form->selectarray('status_batch', $statutarray, $object->status_batch);
|
print $form->selectarray('status_batch', $statutarray, $object->status_batch);
|
||||||
print '</td></tr>';
|
print '</td></tr>';
|
||||||
}
|
}
|
||||||
@@ -2040,11 +2032,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
|||||||
if (!empty($conf->productbatch->enabled)) {
|
if (!empty($conf->productbatch->enabled)) {
|
||||||
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
|
if ($object->isProduct() || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
|
||||||
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="2">';
|
print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td colspan="2">';
|
||||||
if (!empty($conf->use_javascript_ajax) && $usercancreate && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE) && empty($conf->global->MAIN_ADVANCE_NUMLOT)) {
|
print $object->getLibStatut(0, 2);
|
||||||
print ajax_object_onoff($object, 'status_batch', 'tobatch', 'ProductStatusOnBatch', 'ProductStatusNotOnBatch');
|
|
||||||
} else {
|
|
||||||
print $object->getLibStatut(0, 2);
|
|
||||||
}
|
|
||||||
print '</td></tr>';
|
print '</td></tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4747,10 +4747,10 @@ class Product extends CommonObject
|
|||||||
if ($type == 2) {
|
if ($type == 2) {
|
||||||
switch ($mode) {
|
switch ($mode) {
|
||||||
case 0:
|
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);
|
return dolGetStatus($label);
|
||||||
case 1:
|
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);
|
return dolGetStatus($label);
|
||||||
case 2:
|
case 2:
|
||||||
return $this->LibStatut($status, 3, 2).' '.$this->LibStatut($status, 1, 2);
|
return $this->LibStatut($status, 3, 2).' '.$this->LibStatut($status, 1, 2);
|
||||||
@@ -4788,10 +4788,10 @@ class Product extends CommonObject
|
|||||||
$labelStatus = $langs->trans('ProductStatusOnBuyShort');
|
$labelStatus = $langs->trans('ProductStatusOnBuyShort');
|
||||||
$labelStatusShort = $langs->trans('ProductStatusOnBuy');
|
$labelStatusShort = $langs->trans('ProductStatusOnBuy');
|
||||||
} elseif ($type == 2) {
|
} elseif ($type == 2) {
|
||||||
$labelStatus = ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial'));
|
$labelStatus = ($status == 1 ? $langs->trans('ProductStatusOnBatch') : $langs->trans('ProductStatusOnSerial'));
|
||||||
$labelStatusShort = ($status == 1 || empty($conf->global->MAIN_ADVANCE_NUMLOT) ? $langs->trans('ProductStatusOnBatchShort') : $langs->trans('ProductStatusOnSerialShort'));
|
$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');
|
$labelStatus = $langs->trans('ProductStatusOnSerial');
|
||||||
$labelStatusShort = $langs->trans('ProductStatusOnSerialShort');
|
$labelStatusShort = $langs->trans('ProductStatusOnSerialShort');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -971,18 +971,13 @@ if ($resql) {
|
|||||||
// To batch
|
// To batch
|
||||||
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
||||||
print '<td class="liste_titre center">';
|
print '<td class="liste_titre center">';
|
||||||
|
$statutarray = array(
|
||||||
if (empty($conf->global ->MAIN_ADVANCE_NUMLOT)) {
|
'-1' => '',
|
||||||
print $form->selectyesno('search_tobatch', $search_tobatch, 1, false, 1);
|
'0' => $langs->trans("ProductStatusNotOnBatchShort"),
|
||||||
} else {
|
'1' => $langs->trans("ProductStatusOnBatchShort"),
|
||||||
$statutarray = array(
|
'2' => $langs->trans("ProductStatusOnSerialShort")
|
||||||
'-1' => '',
|
);
|
||||||
'0' => $langs->trans("ProductStatusNotOnBatchShort"),
|
print $form->selectarray('search_tobatch', $statutarray, $search_tobatch);
|
||||||
'1' => $langs->trans("ProductStatusOnBatchShort"),
|
|
||||||
'2' => $langs->trans("ProductStatusOnSerialShort")
|
|
||||||
);
|
|
||||||
print $form->selectarray('search_tobatch', $statutarray, $search_tobatch);
|
|
||||||
}
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
}
|
}
|
||||||
// Country
|
// Country
|
||||||
@@ -1672,11 +1667,7 @@ if ($resql) {
|
|||||||
// Lot/Serial
|
// Lot/Serial
|
||||||
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
||||||
print '<td class="center">';
|
print '<td class="center">';
|
||||||
if (empty($conf->global->MAIN_ADVANCE_NUMLOT)) {
|
print $product_static->getLibStatut(1, 2);
|
||||||
print yn($obj->tobatch);
|
|
||||||
} else {
|
|
||||||
print $product_static->getLibStatut(1, 2);
|
|
||||||
}
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
if (!$i) {
|
if (!$i) {
|
||||||
$totalarray['nbfield']++;
|
$totalarray['nbfield']++;
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class MouvementStock extends CommonObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end hook at beginning
|
// end hook at beginning
|
||||||
|
|
||||||
// Clean parameters
|
// Clean parameters
|
||||||
$price = price2num($price, 'MU'); // Clean value for the casse we receive a float zero value, to have it a real zero value.
|
$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;
|
if (empty($price)) $price = 0;
|
||||||
@@ -568,14 +568,32 @@ class MouvementStock extends CommonObject
|
|||||||
// Update detail stock for batch product
|
// Update detail stock for batch product
|
||||||
if (!$error && !empty($conf->productbatch->enabled) && $product->hasbatch() && !$skip_batch)
|
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);
|
if ( $this->getBatchCount($fk_product, $batch) > 0 )
|
||||||
} else {
|
{
|
||||||
$param_batch = array('fk_product_stock' =>$fk_product_stock, 'batchnumber'=>$batch);
|
$error++;
|
||||||
$result = $this->createBatch($param_batch, $qty);
|
$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
|
// 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);
|
||||||
//return $this->deleteCommon($user, $notrigger, 1);
|
//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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user