""" Сервис для работы с комплектами товаров. Содержит бизнес-логику создания, обновления и управления комплектами. """ 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