2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-02 03:30:54 +00:00

Notification on new orders (#3145)

* Trigger a notification when a new SalesOrder is created

- Notify the "responsible" owners (excluding the creator)
- Add unit test for new notification

* Adds notification when a new PurchaseOrder is created

* Add notification when a new build order is created

- Includes unit tests

* Refactor order notification code

- Adds a "exclude users" option for sending notifications

* Fixes for notification refactoring

* make notification a helper

* reduce statements togehter

* make reuse easier

* Add docs

* Make context variables clearer

* fix assertation

* Fix set notation

Co-authored-by: Matthias <code@mjmair.com>
This commit is contained in:
Oliver
2022-06-07 08:11:11 +10:00
committed by GitHub
parent 00b75d792e
commit 6b038d85b6
8 changed files with 232 additions and 37 deletions

View File

@ -1,13 +1,15 @@
"""Base classes and functions for notifications."""
import logging
from dataclasses import dataclass
from datetime import timedelta
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.utils.translation import gettext_lazy as _
import InvenTree.helpers
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
@ -179,7 +181,7 @@ class MethodStorageClass:
selected_classes (class, optional): References to the classes that should be registered. Defaults to None.
"""
logger.info('collecting notification methods')
current_method = inheritors(NotificationMethod) - IGNORED_NOTIFICATION_CLS
current_method = InvenTree.helpers.inheritors(NotificationMethod) - IGNORED_NOTIFICATION_CLS
# for testing selective loading is made available
if selected_classes:
@ -257,12 +259,51 @@ class UIMessageNotification(SingleNotificationMethod):
return True
@dataclass()
class NotificationBody:
"""Information needed to create a notification.
Attributes:
name (str): Name (or subject) of the notification
slug (str): Slugified reference for notification
message (str): Notification message as text. Should not be longer than 120 chars.
template (str): Reference to the html template for the notification.
The strings support f-string sytle fomratting with context variables parsed at runtime.
Context variables:
instance: Text representing the instance
verbose_name: Verbose name of the model
app_label: App label (slugified) of the model
model_name': Name (slugified) of the model
"""
name: str
slug: str
message: str
template: str
class InvenTreeNotificationBodies:
"""Default set of notifications for InvenTree.
Contains regularly used notification bodies.
"""
NewOrder = NotificationBody(
name=_("New {verbose_name}"),
slug='{app_label}.new_{model_name}',
message=_("A new {verbose_name} has been created and ,assigned to you"),
template='email/new_order_assigned.html',
)
"""Send when a new order (build, sale or purchase) was created."""
def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
"""Send out a notification."""
targets = kwargs.get('targets', None)
target_fnc = kwargs.get('target_fnc', None)
target_args = kwargs.get('target_args', [])
target_kwargs = kwargs.get('target_kwargs', {})
target_exclude = kwargs.get('target_exclude', None)
context = kwargs.get('context', {})
delivery_methods = kwargs.get('delivery_methods', None)
@ -290,6 +331,9 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
logger.info(f"Gathering users for notification '{category}'")
if target_exclude is None:
target_exclude = set()
# Collect possible targets
if not targets:
targets = target_fnc(*target_args, **target_kwargs)
@ -302,15 +346,19 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
for target in targets:
# User instance is provided
if isinstance(target, get_user_model()):
target_users.add(target)
if target not in target_exclude:
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)
if user not in target_exclude:
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)
user = owner.owner
if user not in target_exclude:
target_users.add(user)
# Unhandled type
else:
logger.error(f"Unknown target passed to trigger_notification method: {target}")