2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 04:25:42 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters
2022-10-22 19:57:50 +11:00
10 changed files with 80 additions and 18 deletions

View File

@ -21,10 +21,16 @@ jobs:
INVENTREE_MEDIA_ROOT: ./media INVENTREE_MEDIA_ROOT: ./media
INVENTREE_STATIC_ROOT: ./static INVENTREE_STATIC_ROOT: ./static
INVENTREE_BACKUP_DIR: ./backup INVENTREE_BACKUP_DIR: ./backup
python_version: 3.9
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0 uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0
- name: Set Up Python ${{ env.python_version }}
uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # pin@v4.3.0
with:
python-version: ${{ env.python_version }}
cache: 'pip'
- name: Install Dependencies - name: Install Dependencies
run: | run: |
sudo apt-get update sudo apt-get update

View File

@ -30,10 +30,15 @@ jobs:
id-token: write id-token: write
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
python_version: 3.9
steps: steps:
- name: Check out repo - name: Check out repo
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0 uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0
- name: Set Up Python ${{ env.python_version }}
uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # pin@v4.3.0
with:
python-version: ${{ env.python_version }}
cache: 'pip'
- name: Version Check - name: Version Check
run: | run: |
pip install requests pip install requests

View File

@ -557,11 +557,13 @@ class BuildTest(BuildTestBase):
category='build.new_build', category='build.new_build',
) )
self.assertEqual(messages.count(), 2) self.assertEqual(messages.count(), 1)
self.assertFalse(messages.filter(user__pk=2).exists()) self.assertFalse(messages.filter(user__pk=2).exists())
self.assertTrue(messages.filter(user__pk=3).exists()) # Inactive users do not receive notifications
self.assertFalse(messages.filter(user__pk=3).exists())
self.assertTrue(messages.filter(user__pk=4).exists()) self.assertTrue(messages.filter(user__pk=4).exists())

View File

@ -243,8 +243,9 @@ class UIMessageNotification(SingleNotificationMethod):
METHOD_NAME = 'ui_message' METHOD_NAME = 'ui_message'
def get_targets(self): def get_targets(self):
"""Just return the targets - no tricks here.""" """Only send notifications for active users"""
return self.targets
return [target for target in self.targets if target.is_active]
def send(self, target): def send(self, target):
"""Send a UI notification to a user.""" """Send a UI notification to a user."""

View File

@ -778,7 +778,8 @@ class NotificationTest(InvenTreeAPITestCase):
messages = NotificationMessage.objects.all() messages = NotificationMessage.objects.all()
# As there are three staff users (including the 'test' user) we expect 30 notifications # As there are three staff users (including the 'test' user) we expect 30 notifications
self.assertEqual(messages.count(), 30) # However, one user is marked as i nactive
self.assertEqual(messages.count(), 20)
# Only 10 messages related to *this* user # Only 10 messages related to *this* user
my_notifications = messages.filter(user=self.user) my_notifications = messages.filter(user=self.user)
@ -822,7 +823,7 @@ class NotificationTest(InvenTreeAPITestCase):
# Only 7 notifications should have been deleted, # Only 7 notifications should have been deleted,
# as the notifications associated with other users must remain untouched # as the notifications associated with other users must remain untouched
self.assertEqual(NotificationMessage.objects.count(), 23) self.assertEqual(NotificationMessage.objects.count(), 13)
self.assertEqual(NotificationMessage.objects.filter(user=self.user).count(), 3) self.assertEqual(NotificationMessage.objects.filter(user=self.user).count(), 3)

View File

@ -267,7 +267,7 @@ class SalesOrderTest(TestCase):
category='order.overdue_sales_order', category='order.overdue_sales_order',
) )
self.assertEqual(len(messages), 2) self.assertEqual(len(messages), 1)
def test_new_so_notification(self): def test_new_so_notification(self):
"""Test that a notification is sent when a new SalesOrder is created. """Test that a notification is sent when a new SalesOrder is created.

View File

@ -326,6 +326,11 @@ class OrderTest(TestCase):
user__id=user_id, user__id=user_id,
) )
# User ID 3 is inactive, and thus should not receive notifications
if user_id == 3:
self.assertFalse(messages.exists())
continue
else:
self.assertTrue(messages.exists()) self.assertTrue(messages.exists())
msg = messages.first() msg = messages.first()

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from allauth.account.models import EmailAddress from allauth.account.models import EmailAddress
import common.models import common.models
import InvenTree.helpers
import InvenTree.tasks import InvenTree.tasks
from plugin import InvenTreePlugin from plugin import InvenTreePlugin
from plugin.mixins import BulkNotificationMethod, SettingsMixin from plugin.mixins import BulkNotificationMethod, SettingsMixin
@ -61,7 +62,12 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
allowed_users = [] allowed_users = []
for user in self.targets: for user in self.targets:
allows_emails = self.usersetting(user)
if not user.is_active:
# Ignore any users who have been deactivated
continue
allows_emails = InvenTree.helpers.str2bool(self.usersetting(user))
if allows_emails: if allows_emails:
allowed_users.append(user) allowed_users.append(user)

View File

