удалил лишнего из заявок и оборудования

This commit is contained in:
lizerk 2026-03-10 12:38:39 +03:00
parent 46e7059a40
commit a784212ea1
5 changed files with 0 additions and 493 deletions

View File

@ -34,10 +34,6 @@ urlpatterns = [
path("notifications/", include("notifications.urls", namespace="notifications")),
path("search/", include("search.urls", namespace="search")),
path("finance/", include("finance.urls", namespace="finance")),
path(
"procurement/",
include("procurement.urls_frontend", namespace="procurement_frontend")
),
path("api/", include(("api.urls", "api"), namespace="api")),
# Service Worker должен быть в корне для правильного scope
path("sw.js", serve_service_worker, name="sw"),

View File

@ -1,163 +0,0 @@
"""
Django Forms для модуля закупок.
"""
from datetime import date
from django import forms
from .models import ProcurementRequest, ProcurementItem, Budget
class ProcurementRequestForm(forms.ModelForm):
"""Форма создания/редактирования заявки на закупку."""
class Meta:
model = ProcurementRequest
fields = ['title', 'description', 'department', 'urgency']
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Название заявки',
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Описание и обоснование закупки',
}),
'department': forms.Select(attrs={
'class': 'form-select',
}),
'urgency': forms.Select(attrs={
'class': 'form-select',
}),
}
def __init__(self, *args, user=None, **kwargs):
"""Инициализация формы с фильтрацией отделов пользователя."""
super().__init__(*args, **kwargs)
if user:
# Ограничиваем выбор отделов теми, в которых состоит юзер
user_departments = user.departments.filter(
employeedepartment__is_active=True
)
self.fields['department'].queryset = user_departments
# Если у пользователя один отдел - выбираем его по умолчанию
if user_departments.count() == 1:
self.fields['department'].initial = user_departments.first()
class ProcurementItemForm(forms.ModelForm):
"""Форма добавления позиции в заявку."""
class Meta:
model = ProcurementItem
fields = [
'name', 'description', 'quantity', 'unit',
'estimated_unit_price', 'supplier_info'
]
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Наименование',
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Описание позиции',
}),
'quantity': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
}),
'unit': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'шт.',
}),
'estimated_unit_price': forms.NumberInput(attrs={
'class': 'form-control',
'min': 0.01,
'step': '0.01',
}),
'supplier_info': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Ссылки на товар, контакты поставщика',
}),
}
class BudgetForm(forms.ModelForm):
"""Форма создания/редактирования бюджета отдела."""
QUARTER_CHOICES = [
(1, '1 квартал'),
(2, '2 квартал'),
(3, '3 квартал'),
(4, '4 квартал'),
]
class Meta:
model = Budget
fields = ['department', 'year', 'quarter', 'allocated_amount']
widgets = {
'department': forms.Select(attrs={
'class': 'form-select',
}),
'year': forms.NumberInput(attrs={
'class': 'form-control',
'min': 2020,
'max': 2100,
}),
'quarter': forms.Select(attrs={
'class': 'form-select',
}),
'allocated_amount': forms.NumberInput(attrs={
'class': 'form-control',
'min': 0,
'step': '0.01',
'placeholder': '0.00',
}),
}
labels = {
'department': 'Отдел',
'year': 'Год',
'quarter': 'Квартал',
'allocated_amount': 'Выделенная сумма (₽)',
}
def __init__(self, *args, **kwargs):
"""Инициализация формы с текущим годом/кварталом."""
super().__init__(*args, **kwargs)
# Устанавливаем текущий год и квартал по умолчанию для новых бюджетов
if not self.instance.pk:
today = date.today()
self.fields['year'].initial = today.year
self.fields['quarter'].initial = (today.month - 1) // 3 + 1
def clean(self):
"""Проверка уникальности бюджета."""
cleaned_data = super().clean()
department = cleaned_data.get('department')
year = cleaned_data.get('year')
quarter = cleaned_data.get('quarter')
if department and year and quarter:
# Проверяем, нет ли уже такого бюджета
existing = Budget.objects.filter(
department=department,
year=year,
quarter=quarter
)
# Исключаем текущий объект при редактировании
if self.instance.pk:
existing = existing.exclude(pk=self.instance.pk)
if existing.exists():
raise forms.ValidationError(
f'Бюджет для отдела "{department}" на {year} год, '
f'{quarter} квартал уже существует.'
)
return cleaned_data

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,39 +0,0 @@
"""
URL конфигурация для HTML страниц модуля закупок (Frontend).
"""
from django.urls import path
from . import views_frontend as views
app_name = 'procurement_frontend'
urlpatterns = [
# Дашборд
path('', views.dashboard, name='dashboard'),
# Заявки на закупку (orders - чтобы не путать с заявлениями requests)
path('orders/', views.request_list, name='request_list'),
path('orders/my/', views.my_requests, name='my_requests'),
path(
'orders/pending/',
views.pending_approvals,
name='pending_approvals'
),
path('orders/create/', views.request_create, name='request_create'),
path('orders/<int:pk>/', views.request_detail, name='request_detail'),
path('orders/<int:pk>/edit/', views.request_edit, name='request_edit'),
# Оборудование
path('equipment/', views.equipment_list, name='equipment_list'),
path(
'equipment/<int:pk>/',
views.equipment_detail,
name='equipment_detail'
),
# Бюджеты
path('budgets/', views.budget_list, name='budget_list'),
path('budgets/create/', views.budget_create, name='budget_create'),
path('budgets/<int:pk>/edit/', views.budget_edit, name='budget_edit'),
]

