NEW Add option "Stock can be negative". Off by default.

This commit is contained in:
Laurent Destailleur 2016-07-15 19:57:35 +02:00
parent 1195e3bf0c
commit 97b03e225b
4 changed files with 106 additions and 95 deletions

View File

@ -323,63 +323,73 @@ $found++;
print '</table>';
print '<br>';
print '<table class="noborder" width="100%">';
print '<tr class="liste_titre">';
print " <td>".$langs->trans("RuleForStockAvailability")."</td>\n";
print " <td align=\"right\" width=\"160\">&nbsp;</td>\n";
print '</tr>'."\n";
$var=!$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("WarehouseAllowNegativeTransfer").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_ALLOW_NEGATIVE_TRANSFER\">";
print $form->selectyesno("STOCK_ALLOW_NEGATIVE_TRANSFER",$conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
// Option to force stock to be enough before adding a line into document
if ($conf->invoice->enabled || $conf->order->enabled || $conf->expedition->enabled)
{
print '<br>';
print '<table class="noborder" width="100%">';
print '<tr class="liste_titre">';
print " <td>".$langs->trans("RuleForStockAvailability")."</td>\n";
print " <td align=\"right\" width=\"160\">&nbsp;</td>\n";
print '</tr>'."\n";
if($conf->invoice->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForInvoice").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_INVOICE\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_INVOICE",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
if($conf->order->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForOrder").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_ORDER\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_ORDER",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
if($conf->expedition->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForShipment").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
print '</table>';
if($conf->invoice->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForInvoice").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_INVOICE\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_INVOICE",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
if($conf->order->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForOrder").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_ORDER\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_ORDER",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
if($conf->expedition->enabled) {
$var = !$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("StockMustBeEnoughForShipment").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT\">";
print $form->selectyesno("STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT",$conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
}
print '</table>';
$virtualdiffersfromphysical=0;
if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)
|| ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)
@ -432,21 +442,6 @@ print '</form>';
print "</td>\n";
print "</tr>\n";
$var=!$var;
print "<tr ".$bc[$var].">";
print '<td width="60%">'.$langs->trans("WarehouseAllowNegativeTransfer").'</td>';
print '<td width="160" align="right">';
print "<form method=\"post\" action=\"stock.php\">";
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print "<input type=\"hidden\" name=\"action\" value=\"STOCK_ALLOW_NEGATIVE_TRANSFER\">";
print $form->selectyesno("STOCK_ALLOW_NEGATIVE_TRANSFER",$conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER,1);
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print '</form>';
print "</td>\n";
print "</tr>\n";
print '<br>';
/* I keep the option/feature, but hidden to end users for the moment. If feature is used by module, no need to have users see it.

View File

@ -114,13 +114,13 @@ RecordMovement=Record transfert
ReceivingForSameOrder=Receipts for this order
StockMovementRecorded=Stock movements recorded
RuleForStockAvailability=Rules on stock requirements
StockMustBeEnoughForInvoice=Stock level must be enough to add product/service to invoice
StockMustBeEnoughForOrder=Stock level must be enough to add product/service to order
StockMustBeEnoughForShipment= Stock level must be enough to add product/service to shipment
StockMustBeEnoughForInvoice=Stock level must be enough to add product/service to invoice (even if real stock change occurs later on another event)
StockMustBeEnoughForOrder=Stock level must be enough to add product/service to order (even if stock change occurs later on another event)
StockMustBeEnoughForShipment= Stock level must be enough to add product/service to shipment (even if stock change occurs later on another event)
MovementLabel=Label of movement
InventoryCode=Movement or inventory code
IsInPackage=Contained into package
WarehouseAllowNegativeTransfer=Allow transfer even without stock
WarehouseAllowNegativeTransfer=Stock can be negative
qtyToTranferIsNotEnough=You don't have enough stock from your source warehouse
ShowWarehouse=Show warehouse
MovementCorrectStock=Stock correction for product %s

View File

@ -494,11 +494,13 @@ class Productbatch extends CommonObject
if ($fk_product > 0)
{
$sql.= ", pl.eatby as eatby, pl.sellby as sellby";
// TODO May add extrafields to ?
}
$sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t";
if ($fk_product > 0)
{
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON pl.fk_product = ".$fk_product." AND pl.batch = t.batch";
// TODO May add extrafields to ?
}
$sql.= " WHERE fk_product_stock=".$fk_product_stock;
if ($with_qty) $sql.= " AND t.qty <> 0";
@ -523,7 +525,7 @@ class Productbatch extends CommonObject
$tmp->qty = $obj->qty;
$tmp->import_key = $obj->import_key;
array_push($ret,$tmp);
$ret[$tmp->batch] = $tmp; // $ret is for a $fk_product_stock and unique key is on $fk_product_stock+batch
$i++;
}
$db->free($resql);

View File

@ -240,31 +240,45 @@ class MouvementStock extends CommonObject
}
}
// TODO Check qty is ok for stock move.
if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch)
{
}
else
{
}
if (empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER) && in_array($type, array(1, 2)))
{
if (empty($product->stock_warehouse[$entrepot_id]->real) || $product->stock_warehouse[$entrepot_id]->real < abs($qty))
{
$this->error = $langs->trans('qtyToTranferIsNotEnough');
$this->errors[] = $langs->trans('qtyToTranferIsNotEnough');
$this->db->rollback();
return -8;
}
}
// Define if we must make the stock change (If product type is a service or if stock is used also for services)
$movestock=0;
if ($product->type != Product::TYPE_SERVICE || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) $movestock=1;
// Check if stock is enough when qty is < 0
// Note that qty should be > 0 with type 0 or 3, < 0 with type 1 or 2.
if ($qty < 0 && empty($conf->global->STOCK_ALLOW_NEGATIVE_TRANSFER))
{
if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch)
{
$foundforbatch=0;
$qtyisnotenough=0;
foreach($product->stock_warehouse[$entrepot_id]->detail_batch as $batchcursor => $prodbatch)
{
if ($batch != $batchcursor) continue;
$foundforbatch=1;
if ($prodbatch->qty < abs($qty)) $qtyisnotenough=1;
break;
}
if (! $foundforbatch || $qtyisnotenough)
{
$this->error = $langs->trans('qtyToTranferLotIsNotEnough');
$this->errors[] = $langs->trans('qtyToTranferLotIsNotEnough');
$this->db->rollback();
return -8;
}
}
else
{
if (empty($product->stock_warehouse[$entrepot_id]->real) || $product->stock_warehouse[$entrepot_id]->real < abs($qty))
{
$this->error = $langs->trans('qtyToTranferIsNotEnough');
$this->errors[] = $langs->trans('qtyToTranferIsNotEnough');
$this->db->rollback();
return -8;
}
}
}
if ($movestock && $entrepot_id > 0) // Change stock for current product, change for subproduct is done after
{
if(!empty($this->origin)) { // This is set by caller for tracking reason