from django.db import models from django.core.exceptions import ValidationError from products.models import Product, ProductKit from simple_history.models import HistoricalRecords from .order import Order class OrderItem(models.Model): """ Позиция (товар) в заказе. Хранит информацию о товаре или комплекте, количестве и цене на момент заказа. """ order = models.ForeignKey( Order, on_delete=models.CASCADE, related_name='items', verbose_name="Заказ" ) # Товар или комплект (один из двух должен быть заполнен) product = models.ForeignKey( Product, on_delete=models.PROTECT, null=True, blank=True, related_name='order_items', verbose_name="Товар" ) product_kit = models.ForeignKey( ProductKit, on_delete=models.PROTECT, null=True, blank=True, related_name='order_items', verbose_name="Комплект товаров" ) quantity = models.PositiveIntegerField( default=1, verbose_name="Количество" ) price = models.DecimalField( max_digits=10, decimal_places=2, verbose_name="Цена за единицу", help_text="Цена на момент создания заказа (фиксируется)" ) is_custom_price = models.BooleanField( default=False, verbose_name="Цена изменена вручную", help_text="True если цена была изменена вручную при создании заказа" ) # Витринные продажи is_from_showcase = models.BooleanField( default=False, verbose_name="С витрины", help_text="True если товар продан с витрины" ) showcase = models.ForeignKey( 'inventory.Showcase', on_delete=models.SET_NULL, null=True, blank=True, related_name='order_items', verbose_name="Витрина", help_text="Витрина, с которой был продан товар" ) # Временные метки created_at = models.DateTimeField( auto_now_add=True, verbose_name="Дата добавления" ) # История изменений history = HistoricalRecords() class Meta: verbose_name = "Позиция заказа" verbose_name_plural = "Позиции заказа" indexes = [ models.Index(fields=['order']), models.Index(fields=['product']), models.Index(fields=['product_kit']), models.Index(fields=['is_from_showcase']), models.Index(fields=['showcase']), ] def __str__(self): item_name = "" if self.product: item_name = self.product.name elif self.product_kit: item_name = self.product_kit.name return f"{item_name} x{self.quantity} в заказе #{self.order.order_number}" def clean(self): """Валидация модели""" super().clean() # Проверка: должен быть заполнен либо product, либо product_kit if not self.product and not self.product_kit: raise ValidationError( 'Необходимо указать либо товар, либо комплект товаров' ) # Проверка: не должны быть заполнены оба поля одновременно if self.product and self.product_kit: raise ValidationError( 'Нельзя указать одновременно и товар, и комплект' ) def save(self, *args, **kwargs): # Автоматически фиксируем цену при создании, если она не указана if not self.price: if self.product: self.price = self.product.price elif self.product_kit: self.price = self.product_kit.price super().save(*args, **kwargs) def get_total_price(self): """Возвращает общую стоимость позиции""" return self.price * self.quantity @property def item_name(self): """Название товара/комплекта""" if self.product: return self.product.name elif self.product_kit: return self.product_kit.name return "Не указано" @property def original_price(self): """Оригинальная цена товара/комплекта из каталога""" if self.product: return self.product.actual_price elif self.product_kit: return self.product_kit.actual_price return None @property def price_difference(self): """Разница между установленной ценой и оригинальной""" if self.is_custom_price and self.original_price: return self.price - self.original_price return None