- Novo polje Task.scheduled_date (DateField, nullable, db_index) za planiranje - Migracija 0009_task_scheduled_date - Celery task notify_upcoming_tasks: dnevno u 08:00 šalje Pusher + email za sutrašnje zadatke - TaskCalendarWidget: react-spring slide panel s lijeve strane, grid prikaz mjeseca, dot indikatori, klik otvara TaskServiceRecordsModal - FleetDashboardShell: import + render kalendara, Planirano stupac u tablici zadataka - TaskCreateModal + TaskServiceRecordsModal: scheduled_date input i prikaz - Admin + serializer + views: scheduled_date filter i prikaz - 47 backend testova prolaze (uključuje 8 novih za notify_upcoming_tasks) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
# backend/modules/task_management/tasks.py
|
|
|
|
import logging
|
|
from celery import shared_task
|
|
from .models import Task
|
|
from django.contrib.auth import get_user_model
|
|
from modules.fleet.models import Vehicle, WorkOrder
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@shared_task
|
|
def notify_upcoming_tasks():
|
|
"""
|
|
Dnevna Celery periodik task koji šalje Pusher + email notifikacije
|
|
za radne zadatke čiji je planirani datum izvršenja sutra.
|
|
"""
|
|
from datetime import date, timedelta
|
|
from modules.fleet.services import NotificationService
|
|
|
|
tomorrow = date.today() + timedelta(days=1)
|
|
upcoming = Task.objects.filter(
|
|
is_active=True,
|
|
scheduled_date=tomorrow,
|
|
status__in=('aktivan', 'servis'),
|
|
).select_related('assigned_to', 'vehicle')
|
|
|
|
notified = 0
|
|
for task in upcoming:
|
|
recipient = task.assigned_to
|
|
if not recipient:
|
|
continue
|
|
vehicle_label = (
|
|
task.vehicle.registration_number if task.vehicle else 'nepoznato'
|
|
)
|
|
try:
|
|
NotificationService.create_notification(
|
|
recipient=recipient,
|
|
title=f"Sutra planirani zadatak: {task.title}",
|
|
message=(
|
|
f"Radni zadatak '{task.title}' na dizalici {vehicle_label} "
|
|
f"je planiran za sutra ({tomorrow.strftime('%d.%m.%Y')})."
|
|
),
|
|
level="info",
|
|
send_email=True,
|
|
metadata={
|
|
"entity_type": "task",
|
|
"task_id": str(task.id),
|
|
"vehicle_id": str(task.vehicle_id) if task.vehicle_id else None,
|
|
"section": "dashboard",
|
|
"scheduled_date": tomorrow.isoformat(),
|
|
},
|
|
)
|
|
notified += 1
|
|
except Exception:
|
|
logger.exception("Greška pri slanju notifikacije za task %s", task.id)
|
|
|
|
logger.info("notify_upcoming_tasks: obaviješteno %d servisera za datum %s", notified, tomorrow)
|
|
return {"notified": notified, "date": tomorrow.isoformat()}
|
|
|
|
|
|
@shared_task
|
|
def create_task_from_invoice(invoice_data):
|
|
"""
|
|
Ovaj zadatak se poziva asinkrono.
|
|
"""
|
|
User = get_user_model()
|
|
# Logika: dodijeli zadatak adminu ako je račun veći od 1000
|
|
amount = float(invoice_data.get('amount', 0))
|
|
admin_user = User.objects.filter(is_staff=True).first()
|
|
work_order = None
|
|
vehicle = None
|
|
work_order_id = invoice_data.get('work_order_id')
|
|
vehicle_id = invoice_data.get('vehicle_id')
|
|
|
|
if work_order_id:
|
|
work_order = WorkOrder.objects.filter(pk=work_order_id).first()
|
|
if work_order:
|
|
vehicle = work_order.vehicle
|
|
if vehicle is None and vehicle_id:
|
|
vehicle = Vehicle.objects.filter(pk=vehicle_id).first()
|
|
|
|
if amount > 1000:
|
|
Task.objects.create(
|
|
title=f"Provjera računa: {invoice_data['invoice_number']}",
|
|
description=f"Račun iznosi {amount}, potrebna revizija.",
|
|
assigned_to=admin_user,
|
|
status='aktivan',
|
|
vehicle=vehicle,
|
|
work_order=work_order,
|
|
)
|