Рефакторинг системы вариативных товаров и справочник атрибутов

Основные изменения:
- Переименование ConfigurableKitProduct → ConfigurableProduct
- Добавлена поддержка Product как варианта (не только ProductKit)
- Создан справочник атрибутов (ProductAttribute, ProductAttributeValue)
- CRUD для управления атрибутами с inline редактированием значений
- Пересозданы миграции с нуля для всех приложений
- Добавлена ссылка на атрибуты в навигацию

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-30 01:44:34 +03:00
parent 277a514a82
commit 79ff523adb
36 changed files with 1597 additions and 951 deletions

View File

@@ -956,23 +956,21 @@ def release_stock_on_order_delete(sender, instance, **kwargs):
if r not in showcase_reservations
]
# Освобождаем только обычные резервы ПОСЛЕ успешного коммита транзакции
# Это гарантирует целостность: резервы освободятся только если удаление прошло успешно
def release_reservations():
for res in normal_reservations:
res.status = 'released'
res.released_at = timezone.now()
res.save()
# Витринные комплекты остаются зарезервированными, но отвязываем блокировки корзины
# НЕ трогаем order_item - он нужен если заказ снова перейдёт в completed
for res in showcase_reservations:
res.cart_lock_expires_at = None
res.locked_by_user = None
res.cart_session_id = None
res.save(update_fields=['cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
transaction.on_commit(release_reservations)
# Освобождаем резервы СРАЗУ в pre_delete (до каскадного удаления OrderItem)
# Это предотвращает ошибку FK constraint при попытке сохранить резерв после удаления OrderItem
for res in normal_reservations:
res.status = 'released'
res.released_at = timezone.now()
res.order_item = None # Обнуляем ссылку на удаляемый OrderItem
res.save()
# Витринные комплекты остаются зарезервированными, но отвязываем от заказа и блокировки корзины
for res in showcase_reservations:
res.order_item = None # Обнуляем ссылку на удаляемый OrderItem
res.cart_lock_expires_at = None
res.locked_by_user = None
res.cart_session_id = None
res.save(update_fields=['order_item', 'cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
@receiver(post_save, sender=OrderItem)