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(
|
||||
'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' => ''
|
||||
|
||||
@@ -855,7 +855,7 @@ if ($id > 0 || !empty($ref)) {
|
||||
// Already dispatched
|
||||
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';
|
||||
print '<td class="right">';
|
||||
print '</td>'; // Qty to dispatch
|
||||
@@ -967,7 +967,7 @@ if ($id > 0 || !empty($ref)) {
|
||||
print '</td>';
|
||||
|
||||
print '<td>';
|
||||
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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1084,11 +1084,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
|
||||
// Batch number management
|
||||
if (!empty($conf->productbatch->enabled)) {
|
||||
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"));
|
||||
} 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 '</td></tr>';
|
||||
}
|
||||
@@ -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 '<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"));
|
||||
} 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 '</td></tr>';
|
||||
}
|
||||
@@ -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 '<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 ajax_object_onoff($object, 'status_batch', 'tobatch', 'ProductStatusOnBatch', 'ProductStatusNotOnBatch');
|
||||
} else {
|
||||
print $object->getLibStatut(0, 2);
|
||||
}
|
||||
print $object->getLibStatut(0, 2);
|
||||
print '</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -971,18 +971,13 @@ if ($resql) {
|
||||
// To batch
|
||||
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
||||
print '<td class="liste_titre center">';
|
||||
|
||||
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 '</td>';
|
||||
}
|
||||
// Country
|
||||
@@ -1672,11 +1667,7 @@ if ($resql) {
|
||||
// Lot/Serial
|
||||
if (!empty($arrayfields['p.tobatch']['checked'])) {
|
||||
print '<td class="center">';
|
||||
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 '</td>';
|
||||
if (!$i) {
|
||||
$totalarray['nbfield']++;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user