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! 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