mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Allow partially in stock recipes to be consumed (closes #386)
This commit is contained in:
parent
f9c7c67dc7
commit
23d7b6ad3c
@ -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
|
||||
|
@ -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 ""
|
||||
|
@ -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 = '<a class="ml-2 btn btn-outline-primary btn-xs recipe-order-missing-button d-print-none ' + weekRecipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fa-solid fa-cart-plus"></i></a>';
|
||||
}
|
||||
|
||||
weekRecipeConsumeButtonHtml = '<a class="ml-2 btn btn-outline-success btn-xs recipe-consume-button d-print-none ' + weekRecipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this weeks recipes or products") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fa-solid fa-utensils"></i></a>'
|
||||
weekRecipeConsumeButtonHtml = '<a class="ml-2 btn btn-outline-success btn-xs recipe-consume-button d-print-none" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this weeks recipes or products") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fa-solid fa-utensils"></i></a>'
|
||||
}
|
||||
$(".calendar[data-primary-section='true'] .fc-header-toolbar .fc-center").html("<h4>" + weekCostsHtml + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "</h4>");
|
||||
},
|
||||
@ -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 = '<i class="fa-solid fa-check text-success"></i>';
|
||||
if (resolvedRecipe.need_fulfilled != 1)
|
||||
@ -201,7 +189,7 @@ $(".calendar").each(function()
|
||||
<h5 class="d-print-none"> \
|
||||
<a class="ml-2 btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Edit this item") + '"><i class="fa-solid fa-edit"></i></a> \
|
||||
<a class="btn btn-outline-danger btn-xs remove-recipe-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fa-solid fa-trash"></i></a> \
|
||||
<a class="ml-2 btn btn-outline-success btn-xs recipe-consume-button ' + recipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-recipe-id="' + internalShadowRecipe.id.toString() + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fa-solid fa-utensils"></i></a> \
|
||||
<a class="ml-2 btn btn-outline-success btn-xs recipe-consume-button" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-recipe-id="' + internalShadowRecipe.id.toString() + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fa-solid fa-utensils"></i></a> \
|
||||
' + shoppingListButtonHtml + ' \
|
||||
' + doneButtonHtml + ' \
|
||||
</h5> \
|
||||
@ -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) +
|
||||
"<br><br>(" + __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)
|
||||
|
@ -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) +
|
||||
"<br><br>(" + __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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +333,7 @@
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h3 class="card-title mb-0">{{ $recipe->name }}</h3>
|
||||
<div class="card-icons d-flex flex-wrap justify-content-end flex-shrink-1">
|
||||
<a class="btn @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-consume @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
<a class="btn @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-consume"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Consume all ingredients needed by this recipe') }}"
|
||||
@ -369,7 +369,7 @@
|
||||
<div class="mb-4 @if(!empty($recipe->picture_file_name)) d-none @else d-flex @endif d-print-block justify-content-between align-items-center">
|
||||
<h1 class="card-title mb-0">{{ $recipe->name }}</h1>
|
||||
<div class="card-icons d-flex flex-wrap justify-content-end flex-shrink-1 d-print-none">
|
||||
<a class="btn recipe-consume @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
<a class="btn recipe-consume"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Consume all ingredients needed by this recipe') }}"
|
||||
@ -427,7 +427,7 @@
|
||||
<h3>
|
||||
<span class="locale-number locale-number-currency pt-0">{{ $costs }}</span>
|
||||
@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->prices_incomplete)
|
||||
<i class="fa-solid fa-exclamation text-danger"
|
||||
<i class="small fa-solid fa-exclamation text-danger"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('No price information is available for at least one ingredient') }}"></i>
|
||||
|
Loading…
x
Reference in New Issue
Block a user