Рефакторинг: перенос логики создания временных комплектов в сервис

Изменения:
- Удалена функция create_temporary_kit из myproject/orders/views.py
- Перенесена в новый сервис myproject/products/services/kit_service.py
- Добавлен API endpoint products:api-temporary-kit-create для создания временных комплектов
- Обновлены URL-ы соответственно

Преимущества:
- Логика временных комплектов теперь находится в соответствующем приложении (products)
- Упрощена архитектура orders приложения
- Сервис может быть переиспользован в других контекстах
- Лучшее разделение ответственности между приложениями

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-10 23:44:05 +03:00
parent 3c0ba70bc8
commit 5d5de1fe31
15 changed files with 471 additions and 150 deletions

View File

@@ -0,0 +1,159 @@
"""
Сервис для работы с комплектами товаров.
Содержит бизнес-логику создания, обновления и управления комплектами.
"""
from decimal import Decimal
from django.db import transaction
from typing import List, Dict, Optional
from ..models import ProductKit, Product, KitItem
def create_temporary_kit(
name: str,
components: List[Dict],
description: str = '',
order=None
) -> ProductKit:
"""
Создает временный комплект с компонентами.
Временные комплекты используются для создания букетов "на лету" при оформлении заказа.
Они автоматически помечаются как временные (is_temporary=True) и связываются с заказом.
Args:
name: Название комплекта
components: Список компонентов в формате [{"product_id": 1, "quantity": "5"}, ...]
description: Описание комплекта (опционально)
order: Заказ, к которому привязан временный комплект (опционально)
Returns:
ProductKit: Созданный временный комплект
Raises:
ValueError: Если данные невалидны
Product.DoesNotExist: Если товар не найден
Example:
>>> kit = create_temporary_kit(
... name="Букет для Анны",
... description="Красные розы и белые лилии",
... components=[
... {"product_id": 1, "quantity": "5"},
... {"product_id": 2, "quantity": "3"}
... ]
... )
"""
# Валидация
if not name or not name.strip():
raise ValueError('Необходимо указать название комплекта')
if not components or len(components) == 0:
raise ValueError('Комплект должен содержать хотя бы один компонент')
with transaction.atomic():
# Создаем комплект
kit = ProductKit.objects.create(
name=name.strip(),
description=description.strip() if description else '',
is_temporary=True,
is_active=True,
order=order,
price_adjustment_type='none'
)
# Добавляем компоненты
added_count = 0
for component in components:
product_id = component.get('product_id')
quantity = component.get('quantity')
if not product_id or not quantity:
continue
try:
product = Product.objects.get(pk=product_id, is_active=True)
KitItem.objects.create(
kit=kit,
product=product,
quantity=Decimal(str(quantity))
)
added_count += 1
except Product.DoesNotExist:
# Пропускаем несуществующие товары
continue
except (ValueError, TypeError) as e:
# Пропускаем некорректные количества
continue
if added_count == 0:
raise ValueError('Не удалось добавить ни одного компонента в комплект')
# Пересчитываем цену комплекта на основе компонентов
kit.recalculate_base_price()
return kit
def make_kit_permanent(kit: ProductKit) -> bool:
"""
Преобразует временный комплект в постоянный.
Args:
kit: Комплект для преобразования
Returns:
bool: True если комплект был преобразован, False если уже постоянный
"""
if not kit.is_temporary:
return False
kit.is_temporary = False
kit.order = None # Отвязываем от заказа
kit.save()
return True
def duplicate_kit(kit: ProductKit, new_name: Optional[str] = None) -> ProductKit:
"""
Создает копию комплекта со всеми компонентами.
Args:
kit: Комплект для дублирования
new_name: Новое название (если None, используется "Копия {original_name}")
Returns:
ProductKit: Новый комплект-копия
"""
with transaction.atomic():
# Копируем комплект
new_kit = ProductKit.objects.create(
name=new_name or f"Копия {kit.name}",
description=kit.description,
short_description=kit.short_description,
price_adjustment_type=kit.price_adjustment_type,
price_adjustment_value=kit.price_adjustment_value,
sale_price=kit.sale_price,
is_temporary=False, # Копия всегда постоянная
is_active=kit.is_active
)
# Копируем категории
new_kit.categories.set(kit.categories.all())
# Копируем теги
new_kit.tags.set(kit.tags.all())
# Копируем компоненты
for item in kit.kit_items.all():
KitItem.objects.create(
kit=new_kit,
product=item.product,
variant_group=item.variant_group,
quantity=item.quantity
)
# Пересчитываем цену
new_kit.recalculate_base_price()
return new_kit