Исправление ошибок в редактировании комплектов: валидация, верстка, расчет цены
This commit is contained in:
@@ -163,7 +163,11 @@ class ProductKit(BaseProductEntity):
|
||||
total = Decimal('0')
|
||||
for item in self.kit_items.all():
|
||||
qty = item.quantity or Decimal('1')
|
||||
if item.product:
|
||||
if item.sales_unit:
|
||||
# Для sales_unit используем цену единицы продажи
|
||||
unit_price = item.sales_unit.actual_price or Decimal('0')
|
||||
total += unit_price * qty
|
||||
elif item.product:
|
||||
# Используем зафиксированную цену если есть, иначе актуальную цену товара
|
||||
if item.unit_price is not None:
|
||||
unit_price = item.unit_price
|
||||
@@ -213,7 +217,11 @@ class ProductKit(BaseProductEntity):
|
||||
# Пересчитаем базовую цену из компонентов
|
||||
total = Decimal('0')
|
||||
for item in self.kit_items.all():
|
||||
if item.product:
|
||||
if item.sales_unit:
|
||||
actual_price = item.sales_unit.actual_price or Decimal('0')
|
||||
qty = item.quantity or Decimal('1')
|
||||
total += actual_price * qty
|
||||
elif item.product:
|
||||
actual_price = item.product.actual_price or Decimal('0')
|
||||
qty = item.quantity or Decimal('1')
|
||||
total += actual_price * qty
|
||||
@@ -382,8 +390,8 @@ class ProductKit(BaseProductEntity):
|
||||
|
||||
class KitItem(models.Model):
|
||||
"""
|
||||
Состав комплекта: связь между ProductKit и Product или ProductVariantGroup.
|
||||
Позиция может быть либо конкретным товаром, либо группой вариантов.
|
||||
Состав комплекта: связь между ProductKit и Product, ProductVariantGroup или ProductSalesUnit.
|
||||
Позиция может быть либо конкретным товаром, либо группой вариантов, либо конкретной единицей продажи.
|
||||
"""
|
||||
kit = models.ForeignKey(ProductKit, on_delete=models.CASCADE, related_name='kit_items',
|
||||
verbose_name="Комплект")
|
||||
@@ -403,6 +411,14 @@ class KitItem(models.Model):
|
||||
related_name='kit_items',
|
||||
verbose_name="Группа вариантов"
|
||||
)
|
||||
sales_unit = models.ForeignKey(
|
||||
'ProductSalesUnit',
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='kit_items',
|
||||
verbose_name="Единица продажи"
|
||||
)
|
||||
quantity = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True, verbose_name="Количество")
|
||||
unit_price = models.DecimalField(
|
||||
max_digits=10,
|
||||
@@ -428,21 +444,46 @@ class KitItem(models.Model):
|
||||
return f"{self.kit.name} - {self.get_display_name()}"
|
||||
|
||||
def clean(self):
|
||||
"""Валидация: должен быть указан либо product, либо variant_group (но не оба)"""
|
||||
if self.product and self.variant_group:
|
||||
raise ValidationError(
|
||||
"Нельзя указывать одновременно товар и группу вариантов. Выберите что-то одно."
|
||||
)
|
||||
if not self.product and not self.variant_group:
|
||||
"""Валидация: должна быть указана группа вариантов ИЛИ (товар [плюс опционально единица продажи])"""
|
||||
|
||||
has_variant = bool(self.variant_group)
|
||||
has_product = bool(self.product)
|
||||
has_sales_unit = bool(self.sales_unit)
|
||||
|
||||
# 1. Проверка на пустоту
|
||||
if not (has_variant or has_product or has_sales_unit):
|
||||
raise ValidationError(
|
||||
"Необходимо указать либо товар, либо группу вариантов."
|
||||
)
|
||||
|
||||
# 2. Несовместимость: Группа вариантов VS Товар/Единица
|
||||
if has_variant and (has_product or has_sales_unit):
|
||||
raise ValidationError(
|
||||
"Нельзя указывать группу вариантов одновременно с товаром или единицей продажи."
|
||||
)
|
||||
|
||||
# 3. Зависимость: Если есть sales_unit, должен быть product
|
||||
if has_sales_unit and not has_product:
|
||||
raise ValidationError(
|
||||
"Если указана единица продажи, должен быть выбран соответствующий товар."
|
||||
)
|
||||
|
||||
# 4. Проверка принадлежности
|
||||
if has_sales_unit and has_product and self.sales_unit.product != self.product:
|
||||
raise ValidationError(
|
||||
"Выбранная единица продажи не принадлежит указанному товару."
|
||||
)
|
||||
|
||||
def get_display_name(self):
|
||||
"""Возвращает строку для отображения названия компонента"""
|
||||
if self.variant_group:
|
||||
# Приоритет: сначала единица продажи, затем товар, затем группа вариантов
|
||||
if self.sales_unit:
|
||||
return f"[Единица продажи] {self.sales_unit.name}"
|
||||
elif self.product:
|
||||
return self.product.name
|
||||
elif self.variant_group:
|
||||
return f"[Варианты] {self.variant_group.name}"
|
||||
return self.product.name if self.product else "Не указан"
|
||||
return "Не указан"
|
||||
|
||||
def has_priorities_set(self):
|
||||
"""Проверяет, настроены ли приоритеты замены для данного компонента"""
|
||||
@@ -452,10 +493,16 @@ class KitItem(models.Model):
|
||||
"""
|
||||
Возвращает список доступных товаров для этого компонента.
|
||||
|
||||
Если указана единица продажи - возвращает товар, к которому она относится.
|
||||
Если указан конкретный товар - возвращает его.
|
||||
Если указаны приоритеты - возвращает товары в порядке приоритета.
|
||||
Если не указаны приоритеты - возвращает все активные товары из группы вариантов.
|
||||
"""
|
||||
# Приоритет: сначала единица продажи, затем товар, затем группа вариантов
|
||||
if self.sales_unit:
|
||||
# Если указана единица продажи, возвращаем товар, к которому она относится
|
||||
return [self.sales_unit.product]
|
||||
|
||||
if self.product:
|
||||
# Если указан конкретный товар, возвращаем только его
|
||||
return [self.product]
|
||||
|
||||
@@ -141,6 +141,14 @@ class Product(BaseProductEntity):
|
||||
from ..services.cost_calculator import ProductCostCalculator
|
||||
return ProductCostCalculator.get_cost_details(self)
|
||||
|
||||
@property
|
||||
def kit_items_using_as_sales_unit(self):
|
||||
"""
|
||||
Возвращает QuerySet KitItem, где этот товар используется как единица продажи.
|
||||
"""
|
||||
from .kits import KitItem
|
||||
return KitItem.objects.filter(sales_unit__product=self)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Используем сервис для подготовки к сохранению
|
||||
ProductSaveService.prepare_product_for_save(self)
|
||||
|
||||
Reference in New Issue
Block a user