From a5c2ce1727044a141cbdc228c3f6680ac8115179 Mon Sep 17 00:00:00 2001 From: ATM john Date: Tue, 1 Jun 2021 08:45:45 +0200 Subject: [PATCH 1/4] NEW/FIX : Allow auto picking for child warehouse and taking into account of the quantities already affected in list --- htdocs/expedition/card.php | 118 +++++++++++++++++++++++++++-- htdocs/langs/en_US/deliveries.lang | 1 + htdocs/langs/fr_FR/deliveries.lang | 1 + 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index cc441d9f50f..8d4ed510449 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1020,7 +1020,11 @@ if ($action == 'create') { // Load shipments already done for same order $object->loadExpeditions(); - if ($numAsked) { + + $alreadyQtyBatchSetted = $alreadyQtySetted = array(); + + if ($numAsked) + { print ''; print ''.$langs->trans("Description").''; print ''.$langs->trans("QtyOrdered").''; @@ -1044,6 +1048,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); @@ -1293,6 +1306,12 @@ if ($action == 'create') { $subj = 0; // Define nb of lines suggested for this order line $nbofsuggested = 0; + + uasort ( $product->stock_warehouse , function ($a, $b){ + if ($a->real == $b->real) { return 0; } + return ($a->real < $b->real) ? -1 : 1; + }); + foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { if ($stock_warehouse->real > 0) { $nbofsuggested++; @@ -1300,6 +1319,12 @@ 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 +1334,32 @@ 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,6 +1416,12 @@ if ($action == 'create') { $tmpwarehouseObject = new Entrepot($db); $productlotObject = new Productlot($db); + + uasort ( $product->stock_warehouse , function ($a, $b){ + if ($a->real == $b->real) { return 0; } + return ($a->real < $b->real) ? -1 : 1; + }); + // Define nb of lines suggested for this order line $nbofsuggested = 0; foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { @@ -1373,18 +1429,68 @@ if ($action == 'create') { foreach ($stock_warehouse->detail_batch as $dbatch) { $nbofsuggested++; } + + // Sort Batch priority + uasort($stock_warehouse->detail_batch, function ($a, $b) { + $compare = 0; + $multiplePow = 0; + // The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. + + // PRIORITY FOR QTY : Eliminate place with small qty first + $multiplePow++; + $multiple = pow(10, $multiplePow); + $compare += (($a->qty < $b->qty) ? -1 : 1) * $multiple; + + // PRIORITY FOR SELL EXPIRATION DATE + $multiplePow++; + $multiple = pow(10, $multiplePow); + $compare += (($a->sellby < $b->sellby) ? -1 : 1) * $multiple; + + // PRIORITY FOR CONSUMPTION EXPIRATION DATE + $multiplePow++; + $multiple = pow(10, $multiplePow); + $compare += (($a->eatby < $b->eatby) ? -1 : 1) * $multiple; + + return $compare; + }); } } + 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'); + } + + $tooltip = ''; + if(!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])){ + $tooltip = ' class="classfortooltip" title="'.$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 ''; 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 From cf9c9ec8825e5db3da054b9ecb8eeade7126eb9b Mon Sep 17 00:00:00 2001 From: ATM john Date: Tue, 1 Jun 2021 09:07:05 +0200 Subject: [PATCH 2/4] fix condition --- htdocs/expedition/card.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 8d4ed510449..64d554b9780 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1155,10 +1155,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 From 3cc65989c1eee25920d816aa8b50efd5d65f52ea Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Tue, 1 Jun 2021 07:25:29 +0000 Subject: [PATCH 3/4] Fixing style errors. --- htdocs/expedition/card.php | 45 +++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 64d554b9780..543a67311f0 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1021,10 +1021,9 @@ if ($action == 'create') { $object->loadExpeditions(); - $alreadyQtyBatchSetted = $alreadyQtySetted = array(); + $alreadyQtyBatchSetted = $alreadyQtySetted = array(); - if ($numAsked) - { + if ($numAsked) { print ''; print ''.$langs->trans("Description").''; print ''.$langs->trans("QtyOrdered").''; @@ -1049,12 +1048,12 @@ if ($action == 'create') { } $warehouse_id = GETPOST('entrepot_id', 'int'); - $warehousePicking = array(); - // get all warehouse children for picking - if($warehouse_id > 0){ + $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); + $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking); } $indiceAsked = 0; @@ -1306,7 +1305,7 @@ if ($action == 'create') { // Define nb of lines suggested for this order line $nbofsuggested = 0; - uasort ( $product->stock_warehouse , function ($a, $b){ + uasort( $product->stock_warehouse, function ($a, $b) { if ($a->real == $b->real) { return 0; } return ($a->real < $b->real) ? -1 : 1; }); @@ -1318,8 +1317,7 @@ 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 (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) { // if a warehouse was selected by user, picking is limited to this warehouse and his children continue; } @@ -1333,11 +1331,10 @@ if ($action == 'create') { print ''; print ''; if ($line->product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) { - if(isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])){ + 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])){ + } else { + if (!isset($alreadyQtySetted[$line->fk_product])) { $alreadyQtySetted[$line->fk_product] = array(); } @@ -1347,14 +1344,14 @@ if ($action == 'create') { if ($deliverableQty < 0) $deliverableQty = 0; $tooltip = ''; - if(!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])){ + 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)){ + if (GETPOSTISSET($inputName)) { $deliverableQty = GETPOST($inputName, 'int'); } @@ -1416,7 +1413,7 @@ if ($action == 'create') { $tmpwarehouseObject = new Entrepot($db); $productlotObject = new Productlot($db); - uasort ( $product->stock_warehouse , function ($a, $b){ + uasort( $product->stock_warehouse, function ($a, $b) { if ($a->real == $b->real) { return 0; } return ($a->real < $b->real) ? -1 : 1; }); @@ -1459,17 +1456,15 @@ if ($action == 'create') { $tmpwarehouseObject->fetch($warehouse_id); if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) { foreach ($stock_warehouse->detail_batch as $dbatch) { - $batchStock = + $dbatch->qty; // To get a numeric - if(isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])){ + 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])){ + } else { + if (!isset($alreadyQtyBatchSetted[$line->fk_product])) { $alreadyQtyBatchSetted[$line->fk_product] = array(); } - if(!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])){ + if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) { $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array(); } @@ -1479,12 +1474,12 @@ if ($action == 'create') { if ($deliverableQty < 0) $deliverableQty = 0; $inputName = 'qtyl'.$indiceAsked.'_'.$subj; - if(GETPOSTISSET($inputName)){ + if (GETPOSTISSET($inputName)) { $deliverableQty = GETPOST($inputName, 'int'); } $tooltip = ''; - if(!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])){ + if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) { $tooltip = ' class="classfortooltip" title="'.$langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)].'" '; } From f13388122cc57b6fd9b4a8863f64a51367a51715 Mon Sep 17 00:00:00 2001 From: ATM john Date: Tue, 1 Jun 2021 21:43:05 +0200 Subject: [PATCH 4/4] Add fetch stock order --- htdocs/expedition/card.php | 46 +++------------------ htdocs/product/class/product.class.php | 2 + htdocs/product/class/productbatch.class.php | 8 +++- 3 files changed, 14 insertions(+), 42 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 543a67311f0..ef6c80d20d8 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1305,11 +1305,6 @@ if ($action == 'create') { // Define nb of lines suggested for this order line $nbofsuggested = 0; - uasort( $product->stock_warehouse, function ($a, $b) { - if ($a->real == $b->real) { return 0; } - return ($a->real < $b->real) ? -1 : 1; - }); - foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { if ($stock_warehouse->real > 0) { $nbofsuggested++; @@ -1413,42 +1408,11 @@ if ($action == 'create') { $tmpwarehouseObject = new Entrepot($db); $productlotObject = new Productlot($db); - uasort( $product->stock_warehouse, function ($a, $b) { - if ($a->real == $b->real) { return 0; } - return ($a->real < $b->real) ? -1 : 1; - }); - // 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++; - } - - // Sort Batch priority - uasort($stock_warehouse->detail_batch, function ($a, $b) { - $compare = 0; - $multiplePow = 0; - // The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. - - // PRIORITY FOR QTY : Eliminate place with small qty first - $multiplePow++; - $multiple = pow(10, $multiplePow); - $compare += (($a->qty < $b->qty) ? -1 : 1) * $multiple; - - // PRIORITY FOR SELL EXPIRATION DATE - $multiplePow++; - $multiple = pow(10, $multiplePow); - $compare += (($a->sellby < $b->sellby) ? -1 : 1) * $multiple; - - // PRIORITY FOR CONSUMPTION EXPIRATION DATE - $multiplePow++; - $multiple = pow(10, $multiplePow); - $compare += (($a->eatby < $b->eatby) ? -1 : 1) * $multiple; - - return $compare; - }); + $nbofsuggested+=count($stock_warehouse->detail_batch); } } @@ -1478,15 +1442,15 @@ if ($action == 'create') { $deliverableQty = GETPOST($inputName, 'int'); } - $tooltip = ''; + $tooltipClass = $tooltipTitle = ''; if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) { - $tooltip = ' class="classfortooltip" title="'.$langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$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/product/class/product.class.php b/htdocs/product/class/product.class.php index 2ccc5e63c8f..9d8977a2223 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -5147,6 +5147,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 10704f2f53e..7ac2e43740f 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) {