274 lines
10 KiB
Python
274 lines
10 KiB
Python
import json
|
|
from io import BytesIO
|
|
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.views.generic import ListView, DetailView, CreateView, UpdateView, TemplateView, View
|
|
from django.urls import reverse_lazy, reverse
|
|
from django import forms
|
|
from django.db.models import Count, Q
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.http import HttpResponse
|
|
from reportlab.pdfgen import canvas
|
|
from reportlab.lib.pagesizes import A4
|
|
from reportlab.pdfbase import pdfmetrics
|
|
from reportlab.pdfbase.ttfonts import TTFont
|
|
from .models import FleetUnit, Maintenance, Breakdown, PartRequest, Category, Document
|
|
|
|
# Forms
|
|
class FleetUnitForm(forms.ModelForm):
|
|
class Meta:
|
|
model = FleetUnit
|
|
fields = ['name', 'category', 'model_name', 'vin', 'plate_number', 'year', 'photo', 'status', 'commissioning_date', 'notes']
|
|
widgets = {
|
|
'commissioning_date': forms.DateInput(attrs={'type': 'date'}),
|
|
'notes': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
class MaintenanceForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Maintenance
|
|
fields = ['fleet_unit', 'm_type', 'planned_date', 'planned_runtime', 'mechanic', 'notes']
|
|
widgets = {
|
|
'planned_date': forms.DateInput(attrs={'type': 'date'}),
|
|
'notes': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
class BreakdownForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Breakdown
|
|
fields = ['fleet_unit', 'system_node', 'description', 'photo', 'status', 'notes']
|
|
widgets = {
|
|
'description': forms.Textarea(attrs={'rows': 3}),
|
|
'notes': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
class PartRequestForm(forms.ModelForm):
|
|
class Meta:
|
|
model = PartRequest
|
|
fields = ['fleet_unit', 'breakdown', 'part_name', 'article_number', 'quantity', 'photo', 'notes']
|
|
widgets = {
|
|
'notes': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
# Views
|
|
class IndexView(TemplateView):
|
|
template_name = 'core/index.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['total_units'] = FleetUnit.objects.count()
|
|
context['active_units'] = FleetUnit.objects.filter(status='active').count()
|
|
context['broken_units'] = FleetUnit.objects.filter(status='broken').count()
|
|
context['repair_units'] = FleetUnit.objects.filter(status='repair').count()
|
|
|
|
context['status_counts'] = FleetUnit.objects.values('status').annotate(total=Count('status'))
|
|
context['recent_maintenances'] = Maintenance.objects.all().order_by('-planned_date')[:5]
|
|
context['recent_breakdowns'] = Breakdown.objects.all().order_by('-date')[:5]
|
|
|
|
return context
|
|
|
|
# Fleet Views
|
|
class FleetListView(ListView):
|
|
model = FleetUnit
|
|
template_name = 'core/fleet_list.html'
|
|
context_object_name = 'units'
|
|
paginate_by = 12
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(name__icontains=search) |
|
|
Q(plate_number__icontains=search) |
|
|
Q(vin__icontains=search)
|
|
)
|
|
return queryset
|
|
|
|
class FleetDetailView(DetailView):
|
|
model = FleetUnit
|
|
template_name = 'core/fleet_detail.html'
|
|
context_object_name = 'unit'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['maintenances'] = self.object.maintenances.all().order_by('-planned_date')
|
|
context['breakdowns'] = self.object.breakdowns.all().order_by('-date')
|
|
context['part_requests'] = self.object.part_requests.all().order_by('-created_at')
|
|
return context
|
|
|
|
class FleetCreateView(CreateView):
|
|
model = FleetUnit
|
|
template_name = 'core/fleet_form.html'
|
|
form_class = FleetUnitForm
|
|
success_url = reverse_lazy('fleet_list')
|
|
|
|
class FleetUpdateView(UpdateView):
|
|
model = FleetUnit
|
|
template_name = 'core/fleet_form.html'
|
|
form_class = FleetUnitForm
|
|
|
|
def get_success_url(self):
|
|
return reverse_lazy('fleet_detail', kwargs={'pk': self.object.pk})
|
|
|
|
# Maintenance Views
|
|
class MaintenanceListView(ListView):
|
|
model = Maintenance
|
|
template_name = 'core/maintenance_list.html'
|
|
context_object_name = 'maintenances'
|
|
paginate_by = 15
|
|
|
|
class MaintenanceDetailView(DetailView):
|
|
model = Maintenance
|
|
template_name = 'core/maintenance_detail.html'
|
|
context_object_name = 'maintenance'
|
|
|
|
class MaintenanceCreateView(CreateView):
|
|
model = Maintenance
|
|
template_name = 'core/maintenance_form.html'
|
|
form_class = MaintenanceForm
|
|
success_url = reverse_lazy('maintenance_list')
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
fleet_id = self.request.GET.get('fleet_unit')
|
|
if fleet_id:
|
|
initial['fleet_unit'] = get_object_or_404(FleetUnit, pk=fleet_id)
|
|
return initial
|
|
|
|
def form_valid(self, form):
|
|
m_type = form.cleaned_data.get('m_type')
|
|
checklist = []
|
|
if m_type == 'TO-250':
|
|
checklist = [{"task": "Замена масла в двигателе", "done": False}, {"task": "Замена масляного фильтра", "done": False}]
|
|
elif m_type == 'TO-500':
|
|
checklist = [{"task": "Замена масла в двигателе", "done": False}, {"task": "Замена фильтров", "done": False}, {"task": "Осмотр ходовой", "done": False}]
|
|
else:
|
|
checklist = [{"task": "Общий осмотр", "done": False}]
|
|
|
|
form.instance.checklist = checklist
|
|
return super().form_valid(form)
|
|
|
|
class MaintenanceUpdateView(UpdateView):
|
|
model = Maintenance
|
|
template_name = 'core/maintenance_form.html'
|
|
form_class = MaintenanceForm
|
|
|
|
def get_success_url(self):
|
|
return reverse('maintenance_detail', kwargs={'pk': self.object.pk})
|
|
|
|
class MaintenanceProcessView(View):
|
|
def post(self, request, pk):
|
|
maintenance = get_object_or_404(Maintenance, pk=pk)
|
|
task_index = request.POST.get('task_index')
|
|
done = request.POST.get('done') == 'true'
|
|
|
|
if task_index is not None:
|
|
idx = int(task_index)
|
|
if 0 <= idx < len(maintenance.checklist):
|
|
maintenance.checklist[idx]['done'] = done
|
|
maintenance.save()
|
|
|
|
return redirect('maintenance_detail', pk=pk)
|
|
|
|
class MaintenanceCompleteView(View):
|
|
def post(self, request, pk):
|
|
maintenance = get_object_or_404(Maintenance, pk=pk)
|
|
maintenance.status = 'completed'
|
|
maintenance.actual_date = request.POST.get('actual_date') or None
|
|
maintenance.actual_runtime = request.POST.get('actual_runtime') or None
|
|
maintenance.parts_used = request.POST.get('parts_used')
|
|
maintenance.save()
|
|
|
|
unit = maintenance.fleet_unit
|
|
unit.status = 'active'
|
|
unit.save()
|
|
|
|
return redirect('maintenance_detail', pk=pk)
|
|
|
|
class MaintenancePDFView(View):
|
|
def get(self, request, pk):
|
|
maintenance = get_object_or_404(Maintenance, pk=pk)
|
|
buffer = BytesIO()
|
|
p = canvas.Canvas(buffer, pagesize=A4)
|
|
|
|
# Draw PDF content (basic for now as we don't have Cyrillic fonts loaded by default in reportlab without setup)
|
|
# Note: For real Cyrillic support we need to register a TTF font.
|
|
p.drawString(100, 800, f"Maintenance Act - {maintenance.m_type}")
|
|
p.drawString(100, 780, f"Unit: {maintenance.fleet_unit.name}")
|
|
p.drawString(100, 760, f"Date: {maintenance.actual_date}")
|
|
p.drawString(100, 740, f"Runtime: {maintenance.actual_runtime}")
|
|
|
|
y = 700
|
|
p.drawString(100, y, "Checklist:")
|
|
y -= 20
|
|
for item in maintenance.checklist:
|
|
status = "[x]" if item['done'] else "[ ]"
|
|
p.drawString(120, y, f"{status} {item['task']}")
|
|
y -= 20
|
|
|
|
p.showPage()
|
|
p.save()
|
|
|
|
buffer.seek(0)
|
|
return HttpResponse(buffer, content_type='application/pdf',
|
|
headers={'Content-Disposition': f'attachment; filename="maintenance-{pk}.pdf"'})
|
|
|
|
# Breakdown Views
|
|
class BreakdownListView(ListView):
|
|
model = Breakdown
|
|
template_name = 'core/breakdown_list.html'
|
|
context_object_name = 'breakdowns'
|
|
|
|
class BreakdownCreateView(CreateView):
|
|
model = Breakdown
|
|
form_class = BreakdownForm
|
|
template_name = 'core/breakdown_form.html'
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
fleet_id = self.request.GET.get('fleet_unit')
|
|
if fleet_id:
|
|
initial['fleet_unit'] = get_object_or_404(FleetUnit, pk=fleet_id)
|
|
return initial
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
unit = self.object.fleet_unit
|
|
if self.object.status == 'reported':
|
|
unit.status = 'broken'
|
|
elif self.object.status == 'need_part':
|
|
unit.status = 'waiting_parts'
|
|
unit.save()
|
|
return response
|
|
|
|
def get_success_url(self):
|
|
return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk})
|
|
|
|
# Part Request Views
|
|
class PartRequestListView(ListView):
|
|
model = PartRequest
|
|
template_name = 'core/part_request_list.html'
|
|
context_object_name = 'requests'
|
|
|
|
class PartRequestCreateView(CreateView):
|
|
model = PartRequest
|
|
form_class = PartRequestForm
|
|
template_name = 'core/part_request_form.html'
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
breakdown_id = self.request.GET.get('breakdown')
|
|
if breakdown_id:
|
|
breakdown = get_object_or_404(Breakdown, pk=breakdown_id)
|
|
initial['breakdown'] = breakdown
|
|
initial['fleet_unit'] = breakdown.fleet_unit
|
|
fleet_id = self.request.GET.get('fleet_unit')
|
|
if fleet_id:
|
|
initial['fleet_unit'] = get_object_or_404(FleetUnit, pk=fleet_id)
|
|
return initial
|
|
|
|
def get_success_url(self):
|
|
return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk}) |