Files
octopus/myproject/user_roles/views.py
Andrey Smakotin d44ae0b598 Добавлен расчёт и отображение доступного количества комплектов
- Добавлен метод calculate_available_quantity() в модель ProductKit для точного расчёта максимального количества комплектов на основе свободных остатков компонентов
- Обновлён метод check_availability() для использования нового расчёта (обратная совместимость)
- Удалён устаревший сервис kit_availability.py

Исправлено отображение остатков комплектов:
- products_list.html: вместо прочерка показывается количество комплектов
- catalog.html: добавлено отображение доступного количества комплектов с цветовой индикацией
- POS terminal.js: в карточке товара показывается конкретное количество вместо общего 'В наличии'

Обновлены представления:
- ProductsListView: аннотирует комплекты атрибутом total_free
- CatalogView: рассчитывает доступное количество для каждого комплекта
- POS get_products(): убран хардкод, используется реальный расчёт по складу
2026-01-06 01:02:28 +03:00

235 lines
9.4 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.
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.contrib.auth import get_user_model
from user_roles.models import Role, UserRole
from user_roles.services import RoleService
from user_roles.decorators import owner_required
User = get_user_model()
@login_required
@owner_required
def user_role_list(request):
"""Список пользователей с их ролями"""
# Фильтр по активности
show_inactive = request.GET.get('show_inactive', 'false') == 'true'
if show_inactive:
user_roles = UserRole.objects.select_related('user', 'role', 'created_by').all()
else:
# По умолчанию показываем только активных
user_roles = UserRole.objects.select_related('user', 'role', 'created_by').filter(
is_active=True,
user__is_active=True
)
roles = Role.objects.all()
context = {
'user_roles': user_roles,
'roles': roles,
'show_inactive': show_inactive,
}
return render(request, 'user_roles/user_role_list.html', context)
@login_required
@owner_required
def user_role_create(request):
"""Создание нового пользователя с ролью"""
if request.method == 'POST':
email = request.POST.get('email')
name = request.POST.get('name')
role_code = request.POST.get('role')
password = request.POST.get('password', '').strip()
# Если пароль не указан, генерируем случайный
if not password:
password = User.objects.make_random_password(12)
try:
# Проверяем, не существует ли уже пользователь с таким email
existing_user = User.objects.filter(email=email).first()
if existing_user:
# Пользователь существует - проверяем его статус
if not existing_user.is_active:
# Пользователь деактивирован - предлагаем реактивировать
messages.warning(
request,
f'Пользователь {email} уже существует, но деактивирован. '
f'Используйте функцию "Реактивировать" в списке пользователей.'
)
else:
# Пользователь активен - возможно, уже имеет роль в другом тенанте
messages.error(
request,
f'Пользователь с email {email} уже существует в системе.'
)
# ВАЖНО: Возвращаемся к форме, чтобы пользователь мог исправить email
roles = Role.objects.all()
context = {'roles': roles}
return render(request, 'user_roles/user_role_create.html', context)
else:
# Создаем нового пользователя
user = User.objects.create_user(
email=email,
name=name,
password=password,
is_email_confirmed=True,
is_staff=False, # SECURITY: Пользователи ролей НЕ имеют доступа к админке
is_superuser=False # SECURITY: Пользователи ролей НЕ суперпользователи
)
# Назначаем роль
RoleService.assign_role_to_user(user, role_code, created_by=request.user)
# DEBUG: Проверяем, что пароль есть
print(f"DEBUG: created_user_email={email}, generated_password={password}")
# Показываем страницу с паролем
context = {
'created_user_email': email,
'generated_password': password,
'user_role': role_code,
}
return render(request, 'user_roles/user_role_created.html', context)
except Exception as e:
messages.error(request, f'Ошибка при создании пользователя: {str(e)}')
roles = Role.objects.all()
context = {
'roles': roles,
}
return render(request, 'user_roles/user_role_create.html', context)
@login_required
@owner_required
def user_role_edit(request, pk):
"""Изменение роли пользователя"""
user_role = get_object_or_404(UserRole, pk=pk)
# Защита от самоблокировки
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
if not can_modify:
messages.error(request, error_message)
return redirect('user_roles:list')
if request.method == 'POST':
action = request.POST.get('action', 'update')
# Обработка пересоздания пароля
if action == 'regenerate_password':
try:
new_password = User.objects.make_random_password(12)
user_role.user.set_password(new_password)
user_role.user.save()
messages.success(request, f'Пароль для пользователя {user_role.user.email} успешно обновлен')
# Показываем новый пароль в модальном окне
roles = Role.objects.all()
context = {
'user_role': user_role,
'roles': roles,
'generated_password': new_password,
}
return render(request, 'user_roles/user_role_edit.html', context)
except Exception as e:
messages.error(request, f'Ошибка при обновлении пароля: {str(e)}')
# Обычное обновление роли и имени
else:
role_code = request.POST.get('role')
is_active = request.POST.get('is_active') == 'on'
name = request.POST.get('name', '').strip()
try:
# Обновляем роль
role = RoleService.get_role_by_code(role_code)
if not role:
raise ValueError(f"Роль '{role_code}' не найдена")
user_role.role = role
user_role.is_active = is_active
user_role.save()
# Обновляем имя пользователя
if name and name != user_role.user.name:
user_role.user.name = name
user_role.user.save()
messages.success(request, f'Данные пользователя {user_role.user.email} обновлены')
return redirect('user_roles:list')
except Exception as e:
messages.error(request, f'Ошибка при обновлении данных: {str(e)}')
roles = Role.objects.all()
context = {
'user_role': user_role,
'roles': roles,
}
return render(request, 'user_roles/user_role_edit.html', context)
@login_required
@owner_required
def user_role_delete(request, pk):
"""Деактивация пользователя (soft delete)"""
user_role = get_object_or_404(UserRole, pk=pk)
# Защита от самоблокировки
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
if not can_modify:
messages.error(request, error_message)
return redirect('user_roles:list')
if request.method == 'POST':
email = user_role.user.email
# Soft delete: деактивируем пользователя и его роль
user_role.is_active = False
user_role.save()
user_role.user.is_active = False
user_role.user.save()
messages.success(request, f'Пользователь {email} деактивирован')
return redirect('user_roles:list')
context = {
'user_role': user_role,
}
return render(request, 'user_roles/user_role_delete.html', context)
@login_required
@owner_required
def user_role_reactivate(request, pk):
"""Реактивация пользователя"""
user_role = get_object_or_404(UserRole, pk=pk)
if request.method == 'POST':
email = user_role.user.email
# Реактивируем пользователя и его роль
user_role.is_active = True
user_role.save()
user_role.user.is_active = True
user_role.user.save()
messages.success(request, f'Пользователь {email} успешно реактивирован')
return redirect('user_roles:list')
context = {
'user_role': user_role,
}
return render(request, 'user_roles/user_role_reactivate.html', context)