diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php
index 26c8a5beef9..0e4cd66e323 100644
--- a/htdocs/commande/list.php
+++ b/htdocs/commande/list.php
@@ -715,7 +715,7 @@ if ($resql)
// Get local and virtual stock and store it into cache
if (empty($productstat_cache[$generic_commande->lines[$lig]->fk_product])) {
- $generic_product->load_stock();
+ $generic_product->load_stock('nobatch');
//$generic_product->load_virtual_stock(); Already included into load_stock
$productstat_cache[$generic_commande->lines[$lig]->fk_product]['stock_reel'] = $generic_product->stock_reel;
$productstat_cachevirtual[$generic_commande->lines[$lig]->fk_product]['stock_reel'] = $generic_product->stock_theorique;
@@ -723,7 +723,7 @@ if ($resql)
$generic_product->stock_reel = $productstat_cache[$generic_commande->lines[$lig]->fk_product]['stock_reel'];
$generic_product->stock_theorique = $productstat_cachevirtual[$generic_commande->lines[$lig]->fk_product]['stock_reel'] = $generic_product->stock_theorique;
}
-
+
if (empty($conf->global->SHIPPABLE_ORDER_ICON_IN_LIST)) // Default code. Default is when this option is not set, setting it create strange result
{
$text_info .= $generic_commande->lines[$lig]->qty.' X '.$generic_commande->lines[$lig]->ref.' '.dol_trunc($generic_commande->lines[$lig]->product_label, 25);
diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php
index 697e5e8bdeb..e43fa769344 100644
--- a/htdocs/core/class/html.form.class.php
+++ b/htdocs/core/class/html.form.class.php
@@ -2057,17 +2057,27 @@ class Form
{
global $langs,$conf;
global $price_level, $status, $finished;
-
+
+ $selected_input_value='';
if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT))
{
+ if ($selected > 0)
+ {
+ require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+ $producttmpselect = new Product($this->db);
+ $producttmpselect->fetch($selected);
+ $selected_input_value=$producttmpselect->ref;
+ unset($producttmpselect);
+ }
+
if (!empty($conf->global->SUPPLIER_ORDER_WITH_NOPRICEDEFINED))
{
print '';
}
// mode=2 means suppliers products
$urloption=($socid > 0?'socid='.$socid.'&':'').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished;
- print ajax_autocompleter('', $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
- print ($hidelabel?'':$langs->trans("RefOrLabel").' : ').'';
+ print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
+ print ($hidelabel?'':$langs->trans("RefOrLabel").' : ').'';
}
else
{
@@ -2089,7 +2099,7 @@ class Form
* @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service)
* @param string $filtre Pour filtre sql
* @param string $filterkey Filtre des produits
- * @param int $statut -1=Return all products, 0=Products not on sell, 1=Products on sell
+ * @param int $statut -1=Return all products, 0=Products not on sell, 1=Products on sell (not used here, a filter on tobuy is already hard coded in request)
* @param int $outputmode 0=HTML select string, 1=Array
* @param int $limit Limit of line number
* @return array Array of keys for json
@@ -2106,7 +2116,7 @@ class Form
$sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration, p.fk_product_type,";
$sql.= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
$sql.= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,";
- $sql.= " pfp.supplier_reputation";
+ $sql.= " pfp.supplier_reputation";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
if ($socid) $sql.= " AND pfp.fk_soc = ".$socid;
diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php
index 4b3ab3b32f4..8cc14987aa5 100644
--- a/htdocs/core/lib/ajax.lib.php
+++ b/htdocs/core/lib/ajax.lib.php
@@ -136,7 +136,8 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
minLength: '.$minLength.',
select: function( event, ui ) { // Function ran once new value has been selected into javascript combo
console.log("Call change on input '.$htmlname.' because of select definition of autocomplete select call on input#search_'.$htmlname.'");
- $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value
+ console.log("Selected id = "+ui.item.id+" - If this value is null, it means you select a record with key that is null so selection is not effective");
+ $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value
// Disable an element
if (options.option_disabled) {
if (ui.item.disabled) {
@@ -179,7 +180,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
}
});
}
- console.log("ajax_autocompleter new value selected, we trigger change on original component");
+ console.log("ajax_autocompleter new value selected, we trigger change on original component so field #search_'.$htmlname.'");
$("#search_'.$htmlname.'").trigger("change"); // We have changed value of the combo select, we must be sure to trigger all js hook binded on this event. This is required to trigger other javascript change method binded on original field by other code.
}
,delay: 500
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 832107e7f6f..fffca45c77e 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
@@ -66,6 +66,8 @@ ALTER TABLE llx_product ADD COLUMN height_units tinyint DEFAULT NULL;
ALTER TABLE llx_product ADD COLUMN default_vat_code varchar(10) after cost_price;
+ALTER TABLE llx_product MODIFY COLUMN stock real;
+
CREATE TABLE llx_categorie_user
(
fk_categorie integer NOT NULL,
diff --git a/htdocs/install/mysql/tables/llx_product.sql b/htdocs/install/mysql/tables/llx_product.sql
index 9d3446d696d..8b43844498e 100755
--- a/htdocs/install/mysql/tables/llx_product.sql
+++ b/htdocs/install/mysql/tables/llx_product.sql
@@ -79,7 +79,7 @@ create table llx_product
surface_units tinyint DEFAULT NULL,
volume float DEFAULT NULL,
volume_units tinyint DEFAULT NULL,
- stock integer, -- Current physical stock (dernormalized field)
+ stock real, -- Current physical stock (dernormalized field)
pmp double(24,8) DEFAULT 0 NOT NULL, -- To store valuation of stock calculated using average price method, for this product
fifo double(24,8), -- To store valuation of stock calculated using fifo method, for this product
lifo double(24,8), -- To store valuation of stock calculated using lifo method, for this product
diff --git a/htdocs/install/mysql/tables/llx_stock_mouvement.sql b/htdocs/install/mysql/tables/llx_stock_mouvement.sql
index 3db9a4475cd..3ac42c0c852 100644
--- a/htdocs/install/mysql/tables/llx_stock_mouvement.sql
+++ b/htdocs/install/mysql/tables/llx_stock_mouvement.sql
@@ -24,8 +24,8 @@ create table llx_stock_mouvement
datem datetime, -- Date and hour of movement
fk_product integer NOT NULL, -- Id of product
batch varchar(30) DEFAULT NULL, -- Lot or serial number
- eatby date DEFAULT NULL, -- Eatby date
- sellby date DEFAULT NULL, -- Sellby date
+ eatby date DEFAULT NULL, -- Eatby date (deprecated, we should get value from batch number in table llx_product_lot)
+ sellby date DEFAULT NULL, -- Sellby date (deprecated, we should get value from batch number in table llx_product_lot)
fk_entrepot integer NOT NULL, -- Id warehouse
value real, -- Qty of movement
price double(24,8) DEFAULT 0, -- Entry price (used to calculate PMP, FIFO or LIFO value)
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index 685a29cb8e1..f93e5254ce0 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -1850,7 +1850,7 @@ class Product extends CommonObject
}
}
- // We should not load stock at each fetch. If someone need stock, he must call load_stock after fetch.
+ // We should not load stock during the fetch. If someone need stock of product, he must call load_stock after fetching product.
//$res=$this->load_stock();
// instead we just init the stock_warehouse array
$this->stock_warehouse = array();
@@ -2847,7 +2847,7 @@ class Product extends CommonObject
//print "XXX We add id=".$id." - label=".$label." - nb=".$nb." - multiply=".$multiply." fullpath=".$compl_path.$label."\n";
$this->fetch($id); // Load product
- $this->load_stock(); // Load stock
+ $this->load_stock('nobatch,novirtual'); // Load stock to get true this->stock_reel
$this->res[]= array(
'id'=>$id, // Id product
'id_parent'=>$id_parent,
diff --git a/htdocs/product/list.php b/htdocs/product/list.php
index 1df8e667569..1d63964be1c 100644
--- a/htdocs/product/list.php
+++ b/htdocs/product/list.php
@@ -103,6 +103,13 @@ if ($type=='0') $result=restrictedArea($user,'produit','','','','','',$objcanvas
else if ($type=='1') $result=restrictedArea($user,'service','','','','','',$objcanvas);
else $result=restrictedArea($user,'produit|service','','','','','',$objcanvas);
+// Define virtualdiffersfromphysical
+$virtualdiffersfromphysical=0;
+if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER))
+{
+ $virtualdiffersfromphysical=1; // According to increase/decrease stock options, virtual and physical stock may differs.
+}
+
// List of fields to search into when doing a "search in all"
$fieldstosearchall = array(
'p.ref'=>"Ref",
@@ -142,8 +149,9 @@ $arrayfields=array(
'p.minbuyprice'=>array('label'=>$langs->trans("BuyingPriceMinShort"), 'checked'=>1, 'enabled'=>(! empty($user->rights->fournisseur->lire))),
'p.seuil_stock_alerte'=>array('label'=>$langs->trans("StockLimit"), 'checked'=>0, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service')),
'p.desiredstock'=>array('label'=>$langs->trans("DesiredStock"), 'checked'=>1, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service')),
- 'p.tobatch'=>array('label'=>$langs->trans("ManageLotSerial"), 'checked'=>0, 'enabled'=>(! empty($conf->productbatch->enabled))),
'p.stock'=>array('label'=>$langs->trans("PhysicalStock"), 'checked'=>1, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service')),
+ 'stock_virtual'=>array('label'=>$langs->trans("VirtualStock"), 'checked'=>1, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service' && $virtualdiffersfromphysical)),
+ 'p.tobatch'=>array('label'=>$langs->trans("ManageLotSerial"), 'checked'=>0, 'enabled'=>(! empty($conf->productbatch->enabled))),
'p.accountancy_code_sell'=>array('label'=>$langs->trans("ProductAccountancySellCode"), 'checked'=>0),
'p.accountancy_code_buy'=>array('label'=>$langs->trans("ProductAccountancyBuyCode"), 'checked'=>0),
'p.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
@@ -160,6 +168,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
}
}
+
/*
* Actions
@@ -428,8 +437,9 @@ else
if (! empty($arrayfields['p.minbuyprice']['checked'])) print_liste_field_titre($arrayfields['p.minbuyprice']['label'], $_SERVER["PHP_SELF"],"","",$param,'align="right"',$sortfield,$sortorder);
if (! empty($arrayfields['p.seuil_stock_alerte']['checked'])) print_liste_field_titre($arrayfields['p.seuil_stock_alerte']['label'], $_SERVER["PHP_SELF"],"p.seuil_stock_alerte","",$param,'align="right"',$sortfield,$sortorder);
if (! empty($arrayfields['p.desiredstock']['checked'])) print_liste_field_titre($arrayfields['p.desiredstock']['label'], $_SERVER["PHP_SELF"],"p.desiredstock","",$param,'align="right"',$sortfield,$sortorder);
- if (! empty($arrayfields['p.tobatch']['checked'])) print_liste_field_titre($arrayfields['p.tobatch']['label'], $_SERVER["PHP_SELF"],"p.tobatch","",$param,'align="center"',$sortfield,$sortorder);
if (! empty($arrayfields['p.stock']['checked'])) print_liste_field_titre($arrayfields['p.stock']['label'], $_SERVER["PHP_SELF"],"p.stock","",$param,'align="right"',$sortfield,$sortorder);
+ if (! empty($arrayfields['stock_virtual']['checked'])) print_liste_field_titre($arrayfields['stock_virtual']['label'], $_SERVER["PHP_SELF"],"","",$param,'align="right"',$sortfield,$sortorder);
+ if (! empty($arrayfields['p.tobatch']['checked'])) print_liste_field_titre($arrayfields['p.tobatch']['label'], $_SERVER["PHP_SELF"],"p.tobatch","",$param,'align="center"',$sortfield,$sortorder);
if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print_liste_field_titre($arrayfields['p.accountancy_code_sell']['label'], $_SERVER["PHP_SELF"],"p.accountancy_code_sell","",$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['p.accountancy_code_buy']['checked'])) print_liste_field_titre($arrayfields['p.accountancy_code_buy']['label'], $_SERVER["PHP_SELF"],"p.accountancy_code_buy","",$param,'',$sortfield,$sortorder);
if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
@@ -515,11 +525,13 @@ else
print ' ';
print '';
}
- // To batch
- if (! empty($arrayfields['p.tobatch']['checked'])) print '