From a85998dd40c9f651ec1e9258ad5364e8e4c04124 Mon Sep 17 00:00:00 2001 From: fipwmaqzufheoxq92ebc <29818044+fipwmaqzufheoxq92ebc@users.noreply.github.com> Date: Wed, 14 Oct 2020 17:48:37 +0200 Subject: [PATCH] Improvements (#1049) * Fixes #1035: Check available amount after filtering by stock_entry_id * Fixes #1036: Remove stock-related buttons/options from Shopping-list if FEATURE_FLAG_STOCK is disabled * Fixes #1010: Repair recipe-picture upload. * Fixes #958: Disable auto-reload of equipments-page. * Fix uncaught exception in locationpicker.js * Fixes #761 and #762: Add "Remove exact amount" for products with tare weight handling and use it for recipe-consumption. * Fixes #1048: Repair product-group-filter on "Master Data"/Products * Renamed variable Co-authored-by: Bernd Bestel --- controllers/StockApiController.php | 10 ++- public/js/grocy_dbchangedhandling.js | 39 +++++----- public/viewjs/consume.js | 88 ++++++++++++++--------- public/viewjs/recipeform.js | 6 +- public/viewjs/shoppinglistitemform.js | 2 +- services/RecipesService.php | 2 +- services/StockService.php | 16 +++-- views/components/locationpicker.blade.php | 3 +- views/consume.blade.php | 5 +- views/equipment.blade.php | 1 + views/layout/default.blade.php | 3 +- views/products.blade.php | 2 +- views/shoppinglist.blade.php | 12 ++-- views/shoppinglistitemform.blade.php | 20 +++--- 14 files changed, 125 insertions(+), 84 deletions(-) diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 8e54aae2..24d6a84d 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -263,7 +263,15 @@ class StockApiController extends BaseApiController $recipeId = $requestBody['recipe_id']; } - $bookingId = $this->getStockService()->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId, $locationId); + $consumeExact = false; + + if (array_key_exists('exact_amount', $requestBody)) + { + $consumeExact = $requestBody['exact_amount']; + } + $transactionId = null; + + $bookingId = $this->getStockService()->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId, $locationId, $transactionId, false, $consumeExact); return $this->ApiResponse($response, $this->getDatabase()->stock_log($bookingId)); } catch (\Exception $ex) diff --git a/public/js/grocy_dbchangedhandling.js b/public/js/grocy_dbchangedhandling.js index 77ee6c0c..b92e7b3e 100644 --- a/public/js/grocy_dbchangedhandling.js +++ b/public/js/grocy_dbchangedhandling.js @@ -15,31 +15,34 @@ // Check if the database has changed once a minute // If a change is detected, reload the current page, but only if already idling for at least 50 seconds, // when there is no unsaved form data and when the user enabled auto reloading -setInterval(function() +if (Grocy.DbChangedHandlingEnabledForPage) { - Grocy.Api.Get('system/db-changed-time', - function(result) - { - var newDbChangedTime = moment(result.changed_time); - if (newDbChangedTime.isAfter(Grocy.DatabaseChangedTime)) + setInterval(function() + { + Grocy.Api.Get('system/db-changed-time', + function(result) { - if (Grocy.IdleTime >= 50) + var newDbChangedTime = moment(result.changed_time); + if (newDbChangedTime.isAfter(Grocy.DatabaseChangedTime)) { - if (BoolVal(Grocy.UserSettings.auto_reload_on_db_change) && $("form.is-dirty").length === 0 && !$("body").hasClass("fullscreen-card")) + if (Grocy.IdleTime >= 50) { - window.location.reload(); + if (BoolVal(Grocy.UserSettings.auto_reload_on_db_change) && $("form.is-dirty").length === 0 && !$("body").hasClass("fullscreen-card")) + { + window.location.reload(); + } } - } - Grocy.DatabaseChangedTime = newDbChangedTime; + Grocy.DatabaseChangedTime = newDbChangedTime; + } + }, + function(xhr) + { + console.error(xhr); } - }, - function(xhr) - { - console.error(xhr); - } - ); -}, 60000); + ); + }, 60000); +} Grocy.IdleTime = 0; Grocy.ResetIdleTime = function() diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index e007dc55..69e772be 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -9,6 +9,7 @@ var jsonData = {}; jsonData.amount = jsonForm.amount; + jsonData.exact_amount = (jsonForm.exact_amount == "on"); jsonData.spoiled = $('#spoiled').is(':checked'); if ($("#use_specific_stock_entry").is(":checked")) @@ -70,7 +71,7 @@ $("#use_specific_stock_entry").click(); } - if (productDetails.product.enable_tare_weight_handling == 1) + if (productDetails.product.enable_tare_weight_handling == 1 && !jsonData.exact_amount) { var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount - (parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount))) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + ''; } @@ -177,11 +178,11 @@ $('#save-mark-as-open-button').on('click', function(e) } ); }); - +var sumValue = 0; $("#location_id").on('change', function(e) { var locationId = $(e.target).val(); - var sumValue = 0; + sumValue = 0; var stockId = null; $("#specific_stock_entry").find("option").remove().end().append(""); @@ -228,37 +229,8 @@ $("#location_id").on('change', function(e) Grocy.Api.Get('stock/products/' + Grocy.Components.ProductPicker.GetValue(), function(productDetails) { - if (productDetails.product.enable_tare_weight_handling == 1) - { - $("#amount").attr("min", productDetails.product.tare_weight); - $('#amount').attr('max', sumValue + parseFloat(productDetails.product.tare_weight)); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', parseFloat(productDetails.product.tare_weight).toLocaleString(), (parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight)).toLocaleString())); - $("#tare-weight-handling-info").removeClass("d-none"); - } - else - { - $("#tare-weight-handling-info").addClass("d-none"); - - if (productDetails.product.allow_partial_units_in_stock == 1) - { - $("#amount").attr("min", "0.01"); - $("#amount").attr("step", "0.01"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', 0.01.toLocaleString(), parseFloat(productDetails.stock_amount).toLocaleString())); - } - else - { - $("#amount").attr("min", "1"); - $("#amount").attr("step", "1"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', "1", parseFloat(productDetails.stock_amount).toLocaleString())); - } - - $('#amount').attr('max', sumValue); - - if (sumValue == 0) - { - $("#amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location')); - } - } + current_productDetails = productDetails; + RefreshForm(); }, function(xhr) { @@ -447,7 +419,7 @@ $("#specific_stock_entry").on("change", function(e) { if ($(e.target).val() == "") { - var sumValue = 0; + sumValue = 0; Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries', function(stockEntries) { @@ -572,3 +544,49 @@ $("#scan-mode-button").on("click", function(e) $("#scan-mode-status").text(__t("off")); } }); + +$('#consume-exact-amount').on('change', RefreshForm); +var current_productDetails; +function RefreshForm() +{ + var productDetails = current_productDetails; + if (productDetails.product.enable_tare_weight_handling == 1) + { + $("#consume-exact-amount").parent().removeClass("d-none"); + } + else + { + $("#consume-exact-amount").parent().addClass("d-none"); + } + if (productDetails.product.enable_tare_weight_handling == 1 && !$('#consume-exact-amount').is(':checked')) + { + $("#amount").attr("min", productDetails.product.tare_weight); + $('#amount').attr('max', sumValue + parseFloat(productDetails.product.tare_weight)); + $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', parseFloat(productDetails.product.tare_weight).toLocaleString(), (parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight)).toLocaleString())); + $("#tare-weight-handling-info").removeClass("d-none"); + } + else + { + $("#tare-weight-handling-info").addClass("d-none"); + + if (productDetails.product.allow_partial_units_in_stock == 1) + { + $("#amount").attr("min", "0.01"); + $("#amount").attr("step", "0.01"); + $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', 0.01.toLocaleString(), parseFloat(productDetails.stock_amount).toLocaleString())); + } + else + { + $("#amount").attr("min", "1"); + $("#amount").attr("step", "1"); + $("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', "1", parseFloat(productDetails.stock_amount).toLocaleString())); + } + + $('#amount').attr('max', sumValue); + + if (sumValue == 0) + { + $("#amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location')); + } + } +} diff --git a/public/viewjs/recipeform.js b/public/viewjs/recipeform.js index 09501c47..a736b9f3 100644 --- a/public/viewjs/recipeform.js +++ b/public/viewjs/recipeform.js @@ -1,4 +1,4 @@ -function saveRecipePicture(result, location) +function saveRecipePicture(result, location, jsonData) { $recipeId = Grocy.EditObjectId || result.created_object_id; Grocy.Components.UserfieldsForm.Save(() => @@ -43,7 +43,7 @@ $('.save-recipe').on('click', function(e) { console.log(jsonData); Grocy.Api.Post('objects/recipes', jsonData, - (result) => saveRecipePicture(result, location)); + (result) => saveRecipePicture(result, location, jsonData)); return; } @@ -65,7 +65,7 @@ $('.save-recipe').on('click', function(e) } Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, jsonData, - (result) => saveRecipePicture(result, location), + (result) => saveRecipePicture(result, location, jsonData), function(xhr) { Grocy.FrontendHelpers.EndUiBusy("recipe-form"); diff --git a/public/viewjs/shoppinglistitemform.js b/public/viewjs/shoppinglistitemform.js index db706114..f08dae77 100644 --- a/public/viewjs/shoppinglistitemform.js +++ b/public/viewjs/shoppinglistitemform.js @@ -139,7 +139,7 @@ if (Grocy.EditMode === "edit") $('#amount').on('focus', function(e) { - if (Grocy.Components.ProductPicker.GetValue().length === 0) + if (Grocy.Components.ProductPicker.GetValue().length === 0 && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK) { Grocy.Components.ProductPicker.GetInputElement().focus(); } diff --git a/services/RecipesService.php b/services/RecipesService.php index 697ecde9..5e5e53bb 100644 --- a/services/RecipesService.php +++ b/services/RecipesService.php @@ -58,7 +58,7 @@ class RecipesService extends BaseService { if ($recipePosition->only_check_single_unit_in_stock == 0) { - $this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true); + $this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true, true); } } diff --git a/services/StockService.php b/services/StockService.php index 959f8554..b5a0ea49 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -210,7 +210,7 @@ class StockService extends BaseService $this->getDatabase()->shopping_list()->where('shopping_list_id = :1', $listId)->delete(); } - public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null, $allowSubproductSubstitution = false) + public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null, $allowSubproductSubstitution = false, $consumeExactAmount = false) { if (!$this->ProductExists($productId)) { @@ -230,6 +230,10 @@ class StockService extends BaseService if ($productDetails->product->enable_tare_weight_handling == 1) { + if($consumeExactAmount) + { + $amount = floatval($productDetails->stock_amount) + floatval($productDetails->product->tare_weight) - $amount; + } if ($amount < floatval($productDetails->product->tare_weight)) { throw new \Exception('The amount cannot be lower than the defined tare weight'); @@ -249,6 +253,11 @@ class StockService extends BaseService $potentialStockEntries = $this->GetProductStockEntriesForLocation($productId, $locationId, false, $allowSubproductSubstitution); } + if ($specificStockEntryId !== 'default') + { + $potentialStockEntries = FindAllObjectsInArrayByPropertyValue($potentialStockEntries, 'stock_id', $specificStockEntryId); + } + $productStockAmount = SumArrayValue($potentialStockEntries, 'amount'); if ($amount > $productStockAmount) @@ -256,11 +265,6 @@ class StockService extends BaseService throw new \Exception('Amount to be consumed cannot be > current stock amount (if supplied, at the desired location)'); } - if ($specificStockEntryId !== 'default') - { - $potentialStockEntries = FindAllObjectsInArrayByPropertyValue($potentialStockEntries, 'stock_id', $specificStockEntryId); - } - if ($transactionId === null) { $transactionId = uniqid(); diff --git a/views/components/locationpicker.blade.php b/views/components/locationpicker.blade.php index 36e5b678..bbe54cd7 100644 --- a/views/components/locationpicker.blade.php +++ b/views/components/locationpicker.blade.php @@ -6,9 +6,10 @@ @php if(empty($prefillById)) { $prefillById = ''; } @endphp @php if(!isset($isRequired)) { $isRequired = true; } @endphp @php if(empty($hint)) { $hint = ''; } @endphp +@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp