mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +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:
		| @@ -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> | ||||
		Reference in New Issue
	
	Block a user