Adds API endpoints for ReturnOrder

- Add list endpoint
- Add detail endpoint
- Adds required serializer models
This commit is contained in:
Oliver 2023-03-14 17:09:09 +11:00
parent 047e65cdce
commit ab1ed84636
4 changed files with 207 additions and 5 deletions

View File

@ -277,6 +277,19 @@ class SalesOrderAllocationAdmin(ImportExportModelAdmin):
autocomplete_fields = ('line', 'shipment', 'item',)
class ReturnOrderResource(InvenTreeResource):
"""Class for managing import / export of ReturnOrder data"""
class Meta:
"""Metaclass options"""
model = models.ReturnOrder
skip_unchanged = True
clean_model_instances = True
exclude = [
'metadata',
]
class ReturnOrderAdmin(ImportExportModelAdmin):
"""Admin class for the ReturnOrder model"""

View File

@ -28,7 +28,7 @@ from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI,
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
from order.admin import (PurchaseOrderExtraLineResource,
PurchaseOrderLineItemResource, PurchaseOrderResource,
SalesOrderExtraLineResource,
ReturnOrderResource, SalesOrderExtraLineResource,
SalesOrderLineItemResource, SalesOrderResource)
from part.models import Part
from plugin.serializers import MetadataSerializer
@ -1213,6 +1213,106 @@ class PurchaseOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
serializer_class = serializers.PurchaseOrderAttachmentSerializer
class ReturnOrderFilter(OrderFilter):
"""Custom API filters for the ReturnOrderList endpoint"""
class Meta:
"""Metaclass options"""
model = models.ReturnOrder
fields = [
'customer',
]
class ReturnOrderList(APIDownloadMixin, ListCreateAPI):
"""API endpoint for accessing a list of ReturnOrder objects"""
queryset = models.ReturnOrder.objects.all()
serializer_class = serializers.ReturnOrderSerializer
filterset_class = ReturnOrderFilter
def get_serializer(self, *args, **kwargs):
"""Return serializer instance for this endpoint"""
try:
kwargs['customer_detail'] = str2bool(
self.request.query_params.get('customer_detail', False)
)
except AttributeError:
pass
# Ensure the context is passed through to the serializer
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def download_queryset(self, queryset, export_format):
"""Download this queryset as a file"""
dataset = ReturnOrderResource().export(queryset=queryset)
filedata = dataset.export(export_format)
filename = f"InvenTree_ReturnOrders.{export_format}"
return DownloadFile(filedata, filename)
def filter_queryset(self, queryset):
"""Custom queryset filtering not supported by the ReturnOrderFilter class"""
return queryset
filter_backends = [
rest_filters.DjangoFilterBackend,
filters.SearchFilter,
InvenTreeOrderingFilter,
]
ordering_field_aliases = {
'reference': ['reference_int', 'reference'],
}
filterset_fields = [
'customer',
]
ordering_fields = [
'creation_date',
'reference',
'customer__name',
'customer_reference',
'status',
'target_date',
]
search_fields = [
'customer__name',
'reference',
'description',
'customer_reference',
]
ordering = '-reference'
class ReturnOrderDetail(RetrieveUpdateDestroyAPI):
"""API endpoint for detail view of a single ReturnOrder object"""
queryset = models.ReturnOrder.objects.all()
serializer_class = serializers.ReturnOrderSerializer
def get_serializer(self, *args, **kwargs):
"""Return the serializer instance for this endpoint"""
try:
kwargs['customer_detail'] = str2bool(
self.request.query_params.get('customer_detail', False)
)
except AttributeError:
pass
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
class OrderCalendarExport(ICalFeed):
"""Calendar export for Purchase/Sales Orders
@ -1450,6 +1550,16 @@ order_api_urls = [
re_path(r'^.*$', SalesOrderAllocationList.as_view(), name='api-so-allocation-list'),
])),
# API endpoints for return orders
re_path(r'^return/', include([
# Return Order detail
path('<int:pk>/', ReturnOrderDetail.as_view(), name='api-return-order-detail'),
# Return Order list
re_path(r'^.*$', ReturnOrderList.as_view(), name='api-return-order-list'),
])),
# API endpoint for subscribing to ICS calendar of purchase/sales orders
re_path(r'^calendar/(?P<ordertype>purchase-order|sales-order)/calendar.ics', OrderCalendarExport(), name='api-po-so-calendar'),
]

View File

@ -46,9 +46,13 @@ from users import models as UserModels
logger = logging.getLogger('inventree')
class TotalPriceMixin:
class TotalPriceMixin(models.Model):
"""Mixin which provides 'total_price' field for an order"""
class Meta:
"""Meta for MetadataMixin."""
abstract = True
def save(self, *args, **kwargs):
"""Update the total_price field when saved"""
@ -1573,7 +1577,7 @@ class ReturnOrder(Order):
@staticmethod
def get_api_url():
"""Return the API URL associated with the ReturnOrder model"""
return reverse('api-return-list')
return reverse('api-return-order-list')
@classmethod
def api_defaults(cls, request):
@ -1645,7 +1649,7 @@ class ReturnOrderAttachment(InvenTreeAttachment):
def get_api_url():
"""Return the API URL associated with the ReturnOrderAttachment class"""
return reverse('api-return-attachment-list')
return reverse('api-return-order-attachment-list')
def getSubdir(self):
"""Return the directory path where ReturnOrderAttachment files are located"""

View File

@ -645,7 +645,7 @@ class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
"""Serializers for the SalesOrder object."""
"""Serializer for the SalesOrder model class"""
class Meta:
"""Metaclass options."""
@ -1398,3 +1398,78 @@ class SalesOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
fields = InvenTreeAttachmentSerializer.attachment_fields([
'order',
])
class ReturnOrderSerializer(InvenTreeModelSerializer):
"""Serializer for the ReturnOrder model class"""
class Meta:
"""Metaclass options"""
model = order.models.ReturnOrder
fields = [
'pk',
'creation_date',
'customer',
'customer_detail',
'customer_reference',
'description',
'link',
'notes',
'reference',
'responsible',
'responsible_detail',
'status',
'status_text',
]
read_only_fields = [
'status',
'creation_date',
]
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer"""
customer_detail = kwargs.pop('customer_detail', False)
super().__init__(*args, **kwargs)
if customer_detail is not True:
self.fields.pop('customer_detail')
@staticmethod
def annotate_queryset(queryset):
"""Custom annotation for the serializer queryset"""
# TODO
return queryset
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
status_text = serializers.CharField(source='get_status_display', read_only=True)
responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False)
reference = serializers.CharField(required=True)
def validate_reference(self, reference):
"""Custom validation for the reference field"""
order.models.ReturnOrder.validate_reference_field(reference)
return reference
class ReturnOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
"""Serializer for the ReturnOrderAttachment model"""
class Meta:
"""Metaclass options"""
model = order.models.ReturnOrderAttachment
fields = InvenTreeAttachmentSerializer.attachment_fields([
'order',
])