diff --git a/frontend/lib/api/types/household.ts b/frontend/lib/api/types/household.ts index ce2b63bb6..77e112e8c 100644 --- a/frontend/lib/api/types/household.ts +++ b/frontend/lib/api/types/household.ts @@ -58,6 +58,8 @@ export interface GroupEventNotifierOptions { dataExport?: boolean; dataImport?: boolean; mealplanEntryCreated?: boolean; + mealplanEntryUpdated?: boolean; + mealplanEntryDeleted?: boolean; shoppingListCreated?: boolean; shoppingListUpdated?: boolean; shoppingListDeleted?: boolean; @@ -85,6 +87,8 @@ export interface GroupEventNotifierOptionsOut { dataExport?: boolean; dataImport?: boolean; mealplanEntryCreated?: boolean; + mealplanEntryUpdated?: boolean; + mealplanEntryDeleted?: boolean; shoppingListCreated?: boolean; shoppingListUpdated?: boolean; shoppingListDeleted?: boolean; @@ -113,6 +117,8 @@ export interface GroupEventNotifierOptionsSave { dataExport?: boolean; dataImport?: boolean; mealplanEntryCreated?: boolean; + mealplanEntryUpdated?: boolean; + mealplanEntryDeleted?: boolean; shoppingListCreated?: boolean; shoppingListUpdated?: boolean; shoppingListDeleted?: boolean; diff --git a/frontend/pages/household/notifiers.vue b/frontend/pages/household/notifiers.vue index bd135c7e0..aa9f1b8a9 100644 --- a/frontend/pages/household/notifiers.vue +++ b/frontend/pages/household/notifiers.vue @@ -293,9 +293,17 @@ const optionsSections: OptionSection[] = [ text: i18n.t("events.mealplan-events"), options: [ { - text: i18n.t("events.when-a-user-in-your-group-creates-a-new-mealplan"), + text: i18n.t("general.create") as string, key: "mealplanEntryCreated", }, + { + text: i18n.t("general.update") as string, + key: "mealplanEntryUpdated", + }, + { + text: i18n.t("general.delete") as string, + key: "mealplanEntryDeleted", + }, ], }, { diff --git a/mealie/alembic/versions/2026-03-26-20.48.28_cdc93edaf73d_add_mealplan_updated_and_deleted_to_.py b/mealie/alembic/versions/2026-03-26-20.48.28_cdc93edaf73d_add_mealplan_updated_and_deleted_to_.py new file mode 100644 index 000000000..060314181 --- /dev/null +++ b/mealie/alembic/versions/2026-03-26-20.48.28_cdc93edaf73d_add_mealplan_updated_and_deleted_to_.py @@ -0,0 +1,50 @@ +"""'Add mealplan updated and deleted to group notifier options' + +Revision ID: cdc93edaf73d +Revises: a39c7f1826e3 +Create Date: 2026-03-26 20:48:28.584661 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "cdc93edaf73d" +down_revision: str | None = "a39c7f1826e3" +branch_labels: str | tuple[str, ...] | None = None +depends_on: str | tuple[str, ...] | None = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("group_events_notifier_options", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "mealplan_entry_updated", + sa.Boolean(), + nullable=False, + default=False, + server_default=sa.sql.expression.false(), + ) + ) + batch_op.add_column( + sa.Column( + "mealplan_entry_deleted", + sa.Boolean(), + nullable=False, + default=False, + server_default=sa.sql.expression.false(), + ) + ) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("group_events_notifier_options", schema=None) as batch_op: + batch_op.drop_column("mealplan_entry_deleted") + batch_op.drop_column("mealplan_entry_updated") + + # ### end Alembic commands ### diff --git a/mealie/db/models/household/events.py b/mealie/db/models/household/events.py index 9c850d40e..000bc2410 100644 --- a/mealie/db/models/household/events.py +++ b/mealie/db/models/household/events.py @@ -29,6 +29,8 @@ class GroupEventNotifierOptionsModel(SqlAlchemyBase, BaseMixins): data_import: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) mealplan_entry_created: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + mealplan_entry_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + mealplan_entry_deleted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) shopping_list_created: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) shopping_list_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) diff --git a/mealie/routes/households/controller_mealplan.py b/mealie/routes/households/controller_mealplan.py index 25e6922e2..b8d112def 100644 --- a/mealie/routes/households/controller_mealplan.py +++ b/mealie/routes/households/controller_mealplan.py @@ -17,7 +17,11 @@ from mealie.schema.meal_plan.plan_rules import PlanRulesDay from mealie.schema.recipe.recipe import Recipe from mealie.schema.response.pagination import PaginationQuery from mealie.schema.response.responses import ErrorResponse -from mealie.services.event_bus_service.event_types import EventMealplanCreatedData, EventTypes +from mealie.services.event_bus_service.event_types import ( + EventMealplanData, + EventOperation, + EventTypes, +) router = APIRouter(prefix="/households/mealplans", tags=["Households: Mealplans"]) @@ -102,7 +106,8 @@ class GroupMealplanController(BaseCrudController): self.publish_event( event_type=EventTypes.mealplan_entry_created, - document_data=EventMealplanCreatedData( + document_data=EventMealplanData( + operation=EventOperation.create, mealplan_id=result.id, recipe_id=data.recipe_id, recipe_name=result.recipe.name if result.recipe else None, @@ -138,7 +143,7 @@ class GroupMealplanController(BaseCrudController): ) recipe = random_recipes[0] - return self.mixins.create_one( + result = self.mixins.create_one( SavePlanEntry( date=data.date, entry_type=data.entry_type, @@ -148,14 +153,65 @@ class GroupMealplanController(BaseCrudController): ) ) + self.publish_event( + event_type=EventTypes.mealplan_entry_created, + document_data=EventMealplanData( + operation=EventOperation.create, + mealplan_id=result.id, + recipe_id=recipe.id, + recipe_name=recipe.name, + recipe_slug=recipe.slug, + date=data.date, + ), + group_id=result.group_id, + household_id=result.household_id, + message=f"Mealplan entry created for {data.date} for {data.entry_type}", + ) + + return result + @router.get("/{item_id}", response_model=ReadPlanEntry) def get_one(self, item_id: int): return self.mixins.get_one(item_id) @router.put("/{item_id}", response_model=ReadPlanEntry) def update_one(self, item_id: int, data: UpdatePlanEntry): - return self.mixins.update_one(data, item_id) + result = self.mixins.update_one(data, item_id) + + self.publish_event( + event_type=EventTypes.mealplan_entry_updated, + document_data=EventMealplanData( + operation=EventOperation.update, + mealplan_id=result.id, + recipe_id=result.recipe_id, + recipe_name=result.recipe.name if result.recipe else None, + recipe_slug=result.recipe.slug if result.recipe else None, + date=result.date, + ), + group_id=result.group_id, + household_id=result.household_id, + message=f"Mealplan entry updated for {result.date} for {result.entry_type}", + ) + + return result @router.delete("/{item_id}", response_model=ReadPlanEntry) def delete_one(self, item_id: int): - return self.mixins.delete_one(item_id) + result = self.mixins.delete_one(item_id) + + self.publish_event( + event_type=EventTypes.mealplan_entry_deleted, + document_data=EventMealplanData( + operation=EventOperation.delete, + mealplan_id=result.id, + recipe_id=result.recipe_id, + recipe_name=result.recipe.name if result.recipe else None, + recipe_slug=result.recipe.slug if result.recipe else None, + date=result.date, + ), + group_id=result.group_id, + household_id=result.household_id, + message=f"Mealplan entry deleted for {result.date} for {result.entry_type}", + ) + + return result diff --git a/mealie/schema/household/group_events.py b/mealie/schema/household/group_events.py index 7ab964f76..fa4f2237a 100644 --- a/mealie/schema/household/group_events.py +++ b/mealie/schema/household/group_events.py @@ -30,6 +30,8 @@ class GroupEventNotifierOptions(MealieModel): data_import: bool = False mealplan_entry_created: bool = False + mealplan_entry_updated: bool = False + mealplan_entry_deleted: bool = False shopping_list_created: bool = False shopping_list_updated: bool = False diff --git a/mealie/services/event_bus_service/event_types.py b/mealie/services/event_bus_service/event_types.py index b14bdd9af..df90c8559 100644 --- a/mealie/services/event_bus_service/event_types.py +++ b/mealie/services/event_bus_service/event_types.py @@ -36,6 +36,8 @@ class EventTypes(Enum): data_import = auto() mealplan_entry_created = auto() + mealplan_entry_updated = auto() + mealplan_entry_deleted = auto() shopping_list_created = auto() shopping_list_updated = auto() @@ -89,9 +91,8 @@ class EventDocumentDataBase(MealieModel): ... -class EventMealplanCreatedData(EventDocumentDataBase): +class EventMealplanData(EventDocumentDataBase): document_type: EventDocumentType = EventDocumentType.mealplan - operation: EventOperation = EventOperation.create mealplan_id: int date: date recipe_id: UUID4 | None = None diff --git a/tests/integration_tests/user_household_tests/test_group_notifications.py b/tests/integration_tests/user_household_tests/test_group_notifications.py index 3bd2c0c1a..d7e0a83cb 100644 --- a/tests/integration_tests/user_household_tests/test_group_notifications.py +++ b/tests/integration_tests/user_household_tests/test_group_notifications.py @@ -26,6 +26,8 @@ def preferences_generator(): data_export=random_bool(), data_import=random_bool(), mealplan_entry_created=random_bool(), + mealplan_entry_updated=random_bool(), + mealplan_entry_deleted=random_bool(), shopping_list_created=random_bool(), shopping_list_updated=random_bool(), shopping_list_deleted=random_bool(),