mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 20:36:15 +02:00
Include product_group in recipe ingredient list
This commit is contained in:
parent
49edd011b4
commit
ad7426d793
|
|
@ -108,6 +108,9 @@ DefaultUserSetting('batteries_due_soon_days', 5);
|
|||
# Tasks settings
|
||||
DefaultUserSetting('tasks_due_soon_days', 5);
|
||||
|
||||
# Recipe settings
|
||||
DefaultUserSetting('recipe_ingredient_display_product_group', false); // Display the product group information in the ingredient list
|
||||
|
||||
# If the page should be automatically reloaded when there was
|
||||
# an external change
|
||||
DefaultUserSetting('auto_reload_on_db_change', true);
|
||||
|
|
|
|||
|
|
@ -37,20 +37,20 @@ class RecipesController extends BaseController
|
|||
if (isset($request->getQueryParams()['recipe']))
|
||||
{
|
||||
$selectedRecipe = $this->Database->recipes($request->getQueryParams()['recipe']);
|
||||
$selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $request->getQueryParams()['recipe'])->orderBy('ingredient_group');
|
||||
$selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $request->getQueryParams()['recipe'])->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC');
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($recipes as $recipe)
|
||||
{
|
||||
$selectedRecipe = $recipe;
|
||||
$selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $recipe->id)->orderBy('ingredient_group');
|
||||
$selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $recipe->id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$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_resolved()->where('recipe_id = :1', $selectedRecipe->id)->orderBy('ingredient_group')->fetchAll();
|
||||
$selectedRecipeSubRecipesPositions = $this->Database->recipes_pos_resolved()->where('recipe_id = :1', $selectedRecipe->id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC')->fetchAll();
|
||||
|
||||
$includedRecipeIdsAbsolute = array();
|
||||
$includedRecipeIdsAbsolute[] = $selectedRecipe->id;
|
||||
|
|
@ -132,6 +132,11 @@ class RecipesController extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
public function RecipesSettings(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'recipessettings');
|
||||
}
|
||||
|
||||
public function MealPlan(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$recipes = $this->Database->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll();
|
||||
|
|
|
|||
96
migrations/0098.sql
Normal file
96
migrations/0098.sql
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
DROP VIEW recipes_pos_resolved;
|
||||
CREATE VIEW recipes_pos_resolved
|
||||
AS
|
||||
|
||||
-- Multiplication by 1.0 to force conversion to float (REAL)
|
||||
|
||||
SELECT
|
||||
r.id AS recipe_id,
|
||||
rp.id AS recipe_pos_id,
|
||||
rp.product_id AS product_id,
|
||||
rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) AS recipe_amount,
|
||||
IFNULL(sc.amount_aggregated, 0) AS stock_amount,
|
||||
CASE WHEN IFNULL(sc.amount_aggregated, 0) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) END THEN 1 ELSE 0 END AS need_fulfilled,
|
||||
CASE WHEN IFNULL(sc.amount_aggregated, 0) - CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) END < 0 THEN ABS(IFNULL(sc.amount_aggregated, 0) - (CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) 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_aggregated, 0) + (CASE WHEN r.not_check_shoppinglist = 1 THEN 0 ELSE IFNULL(sl.amount, 0) END * p.qu_factor_purchase_to_stock) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) END THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list,
|
||||
rp.qu_id,
|
||||
(CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) END / p.qu_factor_purchase_to_stock) * pcp.last_price * rp.price_factor AS costs,
|
||||
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
|
||||
rp.ingredient_group,
|
||||
pg.name as product_group,
|
||||
rp.id, -- Just a dummy id column
|
||||
rnr.includes_recipe_id as child_recipe_id,
|
||||
rp.note,
|
||||
rp.variable_amount AS recipe_variable_amount,
|
||||
rp.only_check_single_unit_in_stock,
|
||||
rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories
|
||||
FROM recipes r
|
||||
JOIN recipes_nestings_resolved rnr
|
||||
ON r.id = rnr.recipe_id
|
||||
JOIN recipes rnrr
|
||||
ON rnr.includes_recipe_id = rnrr.id
|
||||
JOIN recipes_pos rp
|
||||
ON rnr.includes_recipe_id = rp.recipe_id
|
||||
JOIN products p
|
||||
ON rp.product_id = p.id
|
||||
JOIN product_groups pg
|
||||
ON p.product_group_id = pg.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
|
||||
LEFT JOIN products_current_price pcp
|
||||
ON rp.product_id = pcp.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*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) AS recipe_amount,
|
||||
IFNULL(sc.amount_aggregated, 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,
|
||||
(CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) END / p.qu_factor_purchase_to_stock) * pcp.last_price * rp.price_factor AS costs,
|
||||
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
|
||||
rp.ingredient_group,
|
||||
pg.name as product_group,
|
||||
rp.id, -- Just a dummy id column
|
||||
rnr.includes_recipe_id as child_recipe_id,
|
||||
rp.note,
|
||||
rp.variable_amount AS recipe_variable_amount,
|
||||
rp.only_check_single_unit_in_stock,
|
||||
rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories
|
||||
FROM recipes r
|
||||
JOIN recipes_nestings_resolved rnr
|
||||
ON r.id = rnr.recipe_id
|
||||
JOIN recipes rnrr
|
||||
ON rnr.includes_recipe_id = rnrr.id
|
||||
JOIN recipes_pos rp
|
||||
ON rnr.includes_recipe_id = rp.recipe_id
|
||||
JOIN products p
|
||||
ON rp.product_id = p.id
|
||||
JOIN product_groups pg
|
||||
ON p.product_group_id = pg.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
|
||||
LEFT JOIN products_current_price pcp
|
||||
ON rp.product_id = pcp.product_id
|
||||
WHERE rp.not_check_stock_fulfillment = 1;
|
||||
4
public/viewjs/recipessettings.js
Normal file
4
public/viewjs/recipessettings.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
if (BoolVal(Grocy.UserSettings.recipe_ingredient_display_product_group))
|
||||
{
|
||||
$("#recipe_ingredient_display_product_group").prop("checked", true);
|
||||
}
|
||||
|
|
@ -70,6 +70,7 @@ $app->group('', function()
|
|||
$this->get('/recipe/{recipeId}', '\Grocy\Controllers\RecipesController:RecipeEditForm');
|
||||
$this->get('/recipe/{recipeId}/pos/{recipePosId}', '\Grocy\Controllers\RecipesController:RecipePosEditForm');
|
||||
$this->get('/mealplan', '\Grocy\Controllers\RecipesController:MealPlan');
|
||||
$this->get('/recipessettings', '\Grocy\Controllers\RecipesController:RecipesSettings');
|
||||
}
|
||||
|
||||
// Chore routes
|
||||
|
|
|
|||
|
|
@ -404,6 +404,9 @@
|
|||
@if(GROCY_FEATURE_FLAG_TASKS)
|
||||
<a class="dropdown-item discrete-link" href="{{ $U('/taskssettings') }}"><i class="fas fa-tasks"></i> {{ $__t('Tasks settings') }}</a>
|
||||
@endif
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item discrete-link" href="{{ $U('/recipessettings') }}"><i class="fas fa-cocktail"></i> {{ $__t('Recipes settings') }}</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item discrete-link" href="{{ $U('/users') }}"><i class="fas fa-users"></i> {{ $__t('Manage users') }}</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
|
|
|||
|
|
@ -195,12 +195,16 @@
|
|||
@if(count($selectedRecipeSubRecipePositionsFiltered) > 0)
|
||||
<h5 class="mb-0">{{ $__t('Ingredients') }}</h5>
|
||||
<ul class="list-group list-group-flush">
|
||||
@php $lastGroup = 'undefined'; @endphp
|
||||
@php $lastIngredientGroup = 'undefined'; @endphp
|
||||
@php $lastProductGroup = 'undefined'; @endphp
|
||||
@foreach($selectedRecipeSubRecipePositionsFiltered as $selectedRecipePosition)
|
||||
@if($lastGroup != $selectedRecipePosition->ingredient_group)
|
||||
<h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
|
||||
@if($lastIngredientGroup != $selectedRecipePosition->ingredient_group)
|
||||
<h5 class="mb-2 mt-2 ml-2"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
|
||||
@endif
|
||||
<li class="list-group-item px-0">
|
||||
@if(boolval($userSettings['recipe_ingredient_display_product_group']) && $lastProductGroup != $selectedRecipePosition->product_group)
|
||||
<h6 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->product_group }}</strong></h6>
|
||||
@endif
|
||||
<li class="list-group-item px-0 ml-5">
|
||||
@php
|
||||
$product = FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
|
||||
|
|
@ -228,7 +232,8 @@
|
|||
<div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div>
|
||||
@endif
|
||||
</li>
|
||||
@php $lastGroup = $selectedRecipePosition->ingredient_group; @endphp
|
||||
@php $lastProductGroup = $selectedRecipePosition->product_group; @endphp
|
||||
@php $lastIngredientGroup = $selectedRecipePosition->ingredient_group; @endphp
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
|
@ -247,12 +252,16 @@
|
|||
@if($selectedRecipePositionsResolved->count() > 0)
|
||||
<h5 class="mb-0">{{ $__t('Ingredients') }}</h5>
|
||||
<ul class="list-group list-group-flush">
|
||||
@php $lastGroup = 'undefined'; @endphp
|
||||
@php $lastIngredientGroup = 'undefined'; @endphp
|
||||
@php $lastProductGroup = 'undefined'; @endphp
|
||||
@foreach($selectedRecipePositionsResolved as $selectedRecipePosition)
|
||||
@if($lastGroup != $selectedRecipePosition->ingredient_group)
|
||||
<h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
|
||||
@if($lastIngredientGroup != $selectedRecipePosition->ingredient_group)
|
||||
<h5 class="mb-2 mt-2 ml-2"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
|
||||
@endif
|
||||
<li class="list-group-item px-0">
|
||||
@if(boolval($userSettings['recipe_ingredient_display_product_group']) && $lastProductGroup != $selectedRecipePosition->product_group)
|
||||
<h6 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->product_group }}</strong></h6>
|
||||
@endif
|
||||
<li class="list-group-item px-0 ml-5">
|
||||
@php
|
||||
$product = FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
|
||||
|
|
@ -280,7 +289,8 @@
|
|||
<div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div>
|
||||
@endif
|
||||
</li>
|
||||
@php $lastGroup = $selectedRecipePosition->ingredient_group; @endphp
|
||||
@php $lastProductGroup = $selectedRecipePosition->product_group; @endphp
|
||||
@php $lastIngredientGroup = $selectedRecipePosition->ingredient_group; @endphp
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
|
|
|||
24
views/recipessettings.blade.php
Normal file
24
views/recipessettings.blade.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title', $__t('Recipes settings'))
|
||||
|
||||
@section('viewJsName', 'recipessettings')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-xs-12">
|
||||
<h1>@yield('title')</h1>
|
||||
|
||||
<h4 class="mt-2">{{ $__t('Recipe card ingredients') }}</h4>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="recipe_ingredient_display_product_group">
|
||||
<input type="checkbox" class="user-setting-control" id="recipe_ingredient_display_product_group" name="recipe_ingredient_display_product_group" data-setting-key="recipe_ingredient_display_product_group"> {{ $__t('In the recipe card ingredients, display product groups') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ $U('/recipes') }}" class="btn btn-success">{{ $__t('OK') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
Loading…
Reference in New Issue
Block a user