from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.conf import settings
from decimal import Decimal
from products.models import Product
from phonenumber_field.modelfields import PhoneNumberField


class Warehouse(models.Model):
    """
    Склад (физическое или логическое место хранения).
    Может использоваться как точка самовывоза для заказов.
    """
    name = models.CharField(max_length=200, verbose_name="Название")
    description = models.TextField(blank=True, null=True, verbose_name="Описание")

    # Адрес
    street = models.CharField(max_length=255, blank=True, null=True, verbose_name="Улица")
    building_number = models.CharField(max_length=20, blank=True, null=True, verbose_name="Номер здания")

    # Контакты
    phone = PhoneNumberField(region='BY', blank=True, null=True, verbose_name="Телефон")
    email = models.EmailField(blank=True, null=True, verbose_name="Email")

    # Настройки
    is_active = models.BooleanField(default=True, verbose_name="Активен")
    is_default = models.BooleanField(
        default=False,
        verbose_name="Склад по умолчанию",
        help_text="Автоматически выбирается при создании новых документов"
    )
    is_pickup_point = models.BooleanField(
        default=True,
        verbose_name="Доступен для самовывоза",
        help_text="Можно ли выбрать этот склад как точку самовывоза заказа"
    )

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")

    class Meta:
        verbose_name = "Склад"
        verbose_name_plural = "Склады"
        indexes = [
            models.Index(fields=['is_active']),
            models.Index(fields=['is_default']),
            models.Index(fields=['is_pickup_point']),
        ]

    def __str__(self):
        if self.street and self.building_number:
            return f"{self.name} ({self.street}, {self.building_number})"
        return self.name

    @property
    def full_address(self):
        """Полный адрес склада"""
        parts = []
        if self.street:
            parts.append(self.street)
        if self.building_number:
            parts.append(self.building_number)
        return ', '.join(parts) if parts else "Адрес не указан"

    def save(self, *args, **kwargs):
        """Обеспечиваем что только один склад может быть по умолчанию в рамках одного тенанта"""
        if self.is_default:
            # Снимаем флаг is_default со всех других складов этого тенанта
            Warehouse.objects.filter(is_default=True).exclude(pk=self.pk).update(is_default=False)
        super().save(*args, **kwargs)


class StockBatch(models.Model):
    """
    Партия товара (неделимая единица учета).
    Ключевая сущность для FIFO.
    """
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
                               related_name='stock_batches', verbose_name="Товар")
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='stock_batches', verbose_name="Склад")
    quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
    cost_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Закупочная цена")
    is_active = models.BooleanField(default=True, verbose_name="Активна")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")

    class Meta:
        verbose_name = "Партия товара"
        verbose_name_plural = "Партии товаров"
        ordering = ['created_at']  # FIFO: старые партии первыми
        indexes = [
            models.Index(fields=['product', 'warehouse']),
            models.Index(fields=['created_at']),
            models.Index(fields=['is_active']),
        ]

    def __str__(self):
        return f"{self.product.name} на {self.warehouse.name} - Остаток: {self.quantity} шт @ {self.cost_price} за ед."


# Модели IncomingBatch и Incoming удалены - заменены на IncomingDocument/IncomingDocumentItem
# Теперь используется упрощенная архитектура:
# IncomingDocument → IncomingDocumentItem → StockBatch (напрямую при проведении)


class Sale(models.Model):
    """
    Продажа товара. Списывает по FIFO.
    """
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
                               related_name='sales', verbose_name="Товар")
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='sales', verbose_name="Склад")
    quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
    sale_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Цена продажи")
    order = models.ForeignKey('orders.Order', on_delete=models.SET_NULL, null=True, blank=True,
                             related_name='sales', verbose_name="Заказ")
    document_number = models.CharField(max_length=100, blank=True, null=True,
                                      verbose_name="Номер документа")
    date = models.DateTimeField(auto_now_add=True, verbose_name="Дата операции")
    processed = models.BooleanField(default=False, verbose_name="Обработана (FIFO применена)")

    # === ПОЛЯ ДЛЯ ЕДИНИЦ ПРОДАЖИ ===
    sales_unit = models.ForeignKey(
        'products.ProductSalesUnit',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='sales',
        verbose_name="Единица продажи"
    )
    quantity_base = models.DecimalField(
        max_digits=10,
        decimal_places=6,
        null=True,
        blank=True,
        verbose_name="Количество в базовых единицах",
        help_text="Количество в единицах хранения товара (для списания со склада)"
    )
    unit_name_snapshot = models.CharField(
        max_length=100,
        blank=True,
        default='',
        verbose_name="Название единицы (snapshot)",
        help_text="Название единицы продажи на момент продажи"
    )

    # === ПОЛЯ ДЛЯ ПРОДАЖ "В МИНУС" ===
    is_pending_cost = models.BooleanField(
        default=False,
        verbose_name="Ожидает себестоимости",
        help_text="True если продажа создана без партий (продажа 'в минус')"
    )
    pending_quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        default=Decimal('0'),
        verbose_name="Ожидающее количество",
        help_text="Количество, ожидающее привязки к партиям при приёмке"
    )

    class Meta:
        verbose_name = "Продажа"
        verbose_name_plural = "Продажи"
        ordering = ['-date']
        indexes = [
            models.Index(fields=['product', 'warehouse']),
            models.Index(fields=['date']),
            models.Index(fields=['order']),
            models.Index(fields=['is_pending_cost']),
        ]

    def __str__(self):
        unit_info = f" ({self.unit_name_snapshot})" if self.unit_name_snapshot else ""
        return f"Продажа {self.product.name}: {self.quantity}{unit_info} @ {self.sale_price}"


class SaleBatchAllocation(models.Model):
    """
    Связь между Sale и StockBatch для отслеживания FIFO-списания.
    (Для аудита: какая партия использована при продаже)
    """
    sale = models.ForeignKey(Sale, on_delete=models.CASCADE,
                            related_name='batch_allocations', verbose_name="Продажа")
    batch = models.ForeignKey(StockBatch, on_delete=models.CASCADE,
                             related_name='sale_allocations', verbose_name="Партия")
    quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
    cost_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Закупочная цена")

    class Meta:
        verbose_name = "Распределение продажи по партиям"
        verbose_name_plural = "Распределения продаж по партиям"

    def __str__(self):
        return f"{self.sale} ← {self.batch} ({self.quantity} шт)"


class WriteOff(models.Model):
    """
    Списание товара вручную (брак, порча, недостача).
    Человек выбирает конкретную партию.
    """
    REASON_CHOICES = [
        ('damage', 'Повреждение'),
        ('spoilage', 'Порча'),
        ('shortage', 'Недостача'),
        ('inventory', 'Инвентаризационная недостача'),
        ('other', 'Другое'),
    ]

    batch = models.ForeignKey(StockBatch, on_delete=models.CASCADE,
                             related_name='writeoffs', verbose_name="Партия")
    quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
    reason = models.CharField(max_length=20, choices=REASON_CHOICES,
                             default='other', verbose_name="Причина")
    cost_price = models.DecimalField(max_digits=10, decimal_places=2,
                                    verbose_name="Закупочная цена", editable=False)
    document_number = models.CharField(max_length=100, blank=True, null=True,
                                      verbose_name="Номер документа")
    notes = models.TextField(blank=True, null=True, verbose_name="Примечания")
    date = models.DateTimeField(auto_now_add=True, verbose_name="Дата операции")

    class Meta:
        verbose_name = "Списание"
        verbose_name_plural = "Списания"
        ordering = ['-date']
        indexes = [
            models.Index(fields=['batch']),
            models.Index(fields=['date']),
        ]

    def __str__(self):
        return f"Списание {self.batch.product.name}: {self.quantity} шт ({self.get_reason_display()})"

    def save(self, *args, **kwargs):
        # Автоматически записываем cost_price из партии
        if not self.pk:  # Только при создании
            self.cost_price = self.batch.cost_price

            # Проверяем что не списываем больше чем есть
            if self.quantity > self.batch.quantity:
                raise ValidationError(
                    f"Невозможно списать {self.quantity} шт из партии, "
                    f"где только {self.batch.quantity} шт. "
                    f"Недостаток: {self.quantity - self.batch.quantity} шт."
                )

            # Уменьшаем количество в партии при создании списания
            self.batch.quantity -= self.quantity
            if self.batch.quantity <= 0:
                self.batch.is_active = False
            self.batch.save(update_fields=['quantity', 'is_active', 'updated_at'])

        super().save(*args, **kwargs)



class Inventory(models.Model):
    """
    Инвентаризация (физический пересчет товаров).
    """
    STATUS_CHOICES = [
        ('draft', 'Черновик'),
        ('processing', 'В обработке'),
        ('completed', 'Завершена'),
    ]

    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='inventories', verbose_name="Склад")
    document_number = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        blank=True,
        null=True,
        verbose_name="Номер документа"
    )
    date = models.DateTimeField(auto_now_add=True, verbose_name="Дата инвентаризации")
    status = models.CharField(max_length=20, choices=STATUS_CHOICES,
                             default='draft', verbose_name="Статус")
    conducted_by = models.ForeignKey(
        'user_roles.UserRole',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='inventories',
        verbose_name="Провел инвентаризацию"
    )
    notes = models.TextField(blank=True, null=True, verbose_name="Примечания")

    class Meta:
        verbose_name = "Инвентаризация"
        verbose_name_plural = "Инвентаризации"
        ordering = ['-date']
        indexes = [
            models.Index(fields=['document_number']),
        ]

    def __str__(self):
        if self.document_number:
            return f"{self.document_number} - {self.warehouse.name} ({self.date.strftime('%Y-%m-%d')})"
        return f"Инвентаризация {self.warehouse.name} ({self.date.strftime('%Y-%m-%d')})"


class InventoryLine(models.Model):
    """
    Строка инвентаризации (товар + фактическое количество).
    """
    inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE,
                                 related_name='lines', verbose_name="Инвентаризация")
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
                               verbose_name="Товар")
    quantity_system = models.DecimalField(max_digits=10, decimal_places=3,
                                         verbose_name="Количество в системе")
    quantity_fact = models.DecimalField(max_digits=10, decimal_places=3,
                                       verbose_name="Подсчитано (факт, свободные)",
                                       help_text="Количество свободных товаров, подсчитанных физически")
    difference = models.DecimalField(max_digits=10, decimal_places=3,
                                    default=0, verbose_name="Итоговая разница",
                                    help_text="(Подсчитано + Зарезервировано) - Всего на складе",
                                    editable=False)
    processed = models.BooleanField(default=False,
                                   verbose_name="Обработана (создана операция)")
    
    # Snapshot-значения на момент завершения инвентаризации
    snapshot_quantity_available = models.DecimalField(
        max_digits=10, decimal_places=3, null=True, blank=True,
        verbose_name="Всего на складе (snapshot)",
        help_text="Всего на складе на момент завершения инвентаризации"
    )
    snapshot_quantity_reserved = models.DecimalField(
        max_digits=10, decimal_places=3, null=True, blank=True,
        verbose_name="В резервах (snapshot)",
        help_text="В резервах на момент завершения инвентаризации"
    )
    snapshot_quantity_system = models.DecimalField(
        max_digits=10, decimal_places=3, null=True, blank=True,
        verbose_name="В системе свободно (snapshot)",
        help_text="В системе свободно на момент завершения инвентаризации"
    )
    snapshot_difference = models.DecimalField(
        max_digits=10, decimal_places=3, null=True, blank=True,
        verbose_name="Итоговая разница (snapshot)",
        help_text="Итоговая разница на момент завершения инвентаризации"
    )

    class Meta:
        verbose_name = "Строка инвентаризации"
        verbose_name_plural = "Строки инвентаризации"

    def __str__(self):
        return f"{self.product.name}: {self.quantity_system} (сист.) vs {self.quantity_fact} (факт)"

    def save(self, *args, **kwargs):
        # Автоматически рассчитываем разницу
        # Формула: (quantity_fact + quantity_reserved) - quantity_available
        # Где quantity_fact - подсчитанные свободные товары
        # Для расчета нужны quantity_reserved и quantity_available из Stock
        # Если они не переданы в kwargs, получаем из Stock
        quantity_reserved = kwargs.pop('quantity_reserved', None)
        quantity_available = kwargs.pop('quantity_available', None)
        
        if quantity_reserved is None or quantity_available is None:
            # Получаем из Stock для расчета
            from inventory.models import Stock
            stock = Stock.objects.filter(
                product=self.product,
                warehouse=self.inventory.warehouse
            ).first()
            if stock:
                stock.refresh_from_batches()
                quantity_reserved = stock.quantity_reserved
                quantity_available = stock.quantity_available
        
        # Вычисляем разницу по новой формуле: (quantity_fact + quantity_reserved) - quantity_available
        if quantity_reserved is not None and quantity_available is not None:
            self.difference = (self.quantity_fact + quantity_reserved) - quantity_available
        else:
            # Fallback на старую формулу, если Stock недоступен (не должно происходить в нормальной работе)
            self.difference = self.quantity_fact - self.quantity_system
        
        super().save(*args, **kwargs)


class Showcase(models.Model):
    """
    Витрина - место выкладки собранных букетов/комплектов.
    Привязана к конкретному складу для учёта резервов.
    """
    name = models.CharField(max_length=200, verbose_name="Название")
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='showcases', verbose_name="Склад")
    description = models.TextField(blank=True, null=True, verbose_name="Описание")
    is_active = models.BooleanField(default=True, verbose_name="Активна")
    is_default = models.BooleanField(default=False, verbose_name="По умолчанию")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")

    class Meta:
        verbose_name = "Витрина"
        verbose_name_plural = "Витрины"
        ordering = ['warehouse', 'name']
        indexes = [
            models.Index(fields=['warehouse']),
            models.Index(fields=['is_active']),
            models.Index(fields=['is_default']),
        ]

    def __str__(self):
        return f"{self.name} ({self.warehouse.name})"

    def save(self, *args, **kwargs):
        """Обеспечиваем что только одна витрина может быть по умолчанию для каждого склада"""
        if self.is_default:
            # Снимаем флаг is_default со всех других витрин этого склада
            Showcase.objects.filter(warehouse=self.warehouse, is_default=True).exclude(pk=self.pk).update(is_default=False)
        super().save(*args, **kwargs)


