mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-15 19:45:46 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
6
.github/workflows/check_translations.yaml
vendored
6
.github/workflows/check_translations.yaml
vendored
@ -21,10 +21,16 @@ jobs:
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
INVENTREE_BACKUP_DIR: ./backup
|
||||
python_version: 3.9
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
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
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
7
.github/workflows/docker.yaml
vendored
7
.github/workflows/docker.yaml
vendored
@ -30,10 +30,15 @@ jobs:
|
||||
id-token: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
python_version: 3.9
|
||||
steps:
|
||||
- name: Check out repo
|
||||
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
|
||||
run: |
|
||||
pip install requests
|
||||
|
@ -557,11 +557,13 @@ class BuildTest(BuildTestBase):
|
||||
category='build.new_build',
|
||||
)
|
||||
|
||||
self.assertEqual(messages.count(), 2)
|
||||
self.assertEqual(messages.count(), 1)
|
||||
|
||||
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())
|
||||
|
||||
|
||||
|
@ -243,8 +243,9 @@ class UIMessageNotification(SingleNotificationMethod):
|
||||
METHOD_NAME = 'ui_message'
|
||||
|
||||
def get_targets(self):
|
||||
"""Just return the targets - no tricks here."""
|
||||
return self.targets
|
||||
"""Only send notifications for active users"""
|
||||
|
||||
return [target for target in self.targets if target.is_active]
|
||||
|
||||
def send(self, target):
|
||||
"""Send a UI notification to a user."""
|
||||
|
@ -778,7 +778,8 @@ class NotificationTest(InvenTreeAPITestCase):
|
||||
messages = NotificationMessage.objects.all()
|
||||
|
||||
# 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
|
||||
my_notifications = messages.filter(user=self.user)
|
||||
@ -822,7 +823,7 @@ class NotificationTest(InvenTreeAPITestCase):
|
||||
|
||||
# Only 7 notifications should have been deleted,
|
||||
# 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)
|
||||
|
||||
|
||||
|
@ -267,7 +267,7 @@ class SalesOrderTest(TestCase):
|
||||
category='order.overdue_sales_order',
|
||||
)
|
||||
|
||||
self.assertEqual(len(messages), 2)
|
||||
self.assertEqual(len(messages), 1)
|
||||
|
||||
def test_new_so_notification(self):
|
||||
"""Test that a notification is sent when a new SalesOrder is created.
|
||||
|
@ -326,7 +326,12 @@ class OrderTest(TestCase):
|
||||
user__id=user_id,
|
||||
)
|
||||
|
||||
self.assertTrue(messages.exists())
|
||||
# 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())
|
||||
|
||||
msg = messages.first()
|
||||
|
||||
|
@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from allauth.account.models import EmailAddress
|
||||
|
||||
import common.models
|
||||
import InvenTree.helpers
|
||||
import InvenTree.tasks
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import BulkNotificationMethod, SettingsMixin
|
||||
@ -61,7 +62,12 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
|
||||
allowed_users = []
|
||||
|
||||
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:
|
||||
allowed_users.append(user)
|
||||
|
@ -1036,9 +1036,12 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
// Now that the allocations have been grouped by stock item,
|
||||
// we can update each row in the table,
|
||||
// using the pk value of each row (stock item)
|
||||
|
||||
var data = [];
|
||||
|
||||
rows.forEach(function(row) {
|
||||
row.allocations = allocations[row.pk] || [];
|
||||
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true);
|
||||
data.push(row);
|
||||
|
||||
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) {
|
||||
|
||||
var data = [];
|
||||
// Iterate through each row and find matching test results
|
||||
rows.forEach(function(row) {
|
||||
var test_results = {};
|
||||
@ -1124,8 +1131,10 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
|
||||
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() {
|
||||
// Force a refresh of each row in the table
|
||||
// Note we cannot call 'refresh' because we are passing data from memory
|
||||
// var rows = $(table).bootstrapTable('getData');
|
||||
|
||||
// How many rows are fully allocated?
|
||||
var allocated_rows = 0;
|
||||
|
||||
bom_items.forEach(function(row) {
|
||||
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true);
|
||||
for (var idx = 0; idx < bom_items.length; idx++) {
|
||||
var row = bom_items[idx];
|
||||
|
||||
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
|
||||
var output_progress_bar = $(`#output-progress-${outputId}`);
|
||||
@ -1675,7 +1686,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
setupCallbacks();
|
||||
},
|
||||
sortable: true,
|
||||
showColumns: false,
|
||||
showColumns: true,
|
||||
detailView: true,
|
||||
detailFilter: function(index, row) {
|
||||
return allocatedQuantity(row) > 0;
|
||||
@ -1806,6 +1817,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
field: 'sub_part_detail.full_name',
|
||||
title: '{% trans "Required Part" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var url = `/part/${row.sub_part}/`;
|
||||
var thumb = row.sub_part_detail.thumbnail;
|
||||
@ -1830,16 +1842,37 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
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',
|
||||
title: '{% trans "Quantity Per" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'available_stock',
|
||||
title: '{% trans "Available" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var url = `/part/${row.sub_part_detail.pk}/?display=part-stock`;
|
||||
@ -1903,6 +1936,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
field: 'allocated',
|
||||
title: '{% trans "Allocated" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var required = requiredQuantity(row);
|
||||
var allocated = row.consumable ? required : allocatedQuantity(row);
|
||||
@ -1943,6 +1977,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
{
|
||||
field: 'actions',
|
||||
title: '{% trans "Actions" %}',
|
||||
switchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (row.consumable) {
|
||||
|
Reference in New Issue
Block a user