From 0d1f2ad09d41d66317e0d62691910cf5d37743f6 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Wed, 9 Feb 2022 17:48:21 +0100 Subject: [PATCH] Squashed commit Optimized new chore start date handling (references #1612) Change yearly chore schedule to be on the same day each year (closes #817) Use the last price for out of stock ingredients (closes #779) Make it optionally possible to show the recipes list full-width (closes #1772) --- changelog/66_UNRELEASED_xxxx-xx-xx.md | 6 ++ config-dist.php | 1 + localization/strings.pot | 16 ++-- migrations/0164.sql | 77 ++++++++++++------ migrations/0165.sql | 111 ++++++++++++++++++++++++++ public/viewjs/choreform.js | 2 +- public/viewjs/recipes.js | 55 ++++++++++++- public/viewjs/recipessettings.js | 5 +- views/recipes.blade.php | 10 +-- views/recipessettings.blade.php | 13 +++ 10 files changed, 248 insertions(+), 48 deletions(-) create mode 100644 migrations/0165.sql diff --git a/changelog/66_UNRELEASED_xxxx-xx-xx.md b/changelog/66_UNRELEASED_xxxx-xx-xx.md index 62db668f..dcad93c0 100644 --- a/changelog/66_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/66_UNRELEASED_xxxx-xx-xx.md @@ -25,6 +25,10 @@ ### Recipes +- Optimized recipe costs calculation to better reflect the current real costs: Out of stock ingredients now use the last price + - Background: Before v3.0.0 recipe costs were only based on the last price per product and since v3.0.0 the "real costs" (based on the default consume rule "Opened first, then first due first, then first in first out") are used, means out of stock items have no price - so using the last price for out of stock items should reflect the current real costs better +- Added a new recipes setting (top right corner settings menu) "Show the recipe list and the recipe side by side" (defaults to enabled, so no changed behaviour when not configured) + - When disabled, on the recipes page, the recipe list is displayed full-width and the recipe will be shown in a popup instead of on the right side - Performance improvements (page loading time) of the recipes page - Fixed that when adding missing recipe ingredients, with the option "Only check if any amount is in stock" enabled, to the shopping list, unit conversions (if any) weren't considered - Fixed that the recipe stock fulfillment information about shopping list amounts was not correct when the ingredient had a decimal amount @@ -46,6 +50,8 @@ - Added a new chore option "Start date" which is used as a schedule starting point when the chore was never tracked - Until now, the schedule starting point was the first tracked execution - For all existing chores, the start date will be set to the first tracked execution time (or today, for chores which were never tracked) on migration +- The `Yearly` period type has been changed to be schedule the chore on the _same day_ each year + - This period type scheduled chores 1 year _after the last execution_ before, which is also possible by using the `Daily` period type and a period interval of 365 days - all existing `Yearly` schedules will be converted to that on migration ### Calendar diff --git a/config-dist.php b/config-dist.php index 1dd97cac..6c9c1be4 100644 --- a/config-dist.php +++ b/config-dist.php @@ -169,6 +169,7 @@ DefaultUserSetting('shopping_list_show_calendar', false); // Recipe settings DefaultUserSetting('recipe_ingredients_group_by_product_group', false); // Group recipe ingredients by their product group +DefaultUserSetting('recipes_show_list_side_by_side', true); // If the recipe should be displayed next to recipe list on the recipes page // Chores settings DefaultUserSetting('chores_due_soon_days', 5); diff --git a/localization/strings.pot b/localization/strings.pot index 85bb92e9..a01ec540 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -892,9 +892,6 @@ msgstr "" msgid "Costs" msgstr "" -msgid "Based on the prices of the last purchase per product" -msgstr "" - msgid "The ingredients listed here result in this amount of servings" msgstr "" @@ -1447,7 +1444,7 @@ msgstr "" msgid "This means the next execution of this chore should only be scheduled every %s months" msgstr "" -msgid "This means the next execution of this chore is scheduled 1 year after the last execution" +msgid "This means the next execution of this chore is scheduled each year on the same day (based on the start date)" msgstr "" msgid "This means the next execution of this chore should only be scheduled every %s years" @@ -1684,13 +1681,7 @@ msgstr "" msgid "Edit Barcode" msgstr "" -msgid "Not enough in stock (not included in costs), %s ingredient missing" -msgstr "" - -msgid "Based on the prices of the default consume rule which is \"Opened first, then first due first, then first in first out\"" -msgstr "" - -msgid "Not enough in stock (not included in costs), %1$s missing, %2$s already on shopping list" +msgid "Based on the prices of the default consume rule (Opened first, then first due first, then first in first out) for in-stock ingredients and on the last price for missing ones" msgstr "" msgid "Quantity unit stock cannot be changed after first purchase" @@ -2310,3 +2301,6 @@ msgstr "" msgid "The start date cannot be changed when the chore was once tracked" msgstr "" + +msgid "Show the recipe list and the recipe side by side" +msgstr "" diff --git a/migrations/0164.sql b/migrations/0164.sql index 4d253e38..8f51a595 100644 --- a/migrations/0164.sql +++ b/migrations/0164.sql @@ -10,6 +10,27 @@ UPDATE chores SET start_date = DATETIME('now', 'localtime') WHERE start_date IS NULL; +CREATE TRIGGER default_start_date_when_empty_INS AFTER INSERT ON chores +BEGIN + UPDATE chores + SET start_date = DATETIME('now', 'localtime') + WHERE id = NEW.id + AND IFNULL(start_date, '') = ''; +END; + +CREATE TRIGGER default_start_date_when_empty_UPD AFTER UPDATE ON chores +BEGIN + UPDATE chores + SET start_date = DATETIME('now', 'localtime') + WHERE id = NEW.id + AND IFNULL(start_date, '') = ''; +END; + +UPDATE chores +SET period_type = 'daily', +period_interval = IFNULL(period_interval, 1) * 365 +WHERE period_type = 'yearly'; + DROP VIEW chores_current; CREATE VIEW chores_current AS @@ -39,33 +60,37 @@ SELECT h.id AS chore_id, h.name AS chore_name, MAX(l.tracked_time) AS last_tracked_time, - CASE h.period_type - WHEN 'manually' THEN '2999-12-31 23:59:59' - WHEN 'dynamic-regular' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_days AS TEXT) || ' day') - WHEN 'daily' THEN DATETIME(IFNULL(MAX(l.tracked_time), h.start_date), '+' || CAST(h.period_interval AS TEXT) || ' day') - WHEN 'weekly' THEN ( - SELECT next - FROM ( - SELECT 'sunday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 0') AS next - UNION - SELECT 'monday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 1') AS next - UNION - SELECT 'tuesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 2') AS next - UNION - SELECT 'wednesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 3') AS next - UNION - SELECT 'thursday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 4') AS next - UNION - SELECT 'friday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 5') AS next - UNION - SELECT 'saturday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), h.start_date), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 6') AS next + CASE WHEN MAX(l.tracked_time) IS NULL THEN + h.start_date + ELSE + CASE h.period_type + WHEN 'manually' THEN '2999-12-31 23:59:59' + WHEN 'dynamic-regular' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_days AS TEXT) || ' day') + WHEN 'daily' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_interval AS TEXT) || ' day') + WHEN 'weekly' THEN ( + SELECT next + FROM ( + SELECT 'sunday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 0') AS next + UNION + SELECT 'monday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 1') AS next + UNION + SELECT 'tuesday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 2') AS next + UNION + SELECT 'wednesday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 3') AS next + UNION + SELECT 'thursday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 4') AS next + UNION + SELECT 'friday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 5') AS next + UNION + SELECT 'saturday' AS day, DATETIME((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 6') AS next + ) + WHERE INSTR(period_config, day) > 0 + ORDER BY next + LIMIT 1 ) - WHERE INSTR(period_config, day) > 0 - ORDER BY next - LIMIT 1 - ) - WHEN 'monthly' THEN DATETIME(IFNULL(MAX(l.tracked_time), h.start_date), 'start of month', '+' || CAST(h.period_interval AS TEXT) || ' month', '+' || CAST(h.period_days - 1 AS TEXT) || ' day') - WHEN 'yearly' THEN DATETIME(IFNULL(MAX(l.tracked_time), h.start_date), '+' || CAST(h.period_interval AS TEXT) || ' years') + WHEN 'monthly' THEN DATETIME(MAX(l.tracked_time), 'start of month', '+' || CAST(h.period_interval AS TEXT) || ' month', '+' || CAST(h.period_days - 1 AS TEXT) || ' day') + WHEN 'yearly' THEN DATETIME(SUBSTR(CAST(DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_interval AS TEXT) || ' years') AS TEXT), 1, 4) || SUBSTR(CAST(h.start_date AS TEXT), 5, 6) || SUBSTR(CAST(DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_interval AS TEXT) || ' years') AS TEXT), -9)) + END END AS next_estimated_execution_time, h.track_date_only, h.rollover, diff --git a/migrations/0165.sql b/migrations/0165.sql new file mode 100644 index 00000000..6ff82dbb --- /dev/null +++ b/migrations/0165.sql @@ -0,0 +1,111 @@ +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, + CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) 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 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) 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 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END END < 0 THEN ABS(IFNULL(sc.amount_aggregated, 0) - (CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_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 ROUND(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), 2) >= ROUND(CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END END, 2) THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list, + rp.qu_id, + (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) * rp.amount * IFNULL(pop.price, IFNULL(plp.price, 0)) * rp.price_factor * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1) ELSE 1 END 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 + r.type as recipe_type, + 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.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) * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1) ELSE 1 END AS calories, + p.active AS product_active +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 +LEFT 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_oldest_stock_unit_price pop + ON rp.product_id = pop.product_id +LEFT JOIN products_last_purchased plp + ON rp.product_id = plp.product_id +LEFT JOIN quantity_unit_conversions_resolved qucr + ON rp.product_id = qucr.product_id + AND rp.qu_id = qucr.from_qu_id + AND p.qu_id_stock = qucr.to_qu_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, + CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) 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, + (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) * rp.amount * IFNULL(pop.price, IFNULL(plp.price, 0)) * rp.price_factor * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1) ELSE 1 END 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 + r.type as recipe_type, + 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.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) * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1) ELSE 1 END AS calories, + p.active AS product_active +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 +LEFT 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_oldest_stock_unit_price pop + ON rp.product_id = pop.product_id +LEFT JOIN products_last_purchased plp + ON rp.product_id = plp.product_id +LEFT JOIN quantity_unit_conversions_resolved qucr + ON rp.product_id = qucr.product_id + AND rp.qu_id = qucr.from_qu_id + AND p.qu_id_stock = qucr.to_qu_id +WHERE rp.not_check_stock_fulfillment = 1; diff --git a/public/viewjs/choreform.js b/public/viewjs/choreform.js index c340ff25..91e39c71 100644 --- a/public/viewjs/choreform.js +++ b/public/viewjs/choreform.js @@ -181,7 +181,7 @@ $('.input-group-chore-period-type').on('change', function(e) } else if (periodType === 'yearly') { - $('#chore-period-type-info').attr("data-original-title", __t('This means the next execution of this chore is scheduled 1 year after the last execution')); + $('#chore-period-type-info').attr("data-original-title", __t('This means the next execution of this chore is scheduled each year on the same day (based on the start date)')); $('#chore-period-interval-info').attr("data-original-title", __t('This means the next execution of this chore should only be scheduled every %s years', periodInterval.toString())); } diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index 3c39fb54..8d01afb5 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -279,10 +279,33 @@ recipesTables.on('select', function(e, dt, type, indexes) { var selectedRecipeId = $(recipesTables.row(indexes[0]).node()).data("recipe-id"); var currentRecipeId = location.search.split('recipe=')[1]; - if (selectedRecipeId.toString() !== currentRecipeId) + + if (BoolVal(Grocy.UserSettings.recipes_show_list_side_by_side)) { - UpdateUriParam("recipe", selectedRecipeId.toString()); - window.location.reload(); + if (selectedRecipeId.toString() !== currentRecipeId) + { + UpdateUriParam("recipe", selectedRecipeId.toString()); + window.location.reload(); + } + } + else + { + bootbox.dialog({ + message: '', + size: 'extra-large', + backdrop: true, + closeButton: false, + buttons: { + cancel: { + label: __t('Close'), + className: 'btn-secondary responsive-button', + callback: function() + { + bootbox.hideAll(); + } + } + } + }); } } }); @@ -291,7 +314,31 @@ $(".recipe-gallery-item").on("click", function(e) { e.preventDefault(); - window.location.href = U('/recipes?tab=gallery&recipe=' + $(this).data("recipe-id")); + var selectedRecipeId = $(this).data("recipe-id"); + + if (BoolVal(Grocy.UserSettings.recipes_show_list_side_by_side)) + { + window.location.href = U('/recipes?tab=gallery&recipe=' + selectedRecipeId); + } + else + { + bootbox.dialog({ + message: '', + size: 'extra-large', + backdrop: true, + closeButton: false, + buttons: { + cancel: { + label: __t('Close'), + className: 'btn-secondary responsive-button', + callback: function() + { + bootbox.hideAll(); + } + } + } + }); + } }); $(".recipe-fullscreen").on('click', function(e) diff --git a/public/viewjs/recipessettings.js b/public/viewjs/recipessettings.js index 9b67918b..43bcb317 100644 --- a/public/viewjs/recipessettings.js +++ b/public/viewjs/recipessettings.js @@ -3,4 +3,7 @@ $("#recipe_ingredients_group_by_product_group").prop("checked", true); } -RefreshLocaleNumberInput(); +if (BoolVal(Grocy.UserSettings.recipes_show_list_side_by_side)) +{ + $("#recipes_show_list_side_by_side").prop("checked", true); +} diff --git a/views/recipes.blade.php b/views/recipes.blade.php index c650d7eb..faa367b1 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -21,7 +21,7 @@
-
+
@@ -463,7 +463,7 @@ @if(GROCY_FEATURE_FLAG_STOCK) @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 (not included in costs), %1$s missing, %2$s already on shopping list', round($selectedRecipePosition->missing_amount, 2), round($selectedRecipePosition->amount_on_shopping_list, 2)) }} @endif + @if(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %1$s missing, %2$s already on shopping list', round($selectedRecipePosition->missing_amount, 2), round($selectedRecipePosition->amount_on_shopping_list, 2)) }} @endif @endif @if($selectedRecipePosition->need_fulfilled == 1 && GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {{ $selectedRecipePosition->costs }} @endif diff --git a/views/recipessettings.blade.php b/views/recipessettings.blade.php index 1303dcf5..d28fb95e 100644 --- a/views/recipessettings.blade.php +++ b/views/recipessettings.blade.php @@ -15,6 +15,19 @@
+
+
+ + +
+
+

{{ $__t('Recipe card') }}