mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Merge pull request #2912 from sur5r/issue2314
Implement Attachments for manufacturer parts
This commit is contained in:
commit
d6ebf3fc24
@ -4,11 +4,14 @@ InvenTree API version information
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# 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
|
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
|
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 'active' status
|
||||||
- Allows filtering of plugin list by 'mixin' support
|
- Allows filtering of plugin list by 'mixin' support
|
||||||
|
@ -8,7 +8,7 @@ import import_export.widgets as widgets
|
|||||||
from .models import Company
|
from .models import Company
|
||||||
from .models import SupplierPart
|
from .models import SupplierPart
|
||||||
from .models import SupplierPriceBreak
|
from .models import SupplierPriceBreak
|
||||||
from .models import ManufacturerPart, ManufacturerPartParameter
|
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
|
||||||
|
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
|
|
||||||
@ -109,6 +109,16 @@ class ManufacturerPartAdmin(ImportExportModelAdmin):
|
|||||||
autocomplete_fields = ('part', 'manufacturer',)
|
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 ManufacturerPartParameterResource(ModelResource):
|
||||||
"""
|
"""
|
||||||
Class for managing ManufacturerPartParameter data import/export
|
Class for managing ManufacturerPartParameter data import/export
|
||||||
@ -175,4 +185,5 @@ admin.site.register(SupplierPart, SupplierPartAdmin)
|
|||||||
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)
|
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)
|
||||||
|
|
||||||
admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
|
admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
|
||||||
|
admin.site.register(ManufacturerPartAttachment, ManufacturerPartAttachmentAdmin)
|
||||||
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)
|
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)
|
||||||
|
@ -12,13 +12,14 @@ from django.urls import include, re_path
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
|
from InvenTree.api import AttachmentMixin
|
||||||
|
|
||||||
from .models import Company
|
from .models import Company
|
||||||
from .models import ManufacturerPart, ManufacturerPartParameter
|
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
|
||||||
from .models import SupplierPart, SupplierPriceBreak
|
from .models import SupplierPart, SupplierPriceBreak
|
||||||
|
|
||||||
from .serializers import CompanySerializer
|
from .serializers import CompanySerializer
|
||||||
from .serializers import ManufacturerPartSerializer, ManufacturerPartParameterSerializer
|
from .serializers import ManufacturerPartSerializer, ManufacturerPartAttachmentSerializer, ManufacturerPartParameterSerializer
|
||||||
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
|
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -160,6 +161,32 @@ class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = ManufacturerPartSerializer
|
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):
|
class ManufacturerPartParameterList(generics.ListCreateAPIView):
|
||||||
"""
|
"""
|
||||||
API endpoint for list view of ManufacturerPartParamater model.
|
API endpoint for list view of ManufacturerPartParamater model.
|
||||||
@ -387,6 +414,12 @@ class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
manufacturer_part_api_urls = [
|
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'^parameter/', include([
|
||||||
re_path(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),
|
re_path(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -22,6 +22,7 @@ from stdimage.models import StdImageField
|
|||||||
|
|
||||||
from InvenTree.helpers import getMediaUrl, getBlankImage, getBlankThumbnail
|
from InvenTree.helpers import getMediaUrl, getBlankImage, getBlankThumbnail
|
||||||
from InvenTree.fields import InvenTreeURLField
|
from InvenTree.fields import InvenTreeURLField
|
||||||
|
from InvenTree.models import InvenTreeAttachment
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus
|
from InvenTree.status_codes import PurchaseOrderStatus
|
||||||
|
|
||||||
import InvenTree.validators
|
import InvenTree.validators
|
||||||
@ -380,6 +381,22 @@ class ManufacturerPart(models.Model):
|
|||||||
return s
|
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):
|
class ManufacturerPartParameter(models.Model):
|
||||||
"""
|
"""
|
||||||
A ManufacturerPartParameter represents a key:value parameter for a MnaufacturerPart.
|
A ManufacturerPartParameter represents a key:value parameter for a MnaufacturerPart.
|
||||||
|
@ -8,6 +8,7 @@ from rest_framework import serializers
|
|||||||
|
|
||||||
from sql_util.utils import SubqueryCount
|
from sql_util.utils import SubqueryCount
|
||||||
|
|
||||||
|
from InvenTree.serializers import InvenTreeAttachmentSerializer
|
||||||
from InvenTree.serializers import InvenTreeDecimalField
|
from InvenTree.serializers import InvenTreeDecimalField
|
||||||
from InvenTree.serializers import InvenTreeImageSerializerField
|
from InvenTree.serializers import InvenTreeImageSerializerField
|
||||||
from InvenTree.serializers import InvenTreeModelSerializer
|
from InvenTree.serializers import InvenTreeModelSerializer
|
||||||
@ -16,7 +17,7 @@ from InvenTree.serializers import InvenTreeMoneySerializer
|
|||||||
from part.serializers import PartBriefSerializer
|
from part.serializers import PartBriefSerializer
|
||||||
|
|
||||||
from .models import Company
|
from .models import Company
|
||||||
from .models import ManufacturerPart, ManufacturerPartParameter
|
from .models import ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter
|
||||||
from .models import SupplierPart, SupplierPriceBreak
|
from .models import SupplierPart, SupplierPriceBreak
|
||||||
|
|
||||||
from common.settings import currency_code_default, currency_code_mappings
|
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):
|
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for the ManufacturerPartParameter model
|
Serializer for the ManufacturerPartParameter model
|
||||||
|
@ -144,6 +144,21 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
</div>
|
</div>
|
||||||
</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 panel-hidden' id='panel-parameters'>
|
||||||
<div class='panel-heading'>
|
<div class='panel-heading'>
|
||||||
<div class='d-flex flex-wrap'>
|
<div class='d-flex flex-wrap'>
|
||||||
@ -178,6 +193,34 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ 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() {
|
function reloadParameters() {
|
||||||
$("#parameter-table").bootstrapTable("refresh");
|
$("#parameter-table").bootstrapTable("refresh");
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
|
|
||||||
{% trans "Parameters" as text %}
|
{% trans "Parameters" as text %}
|
||||||
{% include "sidebar_item.html" with label='parameters' text=text icon="fa-th-list" %}
|
{% 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 %}
|
{% trans "Supplier Parts" as text %}
|
||||||
{% include "sidebar_item.html" with label='supplier-parts' text=text icon="fa-building" %}
|
{% include "sidebar_item.html" with label='supplier-parts' text=text icon="fa-building" %}
|
@ -101,6 +101,7 @@ class RuleSet(models.Model):
|
|||||||
'company_supplierpart',
|
'company_supplierpart',
|
||||||
'company_manufacturerpart',
|
'company_manufacturerpart',
|
||||||
'company_manufacturerpartparameter',
|
'company_manufacturerpartparameter',
|
||||||
|
'company_manufacturerpartattachment',
|
||||||
'label_partlabel',
|
'label_partlabel',
|
||||||
],
|
],
|
||||||
'stock_location': [
|
'stock_location': [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user