Corp/backend/procurement/views_frontend.py
Игорь Надеин 272b02377f feat: добавление позиций при создании заявки и исправление списка отделов
- Добавлен список departments в контекст request_list view
- Реализовано динамическое добавление позиций (items) прямо в модале создания заявки
- Кнопка 'Добавить позицию' для добавления товаров/услуг
- Каждая позиция содержит: название, описание, количество, ед.изм., цену
- Позиции можно удалять кнопкой X
- JavaScript собирает все позиции и отправляет в API вместе с заявкой
- Поддержка как создания с позициями, так и без них
- Реализовано на обеих страницах: dashboard и request_list
- Все 72 теста проходят успешно
2025-12-09 13:21:14 +03:00

282 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
HTML Views для модуля закупок (Frontend).
"""
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404, render, redirect
from django.utils import timezone
from employees.models import Department
from .models import (
Budget,
Equipment,
EquipmentCategory,
ProcurementRequest,
)
from .constants import ProcurementStatus
User = get_user_model()
@login_required
def dashboard(request):
"""Главная страница модуля закупок."""
user = request.user
now = timezone.now()
quarter = (now.month - 1) // 3 + 1
# Получаем отделы пользователя
user_departments = user.departments.filter(
employeedepartment__is_active=True
)
# Статистика
stats = {
'pending': ProcurementRequest.objects.filter(
status=ProcurementStatus.PENDING
).count(),
'approved': ProcurementRequest.objects.filter(
status=ProcurementStatus.APPROVED
).count(),
'equipment': Equipment.objects.count(),
}
# Бюджет отдела (берём первый отдел пользователя)
budget = None
if user_departments.exists():
budget = Budget.objects.filter(
department=user_departments.first(),
year=now.year,
quarter=quarter
).first()
# Последние заявки (мои или моего отдела)
recent_requests = ProcurementRequest.objects.filter(
department__in=user_departments
).select_related(
'department', 'requestor'
).order_by('-created_at')[:5]
# Права на согласование
can_approve = user.is_staff or user.headed_departments.exists()
context = {
'stats': stats,
'budget': budget,
'recent_requests': recent_requests,
'can_approve': can_approve,
'departments': Department.objects.all().order_by('name'),
}
return render(request, 'procurement/dashboard.html', context)
@login_required
def request_list(request):
"""Список заявок на закупку."""
user = request.user
scope = request.GET.get('scope', 'mine')
# Базовый queryset
queryset = ProcurementRequest.objects.select_related(
'department', 'requestor'
).prefetch_related('items')
# Фильтрация по области видимости
if scope == 'mine':
queryset = queryset.filter(requestor=user)
page_title = 'Мои заявки'
elif scope == 'department':
user_departments = user.departments.filter(
employeedepartment__is_active=True
)
queryset = queryset.filter(department__in=user_departments)
page_title = 'Заявки отдела'
elif scope == 'all' and user.is_staff:
page_title = 'Все заявки'
else:
queryset = queryset.filter(requestor=user)
page_title = 'Мои заявки'
scope = 'mine'
# Фильтры
filters = {}
if status := request.GET.get('status'):
queryset = queryset.filter(status=status)
filters['status'] = status
if urgency := request.GET.get('urgency'):
queryset = queryset.filter(urgency=urgency)
filters['urgency'] = urgency
# Сортировка
queryset = queryset.order_by('-created_at')
# Пагинация
paginator = Paginator(queryset, 20)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'requests': page_obj,
'page_obj': page_obj,
'page_title': page_title,
'scope': scope,
'filters': filters,
'can_view_all': user.is_staff or user.headed_departments.exists(),
'departments': Department.objects.all().order_by('name'),
}
return render(request, 'procurement/request_list.html', context)
@login_required
def my_requests(request):
"""Мои заявки."""
return redirect('procurement:request_list', permanent=False)
@login_required
def pending_approvals(request):
"""Заявки на согласовании (для руководителей)."""
user = request.user
# Получаем отделы, которыми руководит пользователь
headed_depts = user.headed_departments.all()
if not headed_depts.exists() and not user.is_staff:
return redirect('procurement:dashboard')
queryset = ProcurementRequest.objects.filter(
status=ProcurementStatus.PENDING
).select_related(
'department', 'requestor'
)
# Для не-staff показываем только заявки своих отделов
if not user.is_staff:
queryset = queryset.filter(department__in=headed_depts)
queryset = queryset.order_by('-created_at')
paginator = Paginator(queryset, 20)
page_obj = paginator.get_page(request.GET.get('page'))
context = {
'requests': page_obj,
'page_obj': page_obj,
'page_title': 'На согласовании',
'scope': 'pending',
'can_view_all': user.is_staff,
}
return render(request, 'procurement/request_list.html', context)
@login_required
def request_detail(request, pk):
"""Детальная страница заявки."""
procurement_request = get_object_or_404(
ProcurementRequest.objects.select_related(
'department', 'requestor'
).prefetch_related(
'items__supplier',
'approvals__approver'
),
pk=pk
)
context = {
'request_obj': procurement_request,
'can_edit': (
procurement_request.requestor == request.user and
procurement_request.status == ProcurementStatus.DRAFT
),
'can_approve': (
request.user.is_staff or
request.user.headed_departments.filter(
pk=procurement_request.department_id
).exists()
),
}
return render(request, 'procurement/request_detail.html', context)
@login_required
def request_create(request):
"""Страница/обработка создания заявки."""
if request.method == 'POST':
# Обработка формы - для простоты перенаправляем на API
# В реальности можно обработать здесь или использовать Django Forms
pass
context = {}
return render(request, 'procurement/request_form.html', context)
@login_required
def equipment_list(request):
"""Список оборудования."""
queryset = Equipment.objects.select_related(
'category', 'department', 'responsible_person'
)
# Фильтры
filters = {}
if category := request.GET.get('category'):
queryset = queryset.filter(category_id=category)
filters['category'] = category
if status := request.GET.get('status'):
queryset = queryset.filter(status=status)
filters['status'] = status
if department := request.GET.get('department'):
queryset = queryset.filter(department_id=department)
filters['department'] = department
# Поиск
if search := request.GET.get('q'):
queryset = queryset.filter(
name__icontains=search
) | queryset.filter(
inventory_number__icontains=search
)
queryset = queryset.order_by('-purchase_date')
# Пагинация
paginator = Paginator(queryset, 24)
page_obj = paginator.get_page(request.GET.get('page'))
context = {
'equipment_list': page_obj,
'page_obj': page_obj,
'filters': filters,
'categories': EquipmentCategory.objects.all(),
'departments': Department.objects.all(),
'users': User.objects.filter(
is_active=True
).order_by('last_name', 'first_name'),
'can_manage': request.user.is_staff,
}
return render(request, 'procurement/equipment_list.html', context)
@login_required
def equipment_detail(request, pk):
"""Детальная страница оборудования."""
equipment = get_object_or_404(
Equipment.objects.select_related(
'category', 'department', 'responsible_person'
).prefetch_related(
'maintenance_records', 'transfer_logs'
),
pk=pk
)
context = {
'equipment': equipment,
'can_manage': (
request.user.is_staff or
equipment.responsible_person == request.user
),
}
return render(request, 'procurement/equipment_detail.html', context)