View File

@ -1,284 +0,0 @@
"""
HTML Views для модуля закупок (Frontend).
Все данные загружаются через API на клиенте.
Views только рендерят layout и передают минимальный контекст.
"""
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required
def dashboard(request):
"""Главная страница модуля закупок.
Данные загружаются через API:
- /api/procurement/stats/overview/ — статистика
- /api/procurement/budgets/my-department/ — бюджет отдела
- /api/procurement/requests/?ordering=-created_at&page_size=5 — последние заявки
"""
return render(request, 'procurement/dashboard.html', {})
@login_required
def request_list(request):
"""Список заявок на закупку.
Данные загружаются через API:
- /api/procurement/requests/ — с фильтрами
- /api/v1/departments/ — для фильтра по отделам
"""
scope = request.GET.get('scope', 'mine')
user = request.user
# Читаем фильтры из GET параметров
filters = {
'status': request.GET.get('status', ''),
'urgency': request.GET.get('urgency', ''),
'period': request.GET.get('period', ''),
}
# Определяем доступные представления
can_view_department = user.departments.exists()
can_view_all = (
user.is_staff or user.is_superuser or
user.has_perm('procurement.view_procurementrequest') or
user.has_perm('procurement.change_procurementrequest')
)
return render(request, 'procurement/request_list.html', {
'scope': scope,
'show_tabs': True,
'can_view_department': can_view_department,
'can_view_all': can_view_all,
'filters': filters,
})
@login_required
def my_requests(request):
"""Мои заявки — редирект на request_list."""
from django.shortcuts import redirect
return redirect('procurement_frontend:request_list')
@login_required
def pending_approvals(request):
"""Заявки на согласовании.
Данные загружаются через API:
- /api/procurement/requests/pending_approvals/
"""
user = request.user
# Определяем доступные представления
can_view_department = user.departments.exists()
can_view_all = (
user.is_staff or user.is_superuser or
user.has_perm('procurement.view_procurementrequest') or
user.has_perm('procurement.change_procurementrequest')
)
return render(request, 'procurement/request_list.html', {
'scope': 'pending',
'page_title': 'На согласовании',
'show_tabs': True,
'can_view_department': can_view_department,
'can_view_all': can_view_all,
})
@login_required
@login_required
def request_detail(request, pk):
"""Детальная страница заявки.
Загружает данные из БД и передаёт в шаблон.
Права на редактирование (can_edit):
- Админы/Staff → любая заявка в DRAFT
- Модельные права (change_procurementrequest) → любая заявка в DRAFT
- Автор заявки → своя заявка в DRAFT
- Начальник отдела → заявки своего отдела в DRAFT
Права на согласование (can_approve):
- Пользователи с активным approval для этой заявки
"""
from django.shortcuts import get_object_or_404
from procurement.models import ProcurementRequest
request_obj = get_object_or_404(
ProcurementRequest.objects.select_related(
'department', 'requestor', 'executor'
).prefetch_related('items', 'approvals__approver'),
pk=pk
)
user = request.user
# Проверяем права на редактирование (синхронизировано с API)
can_edit = False
if request_obj.is_editable: # Только DRAFT
# Админы могут редактировать
if user.is_superuser or user.is_staff:
can_edit = True
# Модельные права
elif user.has_perm('procurement.change_procurementrequest'):
can_edit = True
# Автор заявки
elif request_obj.requestor == user:
can_edit = True
# Начальник отдела
elif request_obj.department.head == user:
can_edit = True
# Проверяем права на согласование
can_approve = (
request_obj.approvals.filter(
approver=user,
status='pending'
).exists()
)
return render(request, 'procurement/request_detail.html', {
'request_id': pk,
'request_obj': request_obj,
'can_edit': can_edit,
'can_approve': can_approve,
})
@login_required
def request_create(request):
"""Страница создания заявки.
Данные загружаются через API:
- /api/v1/departments/ — список отделов
Создание через:
- POST /api/procurement/requests/
"""
return render(request, 'procurement/request_form.html', {
'page_title': 'Новая заявка',
'request_id': None,
})
@login_required
def request_edit(request, pk):
"""Страница редактирования заявки.
Данные загружаются через API:
- /api/v1/procurement/requests/{pk}/ — данные заявки
- /api/v1/departments/ — список отделов
Обновление через:
- PATCH /api/v1/procurement/requests/{pk}/
"""
return render(request, 'procurement/request_form.html', {
'page_title': 'Редактирование заявки',
'request_id': pk,
})
@login_required
def equipment_list(request):
"""Список оборудования.
Данные загружаются через API:
- /api/procurement/equipment/ — список с фильтрами
- /api/procurement/equipment-categories/ — категории для фильтра
- /api/v1/departments/ — отделы для фильтра
- /api/procurement/equipment/create-options/ — при создании
"""
return render(request, 'procurement/equipment_list.html', {})
@login_required
def equipment_detail(request, pk):
"""Детальная страница оборудования.
Загружает данные из БД и передаёт в шаблон.
Права на управление (can_manage):
- Админы/Staff → любое оборудование
- Начальник отдела → оборудование своего отдела
- Ответственный за оборудование → своё оборудование
"""
from django.shortcuts import get_object_or_404
from procurement.models import Equipment
equipment = get_object_or_404(
Equipment.objects.select_related(
'department',
'department__head',
'category',
'responsible_person',
'procurement_item',
'procurement_item__request'
).prefetch_related(
'maintenance_history',
'transfer_logs__from_department',
'transfer_logs__to_department',
'transfer_logs__from_person',
'transfer_logs__to_person'
),
pk=pk
)
user = request.user
# Проверяем права на управление
can_manage = False
if user.is_superuser or user.is_staff:
can_manage = True
elif equipment.department and equipment.department.head == user:
can_manage = True
elif equipment.responsible_person == user:
can_manage = True
return render(request, 'procurement/equipment_detail.html', {
'equipment': equipment,
'can_manage': can_manage,
})
# =============================================================================
# БЮДЖЕТЫ
# =============================================================================
@login_required
def budget_list(request):
"""Список бюджетов.
Данные загружаются через API:
- /api/procurement/budgets/ — список с фильтрами
- /api/v1/departments/ — отделы для фильтра
"""
return render(request, 'procurement/budget_list.html', {})
@login_required
def budget_create(request):
"""Создание бюджета.
Данные загружаются через API:
- /api/v1/departments/ — список отделов
Создание через:
- POST /api/procurement/budgets/
"""
return render(request, 'procurement/budget_form.html', {
'budget_id': None,
})
@login_required
def budget_edit(request, pk):
"""Редактирование бюджета.
Данные загружаются через API:
- /api/procurement/budgets/{pk}/ — текущие данные
- /api/v1/departments/ — список отделов
Обновление через:
- PATCH /api/procurement/budgets/{pk}/
"""
return render(request, 'procurement/budget_form.html', {
'budget_id': pk,
})