mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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 = 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
|
||||
|
||||
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
|
||||
- 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.
|
||||
"""
|
||||
|
||||
@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)
|
||||
|
||||
attachment = InvenTreeAttachmentSerializerField(
|
||||
@ -297,6 +316,8 @@ class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
||||
allow_blank=False,
|
||||
)
|
||||
|
||||
upload_date = serializers.DateField(read_only=True)
|
||||
|
||||
|
||||
class InvenTreeImageSerializerField(serializers.ImageField):
|
||||
"""Custom image serializer.
|
||||
|
@ -929,18 +929,6 @@ class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""Serializer metaclass"""
|
||||
model = BuildOrderAttachment
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'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)
|
||||
from plugin.serializers import MetadataSerializer
|
||||
|
||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
||||
ManufacturerPartParameter, SupplierPart,
|
||||
SupplierPriceBreak)
|
||||
from .serializers import (CompanySerializer,
|
||||
from .models import (Company, CompanyAttachment, ManufacturerPart,
|
||||
ManufacturerPartAttachment, ManufacturerPartParameter,
|
||||
SupplierPart, SupplierPriceBreak)
|
||||
from .serializers import (CompanyAttachmentSerializer, CompanySerializer,
|
||||
ManufacturerPartAttachmentSerializer,
|
||||
ManufacturerPartParameterSerializer,
|
||||
ManufacturerPartSerializer, SupplierPartSerializer,
|
||||
@ -96,6 +96,28 @@ class CompanyMetadata(RetrieveUpdateAPI):
|
||||
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):
|
||||
"""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'^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'),
|
||||
|
||||
]
|
||||
|
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()
|
||||
|
||||
|
||||
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):
|
||||
"""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)
|
||||
from part.serializers import PartBriefSerializer
|
||||
|
||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
||||
ManufacturerPartParameter, SupplierPart,
|
||||
SupplierPriceBreak)
|
||||
from .models import (Company, CompanyAttachment, ManufacturerPart,
|
||||
ManufacturerPartAttachment, ManufacturerPartParameter,
|
||||
SupplierPart, SupplierPriceBreak)
|
||||
|
||||
|
||||
class CompanyBriefSerializer(InvenTreeModelSerializer):
|
||||
@ -126,6 +126,18 @@ class CompanySerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||
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):
|
||||
"""Serializer for ManufacturerPart object."""
|
||||
|
||||
@ -179,21 +191,9 @@ class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
|
||||
model = ManufacturerPartAttachment
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'manufacturer_part',
|
||||
'attachment',
|
||||
'filename',
|
||||
'link',
|
||||
'comment',
|
||||
'upload_date',
|
||||
'user',
|
||||
'user_detail',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'upload_date',
|
||||
]
|
||||
])
|
||||
|
||||
|
||||
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
||||
|
@ -194,11 +194,54 @@
|
||||
</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 %}
|
||||
|
||||
{% block js_ready %}
|
||||
{{ 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() {
|
||||
|
||||
setupNotesField(
|
||||
|
@ -24,3 +24,5 @@
|
||||
{% endif %}
|
||||
{% trans "Notes" as text %}
|
||||
{% 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
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'order',
|
||||
'attachment',
|
||||
'link',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
'user',
|
||||
'user_detail',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'upload_date',
|
||||
]
|
||||
])
|
||||
|
||||
|
||||
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
||||
@ -1416,18 +1404,6 @@ class SalesOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
|
||||
model = order.models.SalesOrderAttachment
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'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"""
|
||||
model = PartAttachment
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'part',
|
||||
'attachment',
|
||||
'filename',
|
||||
'link',
|
||||
'comment',
|
||||
'upload_date',
|
||||
'user',
|
||||
'user_detail',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'upload_date',
|
||||
]
|
||||
])
|
||||
|
||||
|
||||
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
||||
|
@ -622,23 +622,9 @@ class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSer
|
||||
|
||||
model = StockItemAttachment
|
||||
|
||||
fields = [
|
||||
'pk',
|
||||
fields = InvenTree.serializers.InvenTreeAttachmentSerializer.attachment_fields([
|
||||
'stock_item',
|
||||
'attachment',
|
||||
'filename',
|
||||
'link',
|
||||
'comment',
|
||||
'upload_date',
|
||||
'user',
|
||||
'user_detail',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'upload_date',
|
||||
'user',
|
||||
'user_detail'
|
||||
]
|
||||
])
|
||||
|
||||
|
||||
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
@ -130,6 +130,7 @@ class RuleSet(models.Model):
|
||||
],
|
||||
'purchase_order': [
|
||||
'company_company',
|
||||
'company_companyattachment',
|
||||
'company_manufacturerpart',
|
||||
'company_manufacturerpartparameter',
|
||||
'company_supplierpart',
|
||||
@ -142,6 +143,7 @@ class RuleSet(models.Model):
|
||||
],
|
||||
'sales_order': [
|
||||
'company_company',
|
||||
'company_companyattachment',
|
||||
'order_salesorder',
|
||||
'order_salesorderallocation',
|
||||
'order_salesorderattachment',
|
||||
|
Loading…
Reference in New Issue
Block a user