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 '';
+ // 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").' | ';
|