feat: WorkOrderPhoto izolacija, upload modal i SN stupac u tablici putnih naloga
- Dodan WorkOrderPhoto model (FK na WorkOrder, odvojen od VehicleServicePhoto) - Migracija 0022_workorderphoto - WorkOrderViewSet.images action refaktoriran: GET/POST koristi WorkOrderPhoto - WorkOrderPhotoSerializer dodan u serializers.py - WorkOrderPhotoInline dodan u admin.py - Novi WorkOrderPhotoUpload.jsx modal s podrskom za onBack prop i z-[60] - WorkOrderDetailModal: gumb Dodaj fotografije u editMode, photoUploadOpen state, ispravljen onSuccess callback (map na URL stringove), reset pri zatvaranju - FleetDashboardShell: stupac SN u tablici putnih naloga (craneInfo.crane_serial_number) - fleetDashboardStore: uklonjen dupli toast iz uploadWorkOrderPhoto - Dodan test test_work_order_images_endpoint.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,7 @@ from .models import (
|
||||
Vehicle,
|
||||
VehicleNotification,
|
||||
WorkOrder,
|
||||
WorkOrderPhoto,
|
||||
WorkOrderInvoice,
|
||||
VehicleServiceRecord,
|
||||
VehicleServicePhoto,
|
||||
@@ -40,6 +41,7 @@ from .serializers import (
|
||||
VehicleSerializer,
|
||||
WorkOrderSerializer,
|
||||
WorkOrderInvoiceSerializer,
|
||||
WorkOrderPhotoSerializer,
|
||||
VehicleServiceRecordSerializer,
|
||||
VehicleNotificationSerializer,
|
||||
VehicleServicePhotoSerializer,
|
||||
@@ -1240,10 +1242,15 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
def get_queryset(self):
|
||||
return _work_orders_queryset_for_user(self.request.user)
|
||||
|
||||
@action(detail=True, methods=['get'], url_path='images')
|
||||
@action(detail=True, methods=['get', 'post'], url_path='images', parser_classes=[MultiPartParser, FormParser])
|
||||
def images(self, request, pk=None):
|
||||
work_order = self.get_object()
|
||||
service_record_id = request.query_params.get('service_record_id')
|
||||
if request.method.lower() == 'post':
|
||||
serializer = WorkOrderPhotoSerializer(data=request.data, context={'request': request})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save(work_order=work_order, uploaded_by=request.user)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
quality = _parse_positive_int(
|
||||
request.query_params.get('q'),
|
||||
field_name='q',
|
||||
@@ -1259,60 +1266,29 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
max_value=3840,
|
||||
)
|
||||
image_format, _ = _parse_format(request.query_params.get('fmt'))
|
||||
work_order_task_ids = list(
|
||||
work_order.work_order_tasks.filter(is_active=True).values_list('id', flat=True)
|
||||
)
|
||||
|
||||
if not work_order_task_ids:
|
||||
return Response({
|
||||
"work_order_id": work_order.pk,
|
||||
"vehicle_id": work_order.vehicle_id,
|
||||
"service_record_id": service_record_id or None,
|
||||
"images": [],
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
photos_qs = VehicleServicePhoto.objects.filter(
|
||||
photos_qs = WorkOrderPhoto.objects.filter(
|
||||
is_active=True,
|
||||
service_record__is_active=True,
|
||||
service_record__vehicle_id=work_order.vehicle_id,
|
||||
service_record__task_id__in=work_order_task_ids,
|
||||
).select_related('service_record', 'uploaded_by').order_by('-created_at')
|
||||
|
||||
if not request.user.is_staff:
|
||||
photos_qs = photos_qs.filter(service_record__performed_by=request.user)
|
||||
|
||||
if service_record_id:
|
||||
record = _service_records_queryset_for_user(request.user).filter(
|
||||
pk=service_record_id,
|
||||
vehicle_id=work_order.vehicle_id,
|
||||
task_id__in=work_order_task_ids,
|
||||
).first()
|
||||
if record is None:
|
||||
raise DRFValidationError({
|
||||
"service_record_id": "Servisni zapis nije pronađen ili ne pripada ovom nalogu."
|
||||
})
|
||||
photos_qs = photos_qs.filter(service_record_id=record.pk)
|
||||
work_order_id=work_order.pk,
|
||||
).select_related('uploaded_by').order_by('-created_at')
|
||||
|
||||
images = []
|
||||
for photo in photos_qs:
|
||||
optimized_path = reverse('service-photo-optimized', kwargs={'pk': photo.pk})
|
||||
optimized_url = request.build_absolute_uri(
|
||||
f"{optimized_path}?w={width}&q={quality}&fmt={image_format.lower()}"
|
||||
)
|
||||
original_url = request.build_absolute_uri(photo.image.url) if photo.image else None
|
||||
images.append({
|
||||
"id": photo.pk,
|
||||
"service_record_id": photo.service_record_id,
|
||||
"work_order_id": photo.work_order_id,
|
||||
"description": photo.description,
|
||||
"original_url": original_url,
|
||||
"optimized_url": optimized_url,
|
||||
"optimized_url": original_url,
|
||||
"created_at": photo.created_at,
|
||||
})
|
||||
|
||||
return Response({
|
||||
"work_order_id": work_order.pk,
|
||||
"vehicle_id": work_order.vehicle_id,
|
||||
"service_record_id": service_record_id or None,
|
||||
"image_quality": quality,
|
||||
"image_width": width,
|
||||
"image_format": image_format.lower(),
|
||||
"images": images,
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user