diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index 192fe5ca..54e0987a 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -35,9 +35,21 @@ class RecipesController extends BaseController } } + // Scale ingredients amount based on desired servings + foreach ($selectedRecipePositions as $selectedRecipePosition) + { + $selectedRecipePosition->amount = $selectedRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings); + } + $selectedRecipeSubRecipes = $this->Database->recipes()->where('id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('name')->fetchAll(); $selectedRecipeSubRecipesPositions = $this->Database->recipes_pos()->where('recipe_id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('ingredient_group')->fetchAll(); + // Scale ingredients amount based on desired servings + foreach ($selectedRecipeSubRecipesPositions as $selectedSubRecipePosition) + { + $selectedSubRecipePosition->amount = $selectedSubRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings); + } + return $this->AppContainer->view->render($response, 'recipes', [ 'recipes' => $recipes, 'recipesFulfillment' => $this->RecipesService->GetRecipesFulfillment(), diff --git a/migrations/0052.sql b/migrations/0052.sql new file mode 100644 index 00000000..884bbc63 --- /dev/null +++ b/migrations/0052.sql @@ -0,0 +1,62 @@ +ALTER TABLE recipes +ADD base_servings INTEGER DEFAULT 1; + +ALTER TABLE recipes +ADD desired_servings INTEGER DEFAULT 1; + +DROP VIEW recipes_fulfillment; +CREATE VIEW recipes_fulfillment +AS +SELECT + r.id AS recipe_id, + rp.id AS recipe_pos_id, + rp.product_id AS product_id, + rp.amount * (r.desired_servings / r.base_servings) AS recipe_amount, + IFNULL(sc.amount, 0) AS stock_amount, + CASE WHEN IFNULL(sc.amount, 0) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END THEN 1 ELSE 0 END AS need_fulfilled, + CASE WHEN IFNULL(sc.amount, 0) - CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END < 0 THEN ABS(IFNULL(sc.amount, 0) - (CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END)) ELSE 0 END AS missing_amount, + IFNULL(sl.amount, 0) * p.qu_factor_purchase_to_stock AS amount_on_shopping_list, + CASE WHEN IFNULL(sc.amount, 0) + (IFNULL(sl.amount, 0) * p.qu_factor_purchase_to_stock) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list, + rp.qu_id +FROM recipes r +JOIN recipes_pos rp + ON r.id = rp.recipe_id +JOIN products p + ON rp.product_id = p.id +LEFT JOIN ( + SELECT product_id, SUM(amount) AS amount + FROM shopping_list + GROUP BY product_id) sl + ON rp.product_id = sl.product_id +LEFT JOIN stock_current sc + ON rp.product_id = sc.product_id +WHERE rp.not_check_stock_fulfillment = 0 + +UNION + +-- Just add all recipe positions which should not be checked against stock with fulfilled need + +SELECT + r.id AS recipe_id, + rp.id AS recipe_pos_id, + rp.product_id AS product_id, + rp.amount * (r.desired_servings / r.base_servings) AS recipe_amount, + IFNULL(sc.amount, 0) AS stock_amount, + 1 AS need_fulfilled, + 0 AS missing_amount, + IFNULL(sl.amount, 0) * p.qu_factor_purchase_to_stock AS amount_on_shopping_list, + 1 AS need_fulfilled_with_shopping_list, + rp.qu_id +FROM recipes r +JOIN recipes_pos rp + ON r.id = rp.recipe_id +JOIN products p + ON rp.product_id = p.id +LEFT JOIN ( + SELECT product_id, SUM(amount) AS amount + FROM shopping_list + GROUP BY product_id) sl + ON rp.product_id = sl.product_id +LEFT JOIN stock_current sc + ON rp.product_id = sc.product_id +WHERE rp.not_check_stock_fulfillment = 1; diff --git a/public/viewjs/recipeform.js b/public/viewjs/recipeform.js index 0fc4acce..2460c817 100644 --- a/public/viewjs/recipeform.js +++ b/public/viewjs/recipeform.js @@ -110,7 +110,7 @@ $('#recipes-includes-table tbody').removeClass("d-none"); Grocy.FrontendHelpers.ValidateForm('recipe-form'); $("#name").focus(); -$('#recipe-form input').keyup(function (event) +$('#recipe-form input').keyup(function(event) { Grocy.FrontendHelpers.ValidateForm('recipe-form'); }); @@ -206,31 +206,6 @@ $(document).on('click', '.recipe-include-delete-button', function(e) }); }); -$(document).on('click', '.recipe-pos-order-missing-button', function(e) -{ - var productName = $(e.currentTarget).attr('data-product-name'); - var productId = $(e.currentTarget).attr('data-product-id'); - var productAmount = $(e.currentTarget).attr('data-product-amount'); - var recipeName = $(e.currentTarget).attr('data-recipe-name'); - - var jsonData = {}; - jsonData.product_id = productId; - jsonData.amount = productAmount; - jsonData.note = L('Added for recipe #1', recipeName); - - Grocy.Api.Post('objects/shopping_list', jsonData, - function(result) - { - Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(), function () { }, function () { }); - window.location.href = U('/recipe/' + Grocy.EditObjectId); - }, - function(xhr) - { - console.error(xhr); - } - ); -}); - $(document).on('click', '.recipe-pos-show-note-button', function(e) { var note = $(e.currentTarget).attr('data-recipe-pos-note'); diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index d0da75ec..3370db15 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -168,8 +168,34 @@ recipesTables.on('select', function(e, dt, type, indexes) $("#selectedRecipeToggleFullscreenButton").on('click', function(e) { + e.preventDefault(); + $("#selectedRecipeCard").toggleClass("fullscreen"); $("body").toggleClass("fullscreen-card"); $("#selectedRecipeCard .card-header").toggleClass("fixed-top"); $("#selectedRecipeCard .card-body").toggleClass("mt-5"); + + window.location.hash = "fullscreen"; }); + +$('#servings-scale').keyup(function(event) +{ + var data = { }; + data.desired_servings = $(this).val(); + + Grocy.Api.Put('objects/recipes/' + $(this).data("recipe-id"), data, + function(result) + { + window.location.reload(); + }, + function(xhr) + { + console.error(xhr); + } + ); +}); + +if (window.location.hash === "#fullscreen") +{ + $("#selectedRecipeToggleFullscreenButton").click(); +} diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 37d288cf..6152e185 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -52,6 +52,16 @@ + @php if($mode == 'edit') { $value = $recipe->base_servings; } else { $value = 1; } @endphp + @include('components.numberpicker', array( + 'id' => 'base_servings', + 'label' => 'Servings', + 'min' => 1, + 'value' => $value, + 'invalidFeedback' => $L('This cannot be lower than #1', '1'), + 'hint' => $L('The ingredients listed here result in this amount of servings') + )) +
@@ -75,20 +85,21 @@ {{ $L('Add') }} + - + @if($mode == "edit") @foreach($recipePositions as $recipePosition) - + + @@ -32,6 +33,9 @@ +
# {{ $L('Product') }} {{ $L('Amount') }}{{ $L('Note') }}{{ $L('Note') }} Hiden ingredient group
@@ -96,16 +107,12 @@ - - - {{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }} @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif {{ Pluralize($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} - @if(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->need_fulfilled == 1) {{ $L('Enough in stock') }} @else {{ $L('Not enough in stock, #1 missing, #2 already on shopping list', round(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->missing_amount), round(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->amount_on_shopping_list)) }} @endif diff --git a/views/recipes.blade.php b/views/recipes.blade.php index c869644a..5ef5f650 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -22,6 +22,7 @@
{{ $L('Name') }}{{ $L('Servings') }} {{ $L('Requirements fulfilled') }} Hidden status for sorting of "Requirements fulfilled" column
{{ $recipe->name }} + {{ $recipe->desired_servings }} + @if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)@else@endif @if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $L('Not enough in stock, #1 ingredients missing but already on the shopping list', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@endif @@ -67,6 +71,19 @@ +
+ @include('components.numberpicker', array( + 'id' => 'servings-scale', + 'label' => 'Servings', + 'min' => 1, + 'value' => $selectedRecipe->desired_servings, + 'invalidFeedback' => $L('This cannot be lower than #1', '1'), + 'additionalGroupCssClasses' => 'mb-0', + 'additionalCssClasses' => 'col-2', + 'additionalAttributes' => 'data-recipe-id="' . $selectedRecipe->id . '"' + )) +
+ @foreach($selectedRecipeSubRecipes as $selectedRecipeSubRecipe)