mirror of
https://github.com/inventree/InvenTree.git
synced 2026-10-16 23:21:09 +00:00
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:
1 parent
de962f5144
commit
31bc5a043c
4 files changed
+96
-3
No files matched your search
@@ -345,6 +345,7 @@ def notify_responsible(
|
|||||||
instance,
|
instance,
|
||||||
sender,
|
sender,
|
||||||
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
|
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
|
||||||
|
html_context: Optional[dict] = None,
|
||||||
exclude=None,
|
exclude=None,
|
||||||
extra_users: Optional[list] = None,
|
extra_users: Optional[list] = None,
|
||||||
):
|
):
|
||||||
@@ -357,6 +358,7 @@ def notify_responsible(
|
|||||||
instance: The newly created instance
|
instance: The newly created instance
|
||||||
sender: Sender model reference
|
sender: Sender model reference
|
||||||
content (NotificationBody, optional): _description_. Defaults to InvenTreeNotificationBodies.NewOrder.
|
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.
|
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.
|
extra_users (list, optional): List of extra users to notify. Defaults to None.
|
||||||
"""
|
"""
|
||||||
@@ -370,7 +372,14 @@ def notify_responsible(
|
|||||||
if extra_users:
|
if extra_users:
|
||||||
users.extend(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(
|
def notify_users(
|
||||||
@@ -378,6 +387,7 @@ def notify_users(
|
|||||||
instance,
|
instance,
|
||||||
sender,
|
sender,
|
||||||
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
|
content: NotificationBody = InvenTreeNotificationBodies.NewOrder,
|
||||||
|
html_context: Optional[dict] = None,
|
||||||
exclude=None,
|
exclude=None,
|
||||||
):
|
):
|
||||||
"""Notify all passed users or groups.
|
"""Notify all passed users or groups.
|
||||||
@@ -390,6 +400,7 @@ def notify_users(
|
|||||||
instance: The newly created instance
|
instance: The newly created instance
|
||||||
sender: Sender model reference
|
sender: Sender model reference
|
||||||
content (NotificationBody, optional): _description_. Defaults to InvenTreeNotificationBodies.NewOrder.
|
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.
|
exclude (User, optional): User instance that should be excluded. Defaults to None.
|
||||||
"""
|
"""
|
||||||
# Setup context for notification parsing
|
# Setup context for notification parsing
|
||||||
@@ -401,7 +412,9 @@ def notify_users(
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Setup notification context
|
# Setup notification context
|
||||||
|
# Extra HTML context is included, but cannot override the standard keys
|
||||||
context = {
|
context = {
|
||||||
|
**(html_context or {}),
|
||||||
'instance': instance,
|
'instance': instance,
|
||||||
'name': content.name.format(**content_context),
|
'name': content.name.format(**content_context),
|
||||||
'message': content.message.format(**content_context),
|
'message': content.message.format(**content_context),
|
||||||
@@ -409,8 +422,7 @@ def notify_users(
|
|||||||
'template': {'subject': content.name.format(**content_context)},
|
'template': {'subject': content.name.format(**content_context)},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp = content.template
|
if tmp := content.template:
|
||||||
if tmp:
|
|
||||||
context['template']['html'] = tmp.format(**content_context)
|
context['template']['html'] = tmp.format(**content_context)
|
||||||
|
|
||||||
# Create notification
|
# Create notification
|
||||||
|
|||||||
@@ -1322,6 +1322,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
|||||||
PurchaseOrder,
|
PurchaseOrder,
|
||||||
exclude=user,
|
exclude=user,
|
||||||
content=InvenTreeNotificationBodies.ItemsReceived,
|
content=InvenTreeNotificationBodies.ItemsReceived,
|
||||||
|
html_context={'order': self, 'stock_items': stock_items},
|
||||||
extra_users=notify_users,
|
extra_users=notify_users,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from decimal import Decimal
|
|||||||
import django.core.exceptions as django_exceptions
|
import django.core.exceptions as django_exceptions
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
|
from django.core import mail
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
@@ -22,6 +23,7 @@ from InvenTree.unit_test import (
|
|||||||
)
|
)
|
||||||
from order.status_codes import PurchaseOrderStatus
|
from order.status_codes import PurchaseOrderStatus
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
|
from plugin import registry
|
||||||
from stock.models import StockItem, StockLocation
|
from stock.models import StockItem, StockLocation
|
||||||
from users.models import Owner
|
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
|
# However *no* notification should have been generated for the creating user
|
||||||
self.assertFalse(messages.filter(user__pk=3).exists())
|
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):
|
def test_metadata(self):
|
||||||
"""Unit tests for the metadata field."""
|
"""Unit tests for the metadata field."""
|
||||||
for model in [PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderExtraLine]:
|
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>
|
<p>{% trans "Click on the following link to view this order" %}: <a href='{{ link }}'>{{ link }}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock title %}
|
{% 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 %}
|
||||||
Reference in new issue
Block a user