diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py
index 86f41816b2..e391a00bd1 100644
--- a/InvenTree/InvenTree/api_version.py
+++ b/InvenTree/InvenTree/api_version.py
@@ -4,11 +4,15 @@ InvenTree API version information
# InvenTree API version
-INVENTREE_API_VERSION = 46
+INVENTREE_API_VERSION = 47
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v47 -> 2022-05-10 : https://github.com/inventree/InvenTree/pull/2964
+ - Fixes barcode API error response when scanning a StockItem which does not exist
+ - Fixes barcode API error response when scanning a StockLocation which does not exist
+
v46 -> 2022-05-09
- Fixes read permissions on settings API
- Allows non-staff users to read global settings via the API
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index de2616b772..492da8f5de 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -49,6 +49,8 @@ from InvenTree import validators
from InvenTree.models import InvenTreeTree, InvenTreeAttachment, DataImportMixin
from InvenTree.fields import InvenTreeURLField
from InvenTree.helpers import decimal2string, normalize, decimal2money
+
+import InvenTree.ready
import InvenTree.tasks
from InvenTree.status_codes import BuildStatus, PurchaseOrderStatus, SalesOrderStatus
@@ -2292,7 +2294,7 @@ def after_save_part(sender, instance: Part, created, **kwargs):
Function to be executed after a Part is saved
"""
- if not created:
+ if not created and not InvenTree.ready.isImportingData():
# Check part stock only if we are *updating* the part (not creating it)
# Run this check in the background
diff --git a/InvenTree/plugin/builtin/barcodes/inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py
index a8d7252f49..4cfbf1cb85 100644
--- a/InvenTree/plugin/builtin/barcodes/inventree_barcode.py
+++ b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py
@@ -84,7 +84,7 @@ class InvenTreeBarcodePlugin(BarcodeMixin, plugin.integration.IntegrationPluginB
item = StockItem.objects.get(pk=pk)
return item
except (ValueError, StockItem.DoesNotExist): # pragma: no cover
- raise ValidationError({k, "Stock item does not exist"})
+ raise ValidationError({k: "Stock item does not exist"})
return None
@@ -112,7 +112,7 @@ class InvenTreeBarcodePlugin(BarcodeMixin, plugin.integration.IntegrationPluginB
loc = StockLocation.objects.get(pk=pk)
return loc
except (ValueError, StockLocation.DoesNotExist): # pragma: no cover
- raise ValidationError({k, "Stock location does not exist"})
+ raise ValidationError({k: "Stock location does not exist"})
return None
@@ -133,12 +133,12 @@ class InvenTreeBarcodePlugin(BarcodeMixin, plugin.integration.IntegrationPluginB
try:
pk = self.data[k]['id']
except (AttributeError, KeyError):
- raise ValidationError({k, 'id parameter not supplied'})
+ raise ValidationError({k: 'id parameter not supplied'})
try:
part = Part.objects.get(pk=pk)
return part
except (ValueError, Part.DoesNotExist): # pragma: no cover
- raise ValidationError({k, 'Part does not exist'})
+ raise ValidationError({k: 'Part does not exist'})
return None
diff --git a/InvenTree/plugin/events.py b/InvenTree/plugin/events.py
index b54581bf71..043f3b97a3 100644
--- a/InvenTree/plugin/events.py
+++ b/InvenTree/plugin/events.py
@@ -17,7 +17,7 @@ from django.dispatch.dispatcher import receiver
from common.models import InvenTreeSetting
import common.notifications
-from InvenTree.ready import canAppAccessDatabase
+from InvenTree.ready import canAppAccessDatabase, isImportingData
from InvenTree.tasks import offload_task
from plugin.registry import registry
@@ -113,6 +113,10 @@ def allow_table_event(table_name):
We *do not* want events to be fired for some tables!
"""
+ if isImportingData():
+ # Prevent table events during the data import process
+ return False
+
table_name = table_name.lower().strip()
# Ignore any tables which start with these prefixes
diff --git a/InvenTree/plugin/samples/integration/templates/panel_demo/childless.html b/InvenTree/plugin/samples/integration/templates/panel_demo/childless.html
index 061dcc514d..b74ce094b7 100644
--- a/InvenTree/plugin/samples/integration/templates/panel_demo/childless.html
+++ b/InvenTree/plugin/samples/integration/templates/panel_demo/childless.html
@@ -6,6 +6,6 @@
This location has no sublocations!
- - Location Name: {{ location.name }}
- - Location Path: {{ location.pathstring }}
+ - Location Name: {{ location.name }}
+ - Location Path: {{ location.pathstring }}
diff --git a/InvenTree/plugin/tests_barcode.py b/InvenTree/plugin/tests_barcode.py
index 33b171d666..43a713a57b 100644
--- a/InvenTree/plugin/tests_barcode.py
+++ b/InvenTree/plugin/tests_barcode.py
@@ -86,6 +86,22 @@ class BarcodeAPITest(APITestCase):
self.assertIn('barcode_data', response.data)
self.assertEqual(response.data['part']['pk'], 1)
+ def test_invalid_part(self):
+ """Test response for invalid part"""
+ response = self.client.post(
+ self.scan_url,
+ {
+ 'barcode': {
+ 'part': 999999999,
+ }
+ },
+ format='json'
+ )
+
+ self.assertEqual(response.status_code, 400)
+
+ self.assertEqual(response.data['part'], 'Part does not exist')
+
def test_find_stock_item(self):
"""
Test that we can lookup a stock item based on ID
@@ -106,6 +122,23 @@ class BarcodeAPITest(APITestCase):
self.assertIn('barcode_data', response.data)
self.assertEqual(response.data['stockitem']['pk'], 1)
+ def test_invalid_item(self):
+ """Test response for invalid stock item"""
+
+ response = self.client.post(
+ self.scan_url,
+ {
+ 'barcode': {
+ 'stockitem': 999999999,
+ }
+ },
+ format='json'
+ )
+
+ self.assertEqual(response.status_code, 400)
+
+ self.assertEqual(response.data['stockitem'], 'Stock item does not exist')
+
def test_find_location(self):
"""
Test that we can lookup a stock location based on ID
@@ -126,6 +159,23 @@ class BarcodeAPITest(APITestCase):
self.assertIn('barcode_data', response.data)
self.assertEqual(response.data['stocklocation']['pk'], 1)
+ def test_invalid_location(self):
+ """Test response for an invalid location"""
+
+ response = self.client.post(
+ self.scan_url,
+ {
+ 'barcode': {
+ 'stocklocation': 999999999,
+ }
+ },
+ format='json'
+ )
+
+ self.assertEqual(response.status_code, 400)
+
+ self.assertEqual(response.data['stocklocation'], 'Stock location does not exist')
+
def test_integer_barcode(self):
response = self.postBarcode(self.scan_url, '123456789')
diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py
index 040b748521..53e1321e1a 100644
--- a/InvenTree/stock/models.py
+++ b/InvenTree/stock/models.py
@@ -30,7 +30,8 @@ from mptt.managers import TreeManager
from decimal import Decimal, InvalidOperation
from datetime import datetime, timedelta
-from InvenTree import helpers
+import InvenTree.helpers
+import InvenTree.ready
import InvenTree.tasks
import common.models
@@ -137,7 +138,7 @@ class StockLocation(InvenTreeTree):
def format_barcode(self, **kwargs):
""" Return a JSON string for formatting a barcode for this StockLocation object """
- return helpers.MakeBarcode(
+ return InvenTree.helpers.MakeBarcode(
'stocklocation',
self.pk,
{
@@ -577,7 +578,7 @@ class StockItem(MPTTModel):
Voltagile data (e.g. stock quantity) should be looked up using the InvenTree API (as it may change)
"""
- return helpers.MakeBarcode(
+ return InvenTree.helpers.MakeBarcode(
"stockitem",
self.id,
{
@@ -1775,7 +1776,7 @@ class StockItem(MPTTModel):
sn=self.serial)
else:
s = '{n} x {part}'.format(
- n=helpers.decimal2string(self.quantity),
+ n=InvenTree.helpers.decimal2string(self.quantity),
part=self.part.full_name)
if self.location:
@@ -1783,7 +1784,7 @@ class StockItem(MPTTModel):
if self.purchase_order:
s += " ({pre}{po})".format(
- pre=helpers.getSetting("PURCHASEORDER_REFERENCE_PREFIX"),
+ pre=InvenTree.helpers.getSetting("PURCHASEORDER_REFERENCE_PREFIX"),
po=self.purchase_order,
)
@@ -1851,7 +1852,7 @@ class StockItem(MPTTModel):
result_map = {}
for result in results:
- key = helpers.generateTestKey(result.test)
+ key = InvenTree.helpers.generateTestKey(result.test)
result_map[key] = result
# Do we wish to "cascade" and include test results from installed stock items?
@@ -1898,7 +1899,7 @@ class StockItem(MPTTModel):
failed = 0
for test in required:
- key = helpers.generateTestKey(test.test_name)
+ key = InvenTree.helpers.generateTestKey(test.test_name)
if key in results:
result = results[key]
@@ -1949,7 +1950,7 @@ class StockItem(MPTTModel):
# Attempt to validate report filter (skip if invalid)
try:
- filters = helpers.validateFilterString(test_report.filters)
+ filters = InvenTree.helpers.validateFilterString(test_report.filters)
if item_query.filter(**filters).exists():
reports.append(test_report)
except (ValidationError, FieldError):
@@ -1977,7 +1978,7 @@ class StockItem(MPTTModel):
for lbl in label.models.StockItemLabel.objects.filter(enabled=True):
try:
- filters = helpers.validateFilterString(lbl.filters)
+ filters = InvenTree.helpers.validateFilterString(lbl.filters)
if item_query.filter(**filters).exists():
labels.append(lbl)
@@ -2016,8 +2017,9 @@ def after_delete_stock_item(sender, instance: StockItem, **kwargs):
Function to be executed after a StockItem object is deleted
"""
- # Run this check in the background
- InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance.part)
+ if not InvenTree.ready.isImportingData():
+ # Run this check in the background
+ InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance.part)
@receiver(post_save, sender=StockItem, dispatch_uid='stock_item_post_save_log')
@@ -2026,8 +2028,9 @@ def after_save_stock_item(sender, instance: StockItem, created, **kwargs):
Hook function to be executed after StockItem object is saved/updated
"""
- # Run this check in the background
- InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance.part)
+ if not InvenTree.ready.isImportingData():
+ # Run this check in the background
+ InvenTree.tasks.offload_task('part.tasks.notify_low_stock_if_required', instance.part)
class StockItemAttachment(InvenTreeAttachment):
@@ -2170,7 +2173,7 @@ class StockItemTestResult(models.Model):
@property
def key(self):
- return helpers.generateTestKey(self.test)
+ return InvenTree.helpers.generateTestKey(self.test)
stock_item = models.ForeignKey(
StockItem,
diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js
index a636cfeec8..94c2780a28 100644
--- a/InvenTree/templates/js/translated/build.js
+++ b/InvenTree/templates/js/translated/build.js
@@ -1056,6 +1056,7 @@ function loadBuildOutputTable(build_info, options={}) {
'{% url "api-stock-test-result-list" %}',
{
build: build_info.pk,
+ ordering: '-date',
},
{
success: function(results) {
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 63535bd83d..cefd2c2b61 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -95,6 +95,9 @@ RUN echo "Downloading InvenTree from ${INVENTREE_GIT_REPO}"
RUN git clone --branch ${INVENTREE_GIT_BRANCH} --depth 1 ${INVENTREE_GIT_REPO} ${INVENTREE_HOME}
+# Ref: https://github.blog/2022-04-12-git-security-vulnerability-announced/
+RUN git config --global --add safe.directory ${INVENTREE_HOME}
+
# Checkout against a particular git tag
RUN if [ -n "${INVENTREE_GIT_TAG}" ] ; then cd ${INVENTREE_HOME} && git fetch --all --tags && git checkout tags/${INVENTREE_GIT_TAG} -b v${INVENTREE_GIT_TAG}-branch ; fi