added vehicles and vehicles extra, dynamic tables for entities, dynamic dropdowns with overlay for selecting and manage data
This commit is contained in:
+168
-11
@@ -2,10 +2,16 @@ from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from .models import ComPortReading, User
|
||||
from .serializers import ComPortReadingSerializer, UserSerializer, UserDetailSerializer, ChangePasswordSerializer, VehicleSerializer, NomenclatureSerializer
|
||||
from .models import ComPortReading, User, Report
|
||||
from .serializers import (
|
||||
ComPortReadingSerializer, UserSerializer, UserDetailSerializer,
|
||||
ChangePasswordSerializer, VehicleSerializer, NomenclatureSerializer,
|
||||
NomenclatureEntrySerializer, ReportSerializer,
|
||||
)
|
||||
from django.utils import timezone
|
||||
from vehicles.models import Vehicle
|
||||
from nomenclatures.models import Nomenclature
|
||||
from nomenclatures.models import Nomenclature, NomenclatureEntry
|
||||
from scalesapp.sse import sse_broadcast_update
|
||||
|
||||
|
||||
class UserViewSet(viewsets.ModelViewSet):
|
||||
@@ -77,7 +83,7 @@ class ComPortReadingViewSet(viewsets.ModelViewSet):
|
||||
serializer = self.get_serializer(reading)
|
||||
return Response(serializer.data)
|
||||
return Response({'detail': 'No readings yet'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def by_port(self, request):
|
||||
"""Get readings for a specific port"""
|
||||
@@ -90,7 +96,7 @@ class ComPortReadingViewSet(viewsets.ModelViewSet):
|
||||
readings = ComPortReading.objects.filter(port=port)
|
||||
serializer = self.get_serializer(readings, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def get_client_ip(self, request):
|
||||
"""Get client IP address"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
@@ -99,7 +105,7 @@ class ComPortReadingViewSet(viewsets.ModelViewSet):
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Save the reading with client IP"""
|
||||
serializer.save(source_ip=self.get_client_ip(self.request))
|
||||
@@ -116,14 +122,31 @@ class VehicleViewSet(viewsets.ModelViewSet):
|
||||
partial_update: Partial update of vehicle
|
||||
destroy: Delete a vehicle (cascades to VehicleExtra)
|
||||
by_number: Get vehicle by vehicle_number
|
||||
set_tare: Set tare weight from scale reading
|
||||
set_gross: Set gross weight from scale reading
|
||||
"""
|
||||
queryset = Vehicle.objects.select_related('extra').all()
|
||||
queryset = Vehicle.objects.select_related('extra', 'tare_user', 'gross_user').all()
|
||||
serializer_class = VehicleSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_fields = ['vehicle_number']
|
||||
search_fields = ['vehicle_number']
|
||||
ordering = ['vehicle_number']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
instance = serializer.save()
|
||||
data = VehicleSerializer(instance).data
|
||||
sse_broadcast_update('vehicle', 'created', data)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save()
|
||||
data = VehicleSerializer(instance).data
|
||||
sse_broadcast_update('vehicle', 'updated', data)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
data = VehicleSerializer(instance).data
|
||||
instance.delete()
|
||||
sse_broadcast_update('vehicle', 'deleted', data)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='by-number')
|
||||
def by_number(self, request):
|
||||
"""Get vehicle by vehicle_number query parameter"""
|
||||
@@ -144,6 +167,48 @@ class VehicleViewSet(viewsets.ModelViewSet):
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='set-tare')
|
||||
def set_tare(self, request, pk=None):
|
||||
"""Set tare weight from scale reading"""
|
||||
vehicle = self.get_object()
|
||||
value = request.data.get('value')
|
||||
if value is None:
|
||||
return Response({'detail': 'value is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
return Response({'detail': 'value must be a number'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
vehicle.tare = value
|
||||
vehicle.tare_date = timezone.now()
|
||||
vehicle.tare_user = request.user
|
||||
vehicle.save()
|
||||
|
||||
serializer = self.get_serializer(vehicle)
|
||||
sse_broadcast_update('vehicle', 'updated', serializer.data)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='set-gross')
|
||||
def set_gross(self, request, pk=None):
|
||||
"""Set gross weight from scale reading"""
|
||||
vehicle = self.get_object()
|
||||
value = request.data.get('value')
|
||||
if value is None:
|
||||
return Response({'detail': 'value is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
return Response({'detail': 'value must be a number'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
vehicle.gross = value
|
||||
vehicle.gross_date = timezone.now()
|
||||
vehicle.gross_user = request.user
|
||||
vehicle.save()
|
||||
|
||||
serializer = self.get_serializer(vehicle)
|
||||
sse_broadcast_update('vehicle', 'updated', serializer.data)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class NomenclatureViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
@@ -154,16 +219,30 @@ class NomenclatureViewSet(viewsets.ModelViewSet):
|
||||
retrieve: Get a specific nomenclature by ID
|
||||
update: Full update of nomenclature and fields
|
||||
partial_update: Partial update of nomenclature
|
||||
destroy: Delete a nomenclature (cascades to fields)
|
||||
destroy: Delete a nomenclature (cascades to fields and entries)
|
||||
by_code: Get nomenclature by code
|
||||
by_applies_to: Filter nomenclatures by applies_to type
|
||||
entries: List or create entries for a specific nomenclature
|
||||
"""
|
||||
queryset = Nomenclature.objects.prefetch_related('nomenclaturefield_set').all()
|
||||
queryset = Nomenclature.objects.prefetch_related('fields').all()
|
||||
serializer_class = NomenclatureSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_fields = ['applies_to', 'code']
|
||||
filterset_fields = ['applies_to', 'code', 'kind']
|
||||
search_fields = ['code', 'name']
|
||||
ordering = ['code']
|
||||
ordering = ['sort_order', 'code']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
instance = serializer.save()
|
||||
sse_broadcast_update('nomenclature', 'insert', NomenclatureSerializer(instance).data)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save()
|
||||
sse_broadcast_update('nomenclature', 'update', NomenclatureSerializer(instance).data)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
data = NomenclatureSerializer(instance).data
|
||||
instance.delete()
|
||||
sse_broadcast_update('nomenclature', 'delete', data)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='by-code')
|
||||
def by_code(self, request):
|
||||
@@ -209,3 +288,81 @@ class NomenclatureViewSet(viewsets.ModelViewSet):
|
||||
|
||||
serializer = self.get_serializer(nomenclatures, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['get', 'post'], url_path='entries')
|
||||
def entries(self, request, pk=None):
|
||||
"""List or create entries for a specific nomenclature."""
|
||||
nomenclature = self.get_object()
|
||||
|
||||
if request.method == 'GET':
|
||||
entries = nomenclature.entries.all()
|
||||
is_active = request.query_params.get('is_active')
|
||||
if is_active is not None:
|
||||
entries = entries.filter(is_active=is_active.lower() == 'true')
|
||||
serializer = NomenclatureEntrySerializer(entries, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
serializer = NomenclatureEntrySerializer(
|
||||
data=request.data,
|
||||
context={'nomenclature': nomenclature},
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save(nomenclature=nomenclature)
|
||||
entry_data = {**serializer.data, 'nomenclature_code': nomenclature.code}
|
||||
sse_broadcast_update('nomenclature_entry', 'insert', entry_data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class NomenclatureEntryViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for individual nomenclature entry management.
|
||||
For list/create use the nested endpoint: /api/nomenclatures/{id}/entries/
|
||||
This viewset handles retrieve/update/delete of individual entries.
|
||||
"""
|
||||
queryset = NomenclatureEntry.objects.select_related('nomenclature').all()
|
||||
serializer_class = NomenclatureEntrySerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_serializer_context(self):
|
||||
context = super().get_serializer_context()
|
||||
if self.kwargs.get('pk'):
|
||||
try:
|
||||
entry = NomenclatureEntry.objects.select_related('nomenclature').get(pk=self.kwargs['pk'])
|
||||
context['nomenclature'] = entry.nomenclature
|
||||
except NomenclatureEntry.DoesNotExist:
|
||||
pass
|
||||
return context
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save()
|
||||
entry_data = {**NomenclatureEntrySerializer(instance).data, 'nomenclature_code': instance.nomenclature.code}
|
||||
sse_broadcast_update('nomenclature_entry', 'update', entry_data)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
entry_data = {**NomenclatureEntrySerializer(instance).data, 'nomenclature_code': instance.nomenclature.code}
|
||||
instance.delete()
|
||||
sse_broadcast_update('nomenclature_entry', 'delete', entry_data)
|
||||
|
||||
|
||||
class ReportViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for report template management.
|
||||
|
||||
list: Get all reports
|
||||
create: Create a new report
|
||||
retrieve: Get a specific report by ID
|
||||
update: Update a report
|
||||
destroy: Delete a report
|
||||
"""
|
||||
queryset = Report.objects.select_related('created_by').all()
|
||||
serializer_class = ReportSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_fields = ['name']
|
||||
search_fields = ['name']
|
||||
ordering = ['-updated_at']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(created_by=self.request.user)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
serializer.save()
|
||||
|
||||
Reference in New Issue
Block a user