class Reservation(models.Model):
    """
    Резервирование товара для заказа или витрины.
    Отслеживает, какой товар зарезервирован за каким заказом или витриной.
    """
    STATUS_CHOICES = [
        ('reserved', 'Зарезервирован'),
        ('released', 'Освобожден'),
        ('converted_to_sale', 'Преобразован в продажу'),
        ('converted_to_writeoff', 'Преобразован в списание'),
        ('converted_to_transformation', 'Преобразован в трансформацию'),
    ]

    order_item = models.ForeignKey('orders.OrderItem', on_delete=models.CASCADE,
                                  related_name='reservations', verbose_name="Позиция заказа",
                                  null=True, blank=True)
    showcase = models.ForeignKey(Showcase, on_delete=models.CASCADE,
                                related_name='reservations', verbose_name="Витрина",
                                null=True, blank=True,
                                help_text="Витрина, на которой выложен букет")
    product_kit = models.ForeignKey('products.ProductKit', on_delete=models.CASCADE,
                                   related_name='reservations', verbose_name="Комплект",
                                   null=True, blank=True,
                                   help_text="Временный комплект, для которого создан резерв")
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
                               related_name='reservations', verbose_name="Товар")
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='reservations', verbose_name="Склад")
    quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
    status = models.CharField(max_length=30, choices=STATUS_CHOICES,
                             default='reserved', verbose_name="Статус")
    reserved_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата резервирования")
    released_at = models.DateTimeField(null=True, blank=True, verbose_name="Дата освобождения")
    converted_at = models.DateTimeField(null=True, blank=True,
                                       verbose_name="Дата преобразования",
                                       help_text="Дата преобразования в продажу или списание")

    # Soft Lock для корзины POS (витринные комплекты)
    cart_lock_expires_at = models.DateTimeField(
        null=True, blank=True,
        verbose_name="Блокировка корзины истекает",
        help_text="Время истечения блокировки в корзине (для витринных комплектов)"
    )
    locked_by_user = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='cart_locks',
        verbose_name="Заблокировано пользователем",
        help_text="Кассир, который добавил комплект в корзину"
    )
    cart_session_id = models.CharField(
        max_length=100,
        null=True, blank=True,
        verbose_name="ID сессии корзины",
        help_text="Дополнительная идентификация сессии для надежности"
    )

    # Связь с конкретным экземпляром витринного букета
    showcase_item = models.ForeignKey(
        'ShowcaseItem',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='reservations',
        verbose_name="Экземпляр на витрине",
        help_text="Для какого физического экземпляра создан резерв"
    )

    # ЗАЩИТА ОТ КРАЖИ ВИТРИННЫХ РЕЗЕРВОВ: запоминаем исходный order_item при отвязывании
    # Когда витринный резерв отвязывается от заказа (order_item=None), здесь сохраняется ID
    # исходного OrderItem. При обратном привязывании резерв вернётся только к своему "хозяину".
    original_order_item_id = models.IntegerField(
        null=True,
        blank=True,
        db_index=True,
        verbose_name="ID исходной позиции заказа",
        help_text="Для витринных резервов: ID OrderItem, которому изначально принадлежал резерв (защита от кражи)"
    )

    # Связь с позицией документа списания (для резервирования в черновике)
    writeoff_document_item = models.ForeignKey(
        'WriteOffDocumentItem',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='reservations',
        verbose_name="Позиция документа списания",
        help_text="Резерв для документа списания (черновик)"
    )

    # Связь с входным товаром трансформации (для резервирования в черновике)
    transformation_input = models.ForeignKey(
        'TransformationInput',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='reservations',
        verbose_name="Входной товар трансформации",
        help_text="Резерв для входного товара трансформации (черновик)"
    )

    # === ПОЛЯ ДЛЯ ЕДИНИЦ ПРОДАЖИ ===
    sales_unit = models.ForeignKey(
        'products.ProductSalesUnit',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='reservations',
        verbose_name="Единица продажи"
    )
    quantity_base = models.DecimalField(
        max_digits=10,
        decimal_places=6,
        null=True,
        blank=True,
        verbose_name="Количество в базовых единицах",
        help_text="Количество в единицах хранения товара"
    )

    class Meta:
        verbose_name = "Резервирование"
        verbose_name_plural = "Резервирования"
        ordering = ['-reserved_at']
        indexes = [
            models.Index(fields=['product', 'warehouse']),
            models.Index(fields=['status']),
            models.Index(fields=['order_item']),
            models.Index(fields=['showcase']),
            models.Index(fields=['product_kit']),
            models.Index(fields=['cart_lock_expires_at']),
            models.Index(fields=['locked_by_user']),
            models.Index(fields=['product_kit', 'cart_lock_expires_at']),
            models.Index(fields=['showcase_item']),
        ]

    def __str__(self):
        if self.order_item:
            context = f" (заказ {self.order_item.order.order_number})"
        elif self.product_kit:
            context = f" (комплект {self.product_kit.name})"
        elif self.showcase:
            context = f" (витрина {self.showcase.name})"
        else:
            context = ""
        return f"Резерв {self.product.name}: {self.quantity} шт{context} [{self.get_status_display()}]"


