From 91834621244c53e17e9b9148aabfe84fe6dcb1d1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 10:40:44 +0000 Subject: [PATCH 01/10] Add global setting to control notifications --- .../InvenTree/common/setting/system.py | 6 +++++ .../pages/Index/Settings/SystemSettings.tsx | 22 +++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index ff54f947143f..19eaa86fa869 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -318,6 +318,12 @@ def __call__(self, value): 'units': _('days'), 'validator': [int, MinValueValidator(7)], }, + 'INVENTREE_NOTIFICATIONS_ENABLE': { + 'name': _('Enable Notifications'), + 'description': _('Enable user notifications for system events'), + 'default': True, + 'validator': bool, + }, 'BARCODE_ENABLE': { 'name': _('Barcode Support'), 'description': _('Enable barcode scanner support in the web interface'), diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index e77831655ab1..d16c193580b9 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -1,12 +1,11 @@ import { t } from '@lingui/core/macro'; -import { Alert, Skeleton, Stack, Text } from '@mantine/core'; +import { Skeleton, Stack } from '@mantine/core'; import { IconBellCog, IconCategory, IconCurrencyDollar, IconFileAnalytics, IconFingerprint, - IconInfoCircle, IconPackages, IconQrcode, IconServerCog, @@ -57,9 +56,7 @@ export default function SystemSettings() { 'INVENTREE_STRICT_URLS', 'INVENTREE_BACKUP_ENABLE', 'INVENTREE_BACKUP_DAYS', - 'INVENTREE_DELETE_TASKS_DAYS', - 'INVENTREE_DELETE_ERRORS_DAYS', - 'INVENTREE_DELETE_NOTIFICATIONS_DAYS' + 'INVENTREE_DELETE_TASKS_DAYS' ]} /> ) @@ -113,15 +110,12 @@ export default function SystemSettings() { label: t`Notifications`, icon: , content: ( - - } - > - This panel has not yet been implemented - - + ) }, { From 14fa129375a9fbd7a09372e347470b01ff879d30 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 10:49:44 +0000 Subject: [PATCH 02/10] Hide UI elements --- src/frontend/src/components/nav/Header.tsx | 38 ++++++++++--------- .../src/components/nav/NavigationDrawer.tsx | 5 ++- src/frontend/src/pages/Notifications.tsx | 9 +++++ 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx index d6e2b12eb815..9d6ac2a0eae3 100644 --- a/src/frontend/src/components/nav/Header.tsx +++ b/src/frontend/src/components/nav/Header.tsx @@ -145,24 +145,26 @@ export function Header() { {globalSettings.isSet('BARCODE_ENABLE') && } - - - - - - - + {globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE') && ( + + + + + + + + )} diff --git a/src/frontend/src/components/nav/NavigationDrawer.tsx b/src/frontend/src/components/nav/NavigationDrawer.tsx index 35ffe6952143..494fd3efb9b9 100644 --- a/src/frontend/src/components/nav/NavigationDrawer.tsx +++ b/src/frontend/src/components/nav/NavigationDrawer.tsx @@ -140,7 +140,8 @@ function DrawerContent({ closeFunc }: Readonly<{ closeFunc?: () => void }>) { id: 'notifications', title: t`Notifications`, link: '/notifications', - icon: 'notification' + icon: 'notification', + hidden: !globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE') }, { id: 'user-settings', @@ -163,7 +164,7 @@ function DrawerContent({ closeFunc }: Readonly<{ closeFunc?: () => void }>) { hidden: !user.isStaff() } ]; - }, [user]); + }, [globalSettings, user]); const menuItemsDocumentation: MenuLinkItem[] = useMemo( () => DocumentationLinks(), diff --git a/src/frontend/src/pages/Notifications.tsx b/src/frontend/src/pages/Notifications.tsx index f978a5a75384..a5e7215986d9 100644 --- a/src/frontend/src/pages/Notifications.tsx +++ b/src/frontend/src/pages/Notifications.tsx @@ -12,15 +12,19 @@ import { useCallback, useMemo } from 'react'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { apiUrl } from '@lib/functions/Api'; +import { useNavigate } from 'react-router-dom'; import { ActionButton } from '../components/buttons/ActionButton'; import { PageDetail } from '../components/nav/PageDetail'; import { PanelGroup } from '../components/panels/PanelGroup'; import { useApi } from '../contexts/ApiContext'; import { useTable } from '../hooks/UseTable'; +import { useGlobalSettingsState } from '../states/SettingsState'; import { NotificationTable } from '../tables/notifications/NotificationTable'; export default function NotificationsPage() { const api = useApi(); + const navigate = useNavigate(); + const globalSettings = useGlobalSettingsState(); const unreadTable = useTable('unreadnotifications'); const readTable = useTable('readnotifications'); @@ -125,6 +129,11 @@ export default function NotificationsPage() { ]; }, [unreadTable, readTable]); + if (!globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE')) { + // Redirect to the dashboard if notifications are not enabled + navigate('/'); + } + return ( From 0b3a5e8bdbb952bb7d121f954a1e2b5d699ca00d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:01:53 +0000 Subject: [PATCH 03/10] Prevent notifications if setting is disabled --- src/backend/InvenTree/common/notifications.py | 5 +++++ src/backend/InvenTree/common/settings.py | 19 +++++++++++++++---- src/backend/InvenTree/part/tasks.py | 10 +++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/backend/InvenTree/common/notifications.py b/src/backend/InvenTree/common/notifications.py index 26f1935de8b5..a816eef5c05d 100644 --- a/src/backend/InvenTree/common/notifications.py +++ b/src/backend/InvenTree/common/notifications.py @@ -13,6 +13,7 @@ import common.models import InvenTree.helpers +from common.settings import notifications_enabled from InvenTree.ready import isImportingData, isRebuildingData from plugin import registry from plugin.models import NotificationUserSetting, PluginConfig @@ -374,6 +375,10 @@ def trigger_notification( if isImportingData() or isRebuildingData(): return + # Ignore if notifications are disabled globally + if not notifications_enabled(): + return + targets = kwargs.get('targets') target_fnc = kwargs.get('target_fnc') target_args = kwargs.get('target_args', []) diff --git a/src/backend/InvenTree/common/settings.py b/src/backend/InvenTree/common/settings.py index e1bc11f51824..82ca833cdd5d 100644 --- a/src/backend/InvenTree/common/settings.py +++ b/src/backend/InvenTree/common/settings.py @@ -28,14 +28,25 @@ def set_global_setting(key, value, change_user=None, create=True, **kwargs): return InvenTreeSetting.set_setting(key, value, **kwargs) -def stock_expiry_enabled(): - """Returns True if the stock expiry feature is enabled.""" +def stock_expiry_enabled() -> bool: + """Helper function which returns True if the stock expiry feature is enabled.""" from common.models import InvenTreeSetting - return InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY', False, create=False) + return InvenTreeSetting.get_setting( + 'STOCK_ENABLE_EXPIRY', backup_value=False, create=False + ) + + +def notifications_enabled() -> bool: + """Helper function which returns True if the notifications feature is enabled.""" + from common.models import InvenTreeSetting + + return InvenTreeSetting.get_setting( + 'INVENTREE_NOTIFICATIONS_ENABLE', backup_value=True, create=False + ) -def prevent_build_output_complete_on_incompleted_tests(): +def prevent_build_output_complete_on_incompleted_tests() -> bool: """Returns True if the completion of the build outputs is disabled until the required tests are passed.""" from common.models import InvenTreeSetting diff --git a/src/backend/InvenTree/part/tasks.py b/src/backend/InvenTree/part/tasks.py index 8d70537fb39a..4c6ca5fcbe31 100644 --- a/src/backend/InvenTree/part/tasks.py +++ b/src/backend/InvenTree/part/tasks.py @@ -12,12 +12,11 @@ import common.currency import common.notifications import company.models -import InvenTree.helpers import InvenTree.helpers_model import InvenTree.tasks import part.models as part_models import part.stocktake -from common.settings import get_global_setting +from common.settings import get_global_setting, notifications_enabled from InvenTree.tasks import ( ScheduledTask, check_daily_holdoff, @@ -52,11 +51,16 @@ def notify_low_stock(part: part_models.Part): ) -def notify_low_stock_if_required(part_id: int): +def notify_low_stock_if_required(part_id: int) -> None: """Check if the stock quantity has fallen below the minimum threshold of part. If true, notify the users who have subscribed to the part """ + # Return early if notifications are not enabled + # This prevents additional tasks from being queued unnecessarily + if not notifications_enabled(): + return + try: part = part_models.Part.objects.get(pk=part_id) except part_models.Part.DoesNotExist: From 0c71dad6cf2fc834e88951239953adb8abba83db Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:09:21 +0000 Subject: [PATCH 04/10] Add specific setting for "low stock" notification category --- .../InvenTree/common/setting/system.py | 19 ++++++++++++------- src/backend/InvenTree/common/settings.py | 2 +- src/backend/InvenTree/part/tasks.py | 3 +++ src/frontend/src/components/nav/Header.tsx | 2 +- .../src/components/nav/NavigationDrawer.tsx | 2 +- .../pages/Index/Settings/SystemSettings.tsx | 3 ++- src/frontend/src/pages/Notifications.tsx | 2 +- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index 19eaa86fa869..1512542681fd 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -15,7 +15,6 @@ import build.validators import common.currency -import common.models import common.validators import order.validators import report.helpers @@ -309,6 +308,18 @@ def __call__(self, value): 'units': _('days'), 'validator': [int, MinValueValidator(7)], }, + 'NOTIFICATIONS_ENABLE': { + 'name': _('Enable Notifications'), + 'description': _('Enable user notifications for system events'), + 'default': True, + 'validator': bool, + }, + 'NOTIFICATIONS_LOW_STOCK': { + 'name': _('Low Stock Notifications'), + 'description': _('Enable notifications when a part is low on stock'), + 'default': True, + 'validator': bool, + }, 'INVENTREE_DELETE_NOTIFICATIONS_DAYS': { 'name': _('Notification Deletion Interval'), 'description': _( @@ -318,12 +329,6 @@ def __call__(self, value): 'units': _('days'), 'validator': [int, MinValueValidator(7)], }, - 'INVENTREE_NOTIFICATIONS_ENABLE': { - 'name': _('Enable Notifications'), - 'description': _('Enable user notifications for system events'), - 'default': True, - 'validator': bool, - }, 'BARCODE_ENABLE': { 'name': _('Barcode Support'), 'description': _('Enable barcode scanner support in the web interface'), diff --git a/src/backend/InvenTree/common/settings.py b/src/backend/InvenTree/common/settings.py index 82ca833cdd5d..5602a3d12136 100644 --- a/src/backend/InvenTree/common/settings.py +++ b/src/backend/InvenTree/common/settings.py @@ -42,7 +42,7 @@ def notifications_enabled() -> bool: from common.models import InvenTreeSetting return InvenTreeSetting.get_setting( - 'INVENTREE_NOTIFICATIONS_ENABLE', backup_value=True, create=False + 'NOTIFICATIONS_ENABLE', backup_value=True, create=False ) diff --git a/src/backend/InvenTree/part/tasks.py b/src/backend/InvenTree/part/tasks.py index 4c6ca5fcbe31..9001198e9384 100644 --- a/src/backend/InvenTree/part/tasks.py +++ b/src/backend/InvenTree/part/tasks.py @@ -61,6 +61,9 @@ def notify_low_stock_if_required(part_id: int) -> None: if not notifications_enabled(): return + if not get_global_setting('NOTIFICATIONS_LOW_STOCK', backup_value=True): + return + try: part = part_models.Part.objects.get(pk=part_id) except part_models.Part.DoesNotExist: diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx index 9d6ac2a0eae3..9eb2ec421c4d 100644 --- a/src/frontend/src/components/nav/Header.tsx +++ b/src/frontend/src/components/nav/Header.tsx @@ -145,7 +145,7 @@ export function Header() { {globalSettings.isSet('BARCODE_ENABLE') && } - {globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE') && ( + {globalSettings.isSet('NOTIFICATIONS_ENABLE') && ( void }>) { title: t`Notifications`, link: '/notifications', icon: 'notification', - hidden: !globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE') + hidden: !globalSettings.isSet('NOTIFICATIONS_ENABLE') }, { id: 'user-settings', diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index d16c193580b9..4e600a2fa57d 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -112,7 +112,8 @@ export default function SystemSettings() { content: ( diff --git a/src/frontend/src/pages/Notifications.tsx b/src/frontend/src/pages/Notifications.tsx index a5e7215986d9..189040c03557 100644 --- a/src/frontend/src/pages/Notifications.tsx +++ b/src/frontend/src/pages/Notifications.tsx @@ -129,7 +129,7 @@ export default function NotificationsPage() { ]; }, [unreadTable, readTable]); - if (!globalSettings.isSet('INVENTREE_NOTIFICATIONS_ENABLE')) { + if (!globalSettings.isSet('NOTIFICATIONS_ENABLE')) { // Redirect to the dashboard if notifications are not enabled navigate('/'); } From 0d035a0ab32e0eac86cebc3af9791e97acecf0fe Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:15:45 +0000 Subject: [PATCH 05/10] Update docs for notifications --- docs/docs/concepts/notifications.md | 65 +++++++++++++++++++++++++++++ docs/docs/part/notification.md | 32 +------------- docs/mkdocs.yml | 1 + 3 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 docs/docs/concepts/notifications.md diff --git a/docs/docs/concepts/notifications.md b/docs/docs/concepts/notifications.md new file mode 100644 index 000000000000..3d5223786aa6 --- /dev/null +++ b/docs/docs/concepts/notifications.md @@ -0,0 +1,65 @@ +--- +title: Notifications +--- + +## Notifications System + +InvenTree provides a notification system which allows users to receive notifications about various events that occur within the system. + +## Notification Types + +### User Interface + +Notifications are displayed in the user interface. New notifications are announced in the header. +{% with id="notification_header", url="part/notification_header.png", description="One new notification in the header" %} +{% include 'img.html' %} +{% endwith %} + +They can be viewed in a flyout. +{% with id="notification_flyout", url="part/notification_flyout.png", description="One new notification in the flyout" %} +{% include 'img.html' %} +{% endwith %} + +All current notifications are listed in the inbox. +{% with id="notification_inbox", url="part/notification_inbox.png", description="One new notification in the notification inbox" %} +{% include 'img.html' %} +{% endwith %} + +All past notification are listed in the history. They can be deleted one-by-one or all at once from there. +{% with id="notification_history", url="part/notification_history.png", description="One old notification in the notification history" %} +{% include 'img.html' %} +{% endwith %} + +### Email + +Users can also receive notifications via email. This is particularly useful for important events that require immediate attention. + +!!! warning "Email Configuration Required" + External notifications require correct [email configuration](../start/config.md#email-settings). They also need to be enabled in the settings under notifications`. + +!!! warning "Valid Email Address" + Each user must have a valid email address associated with their account to receive email notifications + +### Third-Party Integrations + +In addition to the built-in notification system, InvenTree can be integrated with third-party services such as Slack or Discord to send notifications. This allows users to receive real-time updates in their preferred communication channels. + +Third party integrations rely on the [plugin system](../plugins/index.md) to provide the necessary functionality. + +## Notification Options + +The following [global settings](../settings/global.md) control the notification system: + +| Name | Description | Default | Units | +| ---- | ----------- | ------- | ----- | +{{ globalsetting("NOTIFICATION_ENABLE") }} +{{ globalsetting("NOTIFICATIONS_LOW_STOCK") }} +{{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }} + +## Notification Categories + +### Part Notifications + +There are many types of notifications associated with parts in InvenTree. These notifications can be configured to alert users about various events, such as low stock levels, build orders, and more. + +Read the [part notification documentation](../part/notification.md) for more details. diff --git a/docs/docs/part/notification.md b/docs/docs/part/notification.md index 745495434fee..b03e51280185 100644 --- a/docs/docs/part/notification.md +++ b/docs/docs/part/notification.md @@ -2,38 +2,10 @@ title: Part Notifications --- -## General Notification Details - -Users can select to receive notifications when certain events occur. - -!!! warning "Email Configuration Required" - External notifications require correct [email configuration](../start/config.md#email-settings). They also need to be enabled in the settings under notifications`. - -!!! warning "Valid Email Address" - Each user must have a valid email address associated with their account to receive email notifications - -Notifications are also shown in the user interface. New notifications are announced in the header. -{% with id="notification_header", url="part/notification_header.png", description="One new notification in the header" %} -{% include 'img.html' %} -{% endwith %} - -They can be viewed in a flyout. -{% with id="notification_flyout", url="part/notification_flyout.png", description="One new notification in the flyout" %} -{% include 'img.html' %} -{% endwith %} - -All current notifications are listed in the inbox. -{% with id="notification_inbox", url="part/notification_inbox.png", description="One new notification in the notification inbox" %} -{% include 'img.html' %} -{% endwith %} - -All past notification are listed in the history. They can be deleted one-by-one or all at once from there. -{% with id="notification_history", url="part/notification_history.png", description="One old notification in the notification history" %} -{% include 'img.html' %} -{% endwith %} - ## Part Notification Events +Multiple events can trigger [notifications](../concepts/notifications.md) related to *Parts* in InvenTree. These notifications can be delivered via the user interface, email, or third-party integrations. + ### Low Stock Notification If the *minimum stock* threshold is set for a *Part*, then a "low stock" notification can be generated when the stock level for that part falls below the configured level. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 08e9b73e1ed0..1b351e194c6b 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -82,6 +82,7 @@ nav: - Physical Units: concepts/units.md - Companies: concepts/company.md - Custom States: concepts/custom_states.md + - Notifications: concepts/notifications.md - Pricing: concepts/pricing.md - Project Codes: concepts/project_codes.md - Barcodes: From 81ea57b014602865a33a7adda4a5f0313e407f5e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:27:47 +0000 Subject: [PATCH 06/10] Remove duplicate setting doc --- docs/docs/settings/global.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/docs/settings/global.md b/docs/docs/settings/global.md index 68bfbcd0d98c..eaec5db7bea8 100644 --- a/docs/docs/settings/global.md +++ b/docs/docs/settings/global.md @@ -36,7 +36,6 @@ Configuration of basic server settings: {{ globalsetting("INVENTREE_BACKUP_DAYS") }} {{ globalsetting("INVENTREE_DELETE_TASKS_DAYS") }} {{ globalsetting("INVENTREE_DELETE_ERRORS_DAYS") }} -{{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }} ### Login Settings From d23cb273123ea00096d8471b86b7f4441799c58b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:33:25 +0000 Subject: [PATCH 07/10] Additional unit tests --- src/backend/InvenTree/part/tasks.py | 3 +++ src/backend/InvenTree/part/test_part.py | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/backend/InvenTree/part/tasks.py b/src/backend/InvenTree/part/tasks.py index 9001198e9384..35fcf9bc2b16 100644 --- a/src/backend/InvenTree/part/tasks.py +++ b/src/backend/InvenTree/part/tasks.py @@ -34,6 +34,9 @@ def notify_low_stock(part: part_models.Part): - Triggered when the available stock for a given part falls be low the configured threhsold - A notification is delivered to any users who are 'subscribed' to this part """ + if not get_global_setting('NOTIFICATIONS_LOW_STOCK', backup_value=True): + return + name = _('Low stock notification') message = _( f'The available stock for {part.name} has fallen below the configured minimum level' diff --git a/src/backend/InvenTree/part/test_part.py b/src/backend/InvenTree/part/test_part.py index 7931926945a5..c3585b5ececb 100644 --- a/src/backend/InvenTree/part/test_part.py +++ b/src/backend/InvenTree/part/test_part.py @@ -811,8 +811,8 @@ def _notification_run(self, run_class=None): self.part.set_starred(self.user, True) self.part.save() - # There should be 1 (or 2) notifications - in some cases an error is generated, which creates a subsequent notification - self.assertIn(NotificationEntry.objects.all().count(), [1, 2]) + # There should be 0, 1 or 2 notifications - in some cases an error is generated, which creates a subsequent notification + self.assertIn(NotificationEntry.objects.all().count(), [0, 1, 2]) class PartNotificationTest(BaseNotificationIntegrationTest): @@ -820,6 +820,10 @@ class PartNotificationTest(BaseNotificationIntegrationTest): def test_notification(self): """Test that a notification is generated.""" + # Ensure notifications are enabled + set_global_setting('NOTIFICATIONS_ENABLE', True) + set_global_setting('NOTIFICATIONS_LOW_STOCK', True) + self._notification_run(UIMessageNotification) # There should be 1 notification message right now @@ -830,3 +834,19 @@ def test_notification(self): # There should not be more messages self.assertEqual(NotificationMessage.objects.all().count(), 1) + + def test_disabled(self): + """Test that the notification is not generated if notifications are globally disabled.""" + set_global_setting('NOTIFICATIONS_ENABLE', False) + set_global_setting('NOTIFICATIONS_LOW_STOCK', True) + + self._notification_run(UIMessageNotification) + self.assertEqual(NotificationEntry.objects.all().count(), 0) + + def test_disabled_low_stock(self): + """Test that the notification is not generated if low stock notifications are disabled.""" + set_global_setting('NOTIFICATIONS_ENABLE', True) + set_global_setting('NOTIFICATIONS_LOW_STOCK', False) + + self._notification_run(UIMessageNotification) + self.assertEqual(NotificationEntry.objects.all().count(), 0) From 970c7e2fe7c51c0799c0be695478bf0c3abd3e53 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 11:43:33 +0000 Subject: [PATCH 08/10] Fix typo --- docs/docs/concepts/notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/concepts/notifications.md b/docs/docs/concepts/notifications.md index 3d5223786aa6..abc7c844c98d 100644 --- a/docs/docs/concepts/notifications.md +++ b/docs/docs/concepts/notifications.md @@ -52,7 +52,7 @@ The following [global settings](../settings/global.md) control the notification | Name | Description | Default | Units | | ---- | ----------- | ------- | ----- | -{{ globalsetting("NOTIFICATION_ENABLE") }} +{{ globalsetting("NOTIFICATIONS_ENABLE") }} {{ globalsetting("NOTIFICATIONS_LOW_STOCK") }} {{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }} From 80897b6d4b57066948da442a643272717097c8eb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 2 Jun 2025 13:58:15 +0000 Subject: [PATCH 09/10] Simplify unit test code --- src/backend/InvenTree/part/test_part.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/backend/InvenTree/part/test_part.py b/src/backend/InvenTree/part/test_part.py index c3585b5ececb..e00ca649f4aa 100644 --- a/src/backend/InvenTree/part/test_part.py +++ b/src/backend/InvenTree/part/test_part.py @@ -811,9 +811,6 @@ def _notification_run(self, run_class=None): self.part.set_starred(self.user, True) self.part.save() - # There should be 0, 1 or 2 notifications - in some cases an error is generated, which creates a subsequent notification - self.assertIn(NotificationEntry.objects.all().count(), [0, 1, 2]) - class PartNotificationTest(BaseNotificationIntegrationTest): """Integration test for part notifications.""" From e29997d6112eb8ffc800a619da2ccb88352c979f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 3 Jun 2025 05:56:12 +0000 Subject: [PATCH 10/10] Add setting to control error notifications --- docs/docs/concepts/notifications.md | 8 ++++++++ src/backend/InvenTree/InvenTree/models.py | 7 +++++++ src/backend/InvenTree/common/setting/system.py | 6 ++++++ src/frontend/src/pages/Index/Settings/SystemSettings.tsx | 1 + 4 files changed, 22 insertions(+) diff --git a/docs/docs/concepts/notifications.md b/docs/docs/concepts/notifications.md index abc7c844c98d..582ad871a880 100644 --- a/docs/docs/concepts/notifications.md +++ b/docs/docs/concepts/notifications.md @@ -53,11 +53,19 @@ The following [global settings](../settings/global.md) control the notification | Name | Description | Default | Units | | ---- | ----------- | ------- | ----- | {{ globalsetting("NOTIFICATIONS_ENABLE") }} +{{ globalsetting("NOTIFICATIONS_ERRORS") }} {{ globalsetting("NOTIFICATIONS_LOW_STOCK") }} {{ globalsetting("INVENTREE_DELETE_NOTIFICATIONS_DAYS") }} ## Notification Categories +### Error Notifications + +If enabled, InvenTree can send notifications about errors that occur within the system. This is useful for administrators to monitor the health of the application and address issues promptly. + +!!! info "Staff Only" + Error notifications are only sent to staff users. Regular users will not receive these notifications. + ### Part Notifications There are many types of notifications associated with parts in InvenTree. These notifications can be configured to alert users about various events, such as low stock levels, build orders, and more. diff --git a/src/backend/InvenTree/InvenTree/models.py b/src/backend/InvenTree/InvenTree/models.py index a70f5b601ae5..85ebfc753a07 100644 --- a/src/backend/InvenTree/InvenTree/models.py +++ b/src/backend/InvenTree/InvenTree/models.py @@ -1035,6 +1035,13 @@ def notify_staff_users_of_error(instance, label: str, context: dict): """Helper function to notify staff users of an error.""" import common.models import common.notifications + import common.settings + + # Return early if error notifications are not enabled + if not common.settings.get_global_setting( + 'NOTIFICATIONS_ERRORS', backup_value=True + ): + return try: # Get all staff users diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index 1512542681fd..cfad8dbbfeaa 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -314,6 +314,12 @@ def __call__(self, value): 'default': True, 'validator': bool, }, + 'NOTIFICATIONS_ERRORS': { + 'name': _('Error Notifications'), + 'description': _('Enable notifications for system errors'), + 'default': True, + 'validator': bool, + }, 'NOTIFICATIONS_LOW_STOCK': { 'name': _('Low Stock Notifications'), 'description': _('Enable notifications when a part is low on stock'), diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 4e600a2fa57d..d626aa0781ed 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -113,6 +113,7 @@ export default function SystemSettings() {