Merge pull request #2912 from sur5r/issue2314

Implement Attachments for manufacturer parts
This commit is contained in:
Oliver 2022-05-18 22:14:08 +10:00 committed by GitHub
commit d6ebf3fc24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 172 additions and 5 deletions

View File

@ -4,11 +4,14 @@ InvenTree API version information
# InvenTree API version
INVENTREE_API_VERSION = 49
INVENTREE_API_VERSION = 50
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
v50 -> 2022-05-18 : https://github.com/inventree/InvenTree/pull/2912
- Implement Attachments for manufacturer parts
v49 -> 2022-05-09 : https://github.com/inventree/InvenTree/pull/2957
- Allows filtering of plugin list by 'active' status
- Allows filtering of plugin list by 'mixin' support

View File

@ -8,7 +8,7 @@ import import_export.widgets as widgets
from .models import Company
from .models import SupplierPart
from .models import SupplierPriceBreak
from .models import ManufacturerPart, ManufacturerPartParameter
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
from part.models import Part
@ -109,6 +109,16 @@ class ManufacturerPartAdmin(ImportExportModelAdmin):
autocomplete_fields = ('part', 'manufacturer',)
class ManufacturerPartAttachmentAdmin(ImportExportModelAdmin):
"""
Admin class for ManufacturerPartAttachment model
"""
list_display = ('manufacturer_part', 'attachment', 'comment')
autocomplete_fields = ('manufacturer_part',)
class ManufacturerPartParameterResource(ModelResource):
"""
Class for managing ManufacturerPartParameter data import/export
@ -175,4 +185,5 @@ admin.site.register(SupplierPart, SupplierPartAdmin)
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)
admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
admin.site.register(ManufacturerPartAttachment, ManufacturerPartAttachmentAdmin)
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)

View File

@ -12,13 +12,14 @@ from django.urls import include, re_path
from django.db.models import Q
from InvenTree.helpers import str2bool
from InvenTree.api import AttachmentMixin
from .models import Company
from .models import ManufacturerPart, ManufacturerPartParameter
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
from .models import SupplierPart, SupplierPriceBreak
from .serializers import CompanySerializer
from .serializers import ManufacturerPartSerializer, ManufacturerPartParameterSerializer
from .serializers import ManufacturerPartSerializer, ManufacturerPartAttachmentSerializer, ManufacturerPartParameterSerializer
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
@ -160,6 +161,32 @@ class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ManufacturerPartSerializer
class ManufacturerPartAttachmentList(AttachmentMixin, generics.ListCreateAPIView):
"""
API endpoint for listing (and creating) a ManufacturerPartAttachment (file upload).
"""
queryset = ManufacturerPartAttachment.objects.all()
serializer_class = ManufacturerPartAttachmentSerializer
filter_backends = [
DjangoFilterBackend,
]
filter_fields = [
'manufacturer_part',
]
class ManufacturerPartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
"""
Detail endpooint for ManufacturerPartAttachment model
"""
queryset = ManufacturerPartAttachment.objects.all()
serializer_class = ManufacturerPartAttachmentSerializer
class ManufacturerPartParameterList(generics.ListCreateAPIView):
"""
API endpoint for list view of ManufacturerPartParamater model.
@ -387,6 +414,12 @@ class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
manufacturer_part_api_urls = [
# Base URL for ManufacturerPartAttachment API endpoints
re_path(r'^attachment/', include([
re_path(r'^(?P<pk>\d+)/', ManufacturerPartAttachmentDetail.as_view(), name='api-manufacturer-part-attachment-detail'),
re_path(r'^$', ManufacturerPartAttachmentList.as_view(), name='api-manufacturer-part-attachment-list'),
])),
re_path(r'^parameter/', include([
re_path(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.13 on 2022-05-01 12:57
import InvenTree.fields
import InvenTree.models
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),
('company', '0042_supplierpricebreak_updated'),
]
operations = [
migrations.CreateModel(
name='ManufacturerPartAttachment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment')),
('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link')),
('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')),
('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')),
('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.manufacturerpart', verbose_name='Manufacturer Part')),
('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'abstract': False,
},
),
]

View File

