NEW Can use desired stock of a given warehouse for replenishment

This commit is contained in:
Laurent Destailleur 2020-09-23 23:02:31 +02:00
parent 0cced26e46
commit 95cf3d2f51
4 changed files with 97 additions and 61 deletions

View File

@ -4104,15 +4104,14 @@ function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin
//var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1); //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
// If field is used as sort criteria we use a specific css class liste_titre_sel // If field is used as sort criteria we use a specific css class liste_titre_sel
// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom") // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
$liste_titre = 'liste_titre';
if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) { if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
$out .= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '.$moreattrib; $liste_titre = 'liste_titre_sel';
$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
$out .= '>';
} else {
$out .= '<'.$tag.' class="'.$prefix.'liste_titre" '.$moreattrib;
$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
$out .= '>';
} }
$out .= '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
//$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
$out .= '>';
if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field
{ {

View File

@ -211,6 +211,7 @@ class MouvementStock extends CommonObject
{ {
if (empty($batch)) if (empty($batch))
{ {
$langs->load("errors");
$this->errors[] = $langs->transnoentitiesnoconv("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->ref); $this->errors[] = $langs->transnoentitiesnoconv("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->ref);
dol_syslog("Try to make a movement of a product with status_batch on without any batch data"); dol_syslog("Try to make a movement of a product with status_batch on without any batch data");

View File

@ -195,7 +195,7 @@ if ($action == 'setdesiredstock' && !empty($user->rights->produit->creer))
// Correct stock // Correct stock
if ($action == "correct_stock" && !$cancel) if ($action == "correct_stock" && !$cancel)
{ {
if (!(GETPOST("id_entrepot") > 0)) if (!(GETPOST("id_entrepot", 'int') > 0))
{ {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors'); setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
$error++; $error++;

View File

@ -312,6 +312,9 @@ if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entre
$sql .= ' '.$sqldesiredtock.' as desiredstockcombined, '.$sqlalertstock.' as seuil_stock_alertecombined,'; $sql .= ' '.$sqldesiredtock.' as desiredstockcombined, '.$sqlalertstock.' as seuil_stock_alertecombined,';
$sql .= ' s.fk_product,'; $sql .= ' s.fk_product,';
$sql .= ' SUM('.$db->ifsql("s.reel IS NULL", "0", "s.reel").') as stock_physique'; $sql .= ' SUM('.$db->ifsql("s.reel IS NULL", "0", "s.reel").') as stock_physique';
if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
$sql .= ', SUM('.$db->ifsql("s.reel IS NULL OR s.fk_entrepot <> ".$fk_entrepot, "0", "s.reel").') as stock_real_warehouse';
}
// Add fields from hooks // Add fields from hooks
$parameters = array(); $parameters = array();
@ -530,18 +533,20 @@ if (empty($fk_warhouse) && !empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_
if ($usevirtualstock == 1) if ($usevirtualstock == 1)
{ {
print $langs->trans("CurentSelectionMode").': '; print $langs->trans("CurentSelectionMode").': ';
print $langs->trans("CurentlyUsingVirtualStock").' - '; print $langs->trans("CurentlyUsingVirtualStock");
print '<a href="'.$_SERVER["PHP_SELF"].'?mode=physical&fk_supplier='.$fk_supplier.'&fk_entrepot='.$fk_entrepot.'">'.$langs->trans("UsePhysicalStock").'</a><br>'; print ' (<a href="'.$_SERVER["PHP_SELF"].'?mode=physical&fk_supplier='.$fk_supplier.'&fk_entrepot='.$fk_entrepot.'">'.$langs->trans("UsePhysicalStock").'</a>)';
print '<br>';
} }
if ($usevirtualstock == 0) if ($usevirtualstock == 0)
{ {
print $langs->trans("CurentSelectionMode").': '; print $langs->trans("CurentSelectionMode").': ';
print $langs->trans("CurentlyUsingPhysicalStock").' - '; print $langs->trans("CurentlyUsingPhysicalStock");
print '<a href="'.$_SERVER["PHP_SELF"].'?mode=virtual&fk_supplier='.$fk_supplier.'&fk_entrepot='.$fk_entrepot.'">'.$langs->trans("UseVirtualStock").'</a><br>'; print ' (<a href="'.$_SERVER["PHP_SELF"].'?mode=virtual&fk_supplier='.$fk_supplier.'&fk_entrepot='.$fk_entrepot.'">'.$langs->trans("UseVirtualStock").'</a>)';
print '<br>';
} }
print '<br>'."\n"; print '<br>'."\n";
print '<form name="formFilterWarehouse" method="POST" action="">'; print '<form name="formFilterWarehouse" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">'; print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="filter">'; print '<input type="hidden" name="action" value="filter">';
print '<input type="hidden" name="search_ref" value="'.$search_ref.'">'; print '<input type="hidden" name="search_ref" value="'.$search_ref.'">';
@ -636,11 +641,13 @@ $param .= '&fk_supplier='.$fk_supplier;
$param .= '&fk_entrepot='.$fk_entrepot; $param .= '&fk_entrepot='.$fk_entrepot;
$stocklabel = $langs->trans('Stock'); $stocklabel = $langs->trans('Stock');
$stocklabelbis = $langs->trans('Stock');
if ($usevirtualstock == 1) $stocklabel = $langs->trans('VirtualStock'); if ($usevirtualstock == 1) $stocklabel = $langs->trans('VirtualStock');
if ($usevirtualstock == 0) $stocklabel = $langs->trans('PhysicalStock'); if ($usevirtualstock == 0) $stocklabel = $langs->trans('PhysicalStock');
if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0)
{ {
$stocklabel .= ' ('.$langs->trans("AllWarehouses").')'; $stocklabelbis = $stocklabel.' (Selected warehouse)';
$stocklabel .= ' ('.$langs->trans("AllWarehouses").')';
} }
$texte = $langs->trans('Replenishment'); $texte = $langs->trans('Replenishment');
@ -689,6 +696,10 @@ if (!empty($conf->service->enabled) && $type == 1) print '<td class="liste_titre
print '<td class="liste_titre right">'.$form->textwithpicto($langs->trans('IncludeEmptyDesiredStock'), $langs->trans('IncludeProductWithUndefinedAlerts')).'&nbsp;<input type="checkbox" id="includeproductswithoutdesiredqty" name="includeproductswithoutdesiredqty" '.(!empty($includeproductswithoutdesiredqtychecked) ? $includeproductswithoutdesiredqtychecked : '').'></td>'; print '<td class="liste_titre right">'.$form->textwithpicto($langs->trans('IncludeEmptyDesiredStock'), $langs->trans('IncludeProductWithUndefinedAlerts')).'&nbsp;<input type="checkbox" id="includeproductswithoutdesiredqty" name="includeproductswithoutdesiredqty" '.(!empty($includeproductswithoutdesiredqtychecked) ? $includeproductswithoutdesiredqtychecked : '').'></td>';
print '<td class="liste_titre right"></td>'; print '<td class="liste_titre right"></td>';
print '<td class="liste_titre right">'.$langs->trans('AlertOnly').'&nbsp;<input type="checkbox" id="salert" name="salert" '.(!empty($alertchecked) ? $alertchecked : '').'></td>'; print '<td class="liste_titre right">'.$langs->trans('AlertOnly').'&nbsp;<input type="checkbox" id="salert" name="salert" '.(!empty($alertchecked) ? $alertchecked : '').'></td>';
if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0)
{
print '<td class="liste_titre">&nbsp;</td>';
}
print '<td class="liste_titre right">'; print '<td class="liste_titre right">';
if (! empty($conf->global->STOCK_REPLENISH_ADD_CHECKBOX_INCLUDE_DRAFT_ORDER)) { if (! empty($conf->global->STOCK_REPLENISH_ADD_CHECKBOX_INCLUDE_DRAFT_ORDER)) {
print $langs->trans('IncludeAlsoDraftOrders').'&nbsp;<input type="checkbox" id="draftorder" name="draftorder" '.(!empty($draftchecked) ? $draftchecked : '').'>'; print $langs->trans('IncludeAlsoDraftOrders').'&nbsp;<input type="checkbox" id="draftorder" name="draftorder" '.(!empty($draftchecked) ? $draftchecked : '').'>';
@ -715,6 +726,10 @@ if (!empty($conf->service->enabled) && $type == 1) print_liste_field_titre('Dura
print_liste_field_titre('DesiredStock', $_SERVER["PHP_SELF"], 'p.desiredstock', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre('DesiredStock', $_SERVER["PHP_SELF"], 'p.desiredstock', $param, '', '', $sortfield, $sortorder, 'right ');
print_liste_field_titre('StockLimitShort', $_SERVER["PHP_SELF"], 'p.seuil_stock_alerte', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre('StockLimitShort', $_SERVER["PHP_SELF"], 'p.seuil_stock_alerte', $param, '', '', $sortfield, $sortorder, 'right ');
print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], 'stock_physique', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], 'stock_physique', $param, '', '', $sortfield, $sortorder, 'right ');
if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0)
{
print_liste_field_titre($stocklabelbis, $_SERVER["PHP_SELF"], 'stock_real_warehouse', $param, '', '', $sortfield, $sortorder, 'right ');
}
print_liste_field_titre('Ordered', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre('Ordered', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
print_liste_field_titre('StockToBuy', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre('StockToBuy', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
print_liste_field_titre('SupplierRef', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right '); print_liste_field_titre('SupplierRef', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
@ -757,8 +772,10 @@ while ($i < ($limit ? min($num, $limit) : $num))
{ {
// If option to increase/decrease is not on an object validation, virtual stock may differs from physical stock. // If option to increase/decrease is not on an object validation, virtual stock may differs from physical stock.
$stock = $prod->stock_theorique; $stock = $prod->stock_theorique;
//TODO $stockwarehouse = $prod->stock_warehouse[$fk_entrepot]->;
} else { } else {
$stock = $prod->stock_reel; $stock = $prod->stock_reel;
$stockwarehouse = $prod->stock_warehouse[$fk_entrepot]->real;
} }
// Force call prod->load_stats_xxx to choose status to count (otherwise it is loaded by load_stock function) // Force call prod->load_stats_xxx to choose status to count (otherwise it is loaded by load_stock function)
@ -774,20 +791,29 @@ while ($i < ($limit ? min($num, $limit) : $num))
//print $prod->stats_reception['qty']; //print $prod->stats_reception['qty'];
$ordered = $prod->stats_commande_fournisseur['qty'] - $prod->stats_reception['qty']; $ordered = $prod->stats_commande_fournisseur['qty'] - $prod->stats_reception['qty'];
$desiredstock = ($objp->desiredstockpse ? $objp->desiredstockpse : $objp->desiredstock); $desiredstock = $objp->desiredstock;
$alertstock = ($objp->seuil_stock_alertepse ? $objp->seuil_stock_alertepse : $objp->seuil_stock_alerte); $alertstock = $objp->seuil_stock_alerte;
$desiredstockwarehouse = ($objp->desiredstockpse ? $objp->desiredstockpse : '');
$alertstockwarehouse = ($objp->seuil_stock_alertepse ? $objp->seuil_stock_alertepse : '');
$warning = ''; $warning = '';
if ($alertstock && ($stock < $alertstock)) if ($alertstock && ($stock < $alertstock))
{ {
$warning = img_warning($langs->trans('StockTooLow')).' '; $warning = img_warning($langs->trans('StockTooLow')).' ';
} }
$warningwarehouse = '';
if ($alertstockwarehouse && ($stockwarehouse < $alertstockwarehouse))
{
$warningwarehouse = img_warning($langs->trans('StockTooLow')).' ';
}
//depending on conf, use either physical stock or //depending on conf, use either physical stock or
//virtual stock to compute the stock to buy value //virtual stock to compute the stock to buy value
if (empty($usevirtualstock)) $stocktobuy = max(max($desiredstock, $alertstock) - $stock - $ordered, 0); if (empty($usevirtualstock)) $stocktobuy = max(max($desiredstock, $alertstock) - $stock - $ordered, 0);
else $stocktobuy = max(max($desiredstock, $alertstock) - $stock, 0); //ordered is already in $stock in virtual mode else $stocktobuy = max(max($desiredstock, $alertstock) - $stock, 0); //ordered is already in $stock in virtual mode
if (empty($usevirtualstock)) $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse - $ordered, 0);
else $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse, 0); //ordered is already in $stock in virtual mode
$picto = ''; $picto = '';
if ($ordered > 0) if ($ordered > 0)
@ -803,57 +829,67 @@ while ($i < ($limit ? min($num, $limit) : $num))
$picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), 'help'); $picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), 'help');
} }
print '<tr class="oddeven">'; $variants = $prod->hasVariants();
if (!$variants || !empty($conf->global->VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT)) {
print '<tr class="oddeven">';
// Select field // Select field
print '<td><input type="checkbox" class="check" name="choose'.$i.'"></td>'; print '<td><input type="checkbox" class="check" name="choose'.$i.'"></td>';
print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>'; print '<td class="nowrap">'.$prod->getNomUrl(1, 'stock').'</td>';
print '<td>'.$objp->label; print '<td>'.$objp->label;
print '<input type="hidden" name="desc'.$i.'" value="'.dol_escape_htmltag($objp->description).'">'; // TODO Remove this and make a fetch to get description when creating order instead of a GETPOST print '<input type="hidden" name="desc'.$i.'" value="'.dol_escape_htmltag($objp->description).'">'; // TODO Remove this and make a fetch to get description when creating order instead of a GETPOST
print '</td>'; print '</td>';
if (!empty($conf->service->enabled) && $type == 1) if (!empty($conf->service->enabled) && $type == 1)
{ {
if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) { $regs = array();
$duration = $regs[1].' '.$langs->trans('DurationYear'); if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
} elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) { $duration = $regs[1].' '.$langs->trans('DurationYear');
$duration = $regs[1].' '.$langs->trans('DurationMonth'); } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
} elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) { $duration = $regs[1].' '.$langs->trans('DurationMonth');
$duration = $regs[1].' '.$langs->trans('DurationDay'); } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
} else { $duration = $regs[1].' '.$langs->trans('DurationDay');
$duration = $objp->duration; } else {
$duration = $objp->duration;
}
print '<td class="center">'.$duration.'</td>';
} }
print '<td class="center">'.$duration.'</td>';
// Desired stock
print '<td class="right">'.($fk_entrepot > 0 ? $desiredstockwarehouse : $desiredstock).'</td>';
// Limit stock for alert
print '<td class="right">'.($fk_entrepot > 0 ? $alertstockwarehouse : $alertstock).'</td>';
// Current stock (all warehouses)
print '<td class="right">'.$warning.$stock.'</td>';
// Current stock (warehouse selected only)
if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0)
{
print '<td class="right">'.$warningwarehouse.$stockwarehouse.'</td>';
}
// Already ordered
print '<td class="right"><a href="replenishorders.php?search_product='.$prod->id.'">'.$ordered.'</a> '.$picto.'</td>';
// To order
print '<td class="right"><input type="text" size="4" name="tobuy'.$i.'" value="'.($fk_entrepot > 0 ? $stocktobuywarehouse : $stocktobuy).'"></td>';
// Supplier
print '<td class="right">';
print $form->select_product_fourn_price($prod->id, 'fourn'.$i, $fk_supplier);
print '</td>';
// Fields from hook
$parameters = array('objp'=>$objp);
$reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
print '</tr>';
} }
// Desired stock
print '<td class="right">'.$desiredstock.'</td>';
// Limit stock for alert
print '<td class="right">'.$alertstock.'</td>';
// Current stock (all warehouses)
print '<td class="right">'.$warning.$stock.'</td>';
// Already ordered
print '<td class="right"><a href="replenishorders.php?search_product='.$prod->id.'">'.$ordered.'</a> '.$picto.'</td>';
// To order
print '<td class="right"><input type="text" size="4" name="tobuy'.$i.'" value="'.$stocktobuy.'"></td>';
// Supplier
print '<td class="right">';
print $form->select_product_fourn_price($prod->id, 'fourn'.$i, $fk_supplier);
print '</td>';
// Fields from hook
$parameters = array('objp'=>$objp);
$reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
print '</tr>';
} }
$i++; $i++;
} }