Files
octopus/myproject/products/views/uom_views.py
Andrey Smakotin e10f2c413b fix(units): добавить проверку прав PlatformAdmin и исправить запрос связи
- Добавить name="submit" к кнопке формы
- Запретить PlatformAdmin доступ к CRUD операций UnitOfMeasure
- Исправить запрос sales_units_using через ProductSalesUnit.objects.filter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 19:43:18 +03:00

285 lines
9.9 KiB
Python
Raw Permalink 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.
"""
Views для управления единицами измерения (Unit of Measure)
"""
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404
from django.db.models import Q, Count
from django.core.paginator import Paginator
from django.urls import reverse
from products.models import UnitOfMeasure, ProductSalesUnit
from products.forms import ProductSalesUnitForm, UnitOfMeasureForm
@login_required
def unit_of_measure_list(request):
"""
Список всех единиц измерения с возможностью фильтрации и поиска
"""
# Получаем параметры фильтрации
search_query = request.GET.get('q', '').strip()
is_active_filter = request.GET.get('is_active', '')
# Базовый queryset
units = UnitOfMeasure.objects.all()
# Аннотируем количество использований
units = units.annotate(
products_count=Count('products')
)
# Применяем фильтры
if search_query:
units = units.filter(
Q(code__icontains=search_query) |
Q(name__icontains=search_query) |
Q(short_name__icontains=search_query)
)
if is_active_filter:
units = units.filter(is_active=(is_active_filter == 'true'))
# Сортировка
units = units.order_by('position', 'code')
# Пагинация
paginator = Paginator(units, 50)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj': page_obj,
'search_query': search_query,
'is_active_filter': is_active_filter,
'total_units': units.count(),
}
return render(request, 'products/uom/unit_list.html', context)
@login_required
def unit_of_measure_create(request):
"""
Создание новой единицы измерения
"""
# Проверка: PlatformAdmin не имеет доступа к бизнес-данным тенантов
if request.user.__class__.__name__ == 'PlatformAdmin':
messages.error(request, 'У вас недостаточно прав для выполнения этого действия')
return redirect('products:unit-list')
if request.method == 'POST':
form = UnitOfMeasureForm(request.POST)
if form.is_valid():
unit = form.save()
messages.success(request, f'Единица измерения "{unit.name}" успешно создана!')
return redirect('products:unit-list')
else:
form = UnitOfMeasureForm()
context = {
'form': form,
'title': 'Создание единицы измерения',
'submit_text': 'Создать'
}
return render(request, 'products/uom/unit_form.html', context)
@login_required
def unit_of_measure_update(request, pk):
"""
Редактирование единицы измерения
"""
# Проверка: PlatformAdmin не имеет доступа к бизнес-данным тенантов
if request.user.__class__.__name__ == 'PlatformAdmin':
messages.error(request, 'У вас недостаточно прав для выполнения этого действия')
return redirect('products:unit-list')
unit = get_object_or_404(UnitOfMeasure, pk=pk)
if request.method == 'POST':
form = UnitOfMeasureForm(request.POST, instance=unit)
if form.is_valid():
unit = form.save()
messages.success(request, f'Единица измерения "{unit.name}" успешно обновлена!')
return redirect('products:unit-list')
else:
form = UnitOfMeasureForm(instance=unit)
context = {
'form': form,
'unit': unit,
'title': f'Редактирование: {unit.name}',
'submit_text': 'Сохранить'
}
return render(request, 'products/uom/unit_form.html', context)
@login_required
def unit_of_measure_delete(request, pk):
"""
Удаление единицы измерения
"""
# Проверка: PlatformAdmin не имеет доступа к бизнес-данным тенантов
if request.user.__class__.__name__ == 'PlatformAdmin':
messages.error(request, 'У вас недостаточно прав для выполнения этого действия')
return redirect('products:unit-list')
unit = get_object_or_404(UnitOfMeasure, pk=pk)
# Проверяем использование
products_using = unit.products.count()
sales_units_using = ProductSalesUnit.objects.filter(product__base_unit=unit).count()
can_delete = products_using == 0 and sales_units_using == 0
if request.method == 'POST':
if can_delete:
name = unit.name
unit.delete()
messages.success(request, f'Единица измерения "{name}" успешно удалена!')
return redirect('products:unit-list')
else:
messages.error(
request,
f'Невозможно удалить единицу измерения "{unit.name}". '
f'Она используется в {products_using} товарах и {sales_units_using} единицах продажи.'
)
return redirect('products:unit-list')
context = {
'unit': unit,
'can_delete': can_delete,
'products_using': products_using,
'sales_units_using': sales_units_using,
}
return render(request, 'products/uom/unit_delete.html', context)
@login_required
def product_sales_unit_list(request):
"""
Список всех единиц продажи товаров с возможностью фильтрации
"""
# Получаем параметры фильтрации
search_query = request.GET.get('q', '').strip()
is_active_filter = request.GET.get('is_active', '')
is_default_filter = request.GET.get('is_default', '')
# Базовый queryset
sales_units = ProductSalesUnit.objects.select_related('product').all()
# Применяем фильтры
if search_query:
sales_units = sales_units.filter(
Q(product__name__icontains=search_query) |
Q(product__sku__icontains=search_query) |
Q(name__icontains=search_query)
)
if is_active_filter:
sales_units = sales_units.filter(is_active=(is_active_filter == 'true'))
if is_default_filter:
sales_units = sales_units.filter(is_default=(is_default_filter == 'true'))
# Сортировка
sales_units = sales_units.order_by('product__name', 'position')
# Пагинация
paginator = Paginator(sales_units, 50)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj': page_obj,
'search_query': search_query,
'is_active_filter': is_active_filter,
'is_default_filter': is_default_filter,
'total_sales_units': sales_units.count(),
}
return render(request, 'products/uom/sales_unit_list.html', context)
@login_required
def product_sales_unit_create(request):
"""
Создание новой единицы продажи
"""
if request.method == 'POST':
form = ProductSalesUnitForm(request.POST)
if form.is_valid():
sales_unit = form.save()
messages.success(
request,
f'Единица продажи "{sales_unit.name}" для товара "{sales_unit.product.name}" успешно создана!'
)
return redirect('products:sales-unit-list')
else:
# Предзаполнение товара если передан в параметрах
initial = {}
product_id = request.GET.get('product')
if product_id:
initial['product'] = product_id
form = ProductSalesUnitForm(initial=initial)
context = {
'form': form,
'title': 'Создание единицы продажи',
'submit_text': 'Создать'
}
return render(request, 'products/uom/sales_unit_form.html', context)
@login_required
def product_sales_unit_update(request, pk):
"""
Редактирование единицы продажи
"""
sales_unit = get_object_or_404(ProductSalesUnit, pk=pk)
if request.method == 'POST':
form = ProductSalesUnitForm(request.POST, instance=sales_unit)
if form.is_valid():
sales_unit = form.save()
messages.success(
request,
f'Единица продажи "{sales_unit.name}" успешно обновлена!'
)
return redirect('products:sales-unit-list')
else:
form = ProductSalesUnitForm(instance=sales_unit)
context = {
'form': form,
'sales_unit': sales_unit,
'title': f'Редактирование: {sales_unit.name}',
'submit_text': 'Сохранить'
}
return render(request, 'products/uom/sales_unit_form.html', context)
@login_required
def product_sales_unit_delete(request, pk):
"""
Удаление единицы продажи
"""
sales_unit = get_object_or_404(ProductSalesUnit, pk=pk)
if request.method == 'POST':
product_name = sales_unit.product.name
unit_name = sales_unit.name
sales_unit.delete()
messages.success(
request,
f'Единица продажи "{unit_name}" для товара "{product_name}" успешно удалена!'
)
return redirect('products:sales-unit-list')
context = {
'sales_unit': sales_unit,
}
return render(request, 'products/uom/sales_unit_delete.html', context)