mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 01:32:38 +00:00
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)
This commit is contained in:
parent
aaf248c1b3
commit
0d1f2ad09d
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ""
|
||||
|
@ -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,
|
||||
|
111
migrations/0165.sql
Normal file
111
migrations/0165.sql
Normal file
@ -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;
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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: '<iframe height="650px" class="embed-responsive" src="' + U("/recipes?embedded&recipe=") + selectedRecipeId + '#fullscreen"></iframe>',
|
||||
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: '<iframe height="650px" class="embed-responsive" src="' + U("/recipes?embedded&recipe=") + selectedRecipeId + '#fullscreen"></iframe>',
|
||||
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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 d-print-none">
|
||||
<div class="@if(boolval($userSettings['recipes_show_list_side_by_side']) || $embedded) col-12 col-md-6 @else col @endif d-print-none">
|
||||
<div class="title-related-links border-bottom mb-2 py-1">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
@ -218,7 +218,7 @@
|
||||
<h5 class="card-title mb-1">{{ $recipe->name }}</h5>
|
||||
<p class="card-text">
|
||||
@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($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $__t('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing but already on the shopping list', 'Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock (not included in costs), %s ingredient missing', 'Not enough in stock (not included in costs), %s ingredients missing') }}@endif</span>
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $__t('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing but already on the shopping list', 'Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing', 'Not enough in stock, %s ingredients missing') }}@endif</span>
|
||||
</p>
|
||||
<p class="card-text mt-2">
|
||||
<a class="btn btn-xs btn-outline-danger hide-when-embedded hide-on-fullscreen-card recipe-delete"
|
||||
@ -245,7 +245,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($selectedRecipe !== null)
|
||||
@if($selectedRecipe !== null && (boolval($userSettings['recipes_show_list_side_by_side']) || $embedded))
|
||||
@php
|
||||
$allRecipes = $selectedRecipeSubRecipes;
|
||||
array_unshift($allRecipes, $selectedRecipe);
|
||||
@ -370,7 +370,7 @@
|
||||
<i class="fas fa-question-circle text-muted d-print-none"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('Based on the prices of the default consume rule which is "Opened first, then first due first, then first in first out"') }}"></i>
|
||||
title="{{ $__t('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') }}"></i>
|
||||
</label>
|
||||
<h3 class="locale-number locale-number-currency pt-0">{{ $costs }}</h3>
|
||||
</div>
|
||||
@ -463,7 +463,7 @@
|
||||
@if(GROCY_FEATURE_FLAG_STOCK)
|
||||
<span class="d-print-none">
|
||||
@if($selectedRecipePosition->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->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($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</span>
|
||||
<span class="timeago-contextual">@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</span>
|
||||
</span>
|
||||
@endif
|
||||
@if($selectedRecipePosition->need_fulfilled == 1 && GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) <span class="float-right font-italic ml-2 locale-number locale-number-currency">{{ $selectedRecipePosition->costs }}</span> @endif
|
||||
|
@ -15,6 +15,19 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
id="recipes_show_list_side_by_side"
|
||||
data-setting-key="recipes_show_list_side_by_side">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="recipes_show_list_side_by_side">
|
||||
{{ $__t('Show the recipe list and the recipe side by side') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-2">{{ $__t('Recipe card') }}</h4>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
|
Loading…
x
Reference in New Issue
Block a user