diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php
index 02ac007d30f..582b23aec45 100644
--- a/htdocs/expedition/card.php
+++ b/htdocs/expedition/card.php
@@ -1020,6 +1020,9 @@ if ($action == 'create') {
// Load shipments already done for same order
$object->loadExpeditions();
+
+ $alreadyQtyBatchSetted = $alreadyQtySetted = array();
+
if ($numAsked) {
print '
';
print ''.$langs->trans("Description").' ';
@@ -1044,6 +1047,15 @@ if ($action == 'create') {
print " \n";
}
+ $warehouse_id = GETPOST('entrepot_id', 'int');
+ $warehousePicking = array();
+ // get all warehouse children for picking
+ if ($warehouse_id > 0) {
+ $warehousePicking[] = $warehouse_id;
+ $warehouseObj = new Entrepot($db);
+ $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
+ }
+
$indiceAsked = 0;
while ($indiceAsked < $numAsked) {
$product = new Product($db);
@@ -1142,10 +1154,9 @@ if ($action == 'create') {
} else {
$quantityToBeDelivered = $quantityAsked - $quantityDelivered;
}
- $warehouse_id = GETPOST('entrepot_id', 'int');
$warehouseObject = null;
- if ($warehouse_id > 0 || !($line->fk_product > 0) || empty($conf->stock->enabled)) { // If warehouse was already selected or if product is not a predefined, we go into this part with no multiwarehouse selection
+ if (count($warehousePicking) == 1 || !($line->fk_product > 0) || empty($conf->stock->enabled)) { // If warehouse was already selected or if product is not a predefined, we go into this part with no multiwarehouse selection
print '';
//ship from preselected location
$stock = + $product->stock_warehouse[$warehouse_id]->real; // Convert to number
@@ -1293,6 +1304,7 @@ if ($action == 'create') {
$subj = 0;
// Define nb of lines suggested for this order line
$nbofsuggested = 0;
+
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
if ($stock_warehouse->real > 0) {
$nbofsuggested++;
@@ -1300,6 +1312,11 @@ if ($action == 'create') {
}
$tmpwarehouseObject = new Entrepot($db);
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
+ if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
+ // if a warehouse was selected by user, picking is limited to this warehouse and his children
+ continue;
+ }
+
$tmpwarehouseObject->fetch($warehouse_id);
if ($stock_warehouse->real > 0) {
$stock = + $stock_warehouse->real; // Convert it to number
@@ -1309,7 +1326,31 @@ if ($action == 'create') {
print '';
print '';
if ($line->product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
- print ' ';
+ if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
+ $deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
+ } else {
+ if (!isset($alreadyQtySetted[$line->fk_product])) {
+ $alreadyQtySetted[$line->fk_product] = array();
+ }
+
+ $deliverableQty = min($quantityToBeDelivered, $stock);
+ }
+
+ if ($deliverableQty < 0) $deliverableQty = 0;
+
+ $tooltip = '';
+ if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
+ $tooltip = ' class="classfortooltip" title="'.$langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)].'" ';
+ }
+
+ $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
+
+ $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
+ if (GETPOSTISSET($inputName)) {
+ $deliverableQty = GETPOST($inputName, 'int');
+ }
+
+ print ' ';
print ' ';
} else {
print $langs->trans("NA");
@@ -1366,27 +1407,50 @@ if ($action == 'create') {
$tmpwarehouseObject = new Entrepot($db);
$productlotObject = new Productlot($db);
+
// Define nb of lines suggested for this order line
$nbofsuggested = 0;
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) {
- foreach ($stock_warehouse->detail_batch as $dbatch) {
- $nbofsuggested++;
- }
+ $nbofsuggested+=count($stock_warehouse->detail_batch);
}
}
+
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
$tmpwarehouseObject->fetch($warehouse_id);
if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) {
foreach ($stock_warehouse->detail_batch as $dbatch) {
- //var_dump($dbatch);
$batchStock = + $dbatch->qty; // To get a numeric
- $deliverableQty = min($quantityToBeDelivered, $batchStock);
- if ($deliverableQty < 0) {
- $deliverableQty = 0;
+ if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
+ $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
+ } else {
+ if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
+ $alreadyQtyBatchSetted[$line->fk_product] = array();
+ }
+
+ if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
+ $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
+ }
+
+ $deliverableQty = min($quantityToBeDelivered, $batchStock);
}
+
+ if ($deliverableQty < 0) $deliverableQty = 0;
+
+ $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
+ if (GETPOSTISSET($inputName)) {
+ $deliverableQty = GETPOST($inputName, 'int');
+ }
+
+ $tooltipClass = $tooltipTitle = '';
+ if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
+ $tooltipClass = ' classfortooltip';
+ $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
+ }
+ $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
+
print ' ';
- print ' ';
+ print ' ';
print ' ';
print '';
diff --git a/htdocs/langs/en_US/deliveries.lang b/htdocs/langs/en_US/deliveries.lang
index fdfd6404a8a..cd8a36e6c70 100644
--- a/htdocs/langs/en_US/deliveries.lang
+++ b/htdocs/langs/en_US/deliveries.lang
@@ -30,3 +30,4 @@ NonShippable=Not Shippable
ShowShippableStatus=Show shippable status
ShowReceiving=Show delivery receipt
NonExistentOrder=Nonexistent order
+StockQuantitiesAlreadyAllocatedOnPreviousLines = Stock quantities already allocated on previous lines
diff --git a/htdocs/langs/fr_FR/deliveries.lang b/htdocs/langs/fr_FR/deliveries.lang
index bd13cce814c..6af98a54d4f 100644
--- a/htdocs/langs/fr_FR/deliveries.lang
+++ b/htdocs/langs/fr_FR/deliveries.lang
@@ -30,3 +30,4 @@ NonShippable=Non expédiable
ShowShippableStatus=Afficher le statut Expédiable
ShowReceiving=Afficher le bon de réception
NonExistentOrder=Commande inexistante
+StockQuantitiesAlreadyAllocatedOnPreviousLines = Qtés de stock déja attribuées sur une ou des lignes précédentes
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index b0f8ee1aa8d..9957641c13b 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -5192,6 +5192,8 @@ class Product extends CommonObject
$sql .= " AND w.statut IN (".$this->db->sanitize(implode(',', $warehouseStatus)).")";
}
+ $sql .= " ORDER BY ps.reel ".(!empty($conf->global->DO_NOT_TRY_TO_DEFRAGMENT_STOCKS_WAREHOUSE)?'DESC':'ASC'); // Note : qty ASC is important for expedition card, to avoid stock fragmentation;
+
dol_syslog(get_class($this)."::load_stock", LOG_DEBUG);
$result = $this->db->query($sql);
if ($result) {
diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php
index 529284af464..f10adee334e 100644
--- a/htdocs/product/class/productbatch.class.php
+++ b/htdocs/product/class/productbatch.class.php
@@ -436,7 +436,7 @@ class Productbatch extends CommonObject
*/
public static function findAll($db, $fk_product_stock, $with_qty = 0, $fk_product = 0)
{
- global $langs;
+ global $langs, $conf;
$ret = array();
$sql = "SELECT";
@@ -462,6 +462,12 @@ class Productbatch extends CommonObject
$sql .= " AND t.qty <> 0";
}
+ $sql .= " ORDER BY ";
+ // TODO : use product lifo and fifo when product will implement it
+ if ($fk_product > 0) { $sql .= "pl.eatby ASC, pl.sellby ASC, "; }
+ $sql .= "t.eatby ASC, t.sellby ASC ";
+ $sql .= ", t.qty ".(!empty($conf->global->DO_NOT_TRY_TO_DEFRAGMENT_STOCKS_WAREHOUSE)?'DESC':'ASC'); // Note : qty ASC is important for expedition card, to avoid stock fragmentation
+
dol_syslog("productbatch::findAll", LOG_DEBUG);
$resql = $db->query($sql);
if ($resql) {