Implemented that included recipes can have a serving amount (closes #163 and references #127)

This commit is contained in:
Bernd Bestel 2019-03-05 23:45:04 +01:00
parent 45db9ff90c
commit 2d8ad24887
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
7 changed files with 174 additions and 62 deletions

View File

@ -17,40 +17,27 @@ class RecipesController extends BaseController
public function Overview(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function Overview(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{ {
$recipes = $this->Database->recipes()->orderBy('name'); $recipes = $this->Database->recipes()->orderBy('name');
$recipesResolved = $this->RecipesService->GetRecipesResolved();
$selectedRecipe = null; $selectedRecipe = null;
$selectedRecipePositions = null; $selectedRecipePositionsResolved = null;
if (isset($request->getQueryParams()['recipe'])) if (isset($request->getQueryParams()['recipe']))
{ {
$selectedRecipe = $this->Database->recipes($request->getQueryParams()['recipe']); $selectedRecipe = $this->Database->recipes($request->getQueryParams()['recipe']);
$selectedRecipePositions = $this->Database->recipes_pos()->where('recipe_id', $request->getQueryParams()['recipe'])->orderBy('ingredient_group'); $selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id', $request->getQueryParams()['recipe'])->orderBy('ingredient_group');
} }
else else
{ {
foreach ($recipes as $recipe) foreach ($recipes as $recipe)
{ {
$selectedRecipe = $recipe; $selectedRecipe = $recipe;
$selectedRecipePositions = $this->Database->recipes_pos()->where('recipe_id', $recipe->id)->orderBy('ingredient_group'); $selectedRecipePositionsResolved = $this->Database->recipes_pos_resolved()->where('recipe_id', $recipe->id)->orderBy('ingredient_group');
break; 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(); $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(); $selectedRecipeSubRecipesPositions = $this->Database->recipes_pos_resolved()->where('recipe_id = :1', $selectedRecipe->id)->orderBy('ingredient_group')->fetchAll();
// Scale ingredients amount based on desired servings & calculate total costs
$recipesFulfillment = $this->RecipesService->GetRecipesFulfillment();
$totalRecipeCosts = 0;
foreach ($selectedRecipePositions as $selectedRecipePosition)
{
$selectedRecipePosition->amount = $selectedRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings);
$totalRecipeCosts += FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->costs;
}
foreach ($selectedRecipeSubRecipesPositions as $selectedSubRecipePosition)
{
$selectedSubRecipePosition->amount = $selectedSubRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings);
$totalRecipeCosts += FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedSubRecipePosition->id)->costs;
}
$includedRecipeIdsAbsolute = array(); $includedRecipeIdsAbsolute = array();
$includedRecipeIdsAbsolute[] = $selectedRecipe->id; $includedRecipeIdsAbsolute[] = $selectedRecipe->id;
@ -61,16 +48,16 @@ class RecipesController extends BaseController
return $this->AppContainer->view->render($response, 'recipes', [ return $this->AppContainer->view->render($response, 'recipes', [
'recipes' => $recipes, 'recipes' => $recipes,
'recipesFulfillment' => $recipesFulfillment, 'recipesResolved' => $recipesResolved,
'recipesSumFulfillment' => $this->RecipesService->GetRecipesSumFulfillment(), 'recipePositionsResolved' => $this->Database->recipes_pos_resolved(),
'selectedRecipe' => $selectedRecipe, 'selectedRecipe' => $selectedRecipe,
'selectedRecipePositions' => $selectedRecipePositions, 'selectedRecipePositionsResolved' => $selectedRecipePositionsResolved,
'products' => $this->Database->products(), 'products' => $this->Database->products(),
'quantityunits' => $this->Database->quantity_units(), 'quantityunits' => $this->Database->quantity_units(),
'selectedRecipeSubRecipes' => $selectedRecipeSubRecipes, 'selectedRecipeSubRecipes' => $selectedRecipeSubRecipes,
'selectedRecipeSubRecipesPositions' => $selectedRecipeSubRecipesPositions, 'selectedRecipeSubRecipesPositions' => $selectedRecipeSubRecipesPositions,
'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute, 'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute,
'totalRecipeCosts' => $totalRecipeCosts 'selectedRecipeTotalCosts' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->costs
]); ]);
} }
@ -93,8 +80,8 @@ class RecipesController extends BaseController
'mode' => 'edit', 'mode' => 'edit',
'products' => $this->Database->products(), 'products' => $this->Database->products(),
'quantityunits' => $this->Database->quantity_units(), 'quantityunits' => $this->Database->quantity_units(),
'recipesFulfillment' => $this->RecipesService->GetRecipesFulfillment(), 'recipePositionsResolved' => $this->RecipesService->GetRecipesPosResolved(),
'recipesSumFulfillment' => $this->RecipesService->GetRecipesSumFulfillment(), 'recipesResolved' => $this->RecipesService->GetRecipesResolved(),
'recipes' => $this->Database->recipes()->orderBy('name'), 'recipes' => $this->Database->recipes()->orderBy('name'),
'recipeNestings' => $this->Database->recipes_nestings()->where('recipe_id', $recipeId) 'recipeNestings' => $this->Database->recipes_nestings()->where('recipe_id', $recipeId)
]); ]);

