diff --git a/controllers/RecipesApiController.php b/controllers/RecipesApiController.php
index 719a5aae..23e349a2 100644
--- a/controllers/RecipesApiController.php
+++ b/controllers/RecipesApiController.php
@@ -16,13 +16,19 @@ class RecipesApiController extends BaseApiController
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
$excludedProductIds = null;
+ $ignoreStock = false;
if ($requestBody !== null && array_key_exists('excludedProductIds', $requestBody))
{
$excludedProductIds = $requestBody['excludedProductIds'];
}
- $this->getRecipesService()->AddNotFulfilledProductsToShoppingList($args['recipeId'], $excludedProductIds);
+ if ($requestBody !== null && array_key_exists('ignoreStock', $requestBody))
+ {
+ $ignoreStock = $requestBody['ignoreStock'];
+ }
+
+ $this->getRecipesService()->AddNotFulfilledProductsToShoppingList($args['recipeId'], $excludedProductIds, $ignoreStock);
return $this->EmptyApiResponse($response);
}
diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php
index f0c4305a..74ee5b29 100644
--- a/controllers/RecipesController.php
+++ b/controllers/RecipesController.php
@@ -105,10 +105,16 @@ class RecipesController extends BaseController
$totalCalories = FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->calories;
}
+ $recipePositionsResolved = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $selectedRecipe->id);
+ foreach ($recipePositionsResolved as $pos)
+ {
+ $pos["recipe_pos_data"] = $this->getDatabase()->recipes_pos($pos->recipe_pos_id);
+ }
+
$viewData = [
'recipes' => $recipes,
'recipesResolved' => $recipesResolved,
- 'recipePositionsResolved' => $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $selectedRecipe->id),
+ 'recipePositionsResolved' => $recipePositionsResolved,
'selectedRecipe' => $selectedRecipe,
'products' => $this->getDatabase()->products(),
'quantityUnits' => $this->getDatabase()->quantity_units(),
diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js
index 0ce9df68..d55b0386 100644
--- a/public/viewjs/recipes.js
+++ b/public/viewjs/recipes.js
@@ -190,9 +190,13 @@ $(document).on('click', '.recipe-shopping-list', function(e)
{
var objectName = $(e.currentTarget).attr('data-recipe-name');
var objectId = $(e.currentTarget).attr('data-recipe-id');
+ var popUpTemplate = $("#missing-recipe-pos-list")[0]; // save template html for later
+ var popUpHtml = popUpTemplate.outerHTML.replace("d-none", ""); // prepare visible html for current popup
+ var popUpTemplateParent = popUpTemplate.parentElement // remember where the template element was
+ $("#missing-recipe-pos-list").remove() // delete the template from the dom, as we are about to add it into the popup
bootbox.confirm({
- message: __t('Are you sure you want to put all missing ingredients for recipe "%s" on the shopping list?', objectName) + "
" + __t("Uncheck ingredients to not put them on the shopping list") + ":" + $("#missing-recipe-pos-list")[0].outerHTML.replace("d-none", ""),
+ message: __t('Are you sure you want to put all selected ingredients for recipe "%s" on the shopping list?', objectName) + "
" + __t("Uncheck ingredients to not put them on the shopping list") + ":" + popUpHtml,
closeButton: false,
buttons: {
confirm: {
@@ -211,12 +215,16 @@ $(document).on('click', '.recipe-shopping-list', function(e)
Grocy.FrontendHelpers.BeginUiBusy();
var excludedProductIds = new Array();
- $(".missing-recipe-pos-product-checkbox:checkbox:not(:checked)").each(function()
+ $(".missing-recipe-pos-product-checkbox").each(function()
{
- excludedProductIds.push($(this).data("product-id"));
+ if ($(this).data("ignore") || !$(this)[0].checked)
+ {
+ excludedProductIds.push($(this).data("product-id"));
+ }
});
+ var ignoreStock = $("#missing-recipe-pos-list-full-recipe")[0];
- Grocy.Api.Post('recipes/' + objectId + '/add-not-fulfilled-products-to-shoppinglist', { "excludedProductIds": excludedProductIds },
+ Grocy.Api.Post('recipes/' + objectId + '/add-not-fulfilled-products-to-shoppinglist', { "excludedProductIds": excludedProductIds, "ignoreStock": ignoreStock.checked },
function(result)
{
window.location.reload();
@@ -228,10 +236,28 @@ $(document).on('click', '.recipe-shopping-list', function(e)
}
);
}
+
+ popUpTemplateParent.append(popUpTemplate); // restore template, in case we don't reload the page and need the popup again
}
});
});
+$(document).on('click', '#missing-recipe-pos-list-select-missing', function(e)
+{
+ $(".missing-recipe-pos-product-checkbox").each(function()
+ {
+ $(this)[0].checked = !$(this).data("need-fulfilled");
+ });
+});
+
+$(document).on('click', '#missing-recipe-pos-list-select-all', function(e)
+{
+ $(".missing-recipe-pos-product-checkbox").each(function()
+ {
+ $(this)[0].checked = true;
+ });
+});
+
$(".recipe-consume").on('click', function(e)
{
var objectName = $(e.currentTarget).attr('data-recipe-name');
diff --git a/services/RecipesService.php b/services/RecipesService.php
index bdcfbf02..552e5f92 100644
--- a/services/RecipesService.php
+++ b/services/RecipesService.php
@@ -11,7 +11,7 @@ class RecipesService extends BaseService
const RECIPE_TYPE_MEALPLAN_SHADOW = 'mealplan-shadow'; // A recipe per meal plan recipe (for separated stock fulfillment checking) => name = YYYY-MM-DD#
const RECIPE_TYPE_NORMAL = 'normal'; // Normal / manually created recipes
- public function AddNotFulfilledProductsToShoppingList($recipeId, $excludedProductIds = null)
+ public function AddNotFulfilledProductsToShoppingList($recipeId, $excludedProductIds = null, $ignoreStock = false)
{
$recipe = $this->getDataBase()->recipes($recipeId);
$recipePositions = $this->GetRecipesPosResolved();
@@ -26,13 +26,26 @@ class RecipesService extends BaseService
if ($recipePosition->recipe_id == $recipeId && !in_array($recipePosition->product_id, $excludedProductIds))
{
$product = $this->getDataBase()->products($recipePosition->product_id);
- $toOrderAmount = round(($recipePosition->missing_amount - $recipePosition->amount_on_shopping_list), 2);
- $quId = $product->qu_id_purchase;
- if ($recipe->not_check_shoppinglist == 1)
- {
- $toOrderAmount = round($recipePosition->missing_amount, 2);
+ // Determine order amount
+ // First, define the base amount, depending on if the full recipe amount or just the missing amount shall be used
+ if ($ignoreStock) {
+ $toOrderAmount = $recipePosition->recipe_amount;
}
+ else {
+ $toOrderAmount = $recipePosition->missing_amount;
+ }
+
+ // Then, decide if on top of the base amount, the shopping cart shall be considered as well
+ if ($recipe->not_check_shoppinglist == 0)
+ {
+ $toOrderAmount = round($toOrderAmount - $recipePosition->amount_on_shopping_list, 2);
+ }
+ else {
+ $toOrderAmount = round($toOrderAmount, 2);
+ }
+
+ $quId = $product->qu_id_purchase;
// When the recipe ingredient option "Only check if any amount is in stock" is enabled,
// any QU can be used and the amount is not based on qu_stock then
diff --git a/views/recipes.blade.php b/views/recipes.blade.php
index fefb9edd..1e43ed52 100644
--- a/views/recipes.blade.php
+++ b/views/recipes.blade.php
@@ -28,6 +28,17 @@
column-count: 2;
}
}
+
+ .missing-recipe-pos-list-radio-buttons {
+ display: flex;
+ flex-direction: row;
+ gap: 1em;
+ margin: 1em 0;
+ }
+
+ .missing-recipe-pos-list-selection-buttons {
+ margin: 1em 0;
+ }
@endpush
@@ -340,7 +351,7 @@
data-recipe-name="{{ $recipe->name }}">
-
-
+