# backend/modules/task_management/services.py from django.db import transaction from rest_framework.exceptions import ValidationError from django.core.exceptions import ValidationError as DjangoValidationError import logging from .models import Task, TaskTemplate, TaskWorkEntry from .exceptions import OpenWorkOrderBlockingTaskClose from modules.fleet.models import VehicleServiceRecord logger = logging.getLogger(__name__) class TaskService: @staticmethod def create_task_entry(data: dict, user=None) -> Task: """ Univerzalna metoda: prihvaća podatke i opcionalnog korisnika. """ payload = dict(data) template_id = payload.pop('template_id', None) if payload.get('status') == 'DONE' and (user and not user.is_staff): raise ValidationError({"status": "Samo admini mogu odmah označiti zadatak kao gotov."}) template = None if template_id: template = TaskTemplate.objects.filter(id=template_id, is_active=True).prefetch_related('entries').first() if not template: raise ValidationError({"template_id": "Odabrani template nije pronađen."}) payload['title'] = template.title payload['description'] = template.description payload['status'] = 'aktivan' payload.setdefault('work_order', None) title = (payload.get('title') or '').strip() if title and len(title) < 3: raise ValidationError({"title": "Naslov zadatka mora imati barem 3 znaka."}) if not title: raise ValidationError({"title": "Naslov zadatka je obavezan."}) if user and 'assigned_to' not in payload: payload['assigned_to'] = user entry = Task(**payload) try: entry.full_clean() except DjangoValidationError as exc: logger.error(f"Neuspješna validacija zadatka: {exc}") raise ValidationError(exc.message_dict if hasattr(exc, 'message_dict') else exc.messages) with transaction.atomic(): entry.save() if template: TaskService._create_work_entries_from_template(task=entry, template=template) TaskService._create_service_records_from_template(task=entry, template=template, user=user) logger.info(f"Zadatak uspješno kreiran: {entry.id}") return entry @staticmethod def _create_work_entries_from_template(*, task: Task, template: TaskTemplate): template_entries = list(template.entries.filter(is_active=True).order_by('sort_order', 'created_at')[:10]) for idx, source in enumerate(template_entries, start=1): TaskWorkEntry.objects.create( task=task, template_entry=source, sort_order=idx, title=source.title, work_description=source.work_description, active_faults=source.active_faults, explanation_hr=source.explanation_hr, ) @staticmethod def _create_service_records_from_template(*, task: Task, template: TaskTemplate, user): if not user: raise ValidationError({ "template_id": "Kreiranje taska iz templatea zahtijeva prijavljenog korisnika za servisne zapise." }) if not task.vehicle_id: raise ValidationError({ "vehicle": "Task iz templatea mora imati dodijeljenu dizalicu za generiranje servisnih zapisa." }) template_entries = list(template.entries.filter(is_active=True).order_by('sort_order', 'created_at')[:10]) for source in template_entries: description_parts = [] if source.work_description: description_parts.append(source.work_description.strip()) if source.explanation_hr: description_parts.append(f"Obrazloženje (HR): {source.explanation_hr.strip()}") if source.active_faults: description_parts.append(f"Aktivne greške: {source.active_faults.strip()}") description = "\n\n".join([part for part in description_parts if part]).strip() or source.title VehicleServiceRecord.objects.create( vehicle=task.vehicle, task=task, performed_by=user, service_title=source.title, description=description, parts=(source.active_faults or '').strip(), cost=0, ) @staticmethod def update_task_entry(instance, data, user, auto_close_work_order=False): if not user.is_staff and instance.assigned_to != user: raise ValidationError("Nemate dozvolu za izmjenu ovog zadatka.") target_status = data.get('status', instance.status) target_work_order = data.get('work_order', instance.work_order) closing_statuses = {'neaktivan', 'zavrsen'} if target_status in closing_statuses: if not target_work_order: raise ValidationError({"work_order": "Putni nalog je obavezan prije zatvaranja zadatka."}) if target_work_order.status == 'open': if not auto_close_work_order: work_order_label = f"WO-{str(target_work_order.id).split('-')[0].upper()}" raise OpenWorkOrderBlockingTaskClose(detail={ "code": "OPEN_WORK_ORDER_BLOCKING_TASK_CLOSE", "message": f"Putni nalog {work_order_label} je još otvoren. Želite li ga automatski zatvoriti?", "work_order_id": str(target_work_order.id), "work_order_label": work_order_label, }) if target_work_order.end_mileage is None: raise ValidationError({ "work_order": "Automatsko zatvaranje putnog naloga nije moguće bez završne kilometraže." }) from modules.fleet.services import VehicleService target_work_order = VehicleService.update_work_order(target_work_order, {"status": "closed"}) data['work_order'] = target_work_order for attr, value in data.items(): if attr == 'template_id': continue setattr(instance, attr, value) if 'title' in data and len(str(data.get('title') or '').strip()) < 3: raise ValidationError({"title": "Naslov zadatka mora imati barem 3 znaka."}) try: instance.full_clean() except DjangoValidationError as exc: raise ValidationError(exc.message_dict if hasattr(exc, 'message_dict') else exc.messages) instance.save() return instance