feat(products): обеспечить уникальность единицы продажи по умолчанию и улучшить UI формы

Добавлено ограничение на уровне базы данных и валидация форм для обеспечения,
что у товара может быть только одна единица продажи с флагом "по умолчанию".
Переработан интерфейс маркетинговых флагов и единиц продажи для улучшения UX.

Основные изменения:
- Добавлен UniqueConstraint в модель ProductSalesUnit для валидации на уровне БД
- Создан BaseProductSalesUnitFormSet с кастомной валидацией формы
- Обновлен метод save() для корректной обработки новых и существующих записей
- Добавлена транзакционная обертка в представлениях ProductCreateView и ProductUpdateView
- Переработан блок маркетинговых флагов с карточным дизайном и интерактивными переключателями
- Переработан блок единиц продажи в табличный вид с улучшенным UX
- Добавлена клиентская логика для взаимного исключения чекбоксов "По умолчанию
This commit is contained in:
2026-01-27 22:37:00 +03:00
parent 0ee6391810
commit eb7569f983
6 changed files with 831 additions and 163 deletions

View File

@@ -1321,12 +1321,49 @@ class ProductSalesUnitInlineForm(forms.ModelForm):
return super().has_changed()
class BaseProductSalesUnitFormSet(forms.BaseInlineFormSet):
"""
Базовый формсет для единиц продажи с валидацией.
Обеспечивает, что только одна единица продажи может быть по умолчанию.
"""
def clean(self):
if any(self.errors):
return
default_count = 0
default_forms = []
for form in self.forms:
if self.can_delete and self._should_delete_form(form):
continue
if not form.cleaned_data:
continue
if form.cleaned_data.get('is_default'):
default_count += 1
default_forms.append(form)
if default_count > 1:
# Находим названия единиц с is_default для более информативного сообщения
unit_names = []
for form in default_forms:
name = form.cleaned_data.get('name', 'Без названия')
unit_names.append(f'"{name}"')
raise forms.ValidationError(
f'Можно установить только одну единицу продажи как "по умолчанию". '
f'Найдено {default_count} единиц с флагом "по умолчанию": {", ".join(unit_names)}.'
)
# Inline formset для единиц продажи
ProductSalesUnitFormSet = inlineformset_factory(
Product,
ProductSalesUnit,
form=ProductSalesUnitInlineForm,
extra=1,
formset=BaseProductSalesUnitFormSet,
extra=0,
can_delete=True,
min_num=0,
validate_min=False,