FIX: Qty in stock must be float like it is in stock movement.
FIX: Can't create a stock transfer from product card Performance enhancement of stock information loading.
This commit is contained in:
parent
aa31a7e9c5
commit
e723966583
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -142,8 +142,8 @@ $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')),
|
||||
'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),
|
||||
@ -428,8 +428,8 @@ 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['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 +515,11 @@ else
|
||||
print ' ';
|
||||
print '</td>';
|
||||
}
|
||||
// To batch
|
||||
if (! empty($arrayfields['p.tobatch']['checked'])) print '<td class="liste_titre center">'.$form->selectyesno($search_tobatch, '', '', '', 1).'</td>';
|
||||
// Stock
|
||||
if (! empty($arrayfields['p.stock']['checked'])) print '<td class="liste_titre"> </td>';
|
||||
// Accountancy code sell
|
||||
// To batch
|
||||
if (! empty($arrayfields['p.tobatch']['checked'])) print '<td class="liste_titre center">'.$form->selectyesno($search_tobatch, '', '', '', 1).'</td>';
|
||||
// Accountancy code sell
|
||||
if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print '<td class="liste_titre"><input class="flat" type="text" name="search_accountancy_code_sell" size="6" value="'.dol_escape_htmltag($search_accountancy_code_sell).'"></td>';
|
||||
// Accountancy code sell
|
||||
if (! empty($arrayfields['p.accountancy_code_buy']['checked'])) print '<td class="liste_titre"><input class="flat" type="text" name="search_accountancy_code_buy" size="6" value="'.dol_escape_htmltag($search_accountancy_code_buy).'"></td>';
|
||||
@ -600,7 +600,16 @@ else
|
||||
$product_static->status_buy = $objp->tobuy;
|
||||
$product_static->status = $objp->tosell;
|
||||
$product_static->entity = $objp->entity;
|
||||
|
||||
|
||||
if (! empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1) // To optimize call of load_stock
|
||||
{
|
||||
if ($objp->fk_product_type != 1) // Not a service
|
||||
{
|
||||
$product_static->load_stock('nobatch'); // Load stock_reel + stock_warehouse. This also call load_virtual_stock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$var=!$var;
|
||||
print '<tr '.$bc[$var].'>';
|
||||
|
||||
@ -681,15 +690,6 @@ else
|
||||
print '</td>';
|
||||
}
|
||||
|
||||
if (! empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1) // To optimize call of load_stock
|
||||
{
|
||||
if ($objp->fk_product_type != 1) // Not a service
|
||||
{
|
||||
$product_static->id = $objp->rowid;
|
||||
$product_static->load_stock('nobatch'); // Load stock_reel + stock_warehouse + batch detail. This also call load_virtual_stock()
|
||||
}
|
||||
}
|
||||
|
||||
// Limit alert
|
||||
if (! empty($arrayfields['p.seuil_stock_alerte']['checked']))
|
||||
{
|
||||
@ -710,13 +710,6 @@ else
|
||||
}
|
||||
print '</td>';
|
||||
}
|
||||
// Lot/Serial
|
||||
if (! empty($arrayfields['p.tobatch']['checked']))
|
||||
{
|
||||
print '<td align="center">';
|
||||
print yn($objp->tobatch);
|
||||
print '</td>';
|
||||
}
|
||||
// Stock
|
||||
if (! empty($arrayfields['p.stock']['checked']))
|
||||
{
|
||||
@ -728,6 +721,13 @@ else
|
||||
}
|
||||
print '</td>';
|
||||
}
|
||||
// Lot/Serial
|
||||
if (! empty($arrayfields['p.tobatch']['checked']))
|
||||
{
|
||||
print '<td align="center">';
|
||||
print yn($objp->tobatch);
|
||||
print '</td>';
|
||||
}
|
||||
// Accountancy code sell
|
||||
if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print '<td>'.$objp->accountancy_code_sell.'</td>';
|
||||
// Accountancy code sell
|
||||
|
||||
@ -252,25 +252,12 @@ class MouvementStock extends CommonObject
|
||||
|
||||
// Calculate new PMP.
|
||||
$newpmp=0;
|
||||
//$newpmpwarehouse=0;
|
||||
if (! $error)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// If we will change PMP for the warehouse we edit and the product, we must first check/clean that PMP is defined
|
||||
// on every stock entry with old value (so global updated value will match recalculated value from product_stock)
|
||||
/* $sql = "UPDATE ".MAIN_DB_PREFIX."product_stock SET pmp = ".($oldpmp?$oldpmp:'0');
|
||||
$sql.= " WHERE pmp = 0 AND fk_product = ".$fk_product;
|
||||
dol_syslog(get_class($this)."::_create", LOG_DEBUG);
|
||||
$resql=$this->db->query($sql);
|
||||
if (! $resql)
|
||||
{
|
||||
$this->errors[]=$this->db->lasterror();
|
||||
$error = -4;
|
||||
}
|
||||
*/
|
||||
$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');
|
||||
@ -278,13 +265,8 @@ class MouvementStock extends CommonObject
|
||||
{
|
||||
$newpmp=$price; // For this product, PMP was not yet set. We set it to input price.
|
||||
}
|
||||
/*
|
||||
$oldqtywarehousetouse=$oldqtywarehouse;
|
||||
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;
|
||||
//print "oldqtytouse=".$oldqtytouse." oldpmp=".$oldpmp." oldqtywarehousetouse=".$oldqtywarehousetouse." ";
|
||||
//print "qty=".$qty." newpmp=".$newpmp;
|
||||
//exit;
|
||||
}
|
||||
else if ($type == 1 || $type == 2)
|
||||
@ -295,7 +277,6 @@ class MouvementStock extends CommonObject
|
||||
else
|
||||
{
|
||||
$newpmp = $oldpmp;
|
||||
//$newpmpwarehouse = $oldpmpwarehouse;
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,11 +320,13 @@ class MouvementStock extends CommonObject
|
||||
// Update PMP and denormalized value of stock qty at product level
|
||||
if (! $error)
|
||||
{
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."product SET pmp = ".$newpmp.", stock = ".$this->db->ifsql("stock IS NULL", 0, "stock") . " + ".$qty;
|
||||
//$sql = "UPDATE ".MAIN_DB_PREFIX."product SET pmp = ".$newpmp.", stock = ".$this->db->ifsql("stock IS NULL", 0, "stock") . " + ".$qty;
|
||||
//$sql.= " WHERE rowid = ".$fk_product;
|
||||
// Update pmp + denormalized fields because we change content of produt_stock. Warning: Do not use "SET p.stock", does not works with pgsql
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."product as p SET p.pmp = ".$newpmp.", ";
|
||||
$sql.= " stock=(SELECT SUM(ps.reel) FROM llx_product_stock ps WHERE ps.fk_product = p.rowid)";
|
||||
$sql.= " WHERE rowid = ".$fk_product;
|
||||
// May be this request is better:
|
||||
// UPDATE llx_product p SET p.stock= (SELECT SUM(ps.reel) FROM llx_product_stock ps WHERE ps.fk_product = p.rowid);
|
||||
|
||||
print $sql;
|
||||
dol_syslog(get_class($this)."::_create", LOG_DEBUG);
|
||||
$resql=$this->db->query($sql);
|
||||
if (! $resql)
|
||||
|
||||
@ -185,7 +185,7 @@ if ($action == 'createmovements')
|
||||
{
|
||||
$result=$product->fetch($id_product);
|
||||
|
||||
$product->load_stock(); // Load array product->stock_warehouse
|
||||
$product->load_stock('novirtual'); // Load array product->stock_warehouse
|
||||
|
||||
// Define value of products moved
|
||||
$pricesrc=0;
|
||||
|
||||
@ -207,11 +207,10 @@ if ($action == "transfert_stock" && ! $cancel)
|
||||
|
||||
$db->begin();
|
||||
|
||||
$product->load_stock(); // Load array product->stock_warehouse
|
||||
$product->load_stock('novirtual'); // Load array product->stock_warehouse
|
||||
|
||||
// Define value of products moved
|
||||
$pricesrc=0;
|
||||
//if (isset($product->stock_warehouse[GETPOST("id_entrepot_source")]->pmp)) $pricesrc=$product->stock_warehouse[GETPOST("id_entrepot_source")]->pmp;
|
||||
if (isset($product->pmp)) $pricesrc=$product->pmp;
|
||||
$pricedest=$pricesrc;
|
||||
|
||||
|
||||
@ -185,7 +185,7 @@ if ($action == "correct_stock" && ! $cancel)
|
||||
// Transfer stock from a warehouse to another warehouse
|
||||
if ($action == "transfert_stock" && ! $cancel)
|
||||
{
|
||||
if (! (GETPOST("id_entrepot_source",'int') > 0) || ! (GETPOST("id_entrepot_destination",'int') > 0))
|
||||
if (! (GETPOST("id_entrepot",'int') > 0) || ! (GETPOST("id_entrepot_destination",'int') > 0))
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
|
||||
$error++;
|
||||
@ -197,7 +197,7 @@ if ($action == "transfert_stock" && ! $cancel)
|
||||
$error++;
|
||||
$action='transfert';
|
||||
}
|
||||
if (GETPOST("id_entrepot_source",'int') == GETPOST("id_entrepot_destination",'int'))
|
||||
if (GETPOST("id_entrepot",'int') == GETPOST("id_entrepot_destination",'int'))
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorSrcAndTargetWarehouseMustDiffers"), null, 'errors');
|
||||
$error++;
|
||||
@ -225,7 +225,7 @@ if ($action == "transfert_stock" && ! $cancel)
|
||||
|
||||
$db->begin();
|
||||
|
||||
$object->load_stock(); // Load array product->stock_warehouse
|
||||
$object->load_stock('novirtual'); // Load array product->stock_warehouse
|
||||
|
||||
// Define value of products moved
|
||||
$pricesrc=0;
|
||||
@ -254,7 +254,7 @@ if ($action == "transfert_stock" && ! $cancel)
|
||||
}
|
||||
else
|
||||
{
|
||||
$srcwarehouseid=GETPOST('id_entrepot_source','int');
|
||||
$srcwarehouseid=GETPOST('id_entrepot','int');
|
||||
$batch=GETPOST('batch_number');
|
||||
$eatby=$d_eatby;
|
||||
$sellby=$d_sellby;
|
||||
@ -291,7 +291,7 @@ if ($action == "transfert_stock" && ! $cancel)
|
||||
// Remove stock
|
||||
$result1=$object->correct_stock(
|
||||
$user,
|
||||
GETPOST("id_entrepot_source"),
|
||||
GETPOST("id_entrepot"),
|
||||
GETPOST("nbpiece"),
|
||||
1,
|
||||
GETPOST("label"),
|
||||
@ -390,6 +390,7 @@ if ($id > 0 || $ref)
|
||||
{
|
||||
$object = new Product($db);
|
||||
$result = $object->fetch($id,$ref);
|
||||
|
||||
$object->load_stock();
|
||||
|
||||
$help_url='EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks';
|
||||
@ -480,7 +481,6 @@ if ($id > 0 || $ref)
|
||||
print '</td></tr>';
|
||||
|
||||
// Real stock
|
||||
$object->load_stock();
|
||||
$text_stock_options = '';
|
||||
$text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)?$langs->trans("DeStockOnShipment").'<br>':'');
|
||||
$text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER)?$langs->trans("DeStockOnValidateOrder").'<br>':'');
|
||||
@ -702,9 +702,10 @@ if ($resql)
|
||||
$entrepotstatic->id=$obj->rowid;
|
||||
$entrepotstatic->libelle=$obj->label;
|
||||
$entrepotstatic->lieu=$obj->lieu;
|
||||
$stock_real = round($obj->reel, 10);
|
||||
print '<tr '.$bc[$var].'>';
|
||||
print '<td colspan="4">'.$entrepotstatic->getNomUrl(1).'</td>';
|
||||
print '<td align="right">'.$obj->reel.($obj->reel<0?' '.img_warning():'').'</td>';
|
||||
print '<td align="right">'.$stock_real.($stock_real < 0 ?' '.img_warning():'').'</td>';
|
||||
// PMP
|
||||
print '<td align="right">'.(price2num($object->pmp)?price2num($object->pmp,'MU'):'').'</td>';
|
||||
// Value purchase
|
||||
|
||||
@ -411,7 +411,8 @@ function getProductOrService($authentication,$id='',$ref='',$ref_ext='',$lang=''
|
||||
'localtax1_tx' => $product->localtax1_tx,
|
||||
'localtax2_tx' => $product->localtax2_tx,
|
||||
|
||||
'stock_real' => $product->stock_reel,
|
||||
'stock_real' => $product->stock_reel,
|
||||
'stock_virtual' => $product->stock_theorique,
|
||||
'stock_alert' => $product->seuil_stock_alerte,
|
||||
'pmp' => $product->pmp,
|
||||
'import_key' => $product->import_key,
|
||||
@ -572,7 +573,7 @@ function createProductOrService($authentication,$product)
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
|
||||
|
||||
$savstockreal=$newobject->stock_reel;
|
||||
$newobject->load_stock(); // This overwrite ->stock_reel
|
||||
$newobject->load_stock('novirtual,nobatch'); // This overwrite ->stock_reel, surely 0 because we have just created product
|
||||
$getstockreal = $newobject->stock_reel;
|
||||
|
||||
if ($savstockreal != $getstockreal)
|
||||
@ -741,7 +742,7 @@ function updateProductOrService($authentication,$product)
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
|
||||
|
||||
$savstockreal=$newobject->stock_reel;
|
||||
$newobject->load_stock(); // This overwrite ->stock_reel
|
||||
$newobject->load_stock('novirtual,nobatch'); // This overwrite ->stock_reel
|
||||
$getstockreal = $newobject->stock_reel;
|
||||
|
||||
if ($savstockreal != $getstockreal)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user