diff --git a/backend/modules/task_management/serializers.py b/backend/modules/task_management/serializers.py index 2a03fdb..39547b1 100644 --- a/backend/modules/task_management/serializers.py +++ b/backend/modules/task_management/serializers.py @@ -86,6 +86,11 @@ class TaskSerializer(serializers.ModelSerializer): assigned_to_name = serializers.SerializerMethodField() work_order_label = serializers.SerializerMethodField() vehicle_registration = serializers.SerializerMethodField() + vehicle_make = serializers.SerializerMethodField() + vehicle_model = serializers.SerializerMethodField() + vehicle_owner_name = serializers.SerializerMethodField() + vehicle_asset_type = serializers.SerializerMethodField() + crane_serial_number = serializers.SerializerMethodField() template_id = serializers.UUIDField(write_only=True, required=False, allow_null=True) auto_close_work_order = serializers.BooleanField(write_only=True, required=False) @@ -94,7 +99,8 @@ class TaskSerializer(serializers.ModelSerializer): fields = [ 'id', 'title', 'description', 'status', 'assigned_to', 'assigned_to_name', - 'vehicle', 'vehicle_registration', + 'vehicle', 'vehicle_registration', 'vehicle_make', 'vehicle_model', + 'vehicle_owner_name', 'vehicle_asset_type', 'crane_serial_number', 'work_order', 'work_order_label', 'scheduled_date', 'template_id', 'auto_close_work_order', @@ -180,3 +186,28 @@ class TaskSerializer(serializers.ModelSerializer): if not obj.vehicle_id: return None return obj.vehicle.registration_number + + def get_vehicle_make(self, obj): + if not obj.vehicle_id: + return None + return obj.vehicle.make or None + + def get_vehicle_model(self, obj): + if not obj.vehicle_id: + return None + return obj.vehicle.model or None + + def get_vehicle_owner_name(self, obj): + if not obj.vehicle_id or not obj.vehicle.client_id: + return None + return obj.vehicle.client.name + + def get_vehicle_asset_type(self, obj): + if not obj.vehicle_id: + return None + return obj.vehicle.asset_type + + def get_crane_serial_number(self, obj): + if not obj.vehicle_id: + return None + return obj.vehicle.crane_serial_number or None diff --git a/backend/modules/task_management/tests/test_serializers.py b/backend/modules/task_management/tests/test_serializers.py index 513a367..efaf0c7 100644 --- a/backend/modules/task_management/tests/test_serializers.py +++ b/backend/modules/task_management/tests/test_serializers.py @@ -4,13 +4,23 @@ from rest_framework.exceptions import ValidationError from django.contrib.auth import get_user_model from modules.task_management.models import Task from modules.fleet.models import Vehicle, WorkOrder +from modules.crm.models import Client class TaskSerializerTests(TestCase): def setUp(self): User = get_user_model() self.user = User.objects.create_user(username="task-user-base", email="task-user-base@example.test", password="pass") - self.vehicle = Vehicle.objects.create(registration_number="ZG-TSK-01", asset_type='crane') + self.client = Client.objects.create(name="Klijent zadatka") + self.vehicle = Vehicle.objects.create( + registration_number="ZG-TSK-01", + asset_type='crane', + make='Liebherr', + model='LTM', + crane_serial_number='123456', + client=self.client, + is_company_vehicle=False, + ) def test_validate_title_too_short_raises(self): serializer = TaskSerializer(data={"title": "ab", "description": "", "status": "aktivan", "vehicle": str(self.vehicle.id)}) @@ -67,6 +77,15 @@ class TaskSerializerTests(TestCase): serializer = TaskSerializer(instance=task) assert serializer.data['scheduled_date'] is None + def test_vehicle_details_are_included_in_serialized_output(self): + task = Task.objects.create(title="Task crane details", assigned_to=self.user, vehicle=self.vehicle) + serializer = TaskSerializer(instance=task) + assert serializer.data['vehicle_make'] == 'Liebherr' + assert serializer.data['vehicle_model'] == 'LTM' + assert serializer.data['crane_serial_number'] == '123456' + assert serializer.data['vehicle_owner_name'] == 'Klijent zadatka' + assert serializer.data['vehicle_asset_type'] == 'crane' + def test_scheduled_date_accepts_valid_date(self): serializer = TaskSerializer( data={"title": "Task mit datum", "status": "aktivan", diff --git a/backend/modules/task_management/views.py b/backend/modules/task_management/views.py index 7a0eb6c..7e4c8c8 100644 --- a/backend/modules/task_management/views.py +++ b/backend/modules/task_management/views.py @@ -22,7 +22,7 @@ class TaskViewSet(viewsets.ModelViewSet): def get_queryset(self): qs = Task.objects.filter(is_active=True).select_related( - 'assigned_to', 'vehicle', 'work_order', 'work_order__vehicle', 'work_hours_table' + 'assigned_to', 'vehicle', 'vehicle__client', 'work_order', 'work_order__vehicle', 'work_hours_table' ) vehicle_id = self.request.query_params.get('vehicle') if vehicle_id: diff --git a/frontend/src/components/dashboard/TaskCalendarWidget.jsx b/frontend/src/components/dashboard/TaskCalendarWidget.jsx index c67ee89..fbfa404 100644 --- a/frontend/src/components/dashboard/TaskCalendarWidget.jsx +++ b/frontend/src/components/dashboard/TaskCalendarWidget.jsx @@ -1,6 +1,7 @@ import { useMemo, useState } from 'preact/hooks'; import { getStatusLabel } from '../../stores/taskStore'; import LeftSlideDrawer from '../layout/LeftSlideDrawer'; +import { getTaskCraneKind, getTaskCraneOwner, getTaskCraneSerial } from '../../lib/taskCraneDisplay'; const WEEKDAY_LABELS = ['Po', 'Ut', 'Sr', 'Če', 'Pe', 'Su', 'Ne']; const MONTH_NAMES = [ @@ -242,6 +243,9 @@ export default function TaskCalendarWidget({ tasks = [], notes = [], onTaskClick {task.assigned_to_name || '-'} •{' '} {getStatusLabel(task.status || '-')} + + {getTaskCraneSerial(task)} • {getTaskCraneKind(task)} • {getTaskCraneOwner(task)} + ))} diff --git a/frontend/src/components/dashboard/TaskServiceRecordsModal.jsx b/frontend/src/components/dashboard/TaskServiceRecordsModal.jsx index 337078c..36d574e 100644 --- a/frontend/src/components/dashboard/TaskServiceRecordsModal.jsx +++ b/frontend/src/components/dashboard/TaskServiceRecordsModal.jsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'preact/hooks'; import ModalShell from '../ui/ModalShell'; import { formatWorkOrderDisplayCode } from '../../lib/displayIds'; import { getStatusLabel } from '../../stores/taskStore'; +import { getTaskCraneDisplay, getTaskCraneOwner } from '../../lib/taskCraneDisplay'; const STATUS_OPTIONS = ['aktivan', 'servis', 'spreman_za_zavrsetak', 'zavrsen', 'neaktivan']; @@ -211,9 +212,13 @@ export default function TaskServiceRecordsModal({