class ShowcaseItem(models.Model):
    """
    Физический экземпляр комплекта на витрине.

    Один ProductKit (шаблон) -> N ShowcaseItem (экземпляры).
    Каждый экземпляр имеет свой набор резервов и может быть продан независимо.

    Защита от двойной продажи:
    - sold_order_item = OneToOneField гарантирует что один экземпляр
      может быть продан только в один OrderItem (на уровне БД).
    """
    showcase = models.ForeignKey(
        'Showcase',
        on_delete=models.CASCADE,
        related_name='showcase_items',
        verbose_name="Витрина"
    )
    product_kit = models.ForeignKey(
        'products.ProductKit',
        on_delete=models.CASCADE,
        related_name='showcase_items',
        verbose_name="Шаблон комплекта"
    )

    # Статусы жизненного цикла
    STATUS_CHOICES = [
        ('available', 'Доступен'),
        ('in_cart', 'В корзине'),
        ('reserved', 'Зарезервирован'),
        ('sold', 'Продан'),
        ('dismantled', 'Разобран'),
    ]
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='available',
        db_index=True,
        verbose_name="Статус"
    )

    # === ЗАЩИТА ОТ ДВОЙНОЙ ПРОДАЖИ ===
    # ForeignKey позволяет привязать несколько ShowcaseItem к одному OrderItem
    # (например, при продаже 2+ экземпляров одного букета)
    sold_order_item = models.ForeignKey(
        'orders.OrderItem',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='sold_showcase_items',
        verbose_name="Позиция заказа (продажа)"
    )
    sold_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Дата продажи"
    )

    # === SOFT LOCK для корзины ===
    locked_by_user = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='locked_showcase_items',
        verbose_name="Заблокировано пользователем"
    )
    cart_lock_expires_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Блокировка истекает"
    )
    cart_session_id = models.CharField(
        max_length=100,
        null=True,
        blank=True,
        verbose_name="ID сессии корзины"
    )

    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлен")

    class Meta:
        verbose_name = "Экземпляр на витрине"
        verbose_name_plural = "Экземпляры на витрине"
        indexes = [
            models.Index(fields=['showcase', 'status']),
            models.Index(fields=['product_kit', 'status']),
            models.Index(fields=['status', 'cart_lock_expires_at']),
            models.Index(fields=['locked_by_user', 'status']),
        ]

    def __str__(self):
        return f"{self.product_kit.name} #{self.id} ({self.get_status_display()})"

    def lock_for_cart(self, user, session_id=None, duration_minutes=30):
        """Заблокировать экземпляр для корзины"""
        from datetime import timedelta

        self.status = 'in_cart'
        
        # Проверяем тип пользователя - locked_by_user только для CustomUser
        from accounts.models import CustomUser
        if isinstance(user, CustomUser):
            self.locked_by_user = user
        else:
            # Для PlatformAdmin и других типов пользователей поле оставляем пустым
            # Блокировка будет работать через cart_session_id
            self.locked_by_user = None
            
        self.cart_lock_expires_at = timezone.now() + timedelta(minutes=duration_minutes)
        self.cart_session_id = session_id
        self.save(update_fields=['status', 'locked_by_user', 'cart_lock_expires_at', 'cart_session_id', 'updated_at'])

    def release_lock(self):
        """Снять блокировку корзины"""
        self.status = 'available'
        self.locked_by_user = None
        self.cart_lock_expires_at = None
        self.cart_session_id = None
        self.save(update_fields=['status', 'locked_by_user', 'cart_lock_expires_at', 'cart_session_id', 'updated_at'])

    def reserve_for_order(self, order_item):
        """
        Зарезервировать экземпляр под конкретный заказ (жёсткая блокировка).
        Используется для отложенных заказов, когда букет привязан к заказу,
        но заказ ещё не в финальном статусе.
        
        Args:
            order_item: OrderItem - позиция заказа, к которой привязывается экземпляр
        
        Raises:
            ValidationError: если экземпляр уже продан или разобран
        """
        if self.status == 'sold':
            raise ValidationError(f'Экземпляр {self} уже продан')
        if self.status == 'dismantled':
            raise ValidationError(f'Экземпляр {self} разобран')
        
        self.status = 'reserved'
        self.sold_order_item = order_item
        # sold_at оставляем пустым - это не финальная продажа
        # Очищаем soft-lock поля корзины
        self.locked_by_user = None
        self.cart_lock_expires_at = None
        self.cart_session_id = None
        self.save(update_fields=['status', 'sold_order_item', 'locked_by_user', 
                                 'cart_lock_expires_at', 'cart_session_id', 'updated_at'])

    def mark_sold(self, order_item):
        """
        Пометить как проданный.
        Проверяет статус перед продажей чтобы избежать дублей.
        
        Для прямой продажи (POS "Продать сейчас"):
            available/in_cart -> sold
        
        Для финализации отложенного заказа:
            reserved -> sold (через mark_sold_from_reserved)
        """
        if self.status == 'sold':
            raise ValidationError(f'Экземпляр {self} уже продан')
        
        self.status = 'sold'
        self.sold_order_item = order_item
        self.sold_at = timezone.now()
        self.locked_by_user = None
        self.cart_lock_expires_at = None
        self.cart_session_id = None
        self.save()

    def mark_sold_from_reserved(self):
        """
        Финализировать продажу из зарезервированного состояния.
        Используется при переходе заказа в положительный конечный статус (completed).
        
        Raises:
            ValidationError: если экземпляр не в статусе 'reserved'
        """
        if self.status != 'reserved':
            raise ValidationError(
                f'Экземпляр {self} не в статусе "reserved" (текущий: {self.get_status_display()})'
            )
        
        self.status = 'sold'
        self.sold_at = timezone.now()
        # sold_order_item уже установлен при резервировании
        self.save(update_fields=['status', 'sold_at', 'updated_at'])

    def return_to_available(self):
        """
        Вернуть экземпляр на витрину (освободить).
        Используется при отмене заказа.
        
        Raises:
            ValidationError: если экземпляр уже разобран
        """
        if self.status == 'dismantled':
            raise ValidationError(f'Экземпляр {self} разобран и не может быть возвращён на витрину')
        
        self.status = 'available'
        self.sold_order_item = None
        self.sold_at = None
        self.locked_by_user = None
        self.cart_lock_expires_at = None
        self.cart_session_id = None
        self.save(update_fields=['status', 'sold_order_item', 'sold_at', 'locked_by_user',
                                 'cart_lock_expires_at', 'cart_session_id', 'updated_at'])

    def return_to_reserved(self, order_item):
        """
        Вернуть экземпляр в зарезервированное состояние.
        Используется при откате заказа из completed в нейтральный статус.
        
        Args:
            order_item: OrderItem - позиция заказа, за которой остаётся экземпляр
        """
        if self.status == 'dismantled':
            raise ValidationError(f'Экземпляр {self} разобран')
        
        self.status = 'reserved'
        self.sold_order_item = order_item
        self.sold_at = None  # Сбрасываем дату продажи
        self.save(update_fields=['status', 'sold_order_item', 'sold_at', 'updated_at'])

    def is_lock_expired(self):
        """Проверить истекла ли блокировка"""
        if self.cart_lock_expires_at is None:
            return True
        return timezone.now() > self.cart_lock_expires_at

    @classmethod
    def cleanup_expired_locks(cls):
        """Снять все просроченные блокировки (для Celery задачи)"""
        expired = cls.objects.filter(
            status='in_cart',
            cart_lock_expires_at__lt=timezone.now()
        )
        count = expired.update(
            status='available',
            locked_by_user=None,
            cart_lock_expires_at=None,
            cart_session_id=None
        )
        return count


