diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 35ef726be20..6d3f7b05966 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -705,7 +705,9 @@ class Expedition extends CommonObject // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref)); if ($result < 0) { - $error++; break; + $error++; + $this->errors[]=$mouvS->error; + break; } } else @@ -713,17 +715,12 @@ class Expedition extends CommonObject // line with batch detail // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record - $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch); + // ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version) + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch, $obj->fk_origin_stock); if ($result < 0) { - $error++; break; - } - - // We update content of table llx_product_batch (will be rename into llx_product_stock_batch inantoher version) - // We can set livraison_batch to deprecated and adapt livraison to handle batch too (mouvS->_create also calls mouvS->_create_batch) - if (! empty($conf->productbatch->enabled)) - { - $result=$mouvS->livraison_batch($obj->fk_origin_stock, $qty); // ->fk_origin_stock = id into table llx_product_batch (will be rename into llx_product_stock_batch in another version) - if ($result < 0) { $error++; $this->errors[]=$mouvS->error; break; } + $error++; + $this->errors[]=$mouvS->error; + break; } } } diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 14849e6fa9c..c72ec02ca51 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -454,3 +454,9 @@ CREATE TABLE llx_advtargetemailing )ENGINE=InnoDB; ALTER TABLE llx_advtargetemailing ADD UNIQUE INDEX uk_advtargetemailing_name (name); + + + +-- At end +ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); + diff --git a/htdocs/install/mysql/tables/llx_product_batch.key.sql b/htdocs/install/mysql/tables/llx_product_batch.key.sql index d022beac743..100e092399d 100644 --- a/htdocs/install/mysql/tables/llx_product_batch.key.sql +++ b/htdocs/install/mysql/tables/llx_product_batch.key.sql @@ -19,3 +19,6 @@ 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); + +ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); + diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php index 749da6d86e2..d4440dac0f3 100644 --- a/htdocs/product/class/productbatch.class.php +++ b/htdocs/product/class/productbatch.class.php @@ -429,8 +429,8 @@ class Productbatch extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t"; $sql.= " WHERE fk_product_stock=".$fk_product_stock; - if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'"); - if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'"); + if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'"); // deprecated + if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'"); // deprecated if (! empty($batch_number)) $sql.= " AND batch = '".$this->db->escape($batch_number)."'"; if (! empty($where)) $sql.= " AND (".implode(" OR ",$where).")"; diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index a3cfaca9bd6..f1542d593b6 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -65,9 +65,10 @@ class MouvementStock extends CommonObject * @param date $sellby sell-by date * @param string $batch batch number * @param boolean $skip_batch If set to true, stock movement is done without impacting batch record + * @param int $id_product_batch Id product_batch (when skip_batch is flase and we already know which record of product_batch to use) * @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_batch=false) + function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $inventorycode='', $datem='',$eatby='',$sellby='',$batch='',$skip_batch=false, $id_product_batch=0) { global $conf, $langs; @@ -125,9 +126,14 @@ class MouvementStock extends CommonObject 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)."'"; + // FIXME Code not complete to implement this + // Check table llx_product_lot from batchnumber for same product + // If found and eatby/sellby defined into table and provided and differs, return error + // If found and eatby/sellby not defined into table and provided, we update table + // If found and eatby/sellby defined into table and not provided, we take value from table + // If not found, we add record + $sql = "SELECT pb.rowid, pb.batch, pb.eatby, pb.sellby FROM ".MAIN_DB_PREFIX."product_lot as pb"; + $sql.= " WHERE pb.fk_product = ".$fk_product." AND pb.batch = '".$this->db->escape($batch)."'"; 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) @@ -310,8 +316,15 @@ class MouvementStock extends CommonObject // 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); + 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++; } @@ -436,24 +449,14 @@ class MouvementStock extends CommonObject * @param date $eatby eat-by date * @param date $sellby sell-by date * @param string $batch batch number + * @param int $id_product_batch Id product_batch * @return int <0 if KO, >0 if OK */ - function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='', $eatby='', $sellby='', $batch='') + function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='', $eatby='', $sellby='', $batch='', $id_product_batch=0) { - return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, '', $datem, $eatby, $sellby, $batch, true); - } + $skip_batch = empty($conf->productbatch->enabled); - /** - * Decrease stock for batch record - * - * @param int $id_stock_dluo Id product_dluo - * @param int $qty Quantity - * @return int <0 if KO, >0 if OK - */ - function livraison_batch($id_stock_dluo, $qty) - { - $ret=$this->_create_batch($id_stock_dluo, (0 - $qty)); - return $ret; + return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, '', $datem, $eatby, $sellby, $batch, $skip_batch, $id_product_batch); } /** @@ -527,16 +530,18 @@ class MouvementStock extends CommonObject } /** - * Create or update batch record (update table llx_product_batch) + * Create or update batch record (update table llx_product_batch). No check is done here, done by parent. * - * @param array|int $dluo Could be either - * - int if row id of product_batch table - * - or complete array('fk_product_stock'=>, 'eatby'=>, 'sellby'=> , 'batchnumber'=>) - * @param int $qty Quantity of product with batch number. May be a negative amount. - * @return int <0 if KO, else return productbatch id + * @param array|int $dluo Could be either + * - int if row id of product_batch table + * - or complete array('fk_product_stock'=>, 'batchnumber'=>) + * @param int $qty Quantity of product with batch number. May be a negative amount. + * @return int <0 if KO, else return productbatch id */ - function _create_batch($dluo, $qty) + private function createBatch($dluo, $qty) { + global $user; + $pdluo=new Productbatch($this->db); $result=0; @@ -548,7 +553,7 @@ class MouvementStock extends CommonObject if (empty($pdluo->id)) { // We didn't find the line. May be it was deleted before by a previous move in same transaction. - $this->error = 'Error. You ask a move on a record for a serial that does not exists anymore. May be you take the same serial on samewarehouse several times in same shipment or it was used by another shipment. Remove this shipment and prepare another one.'; + $this->error = 'Error. You ask a move on a record for a serial that does not exists anymore. May be you take the same serial on same warehouse several times in same shipment or it was used by another shipment. Remove this shipment and prepare another one.'; $this->errors[] = $this->error; $result = -2; } @@ -558,21 +563,19 @@ class MouvementStock extends CommonObject if (isset($dluo['fk_product_stock'])) { $vfk_product_stock=$dluo['fk_product_stock']; - $veatby = $dluo['eatby']; - $vsellby = $dluo['sellby']; $vbatchnumber = $dluo['batchnumber']; - $result = $pdluo->find($vfk_product_stock,$veatby,$vsellby,$vbatchnumber); + $result = $pdluo->find($vfk_product_stock,'','',$vbatchnumber); // Search on batch number only (eatby and sellby are deprecated here) } else { - dol_syslog(get_class($this)."::_create_batch array param dluo must contain at least key fk_product_stock".$error, LOG_ERR); + dol_syslog(get_class($this)."::createBatch array param dluo must contain at least key fk_product_stock".$error, LOG_ERR); $result = -1; } } else { - dol_syslog(get_class($this)."::_create_batch error invalid param dluo".$error, LOG_ERR); + dol_syslog(get_class($this)."::createBatch error invalid param dluo".$error, LOG_ERR); $result = -1; } @@ -585,9 +588,9 @@ class MouvementStock extends CommonObject $pdluo->qty += $qty; if ($pdluo->qty == 0) { - $result=$pdluo->delete(0,1); + $result=$pdluo->delete($user,1); } else { - $result=$pdluo->update(0,1); + $result=$pdluo->update($user,1); } } else // product_batch record not found @@ -598,7 +601,7 @@ class MouvementStock extends CommonObject $pdluo->sellby = $vsellby; $pdluo->batch = $vbatchnumber; - $result=$pdluo->create(0,1); + $result=$pdluo->create($user,1); if ($result < 0) { $this->error=$pdluo->error;