View File

@ -344,5 +344,6 @@ return array(
'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated' => 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated', 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated' => 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated',
'You have to select a location' => 'You have to select a location', 'You have to select a location' => 'You have to select a location',
'List' => 'List', 'List' => 'List',
'Gallery' => 'Gallery' 'Gallery' => 'Gallery',
'The current picture will be deleted when you save the recipe' => 'The current picture will be deleted when you save the recipe'
); );

109
migrations/0058.sql Normal file
View File

@ -0,0 +1,109 @@
ALTER TABLE recipes_nestings
ADD servings INTEGER DEFAULT 1;
DROP VIEW recipes_nestings_resolved;
CREATE VIEW recipes_nestings_resolved
AS
WITH RECURSIVE r1(recipe_id, includes_recipe_id, includes_servings)
AS (
SELECT id, id, 1
FROM recipes
UNION ALL
SELECT rn.recipe_id, r1.includes_recipe_id, rn.servings
FROM recipes_nestings rn, r1 r1
WHERE rn.includes_recipe_id = r1.recipe_id
LIMIT 100 -- This is just a safety limit to prevent infinite loops due to infinite nested recipes
)
SELECT *
FROM r1;
DROP VIEW recipes_fulfillment;
CREATE VIEW recipes_pos_resolved
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) * rnr.includes_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) * rnr.includes_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) * rnr.includes_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) * rnr.includes_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) + (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 IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) * rnr.includes_servings 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 / r.base_servings) * rnr.includes_servings END / p.qu_factor_purchase_to_stock) * pcp.last_price AS costs,
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
rp.ingredient_group,
rp.id, -- Just a dummy id column
rnr.includes_recipe_id as child_recipe_id
FROM recipes r
JOIN recipes_nestings_resolved rnr
ON r.id = rnr.recipe_id
JOIN recipes_pos rp
ON rnr.includes_recipe_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
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 / r.base_servings) * rnr.includes_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,
(CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings / r.base_servings) * rnr.includes_servings END / p.qu_factor_purchase_to_stock) * pcp.last_price AS costs,
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
rp.ingredient_group,
rp.id, -- Just a dummy id column
rnr.includes_recipe_id as child_recipe_id
FROM recipes r
JOIN recipes_nestings_resolved rnr
ON r.id = rnr.recipe_id
JOIN recipes_pos rp
ON rnr.includes_recipe_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
LEFT JOIN products_current_price pcp
ON rp.product_id = pcp.product_id
WHERE rp.not_check_stock_fulfillment = 1;
DROP VIEW recipes_fulfillment_sum;
CREATE VIEW recipes_resolved
AS
SELECT
r.id AS recipe_id,
IFNULL(MIN(rpr.need_fulfilled), 1) AS need_fulfilled,
IFNULL(MIN(rpr.need_fulfilled_with_shopping_list), 1) AS need_fulfilled_with_shopping_list,
(SELECT COUNT(*) FROM recipes_pos_resolved WHERE recipe_id = r.id AND need_fulfilled = 0) AS missing_products_count,
SUM(rpr.costs) AS costs
FROM recipes r
LEFT JOIN recipes_pos_resolved rpr
ON r.id = rpr.recipe_id
GROUP BY r.id;

View File

