From 23d7b6ad3cb94b85b3dd0247618e33953782889c Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sat, 18 Jan 2025 10:23:31 +0100 Subject: [PATCH] Allow partially in stock recipes to be consumed (closes #386) --- changelog/77_UNRELEASED_xxxx-xx-xx.md | 1 + localization/strings.pot | 5 ++++- public/viewjs/mealplan.js | 21 +++++---------------- public/viewjs/recipes.js | 5 +++-- services/RecipesService.php | 16 ++++++++-------- views/recipes.blade.php | 6 +++--- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/changelog/77_UNRELEASED_xxxx-xx-xx.md b/changelog/77_UNRELEASED_xxxx-xx-xx.md index e85acb05..42f64465 100644 --- a/changelog/77_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/77_UNRELEASED_xxxx-xx-xx.md @@ -37,6 +37,7 @@ ### Recipes +- Consuming a recipe is now also possible when not all needed ingredients are currently in stock (the in stock amount, if any, of the corresponding ingredient will be consumed in that case) - For in-stock ingredients, the amount actually in-stock is now displayed next to the hint "Enough in stock" - Optimized that when adding missing recipe ingredients with the option "Only check if any amount is in stock" enabled to the shopping list and when no corresponding unit conversion exists, the amount/unit is now taken "as is" (as defined in the recipe ingredient) into the created shopping list item - When no price information is available for at least one ingredient, a red exclamation mark is now displayed next to the recipe total cost information diff --git a/localization/strings.pot b/localization/strings.pot index 34235b3c..07e2c883 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -585,7 +585,7 @@ msgstr "" msgid "Are you sure you want to consume all ingredients needed by recipe \"%s\" (ingredients marked with \"only check if any amount is in stock\" will be ignored)?" msgstr "" -msgid "Removed all ingredients of recipe \"%s\" from stock" +msgid "Removed all in stock ingredients needed by recipe \"%s\" from stock" msgstr "" msgid "Consume all ingredients needed by this recipe" @@ -2455,3 +2455,6 @@ msgstr "" msgid "No price information is available for at least one ingredient" msgstr "" + +msgid "For ingredients that are only partially in stock, the in stock amount will be consumed." +msgstr "" diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index fada7fe1..7bd566f8 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -98,19 +98,13 @@ $(".calendar").each(function() weekRecipeOrderMissingButtonDisabledClasses = "disabled"; } - var weekRecipeConsumeButtonDisabledClasses = ""; - if (weekRecipeResolved.need_fulfilled == 0 || weekCosts == 0) - { - weekRecipeConsumeButtonDisabledClasses = "disabled"; - } - var weekRecipeOrderMissingButtonHtml = ""; if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_SHOPPINGLIST) { weekRecipeOrderMissingButtonHtml = ''; } - weekRecipeConsumeButtonHtml = '' + weekRecipeConsumeButtonHtml = '' } $(".calendar[data-primary-section='true'] .fc-header-toolbar .fc-center").html("

" + weekCostsHtml + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "

"); }, @@ -157,12 +151,6 @@ $(".calendar").each(function() recipeOrderMissingButtonDisabledClasses = "disabled"; } - var recipeConsumeButtonDisabledClasses = ""; - if (resolvedRecipe.need_fulfilled == 0) - { - recipeConsumeButtonDisabledClasses = "disabled"; - } - var fulfillmentInfoHtml = __t('Enough in stock'); var fulfillmentIconHtml = ''; if (resolvedRecipe.need_fulfilled != 1) @@ -201,7 +189,7 @@ $(".calendar").each(function()
\ \ \ - \ + \ ' + shoppingListButtonHtml + ' \ ' + doneButtonHtml + ' \
\ @@ -865,7 +853,8 @@ $(document).on('click', '.recipe-consume-button', function(e) var mealPlanEntryId = $(e.currentTarget).attr('data-mealplan-entry-id'); bootbox.confirm({ - message: __t('Are you sure you want to consume all ingredients needed by recipe "%s" (ingredients marked with "only check if any amount is in stock" will be ignored)?', objectName), + message: __t('Are you sure you want to consume all ingredients needed by recipe "%s" (ingredients marked with "only check if any amount is in stock" will be ignored)?', objectName) + + "

(" + __t("For ingredients that are only partially in stock, the in stock amount will be consumed.") + ")", closeButton: false, buttons: { confirm: { @@ -890,7 +879,7 @@ $(document).on('click', '.recipe-consume-button', function(e) function(result) { Grocy.FrontendHelpers.EndUiBusy(); - toastr.success(__t('Removed all ingredients of recipe "%s" from stock', objectName)); + toastr.success(__t('Removed all in stock ingredients needed by recipe \"%s\" from stock', objectName)); window.location.reload(); }, function(xhr) diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index ec4ba11a..bb7f2480 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -238,7 +238,8 @@ $(".recipe-consume").on('click', function(e) var objectId = $(e.currentTarget).attr('data-recipe-id'); bootbox.confirm({ - message: __t('Are you sure you want to consume all ingredients needed by recipe "%s" (ingredients marked with "only check if any amount is in stock" will be ignored)?', objectName), + message: __t('Are you sure you want to consume all ingredients needed by recipe "%s" (ingredients marked with "only check if any amount is in stock" will be ignored)?', objectName) + + "

(" + __t("For ingredients that are only partially in stock, the in stock amount will be consumed.") + ")", closeButton: false, buttons: { confirm: { @@ -260,7 +261,7 @@ $(".recipe-consume").on('click', function(e) function(result) { Grocy.FrontendHelpers.EndUiBusy(); - toastr.success(__t('Removed all ingredients of recipe "%s" from stock', objectName)); + toastr.success(__t('Removed all in stock ingredients needed by recipe \"%s\" from stock', objectName)); }, function(xhr) { diff --git a/services/RecipesService.php b/services/RecipesService.php index 4e2b36c4..3583216b 100644 --- a/services/RecipesService.php +++ b/services/RecipesService.php @@ -78,12 +78,6 @@ class RecipesService extends BaseService throw new \Exception('Recipe does not exist'); } - $recipeResolved = $this->getDatabase()->recipes_resolved()->where('recipe_id', $recipeId)->fetch(); - if ($recipeResolved->need_fulfilled == 0) - { - throw new \Exception('Recipe need is not fulfilled, consuming not possible'); - } - $transactionId = uniqid(); $recipePositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll(); @@ -92,9 +86,15 @@ class RecipesService extends BaseService { foreach ($recipePositions as $recipePosition) { - if ($recipePosition->only_check_single_unit_in_stock == 0) + if ($recipePosition->only_check_single_unit_in_stock == 0 && $recipePosition->stock_amount > 0) { - $this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true, true); + $amount = $recipePosition->recipe_amount; + if ($recipePosition->stock_amount > 0 && $recipePosition->stock_amount < $recipePosition->recipe_amount) + { + $amount = $recipePosition->stock_amount; + } + + $this->getStockService()->ConsumeProduct($recipePosition->product_id, $amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true, true); } } } diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 9fa56cfd..a0b334fc 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -333,7 +333,7 @@

{{ $recipe->name }}