This commit is contained in:
Flatlogic Bot 2026-01-27 20:34:31 +00:00
parent d58a583044
commit 6ed717407d
9 changed files with 129 additions and 21 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View File

@ -43,10 +43,18 @@
{% endif %} {% endif %}
</td> </td>
<td class="text-end px-4"> <td class="text-end px-4">
<a href="{{ b.fleet_unit.get_absolute_url }}" class="btn btn-sm btn-outline-primary rounded-pill px-3">К технике</a> <div class="btn-group">
{% if b.status == 'need_part' %} <a href="{{ b.fleet_unit.get_absolute_url }}" class="btn btn-sm btn-outline-primary rounded-pill px-3 me-2">К технике</a>
<a href="{% url 'part_request_add' %}?breakdown={{ b.pk }}" class="btn btn-sm btn-outline-warning rounded-pill px-3">Заказать запчасть</a> {% if b.status == 'need_part' %}
{% endif %} <a href="{% url 'part_request_add' %}?breakdown={{ b.pk }}" class="btn btn-sm btn-outline-warning rounded-pill px-3 me-2">Заказать запчасть</a>
{% endif %}
{% if user.is_staff %}
<form action="{% url 'breakdown_delete' b.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить запись о поломке?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-outline-danger rounded-pill px-2" title="Удалить"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</div>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
@ -59,4 +67,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -51,6 +51,12 @@
<div class="d-grid gap-2"> <div class="d-grid gap-2">
<a href="{% url 'fleet_edit' unit.pk %}" class="btn btn-outline-primary rounded-pill">Редактировать</a> <a href="{% url 'fleet_edit' unit.pk %}" class="btn btn-outline-primary rounded-pill">Редактировать</a>
{% if user.is_staff %}
<form action="{% url 'fleet_delete' unit.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Вы уверены, что хотите удалить эту единицу техники? Все связанные данные будут удалены.')">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger rounded-pill w-100 mb-2">Удалить технику</button>
</form>
{% endif %}
<a href="{% url 'breakdown_add' %}?fleet_unit={{ unit.pk }}" class="btn btn-danger rounded-pill">Заявить о поломке</a> <a href="{% url 'breakdown_add' %}?fleet_unit={{ unit.pk }}" class="btn btn-danger rounded-pill">Заявить о поломке</a>
</div> </div>
</div> </div>
@ -114,7 +120,15 @@
<td><span class="badge bg-{{ m.status|yesno:'success,warning,secondary' }} rounded-pill small">{{ m.get_status_display }}</span></td> <td><span class="badge bg-{{ m.status|yesno:'success,warning,secondary' }} rounded-pill small">{{ m.get_status_display }}</span></td>
<td>{{ m.mechanic|default:"-" }}</td> <td>{{ m.mechanic|default:"-" }}</td>
<td class="text-end"> <td class="text-end">
<a href="{% url 'maintenance_detail' m.pk %}" class="btn btn-sm btn-link text-primary">Открыть</a> <div class="btn-group">
<a href="{% url 'maintenance_detail' m.pk %}" class="btn btn-sm btn-link text-primary p-0 me-2">Открыть</a>
{% if user.is_staff %}
<form action="{% url 'maintenance_delete' m.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить запись ТО?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</div>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
@ -136,7 +150,15 @@
<div class="list-group-item px-0 py-3"> <div class="list-group-item px-0 py-3">
<div class="d-flex justify-content-between mb-1"> <div class="d-flex justify-content-between mb-1">
<h6 class="mb-0 fw-bold text-danger">{{ b.system_node }}</h6> <h6 class="mb-0 fw-bold text-danger">{{ b.system_node }}</h6>
<span class="badge bg-light text-dark border small">{{ b.get_status_display }}</span> <div>
<span class="badge bg-light text-dark border small me-2">{{ b.get_status_display }}</span>
{% if user.is_staff %}
<form action="{% url 'breakdown_delete' b.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить запись о поломке?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</div>
</div> </div>
<p class="small mb-1 text-muted">{{ b.date|date:"d.m.Y" }} — {{ b.description }}</p> <p class="small mb-1 text-muted">{{ b.date|date:"d.m.Y" }} — {{ b.description }}</p>
<div class="d-flex align-items-center mt-2"> <div class="d-flex align-items-center mt-2">
@ -164,6 +186,7 @@
<th>Кол-во</th> <th>Кол-во</th>
<th>Статус</th> <th>Статус</th>
<th>Дата</th> <th>Дата</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -173,9 +196,17 @@
<td>{{ r.quantity }}</td> <td>{{ r.quantity }}</td>
<td><span class="badge bg-light text-dark border rounded-pill small">{{ r.get_status_display }}</span></td> <td><span class="badge bg-light text-dark border rounded-pill small">{{ r.get_status_display }}</span></td>
<td class="small">{{ r.created_at|date:"d.m.Y" }}</td> <td class="small">{{ r.created_at|date:"d.m.Y" }}</td>
<td class="text-end">
{% if user.is_staff %}
<form action="{% url 'part_request_delete' r.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить заявку?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</td>
</tr> </tr>
{% empty %} {% empty %}
<tr><td colspan="4" class="text-center py-4 text-muted">Нет заявок</td></tr> <tr><td colspan="5" class="text-center py-4 text-muted">Нет заявок</td></tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
@ -223,6 +254,7 @@
<th>Деталь</th> <th>Деталь</th>
<th>Статус</th> <th>Статус</th>
<th>Дата</th> <th>Дата</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -231,9 +263,17 @@
<td>{{ r.part_name }}</td> <td>{{ r.part_name }}</td>
<td><span class="badge bg-light text-dark border rounded-pill small">{{ r.get_status_display }}</span></td> <td><span class="badge bg-light text-dark border rounded-pill small">{{ r.get_status_display }}</span></td>
<td class="small">{{ r.created_at|date:"d.m.Y" }}</td> <td class="small">{{ r.created_at|date:"d.m.Y" }}</td>
<td class="text-end">
{% if user.is_staff %}
<form action="{% url 'part_request_delete' r.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить заявку?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</td>
</tr> </tr>
{% empty %} {% empty %}
<tr><td colspan="3" class="text-center py-4 text-muted">Нет заявок</td></tr> <tr><td colspan="4" class="text-center py-4 text-muted">Нет заявок</td></tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
@ -252,4 +292,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -41,7 +41,15 @@
</td> </td>
<td>{{ m.mechanic|default:"-" }}</td> <td>{{ m.mechanic|default:"-" }}</td>
<td class="text-end px-4"> <td class="text-end px-4">
<a href="{% url 'maintenance_detail' m.pk %}" class="btn btn-sm btn-outline-primary rounded-pill px-3">Открыть</a> <div class="btn-group">
<a href="{% url 'maintenance_detail' m.pk %}" class="btn btn-sm btn-outline-primary rounded-pill px-3 me-2">Открыть</a>
{% if user.is_staff %}
<form action="{% url 'maintenance_delete' m.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить запись ТО?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-outline-danger rounded-pill px-2" title="Удалить"><i class="bi bi-trash"></i></button>
</form>
{% endif %}
</div>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
@ -54,4 +62,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -43,7 +43,17 @@
</span> </span>
</td> </td>
<td> <td>
<a href="#" class="btn btn-sm btn-outline-primary">Управление</a> <div class="btn-group">
<a href="#" class="btn btn-sm btn-outline-primary">Управление</a>
{% if user.is_staff %}
<form action="{% url 'part_request_delete' req.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить заявку?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-outline-danger ms-1" title="Удалить">
<i class="bi bi-trash"></i>
</button>
</form>
{% endif %}
</div>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
@ -63,7 +73,7 @@
<div class="card shadow-sm"> <div class="card shadow-sm">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center"> <div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Поставщики</h5> <h5 class="card-title mb-0">Поставщики</h5>
<a href="{% url 'supplier_add' %}" class="btn btn-sm btn-primary"> <a href="{% url 'supplier_create' %}" class="btn btn-sm btn-primary">
<i class="bi bi-plus-lg"></i> Добавить <i class="bi bi-plus-lg"></i> Добавить
</a> </a>
</div> </div>
@ -73,9 +83,19 @@
<div class="list-group-item px-0 py-3"> <div class="list-group-item px-0 py-3">
<div class="d-flex w-100 justify-content-between align-items-center"> <div class="d-flex w-100 justify-content-between align-items-center">
<h6 class="mb-1">{{ supplier.name }}</h6> <h6 class="mb-1">{{ supplier.name }}</h6>
<a href="{% url 'supplier_edit' supplier.pk %}" class="btn btn-sm btn-link p-0 text-muted"> <div class="btn-group">
<i class="bi bi-pencil-square"></i> <a href="{% url 'supplier_edit' supplier.pk %}" class="btn btn-sm btn-link p-0 text-muted me-2" title="Редактировать">
</a> <i class="bi bi-pencil-square"></i>
</a>
{% if user.is_staff %}
<form action="{% url 'supplier_delete' supplier.pk %}" method="POST" class="d-inline" onsubmit="return confirm('Удалить поставщика?')">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-link p-0 text-danger" title="Удалить">
<i class="bi bi-trash"></i>
</button>
</form>
{% endif %}
</div>
</div> </div>
<p class="mb-1 small text-muted"> <p class="mb-1 small text-muted">
<strong>Менеджер:</strong> {{ supplier.representative_name|default:"-" }}<br> <strong>Менеджер:</strong> {{ supplier.representative_name|default:"-" }}<br>