@ -1036,9 +1036,12 @@ function loadBuildOutputTable(build_info, options={}) {
// Now that the allocations have been grouped by stock item, // Now that the allocations have been grouped by stock item,
// we can update each row in the table, // we can update each row in the table,
// using the pk value of each row (stock item) // using the pk value of each row (stock item)
var data = [];
rows.forEach(function(row) { rows.forEach(function(row) {
row.allocations = allocations[row.pk] || []; row.allocations = allocations[row.pk] || [];
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true); data.push(row);
var n_completed_lines = 0; var n_completed_lines = 0;
@ -1066,6 +1069,9 @@ function loadBuildOutputTable(build_info, options={}) {
} }
}); });
}); });
// Reload table with updated data
$(table).bootstrapTable('load', data);
} }
} }
); );
@ -1108,6 +1114,7 @@ function loadBuildOutputTable(build_info, options={}) {
{ {
success: function(results) { success: function(results) {
var data = [];
// Iterate through each row and find matching test results // Iterate through each row and find matching test results
rows.forEach(function(row) { rows.forEach(function(row) {
var test_results = {}; var test_results = {};
@ -1124,8 +1131,10 @@ function loadBuildOutputTable(build_info, options={}) {
row.passed_tests = test_results; row.passed_tests = test_results;
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true); data.push(row);
}); });
$(table).bootstrapTable('load', row);
} }
} }
); );
@ -1466,18 +1475,20 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
function redrawAllocationData() { function redrawAllocationData() {
// Force a refresh of each row in the table // Force a refresh of each row in the table
// Note we cannot call 'refresh' because we are passing data from memory // Note we cannot call 'refresh' because we are passing data from memory
// var rows = $(table).bootstrapTable('getData');
// How many rows are fully allocated? // How many rows are fully allocated?
var allocated_rows = 0; var allocated_rows = 0;
bom_items.forEach(function(row) { for (var idx = 0; idx < bom_items.length; idx++) {
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true); var row = bom_items[idx];
if (isRowFullyAllocated(row)) { if (isRowFullyAllocated(row)) {
allocated_rows += 1; allocated_rows++;
} }
}); }
// Reload table data
$(table).bootstrapTable('load', bom_items);
// Find the top-level progess bar for this build output // Find the top-level progess bar for this build output
var output_progress_bar = $(`#output-progress-${outputId}`); var output_progress_bar = $(`#output-progress-${outputId}`);
@ -1675,7 +1686,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
setupCallbacks(); setupCallbacks();
}, },
sortable: true, sortable: true,
showColumns: false, showColumns: true,
detailView: true, detailView: true,
detailFilter: function(index, row) { detailFilter: function(index, row) {
return allocatedQuantity(row) > 0; return allocatedQuantity(row) > 0;
@ -1806,6 +1817,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
field: 'sub_part_detail.full_name', field: 'sub_part_detail.full_name',
title: '{% trans "Required Part" %}', title: '{% trans "Required Part" %}',
sortable: true, sortable: true,
switchable: false,
formatter: function(value, row) { formatter: function(value, row) {
var url = `/part/${row.sub_part}/`; var url = `/part/${row.sub_part}/`;
var thumb = row.sub_part_detail.thumbnail; var thumb = row.sub_part_detail.thumbnail;
@ -1830,16 +1842,37 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
field: 'reference', field: 'reference',
title: '{% trans "Reference" %}', title: '{% trans "Reference" %}',
sortable: true, sortable: true,
switchable: true,
},
{
field: 'consumable',
title: '{% trans "Consumable" %}',
sortable: true,
switchable: true,
formatter: function(value) {
return yesNoLabel(value);
}
},
{
field: 'optional',
title: '{% trans "Optional" %}',
sortable: true,
switchable: true,
formatter: function(value) {
return yesNoLabel(value);
}
}, },
{ {
field: 'quantity', field: 'quantity',
title: '{% trans "Quantity Per" %}', title: '{% trans "Quantity Per" %}',
sortable: true, sortable: true,
switchable: false,
}, },
{ {
field: 'available_stock', field: 'available_stock',
title: '{% trans "Available" %}', title: '{% trans "Available" %}',
sortable: true, sortable: true,
switchable: true,
formatter: function(value, row) { formatter: function(value, row) {
var url = `/part/${row.sub_part_detail.pk}/?display=part-stock`; var url = `/part/${row.sub_part_detail.pk}/?display=part-stock`;
@ -1903,6 +1936,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
field: 'allocated', field: 'allocated',
title: '{% trans "Allocated" %}', title: '{% trans "Allocated" %}',
sortable: true, sortable: true,
switchable: false,
formatter: function(value, row) { formatter: function(value, row) {
var required = requiredQuantity(row); var required = requiredQuantity(row);
var allocated = row.consumable ? required : allocatedQuantity(row); var allocated = row.consumable ? required : allocatedQuantity(row);
@ -1943,6 +1977,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
{ {
field: 'actions', field: 'actions',
title: '{% trans "Actions" %}', title: '{% trans "Actions" %}',
switchable: false,
sortable: false,
formatter: function(value, row) { formatter: function(value, row) {
if (row.consumable) { if (row.consumable) {