class Stock(models.Model):
    """
    Агрегированные остатки по товарам и складам.
    Читаемое представление (может быть кешировано или пересчитано из StockBatch).
    """
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
                               related_name='stocks', verbose_name="Товар")
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
                                 related_name='stocks', verbose_name="Склад")
    quantity_available = models.DecimalField(max_digits=10, decimal_places=3, default=0,
                                            verbose_name="Доступное количество",
                                            editable=False)
    quantity_reserved = models.DecimalField(max_digits=10, decimal_places=3, default=0,
                                           verbose_name="Зарезервированное количество",
                                           editable=False)
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")

    class Meta:
        verbose_name = "Остаток на складе"
        verbose_name_plural = "Остатки на складе"
        unique_together = [['product', 'warehouse']]
        indexes = [
            models.Index(fields=['product', 'warehouse']),
        ]

    def __str__(self):
        return f"{self.product.name} на {self.warehouse.name}: {self.quantity_available} (зарезерв: {self.quantity_reserved})"

    @property
    def quantity_free(self):
        """Свободное количество (доступное минус зарезервированное)"""
        return self.quantity_available - self.quantity_reserved

    def refresh_from_batches(self):
        """
        Пересчитать остатки из StockBatch.
        Учитывает "ожидающие" продажи (продажи "в минус").
        quantity_available может быть отрицательным!
        """
        # Сумма из активных партий
        total_qty = StockBatch.objects.filter(
            product=self.product,
            warehouse=self.warehouse,
            is_active=True
        ).aggregate(models.Sum('quantity'))['quantity__sum'] or Decimal('0')

        # Учитываем ожидающие продажи (уменьшают доступное количество)
        pending_sales = Sale.objects.filter(
            product=self.product,
            warehouse=self.warehouse,
            is_pending_cost=True
        ).aggregate(models.Sum('pending_quantity'))['pending_quantity__sum'] or Decimal('0')

        # quantity_available может быть отрицательным при продажах "в минус"
        self.quantity_available = total_qty - pending_sales

        # Резервы остаются как есть
        total_reserved = Reservation.objects.filter(
            product=self.product,
            warehouse=self.warehouse,
            status='reserved'
        ).aggregate(models.Sum('quantity_base'))['quantity_base__sum'] or Decimal('0')

        self.quantity_reserved = total_reserved
        self.save()


class DocumentCounter(models.Model):
    """
    Счетчик номеров документов для различных операций.
    Используется для генерации уникальных номеров документов.
    """
    COUNTER_TYPE_CHOICES = [
        ('transfer', 'Перемещение товара'),
        ('writeoff', 'Списание товара'),
        ('incoming', 'Поступление товара'),
        ('inventory', 'Инвентаризация'),
        ('transformation', 'Трансформация товара'),
    ]

    counter_type = models.CharField(
        max_length=20,
        choices=COUNTER_TYPE_CHOICES,
        unique=True,
        verbose_name="Тип счетчика"
    )
    current_value = models.IntegerField(
        default=0,
        verbose_name="Текущее значение"
    )

    class Meta:
        verbose_name = "Счетчик документов"
        verbose_name_plural = "Счетчики документов"

    def __str__(self):
        return f"Счетчик {self.get_counter_type_display()}: {self.current_value}"

    @classmethod
    def get_next_value(cls, counter_type):
        """
        Получить следующее значение для счетчика.
        Thread-safe операция с использованием select_for_update.
        """
        from django.db import transaction

        with transaction.atomic():
            obj, _ = cls.objects.select_for_update().get_or_create(
                counter_type=counter_type
            )
            obj.current_value += 1
            obj.save(update_fields=['current_value'])
            return obj.current_value


class TransferDocument(models.Model):
    """
    Документ перемещения товара между складами.
    Один номер документа = одна операция перемещения множественных товаров.
    """
    from_warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.CASCADE,
        related_name='transfer_documents_from',
        verbose_name="Склад-отгрузки"
    )
    to_warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.CASCADE,
        related_name='transfer_documents_to',
        verbose_name="Склад-приемки"
    )
    document_number = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        verbose_name="Номер документа"
    )
    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Примечания"
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Дата создания"
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name="Дата обновления"
    )

    class Meta:
        verbose_name = "Документ перемещения"
        verbose_name_plural = "Документы перемещения"
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['document_number']),
            models.Index(fields=['from_warehouse', 'to_warehouse']),
            models.Index(fields=['-created_at']),
        ]

    def __str__(self):
        total_items = self.items.count()
        total_qty = self.items.aggregate(
            models.Sum('quantity')
        )['quantity__sum'] or Decimal('0')
        return f"Перемещение {self.document_number}: {total_items} товаров, {total_qty} шт ({self.from_warehouse} → {self.to_warehouse})"


class TransferDocumentItem(models.Model):
    """
    Строка документа перемещения (товар в перемещении).
    Связь между документом и товарами.
    """
    transfer_document = models.ForeignKey(
        TransferDocument,
        on_delete=models.CASCADE,
        related_name='items',
        verbose_name="Документ перемещения"
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='transfer_document_items',
        verbose_name="Товар"
    )
    batch = models.ForeignKey(
        StockBatch,
        on_delete=models.CASCADE,
        related_name='transfer_document_items',
        verbose_name="Исходная партия (FIFO)"
    )
    quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        verbose_name="Количество"
    )
    new_batch = models.ForeignKey(
        StockBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transfer_document_items_created',
        verbose_name="Созданная партия на целевом складе"
    )

    class Meta:
        verbose_name = "Строка перемещения"
        verbose_name_plural = "Строки перемещения"
        unique_together = [['transfer_document', 'batch']]
        ordering = ['id']
        indexes = [
            models.Index(fields=['transfer_document']),
            models.Index(fields=['product']),
        ]

    def __str__(self):
        return f"{self.product.name}: {self.quantity} шт (партия {self.batch.id})"


