mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 13:06:45 +00:00
Company attachment (#4346)
* Adds new model, API, serializers, etc - Refactor InvenTreeAttachmentSerializer class - Reduces code duplication * Update front end * Increment API version
This commit is contained in:
parent
f4bc65523c
commit
45d50fc618
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 94
|
INVENTREE_API_VERSION = 95
|
||||||
|
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
||||||
|
v95 -> 2023-02-16 : https://github.com/inventree/InvenTree/pull/4346
|
||||||
|
- Adds "CompanyAttachment" model (and associated API endpoints)
|
||||||
|
|
||||||
v94 -> 2023-02-10 : https://github.com/inventree/InvenTree/pull/4327
|
v94 -> 2023-02-10 : https://github.com/inventree/InvenTree/pull/4327
|
||||||
- Adds API endpoints for the "Group" auth model
|
- Adds API endpoints for the "Group" auth model
|
||||||
|
|
||||||
|
@ -282,6 +282,25 @@ class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
|||||||
The only real addition here is that we support "renaming" of the attachment file.
|
The only real addition here is that we support "renaming" of the attachment file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def attachment_fields(extra_fields=None):
|
||||||
|
"""Default set of fields for an attachment serializer"""
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'attachment',
|
||||||
|
'filename',
|
||||||
|
'link',
|
||||||
|
'comment',
|
||||||
|
'upload_date',
|
||||||
|
'user',
|
||||||
|
'user_detail',
|
||||||
|
]
|
||||||
|
|
||||||
|
if extra_fields:
|
||||||
|
fields += extra_fields
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||||
|
|
||||||
attachment = InvenTreeAttachmentSerializerField(
|
attachment = InvenTreeAttachmentSerializerField(
|
||||||
@ -297,6 +316,8 @@ class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
|||||||
allow_blank=False,
|
allow_blank=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
upload_date = serializers.DateField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeImageSerializerField(serializers.ImageField):
|
class InvenTreeImageSerializerField(serializers.ImageField):
|
||||||
"""Custom image serializer.
|
"""Custom image serializer.
|
||||||
|
@ -929,18 +929,6 @@ class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
"""Serializer metaclass"""
|
"""Serializer metaclass"""
|
||||||
model = BuildOrderAttachment
|
model = BuildOrderAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'build',
|
'build',
|
||||||
'attachment',
|
])
|
||||||
'link',
|
|
||||||
'filename',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
]
|
|
||||||
|
@ -15,10 +15,10 @@ from InvenTree.mixins import (ListCreateAPI, RetrieveUpdateAPI,
|
|||||||
RetrieveUpdateDestroyAPI)
|
RetrieveUpdateDestroyAPI)
|
||||||
from plugin.serializers import MetadataSerializer
|
from plugin.serializers import MetadataSerializer
|
||||||
|
|
||||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
from .models import (Company, CompanyAttachment, ManufacturerPart,
|
||||||
ManufacturerPartParameter, SupplierPart,
|
ManufacturerPartAttachment, ManufacturerPartParameter,
|
||||||
SupplierPriceBreak)
|
SupplierPart, SupplierPriceBreak)
|
||||||
from .serializers import (CompanySerializer,
|
from .serializers import (CompanyAttachmentSerializer, CompanySerializer,
|
||||||
ManufacturerPartAttachmentSerializer,
|
ManufacturerPartAttachmentSerializer,
|
||||||
ManufacturerPartParameterSerializer,
|
ManufacturerPartParameterSerializer,
|
||||||
ManufacturerPartSerializer, SupplierPartSerializer,
|
ManufacturerPartSerializer, SupplierPartSerializer,
|
||||||
@ -96,6 +96,28 @@ class CompanyMetadata(RetrieveUpdateAPI):
|
|||||||
queryset = Company.objects.all()
|
queryset = Company.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||||
|
"""API endpoint for the CompanyAttachment model"""
|
||||||
|
|
||||||
|
queryset = CompanyAttachment.objects.all()
|
||||||
|
serializer_class = CompanyAttachmentSerializer
|
||||||
|
|
||||||
|
filter_backends = [
|
||||||
|
DjangoFilterBackend,
|
||||||
|
]
|
||||||
|
|
||||||
|
filterset_fields = [
|
||||||
|
'company',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
|
"""Detail endpoint for CompanyAttachment model."""
|
||||||
|
|
||||||
|
queryset = CompanyAttachment.objects.all()
|
||||||
|
serializer_class = CompanyAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartFilter(rest_filters.FilterSet):
|
class ManufacturerPartFilter(rest_filters.FilterSet):
|
||||||
"""Custom API filters for the ManufacturerPart list endpoint."""
|
"""Custom API filters for the ManufacturerPart list endpoint."""
|
||||||
|
|
||||||
@ -521,6 +543,11 @@ company_api_urls = [
|
|||||||
re_path(r'^.*$', CompanyDetail.as_view(), name='api-company-detail'),
|
re_path(r'^.*$', CompanyDetail.as_view(), name='api-company-detail'),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
re_path(r'^attachment/', include([
|
||||||
|
re_path(r'^(?P<pk>\d+)/', CompanyAttachmentDetail.as_view(), name='api-company-attachment-detail'),
|
||||||
|
re_path(r'^$', CompanyAttachmentList.as_view(), name='api-company-attachment-list'),
|
||||||
|
])),
|
||||||
|
|
||||||
re_path(r'^.*$', CompanyList.as_view(), name='api-company-list'),
|
re_path(r'^.*$', CompanyList.as_view(), name='api-company-list'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
33
InvenTree/company/migrations/0054_companyattachment.py
Normal file
33
InvenTree/company/migrations/0054_companyattachment.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 3.2.16 on 2023-02-15 12:55
|
||||||
|
|
||||||
|
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', '0053_supplierpart_updated'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CompanyAttachment',
|
||||||
|
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')),
|
||||||
|
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.company', verbose_name='Company')),
|
||||||
|
('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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -205,6 +205,25 @@ class Company(MetadataMixin, models.Model):
|
|||||||
return stock.objects.filter(Q(supplier_part__supplier=self.id) | Q(supplier_part__manufacturer_part__manufacturer=self.id)).all()
|
return stock.objects.filter(Q(supplier_part__supplier=self.id) | Q(supplier_part__manufacturer_part__manufacturer=self.id)).all()
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyAttachment(InvenTreeAttachment):
|
||||||
|
"""Model for storing file or URL attachments against a Company object"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_api_url():
|
||||||
|
"""Return the API URL associated with this model"""
|
||||||
|
return reverse('api-company-attachment-list')
|
||||||
|
|
||||||
|
def getSubdir(self):
|
||||||
|
"""Return the subdirectory where these attachments are uploaded"""
|
||||||
|
return os.path.join('company_files', str(self.company.pk))
|
||||||
|
|
||||||
|
company = models.ForeignKey(
|
||||||
|
Company, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Company'),
|
||||||
|
related_name='attachments',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Contact(models.Model):
|
class Contact(models.Model):
|
||||||
"""A Contact represents a person who works at a particular company. A Company may have zero or more associated Contact objects.
|
"""A Contact represents a person who works at a particular company. A Company may have zero or more associated Contact objects.
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ from InvenTree.serializers import (InvenTreeAttachmentSerializer,
|
|||||||
InvenTreeMoneySerializer, RemoteImageMixin)
|
InvenTreeMoneySerializer, RemoteImageMixin)
|
||||||
from part.serializers import PartBriefSerializer
|
from part.serializers import PartBriefSerializer
|
||||||
|
|
||||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
from .models import (Company, CompanyAttachment, ManufacturerPart,
|
||||||
ManufacturerPartParameter, SupplierPart,
|
ManufacturerPartAttachment, ManufacturerPartParameter,
|
||||||
SupplierPriceBreak)
|
SupplierPart, SupplierPriceBreak)
|
||||||
|
|
||||||
|
|
||||||
class CompanyBriefSerializer(InvenTreeModelSerializer):
|
class CompanyBriefSerializer(InvenTreeModelSerializer):
|
||||||
@ -126,6 +126,18 @@ class CompanySerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
|||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||||
|
"""Serializer for the CompanyAttachment class"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines serializer options"""
|
||||||
|
model = CompanyAttachment
|
||||||
|
|
||||||
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
|
'company',
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartSerializer(InvenTreeModelSerializer):
|
class ManufacturerPartSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for ManufacturerPart object."""
|
"""Serializer for ManufacturerPart object."""
|
||||||
|
|
||||||
@ -179,21 +191,9 @@ class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
|
|
||||||
model = ManufacturerPartAttachment
|
model = ManufacturerPartAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'manufacturer_part',
|
'manufacturer_part',
|
||||||
'attachment',
|
])
|
||||||
'filename',
|
|
||||||
'link',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
||||||
|
@ -194,11 +194,54 @@
|
|||||||
</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>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
onPanelLoad("attachments", function() {
|
||||||
|
loadAttachmentTable('{% url "api-company-attachment-list" %}', {
|
||||||
|
filters: {
|
||||||
|
company: {{ company.pk }},
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
company: {
|
||||||
|
value: {{ company.pk }},
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
enableDragAndDrop(
|
||||||
|
'#attachment-dropzone',
|
||||||
|
'{% url "api-company-attachment-list" %}',
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
company: {{ company.id }},
|
||||||
|
},
|
||||||
|
label: 'attachment',
|
||||||
|
success: function(data, status, xhr) {
|
||||||
|
reloadAttachmentTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
onPanelLoad('company-notes', function() {
|
onPanelLoad('company-notes', function() {
|
||||||
|
|
||||||
setupNotesField(
|
setupNotesField(
|
||||||
|
@ -24,3 +24,5 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% trans "Notes" as text %}
|
{% trans "Notes" as text %}
|
||||||
{% include "sidebar_item.html" with label='company-notes' text=text icon="fa-clipboard" %}
|
{% include "sidebar_item.html" with label='company-notes' text=text icon="fa-clipboard" %}
|
||||||
|
{% trans "Attachments" as text %}
|
||||||
|
{% include "sidebar_item.html" with label='attachments' text=text icon="fa-paperclip" %}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
{% load inventree_extras %}
|
|
||||||
|
|
||||||
<ul class='list-group'>
|
|
||||||
|
|
||||||
<li class='list-group-item'>
|
|
||||||
<a href='#' id='supplier-part-menu-toggle'>
|
|
||||||
<span class='menu-tab-icon fas fa-expand-arrows-alt'></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class='list-group-item' title='{% trans "Supplier Part Stock" %}'>
|
|
||||||
<a href='#' id='select-stock' class='nav-toggle'>
|
|
||||||
<span class='fas fa-boxes sidebar-icon'></span>
|
|
||||||
{% trans "Stock" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class='list-group-item' title='{% trans "Supplier Part Orders" %}'>
|
|
||||||
<a href='#' id='select-purchase-orders' class='nav-toggle'>
|
|
||||||
<span class='fas fa-shopping-cart sidebar-icon'></span>
|
|
||||||
{% trans "Orders" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class='list-group-item' title='{% trans "Supplier Part Pricing" %}'>
|
|
||||||
<a href='#' id='select-pricing' class='nav-toggle'>
|
|
||||||
<span class='fas fa-dollar-sign sidebar-icon'></span>
|
|
||||||
{% trans "Pricing" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
@ -644,21 +644,9 @@ class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
|
|
||||||
model = order.models.PurchaseOrderAttachment
|
model = order.models.PurchaseOrderAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'order',
|
'order',
|
||||||
'attachment',
|
])
|
||||||
'link',
|
|
||||||
'filename',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
||||||
@ -1416,18 +1404,6 @@ class SalesOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
|
|
||||||
model = order.models.SalesOrderAttachment
|
model = order.models.SalesOrderAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'order',
|
'order',
|
||||||
'attachment',
|
])
|
||||||
'filename',
|
|
||||||
'link',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
]
|
|
||||||
|
@ -106,21 +106,9 @@ class PartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartAttachment
|
model = PartAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'part',
|
'part',
|
||||||
'attachment',
|
])
|
||||||
'filename',
|
|
||||||
'link',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
||||||
|
@ -622,23 +622,9 @@ class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSer
|
|||||||
|
|
||||||
model = StockItemAttachment
|
model = StockItemAttachment
|
||||||
|
|
||||||
fields = [
|
fields = InvenTree.serializers.InvenTreeAttachmentSerializer.attachment_fields([
|
||||||
'pk',
|
|
||||||
'stock_item',
|
'stock_item',
|
||||||
'attachment',
|
])
|
||||||
'filename',
|
|
||||||
'link',
|
|
||||||
'comment',
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'upload_date',
|
|
||||||
'user',
|
|
||||||
'user_detail'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
|
@ -130,6 +130,7 @@ class RuleSet(models.Model):
|
|||||||
],
|
],
|
||||||
'purchase_order': [
|
'purchase_order': [
|
||||||
'company_company',
|
'company_company',
|
||||||
|
'company_companyattachment',
|
||||||
'company_manufacturerpart',
|
'company_manufacturerpart',
|
||||||
'company_manufacturerpartparameter',
|
'company_manufacturerpartparameter',
|
||||||
'company_supplierpart',
|
'company_supplierpart',
|
||||||
@ -142,6 +143,7 @@ class RuleSet(models.Model):
|
|||||||
],
|
],
|
||||||
'sales_order': [
|
'sales_order': [
|
||||||
'company_company',
|
'company_company',
|
||||||
|
'company_companyattachment',
|
||||||
'order_salesorder',
|
'order_salesorder',
|
||||||
'order_salesorderallocation',
|
'order_salesorderallocation',
|
||||||
'order_salesorderattachment',
|
'order_salesorderattachment',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user