diff --git a/InvenTree/stock/migrations/0040_stockitemtestresult.py b/InvenTree/stock/migrations/0040_stockitemtestresult.py new file mode 100644 index 0000000000..fdf0344925 --- /dev/null +++ b/InvenTree/stock/migrations/0040_stockitemtestresult.py @@ -0,0 +1,29 @@ +# Generated by Django 3.0.5 on 2020-05-16 09:55 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('stock', '0039_auto_20200513_0016'), + ] + + operations = [ + migrations.CreateModel( + name='StockItemTestResult', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('test', models.CharField(help_text='Test name', max_length=100, verbose_name='Test')), + ('result', models.BooleanField(default=False, help_text='Test result', verbose_name='Result')), + ('value', models.CharField(blank=True, help_text='Test output value', max_length=500, verbose_name='Value')), + ('date', models.DateTimeField(auto_now_add=True)), + ('attachment', models.ForeignKey(blank=True, help_text='Test result attachment', null=True, on_delete=django.db.models.deletion.SET_NULL, to='stock.StockItemAttachment', verbose_name='Attachment')), + ('stock_item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='test_results', to='stock.StockItem')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index bfdb8461e2..0d113e8d9e 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -993,3 +993,79 @@ class StockItemTracking(models.Model): # TODO # file = models.FileField() + + +class StockItemTestResult(models.Model): + """ + A StockItemTestResult records results of custom tests against individual StockItem objects. + This is useful for tracking unit acceptance tests, and particularly useful when integrated + with automated testing setups. + + Multiple results can be recorded against any given test, allowing tests to be run many times. + + Attributes: + stock_item: Link to StockItem + test: Test name (simple string matching) + result: Test result value (pass / fail / etc) + value: Recorded test output value (optional) + attachment: Link to StockItem attachment (optional) + user: User who uploaded the test result + date: Date the test result was recorded + """ + + def clean(self): + + super().clean() + + # 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 + + stock_item = models.ForeignKey( + StockItem, + on_delete=models.CASCADE, + related_name='test_results' + ) + + test = models.CharField( + blank=False, max_length=100, + verbose_name=_('Test'), + help_text=_('Test name') + ) + + result = models.BooleanField( + default=False, + verbose_name=_('Result'), + help_text=_('Test result') + ) + + value = models.CharField( + blank=True, max_length=500, + verbose_name=_('Value'), + help_text=_('Test output value') + ) + + attachment = models.ForeignKey( + StockItemAttachment, + on_delete=models.SET_NULL, + blank=True, null=True, + verbose_name=_('Attachment'), + help_text=_('Test result attachment'), + ) + + user = models.ForeignKey( + User, + on_delete = models.SET_NULL, + blank=True, null=True + ) + + date = models.DateTimeField( + auto_now_add=True, + editable=False + ) \ No newline at end of file