Merge pull request #828 from SchrodingersGat/api-stuff

Api stuff
This commit is contained in:
Oliver 2020-05-24 20:12:28 +10:00 committed by GitHub
commit 8537dc2a85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 89 deletions

View File

@ -1033,6 +1033,9 @@ class Part(MPTTModel):
# Return the tests which are required by this part # Return the tests which are required by this part
return self.getTestTemplates(required=True) return self.getTestTemplates(required=True)
def requiredTestCount(self):
return self.getRequiredTests().count()
@property @property
def attachment_count(self): def attachment_count(self):
""" Count the number of attachments for this part. """ Count the number of attachments for this part.

View File

@ -105,9 +105,11 @@ class PartBriefSerializer(InvenTreeModelSerializer):
'thumbnail', 'thumbnail',
'active', 'active',
'assembly', 'assembly',
'is_template',
'purchaseable', 'purchaseable',
'salable', 'salable',
'stock', 'stock',
'trackable',
'virtual', 'virtual',
] ]

View File

@ -80,21 +80,10 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: kwargs['part_detail'] = True
kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', False)) kwargs['location_detail'] = True
except AttributeError: kwargs['supplier_part_detail'] = True
pass kwargs['test_detail'] = True
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['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs) return self.serializer_class(*args, **kwargs)
@ -698,17 +687,14 @@ class StockItemTestResultList(generics.ListCreateAPIView):
'value', 'value',
] ]
ordering = 'date'
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
kwargs['user_detail'] = str2bool(self.request.query_params.get('user_detail', False)) kwargs['user_detail'] = str2bool(self.request.query_params.get('user_detail', False))
except: except:
pass pass
try:
kwargs['attachment_detail'] = str2bool(self.request.query_params.get('attachment_detail', False))
except:
pass
kwargs['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs) return self.serializer_class(*args, **kwargs)
@ -724,23 +710,6 @@ class StockItemTestResultList(generics.ListCreateAPIView):
# Capture the user information # Capture the user information
test_result = serializer.save() test_result = serializer.save()
test_result.user = self.request.user 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() test_result.save()

View File

@ -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'),
),
]

View File

@ -1007,6 +1007,10 @@ class StockItem(MPTTModel):
'failed': failed, 'failed': failed,
} }
@property
def required_test_count(self):
return self.part.getRequiredTests().count()
def hasRequiredTests(self): def hasRequiredTests(self):
return self.part.getRequiredTests().count() > 0 return self.part.getRequiredTests().count() > 0
@ -1090,6 +1094,11 @@ class StockItemTracking(models.Model):
# file = models.FileField() # 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): class StockItemTestResult(models.Model):
""" """
A StockItemTestResult records results of custom tests against individual StockItem objects. A StockItemTestResult records results of custom tests against individual StockItem objects.
@ -1119,13 +1128,11 @@ class StockItemTestResult(models.Model):
super().clean() super().clean()
"""
# If this test result corresponds to a template, check the requirements of the template # 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() 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: for template in templates:
if key == template.key: if key == template.key:
@ -1142,17 +1149,10 @@ class StockItemTestResult(models.Model):
}) })
break break
"""
# If an attachment is linked to this result, the attachment must also point to the item @property
try: def key(self):
if self.attachment: return helpers.generateTestKey(self.test)
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( stock_item = models.ForeignKey(
StockItem, StockItem,
@ -1178,10 +1178,9 @@ class StockItemTestResult(models.Model):
help_text=_('Test output value') help_text=_('Test output value')
) )
attachment = models.ForeignKey( attachment = models.FileField(
StockItemAttachment, null=True, blank=True,
on_delete=models.SET_NULL, upload_to=rename_stock_item_test_result_attachment,
blank=True, null=True,
verbose_name=_('Attachment'), verbose_name=_('Attachment'),
help_text=_('Test result attachment'), help_text=_('Test result attachment'),
) )

View File

