From a062a4beaa407d5211bf901b28221ad35d1f3314 Mon Sep 17 00:00:00 2001
From: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
Date: Sat, 29 Jun 2024 01:16:04 +1000
Subject: [PATCH] feat: Add the ability to flag a food as "on hand", to exclude
from shopping list (#3777)
---
...3_32d69327997b_add_staple_flag_to_foods.py | 48 +++++++++++++++++++
.../Recipe/RecipeDialogAddToShoppingList.vue | 2 +-
frontend/composables/store/use-food-store.ts | 2 +
frontend/lang/messages/en-US.json | 3 +-
frontend/lib/api/types/recipe.ts | 4 +-
frontend/pages/group/data/foods.vue | 26 ++++++++++
mealie/db/models/recipe/ingredient.py | 25 ++++++++--
mealie/schema/recipe/recipe_ingredient.py | 11 ++++-
8 files changed, 112 insertions(+), 9 deletions(-)
create mode 100644 alembic/versions/2024-06-22-10.17.03_32d69327997b_add_staple_flag_to_foods.py
diff --git a/alembic/versions/2024-06-22-10.17.03_32d69327997b_add_staple_flag_to_foods.py b/alembic/versions/2024-06-22-10.17.03_32d69327997b_add_staple_flag_to_foods.py
new file mode 100644
index 000000000000..65e2d4e02f06
--- /dev/null
+++ b/alembic/versions/2024-06-22-10.17.03_32d69327997b_add_staple_flag_to_foods.py
@@ -0,0 +1,48 @@
+"""Add staple flag to foods
+
+Revision ID: 32d69327997b
+Revises: 7788478a0338
+Create Date: 2024-06-22 10:17:03.323966
+
+"""
+
+import sqlalchemy as sa
+from sqlalchemy import orm
+
+from alembic import op
+
+
+# revision identifiers, used by Alembic.
+revision = "32d69327997b"
+down_revision = "7788478a0338"
+branch_labels = None
+depends_on = None
+
+
+def is_postgres():
+ return op.get_context().dialect.name == "postgresql"
+
+
+def upgrade():
+ with op.batch_alter_table("ingredient_foods") as batch_op:
+ batch_op.add_column(sa.Column("on_hand", sa.Boolean(), nullable=True, default=False))
+
+ bind = op.get_bind()
+ session = orm.Session(bind=bind)
+
+ with session:
+ if is_postgres():
+ stmt = "UPDATE ingredient_foods SET on_hand = FALSE;"
+ else:
+ stmt = "UPDATE ingredient_foods SET on_hand = 0;"
+
+ session.execute(sa.text(stmt))
+
+ # forbid nulls after migration
+ with op.batch_alter_table("ingredient_foods") as batch_op:
+ batch_op.alter_column("on_hand", nullable=False)
+
+
+def downgrade():
+ with op.batch_alter_table("ingredient_foods") as batch_op:
+ batch_op.drop_column("on_hand")
diff --git a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue
index 9209b079cc92..89dd45eb115b 100644
--- a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue
+++ b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue
@@ -231,7 +231,7 @@ export default defineComponent({
const shoppingListIngredients: ShoppingListIngredient[] = recipe.recipeIngredient.map((ing) => {
return {
- checked: true,
+ checked: !ing.food?.onHand,
ingredient: ing,
disableAmount: recipe.settings?.disableAmount || false,
}
diff --git a/frontend/composables/store/use-food-store.ts b/frontend/composables/store/use-food-store.ts
index ff0fb7711e48..4b02210c382a 100644
--- a/frontend/composables/store/use-food-store.ts
+++ b/frontend/composables/store/use-food-store.ts
@@ -19,6 +19,7 @@ export const useFoodData = function () {
name: "",
description: "",
labelId: undefined,
+ onHand: false,
});
function reset() {
@@ -26,6 +27,7 @@ export const useFoodData = function () {
data.name = "";
data.description = "";
data.labelId = undefined;
+ data.onHand = false;
}
return {
diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json
index a9f949e5b319..226207109f19 100644
--- a/frontend/lang/messages/en-US.json
+++ b/frontend/lang/messages/en-US.json
@@ -988,7 +988,8 @@
"food-data": "Food Data",
"example-food-singular": "ex: Onion",
"example-food-plural": "ex: Onions",
- "label-overwrite-warning": "This will assign the chosen label to all selected foods and potentially overwrite your existing labels."
+ "label-overwrite-warning": "This will assign the chosen label to all selected foods and potentially overwrite your existing labels.",
+ "on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
},
"units": {
"seed-dialog-text": "Seed the database with common units based on your local language.",
diff --git a/frontend/lib/api/types/recipe.ts b/frontend/lib/api/types/recipe.ts
index 23189db60119..8085948eab7b 100644
--- a/frontend/lib/api/types/recipe.ts
+++ b/frontend/lib/api/types/recipe.ts
@@ -63,6 +63,7 @@ export interface CreateIngredientFood {
};
labelId?: string;
aliases?: CreateIngredientFoodAlias[];
+ onHand?: boolean;
}
export interface CreateIngredientFoodAlias {
name: string;
@@ -135,6 +136,7 @@ export interface IngredientFood {
label?: MultiPurposeLabelSummary;
createdAt?: string;
updateAt?: string;
+ onHand?: boolean;
}
export interface IngredientFoodAlias {
name: string;
@@ -464,7 +466,7 @@ export interface ScrapeRecipe {
export interface ScrapeRecipeTest {
url: string;
}
-export interface SlugResponse {}
+export interface SlugResponse { }
export interface TagIn {
name: string;
}
diff --git a/frontend/pages/group/data/foods.vue b/frontend/pages/group/data/foods.vue
index cf0ad1f6ab2c..290b7dcef8e3 100644
--- a/frontend/pages/group/data/foods.vue
+++ b/frontend/pages/group/data/foods.vue
@@ -87,6 +87,14 @@
:label="$t('data-pages.foods.food-label')"
>
+
+ {{ $t("data-pages.foods.on-hand-checkbox-label") }} +
@@ -134,6 +142,14 @@ :label="$t('data-pages.foods.food-label')" > ++ {{ $t("data-pages.foods.on-hand-checkbox-label") }} +
@@ -243,6 +259,11 @@ {{ item.label.name }} + +