mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-02 03:30:54 +00:00
Overdue order notification (#3114)
* Adds a background task to notify users when a PurchaseOrder becomes overdue * Schedule the overdue purchaseorder check to occur daily * Allow notifications to be sent to "Owner" instances - Extract user information from the Owner instance * add unit test to ensure notifications are sent for overdue purchase orders * Adds notification for overdue sales orders * Clean up notification display panel - Simplify rendering - Order "newest at top" - Element alignment tweaks * style fixes * More style fixes * Tweak notification padding * Fix import order * Adds task to notify user of overdue build orders * Adds unit tests for build order notifications * Refactor subject line for emails: - Use the configured instance title as a prefix for the subject line * Add email template for overdue build orders * Fix unit tests to accommodate new default value * Logic error fix
This commit is contained in:
@ -274,6 +274,7 @@ class NotificationList(generics.ListAPIView):
|
||||
'category',
|
||||
'name',
|
||||
'read',
|
||||
'creation',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
|
@ -710,7 +710,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
|
||||
'INVENTREE_INSTANCE': {
|
||||
'name': _('Server Instance Name'),
|
||||
'default': 'InvenTree server',
|
||||
'default': 'InvenTree',
|
||||
'description': _('String descriptor for the server instance'),
|
||||
},
|
||||
|
||||
|
@ -3,11 +3,15 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from common.models import NotificationEntry, NotificationMessage
|
||||
from InvenTree.helpers import inheritors
|
||||
from InvenTree.ready import isImportingData
|
||||
from plugin import registry
|
||||
from plugin.models import NotificationUserSetting
|
||||
from users.models import Owner
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
@ -266,7 +270,7 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
if isImportingData():
|
||||
return
|
||||
|
||||
# Resolve objekt reference
|
||||
# Resolve object reference
|
||||
obj_ref_value = getattr(obj, obj_ref)
|
||||
|
||||
# Try with some defaults
|
||||
@ -285,11 +289,33 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
return
|
||||
|
||||
logger.info(f"Gathering users for notification '{category}'")
|
||||
|
||||
# Collect possible targets
|
||||
if not targets:
|
||||
targets = target_fnc(*target_args, **target_kwargs)
|
||||
|
||||
# Convert list of targets to a list of users
|
||||
# (targets may include 'owner' or 'group' classes)
|
||||
target_users = set()
|
||||
|
||||
if targets:
|
||||
for target in targets:
|
||||
# User instance is provided
|
||||
if isinstance(target, get_user_model()):
|
||||
target_users.add(target)
|
||||
# Group instance is provided
|
||||
elif isinstance(target, Group):
|
||||
for user in get_user_model().objects.filter(groups__name=target.name):
|
||||
target_users.add(user)
|
||||
# Owner instance (either 'user' or 'group' is provided)
|
||||
elif isinstance(target, Owner):
|
||||
for owner in target.get_related_owners(include_group=False):
|
||||
target_users.add(owner.owner)
|
||||
# Unhandled type
|
||||
else:
|
||||
logger.error(f"Unknown target passed to trigger_notification method: {target}")
|
||||
|
||||
if target_users:
|
||||
logger.info(f"Sending notification '{category}' for '{str(obj)}'")
|
||||
|
||||
# Collect possible methods
|
||||
@ -301,7 +327,7 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
for method in delivery_methods:
|
||||
logger.info(f"Triggering notification method '{method.METHOD_NAME}'")
|
||||
try:
|
||||
deliver_notification(method, obj, category, targets, context)
|
||||
deliver_notification(method, obj, category, target_users, context)
|
||||
except NotImplementedError as error:
|
||||
# Allow any single notification method to fail, without failing the others
|
||||
logger.error(error)
|
||||
|
@ -78,7 +78,7 @@ class SettingsTest(InvenTreeTestCase):
|
||||
|
||||
# check as_int
|
||||
self.assertEqual(stale_days.as_int(), 0)
|
||||
self.assertEqual(instance_obj.as_int(), 'InvenTree server') # not an int -> return default
|
||||
self.assertEqual(instance_obj.as_int(), 'InvenTree') # not an int -> return default
|
||||
|
||||
# check as_bool
|
||||
self.assertEqual(report_test_obj.as_bool(), True)
|
||||
@ -258,7 +258,7 @@ class GlobalSettingsApiTest(InvenTreeAPITestCase):
|
||||
# Access via the API, and the default value should be received
|
||||
response = self.get(url, expected_code=200)
|
||||
|
||||
self.assertEqual(response.data['value'], 'InvenTree server')
|
||||
self.assertEqual(response.data['value'], 'InvenTree')
|
||||
|
||||
# Now, the object should have been created in the DB
|
||||
self.patch(
|
||||
|
Reference in New Issue
Block a user