View File

@ -9,12 +9,14 @@ urlpatterns = [
path('fleet/<int:pk>/', views.FleetDetailView.as_view(), name='fleet_detail'), path('fleet/<int:pk>/', views.FleetDetailView.as_view(), name='fleet_detail'),
path('fleet/add/', views.FleetCreateView.as_view(), name='fleet_add'), path('fleet/add/', views.FleetCreateView.as_view(), name='fleet_add'),
path('fleet/<int:pk>/edit/', views.FleetUpdateView.as_view(), name='fleet_edit'), path('fleet/<int:pk>/edit/', views.FleetUpdateView.as_view(), name='fleet_edit'),
path('fleet/<int:pk>/delete/', views.FleetUnitDeleteView.as_view(), name='fleet_delete'),
# Maintenance # Maintenance
path('maintenance/', views.MaintenanceListView.as_view(), name='maintenance_list'), path('maintenance/', views.MaintenanceListView.as_view(), name='maintenance_list'),
path('maintenance/<int:pk>/', views.MaintenanceDetailView.as_view(), name='maintenance_detail'), path('maintenance/<int:pk>/', views.MaintenanceDetailView.as_view(), name='maintenance_detail'),
path('maintenance/add/', views.MaintenanceCreateView.as_view(), name='maintenance_add'), path('maintenance/add/', views.MaintenanceCreateView.as_view(), name='maintenance_add'),
path('maintenance/<int:pk>/edit/', views.MaintenanceUpdateView.as_view(), name='maintenance_edit'), path('maintenance/<int:pk>/edit/', views.MaintenanceUpdateView.as_view(), name='maintenance_edit'),
path('maintenance/<int:pk>/delete/', views.MaintenanceDeleteView.as_view(), name='maintenance_delete'),
path('maintenance/<int:pk>/process/', views.MaintenanceProcessView.as_view(), name='maintenance_process'), path('maintenance/<int:pk>/process/', views.MaintenanceProcessView.as_view(), name='maintenance_process'),
path('maintenance/<int:pk>/complete/', views.MaintenanceCompleteView.as_view(), name='maintenance_complete'), path('maintenance/<int:pk>/complete/', views.MaintenanceCompleteView.as_view(), name='maintenance_complete'),
path('maintenance/<int:pk>/pdf/', views.MaintenancePDFView.as_view(), name='maintenance_pdf'), path('maintenance/<int:pk>/pdf/', views.MaintenancePDFView.as_view(), name='maintenance_pdf'),
@ -22,13 +24,16 @@ urlpatterns = [
# Breakdown # Breakdown
path('breakdown/', views.BreakdownListView.as_view(), name='breakdown_list'), path('breakdown/', views.BreakdownListView.as_view(), name='breakdown_list'),
path('breakdown/add/', views.BreakdownCreateView.as_view(), name='breakdown_add'), path('breakdown/add/', views.BreakdownCreateView.as_view(), name='breakdown_add'),
path('breakdown/<int:pk>/delete/', views.BreakdownDeleteView.as_view(), name='breakdown_delete'),
# Part Request # Part Request
path('part-request/', views.PartRequestListView.as_view(), name='part_request_list'), path('part-request/', views.PartRequestListView.as_view(), name='part_request_list'),
path('part-request/add/', views.PartRequestCreateView.as_view(), name='part_request_add'), path('part-request/add/', views.PartRequestCreateView.as_view(), name='part_request_add'),
path('part-request/<int:pk>/delete/', views.PartRequestDeleteView.as_view(), name='part_request_delete'),
# Supply # Supply
path('supply/', views.SupplyListView.as_view(), name='supply_list'), path('supply/', views.SupplyListView.as_view(), name='supply_list'),
path('supplier/add/', views.SupplierCreateView.as_view(), name='supplier_add'), path('supplier/add/', views.SupplierCreateView.as_view(), name='supplier_create'),
path('supplier/<int:pk>/edit/', views.SupplierUpdateView.as_view(), name='supplier_edit'), path('supplier/<int:pk>/edit/', views.SupplierUpdateView.as_view(), name='supplier_edit'),
path('supplier/<int:pk>/delete/', views.SupplierDeleteView.as_view(), name='supplier_delete'),
] ]

