From 12ba99f649a354e55161904cc2e449f6a2458187 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sat, 4 May 2019 14:50:15 +0200 Subject: [PATCH] Added "variable amount" for recipe ingredients (closes #181) --- changelog/47_UNRELEASED_xxxx-xx-xx.md | 2 + localization/strings.pot | 6 ++ migrations/0068.sql | 84 +++++++++++++++++++++++++++ views/recipeform.blade.php | 7 ++- views/recipeposform.blade.php | 5 ++ views/recipes.blade.php | 14 ++++- 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 migrations/0068.sql diff --git a/changelog/47_UNRELEASED_xxxx-xx-xx.md b/changelog/47_UNRELEASED_xxxx-xx-xx.md index dea705e1..3af1b79e 100644 --- a/changelog/47_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/47_UNRELEASED_xxxx-xx-xx.md @@ -14,6 +14,8 @@ - It's now possible to customize the default amount for purchase/consume (see stock settings under the settings icon on the top right) - Chores improvements - New recurrence patterns - chores can now also be "scheduled" to repat daily/weekly/monthly +- Recipe improvements + - It's now possible to enter a "variable amount" (e. g. if a recipe needs "1 - 2 cups"), the original amount is still used for stock fulfillment checking (if enabled for that recipe ingredient) - New translations: (thanks all the translators) - Swedish (demo available at https://demo-sv.grocy.info) - Polish (demo available at https://demo-pl.grocy.info) diff --git a/localization/strings.pot b/localization/strings.pot index 6afca128..ced2fd83 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1221,3 +1221,9 @@ msgstr "" msgid "Default amount for consume" msgstr "" + +msgid "Variable amount" +msgstr "" + +msgid "When this is not empty, it will be shown instead of the amount entered above while the amount there will still be used for stock fulfillment checking" +msgstr "" diff --git a/migrations/0068.sql b/migrations/0068.sql new file mode 100644 index 00000000..7b59a4f1 --- /dev/null +++ b/migrations/0068.sql @@ -0,0 +1,84 @@ +ALTER TABLE recipes_pos +ADD variable_amount TEXT; + +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 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 rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * rnr.includes_servings*1.0 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 rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * rnr.includes_servings*1.0 END < 0 THEN ABS(IFNULL(sc.amount, 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 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 rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * rnr.includes_servings*1.0 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 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, + rp.note, + rp.variable_amount AS recipe_variable_amount +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*1.0 / r.base_servings*1.0) * rnr.includes_servings*1.0 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*1.0 / r.base_servings*1.0) * rnr.includes_servings*1.0 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, + rp.note, + rp.variable_amount AS recipe_variable_amount +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; diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 67ca29fb..6c5ebd2e 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -128,7 +128,12 @@ {{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }} - @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif {{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} + @if(!empty($recipePosition->variable_amount)) + {{ $recipePosition->variable_amount }} + @else + @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif + @endif + {{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} diff --git a/views/recipeposform.blade.php b/views/recipeposform.blade.php index aebfdfba..7fa5e41f 100644 --- a/views/recipeposform.blade.php +++ b/views/recipeposform.blade.php @@ -69,6 +69,11 @@ +
+ + +
+
not_check_stock_fulfillment == 1 || FindObjectInArrayByPropertyValue($products, 'id', $recipePos->product_id)->not_check_stock_fulfillment_for_recipes == 1)) checked @endif class="form-check-input" type="checkbox" id="not_check_stock_fulfillment" name="not_check_stock_fulfillment" value="1"> diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 1b35bc8c..2732b800 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -183,7 +183,12 @@
{{ $selectedRecipePosition->ingredient_group }}
@endif
  • - @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif {{ $__n($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 }} + @if(!empty($selectedRecipePosition->recipe_variable_amount)) + {{ $selectedRecipePosition->recipe_variable_amount }} + @else + @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif + @endif + {{ $__n($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 }} @if($selectedRecipePosition->need_fulfilled == 1)@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)@else@endif @if(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %s missing, %s already on shopping list', round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount, 2), round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list, 2)) }} @endif @@ -219,7 +224,12 @@
    {{ $selectedRecipePosition->ingredient_group }}
    @endif
  • - @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif {{ $__n($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 }} + @if(!empty($selectedRecipePosition->recipe_variable_amount)) + {{ $selectedRecipePosition->recipe_variable_amount }} + @else + @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif + @endif + {{ $__n($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 }} @if($selectedRecipePosition->need_fulfilled == 1)@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)@else@endif @if(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %s missing, %s already on shopping list', round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount, 2), round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list, 2)) }} @endif