Проблема: при создании тенанта автоматически создаются дефолтные Склад и Витрина. Если пользователь удалит их, система может сломаться: POS, создание заказов и резервирование перестанут работать. Решение: реализована строгая валидация + мягкое удаление для витрин. Изменения в inventory/views/warehouse.py: - Добавлена валидация перед деактивацией склада: * Блокировка деактивации последнего активного склада * Проверка ненулевых остатков товаров * Проверка активных резервов * Предупреждение при деактивации дефолтного склада Изменения в inventory/views/showcase.py: - ShowcaseListView: по умолчанию показывает только активные витрины - ShowcaseDeleteView: изменена логика с жесткого на мягкое удаление - Добавлена валидация перед деактивацией витрины: * Блокировка деактивации последней активной витрины склада * Проверка активных резервов * Проверка физических экземпляров комплектов (ShowcaseItem) * Предупреждение при деактивации дефолтной витрины Изменения в inventory/forms_showcase.py: - Проверка уникальности названия витрины учитывает только активные Изменения в inventory/admin.py: - ShowcaseAdmin: добавлены методы delete_model() и delete_queryset() для блокировки удаления последней витрины через админку - WarehouseAdmin: добавлены методы delete_model() и delete_queryset() для блокировки удаления последнего склада через админку Преимущества: ✅ Система не сломается - всегда есть хотя бы один активный склад/витрина ✅ Данные в безопасности - мягкое удаление для обеих сущностей ✅ Понятные сообщения об ошибках для пользователя ✅ Защита работает как в UI, так и в Django Admin 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
77 lines
3.7 KiB
Python
77 lines
3.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
from django import forms
|
||
from django.core.exceptions import ValidationError
|
||
|
||
from .models import Showcase, Warehouse
|
||
|
||
|
||
class ShowcaseForm(forms.ModelForm):
|
||
"""
|
||
Форма для создания и редактирования витрин.
|
||
Витрина привязывается к складу и используется для выкладки готовых букетов.
|
||
"""
|
||
class Meta:
|
||
model = Showcase
|
||
fields = ['name', 'warehouse', 'description', 'is_active', 'is_default']
|
||
widgets = {
|
||
'name': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Название витрины (например: Витрина №1, Витрина у входа)'
|
||
}),
|
||
'warehouse': forms.Select(attrs={'class': 'form-control'}),
|
||
'description': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 3,
|
||
'placeholder': 'Описание витрины, её расположение или особенности'
|
||
}),
|
||
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||
'is_default': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||
}
|
||
labels = {
|
||
'name': 'Название витрины',
|
||
'warehouse': 'Склад',
|
||
'description': 'Описание',
|
||
'is_active': 'Активна',
|
||
'is_default': 'Витрина по умолчанию',
|
||
}
|
||
help_texts = {
|
||
'warehouse': 'Склад, к которому привязана витрина',
|
||
'is_active': 'Неактивные витрины скрыты из списка выбора',
|
||
'is_default': 'Витрина будет автоматически выбрана при создании резерва на этом складе',
|
||
}
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
# Фильтруем только активные склады
|
||
self.fields['warehouse'].queryset = Warehouse.objects.filter(is_active=True).order_by('name')
|
||
|
||
# Если создаём новую витрину и есть склад по умолчанию - предвыбираем его
|
||
if not self.instance.pk and not self.initial.get('warehouse'):
|
||
default_warehouse = Warehouse.objects.filter(
|
||
is_active=True,
|
||
is_default=True
|
||
).first()
|
||
if default_warehouse:
|
||
self.initial['warehouse'] = default_warehouse.id
|
||
|
||
def clean_name(self):
|
||
"""Проверка уникальности названия витрины в рамках склада (среди активных)"""
|
||
name = self.cleaned_data.get('name')
|
||
warehouse = self.cleaned_data.get('warehouse')
|
||
|
||
if name and warehouse:
|
||
# Проверяем уникальность названия в рамках склада (только среди активных)
|
||
queryset = Showcase.objects.filter(name=name, warehouse=warehouse, is_active=True)
|
||
|
||
# При редактировании исключаем текущий экземпляр
|
||
if self.instance and self.instance.pk:
|
||
queryset = queryset.exclude(pk=self.instance.pk)
|
||
|
||
if queryset.exists():
|
||
raise ValidationError(
|
||
f'Активная витрина с названием "{name}" уже существует на складе "{warehouse.name}". '
|
||
'Пожалуйста, выберите другое название.'
|
||
)
|
||
|
||
return name
|