Solve blocking feature. Can increase stock of a Kit without changing

subproduct stock.
This commit is contained in:
Laurent Destailleur 2020-12-15 15:18:01 +01:00
parent 1f6f4742d5
commit 37fe5bb3ef
9 changed files with 109 additions and 71 deletions

View File

@ -29,6 +29,7 @@ AvailableVariables=Available substitution variables
NoTranslation=No translation
Translation=Translation
EmptySearchString=Enter non empty search criterias
EnterADateCriteria=Enter a date criteria
NoRecordFound=No record found
NoRecordDeleted=No record deleted
NotEnoughDataYet=Not enough data

View File

@ -240,3 +240,4 @@ InventoryRealQtyHelp=Set value to 0 to reset qty<br>Keep field empty, or remove
UpdateByScaning=Update by scaning
UpdateByScaningProductBarcode=Update by scan (product barcode)
UpdateByScaningLot=Update by scan (lot|serial barcode)
DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement.

View File

@ -4229,9 +4229,9 @@ class Product extends CommonObject
// phpcs:enable
$this->res = array();
if (isset($this->sousprods) && is_array($this->sousprods)) {
foreach ($this->sousprods as $prod_name => $desc_product)
{
if (is_array($desc_product)) { $this->fetch_prod_arbo($desc_product, "", $multiply, 1, $this->id);
foreach ($this->sousprods as $prod_name => $desc_product) {
if (is_array($desc_product)) {
$this->fetch_prod_arbo($desc_product, "", $multiply, 1, $this->id);
}
}
}
@ -4777,9 +4777,10 @@ class Product extends CommonObject
* @param string $inventorycode Inventory code
* @param string $origin_element Origin element type
* @param int $origin_id Origin id of element
* @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct)
* @return int <0 if KO, >0 if OK
*/
public function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $inventorycode = '', $origin_element = '', $origin_id = null)
public function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $inventorycode = '', $origin_element = '', $origin_id = null, $disablestockchangeforsubproduct = 0)
{
// phpcs:enable
if ($id_entrepot) {
@ -4792,7 +4793,7 @@ class Product extends CommonObject
$movementstock = new MouvementStock($this->db);
$movementstock->setOrigin($origin_element, $origin_id); // Set ->origin and ->origin->id
$result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode);
$result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', '', '', '', false, 0, $disablestockchangeforsubproduct);
if ($result >= 0) {
$this->db->commit();
@ -4823,9 +4824,10 @@ class Product extends CommonObject
* @param string $inventorycode Inventory code
* @param string $origin_element Origin element type
* @param int $origin_id Origin id of element
* @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct)
* @return int <0 if KO, >0 if OK
*/
public function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $dlc = '', $dluo = '', $lot = '', $inventorycode = '', $origin_element = '', $origin_id = null)
public function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $dlc = '', $dluo = '', $lot = '', $inventorycode = '', $origin_element = '', $origin_id = null, $disablestockchangeforsubproduct = 0)
{
// phpcs:enable
if ($id_entrepot) {
@ -4838,7 +4840,7 @@ class Product extends CommonObject
$movementstock = new MouvementStock($this->db);
$movementstock->setOrigin($origin_element, $origin_id);
$result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', $dlc, $dluo, $lot);
$result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', $dlc, $dluo, $lot, false, 0, $disablestockchangeforsubproduct);
if ($result >= 0) {
$this->db->commit();

View File

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2004-2020 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005 Eric Seigne <eric.seigne@ryxeo.com>
* Copyright (C) 2005-2018 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
@ -137,7 +137,7 @@ if ($action == 'search')
$current_lang = $langs->getDefaultLang();
$sql = 'SELECT DISTINCT p.rowid, p.ref, p.label, p.fk_product_type as type, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
$sql .= ' p.fk_product_type, p.tms as datem';
$sql .= ' p.fk_product_type, p.tms as datem, p.tobatch';
if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= ', pl.label as labelm, pl.description as descriptionm';
$sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON p.rowid = cp.fk_product';
@ -204,7 +204,7 @@ if ($id > 0 || !empty($ref))
dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref', '', '', '', 0, '', '', 0);
if ($object->type != Product::TYPE_SERVICE || empty($conf->global->PRODUIT_MULTIPRICES))
if ($object->type != Product::TYPE_SERVICE || !empty($conf->global->STOCK_SUPPORTS_SERVICES) || empty($conf->global->PRODUIT_MULTIPRICES))
{
print '<div class="fichecenter">';
print '<div class="underbanner clearboth"></div>';
@ -320,14 +320,11 @@ if ($id > 0 || !empty($ref))
print '</tr>'."\n";
$totalsell = 0;
if (count($prods_arbo))
{
foreach ($prods_arbo as $value)
{
if (count($prods_arbo)) {
foreach ($prods_arbo as $value) {
$productstatic->fetch($value['id']);
if ($value['level'] <= 1)
{
if ($value['level'] <= 1) {
print '<tr class="oddeven">';
$notdefined = 0;
@ -380,7 +377,7 @@ if ($id > 0 || !empty($ref))
// Qty + IncDec
if ($user->rights->produit->creer || $user->rights->service->creer)
{
print '<td class="center"><input type="text" value="'.$nb_of_subproduct.'" name="TProduct['.$productstatic->id.'][qty]" size="4" /></td>';
print '<td class="center"><input type="text" value="'.$nb_of_subproduct.'" name="TProduct['.$productstatic->id.'][qty]" size="4" class="right" /></td>';
print '<td class="center"><input type="checkbox" name="TProduct['.$productstatic->id.'][incdec]" value="1" '.($value['incdec'] == 1 ? 'checked' : '').' /></td>';
} else {
print '<td>'.$nb_of_subproduct.'</td>';
@ -566,6 +563,7 @@ if ($id > 0 || !empty($ref))
$productstatic->label = $objp->label;
$productstatic->type = $objp->type;
$productstatic->entity = $objp->entity;
$productstatic->status_batch = $objp->tobatch;
print '<td>'.$productstatic->getNomUrl(1, '', 24).'</td>';
$labeltoshow = $objp->label;

View File

@ -128,26 +128,27 @@ class MouvementStock extends CommonObject
* Add a movement of stock (in one direction only).
* $this->origin can be also be set to save the source object of movement.
*
* @param User $user User object
* @param int $fk_product Id of product
* @param int $entrepot_id Id of warehouse
* @param int $qty Qty of movement (can be <0 or >0 depending on parameter type)
* @param int $type Direction of movement:
* 0=input (stock increase by a stock transfer), 1=output (stock decrease by a stock transfer),
* 2=output (stock decrease), 3=input (stock increase)
* Note that qty should be > 0 with 0 or 3, < 0 with 1 or 2.
* @param int $price Unit price HT of product, used to calculate average weighted price (AWP or 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 integer $eatby eat-by date. Will be used if lot does not exists yet and will be created.
* @param integer $sellby sell-by date. Will be used if lot does not exists yet and will be created.
* @param string $batch batch number
* @param boolean $skip_batch If set to true, stock movement is done without impacting batch record
* @param User $user User object
* @param int $fk_product Id of product
* @param int $entrepot_id Id of warehouse
* @param int $qty Qty of movement (can be <0 or >0 depending on parameter type)
* @param int $type Direction of movement:
* 0=input (stock increase by a stock transfer), 1=output (stock decrease by a stock transfer),
* 2=output (stock decrease), 3=input (stock increase)
* Note that qty should be > 0 with 0 or 3, < 0 with 1 or 2.
* @param int $price Unit price HT of product, used to calculate average weighted price (AWP or 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 integer $eatby eat-by date. Will be used if lot does not exists yet and will be created.
* @param integer $sellby sell-by date. Will be used if lot does not exists yet and will be created.
* @param string $batch batch number
* @param boolean $skip_batch If set to true, stock movement is done without impacting batch record
* @param int $id_product_batch Id product_batch (when skip_batch is false and we already know which record of product_batch to use)
* @return int <0 if KO, 0 if fk_product is null or product id does not exists, >0 if OK
* @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct)
* @return int <0 if KO, 0 if fk_product is null or product id does not exists, >0 if OK
*/
public function _create($user, $fk_product, $entrepot_id, $qty, $type, $price = 0, $label = '', $inventorycode = '', $datem = '', $eatby = '', $sellby = '', $batch = '', $skip_batch = false, $id_product_batch = 0)
public function _create($user, $fk_product, $entrepot_id, $qty, $type, $price = 0, $label = '', $inventorycode = '', $datem = '', $eatby = '', $sellby = '', $batch = '', $skip_batch = false, $id_product_batch = 0, $disablestockchangeforsubproduct = 0)
{
// phpcs:disable
global $conf, $langs;
@ -571,9 +572,9 @@ class MouvementStock extends CommonObject
}
// Add movement for sub products (recursive call)
if (!$error && !empty($conf->global->PRODUIT_SOUSPRODUITS) && empty($conf->global->INDEPENDANT_SUBPRODUCT_STOCK))
if (!$error && !empty($conf->global->PRODUIT_SOUSPRODUITS) && empty($conf->global->INDEPENDANT_SUBPRODUCT_STOCK) && empty($disablestockchangeforsubproduct))
{
$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
$error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label, $inventorycode); // we use 0 as price, because AWP must not change for subproduct
}
if ($movestock && !$error)

View File

@ -108,6 +108,8 @@ if (!empty($canvas))
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager->initHooks(array('stockproductcard', 'globalcard'));
$error = 0;
/*
* Actions
@ -221,17 +223,14 @@ if ($action == "correct_stock" && !$cancel)
}
}
if (!$error)
{
if (!$error) {
$priceunit = price2num(GETPOST("unitprice"));
$nbpiece = price2num(GETPOST("nbpiece", 'alphanohtml'));
if (is_numeric($nbpiece) && $nbpiece != 0 && $id)
{
if (is_numeric($nbpiece) && $nbpiece != 0 && $id) {
$origin_element = '';
$origin_id = null;
if (GETPOST('projectid', 'int'))
{
if (GETPOST('projectid', 'int')) {
$origin_element = 'project';
$origin_id = GETPOST('projectid', 'int');
}
@ -240,8 +239,13 @@ if ($action == "correct_stock" && !$cancel)
$object = new Product($db);
$result = $object->fetch($id);
}
if ($object->hasbatch())
{
$disablestockchangeforsubproduct = 0;
if (GETPOST('disablesubproductstockchange')) {
$disablestockchangeforsubproduct = 1;
}
if ($object->hasbatch()) {
$result = $object->correct_stock_batch(
$user,
GETPOST("id_entrepot", 'int'),
@ -254,7 +258,8 @@ if ($action == "correct_stock" && !$cancel)
$batchnumber,
GETPOST('inventorycode', 'alphanohtml'),
$origin_element,
$origin_id
$origin_id,
$disablestockchangeforsubproduct
); // We do not change value of stock for a correction
} else {
$result = $object->correct_stock(
@ -266,14 +271,13 @@ if ($action == "correct_stock" && !$cancel)
$priceunit,
GETPOST('inventorycode', 'alphanohtml'),
$origin_element,
$origin_id
$origin_id,
$disablestockchangeforsubproduct
); // We do not change value of stock for a correction
}
if ($result > 0)
{
if ($backtopage)
{
if ($result > 0) {
if ($backtopage) {
header("Location: ".$backtopage);
exit;
} else {
@ -535,12 +539,14 @@ if ($id > 0 || $ref)
dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
print '<div class="fichecenter">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield" width="100%">';
if (!$variants) {
print '<div class="fichecenter">';
print '<div class="fichehalfleft">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield centpercent">';
if ($conf->productbatch->enabled) {
print '<tr><td class="titlefield">'.$langs->trans("ManageLotSerial").'</td><td>';
print $object->getLibStatut(0, 2);
@ -604,16 +610,23 @@ if ($id > 0 || $ref)
print '</td></tr>';
}
// Stock alert threshold
print '<tr><td>'.$form->editfieldkey($form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1), 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer).'</td><td>';
print $form->editfieldval("StockLimit", 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer, 'string');
print '</td></tr>';
// Hook formObject
$parameters = array();
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
print '</table>';
print '</div>';
print '<div class="fichehalfright"><div class="ficheaddleft"><div class="underbanner clearboth"></div>';
print '<table class="border tableforfield centpercent">';
// Stock alert threshold
print '<tr><td>'.$form->editfieldkey($form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1), 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer).'</td><td>';
print $form->editfieldval("StockLimit", 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer, 'string');
print '</td></tr>';
// Desired stock
print '<tr><td>'.$form->editfieldkey($form->textwithpicto($langs->trans("DesiredStock"), $langs->trans("DesiredStockDesc"), 1), 'desiredstock', $object->desiredstock, $object, $user->rights->produit->creer);
print '</td><td>';
@ -727,11 +740,15 @@ if ($id > 0 || $ref)
}
print "</td></tr>";
}
}
print "</table>";
print '</div>';
print '<div style="clear:both"></div>';
print "</table>";
print '</div>';
print '</div>';
print '</div>';
print '<div style="clear:both"></div>';
}
print dol_get_fiche_end();
}

View File

@ -347,10 +347,14 @@ print '<input type="hidden" name="mode" value="'.$mode.'">';
print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
print '<span class="fieldrequired">'.$langs->trans('Date').'</span> '.$form->selectDate(($date ? $date : -1), 'date');
print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddinrightonly">&nbsp;</span> '.$langs->trans('Product').'</span> ';
print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddinrightonly">&nbsp;</span> ';
print img_picto('', 'product').' ';
print $langs->trans('Product').'</span> ';
$form->select_produits($productid, 'productid', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'maxwidth300');
print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddinrightonly">&nbsp;</span> '.$langs->trans('Warehouse').'</span> ';
print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddinrightonly">&nbsp;</span> ';
print img_picto('', 'stock').' ';
print $langs->trans('Warehouse').'</span> ';
print $formproduct->selectWarehouses((GETPOSTISSET('fk_warehouse') ? $fk_warehouse : 'ifone'), 'fk_warehouse', '', 1);
print '</div>';
@ -558,7 +562,7 @@ print $hookmanager->resPrint;
if (empty($date) || ! $dateIsValid) {
$colspan = 6;
if ($mode == 'future') $colspan++;
print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EmptySearchString").'</span></td></tr>';
print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
}
print '</table>';

View File

@ -70,7 +70,7 @@ if ($object->element == 'product') {
print '<td>';
$ident = (GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ? GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone')));
if (empty($ident) && !empty($conf->global->MAIN_DEFAULT_WAREHOUSE)) $ident = $conf->global->MAIN_DEFAULT_WAREHOUSE;
print $formproduct->selectWarehouses($ident, 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100');
print img_picto('', 'stock').$formproduct->selectWarehouses($ident, 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100');
print ' &nbsp; <select class="button buttongen" name="mouvement" id="mouvement">';
print '<option value="0">'.$langs->trans("Add").'</option>';
print '<option value="1"'.(GETPOST('mouvement') ? ' selected="selected"' : '').'>'.$langs->trans("Delete").'</option>';
@ -80,6 +80,7 @@ if ($object->element == 'product') {
if ($object->element == 'stock') {
print '<td class="fieldrequired">'.$langs->trans("Product").'</td>';
print '<td>';
print img_picto('', 'product');
$form->select_produits(GETPOST('product_id', 'int'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES) ? '0' : ''), 0, 0, -1, 2, '', 0, null, 0, 1, 0, 'maxwidth500');
print ' &nbsp; <select class="button buttongen" name="mouvement" id="mouvement">';
print '<option value="0">'.$langs->trans("Add").'</option>';
@ -91,6 +92,17 @@ print '<td class="fieldrequired">'.$langs->trans("NumberOfUnit").'</td>';
print '<td><input name="nbpiece" id="nbpiece" class="maxwidth75" value="'.GETPOST("nbpiece").'"></td>';
print '</tr>';
// If product is a Kit, we ask if we must disable stock change of subproducts
if (!empty($conf->global->PRODUIT_SOUSPRODUITS) && $object->element == 'product' && $object->hasFatherOrChild(1)) {
print '<tr>';
print '<td></td>';
print '<td colspan="3">';
print '<input type="checkbox" name="disablesubproductstockchange" id="disablesubproductstockchange" value="1"'.(GETPOST('disablesubproductstockchange') ? ' checked="checked"' : '').'">';
print ' <label for="disablesubproductstockchange">'.$langs->trans("DisableStockChangeOfSubProduct").'</label>';
print '</td>';
print '</tr>';
}
// Serial / Eat-by date
if (!empty($conf->productbatch->enabled) &&
(($object->element == 'product' && $object->hasbatch())
@ -126,6 +138,7 @@ if (!empty($conf->projet->enabled))
{
print '<td>'.$langs->trans('Project').'</td>';
print '<td>';
print img_picto('', 'project');
$formproject->select_projects(-1, '', 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 0, 0, 'maxwidth300');
print '</td>';
}

View File

@ -71,18 +71,19 @@ print '<tr>';
if ($object->element == 'product') {
print '<td class="fieldrequired">'.$langs->trans("WarehouseSource").'</td>';
print '<td>';
print $formproduct->selectWarehouses((GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ?GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1);
print img_picto('', 'stock').$formproduct->selectWarehouses((GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ?GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1);
print '</td>';
}
if ($object->element == 'stock') {
print '<td class="fieldrequired">'.$langs->trans("Product").'</td>';
print '<td>';
print img_picto('', 'product');
$form->select_produits(GETPOST('product_id', 'int'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES) ? '0' : ''), 0, 0, -1, 2, '', 0, null, 0, 1, 0, 'maxwidth500');
print '</td>';
}
print '<td class="fieldrequired">'.$langs->trans("WarehouseTarget").'</td><td>';
print $formproduct->selectWarehouses(GETPOST('id_entrepot_destination'), 'id_entrepot_destination', 'warehouseopen,warehouseinternal', 1);
print img_picto('', 'stock').$formproduct->selectWarehouses(GETPOST('id_entrepot_destination'), 'id_entrepot_destination', 'warehouseopen,warehouseinternal', 1);
print '</td></tr>';
print '<tr><td class="fieldrequired">'.$langs->trans("NumberOfUnit").'</td><td colspan="3"><input type="text" name="nbpiece" size="10" value="'.dol_escape_htmltag(GETPOST("nbpiece")).'"></td>';
print '</tr>';