diff --git a/htdocs/install/mysql/migration/2.8.0-2.9.0.sql b/htdocs/install/mysql/migration/2.8.0-2.9.0.sql index e5256fc47d8..0c4403c3486 100755 --- a/htdocs/install/mysql/migration/2.8.0-2.9.0.sql +++ b/htdocs/install/mysql/migration/2.8.0-2.9.0.sql @@ -6,6 +6,9 @@ -- when current version is 2.8.0 or higher. -- +-- Add unique key +ALTER TABLE llx_product_stock ADD UNIQUE INDEX uk_product_stock (fk_product,fk_entrepot); + -- Add missing table llx_product_association create table llx_product_association diff --git a/htdocs/install/mysql/tables/llx_product_stock.key.sql b/htdocs/install/mysql/tables/llx_product_stock.key.sql index f80f2ed0cc9..93a28593f8e 100644 --- a/htdocs/install/mysql/tables/llx_product_stock.key.sql +++ b/htdocs/install/mysql/tables/llx_product_stock.key.sql @@ -1,6 +1,6 @@ -- ============================================================================ -- Copyright (C) 2002-2004 Rodolphe Quiedeville --- Copyright (C) 2004-2005 Laurent Destailleur +-- Copyright (C) 2004-2010 Laurent Destailleur -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -21,5 +21,6 @@ ALTER TABLE llx_product_stock ADD INDEX idx_product_stock_fk_product (fk_product); - ALTER TABLE llx_product_stock ADD INDEX idx_product_stock_fk_entrepot (fk_entrepot); + +ALTER TABLE llx_product_stock ADD UNIQUE INDEX uk_product_stock (fk_product,fk_entrepot); diff --git a/htdocs/install/mysql/tables/llx_product_stock.sql b/htdocs/install/mysql/tables/llx_product_stock.sql index 082c040b80d..8153b6447ff 100644 --- a/htdocs/install/mysql/tables/llx_product_stock.sql +++ b/htdocs/install/mysql/tables/llx_product_stock.sql @@ -26,7 +26,6 @@ create table llx_product_stock fk_product integer NOT NULL, fk_entrepot integer NOT NULL, reel real, -- physical stock - pmp double(24,8) default 0 NOT NULL, -- PMP value for product in this warehouse - location varchar(32) -- More information on location of product in warehouse + pmp double(24,8) default 0 NOT NULL -- PMP value for product in this warehouse )type=innodb; diff --git a/htdocs/livraison/class/livraison.class.php b/htdocs/livraison/class/livraison.class.php index 6b2d5c1251b..597ee12c4ff 100644 --- a/htdocs/livraison/class/livraison.class.php +++ b/htdocs/livraison/class/livraison.class.php @@ -394,7 +394,7 @@ class Livraison extends CommonObject $i=0; while($i < $num) { - dol_syslog("livraison.class.php::valid movment $i"); + dol_syslog("livraison.class.php::valid movement $i"); $obj = $this->db->fetch_object($resql); diff --git a/htdocs/livraison/fiche.php b/htdocs/livraison/fiche.php index 45bbbc17aaa..25bd122287c 100644 --- a/htdocs/livraison/fiche.php +++ b/htdocs/livraison/fiche.php @@ -331,7 +331,7 @@ if ($_GET["action"] == 'create') if ($conf->stock->enabled) { - $stock = $product->stock_entrepot[$_GET["entrepot_id"]]; + $stock = $product->stock_warehouse[$_GET["entrepot_id"]]->real; $stock+=0; // Convertit en numerique // Quantite a livrer diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 6734d18c72b..342fa277232 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -130,6 +130,10 @@ class Product extends CommonObject var $nbphoto; + //! Contains detail of stock of product into each warehouse + var $stock_warehouse=array(); + + /** * \brief Constructeur de la classe * \param DB Handler acces base de donnees @@ -1933,7 +1937,7 @@ class Product extends CommonObject $img=""; $this->fetch($desc_pere[0]); $this->load_stock(); - if ($this->stock_entrepot[1] < $this->seuil_stock_alerte) + if ($this->stock_warehouse[1]->real < $this->seuil_stock_alerte) { $img=img_warning($langs->trans("StockTooLow")); } @@ -1946,7 +1950,7 @@ class Product extends CommonObject */ 'id'=>$desc_pere[0], // Id product 'nb'=>$desc_pere[1], // Nb of units that compose parent product 'nb_total'=>$desc_pere[1]*$multiply, // Nb of units for all nb of product - 'stock'=>$this->stock_entrepot[1], // Stock + 'stock'=>$this->stock_warehouse[1]->real, // Stock 'stock_alert'=>$this->seuil_stock_alerte, // Stock alert 'fullpath' => $compl_path.$nom_pere, // Label 'type'=>$desc_pere[2] // Nb of units that compose parent product @@ -1962,7 +1966,7 @@ class Product extends CommonObject */ 'id'=>$desc_pere[0], // Id product 'nb'=>$desc_pere[1], // Nb of units that compose parent product 'nb_total'=>$desc_pere[1], // Nb of units for all nb of product - 'stock'=>$this->stock_entrepot[1], // Stock + 'stock'=>$this->stock_warehouse[1]->real, // Stock 'stock_alert'=>$this->seuil_stock_alerte, // Stock alert 'fullpath' => $compl_path.$nom_pere, // Label 'type'=>$desc_pere[2] // Nb of units that compose parent product @@ -2235,41 +2239,28 @@ class Product extends CommonObject /** - * \brief Ajuste le stock d'un entrepot pour le produit d'un delta donne - * \param user utilisateur qui demande l'ajustement + * \brief Adjust stock in a warehouse for product + * \param user user asking change * \param id_entrepot id of warehouse * \param nbpiece nb of units - * \param mouvement 0 = add, 1 = remove - * \param label Label of stock movment + * \param movement 0 = add, 1 = remove + * \param label Label of stock movement + * \param price Price to use for stock eval * \return int <0 if KO, >0 if OK */ - function correct_stock($user, $id_entrepot, $nbpiece, $mouvement, $label='') + function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0) { if ($id_entrepot) { $this->db->begin(); - $sql = "SELECT count(*) as nb"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_stock"; - $sql.= " WHERE fk_product = ".$this->id; - $sql.= " AND fk_entrepot = ".$id_entrepot; + require_once(DOL_DOCUMENT_ROOT ."/product/stock/class/mouvementstock.class.php"); - dol_syslog("Product::correct_stock sql=".$sql, LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - $row = $this->db->fetch_object($resql); - if ($row->nb > 0) - { - // Record already exists, we make an update - $result=$this->ajust_stock($user, $id_entrepot, $nbpiece, $mouvement, $label); - } - else - { - // Record not yet available, we make an insert - $result=$this->create_stock($user, $id_entrepot, $nbpiece, $mouvement, $label); - } - } + $op[0] = "+".trim($nbpiece); + $op[1] = "-".trim($nbpiece); + + $movementstock=new MouvementStock($this->db); + $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label); if ($result >= 0) { @@ -2286,60 +2277,14 @@ class Product extends CommonObject } /** - * \brief Augmente ou reduit la valeur de stock pour le produit - * \param user user that make change - * \param id_entrepot id of warehouse - * \param nbpiece nb of units - * \param movement 0 = add, 1 = remove - * \param label Label of stock movment - * \return int <0 if KO, >0 if OK - * \remarks Called by correct_stock - */ - function ajust_stock($user, $id_entrepot, $nbpiece, $movement, $label='') - { - require_once(DOL_DOCUMENT_ROOT ."/product/stock/class/mouvementstock.class.php"); - - $op[0] = "+".trim($nbpiece); - $op[1] = "-".trim($nbpiece); - - $movementstock=new MouvementStock($this->db); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],0,0,$label); - - return $result; - } - - /** - * \brief Entre un nombre de piece du produit en stock dans un entrepot - * \param user user that make change - * \param id_entrepot id of warehouse - * \param nbpiece nb of units - * \param movement 0 = add, 1 = remove - * \param label Label of stock movment - * \return int <0 if KO, >0 if OK - * \remarks Called by correct_stock - */ - function create_stock($user, $id_entrepot, $nbpiece, $movement=0, $label='') - { - require_once(DOL_DOCUMENT_ROOT ."/product/stock/class/mouvementstock.class.php"); - - $op[0] = "+".trim($nbpiece); - $op[1] = "-".trim($nbpiece); - - $movementstock=new MouvementStock($this->db); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],0,0,$label); - - return $result; - } - - /** - * \brief Charge les informations en stock du produit dans stock_entrepot[] et stock_reel + * \brief Load information about stock of a product into stock_warehouse[] and stock_reel * \return int < 0 si erreur, > 0 si ok */ function load_stock() { $this->stock_reel = 0; - $sql = "SELECT reel, fk_entrepot"; + $sql = "SELECT reel, fk_entrepot, pmp"; $sql.= " FROM ".MAIN_DB_PREFIX."product_stock"; $sql.= " WHERE fk_product = '".$this->id."'"; @@ -2353,9 +2298,10 @@ class Product extends CommonObject { while ($i < $num) { - $row = $this->db->fetch_row($result); - $this->stock_entrepot[$row[1]] = $row[0]; - $this->stock_reel = $this->stock_reel + $row[0]; + $row = $this->db->fetch_object($result); + $this->stock_warehouse[$row->fk_entrepot]->real = $row->reel; + $this->stock_warehouse[$row->fk_entrepot]->pmp = $row->pmp; + $this->stock_reel+=$row->reel; $i++; } diff --git a/htdocs/product/reassort.php b/htdocs/product/reassort.php index 3f7419af97c..d41144491ec 100644 --- a/htdocs/product/reassort.php +++ b/htdocs/product/reassort.php @@ -95,8 +95,15 @@ if ($_GET["fourn_id"] > 0) $sql.= " WHERE p.rowid = s.fk_product"; $sql.= " AND p.entity = ".$conf->entity; if ($search_categ) $sql.= " AND p.rowid = cp.fk_product"; // Join for the needed table to filter by categ -if (!$user->rights->produit->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 0)'; -if (!$user->rights->service->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 1)'; +if (!$user->rights->produit->hidden && !$user->rights->service->hidden) +{ + $sql.=' AND p.hidden=0'; +} +else +{ + if (!$user->rights->produit->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 0)'; + if (!$user->rights->service->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 1)'; +} if ($sall) { $sql.= " AND (p.ref like '%".addslashes($sall)."%' OR p.label like '%".addslashes($sall)."%' OR p.description like '%".addslashes($sall)."%' OR p.note like '%".addslashes($sall)."%')"; diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 2840cb4dde5..b411e599d7d 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -38,14 +38,16 @@ class MouvementStock } /** - * \brief Add a movement in stock (in one direction only) + * \brief Add a movement of stock (in one direction only) * \param user User object * \param fk_product Id of product * \param entrepot_id Id of warehouse * \param qty Qty of movement (can be <0 or >0) - * \param type Direction of movement: 2=output (stock decrease), 3=input (stock increase) - * \param type Unit price HT of product - * \pamam label Label of stock movment + * \param type Direction of movement: + * 0=input (stock increase after stock transfert), 1=output (stock decrease after stock transfer), + * 2=output (stock decrease), 3=input (stock increase) + * \param price Unit price HT of product + * \pamam label Label of stock movement * \return int <0 if KO, >0 if OK */ function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='') @@ -126,17 +128,21 @@ class MouvementStock { $newpmp=0; $newpmpwarehouse=0; - // Note: PMP is calculated on stock input only (type = 3). If type == 3, qty should be > 0. + // Note: PMP is calculated on stock input only (type = 0 or 3). If type == 0 or 3, qty should be > 0. // Note: Price should always be >0 or 0. PMP should be always >0 (calculated on input) - if ($type == 3 && $price > 0) + if (($type == 0 || $type == 3) && $price > 0) { $oldqtytouse=($oldqty >= 0?$oldqty:0); // We make a test on oldpmp>0 to avoid to use normal rule on old data with no pmp field defined if ($oldpmp > 0) $newpmp=price2num((($oldqtytouse * $oldpmp) + ($qty * $price)) / ($oldqtytouse + $qty), 'MU'); else $newpmp=$price; - $oldqtywarehousetouse=($oldqtywarehouse >= 0?$oldqty:0); + $oldqtywarehousetouse=($oldqtywarehouse >= 0?$oldqtywarehouse:0); if ($oldpmpwarehouse > 0) $newpmpwarehouse=price2num((($oldqtywarehousetouse * $oldpmpwarehouse) + ($qty * $price)) / ($oldqtywarehousetouse + $qty), 'MU'); else $newpmpwarehouse=$price; + + //print "oldqtytouse=".$oldqtytouse." oldpmp=".$oldpmp." oldqtywarehousetouse=".$oldqtywarehousetouse." oldpmpwarehouse=".$oldpmpwarehouse." "; + //print "qty=".$qty." newpmp=".$newpmp." newpmpwarehouse=".$newpmpwarehouse; + //exit; } else { @@ -192,7 +198,7 @@ class MouvementStock $error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0); // pmp is not change for subproduct } - // composition module + // composition module (this is a non official external module) if (! $error && $qty < 0 && $conf->global->MAIN_MODULE_COMPOSITION) { $error = $this->_createProductComposition($user, $fk_product, $entrepot_id, $qty, $type, 0); // pmp is not change for subproduct diff --git a/htdocs/product/stock/fiche.php b/htdocs/product/stock/fiche.php index 87b8de2442a..e63360b268d 100644 --- a/htdocs/product/stock/fiche.php +++ b/htdocs/product/stock/fiche.php @@ -361,8 +361,15 @@ else $sql.= " WHERE ps.fk_product = p.rowid"; $sql.= " AND ps.reel <> 0"; // We do not show if stock is 0 (no product in this warehouse) $sql.= " AND ps.fk_entrepot = ".$entrepot->id; - if (!$user->rights->produit->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 0)'; - if (!$user->rights->service->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 1)'; + if (!$user->rights->produit->hidden && !$user->rights->service->hidden) + { + $sql.=' AND p.hidden=0'; + } + else + { + if (!$user->rights->produit->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 0)'; + if (!$user->rights->service->hidden) $sql.=' AND (p.hidden=0 OR p.fk_product_type != 1)'; + } $sql.= $db->order($sortfield,$sortorder); //$sql .= $db->plimit($limit + 1 ,$offset); diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index 1b02d854147..be79c6e419c 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -40,7 +40,7 @@ if (!$user->rights->produit->lire) accessforbidden(); $idproduct = isset($_GET["idproduct"])?$_GET["idproduct"]:$_PRODUCT["idproduct"]; $year = isset($_GET["year"])?$_GET["year"]:$_POST["year"]; $month = isset($_GET["month"])?$_GET["month"]:$_POST["month"]; -$search_movment = isset($_REQUEST["search_movment"])?$_REQUEST["search_movment"]:''; +$search_movement = isset($_REQUEST["search_movement"])?$_REQUEST["search_movement"]:''; $search_product = isset($_REQUEST["search_product"])?$_REQUEST["search_product"]:''; $search_warehouse = isset($_REQUEST["search_warehouse"])?$_REQUEST["search_warehouse"]:''; $search_user = isset($_REQUEST["search_user"])?$_REQUEST["search_user"]:''; @@ -57,7 +57,7 @@ if ($_REQUEST["button_removefilter"]) { $year=''; $month=''; - $search_movment=""; + $search_movement=""; $search_product=""; $search_warehouse=""; $search_user=""; @@ -103,9 +103,9 @@ else if ($year > 0) { $sql.= " AND m.datem BETWEEN '".$db->idate(dol_get_first_day($year,1,false))."' AND '".$db->idate(dol_get_last_day($year,12,false))."'"; } -if (! empty($search_movment)) +if (! empty($search_movement)) { - $sql.= " AND m.label LIKE '%".addslashes($search_movment)."%'"; + $sql.= " AND m.label LIKE '%".addslashes($search_movement)."%'"; } if (! empty($search_product)) { @@ -234,7 +234,7 @@ if ($resql) $param=''; if ($_GET["id"]) $param.='&id='.$_GET["id"]; - if ($search_movment) $param.='&search_movment='.urlencode($search_movment); + if ($search_movement) $param.='&search_movement='.urlencode($search_movement); if ($search_product) $param.='&search_product='.urlencode($search_product); if ($search_warehouse) $param.='&search_warehouse='.urlencode($search_warehouse); if ($sref) $param.='&sref='.urlencode($sref); @@ -266,9 +266,9 @@ if ($resql) $syear = $year; $form->select_year($syear,'year',1, '', $max_year); print ''; - // Label of movment + // Label of movement print ''; - print ''; + print ''; print ''; // Product print ''; @@ -296,7 +296,7 @@ if ($resql) //print ''.$objp->mid.''; // This is primary not movement id // Date print ''.dol_print_date($db->jdate($objp->datem),'dayhour').''; - // Lbale of movment + // Label of movement print ''.$objp->label.''; // Product print ''; diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index a1abaabb5f0..6db8ebc14e8 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -54,29 +54,24 @@ $mesg = ''; * Actions */ -if ($_POST["action"] == "create_stock" && ! $_POST["cancel"]) -{ - $product = new Product($db); - $product->id = $_GET["id"]; - $product->create_stock($user, $_POST["id_entrepot"], $_POST["nbpiece"]); -} - -// Transfer stock +// Correct stock if ($_POST["action"] == "correct_stock" && ! $_POST["cancel"]) { if (is_numeric($_POST["nbpiece"])) { $product = new Product($db); - $product->id = $_GET["id"]; + $result=$product->fetch($_GET["id"]); + $product->correct_stock($user, $_POST["id_entrepot"], $_POST["nbpiece"], $_POST["mouvement"], - $_POST["label"]); + $_POST["label"], + 0); // We do not change value of stock for a correction } } -// Correct stock +// Transfer stock from a warehouse to another warehouse if ($_POST["action"] == "transfert_stock" && ! $_POST["cancel"]) { if ($_POST["id_entrepot_source"] <> $_POST["id_entrepot_destination"]) @@ -84,21 +79,34 @@ if ($_POST["action"] == "transfert_stock" && ! $_POST["cancel"]) if (is_numeric($_POST["nbpiece"])) { $product = new Product($db); - $product->id = $_GET["id"]; + $result=$product->fetch($_GET["id"]); $db->begin(); + $product->load_stock(); // Load array product->stock_warehouse + + // Define value of products moved + $pricesrc=0; + if (isset($product->stock_warehouse[$_POST["id_entrepot_source"]]->pmp)) $pricesrc=$product->stock_warehouse[$_POST["id_entrepot_source"]]->pmp; + $pricedest=$pricesrc; + + //print 'price src='.$pricesrc.', price dest='.$pricedest;exit; + + // Remove stock $result1=$product->correct_stock($user, $_POST["id_entrepot_source"], $_POST["nbpiece"], 1, - $_POST["label"]); + $_POST["label"], + $pricesrc); + // Add stock $result2=$product->correct_stock($user, $_POST["id_entrepot_destination"], $_POST["nbpiece"], 0, - $_POST["label"]); + $_POST["label"], + $pricedest); if ($result1 >= 0 && $result2 >= 0) { @@ -334,6 +342,7 @@ if ($_GET["id"] || $_GET["ref"]) /* * Set initial stock */ + /* if ($_GET["action"] == "definir") { print_titre($langs->trans("SetStock")); @@ -349,6 +358,7 @@ if ($_GET["id"] || $_GET["ref"]) print ''; print ''; } + */ } else { @@ -416,7 +426,7 @@ if ($resql) $entrepotstatic->libelle=$obj->label; print ''; print ''.$entrepotstatic->getNomUrl(1).''; - print ''.$obj->reel.''; + print ''.$obj->reel.($obj->reel<0?' '.img_warning():'').''; print ''.price2num($obj->pmp,'MU').''; print ''.price(price2num($obj->pmp*$obj->reel,'MT')).''; print ''; ; @@ -428,7 +438,7 @@ if ($resql) } print ''.$langs->trans("Total").':'; print ''.$total.''; -print ' '; +print ''.price($totalvalue/$total).''; print ''.price($totalvalue).''; print ""; print ""; diff --git a/htdocs/product/stock/valo.php b/htdocs/product/stock/valo.php index 9ce6a16bcc6..e4201dcc85c 100644 --- a/htdocs/product/stock/valo.php +++ b/htdocs/product/stock/valo.php @@ -56,7 +56,8 @@ $year = strftime("%Y",time()); // Affichage valorisation par entrepot $sql = "SELECT e.rowid as ref, e.label, e.statut, e.lieu,"; $sql.= " SUM(ps.pmp * ps.reel) as estimatedvalue"; -$sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps ON e.rowid = ps.fk_entrepot"; +$sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps ON e.rowid = ps.fk_entrepot"; $sql.= " WHERE e.entity = ".$conf->entity; if ($sref) {