fix: Улучшения системы ценообразования комплектов

Исправлены 4 проблемы:
1. Расчёт цены первого товара - улучшена валидация в getProductPrice и calculateFinalPrice
2. Отображение actual_price в Select2 вместо обычной цены
3. Количество по умолчанию = 1 для новых форм компонентов
4. Auto-select текста при клике на поле количества для удобства редактирования

Изменённые файлы:
- products/forms.py: добавлен __init__ в KitItemForm для quantity.initial = 1
- products/templates/includes/select2-product-init.html: обновлена formatSelectResult
- products/templates/productkit_create.html: добавлен focus handler для auto-select
- products/templates/productkit_edit.html: добавлен focus handler для auto-select

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-02 19:04:03 +03:00
parent c84a372f98
commit 6c8af5ab2c
120 changed files with 9035 additions and 3036 deletions

View File

@@ -0,0 +1,102 @@
"""
Модели для работы с группами вариантов товаров.
Позволяет группировать взаимозаменяемые товары (например, розы разной длины).
"""
from django.db import models
class ProductVariantGroup(models.Model):
"""
Группа вариантов товара (взаимозаменяемые товары).
Например: "Роза красная Freedom" включает розы 50см, 60см, 70см.
"""
name = models.CharField(max_length=200, verbose_name="Название")
description = 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 = ['name']
def __str__(self):
return self.name
def get_products_count(self):
"""Возвращает количество товаров в группе"""
return self.items.count()
@property
def in_stock(self):
"""
Вариант в наличии, если хотя бы один из его товаров в наличии.
Товар в наличии, если Product.in_stock = True.
"""
return self.items.filter(product__in_stock=True).exists()
@property
def price(self):
"""
Цена варианта определяется по приоритету товаров:
1. Берётся цена товара с приоритетом 1, если он в наличии
2. Если нет - цена товара с приоритетом 2
3. И так далее по приоритетам
4. Если ни один товар не в наличии - берётся самый дорогой товар из группы
Возвращает Decimal (цену) или None если группа пуста.
"""
items = self.items.all().order_by('priority', 'id')
if not items.exists():
return None
# Ищем первый товар в наличии
for item in items:
if item.product.in_stock:
return item.product.sale_price
# Если ни один товар не в наличии - берем самый дорогой
max_price = None
for item in items:
if max_price is None or item.product.sale_price > max_price:
max_price = item.product.sale_price
return max_price
class ProductVariantGroupItem(models.Model):
"""
Товар в группе вариантов с приоритетом для этой конкретной группы.
Приоритет определяет порядок выбора товара при использовании группы в комплектах.
Например: в группе "Роза красная Freedom" - роза 50см имеет приоритет 1, 60см = 2, 70см = 3.
"""
variant_group = models.ForeignKey(
ProductVariantGroup,
on_delete=models.CASCADE,
related_name='items',
verbose_name="Группа вариантов"
)
product = models.ForeignKey(
'Product',
on_delete=models.CASCADE,
related_name='variant_group_items',
verbose_name="Товар"
)
priority = models.PositiveIntegerField(
default=0,
help_text="Меньше = выше приоритет (1 - наивысший приоритет в этой группе)"
)
class Meta:
verbose_name = "Товар в группе вариантов"
verbose_name_plural = "Товары в группах вариантов"
ordering = ['priority', 'id']
unique_together = [['variant_group', 'product']]
indexes = [
models.Index(fields=['variant_group', 'priority']),
models.Index(fields=['product']),
]
def __str__(self):
return f"{self.variant_group.name} - {self.product.name} (приоритет {self.priority})"