@ -22,6 +22,7 @@ from stdimage.models import StdImageField
from InvenTree.helpers import getMediaUrl, getBlankImage, getBlankThumbnail
from InvenTree.fields import InvenTreeURLField
from InvenTree.models import InvenTreeAttachment
from InvenTree.status_codes import PurchaseOrderStatus
import InvenTree.validators
@ -380,6 +381,22 @@ class ManufacturerPart(models.Model):
return s
class ManufacturerPartAttachment(InvenTreeAttachment):
"""
Model for storing file attachments against a ManufacturerPart object
"""
@staticmethod
def get_api_url():
return reverse('api-manufacturer-part-attachment-list')
def getSubdir(self):
return os.path.join("manufacturer_part_files", str(self.manufacturer_part.id))
manufacturer_part = models.ForeignKey(ManufacturerPart, on_delete=models.CASCADE,
verbose_name=_('Manufacturer Part'), related_name='attachments')
class ManufacturerPartParameter(models.Model):
"""
A ManufacturerPartParameter represents a key:value parameter for a MnaufacturerPart.

View File

@ -8,6 +8,7 @@ from rest_framework import serializers
from sql_util.utils import SubqueryCount
from InvenTree.serializers import InvenTreeAttachmentSerializer
from InvenTree.serializers import InvenTreeDecimalField
from InvenTree.serializers import InvenTreeImageSerializerField
from InvenTree.serializers import InvenTreeModelSerializer
@ -16,7 +17,7 @@ from InvenTree.serializers import InvenTreeMoneySerializer
from part.serializers import PartBriefSerializer
from .models import Company
from .models import ManufacturerPart, ManufacturerPartParameter
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
from .models import SupplierPart, SupplierPriceBreak
from common.settings import currency_code_default, currency_code_mappings
@ -142,6 +143,29 @@ class ManufacturerPartSerializer(InvenTreeModelSerializer):
]
class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
"""
Serializer for the ManufacturerPartAttachment class
"""
class Meta:
model = ManufacturerPartAttachment
fields = [
'pk',
'manufacturer_part',
'attachment',
'filename',
'link',
'comment',
'upload_date',
]
read_only_fields = [
'upload_date',
]
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
"""
Serializer for the ManufacturerPartParameter model

View File

@ -144,6 +144,21 @@ src="{% static 'img/blank_image.png' %}"
</div>
</div>
<div class='panel panel-hidden' id='panel-attachments'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>
<h4>{% trans "Attachments" %}</h4>
{% include "spacer.html" %}
<div class='btn-group' role='group'>
{% include "attachment_button.html" %}
</div>
</div>
</div>
<div class='panel-content'>
{% include "attachment_table.html" %}
</div>
</div>
<div class='panel panel-hidden' id='panel-parameters'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>
@ -178,6 +193,34 @@ src="{% static 'img/blank_image.png' %}"
{% block js_ready %}
{{ block.super }}
onPanelLoad("attachments", function() {
loadAttachmentTable('{% url "api-manufacturer-part-attachment-list" %}', {
filters: {
manufacturer_part: {{ part.pk }},
},
fields: {
manufacturer_part: {
value: {{ part.pk }},
hidden: true
}
}
});
enableDragAndDrop(
'#attachment-dropzone',
'{% url "api-manufacturer-part-attachment-list" %}',
{
data: {
manufacturer_part: {{ part.id }},
},
label: 'attachment',
success: function(data, status, xhr) {
reloadAttachmentTable();
}
}
);
});
function reloadParameters() {
$("#parameter-table").bootstrapTable("refresh");
}

View File

@ -4,5 +4,7 @@
{% trans "Parameters" as text %}
{% include "sidebar_item.html" with label='parameters' text=text icon="fa-th-list" %}
{% trans "Attachments" as text %}
{% include "sidebar_item.html" with label='attachments' text=text icon="fa-paperclip" %}
{% trans "Supplier Parts" as text %}
{% include "sidebar_item.html" with label='supplier-parts' text=text icon="fa-building" %}

View File

@ -101,6 +101,7 @@ class RuleSet(models.Model):
'company_supplierpart',
'company_manufacturerpart',
'company_manufacturerpartparameter',
'company_manufacturerpartattachment',
'label_partlabel',
],
'stock_location': [