@ -235,14 +235,16 @@ $(document).on('click', '.recipe-include-edit-button', function (e)
{ {
var id = $(e.currentTarget).attr('data-recipe-include-id'); var id = $(e.currentTarget).attr('data-recipe-include-id');
var recipeId = $(e.currentTarget).attr('data-recipe-included-recipe-id'); var recipeId = $(e.currentTarget).attr('data-recipe-included-recipe-id');
console.log(recipeId); var recipeServings = $(e.currentTarget).attr('data-recipe-included-recipe-servings');
Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(), Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(),
function(result) function(result)
{ {
$("#recipe-include-editform-title").text(L("Edit included recipe")); $("#recipe-include-editform-title").text(L("Edit included recipe"));
$("#recipe-include-form").data("edit-mode", "edit"); $("#recipe-include-form").data("edit-mode", "edit");
$("#recipe-include-form").data("recipe-nesting-id", id); $("#recipe-include-form").data("recipe-nesting-id", id);
$("#includes_recipe_id").val(recipeId); Grocy.Components.RecipePicker.SetId(recipeId);
$("#includes_servings").val(recipeServings);
$("#recipe-include-editform-modal").modal("show"); $("#recipe-include-editform-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("recipe-include-form"); Grocy.FrontendHelpers.ValidateForm("recipe-include-form");
}, },
@ -274,7 +276,7 @@ $("#recipe-include-add-button").on("click", function(e)
{ {
$("#recipe-include-editform-title").text(L("Add included recipe")); $("#recipe-include-editform-title").text(L("Add included recipe"));
$("#recipe-include-form").data("edit-mode", "create"); $("#recipe-include-form").data("edit-mode", "create");
$("#includes_recipe_id").val(""); Grocy.Components.RecipePicker.Clear();
$("#recipe-include-editform-modal").modal("show"); $("#recipe-include-editform-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("recipe-include-form"); Grocy.FrontendHelpers.ValidateForm("recipe-include-form");
}, },
@ -289,10 +291,17 @@ $('#save-recipe-include-button').on('click', function(e)
{ {
e.preventDefault(); e.preventDefault();
if (document.getElementById("recipe-include-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
var nestingId = $("#recipe-include-form").data("recipe-nesting-id"); var nestingId = $("#recipe-include-form").data("recipe-nesting-id");
var editMode = $("#recipe-include-form").data("edit-mode"); var editMode = $("#recipe-include-form").data("edit-mode");
var jsonData = $('#recipe-include-form').serializeJSON(); var jsonData = {};
jsonData.includes_recipe_id = Grocy.Components.RecipePicker.GetValue();
jsonData.servings = $("#includes_servings").val();
jsonData.recipe_id = Grocy.EditObjectId; jsonData.recipe_id = Grocy.EditObjectId;
if (editMode === 'create') if (editMode === 'create')

View File

@ -14,15 +14,15 @@ class RecipesService extends BaseService
protected $StockService; protected $StockService;
public function GetRecipesFulfillment() public function GetRecipesPosResolved()
{ {
$sql = 'SELECT * from recipes_fulfillment'; $sql = 'SELECT * FROM recipes_pos_resolved';
return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
} }
public function GetRecipesSumFulfillment() public function GetRecipesResolved()
{ {
$sql = 'SELECT * from recipes_fulfillment_sum'; $sql = 'SELECT * FROM recipes_resolved';
return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
} }
@ -30,7 +30,7 @@ class RecipesService extends BaseService
{ {
$recipe = $this->Database->recipes($recipeId); $recipe = $this->Database->recipes($recipeId);
$recipePositions = $this->GetRecipesFulfillment(); $recipePositions = $this->GetRecipesPosResolved();
foreach ($recipePositions as $recipePosition) foreach ($recipePositions as $recipePosition)
{ {
if($recipePosition->recipe_id == $recipeId && !in_array($recipePosition->product_id, $excludedProductIds)) if($recipePosition->recipe_id == $recipeId && !in_array($recipePosition->product_id, $excludedProductIds))

View File

@ -154,6 +154,7 @@
<tr> <tr>
<th>#</th> <th>#</th>
<th>{{ $L('Recipe') }}</th> <th>{{ $L('Recipe') }}</th>
<th>{{ $L('Servings') }}</th>
</tr> </tr>
</thead> </thead>
<tbody class="d-none"> <tbody class="d-none">
@ -161,7 +162,7 @@
@foreach($recipeNestings as $recipeNesting) @foreach($recipeNestings as $recipeNesting)
<tr> <tr>
<td class="fit-content"> <td class="fit-content">
<a class="btn btn-sm btn-info recipe-include-edit-button" href="#" data-recipe-include-id="{{ $recipeNesting->id }}" data-recipe-included-recipe-id="{{ $recipeNesting->includes_recipe_id }}"> <a class="btn btn-sm btn-info recipe-include-edit-button" href="#" data-recipe-include-id="{{ $recipeNesting->id }}" data-recipe-included-recipe-id="{{ $recipeNesting->includes_recipe_id }}" data-recipe-included-recipe-servings="{{ $recipeNesting->servings }}">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</a> </a>
<a class="btn btn-sm btn-danger recipe-include-delete-button" href="#" data-recipe-include-id="{{ $recipeNesting->id }}" data-recipe-include-name="{{ FindObjectInArrayByPropertyValue($recipes, 'id', $recipeNesting->includes_recipe_id)->name }}"> <a class="btn btn-sm btn-danger recipe-include-delete-button" href="#" data-recipe-include-id="{{ $recipeNesting->id }}" data-recipe-include-name="{{ FindObjectInArrayByPropertyValue($recipes, 'id', $recipeNesting->includes_recipe_id)->name }}">
@ -171,6 +172,9 @@
<td> <td>
{{ FindObjectInArrayByPropertyValue($recipes, 'id', $recipeNesting->includes_recipe_id)->name }} {{ FindObjectInArrayByPropertyValue($recipes, 'id', $recipeNesting->includes_recipe_id)->name }}
</td> </td>
<td>
{{ $recipeNesting->servings }}
</td>
</tr> </tr>
@endforeach @endforeach
@endif @endif
@ -202,18 +206,20 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="recipe-include-form" novalidate> <form id="recipe-include-form" novalidate>
<div class="form-group">
<label for="includes_recipe_id">{{ $L('Recipe') }}</label> @include('components.recipepicker', array(
<select required class="form-control" id="includes_recipe_id" name="includes_recipe_id"> 'recipes' => $recipes,
<option></option> 'isRequired' => true
@foreach($recipes as $recipeForList) ))
@if($recipeForList->id !== $recipe->id)
<option data-already-included="{{ BoolToString(FindObjectInArrayByPropertyValue($recipeNestings, 'includes_recipe_id', $recipeForList->id) === null) }}" value="{{ $recipeForList->id }}">{{ $recipeForList->name }}</option> @include('components.numberpicker', array(
@endif 'id' => 'includes_servings',
@endforeach 'label' => 'Servings',
</select> 'min' => 1,
<div class="invalid-feedback">{{ $L('A recipe is required') }}</div> 'value' => '1',
</div> 'invalidFeedback' => $L('This cannot be lower than #1', '1')
))
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -49,11 +49,11 @@
{{ $recipe->desired_servings }} {{ $recipe->desired_servings }}
</td> </td>
<td> <td>
@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
<span class="timeago-contextual">@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</span> <span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, '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($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count) }}@endif</span>
</td> </td>
<td class="d-none"> <td class="d-none">
{{ FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count }} {{ FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count }}
</td> </td>
</tr> </tr>
@endforeach @endforeach
@ -73,8 +73,8 @@
<div class="card-body text-center"> <div class="card-body text-center">
<h5 class="card-title mb-1">{{ $recipe->name }}</h5> <h5 class="card-title mb-1">{{ $recipe->name }}</h5>
<p class="card-text"> <p class="card-text">
@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
<span class="timeago-contextual">@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</span> <span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, '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($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count) }}@endif</span>
</p> </p>
</div> </div>
</div> </div>
@ -95,7 +95,7 @@
<a id="selectedRecipeConsumeButton" class="btn btn-sm btn-outline-success py-0" href="#" data-toggle="tooltip" title="{{ $L('Consume all ingredients needed by this recipe') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}"> <a id="selectedRecipeConsumeButton" class="btn btn-sm btn-outline-success py-0" href="#" data-toggle="tooltip" title="{{ $L('Consume all ingredients needed by this recipe') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}">
<i class="fas fa-utensils"></i> <i class="fas fa-utensils"></i>
</a> </a>
<a class="btn btn-sm btn-outline-primary py-0 recipe-order-missing-button @if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $selectedRecipe->id)->need_fulfilled_with_shopping_list == 1){{ disabled }}@endif" href="#" data-toggle="tooltip" title="{{ $L('Put missing products on shopping list') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}"> <a class="btn btn-sm btn-outline-primary py-0 recipe-order-missing-button @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->need_fulfilled_with_shopping_list == 1){{ disabled }}@endif" href="#" data-toggle="tooltip" title="{{ $L('Put missing products on shopping list') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}">
<i class="fas fa-cart-plus"></i> <i class="fas fa-cart-plus"></i>
</a>&nbsp;&nbsp; </a>&nbsp;&nbsp;
<a id="selectedRecipeEditButton" class="btn btn-sm btn-outline-info py-0" href="{{ $U('/recipe/') }}{{ $selectedRecipe->id }}"> <a id="selectedRecipeEditButton" class="btn btn-sm btn-outline-info py-0" href="{{ $U('/recipe/') }}{{ $selectedRecipe->id }}">
@ -126,7 +126,7 @@
<span class="small text-muted">{{ $L('Based on the prices of the last purchase per product') }}</span> <span class="small text-muted">{{ $L('Based on the prices of the last purchase per product') }}</span>
</label> </label>
<p class="font-weight-bold font-italic"> <p class="font-weight-bold font-italic">
<span class="locale-number-format" data-format="currency">{{ $totalRecipeCosts }}</span> <span class="locale-number-format" data-format="currency">{{ $selectedRecipeTotalCosts }}</span>
</p> </p>
</div> </div>
</div> </div>
@ -142,7 +142,7 @@
<p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p> <p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p>
@endif @endif
@php $selectedRecipeSubRecipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_id', $selectedRecipeSubRecipe->id); @endphp @php $selectedRecipeSubRecipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'child_recipe_id', $selectedRecipeSubRecipe->id); @endphp
@if(count($selectedRecipeSubRecipePositionsFiltered) > 0) @if(count($selectedRecipeSubRecipePositionsFiltered) > 0)
<div class="card-body"> <div class="card-body">
<h5 class="mb-0">{{ $L('Ingredients') }}</h5> <h5 class="mb-0">{{ $L('Ingredients') }}</h5>
@ -154,8 +154,8 @@
<h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5> <h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
@endif @endif
<li class="list-group-item"> <li class="list-group-item">
@if($selectedRecipePosition->amount == round($selectedRecipePosition->amount)){{ round($selectedRecipePosition->amount) }}@else{{ $selectedRecipePosition->amount }}@endif {{ Pluralize($selectedRecipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }} @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount)){{ round($selectedRecipePosition->recipe_amount) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif {{ Pluralize($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $L('Enough in stock') }} @else {{ $L('Not enough in stock, #1 missing, #2 already on shopping list', FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', round($selectedRecipePosition->id)->missing_amount), round(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list)) }} @endif</span> <span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $L('Enough in stock') }} @else {{ $L('Not enough in stock, #1 missing, #2 already on shopping list', round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount), round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list)) }} @endif</span>
@if(!empty($selectedRecipePosition->note)) @if(!empty($selectedRecipePosition->note))
<div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div> <div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div>
@ -178,19 +178,19 @@
<p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p> <p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p>
@endif @endif
@if($selectedRecipePositions->count() > 0) @if($selectedRecipePositionsResolved->count() > 0)
<div class="card-body"> <div class="card-body">
<h5 class="mb-0">{{ $L('Ingredients') }}</h5> <h5 class="mb-0">{{ $L('Ingredients') }}</h5>
</div> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
@php $lastGroup = 'undefined'; @endphp @php $lastGroup = 'undefined'; @endphp
@foreach($selectedRecipePositions as $selectedRecipePosition) @foreach($selectedRecipePositionsResolved as $selectedRecipePosition)
@if($lastGroup != $selectedRecipePosition->ingredient_group) @if($lastGroup != $selectedRecipePosition->ingredient_group)
<h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5> <h5 class="mb-2 mt-2 ml-4"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
@endif @endif
<li class="list-group-item"> <li class="list-group-item">
@if($selectedRecipePosition->amount == round($selectedRecipePosition->amount)){{ round($selectedRecipePosition->amount) }}@else{{ $selectedRecipePosition->amount }}@endif {{ Pluralize($selectedRecipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }} @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount)){{ round($selectedRecipePosition->recipe_amount) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif {{ Pluralize($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->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', $selectedRecipePosition->id)->missing_amount), round(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list)) }} @endif</span> <span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $L('Enough in stock') }} @else {{ $L('Not enough in stock, #1 missing, #2 already on shopping list', round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount), round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list)) }} @endif</span>
@if(!empty($selectedRecipePosition->note)) @if(!empty($selectedRecipePosition->note))
<div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div> <div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div>
@ -212,7 +212,7 @@
</div> </div>
<div id="missing-recipe-pos-list" class="list-group d-none mt-3"> <div id="missing-recipe-pos-list" class="list-group d-none mt-3">
@foreach($recipesFulfillment as $recipePos) @foreach($recipePositionsResolved as $recipePos)
@if(in_array($recipePos->recipe_id, $includedRecipeIdsAbsolute) && $recipePos->missing_amount > 0) @if(in_array($recipePos->recipe_id, $includedRecipeIdsAbsolute) && $recipePos->missing_amount > 0)
<a href="#" class="list-group-item list-group-item-action list-group-item-primary missing-recipe-pos-select-button"> <a href="#" class="list-group-item list-group-item-action list-group-item-primary missing-recipe-pos-select-button">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">