From 3bdd342df15ba1961eded3a4486cc65a1dccd64f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 14 Aug 2015 23:53:59 +0200 Subject: [PATCH] Fix #3116 --- htdocs/expedition/card.php | 39 ++++++---- htdocs/expedition/class/expedition.class.php | 64 ++++++++++------ htdocs/product/class/productbatch.class.php | 21 ++++-- .../stock/class/mouvementstock.class.php | 74 ++++++++++++------- 4 files changed, 131 insertions(+), 67 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index af1c4d4db13..ffc453d395c 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -76,7 +76,7 @@ $hideref = (GETPOST('hideref','int') ? GETPOST('hideref','int') : (! empty($co $object = new Expedition($db); -// Load object +// Load object. Make an object->fetch include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not includ_once // Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array @@ -160,6 +160,7 @@ if (empty($reshook)) $num=count($objectsrc->lines); $totalqty=0; + for ($i = 0; $i < $num; $i++) { $idl="idl".$i; @@ -178,11 +179,12 @@ if (empty($reshook)) while (isset($_POST[$batch])) { // save line of detail into sub_qty - $sub_qty[$j]['q']=GETPOST($qty,'int'); - $sub_qty[$j]['id_batch']=GETPOST($batch,'int'); - + $sub_qty[$j]['q']=GETPOST($qty,'int'); // the qty we want to move for this stock record + $sub_qty[$j]['id_batch']=GETPOST($batch,'int'); // the id into llx_product_batch of stock record to move $subtotalqty+=$sub_qty[$j]['q']; - + + //var_dump($qty);var_dump($batch);var_dump($sub_qty[$j]['q']);var_dump($sub_qty[$j]['id_batch']); + $j++; $batch="batchl".$i."_".$j; $qty = "qtyl".$i.'_'.$j; @@ -200,6 +202,8 @@ if (empty($reshook)) if (GETPOST($qty,'int') > 0) $totalqty+=GETPOST($qty,'int'); } } + + //var_dump($batch_line[2]); if ($totalqty > 0) // There is at least one thing to ship { @@ -208,7 +212,8 @@ if (empty($reshook)) { $qty = "qtyl".$i; if (! isset($batch_line[$i])) - { // not batch mode + { + // not batch mode if (GETPOST($qty,'int') > 0 || (GETPOST($qty,'int') == 0 && $conf->global->SHIPMENT_GETS_ALL_ORDER_PRODUCTS)) { $ent = "entl".$i; @@ -225,7 +230,8 @@ if (empty($reshook)) } } else - { // batch mode + { + // batch mode if ($batch_line[$i]['qty']>0) { $ret=$object->addline_batch($batch_line[$i]); @@ -1257,8 +1263,8 @@ else if ($id || $ref) print "\n"; /* - * Lignes produits - */ + * Lines of products + */ print '
'; print ''; if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) @@ -1417,12 +1423,19 @@ else if ($id || $ref) if (isset($lines[$i]->detail_batch)) { print ''; } else { print ''; diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 3699399bdca..5863161a995 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -391,7 +391,7 @@ class Expedition extends CommonObject /** - * Create a expedition line with eat-by date + * Create the detail (eat-by date) of the expedition line * * @param object $line_ext full line informations * @return int <0 if KO, >0 if OK @@ -626,9 +626,12 @@ class Expedition extends CommonObject // Loop on each product line to add a stock movement // TODO possibilite d'expedier a partir d'une propale ou autre origine - $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.rowid"; + $sql = "SELECT cd.fk_product, cd.subprice,"; + $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,"; + $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty, edb.fk_origin_stock"; $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,"; $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid"; $sql.= " WHERE ed.fk_expedition = ".$this->id; $sql.= " AND cd.rowid = ed.fk_origin_line"; @@ -640,27 +643,38 @@ class Expedition extends CommonObject for ($i = 0; $i < $cpt; $i++) { $obj = $this->db->fetch_object($resql); - if($obj->qty <= 0) continue; - dol_syslog(get_class($this)."::valid movement index ".$i); + if ($obj->qty <= 0) continue; + dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid); //var_dump($this->lines[$i]); $mouvS = new MouvementStock($this->db); $mouvS->origin = &$this; - // We decrement stock of product (and sub-products) - // We use warehouse selected for each line - $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref)); - if ($result < 0) { $error++; break; } - - if (! empty($conf->productbatch->enabled)) + + if (empty($obj->edbrowid)) { - $details=ExpeditionLineBatch::fetchAll($this->db,$obj->rowid); - if (! empty($details)) + // line without 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, $obj->qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref)); + if ($result < 0) { + $error++; break; + } + } + else + { + // 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, $obj->qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch); + if ($result < 0) { + $error++; break; + } + + // We update content of table llx_product_batch (will be rename into llx_product_stock_batch inantoher version) + if (! empty($conf->productbatch->enabled)) { - foreach ($details as $dbatch) - { - $result=$mouvS->livraison_batch($dbatch->fk_origin_stock,$dbatch->dluo_qty); - if ($result < 0) { $error++; $this->errors[]=$mouvS->$error; break 2; } - } + $result=$mouvS->livraison_batch($obj->fk_origin_stock, $obj->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; } } } } @@ -672,7 +686,7 @@ class Expedition extends CommonObject return -2; } } - + if (! $error && ! $notrigger) { // Call trigger @@ -843,6 +857,10 @@ class Expedition extends CommonObject { if ($value['q']>0) { + // $value['q']=qty to move + // $value['id_batch']=id into llx_product_batch of record to move + //var_dump($value); + $linebatch = new ExpeditionLineBatch($this->db); $ret=$linebatch->fetchFromStock($value['id_batch']); // load serial, sellby, eatby if ($ret<0) @@ -857,13 +875,16 @@ class Expedition extends CommonObject { // TODO } - + + //var_dump($linebatch); } } $line->entrepot_id = $linebatch->entrepot_id; $line->origin_line_id = $dbatch['ix_l']; $line->qty = $dbatch['qty']; $line->detail_batch=$tab; + + //var_dump($line); $this->lines[$num] = $line; } } @@ -1146,9 +1167,9 @@ class Expedition extends CommonObject $sql = "SELECT cd.rowid, cd.fk_product, cd.label as custom_label, cd.description, cd.qty as qty_asked"; $sql.= ", cd.total_ht, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.total_tva"; $sql.= ", cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx, cd.price, cd.subprice, cd.remise_percent"; - $sql.= ", ed.qty as qty_shipped, ed.fk_origin_line, ed.fk_entrepot"; + $sql.= ", ed.rowid as line_id, ed.qty as qty_shipped, ed.fk_origin_line, ed.fk_entrepot"; $sql.= ", p.ref as product_ref, p.label as product_label, p.fk_product_type"; - $sql.= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, ed.rowid as line_id"; + $sql.= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tobatch as product_tobatch"; $sql.= " FROM (".MAIN_DB_PREFIX."expeditiondet as ed,"; $sql.= " ".MAIN_DB_PREFIX."commandedet as cd)"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product"; @@ -1202,6 +1223,7 @@ class Expedition extends CommonObject $line->product_ref = $obj->product_ref; $line->product_label = $obj->product_label; $line->libelle = $obj->product_label; // TODO deprecated + $line->product_tobatch = $obj->product_tobatch; $line->label = $obj->custom_label; $line->description = $obj->description; $line->qty_asked = $obj->qty_asked; diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php index 5495584ea5f..8eac443c244 100644 --- a/htdocs/product/class/productbatch.class.php +++ b/htdocs/product/class/productbatch.class.php @@ -135,7 +135,7 @@ class Productbatch extends CommonObject /** * Load object in memory from the database * - * @param int $id Id object + * @param int $id Id object * @return int <0 if KO, >0 if OK */ function fetch($id) @@ -201,6 +201,12 @@ class Productbatch extends CommonObject // Clean parameters $this->clean_param(); + // TODO Check qty is ok for stock move. Negative may not be allowed. + if ($this->qty < 0) + { + + } + // Update request $sql = "UPDATE ".MAIN_DB_PREFIX.self::$_table_element." SET"; $sql.= " fk_product_stock=".(isset($this->fk_product_stock)?$this->fk_product_stock:"null").","; @@ -389,11 +395,12 @@ class Productbatch extends CommonObject } /** - * Clean fields (triming) + * Clean fields (triming) * * @return void */ - private function clean_param() { + private function clean_param() + { if (isset($this->fk_product_stock)) $this->fk_product_stock=(int) trim($this->fk_product_stock); if (isset($this->batch)) $this->batch=trim($this->batch); if (isset($this->qty)) $this->qty=(float) trim($this->qty); @@ -403,11 +410,11 @@ class Productbatch extends CommonObject /** * Find first detail record that match eather eat-by or sell-by or batch within given warehouse * - * @param int $fk_product_stock id product_stock for objet - * @param date $eatby eat-by date for objet - * @param date $sellby sell-by date for objet + * @param int $fk_product_stock id product_stock for objet + * @param date $eatby eat-by date for objet + * @param date $sellby sell-by date for objet * @param string $batch_number batch number for objet - * @return int <0 if KO, >0 if OK + * @return int <0 if KO, >0 if OK */ function find($fk_product_stock=0, $eatby='',$sellby='',$batch_number='') { diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 2e9fcfab68e..52c69b16ec6 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -229,8 +229,8 @@ class MouvementStock extends CommonObject $num = 0; if (! $error) { - $sql = "SELECT rowid, reel, pmp FROM ".MAIN_DB_PREFIX."product_stock"; - $sql.= " WHERE fk_entrepot = ".$entrepot_id." AND fk_product = ".$fk_product; + $sql = "SELECT rowid, reel FROM ".MAIN_DB_PREFIX."product_stock"; + $sql.= " WHERE fk_entrepot = ".$entrepot_id." AND fk_product = ".$fk_product; // This is a unique key dol_syslog(get_class($this)."::_create", LOG_DEBUG); $resql=$this->db->query($sql); @@ -327,7 +327,7 @@ class MouvementStock extends CommonObject $this->errors[]=$this->db->lasterror(); $error = -3; } - else if(empty($fk_product_stock)) + else if (empty($fk_product_stock)) { $fk_product_stock = $this->db->last_insert_id(MAIN_DB_PREFIX."product_stock"); } @@ -452,19 +452,22 @@ class MouvementStock extends CommonObject * @param int $price Price * @param string $label Label of stock movement * @param string $datem Force date of movement + * @param date $eatby eat-by date + * @param date $sellby sell-by date + * @param string $batch batch number * @return int <0 if KO, >0 if OK */ - function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='') + function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='', $eatby='', $sellby='', $batch='') { - return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, '', $datem,'','','',true); + return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, '', $datem, $eatby, $sellby, $batch, true); } /** * 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 + * @param int $qty Quantity + * @return int <0 if KO, >0 if OK */ function livraison_batch($id_stock_dluo, $qty) { @@ -543,10 +546,12 @@ class MouvementStock extends CommonObject } /** - * Create or update batch record + * Create or update batch record (update table llx_product_batch) * - * @param variant $dluo Could be either int if id of product_batch or array with at leat fk_product_stock - * @param int $qty Quantity of product with batch number + * @param array|int $dluo Could be either + * - int if id of product_batch + * - 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 */ function _create_batch($dluo, $qty) @@ -555,44 +560,63 @@ class MouvementStock extends CommonObject $result=0; - // Try to find an existing record with batch same batch number or id - if (is_numeric($dluo)) { + // Try to find an existing record with same batch number or id + if (is_numeric($dluo)) + { $result=$pdluo->fetch($dluo); - } else if (is_array($dluo)) { - if (isset($dluo['fk_product_stock'])) { + 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->errors[] = $this->error; + $result = -2; + } + } + else if (is_array($dluo)) + { + 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); - } else { + } + else + { dol_syslog(get_class($this)."::_create_batch array param dluo must contain at least key fk_product_stock".$error, LOG_ERR); $result = -1; } - } else { + } + else + { dol_syslog(get_class($this)."::_create_batch error invalid param dluo".$error, LOG_ERR); $result = -1; } - // Batch record found so we update it - if ($result>0) + if ($result >= 0) { - if ($pdluo->id >0) + // No error + if ($pdluo->id > 0) // product_batch record found { - $pdluo->qty +=$qty; - if ($pdluo->qty == 0) { + //print "Avant ".$pdluo->qty." Apres ".($pdluo->qty + $qty)."
"; + $pdluo->qty += $qty; + if ($pdluo->qty == 0) + { $result=$pdluo->delete(0,1); } else { $result=$pdluo->update(0,1); } } - else + else // product_batch record not found { $pdluo->fk_product_stock=$vfk_product_stock; $pdluo->qty = $qty; $pdluo->eatby = $veatby; $pdluo->sellby = $vsellby; $pdluo->batch = $vbatchnumber; + $result=$pdluo->create(0,1); if ($result < 0) { @@ -600,11 +624,9 @@ class MouvementStock extends CommonObject $this->errors=$pdluo->errors; } } - return $result; - } else { - return -1; } - + + return $result; } /**
'; - $detail = ''; - foreach ($lines[$i]->detail_batch as $dbatch) + if ($lines[$i]->product_tobatch) { - $detail.= $langs->trans("DetailBatchFormat",$dbatch->batch,dol_print_date($dbatch->eatby,"day"),dol_print_date($dbatch->sellby,"day"),$dbatch->dluo_qty).'
'; + $detail = ''; + foreach ($lines[$i]->detail_batch as $dbatch) + { + $detail.= $langs->trans("DetailBatchFormat",$dbatch->batch,dol_print_date($dbatch->eatby,"day"),dol_print_date($dbatch->sellby,"day"),$dbatch->dluo_qty).'
'; + } + print $form->textwithtooltip($langs->trans("DetailBatchNumber"),$detail); + } + else + { + print $langs->trans("NA"); } - print $form->textwithtooltip($langs->trans("DetailBatchNumber"),$detail); print '