@ -108,11 +108,14 @@ class StockItemSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField() quantity = serializers.FloatField()
allocated = serializers.FloatField() allocated = serializers.FloatField()
required_tests = serializers.IntegerField(source='required_test_count', read_only=True)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
part_detail = kwargs.pop('part_detail', False) part_detail = kwargs.pop('part_detail', False)
location_detail = kwargs.pop('location_detail', False) location_detail = kwargs.pop('location_detail', False)
supplier_part_detail = kwargs.pop('supplier_part_detail', False) supplier_part_detail = kwargs.pop('supplier_part_detail', False)
test_detail = kwargs.pop('test_detail', False)
super(StockItemSerializer, self).__init__(*args, **kwargs) super(StockItemSerializer, self).__init__(*args, **kwargs)
@ -125,6 +128,9 @@ class StockItemSerializer(InvenTreeModelSerializer):
if supplier_part_detail is not True: if supplier_part_detail is not True:
self.fields.pop('supplier_part_detail') self.fields.pop('supplier_part_detail')
if test_detail is not True:
self.fields.pop('required_tests')
class Meta: class Meta:
model = StockItem model = StockItem
fields = [ fields = [
@ -141,6 +147,7 @@ class StockItemSerializer(InvenTreeModelSerializer):
'part_detail', 'part_detail',
'pk', 'pk',
'quantity', 'quantity',
'required_tests',
'sales_order', 'sales_order',
'serial', 'serial',
'supplier_part', 'supplier_part',
@ -222,31 +229,28 @@ class StockItemTestResultSerializer(InvenTreeModelSerializer):
""" Serializer for the StockItemTestResult model """ """ Serializer for the StockItemTestResult model """
user_detail = UserSerializerBrief(source='user', read_only=True) 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): def __init__(self, *args, **kwargs):
user_detail = kwargs.pop('user_detail', False) user_detail = kwargs.pop('user_detail', False)
attachment_detail = kwargs.pop('attachment_detail', False)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if user_detail is not True: if user_detail is not True:
self.fields.pop('user_detail') self.fields.pop('user_detail')
if attachment_detail is not True:
self.fields.pop('attachment_detail')
class Meta: class Meta:
model = StockItemTestResult model = StockItemTestResult
fields = [ fields = [
'pk', 'pk',
'stock_item', 'stock_item',
'key',
'test', 'test',
'result', 'result',
'value', 'value',
'attachment', 'attachment',
'attachment_detail',
'notes', 'notes',
'user', 'user',
'user_detail', 'user_detail',

View File

@ -13,10 +13,13 @@
<div id='button-toolbar'> <div id='button-toolbar'>
<div class='button-toolbar container-fluid' style="float: right;"> <div class='button-toolbar container-fluid' style="float: right;">
<div class='btn-group' role='group'> <div class='btn-group' role='group'>
<button type='button' class='btn btn-success' id='add-test-result'>{% trans "Add Test Data" %}</button>
{% if user.is_staff %} {% if user.is_staff %}
<button type='button' class='btn btn-danger' id='delete-test-results'>{% trans "Delete Test Data" %}</button> <button type='button' class='btn btn-danger' id='delete-test-results'>{% trans "Delete Test Data" %}</button>
{% endif %} {% endif %}
<button type='button' class='btn btn-success' id='add-test-result'>{% trans "Add Test Data" %}</button>
{% if item.part.has_test_report_templates %}
<button type='button' class='btn btn-default' id='test-report'>{% trans "Test Report" %} <span class='fas fa-tasks'></span></button>
{% endif %}
</div> </div>
<div class='filter-list' id='filter-list-stocktests'> <div class='filter-list' id='filter-list-stocktests'>
<!-- Empty div --> <!-- Empty div -->
@ -43,6 +46,17 @@ function reloadTable() {
//$("#test-result-table").bootstrapTable("refresh"); //$("#test-result-table").bootstrapTable("refresh");
} }
{% if item.part.has_test_report_templates %}
$("#test-report").click(function() {
launchModalForm(
"{% url 'stock-item-test-report-select' item.id %}",
{
follow: true,
}
);
});
{% endif %}
{% if user.is_staff %} {% if user.is_staff %}
$("#delete-test-results").click(function() { $("#delete-test-results").click(function() {
launchModalForm( launchModalForm(

View File

@ -292,17 +292,6 @@ class StockItemTestResultCreate(AjaxCreateView):
form = super().get_form() form = super().get_form()
form.fields['stock_item'].widget = HiddenInput() form.fields['stock_item'].widget = HiddenInput()
# Extract the StockItem object
item_id = form['stock_item'].value()
# Limit the options for the file attachments
try:
stock_item = StockItem.objects.get(pk=item_id)
form.fields['attachment'].queryset = stock_item.attachments.all()
except (ValueError, StockItem.DoesNotExist):
# Hide the attachments field
form.fields['attachment'].widget = HiddenInput()
return form return form
@ -321,8 +310,6 @@ class StockItemTestResultEdit(AjaxUpdateView):
form.fields['stock_item'].widget = HiddenInput() form.fields['stock_item'].widget = HiddenInput()
form.fields['attachment'].queryset = self.object.stock_item.attachments.all()
return form return form

View File

@ -32,17 +32,6 @@ function noResultBadge() {
return `<span class='label label-blue float-right'>{% trans "NO RESULT" %}</span>`; return `<span class='label label-blue float-right'>{% trans "NO RESULT" %}</span>`;
} }
function testKey(test_name) {
// Convert test name to a unique key without any illegal chars
test_name = test_name.trim().toLowerCase();
test_name = test_name.replace(' ', '');
test_name = test_name.replace(/[^0-9a-z]/gi, '');
return test_name;
}
function loadStockTestResultsTable(table, options) { function loadStockTestResultsTable(table, options) {
/* /*
* Load StockItemTestResult table * Load StockItemTestResult table
@ -56,8 +45,8 @@ function loadStockTestResultsTable(table, options) {
html += `<span class='badge'>${row.user_detail.username}</span>`; html += `<span class='badge'>${row.user_detail.username}</span>`;
} }
if (row.attachment_detail) { if (row.attachment) {
html += `<a href='${row.attachment_detail.attachment}'><span class='fas fa-file-alt label-right'></span></a>`; html += `<a href='${row.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
} }
return html; return html;
@ -177,14 +166,14 @@ function loadStockTestResultsTable(table, options) {
var match = false; var match = false;
var override = false; var override = false;
var key = testKey(item.test); var key = item.key;
// Try to associate this result with a test row // Try to associate this result with a test row
tableData.forEach(function(row, index) { tableData.forEach(function(row, index) {
// The result matches the test template row // The result matches the test template row
if (key == testKey(row.test_name)) { if (key == row.key) {
// Force the names to be the same! // Force the names to be the same!
item.test_name = row.test_name; item.test_name = row.test_name;