Notify context (#11481)

* Pass extra HTML context through to "notify_responsible"

* Supply additional context

* Update template

* Add unit test for email template

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
OliverandMatthias Mair authored and GitHub committed 2026-10-14 12:43:28 +11:00
1 parent de962f5144
commit 31bc5a043c
4 files changed
+96 -3

No files matched your search

@@ -345,6 +345,7 @@ def notify_responsible(
instance,
sender,
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
html_context: Optional[dict] = None,
exclude=None,
extra_users: Optional[list] = None,
):
@@ -357,6 +358,7 @@ def notify_responsible(
instance: The newly created instance
sender: Sender model reference
content (NotificationBody, optional): _description_. Defaults to InvenTreeNotificationBodies.NewOrder.
html_context: Optional HTML context to include in the notification. Defaults to None.
exclude (User, optional): User instance that should be excluded. Defaults to None.
extra_users (list, optional): List of extra users to notify. Defaults to None.
"""
@@ -370,7 +372,14 @@ def notify_responsible(
if extra_users:
users.extend(extra_users)
notify_users(users, instance, sender, content=content, exclude=exclude)
notify_users(
users,
instance,
sender,
content=content,
html_context=html_context,
exclude=exclude,
)
def notify_users(
@@ -378,6 +387,7 @@ def notify_users(
instance,
sender,
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
html_context: Optional[dict] = None,
exclude=None,
):
"""Notify all passed users or groups.
@@ -390,6 +400,7 @@ def notify_users(
instance: The newly created instance
sender: Sender model reference
content (NotificationBody, optional): _description_. Defaults to InvenTreeNotificationBodies.NewOrder.
html_context (dict, optional): Optional HTML context to include in the notification. Defaults to None.
exclude (User, optional): User instance that should be excluded. Defaults to None.
"""
# Setup context for notification parsing
@@ -401,7 +412,9 @@ def notify_users(
}
# Setup notification context
# Extra HTML context is included, but cannot override the standard keys
context = {
**(html_context or {}),
'instance': instance,
'name': content.name.format(**content_context),
'message': content.message.format(**content_context),
@@ -409,8 +422,7 @@ def notify_users(
'template': {'subject': content.name.format(**content_context)},
}
tmp = content.template
if tmp:
if tmp := content.template:
context['template']['html'] = tmp.format(**content_context)
# Create notification
+1
View File
@@ -1322,6 +1322,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
PurchaseOrder,
exclude=user,
content=InvenTreeNotificationBodies.ItemsReceived,
html_context={'order': self, 'stock_items': stock_items},
extra_users=notify_users,
)
+64
View File
@@ -6,6 +6,7 @@ from decimal import Decimal
import django.core.exceptions as django_exceptions
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core import mail
from django.test import TestCase
from djmoney.money import Money
@@ -22,6 +23,7 @@ from InvenTree.unit_test import (
)
from order.status_codes import PurchaseOrderStatus
from part.models import Part
from plugin import registry
from stock.models import StockItem, StockLocation
from users.models import Owner
@@ -556,6 +558,68 @@ class OrderTest(ExchangeRateMixin, PluginRegistryMixin, TestCase):
# However *no* notification should have been generated for the creating user
self.assertFalse(messages.filter(user__pk=3).exists())
def test_received_email_notification(self):
"""Test the email notification sent when items are received against a PurchaseOrder.
- The responsible user should receive an email
- The email should list the received stock items
"""
user = get_user_model().objects.get(pk=4)
user.email = 'sam@demo.inventree.org'
user.save()
# Enable email notifications for the responsible user
plugin = registry.get_plugin('inventree-email-notification')
self.assertIsNotNone(plugin)
plugin.set_user_setting('NOTIFY_BY_EMAIL', True, user)
po = PurchaseOrder.objects.get(pk=1)
po.responsible = Owner.create(obj=user)
po.save()
po.place_order()
# Ignore any emails sent when the order was placed
common.models.NotificationEntry.objects.all().delete()
mail.outbox.clear()
location = StockLocation.objects.get(pk=1)
line_1 = PurchaseOrderLineItem.objects.get(pk=1)
line_2 = PurchaseOrderLineItem.objects.get(pk=3)
stock_items = po.receive_line_items(
location,
[
{'line_item': line_1, 'quantity': 15},
{'line_item': line_2, 'quantity': 37},
],
user=get_user_model().objects.get(pk=1),
)
self.assertEqual(stock_items.count(), 2)
# Exactly one email should have been sent, to the responsible user
self.assertEqual(len(mail.outbox), 1)
email = mail.outbox[0]
self.assertEqual(email.to, [user.email])
self.assertIn('Items Received', email.subject)
html = email.alternatives[0][0]
# Order information
self.assertIn('Items have been received against a purchase order', html)
self.assertIn(po.get_absolute_url(), html)
# Table of received items
self.assertIn('<th>Part</th>', html)
self.assertIn('<th>Quantity</th>', html)
self.assertIn('<th>Location</th>', html)
for item in stock_items:
self.assertIn(f'<td>{item.part.name}</td>', html)
self.assertIn(f'<td>{item.quantity}</td>', html)
self.assertIn(f'<td>{item.location.pathstring}</td>', html)
def test_metadata(self):
"""Unit tests for the metadata field."""
for model in [PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderExtraLine]:
@@ -9,3 +9,19 @@
<p>{% trans "Click on the following link to view this order" %}: <a href='{{ link }}'>{{ link }}</a></p>
{% endif %}
{% endblock title %}
{% block body %}
<tr style="height: 3rem; border-bottom: 1px solid">
<th>{% trans "Part" %}</th>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Location" %}</th>
</tr>
{% for item in stock_items %}
<tr>
<td>{{ item.part.name }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.location.pathstring }}</td>
</tr>
{% endfor %}
{% endblock body %}