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) {