class WriteOffDocument(models.Model):
    """
    Документ списания товара.

    Сценарий использования:
    1. В начале смены создается черновик (draft)
    2. В течение дня добавляются испорченные товары (WriteOffDocumentItem)
    3. Товары в черновике ЗАРЕЗЕРВИРОВАНЫ (уменьшают quantity_free)
    4. В конце смены документ проводится (confirmed) → создаются WriteOff записи
    """
    STATUS_CHOICES = [
        ('draft', 'Черновик'),
        ('confirmed', 'Проведён'),
        ('cancelled', 'Отменён'),
    ]

    document_number = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        verbose_name="Номер документа"
    )

    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        related_name='writeoff_documents',
        verbose_name="Склад"
    )

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='draft',
        db_index=True,
        verbose_name="Статус"
    )

    date = models.DateField(
        verbose_name="Дата документа",
        help_text="Дата, к которой относится списание"
    )

    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Примечания"
    )

    # Связь с инвентаризацией
    inventory = models.ForeignKey(
        'Inventory',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='writeoff_documents',
        verbose_name="Инвентаризация"
    )

    # Аудит
    created_by = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_writeoff_documents',
        verbose_name="Создал"
    )

    confirmed_by = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='confirmed_writeoff_documents',
        verbose_name="Провёл"
    )

    confirmed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Дата проведения"
    )

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлён")

    class Meta:
        verbose_name = "Документ списания"
        verbose_name_plural = "Документы списания"
        ordering = ['-date', '-created_at']
        indexes = [
            models.Index(fields=['document_number']),
            models.Index(fields=['warehouse', 'status']),
            models.Index(fields=['date']),
            models.Index(fields=['-created_at']),
        ]

    def __str__(self):
        return f"{self.document_number} ({self.get_status_display()})"

    @property
    def total_quantity(self):
        """Общее количество товаров в документе"""
        return self.items.aggregate(
            total=models.Sum('quantity')
        )['total'] or Decimal('0')

    @property
    def total_cost(self):
        """Общая себестоимость списания"""
        return sum(item.total_cost for item in self.items.select_related('product'))

    @property
    def can_edit(self):
        """Можно ли редактировать документ"""
        return self.status == 'draft'

    @property
    def can_confirm(self):
        """Можно ли провести документ"""
        return self.status == 'draft' and self.items.exists()

    @property
    def can_cancel(self):
        """Можно ли отменить документ"""
        return self.status == 'draft'


class WriteOffDocumentItem(models.Model):
    """
    Строка документа списания.

    При создании:
    1. Создается Reservation для резервирования товара
    2. Stock.quantity_reserved увеличивается
    3. Stock.quantity_free уменьшается

    При проведении документа:
    1. Создается WriteOff запись по FIFO
    2. Reservation переводится в статус 'converted_to_sale'
    """
    REASON_CHOICES = WriteOff.REASON_CHOICES

    document = models.ForeignKey(
        WriteOffDocument,
        on_delete=models.CASCADE,
        related_name='items',
        verbose_name="Документ"
    )

    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name='writeoff_document_items',
        verbose_name="Товар"
    )

    quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        verbose_name="Количество"
    )

    reason = models.CharField(
        max_length=20,
        choices=REASON_CHOICES,
        default='damage',
        verbose_name="Причина списания"
    )

    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Примечания"
    )

    # Резерв (создается автоматически при добавлении в черновик)
    reservation = models.OneToOneField(
        Reservation,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='writeoff_document_item_reverse',
        verbose_name="Резерв"
    )

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Позиция документа списания"
        verbose_name_plural = "Позиции документа списания"
        ordering = ['id']
        indexes = [
            models.Index(fields=['document']),
            models.Index(fields=['product']),
        ]

    def __str__(self):
        return f"{self.product.name}: {self.quantity} шт ({self.get_reason_display()})"

    @property
    def total_cost(self):
        """Себестоимость позиции (средневзвешенная из cost_price товара)"""
        return self.quantity * (self.product.cost_price or Decimal('0'))


