Merge pull request #739 from SchrodingersGat/api-consolidation

Api consolidation
This commit is contained in:
Oliver 2020-04-20 09:55:16 +10:00 committed by GitHub
commit 85e20041c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 69 deletions

View File

@ -104,21 +104,23 @@ function removePurchaseOrderLineItem(e) {
function loadPurchaseOrderTable(table, options) { function loadPurchaseOrderTable(table, options) {
/* Create a purchase-order table */ /* Create a purchase-order table */
var params = options.params || {}; options.params = options.params || {};
options.params['supplier_detail'] = true;
var filters = loadTableFilters("order"); var filters = loadTableFilters("order");
for (var key in params) { for (var key in options.params) {
filters[key] = params[key]; filters[key] = options.params[key];
} }
setupFilterList("order", table); setupFilterList("order", $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: options.url, url: options.url,
queryParams: filters, queryParams: filters,
groupBy: false, groupBy: false,
original: params, original: options.params,
formatNoMatches: function() { return "No purchase orders found"; }, formatNoMatches: function() { return "No purchase orders found"; },
columns: [ columns: [
{ {
@ -131,15 +133,15 @@ function loadPurchaseOrderTable(table, options) {
field: 'reference', field: 'reference',
title: 'Purchase Order', title: 'Purchase Order',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return renderLink(value, "/order/purchase-order/" + row.pk + "/"); return renderLink(value, `/order/purchase-order/${row.pk}/`);
} }
}, },
{ {
sortable: true, sortable: true,
field: 'supplier', field: 'supplier_detail',
title: 'Supplier', title: 'Supplier',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return imageHoverIcon(row.supplier__image) + renderLink(row.supplier__name, '/company/' + value + '/purchase-orders/'); return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`);
} }
}, },
{ {
@ -162,7 +164,7 @@ function loadPurchaseOrderTable(table, options) {
}, },
{ {
sortable: true, sortable: true,
field: 'lines', field: 'line_items',
title: 'Items' title: 'Items'
}, },
], ],

View File

@ -8,14 +8,10 @@ from __future__ import unicode_literals
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, permissions from rest_framework import generics, permissions
from rest_framework import filters from rest_framework import filters
from rest_framework.response import Response
from django.conf import settings
from django.conf.urls import url from django.conf.urls import url
from InvenTree.status_codes import OrderStatus from InvenTree.helpers import str2bool
import os
from part.models import Part from part.models import Part
from company.models import SupplierPart from company.models import SupplierPart
@ -34,66 +30,66 @@ class POList(generics.ListCreateAPIView):
queryset = PurchaseOrder.objects.all() queryset = PurchaseOrder.objects.all()
serializer_class = POSerializer serializer_class = POSerializer
def list(self, request, *args, **kwargs): def get_serializer(self, *args, **kwargs):
queryset = self.get_queryset().prefetch_related('supplier', 'lines') try:
kwargs['supplier_detail'] = str2bool(self.request.query_params.get('supplier_detail', False))
except AttributeError:
pass
queryset = self.filter_queryset(queryset) # Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = queryset.prefetch_related(
'supplier',
'lines',
)
queryset = POSerializer.annotate_queryset(queryset)
return queryset
def filter_queryset(self, queryset):
# Perform basic filtering
queryset = super().filter_queryset(queryset)
params = self.request.query_params
# Special filtering for 'status' field # Special filtering for 'status' field
if 'status' in request.GET: status = params.get('status', None)
status = request.GET['status']
if status is not None:
# First attempt to filter by integer value # First attempt to filter by integer value
try: queryset = queryset.filter(status=status)
status = int(status)
queryset = queryset.filter(status=status)
except ValueError:
try:
value = OrderStatus.value(status)
queryset = queryset.filter(status=value)
except ValueError:
pass
# Attempt to filter by part # Attempt to filter by part
if 'part' in request.GET: part = params.get('part', None)
if part is not None:
try: try:
part = Part.objects.get(pk=request.GET['part']) part = Part.objects.get(pk=part)
queryset = queryset.filter(id__in=[p.id for p in part.purchase_orders()]) queryset = queryset.filter(id__in=[p.id for p in part.purchase_orders()])
except (Part.DoesNotExist, ValueError): except (Part.DoesNotExist, ValueError):
pass pass
# Attempt to filter by supplier part # Attempt to filter by supplier part
if 'supplier_part' in request.GET: supplier_part = params.get('supplier_part', None)
if supplier_part is not None:
try: try:
supplier_part = SupplierPart.objects.get(pk=request.GET['supplier_part']) supplier_part = SupplierPart.objects.get(pk=supplier_part)
queryset = queryset.filter(id__in=[p.id for p in supplier_part.purchase_orders()]) queryset = queryset.filter(id__in=[p.id for p in supplier_part.purchase_orders()])
except (ValueError, SupplierPart.DoesNotExist): except (ValueError, SupplierPart.DoesNotExist):
pass pass
data = queryset.values( return queryset
'pk',
'supplier',
'supplier_reference',
'supplier__name',
'supplier__image',
'reference',
'description',
'link',
'status',
'notes',
'creation_date',
)
for item in data:
order = queryset.get(pk=item['pk'])
item['supplier__image'] = os.path.join(settings.MEDIA_URL, item['supplier__image'])
item['status_text'] = OrderStatus.label(item['status'])
item['lines'] = order.lines.count()
return Response(data)
permission_classes = [ permission_classes = [
permissions.IsAuthenticated, permissions.IsAuthenticated,
@ -123,6 +119,31 @@ class PODetail(generics.RetrieveUpdateAPIView):
queryset = PurchaseOrder.objects.all() queryset = PurchaseOrder.objects.all()
serializer_class = POSerializer serializer_class = POSerializer
def get_serializer(self, *args, **kwargs):
try:
kwargs['supplier_detail'] = str2bool(self.request.query_params.get('supplier_detail', False))
except AttributeError:
pass
# Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = queryset.prefetch_related(
'supplier',
'lines',
)
queryset = POSerializer.annotate_queryset(queryset)
return queryset
permission_classes = [ permission_classes = [
permissions.IsAuthenticated permissions.IsAuthenticated
] ]

View File

@ -5,7 +5,12 @@ JSON serializers for the Order API
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from rest_framework import serializers
from django.db.models import Count
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
from company.serializers import CompanyBriefSerializer
from .models import PurchaseOrder, PurchaseOrderLineItem from .models import PurchaseOrder, PurchaseOrderLineItem
@ -13,17 +18,48 @@ from .models import PurchaseOrder, PurchaseOrderLineItem
class POSerializer(InvenTreeModelSerializer): class POSerializer(InvenTreeModelSerializer):
""" Serializes an Order object """ """ Serializes an Order object """
def __init__(self, *args, **kwargs):
supplier_detail = kwargs.pop('supplier_detail', False)
super().__init__(*args, **kwargs)
if supplier_detail is not True:
self.fields.pop('supplier_detail')
@staticmethod
def annotate_queryset(queryset):
"""
Add extra information to the queryset
"""
return queryset.annotate(
line_items=Count('lines'),
)
supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)
line_items = serializers.IntegerField(read_only=True)
status_text = serializers.CharField(source='get_status_display', read_only=True)
class Meta: class Meta:
model = PurchaseOrder model = PurchaseOrder
fields = [ fields = [
'pk', 'pk',
'supplier', 'issue_date',
'supplier_reference', 'complete_date',
'reference', 'creation_date',
'description', 'description',
'line_items',
'link', 'link',
'reference',
'supplier',
'supplier_detail',
'supplier_reference',
'status', 'status',
'status_text',
'notes', 'notes',
] ]

View File

@ -134,11 +134,41 @@ class PartDetail(generics.RetrieveUpdateAPIView):
queryset = Part.objects.all() queryset = Part.objects.all()
serializer_class = part_serializers.PartSerializer serializer_class = part_serializers.PartSerializer
starred_parts = None
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = part_serializers.PartSerializer.prefetch_queryset(queryset)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
return queryset
permission_classes = [ permission_classes = [
permissions.IsAuthenticated, permissions.IsAuthenticated,
] ]
def get_serializer(self, *args, **kwargs):
try:
cat_detail = str2bool(self.request.query_params.get('category_detail', False))
except AttributeError:
cat_detail = None
# Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context()
kwargs['category_detail'] = cat_detail
# Pass a list of "starred" parts fo the current user to the serializer
# We do this to reduce the number of database queries required!
if self.starred_parts is None and self.request is not None:
self.starred_parts = [star.part for star in self.request.user.starred_parts.all()]
kwargs['starred_parts'] = self.starred_parts
return self.serializer_class(*args, **kwargs)
class PartList(generics.ListCreateAPIView): class PartList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of Part objects """ API endpoint for accessing a list of Part objects

View File

@ -58,20 +58,28 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = StockItemSerializer serializer_class = StockItemSerializer
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = StockItemSerializer.prefetch_queryset(queryset)
queryset = StockItemSerializer.annotate_queryset(queryset)
return queryset
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
kwargs['part_detail'] = str2bool(self.request.GET.get('part_detail', False)) kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', False))
except AttributeError: except AttributeError:
pass pass
try: try:
kwargs['location_detail'] = str2bool(self.request.GET.get('location_detail', False)) kwargs['location_detail'] = str2bool(self.request.query_params.get('location_detail', False))
except AttributeError: except AttributeError:
pass pass
try: try:
kwargs['supplier_detail'] = str2bool(self.request.GET.get('supplier_detail', False)) kwargs['supplier_part_detail'] = str2bool(self.request.query_params.get('supplier_detail', False))
except AttributeError: except AttributeError:
pass pass
@ -321,14 +329,19 @@ class StockList(generics.ListCreateAPIView):
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
part_detail = str2bool(self.request.query_params.get('part_detail', None)) kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', None))
location_detail = str2bool(self.request.query_params.get('location_detail', None))
except AttributeError: except AttributeError:
part_detail = None pass
location_detail = None
kwargs['part_detail'] = part_detail try:
kwargs['location_detail'] = location_detail kwargs['location_detail'] = str2bool(self.request.query_params.get('location_detail', None))
except AttributeError:
pass
try:
kwargs['supplier_part_detail'] = str2bool(self.request.query_params.get('supplier_part_detail', None))
except AttributeError:
pass
# Ensure the request context is passed through # Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()

View File

@ -7,6 +7,7 @@ from rest_framework import serializers
from .models import StockItem, StockLocation from .models import StockItem, StockLocation
from .models import StockItemTracking from .models import StockItemTracking
from company.serializers import SupplierPartSerializer
from part.serializers import PartBriefSerializer from part.serializers import PartBriefSerializer
from InvenTree.serializers import UserSerializerBrief, InvenTreeModelSerializer from InvenTree.serializers import UserSerializerBrief, InvenTreeModelSerializer
@ -78,13 +79,14 @@ class StockItemSerializer(InvenTreeModelSerializer):
performing database queries as efficiently as possible. performing database queries as efficiently as possible.
""" """
# TODO # TODO - Add custom annotated fields
pass return queryset
status_text = serializers.CharField(source='get_status_display', read_only=True) status_text = serializers.CharField(source='get_status_display', read_only=True)
part_detail = PartBriefSerializer(source='part', many=False, read_only=True) part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True) location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
supplier_part_detail = SupplierPartSerializer(source='supplier_part', many=False, read_only=True)
tracking_items = serializers.IntegerField(source='tracking_info_count', read_only=True) tracking_items = serializers.IntegerField(source='tracking_info_count', read_only=True)
@ -92,6 +94,7 @@ class StockItemSerializer(InvenTreeModelSerializer):
part_detail = kwargs.pop('part_detail', False) part_detail = kwargs.pop('part_detail', False)
location_detail = kwargs.pop('location_detail', False) location_detail = kwargs.pop('location_detail', False)
supplier_part_detail = kwargs.pop('supplier_part_detail', False)
super(StockItemSerializer, self).__init__(*args, **kwargs) super(StockItemSerializer, self).__init__(*args, **kwargs)
@ -101,6 +104,9 @@ class StockItemSerializer(InvenTreeModelSerializer):
if location_detail is not True: if location_detail is not True:
self.fields.pop('location_detail') self.fields.pop('location_detail')
if supplier_part_detail is not True:
self.fields.pop('supplier_part_detail')
class Meta: class Meta:
model = StockItem model = StockItem
fields = [ fields = [
@ -116,6 +122,7 @@ class StockItemSerializer(InvenTreeModelSerializer):
'quantity', 'quantity',
'serial', 'serial',
'supplier_part', 'supplier_part',
'supplier_part_detail',
'status', 'status',
'status_text', 'status_text',
'tracking_items', 'tracking_items',