View File

@ -1,11 +1,11 @@
import json import json
from io import BytesIO from io import BytesIO
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import ListView, DetailView, CreateView, UpdateView, TemplateView, View from django.views.generic import ListView, DetailView, CreateView, UpdateView, TemplateView, View, DeleteView
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django import forms from django import forms
from django.db.models import Count, Q from django.db.models import Count, Q
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import HttpResponse from django.http import HttpResponse
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
@ -13,6 +13,11 @@ from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfbase.ttfonts import TTFont
from .models import FleetUnit, Maintenance, Breakdown, PartRequest, Category, Document, Supplier from .models import FleetUnit, Maintenance, Breakdown, PartRequest, Category, Document, Supplier
# Mixins
class StaffRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_staff
# Forms # Forms
class FleetUnitForm(forms.ModelForm): class FleetUnitForm(forms.ModelForm):
class Meta: class Meta:
@ -159,6 +164,10 @@ class FleetUpdateView(UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse_lazy('fleet_detail', kwargs={'pk': self.object.pk}) return reverse_lazy('fleet_detail', kwargs={'pk': self.object.pk})
class FleetUnitDeleteView(LoginRequiredMixin, StaffRequiredMixin, DeleteView):
model = FleetUnit
success_url = reverse_lazy('fleet_list')
# Maintenance Views # Maintenance Views
class MaintenanceListView(ListView): class MaintenanceListView(ListView):
model = Maintenance model = Maintenance
@ -205,6 +214,10 @@ class MaintenanceUpdateView(UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse('maintenance_detail', kwargs={'pk': self.object.pk}) return reverse('maintenance_detail', kwargs={'pk': self.object.pk})
class MaintenanceDeleteView(LoginRequiredMixin, StaffRequiredMixin, DeleteView):
model = Maintenance
success_url = reverse_lazy('maintenance_list')
class MaintenanceProcessView(View): class MaintenanceProcessView(View):
def post(self, request, pk): def post(self, request, pk):
maintenance = get_object_or_404(Maintenance, pk=pk) maintenance = get_object_or_404(Maintenance, pk=pk)
@ -299,6 +312,11 @@ class BreakdownCreateView(CreateView):
def get_success_url(self): def get_success_url(self):
return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk}) return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk})
class BreakdownDeleteView(LoginRequiredMixin, StaffRequiredMixin, DeleteView):
model = Breakdown
def get_success_url(self):
return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk})
# Part Request Views # Part Request Views
class PartRequestListView(ListView): class PartRequestListView(ListView):
model = PartRequest model = PartRequest
@ -325,6 +343,11 @@ class PartRequestCreateView(CreateView):
def get_success_url(self): def get_success_url(self):
return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk}) return reverse('fleet_detail', kwargs={'pk': self.object.fleet_unit.pk})
class PartRequestDeleteView(LoginRequiredMixin, StaffRequiredMixin, DeleteView):
model = PartRequest
def get_success_url(self):
return reverse('supply_list')
class SupplyListView(ListView): class SupplyListView(ListView):
model = PartRequest model = PartRequest
template_name = 'core/supply_list.html' template_name = 'core/supply_list.html'
@ -345,4 +368,8 @@ class SupplierUpdateView(UpdateView):
model = Supplier model = Supplier
form_class = SupplierForm form_class = SupplierForm
template_name = 'core/supplier_form.html' template_name = 'core/supplier_form.html'
success_url = reverse_lazy('supply_list') success_url = reverse_lazy('supply_list')
class SupplierDeleteView(LoginRequiredMixin, StaffRequiredMixin, DeleteView):
model = Supplier
success_url = reverse_lazy('supply_list')