diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 735a6de574..451c916542 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -1033,6 +1033,9 @@ class Part(MPTTModel): # Return the tests which are required by this part return self.getTestTemplates(required=True) + def requiredTestCount(self): + return self.getRequiredTests().count() + @property def attachment_count(self): """ Count the number of attachments for this part. diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 64cf656f36..2cb893b304 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -105,9 +105,11 @@ class PartBriefSerializer(InvenTreeModelSerializer): 'thumbnail', 'active', 'assembly', + 'is_template', 'purchaseable', 'salable', 'stock', + 'trackable', 'virtual', ] diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 93cf9067f3..516edc5040 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -80,21 +80,10 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView): def get_serializer(self, *args, **kwargs): - try: - kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', False)) - except AttributeError: - pass - - try: - kwargs['location_detail'] = str2bool(self.request.query_params.get('location_detail', False)) - except AttributeError: - pass - - try: - kwargs['supplier_part_detail'] = str2bool(self.request.query_params.get('supplier_detail', False)) - except AttributeError: - pass - + kwargs['part_detail'] = True + kwargs['location_detail'] = True + kwargs['supplier_part_detail'] = True + kwargs['test_detail'] = True kwargs['context'] = self.get_serializer_context() return self.serializer_class(*args, **kwargs) @@ -698,17 +687,14 @@ class StockItemTestResultList(generics.ListCreateAPIView): 'value', ] + ordering = 'date' + def get_serializer(self, *args, **kwargs): try: kwargs['user_detail'] = str2bool(self.request.query_params.get('user_detail', False)) except: pass - try: - kwargs['attachment_detail'] = str2bool(self.request.query_params.get('attachment_detail', False)) - except: - pass - kwargs['context'] = self.get_serializer_context() return self.serializer_class(*args, **kwargs) @@ -724,23 +710,6 @@ class StockItemTestResultList(generics.ListCreateAPIView): # Capture the user information test_result = serializer.save() test_result.user = self.request.user - - # Check if a file has been attached to the request - attachment_file = self.request.FILES.get('attachment', None) - - if attachment_file: - # Create a new attachment associated with the stock item - attachment = StockItemAttachment( - attachment=attachment_file, - stock_item=test_result.stock_item, - user=test_result.user - ) - - attachment.save() - - # Link the attachment back to the test result - test_result.attachment = attachment - test_result.save() diff --git a/InvenTree/stock/migrations/0042_auto_20200523_0121.py b/InvenTree/stock/migrations/0042_auto_20200523_0121.py new file mode 100644 index 0000000000..66db1441e3 --- /dev/null +++ b/InvenTree/stock/migrations/0042_auto_20200523_0121.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2020-05-23 01:21 + +from django.db import migrations, models +import stock.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0041_stockitemtestresult_notes'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitemtestresult', + name='attachment', + field=models.FileField(blank=True, help_text='Test result attachment', null=True, upload_to=stock.models.rename_stock_item_test_result_attachment, verbose_name='Attachment'), + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index f34344383a..02393023b9 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1007,6 +1007,10 @@ class StockItem(MPTTModel): 'failed': failed, } + @property + def required_test_count(self): + return self.part.getRequiredTests().count() + def hasRequiredTests(self): return self.part.getRequiredTests().count() > 0 @@ -1090,6 +1094,11 @@ class StockItemTracking(models.Model): # file = models.FileField() +def rename_stock_item_test_result_attachment(instance, filename): + + return os.path.join('stock_files', str(instance.stock_item.pk), os.path.basename(filename)) + + class StockItemTestResult(models.Model): """ A StockItemTestResult records results of custom tests against individual StockItem objects. @@ -1119,13 +1128,11 @@ class StockItemTestResult(models.Model): super().clean() - """ # If this test result corresponds to a template, check the requirements of the template - key = helpers.generateTestKey(self.test) + key = self.key templates = self.stock_item.part.getTestTemplates() - TODO: Re-introduce this at a later stage, it is buggy when uplaoding an attachment via the API for template in templates: if key == template.key: @@ -1142,17 +1149,10 @@ class StockItemTestResult(models.Model): }) break - """ - # If an attachment is linked to this result, the attachment must also point to the item - try: - if self.attachment: - if not self.attachment.stock_item == self.stock_item: - raise ValidationError({ - 'attachment': _("Test result attachment must be linked to the same StockItem"), - }) - except (StockItem.DoesNotExist, StockItemAttachment.DoesNotExist): - pass + @property + def key(self): + return helpers.generateTestKey(self.test) stock_item = models.ForeignKey( StockItem, @@ -1178,10 +1178,9 @@ class StockItemTestResult(models.Model): help_text=_('Test output value') ) - attachment = models.ForeignKey( - StockItemAttachment, - on_delete=models.SET_NULL, - blank=True, null=True, + attachment = models.FileField( + null=True, blank=True, + upload_to=rename_stock_item_test_result_attachment, verbose_name=_('Attachment'), help_text=_('Test result attachment'), ) diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 865a63a2c2..19d05d7b7b 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -108,11 +108,14 @@ class StockItemSerializer(InvenTreeModelSerializer): quantity = serializers.FloatField() allocated = serializers.FloatField() + required_tests = serializers.IntegerField(source='required_test_count', read_only=True) + def __init__(self, *args, **kwargs): part_detail = kwargs.pop('part_detail', False) location_detail = kwargs.pop('location_detail', False) supplier_part_detail = kwargs.pop('supplier_part_detail', False) + test_detail = kwargs.pop('test_detail', False) super(StockItemSerializer, self).__init__(*args, **kwargs) @@ -125,6 +128,9 @@ class StockItemSerializer(InvenTreeModelSerializer): if supplier_part_detail is not True: self.fields.pop('supplier_part_detail') + if test_detail is not True: + self.fields.pop('required_tests') + class Meta: model = StockItem fields = [ @@ -141,6 +147,7 @@ class StockItemSerializer(InvenTreeModelSerializer): 'part_detail', 'pk', 'quantity', + 'required_tests', 'sales_order', 'serial', 'supplier_part', @@ -222,31 +229,28 @@ class StockItemTestResultSerializer(InvenTreeModelSerializer): """ Serializer for the StockItemTestResult model """ user_detail = UserSerializerBrief(source='user', read_only=True) - attachment_detail = StockItemAttachmentSerializer(source='attachment', read_only=True) + + key = serializers.CharField(read_only=True) def __init__(self, *args, **kwargs): user_detail = kwargs.pop('user_detail', False) - attachment_detail = kwargs.pop('attachment_detail', False) super().__init__(*args, **kwargs) if user_detail is not True: self.fields.pop('user_detail') - if attachment_detail is not True: - self.fields.pop('attachment_detail') - class Meta: model = StockItemTestResult fields = [ 'pk', 'stock_item', + 'key', 'test', 'result', 'value', 'attachment', - 'attachment_detail', 'notes', 'user', 'user_detail', diff --git a/InvenTree/stock/templates/stock/item_tests.html b/InvenTree/stock/templates/stock/item_tests.html index b311f13584..c79068349d 100644 --- a/InvenTree/stock/templates/stock/item_tests.html +++ b/InvenTree/stock/templates/stock/item_tests.html @@ -13,10 +13,13 @@