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

@@ -128,6 +128,14 @@ class ProductSalesUnit(models.Model):
verbose_name_plural = "Единицы продажи товаров"
ordering = ['position', 'id']
unique_together = [['product', 'name']]
constraints = [
models.UniqueConstraint(
fields=['product'],
condition=models.Q(is_default=True),
name='unique_default_sales_unit_per_product',
violation_error_message='У товара может быть только одна единица продажи по умолчанию'
)
]
def __str__(self):
return f"{self.product.name} - {self.name}"
@@ -146,10 +154,15 @@ class ProductSalesUnit(models.Model):
def save(self, *args, **kwargs):
# Если это единица по умолчанию, снимаем флаг с других
if self.is_default:
ProductSalesUnit.objects.filter(
# Используем exclude только если pk уже существует
# Для новых записей (pk=None) exclude не нужен, т.к. запись еще не в БД
queryset = ProductSalesUnit.objects.filter(
product=self.product,
is_default=True
).exclude(pk=self.pk).update(is_default=False)
)
if self.pk:
queryset = queryset.exclude(pk=self.pk)
queryset.update(is_default=False)
super().save(*args, **kwargs)
@property