From 2638bce851f1080483b2c115490c4b8201a05fbe Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Fri, 9 Jul 2021 22:16:08 +0200 Subject: [PATCH] Improve handling of not in-stock but valid manually entered products on the consume and transfer page (references #1429) --- changelog/62_UNRELEASED_xxxx-xx-xx.md | 2 +- controllers/StockController.php | 2 +- public/viewjs/components/productpicker.js | 101 ++++++++++++++++------ public/viewjs/consume.js | 17 +--- public/viewjs/transfer.js | 16 +--- 5 files changed, 81 insertions(+), 57 deletions(-) diff --git a/changelog/62_UNRELEASED_xxxx-xx-xx.md b/changelog/62_UNRELEASED_xxxx-xx-xx.md index 5185c185..0da58711 100644 --- a/changelog/62_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/62_UNRELEASED_xxxx-xx-xx.md @@ -28,7 +28,7 @@ ### Stock improvements/fixes - Product barcodes are now enforced to be unique across products - On the stock overview page it's now also possible to search/filter by product barcodes (via the general search field) -- The product picker on the consume page now only shows products which are currently in stock +- The product picker on the consume and transfer page now only shows products which are currently in stock - Added a filter option to only show in-stock products on the products list page (master data) - Added new columns on the stock overview page (hidden by default): Product description, product default location, parent product - Fixed that editing stock entries was not possible diff --git a/controllers/StockController.php b/controllers/StockController.php index 9516a033..9eb96b37 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -533,7 +533,7 @@ class StockController extends BaseController public function Transfer(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { return $this->renderPage($response, 'transfer', [ - 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'), + 'products' => $this->getDatabase()->products()->where('active = 1')->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)')->orderBy('name', 'COLLATE NOCASE'), 'barcodes' => $this->getDatabase()->product_barcodes_comma_separated(), 'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'), 'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'), diff --git a/public/viewjs/components/productpicker.js b/public/viewjs/components/productpicker.js index 9b32c570..88def83e 100644 --- a/public/viewjs/components/productpicker.js +++ b/public/viewjs/components/productpicker.js @@ -248,38 +248,83 @@ $('#product_id_text_input').on('blur', function(e) }; } - Grocy.Components.ProductPicker.PopupOpen = true; - bootbox.dialog({ - message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input), - title: __t('Create or assign product'), - onEscape: function() + // The product picker contains only in-stock products on some pages, + // so only show the workflow dialog when the entered input + // does not match in existing product (name) or barcode, + // otherwise an error validation message that the product is not in stock + var existsAsProduct = false; + var existsAsBarcode = false; + Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + input, + function(barcodeResult) { - Grocy.Components.ProductPicker.PopupOpen = false; - Grocy.Components.ProductPicker.SetValue(''); + if (barcodeResult.length > 0) + { + existsAsProduct = true; + } + + Grocy.Api.Get('objects/products?query[]=name=' + input, + function(productResult) + { + if (productResult.length > 0) + { + existsAsProduct = true; + } + + if (!existsAsBarcode && !existsAsProduct) + { + Grocy.Components.ProductPicker.PopupOpen = true; + bootbox.dialog({ + message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input), + title: __t('Create or assign product'), + onEscape: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + Grocy.Components.ProductPicker.SetValue(''); + }, + size: 'large', + backdrop: true, + closeButton: false, + buttons: buttons + }).on('keypress', function(e) + { + if (e.key === 'B' || e.key === 'b') + { + $('.add-new-barcode-dialog-button').not(".d-none").click(); + } + if (e.key === 'p' || e.key === 'P') + { + $('.add-new-product-dialog-button').not(".d-none").click(); + } + if (e.key === 'a' || e.key === 'A') + { + $('.add-new-product-with-barcode-dialog-button').not(".d-none").click(); + } + if (e.key === 'c' || e.key === 'C') + { + $('.retry-camera-scanning-button').not(".d-none").click(); + } + }); + } + else + { + Grocy.Components.ProductAmountPicker.Reset(); + Grocy.Components.ProductPicker.Clear(); + Grocy.FrontendHelpers.ValidateForm('consume-form'); + Grocy.Components.ProductPicker.ShowCustomError(__t('This product is not in stock')); + Grocy.Components.ProductPicker.GetInputElement().focus(); + } + }, + function(xhr) + { + console.error(xhr); + } + ); }, - size: 'large', - backdrop: true, - closeButton: false, - buttons: buttons - }).on('keypress', function(e) - { - if (e.key === 'B' || e.key === 'b') + function(xhr) { - $('.add-new-barcode-dialog-button').not(".d-none").click(); + console.error(xhr); } - if (e.key === 'p' || e.key === 'P') - { - $('.add-new-product-dialog-button').not(".d-none").click(); - } - if (e.key === 'a' || e.key === 'A') - { - $('.add-new-product-with-barcode-dialog-button').not(".d-none").click(); - } - if (e.key === 'c' || e.key === 'C') - { - $('.retry-camera-scanning-button').not(".d-none").click(); - } - }); + ); } } }); diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index 8f6e4a39..082b2ed5 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -419,20 +419,9 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) $("#tare-weight-handling-info").addClass("d-none"); } - if ((parseFloat(productDetails.stock_amount_aggregated) || 0) === 0) - { - Grocy.Components.ProductAmountPicker.Reset(); - Grocy.Components.ProductPicker.Clear(); - Grocy.FrontendHelpers.ValidateForm('consume-form'); - Grocy.Components.ProductPicker.ShowCustomError(__t('This product is not in stock')); - Grocy.Components.ProductPicker.GetInputElement().focus(); - } - else - { - Grocy.Components.ProductPicker.HideCustomError(); - Grocy.FrontendHelpers.ValidateForm('consume-form'); - $('#display_amount').focus(); - } + Grocy.Components.ProductPicker.HideCustomError(); + Grocy.FrontendHelpers.ValidateForm('consume-form'); + $('#display_amount').focus(); if (productDetails.stock_amount == productDetails.stock_amount_opened || productDetails.product.enable_tare_weight_handling == 1) { diff --git a/public/viewjs/transfer.js b/public/viewjs/transfer.js index 92a608cb..d30ee90f 100644 --- a/public/viewjs/transfer.js +++ b/public/viewjs/transfer.js @@ -249,19 +249,9 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) $('#display_amount').attr("data-stock-amount", productDetails.stock_amount); - if ((parseFloat(productDetails.stock_amount) || 0) === 0) - { - Grocy.Components.ProductPicker.Clear(); - Grocy.FrontendHelpers.ValidateForm('transfer-form'); - Grocy.Components.ProductPicker.ShowCustomError(__t('This product is not in stock')); - Grocy.Components.ProductPicker.GetInputElement().focus(); - } - else - { - Grocy.Components.ProductPicker.HideCustomError(); - Grocy.FrontendHelpers.ValidateForm('transfer-form'); - $('#display_amount').focus(); - } + Grocy.Components.ProductPicker.HideCustomError(); + Grocy.FrontendHelpers.ValidateForm('transfer-form'); + $('#display_amount').focus(); }, function(xhr) {