diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 0f84d6bc32..cb48b4c11d 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -9,7 +9,7 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, permissions from rest_framework import filters -from django.conf.urls import url +from django.conf.urls import url, include from InvenTree.helpers import str2bool @@ -17,10 +17,12 @@ from part.models import Part from company.models import SupplierPart from .models import PurchaseOrder, PurchaseOrderLineItem -from .serializers import POSerializer, POLineItemSerializer +from .models import PurchaseOrderAttachment +from .serializers import POSerializer, POLineItemSerializer, POAttachmentSerializer from .models import SalesOrder, SalesOrderLineItem -from .serializers import SalesOrderSerializer, SOLineItemSerializer +from .models import SalesOrderAttachment +from .serializers import SalesOrderSerializer, SOLineItemSerializer, SOAttachmentSerializer class POList(generics.ListCreateAPIView): @@ -198,6 +200,25 @@ class POLineItemDetail(generics.RetrieveUpdateAPIView): ] +class SOAttachmentList(generics.ListCreateAPIView): + """ + API endpoint for listing (and creating) a SalesOrderAttachment (file upload) + """ + + queryset = SalesOrderAttachment.objects.all() + serializer_class = SOAttachmentSerializer + + filter_backends = [ + DjangoFilterBackend, + filters.OrderingFilter, + filters.SearchFilter, + ] + + filter_fields = [ + 'order', + ] + + class SOList(generics.ListCreateAPIView): """ API endpoint for accessing a list of SalesOrder objects. @@ -378,10 +399,32 @@ class SOLineItemDetail(generics.RetrieveUpdateAPIView): permission_classes = [permissions.IsAuthenticated] +class POAttachmentList(generics.ListCreateAPIView): + """ + API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload) + """ + + queryset = PurchaseOrderAttachment.objects.all() + serializer_class = POAttachmentSerializer + + filter_backends = [ + DjangoFilterBackend, + filters.OrderingFilter, + filters.SearchFilter, + ] + + filter_fields = [ + 'order', + ] + + order_api_urls = [ # API endpoints for purchase orders url(r'^po/(?P\d+)/$', PODetail.as_view(), name='api-po-detail'), - url(r'^po/$', POList.as_view(), name='api-po-list'), + url(r'po/attachment/', include([ + url(r'^.*$', POAttachmentList.as_view(), name='api-po-attachment-list'), + ])), + url(r'^po/.*$', POList.as_view(), name='api-po-list'), # API endpoints for purchase order line items url(r'^po-line/(?P\d+)/$', POLineItemDetail.as_view(), name='api-po-line-detail'), @@ -389,7 +432,11 @@ order_api_urls = [ # API endpoints for sales ordesr url(r'^so/(?P\d+)/$', SODetail.as_view(), name='api-so-detail'), - url(r'^so/$', SOList.as_view(), name='api-so-list'), + url(r'so/attachment/', include([ + url(r'^.*$', SOAttachmentList.as_view(), name='api-so-attachment-list'), + ])), + + url(r'^so/.*$', SOList.as_view(), name='api-so-list'), # API endpoints for sales order line items url(r'^so-line/(?P\d+)/$', SOLineItemDetail.as_view(), name='api-so-line-detail'), diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index e0ef57f802..c22a637c67 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -14,6 +14,7 @@ from company.serializers import CompanyBriefSerializer, SupplierPartSerializer from part.serializers import PartBriefSerializer from .models import PurchaseOrder, PurchaseOrderLineItem +from .models import PurchaseOrderAttachment, SalesOrderAttachment from .models import SalesOrder, SalesOrderLineItem from .models import SalesOrderAllocation @@ -106,6 +107,22 @@ class POLineItemSerializer(InvenTreeModelSerializer): ] +class POAttachmentSerializer(InvenTreeModelSerializer): + """ + Serializers for the PurchaseOrderAttachment model + """ + + class Meta: + model = PurchaseOrderAttachment + + fields = [ + 'pk', + 'order', + 'attachment', + 'comment', + ] + + class SalesOrderSerializer(InvenTreeModelSerializer): """ Serializers for the SalesOrder object @@ -231,3 +248,19 @@ class SOLineItemSerializer(InvenTreeModelSerializer): 'part', 'part_detail', ] + + +class SOAttachmentSerializer(InvenTreeModelSerializer): + """ + Serializers for the SalesOrderAttachment model + """ + + class Meta: + model = SalesOrderAttachment + + fields = [ + 'pk', + 'order', + 'attachment', + 'comment', + ] diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index cb0ffa2566..3a0c48a809 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -31,7 +31,7 @@ class OrderTest(APITestCase): return self.client.get(url + "?" + options, format='json') - def test_po_list(self,): + def test_po_list(self): url = reverse('api-po-list') @@ -42,3 +42,19 @@ class OrderTest(APITestCase): # Filter by stuff response = self.doGet(url, 'status=10&part=1&supplier_part=1') self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_po_attachments(self): + + url = reverse('api-po-attachment-list') + + response = self.doGet(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_so_attachments(self): + + url = reverse('api-so-attachment-list') + + response = self.doGet(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 6f5327baa4..8f9121885d 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -19,6 +19,7 @@ from django.urls import reverse from .models import Part, PartCategory, BomItem, PartStar from .models import PartParameter, PartParameterTemplate +from .models import PartAttachment from . import serializers as part_serializers @@ -105,6 +106,27 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView): queryset = PartCategory.objects.all() +class PartAttachmentList(generics.ListCreateAPIView): + """ + API endpoint for listing (and creating) a PartAttachment (file upload). + """ + + queryset = PartAttachment.objects.all() + serializer_class = part_serializers.PartAttachmentSerializer + + permission_classes = [permissions.IsAuthenticated] + + filter_backends = [ + DjangoFilterBackend, + filters.OrderingFilter, + filters.SearchFilter, + ] + + filter_fields = [ + 'part', + ] + + class PartThumbs(generics.ListAPIView): """ API endpoint for retrieving information on available Part thumbnails """ @@ -618,33 +640,31 @@ class BomItemValidate(generics.UpdateAPIView): return Response(serializer.data) -cat_api_urls = [ - - url(r'^(?P\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'), - - url(r'^$', CategoryList.as_view(), name='api-part-category-list'), -] - - -part_star_api_urls = [ - url(r'^(?P\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'), - - # Catchall - url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'), -] - -part_param_api_urls = [ - url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-param-template-list'), - - url(r'^.*$', PartParameterList.as_view(), name='api-part-param-list'), -] - part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), - url(r'^category/', include(cat_api_urls)), - url(r'^star/', include(part_star_api_urls)), - url(r'^parameter/', include(part_param_api_urls)), + # Base URL for PartCategory API endpoints + url(r'^category/', include([ + url(r'^(?P\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'), + url(r'^$', CategoryList.as_view(), name='api-part-category-list'), + ])), + + # Base URL for PartAttachment API endpoints + url(r'attachment/', include([ + url(r'^$', PartAttachmentList.as_view(), name='api-part-attachment-list'), + ])), + + # Base URL for PartStar API endpoints + url(r'^star/', include([ + url(r'^(?P\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'), + url(r'^$', PartStarList.as_view(), name='api-part-star-list'), + ])), + + # Base URL for PartParameter API endpoints + url(r'^parameter/', include([ + url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-param-template-list'), + url(r'^.*$', PartParameterList.as_view(), name='api-part-param-list'), + ])), url(r'^thumbs/', PartThumbs.as_view(), name='api-part-thumbs'), @@ -653,16 +673,12 @@ part_api_urls = [ url(r'^.*$', PartList.as_view(), name='api-part-list'), ] -bom_item_urls = [ - - url(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'), - - url(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'), -] - bom_api_urls = [ # BOM Item Detail - url(r'^(?P\d+)/', include(bom_item_urls)), + url(r'^(?P\d+)/', include([ + url(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'), + url(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'), + ])), # Catch-all url(r'^.*$', BomList.as_view(), name='api-bom-list'), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 920e0486c3..74005dd5af 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -9,6 +9,7 @@ from .models import Part, PartStar from .models import PartCategory from .models import BomItem from .models import PartParameter, PartParameterTemplate +from .models import PartAttachment from decimal import Decimal @@ -39,6 +40,22 @@ class CategorySerializer(InvenTreeModelSerializer): ] +class PartAttachmentSerializer(InvenTreeModelSerializer): + """ + Serializer for the PartAttachment class + """ + + class Meta: + model = PartAttachment + + fields = [ + 'pk', + 'part', + 'attachment', + 'comment' + ] + + class PartThumbSerializer(serializers.Serializer): """ Serializer for the 'image' field of the Part model. diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 6b7c15f980..03333569ef 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -12,6 +12,7 @@ from django.db.models import Q from .models import StockLocation, StockItem from .models import StockItemTracking +from .models import StockItemAttachment from part.models import Part, PartCategory from part.serializers import PartBriefSerializer @@ -22,6 +23,7 @@ from company.serializers import SupplierPartSerializer from .serializers import StockItemSerializer from .serializers import LocationSerializer, LocationBriefSerializer from .serializers import StockTrackingSerializer +from .serializers import StockItemAttachmentSerializer from InvenTree.views import TreeSerializer from InvenTree.helpers import str2bool, isNull @@ -624,6 +626,25 @@ class StockList(generics.ListCreateAPIView): ] +class StockAttachmentList(generics.ListCreateAPIView): + """ + API endpoint for listing (and creating) a StockItemAttachment (file upload) + """ + + queryset = StockItemAttachment.objects.all() + serializer_class = StockItemAttachmentSerializer + + filter_backends = [ + DjangoFilterBackend, + filters.OrderingFilter, + filters.SearchFilter, + ] + + filter_fields = [ + 'stock_item', + ] + + class StockTrackingList(generics.ListCreateAPIView): """ API endpoint for list view of StockItemTracking objects. @@ -692,6 +713,11 @@ stock_api_urls = [ url(r'remove/?', StockRemove.as_view(), name='api-stock-remove'), url(r'transfer/?', StockTransfer.as_view(), name='api-stock-transfer'), + # Base URL for StockItemAttachment API endpoints + url(r'^attachment/', include([ + url(r'^$', StockAttachmentList.as_view(), name='api-stock-attachment-list'), + ])), + url(r'track/?', StockTrackingList.as_view(), name='api-stock-track'), url(r'^tree/?', StockCategoryTree.as_view(), name='api-stock-tree'), diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index e04e2a149b..7e9a59470b 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -6,6 +6,7 @@ from rest_framework import serializers from .models import StockItem, StockLocation from .models import StockItemTracking +from .models import StockItemAttachment from django.db.models import Sum, Count from django.db.models.functions import Coalesce @@ -189,6 +190,20 @@ class LocationSerializer(InvenTreeModelSerializer): ] +class StockItemAttachmentSerializer(InvenTreeModelSerializer): + """ Serializer for StockItemAttachment model """ + + class Meta: + model = StockItemAttachment + + fields = [ + 'pk', + 'stock_item', + 'attachment', + 'comment' + ] + + class StockTrackingSerializer(InvenTreeModelSerializer): """ Serializer for StockItemTracking model """