diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php old mode 100644 new mode 100755 index 76577dc9ea6..cdfee4c2398 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -623,10 +623,10 @@ class ProductFournisseur extends Product */ function getSocNomUrl($withpicto=0,$option='supplier') { - $cust = new Fournisseur($this->db); - $cust->fetch($this->fourn_id); + $thirdparty = new Fournisseur($this->db); + $thirdparty->fetch($this->fourn_id); - return $cust->getNomUrl($withpicto,$option); + return $thirdparty->getNomUrl($withpicto,$option); } /** @@ -640,7 +640,7 @@ class ProductFournisseur extends Product { global $langs; $langs->load("suppliers"); - $out=($showunitprice?price($this->fourn_unitprice).' '.$langs->trans("HT").'   (':'').($showsuptitle?$langs->trans("Supplier").': ':'').$this->getSocNomUrl(1).' / '.$langs->trans("SupplierRef").': '.$this->fourn_ref.($showunitprice?')':''); + $out=($showunitprice?price($this->fourn_unitprice).' '.$langs->trans("HT").'   (':'').($showsuptitle?$langs->trans("Supplier").': ':'').$this->getSocNomUrl(1, 'supplier').' / '.$langs->trans("SupplierRef").': '.$this->fourn_ref.($showunitprice?')':''); return $out; } diff --git a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql index 301f6047ab6..60d0e0660da 100755 --- a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql +++ b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql @@ -76,6 +76,14 @@ create table llx_bank_account_extrafields import_key varchar(14) -- import key ) ENGINE=innodb; + +ALTER TABLE llx_stock_mouvement MODIFY COLUMN label varchar(255); +ALTER TABLE llx_stock_mouvement ADD COLUMN inventorycode varchar(128); + +ALTER TABLE llx_product_association ADD COLUMN incdec integer DEFAULT 1; + + + ALTER TABLE llx_bank_account_extrafields ADD INDEX idx_bank_account_extrafields (fk_object); diff --git a/htdocs/install/mysql/tables/llx_product_association.sql b/htdocs/install/mysql/tables/llx_product_association.sql index 731a9046609..6b248aef17f 100644 --- a/htdocs/install/mysql/tables/llx_product_association.sql +++ b/htdocs/install/mysql/tables/llx_product_association.sql @@ -22,6 +22,7 @@ create table llx_product_association rowid integer AUTO_INCREMENT PRIMARY KEY, fk_product_pere integer NOT NULL DEFAULT 0, -- id du produit maitre fk_product_fils integer NOT NULL DEFAULT 0, -- id du sous-produit - qty double NULL + qty double NULL, + incdec integer DEFAULT 1 -- when set to 1 changing stock of product will change stock of linked product too )ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_stock_mouvement.sql b/htdocs/install/mysql/tables/llx_stock_mouvement.sql index c0fdd702074..85b0290681f 100644 --- a/htdocs/install/mysql/tables/llx_stock_mouvement.sql +++ b/htdocs/install/mysql/tables/llx_stock_mouvement.sql @@ -28,7 +28,8 @@ create table llx_stock_mouvement price float(13,4) DEFAULT 0, type_mouvement smallint, fk_user_author integer, - label varchar(128), + label varchar(255), + inventorycode varchar(128), fk_origin integer, origintype varchar(32) )ENGINE=innodb; diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index d77c65a9a13..66e23987f09 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -122,4 +122,6 @@ RuleForStockAvailability=Rules on stock requirements StockMustBeEnoughForInvoice=Stock level must be enough to add product/service into invoice StockMustBeEnoughForOrder=Stock level must be enough to add product/service into order StockMustBeEnoughForShipment= Stock level must be enough to add product/service into shipment - +MovementLabel=Label of movement +InventoryCode=Inventory code +IsInPackage=Contained into package diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php old mode 100644 new mode 100755 index 6de6c80f3d6..243c1723af2 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -1697,7 +1697,6 @@ class Product extends CommonObject // We should not load stock at each fetch. If someone need stock, he must call load_stock after fetch. //$res=$this->load_stock(); - // instead we just init the stock_warehouse array $this->stock_warehouse = array(); @@ -3027,9 +3026,10 @@ class Product extends CommonObject * @param int $movement 0 = add, 1 = remove * @param string $label Label of stock movement * @param double $price Price to use for stock eval + * @param string $inventorycode Inventory code * @return int <0 if KO, >0 if OK */ - function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0) + function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='') { if ($id_entrepot) { @@ -3041,7 +3041,7 @@ class Product extends CommonObject $op[1] = "-".trim($nbpiece); $movementstock=new MouvementStock($this->db); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label); + $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode); if ($result >= 0) { @@ -3643,9 +3643,10 @@ class Product extends CommonObject * @param date $dlc eat-by date * @param date $dluo sell-by date * @param string $lot Lot number + * @param string $inventorycode Inventory code * @return int <0 if KO, >0 if OK */ - function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='') + function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='', $inventorycode='') { if ($id_entrepot) { @@ -3657,7 +3658,7 @@ class Product extends CommonObject $op[1] = "-".trim($nbpiece); $movementstock=new MouvementStock($this->db); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,'',$dlc,$dluo,$lot); + $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode,'',$dlc,$dluo,$lot); if ($result >= 0) { diff --git a/htdocs/product/composition/card.php b/htdocs/product/composition/card.php index ca139a87965..12553ada59a 100644 --- a/htdocs/product/composition/card.php +++ b/htdocs/product/composition/card.php @@ -34,6 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; $langs->load("bills"); $langs->load("products"); +$langs->load("stocks"); $id=GETPOST('id','int'); $ref=GETPOST('ref','alpha'); @@ -456,7 +457,7 @@ if ($id > 0 || ! empty($ref)) print ''; print ''.$langs->trans("Ref").''; print ''.$langs->trans("Label").''; - print ''.$langs->trans("AddDel").''; + print ''.$langs->trans("IsInPackage").''; print ''.$langs->trans("Qty").''; print ''; if ($resql) diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index ad99f68c9b0..0c8553e424d 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -55,6 +55,7 @@ class MouvementStock extends CommonObject * 2=output (stock decrease), 3=input (stock increase) * @param int $price Unit price HT of product, used to calculate average weighted price (PMP in french). If 0, average weighted price is not changed. * @param string $label Label of stock movement + * @param string $inventorycode Inventory code * @param string $datem Force date of movement * @param date $eatby eat-by date * @param date $sellby sell-by date @@ -62,21 +63,27 @@ class MouvementStock extends CommonObject * @param boolean $skip_sellby If set to true, stock mouvement is done without impacting batch record * @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='', $datem='',$eatby='',$sellby='',$batch='',$skip_sellby=false) + function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $inventorycode='', $datem='',$eatby='',$sellby='',$batch='',$skip_sellby=false) { global $conf, $langs; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; $error = 0; - dol_syslog(get_class($this)."::_create start userid=$user->id, fk_product=$fk_product, warehouse=$entrepot_id, qty=$qty, type=$type, price=$price label=$label"); + dol_syslog(get_class($this)."::_create start userid=$user->id, fk_product=$fk_product, warehouse=$entrepot_id, qty=$qty, type=$type, price=$price, label=$label, inventorycode=$inventorycode"); // Clean parameters if (empty($price)) $price=0; + $now=(! empty($datem) ? $datem : dol_now()); + // Check parameters if (empty($fk_product)) return 0; - $now=(! empty($datem) ? $datem : dol_now()); - + // Set properties of movement + $this->product_id = $fk_product; + $this->entrepot_id = $entrepot_id; + $this->qty = $qty; + $this->type = $type; + $this->db->begin(); $product = new Product($this->db); @@ -94,19 +101,22 @@ class MouvementStock extends CommonObject if ($movestock && $entrepot_id > 0) // Change stock for current product, change for subproduct is done after { - if(!empty($this->origin)) { + if(!empty($this->origin)) { // This is set by caller for tracking reason $origintype = $this->origin->element; $fk_origin = $this->origin->id; } else { $origintype = ''; $fk_origin = 0; } + + $mvid = 0; $sql = "INSERT INTO ".MAIN_DB_PREFIX."stock_mouvement"; - $sql.= " (datem, fk_product, fk_entrepot, value, type_mouvement, fk_user_author, label, price, fk_origin, origintype)"; - $sql.= " VALUES ('".$this->db->idate($now)."', ".$fk_product.", ".$entrepot_id.", ".$qty.", ".$type.","; + $sql.= " (datem, fk_product, fk_entrepot, value, type_mouvement, fk_user_author, label, inventorycode, price, fk_origin, origintype)"; + $sql.= " VALUES ('".$this->db->idate($now)."', ".$this->product_id.", ".$this->entrepot_id.", ".$this->qty.", ".$this->type.","; $sql.= " ".$user->id.","; $sql.= " '".$this->db->escape($label)."',"; + $sql.= " '".$this->db->escape($inventorycode)."',"; $sql.= " '".price2num($price)."',"; $sql.= " '".$fk_origin."',"; $sql.= " '".$origintype."'"; @@ -117,6 +127,7 @@ class MouvementStock extends CommonObject if ($resql) { $mvid = $this->db->last_insert_id(MAIN_DB_PREFIX."stock_mouvement"); + $this->id = $mvid; } else { @@ -159,10 +170,11 @@ class MouvementStock extends CommonObject } // Calculate new PMP. + $newpmp=0; + $newpmpwarehouse=0; + /* if (! $error) { - $newpmp=0; - $newpmpwarehouse=0; // Note: PMP is calculated on stock input only (type of movement = 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 == 0 || $type == 3) && $price > 0) @@ -190,9 +202,9 @@ class MouvementStock extends CommonObject 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;*/ + //print "oldqtytouse=".$oldqtytouse." oldpmp=".$oldpmp." oldqtywarehousetouse=".$oldqtywarehousetouse." oldpmpwarehouse=".$oldpmpwarehouse." "; + //print "qty=".$qty." newpmp=".$newpmp." newpmpwarehouse=".$newpmpwarehouse; + //exit; } else if ($type == 1 || $type == 2) { @@ -204,7 +216,8 @@ class MouvementStock extends CommonObject $newpmpwarehouse = $oldpmpwarehouse; } } - + */ + // Update denormalized value of stock in product_stock and product if (! $error) { @@ -262,21 +275,15 @@ class MouvementStock extends CommonObject // Add movement for sub products (recursive call) if (! $error && ! empty($conf->global->PRODUIT_SOUSPRODUITS)) { - $error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label); // we use 0 as price, because pmp is not changed for subproduct + $error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label, $inventorycode); // we use 0 as price, because pmp is not changed for subproduct } if ($movestock && ! $error) { - $this->product_id = $fk_product; - $this->entrepot_id = $entrepot_id; - $this->qty = $qty; - // Call trigger $result=$this->call_trigger('STOCK_MOVEMENT',$user); if ($result < 0) $error++; // End call triggers - - //FIXME: Restore previous value of product_id, entrepot_id, qty if trigger fail } if (! $error) @@ -303,9 +310,10 @@ class MouvementStock extends CommonObject * @param int $type Type * @param int $price Price * @param string $label Label of movement + * @param string $inventorycode Inventory code * @return int <0 if KO, 0 if OK */ - function _createSubProduct($user, $idProduct, $entrepot_id, $qty, $type, $price=0, $label='') + function _createSubProduct($user, $idProduct, $entrepot_id, $qty, $type, $price=0, $label='', $inventorycode='') { $error = 0; $pids = array(); @@ -314,7 +322,9 @@ class MouvementStock extends CommonObject $sql = "SELECT fk_product_pere, fk_product_fils, qty"; $sql.= " FROM ".MAIN_DB_PREFIX."product_association"; $sql.= " WHERE fk_product_pere = ".$idProduct; - + // TODO Select only subproduct with incdec tag + //$sql.= " AND incdec = 1"; + dol_syslog(get_class($this)."::_createSubProduct", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) @@ -336,7 +346,9 @@ class MouvementStock extends CommonObject // Create movement for each subproduct foreach($pids as $key => $value) { - $this->_create($user, $pids[$key], $entrepot_id, ($qty * $pqtys[$key]), $type, 0, $label); + $tmpmove = dol_clone($this); + $tmpmove->_create($user, $pids[$key], $entrepot_id, ($qty * $pqtys[$key]), $type, 0, $label, $inventorycode); // This will also call _createSubProduct making this recursive + unset($tmpmove); } return $error; @@ -357,7 +369,7 @@ class MouvementStock extends CommonObject */ function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='') { - 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,'','','',true); } /** @@ -389,7 +401,7 @@ class MouvementStock extends CommonObject */ function reception($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $eatby='', $sellby='', $batch='') { - return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, '', $eatby, $sellby, $batch); + return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, '', '', $eatby, $sellby, $batch); } diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index f525457e1c5..c9451a2d744 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -50,6 +50,7 @@ $search_movement = GETPOST("search_movement"); $search_product_ref = trim(GETPOST("search_product_ref")); $search_product = trim(GETPOST("search_product")); $search_warehouse = trim(GETPOST("search_warehouse")); +$search_inventorycode = trim(GETPOST("search_inventorycode")); $search_user = trim(GETPOST("search_user")); $page = GETPOST("page",'int'); $sortfield = GETPOST("sortfield",'alpha'); @@ -120,7 +121,7 @@ $formproduct=new FormProduct($db); $sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.fk_product_type as type,"; $sql.= " e.label as stock, e.rowid as entrepot_id, e.lieu,"; -$sql.= " m.rowid as mid, m.value, m.datem, m.fk_user_author, m.label, m.fk_origin, m.origintype,"; +$sql.= " m.rowid as mid, m.value, m.datem, m.fk_user_author, m.label, m.inventorycode, m.fk_origin, m.origintype,"; $sql.= " u.login"; $sql.= " FROM (".MAIN_DB_PREFIX."entrepot as e,"; $sql.= " ".MAIN_DB_PREFIX."product as p,"; @@ -149,6 +150,10 @@ if (! empty($search_movement)) { $sql.= " AND m.label LIKE '%".$db->escape($search_movement)."%'"; } +if (! empty($search_inventorycode)) +{ + $sql.= " AND m.inventorycode LIKE '%".$db->escape($search_inventorycode)."%'"; +} if (! empty($search_product_ref)) { $sql.= " AND p.ref LIKE '%".$db->escape($search_product_ref)."%'"; @@ -410,6 +415,7 @@ if ($resql) $param=''; if ($id) $param.='&id='.$id; if ($search_movement) $param.='&search_movement='.urlencode($search_movement); + if ($search_inventorycode) $param.='&search_inventorycode='.urlencode($search_inventorycode); if ($search_product_ref) $param.='&search_product_ref='.urlencode($search_product_ref); if ($search_product) $param.='&search_product='.urlencode($search_product); if ($search_warehouse) $param.='&search_warehouse='.urlencode($search_warehouse); @@ -425,7 +431,8 @@ if ($resql) //print_liste_field_titre($langs->trans("Id"),$_SERVER["PHP_SELF"], "m.rowid","",$param,"",$sortfield,$sortorder); print_liste_field_titre($langs->trans("Date"),$_SERVER["PHP_SELF"], "m.datem","",$param,"",$sortfield,$sortorder); print_liste_field_titre($langs->trans("LabelMovement"),$_SERVER["PHP_SELF"], "m.label","",$param,"",$sortfield,$sortorder); - print_liste_field_titre($langs->trans("Source"),$_SERVER["PHP_SELF"], "m.label","",$param,"",$sortfield,$sortorder); + print_liste_field_titre($langs->trans("InventoryCode"),$_SERVER["PHP_SELF"], "m.inventorycode","",$param,"",$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Source"),$_SERVER["PHP_SELF"], "m.label","",$param,"",$sortfield,$sortorder); print_liste_field_titre($langs->trans("ProductRef"),$_SERVER["PHP_SELF"], "p.ref","",$param,"",$sortfield,$sortorder); print_liste_field_titre($langs->trans("ProductLabel"),$_SERVER["PHP_SELF"], "p.ref","",$param,"",$sortfield,$sortorder); print_liste_field_titre($langs->trans("Warehouse"),$_SERVER["PHP_SELF"], "","",$param,"",$sortfield,$sortorder); // We are on a specific warehouse card, no filter on other should be possible @@ -448,6 +455,10 @@ if ($resql) print ''; print ''; print ''; + // Inventory code + print ''; + print ''; + print ''; // Origin of movement print ''; print '  '; @@ -495,7 +506,9 @@ if ($resql) print ''.dol_print_date($db->jdate($objp->datem),'dayhour').''; // Label of movement print ''.$objp->label.''; - // Origin of movement + // Inventory code + print ''.$objp->inventorycode.''; + // Origin of movement print ''.$origin.''; // Product ref print ''; diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 7d900480bea..8517a384f06 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -135,11 +135,12 @@ if ($action == "correct_stock" && ! $cancel) GETPOST("id_entrepot"), GETPOST("nbpiece"), GETPOST("mouvement"), - GETPOST("label"), + GETPOST("label"), // label movement $priceunit, $d_eatby, $d_sellby, - GETPOST('batch_number') + GETPOST('batch_number'), + GETPOST('inventorycode') ); // We do not change value of stock for a correction } else @@ -150,7 +151,8 @@ if ($action == "correct_stock" && ! $cancel) GETPOST("nbpiece"), GETPOST("mouvement"), GETPOST("label"), - $priceunit + $priceunit, + GETPOST('inventorycode') ); // We do not change value of stock for a correction } @@ -566,7 +568,7 @@ if ($id > 0 || $ref) print ''; print ''.$langs->trans("Warehouse").''; print ''; - print $formproduct->selectWarehouses(($_GET["dwid"]?$_GET["dwid"]:GETPOST('id_entrepot')),'id_entrepot','',1); + print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOT("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot','int'):'ifone')),'id_entrepot','',1); print ''; print ''; print ''; - print ''; - print ''.$langs->trans("UnitPurchaseValue").''; + print ''.$langs->trans("UnitPurchaseValue").''; + print ''; print ''; //eat-by date @@ -601,8 +600,18 @@ if ($id > 0 || $ref) print ''; print ''; } - print ''; + // Label of mouvement of id of inventory + print ''; + print ''.$langs->trans("MovementLabel").''; + print ''; + print ''; + print ''; + print ''.$langs->trans("InventoryCode").''; + print ''; + + print ''; + print '
'; print ''; print '     '; @@ -651,7 +660,7 @@ if ($id > 0 || $ref) } else { - print $formproduct->selectWarehouses(($_GET["dwid"]?$_GET["dwid"]:GETPOST('id_entrepot_source')),'id_entrepot_source','',1); + print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOT("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot_source','int'):'ifone')),'id_entrepot_source','',1); } print ''; print ''.$langs->trans("WarehouseTarget").'';