From 90a0caf1dcf4171f7d6812a62937500856a3a7f7 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sat, 10 Jul 2021 12:32:29 +0200 Subject: [PATCH] Fixed meal plan recipe servings stock fulfillment checking (fixes #1391) --- changelog/62_UNRELEASED_xxxx-xx-xx.md | 3 + migrations/0139.sql | 256 ++++++++++++++++++++++++++ public/viewjs/mealplan.js | 3 +- services/RecipesService.php | 8 +- 4 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 migrations/0139.sql diff --git a/changelog/62_UNRELEASED_xxxx-xx-xx.md b/changelog/62_UNRELEASED_xxxx-xx-xx.md index 7817355b..67228d27 100644 --- a/changelog/62_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/62_UNRELEASED_xxxx-xx-xx.md @@ -61,6 +61,9 @@ - Fixed that the amount of self produced products with tare weight handling enabled was wrong ("Produces product" recipe option) - Fixed that the ingredient amount calculation for included/nested recipes was (for most cases) wrong +### Meal plan fixes +- Fixed that stock fulfillment checking used the desired servings of the recipe (those selected on the recipes page, not them from the meal plan entry) + ### Chores fixes - Fixed that tracking chores with "Done by" a different user was not possible diff --git a/migrations/0139.sql b/migrations/0139.sql new file mode 100644 index 00000000..ab55ebe3 --- /dev/null +++ b/migrations/0139.sql @@ -0,0 +1,256 @@ +DROP TRIGGER create_internal_recipe; +CREATE TRIGGER create_internal_recipe AFTER INSERT ON meal_plan +BEGIN + /* This contains practically the same logic as the trigger remove_internal_recipe */ + + -- Create a recipe per day + DELETE FROM recipes + WHERE name = NEW.day + AND type = 'mealplan-day'; + + INSERT OR REPLACE INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), NEW.day, 'mealplan-day'); + + -- Create a recipe per week + DELETE FROM recipes + WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') + AND type = 'mealplan-week'; + + INSERT INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), LTRIM(STRFTIME('%Y-%W', NEW.day), '0'), 'mealplan-week'); + + -- Delete all current nestings entries for the day and week recipe + DELETE FROM recipes_nestings + WHERE recipe_id IN (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day') + OR recipe_id IN (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'); + + -- Add all recipes for this day as included recipes in the day-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all recipes for this week as included recipes in the week-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', NEW.day) + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all products for this day as ingredients in the day-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE day = NEW.day + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Add all products for this week as ingredients in the week-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', NEW.day) + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Create a shadow recipe per meal plan recipe + INSERT OR REPLACE INTO recipes + (id, name, type) + SELECT (SELECT MIN(id) - 1 FROM recipes), CAST(NEW.day AS TEXT) || '#' || CAST(id AS TEXT), 'mealplan-shadow' + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL; + + DELETE FROM recipes_nestings + WHERE recipe_id IN (SELECT id FROM recipes WHERE name IN (SELECT CAST(NEW.day AS TEXT) || '#' || CAST(id AS TEXT) FROM meal_plan WHERE day = NEW.day) AND type = 'mealplan-shadow'); + + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = CAST(NEW.day AS TEXT) || '#' || CAST(meal_plan.id AS TEXT) AND type = 'mealplan-shadow'), recipe_id, recipe_servings + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL; +END; + +DROP TRIGGER remove_internal_recipe; +CREATE TRIGGER remove_internal_recipe AFTER DELETE ON meal_plan +BEGIN + /* This contains practically the same logic as the trigger create_internal_recipe */ + + -- Create a recipe per day + DELETE FROM recipes + WHERE name = OLD.day + AND type = 'mealplan-day'; + + INSERT OR REPLACE INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), OLD.day, 'mealplan-day'); + + -- Create a recipe per week + DELETE FROM recipes + WHERE name = LTRIM(STRFTIME('%Y-%W', OLD.day), '0') + AND type = 'mealplan-week'; + + INSERT INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), LTRIM(STRFTIME('%Y-%W', OLD.day), '0'), 'mealplan-week'); + + -- Delete all current nestings entries for the day and week recipe + DELETE FROM recipes_nestings + WHERE recipe_id IN (SELECT id FROM recipes WHERE name = OLD.day AND type = 'mealplan-day') + OR recipe_id IN (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', OLD.day), '0') AND type = 'mealplan-week'); + + -- Add all recipes for this day as included recipes in the day-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = OLD.day AND type = 'mealplan-day'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE day = OLD.day + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all recipes for this week as included recipes in the week-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', OLD.day), '0') AND type = 'mealplan-week'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', OLD.day) + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all products for this day as ingredients in the day-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = OLD.day AND type = 'mealplan-day'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE day = OLD.day + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Add all products for this week as ingredients in the week-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', OLD.day), '0') AND type = 'mealplan-week'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', OLD.day) + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Remove shadow recipes per meal plan recipe + DELETE FROM recipes + WHERE type = 'mealplan-shadow' + AND name NOT IN (SELECT CAST(OLD.day AS TEXT) || '#' || CAST(id AS TEXT) FROM meal_plan WHERE type = 'recipe'); +END; + +CREATE TRIGGER update_internal_recipe AFTER UPDATE ON meal_plan +BEGIN + /* This contains practically the same logic as the trigger create_internal_recipe */ + + -- Create a recipe per day + DELETE FROM recipes + WHERE name = NEW.day + AND type = 'mealplan-day'; + + INSERT OR REPLACE INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), NEW.day, 'mealplan-day'); + + -- Create a recipe per week + DELETE FROM recipes + WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') + AND type = 'mealplan-week'; + + INSERT INTO recipes + (id, name, type) + VALUES + ((SELECT MIN(id) - 1 FROM recipes), LTRIM(STRFTIME('%Y-%W', NEW.day), '0'), 'mealplan-week'); + + -- Delete all current nestings entries for the day and week recipe + DELETE FROM recipes_nestings + WHERE recipe_id IN (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day') + OR recipe_id IN (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'); + + -- Add all recipes for this day as included recipes in the day-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all recipes for this week as included recipes in the week-recipe + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'), recipe_id, SUM(recipe_servings) + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', NEW.day) + AND type = 'recipe' + AND recipe_id IS NOT NULL + GROUP BY recipe_id; + + -- Add all products for this day as ingredients in the day-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE day = NEW.day + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Add all products for this week as ingredients in the week-recipe + INSERT INTO recipes_pos + (recipe_id, product_id, amount, qu_id) + SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'), product_id, SUM(product_amount), product_qu_id + FROM meal_plan + WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', NEW.day) + AND type = 'product' + AND product_id IS NOT NULL + GROUP BY product_id, product_qu_id; + + -- Create a shadow recipe per meal plan recipe + INSERT OR REPLACE INTO recipes + (id, name, type) + SELECT (SELECT MIN(id) - 1 FROM recipes), CAST(NEW.day AS TEXT) || '#' || CAST(id AS TEXT), 'mealplan-shadow' + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL; + + DELETE FROM recipes_nestings + WHERE recipe_id IN (SELECT id FROM recipes WHERE name IN (SELECT CAST(NEW.day AS TEXT) || '#' || CAST(id AS TEXT) FROM meal_plan WHERE day = NEW.day) AND type = 'mealplan-shadow'); + + INSERT INTO recipes_nestings + (recipe_id, includes_recipe_id, servings) + SELECT (SELECT id FROM recipes WHERE name = CAST(NEW.day AS TEXT) || '#' || CAST(meal_plan.id AS TEXT) AND type = 'mealplan-shadow'), recipe_id, recipe_servings + FROM meal_plan + WHERE day = NEW.day + AND type = 'recipe' + AND recipe_id IS NOT NULL; +END; diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 64060f21..6df0e044 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -93,7 +93,8 @@ var calendar = $("#calendar").fullCalendar({ return false; } - var resolvedRecipe = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", recipe.id); + var internalShadowRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", mealPlanEntry.day + "#" + mealPlanEntry.id); + var resolvedRecipe = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", internalShadowRecipe.id); element.attr("data-recipe", event.recipe); diff --git a/services/RecipesService.php b/services/RecipesService.php index 249e7a42..44653993 100644 --- a/services/RecipesService.php +++ b/services/RecipesService.php @@ -6,11 +6,13 @@ use LessQL\Result; class RecipesService extends BaseService { - const RECIPE_TYPE_MEALPLAN_DAY = 'mealplan-day'; + const RECIPE_TYPE_MEALPLAN_DAY = 'mealplan-day'; // A recipe per meal plan day => name = YYYY-MM-DD - const RECIPE_TYPE_MEALPLAN_WEEK = 'mealplan-week'; + const RECIPE_TYPE_MEALPLAN_WEEK = 'mealplan-week'; // A recipe per meal plan week => name = YYYY-WW (week number) - const RECIPE_TYPE_NORMAL = 'normal'; + const RECIPE_TYPE_MEALPLAN_SHADOW = 'mealplan-shadow'; // A recipe per meal plan recipe (for separated stock fulfillment checking) => name = YYYY-MM-DD# + + const RECIPE_TYPE_NORMAL = 'normal'; // Normal / manually created recipes public function AddNotFulfilledProductsToShoppingList($recipeId, $excludedProductIds = null) {