class IncomingDocument(models.Model):
    """
    Документ поступления товара на склад.

    Сценарий использования:
    1. Создается черновик (draft)
    2. В течение дня добавляются товары (IncomingDocumentItem)
    3. В конце смены документ проводится (confirmed) → создается StockBatch напрямую
    4. Stock автоматически обновляется
    """
    STATUS_CHOICES = [
        ('draft', 'Черновик'),
        ('confirmed', 'Проведён'),
        ('cancelled', 'Отменён'),
    ]

    RECEIPT_TYPE_CHOICES = [
        ('supplier', 'Поступление от поставщика'),
        ('inventory', 'Оприходование при инвентаризации'),
        ('adjustment', 'Оприходование без инвентаризации'),
    ]

    document_number = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        verbose_name="Номер документа"
    )

    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        related_name='incoming_documents',
        verbose_name="Склад"
    )

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='draft',
        db_index=True,
        verbose_name="Статус"
    )

    date = models.DateField(
        verbose_name="Дата документа",
        help_text="Дата, к которой относится поступление"
    )

    receipt_type = models.CharField(
        max_length=20,
        choices=RECEIPT_TYPE_CHOICES,
        default='supplier',
        db_index=True,
        verbose_name="Тип поступления"
    )

    supplier_name = models.CharField(
        max_length=200,
        blank=True,
        null=True,
        verbose_name="Наименование поставщика",
        help_text="Заполняется для типа 'Поступление от поставщика'"
    )

    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Примечания"
    )

    # Связь с инвентаризацией
    inventory = models.ForeignKey(
        'Inventory',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='incoming_documents',
        verbose_name="Инвентаризация"
    )

    # Аудит
    created_by = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_incoming_documents',
        verbose_name="Создал"
    )

    confirmed_by = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='confirmed_incoming_documents',
        verbose_name="Провёл"
    )

    confirmed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Дата проведения"
    )

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлён")

    class Meta:
        verbose_name = "Документ поступления"
        verbose_name_plural = "Документы поступления"
        ordering = ['-date', '-created_at']
        indexes = [
            models.Index(fields=['document_number']),
            models.Index(fields=['warehouse', 'status']),
            models.Index(fields=['date']),
            models.Index(fields=['receipt_type']),
            models.Index(fields=['-created_at']),
        ]

    def __str__(self):
        return f"{self.document_number} ({self.get_status_display()})"

    @property
    def total_quantity(self):
        """Общее количество товаров в документе"""
        return self.items.aggregate(
            total=models.Sum('quantity')
        )['total'] or Decimal('0')

    @property
    def total_cost(self):
        """Общая себестоимость поступления"""
        return sum(item.total_cost for item in self.items.select_related('product'))

    @property
    def can_edit(self):
        """Можно ли редактировать документ"""
        return self.status == 'draft'

    @property
    def can_confirm(self):
        """Можно ли провести документ"""
        return self.status == 'draft' and self.items.exists()

    @property
    def can_cancel(self):
        """Можно ли отменить документ"""
        return self.status == 'draft'


class IncomingDocumentItem(models.Model):
    """
    Строка документа поступления.

    При создании:
    - Товар добавляется в черновик документа
    - Резервирование НЕ создается (товар еще не поступил)

    При проведении документа:
    1. Для каждой позиции напрямую создается StockBatch
    2. Stock автоматически обновляется
    """
    document = models.ForeignKey(
        IncomingDocument,
        on_delete=models.CASCADE,
        related_name='items',
        verbose_name="Документ"
    )

    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name='incoming_document_items',
        verbose_name="Товар"
    )

    quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        verbose_name="Количество"
    )

    cost_price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name="Закупочная цена"
    )

    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Примечания"
    )

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлён")

    class Meta:
        verbose_name = "Позиция документа поступления"
        verbose_name_plural = "Позиции документа поступления"
        ordering = ['id']
        indexes = [
            models.Index(fields=['document']),
            models.Index(fields=['product']),
        ]

    def __str__(self):
        return f"{self.product.name}: {self.quantity} шт @ {self.cost_price}"

    @property
    def total_cost(self):
        """Себестоимость позиции (quantity * cost_price)"""
        return self.quantity * self.cost_price


class Transformation(models.Model):
    """
    Документ трансформации товара (превращение одного товара в другой).

    Пример: белая гипсофила → крашеная гипсофила
    """
    STATUS_CHOICES = [
        ('draft', 'Черновик'),
        ('completed', 'Проведён'),
        ('cancelled', 'Отменён'),
    ]

    document_number = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        verbose_name="Номер документа"
    )

    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        related_name='transformations',
        verbose_name="Склад"
    )

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='draft',
        db_index=True,
        verbose_name="Статус"
    )

    date = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Дата создания"
    )

    employee = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transformations',
        verbose_name="Сотрудник"
    )

    comment = models.TextField(
        blank=True,
        verbose_name="Комментарий"
    )

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлён")

    class Meta:
        verbose_name = "Трансформация товара"
        verbose_name_plural = "Трансформации товаров"
        ordering = ['-date']
        indexes = [
            models.Index(fields=['document_number']),
            models.Index(fields=['warehouse', 'status']),
            models.Index(fields=['-date']),
        ]

    def __str__(self):
        return f"{self.document_number} ({self.get_status_display()})"


class TransformationInput(models.Model):
    """
    Входной товар трансформации (что списываем).
    """
    transformation = models.ForeignKey(
        Transformation,
        on_delete=models.CASCADE,
        related_name='inputs',
        verbose_name="Трансформация"
    )

    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name='transformation_inputs',
        verbose_name="Товар"
    )

    quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        verbose_name="Количество"
    )

    # Резерв (создается автоматически при draft)
    # Связь через Reservation.transformation_input

    class Meta:
        verbose_name = "Входной товар трансформации"
        verbose_name_plural = "Входные товары трансформации"

    def __str__(self):
        return f"{self.product.name}: {self.quantity}"


class TransformationOutput(models.Model):
    """
    Выходной товар трансформации (что получаем).
    """
    transformation = models.ForeignKey(
        Transformation,
        on_delete=models.CASCADE,
        related_name='outputs',
        verbose_name="Трансформация"
    )

    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name='transformation_outputs',
        verbose_name="Товар"
    )

    quantity = models.DecimalField(
        max_digits=10,
        decimal_places=3,
        verbose_name="Количество"
    )

    # Ссылка на созданную партию (после проведения)
    stock_batch = models.ForeignKey(
        StockBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transformation_outputs',
        verbose_name="Созданная партия"
    )

    class Meta:
        verbose_name = "Выходной товар трансформации"
        verbose_name_plural = "Выходные товары трансформации"

    def __str__(self):
        return f"{self.product.name}: {self.quantity}"
