# Быстрая справка: Наличие товаров и цены вариантов ## В Python коде ### Product (товар) ```python from products.models import Product product = Product.objects.get(id=1) # Проверить есть ли в наличии if product.in_stock: print(f"{product.name} - в наличии") # Получить цену print(product.sale_price) # Фильтровать товары в наличии in_stock = Product.objects.filter(in_stock=True) out_of_stock = Product.objects.filter(in_stock=False) ``` ### ProductVariantGroup (группа вариантов) ```python from products.models import ProductVariantGroup group = ProductVariantGroup.objects.prefetch_related('items__product').get(id=1) # Проверить есть ли в наличии хотя бы один вариант if group.in_stock: print(f"Группа '{group.name}' - есть в наличии") # Получить цену группы price = group.price # Decimal('50.00') # Перебрать товары по приоритету for item in group.items.all().order_by('priority'): print(f"{item.priority}. {item.product.name}") if item.product.in_stock: print(f" -> В наличии ({item.product.sale_price} руб)") else: print(f" -> Не в наличии") ``` --- ## В шаблонах (HTML) ### Проверка наличия товара ```html {% if product.in_stock %} В наличии {% else %} Нет в наличии {% endif %} ``` ### Отображение цены товара ```html
{{ product.sale_price }} руб
``` ### Группа вариантов - статус наличия ```html {% if variant_group.in_stock %}

Доступно - есть варианты в наличии

{% else %}

Недоступно - все варианты закончились

{% endif %} ``` ### Группа вариантов - цена ```html
Цена: {{ variant_group.price }} руб
``` ### Список товаров в группе ```html {% for item in variant_group.items.all %} {% endfor %}
Приоритет Товар Цена Статус
{{ item.priority }} {{ item.product.name }} {{ item.product.sale_price }} руб {% if item.product.in_stock %} В наличии {% else %} Нет {% endif %}
``` ### Полный пример - карточка варианта ```html

{{ variant_group.name }}

{% if variant_group.in_stock %} ✓ В наличии {% else %} ✗ Нет в наличии {% endif %}
{{ variant_group.price }} руб
Доступные варианты:
{% if variant_group.in_stock %} {% else %} {% endif %}
``` --- ## В View (запросы к БД) ### Оптимизация запросов ```python from django.shortcuts import render from products.models import ProductVariantGroup def variant_groups_list(request): # ПРАВИЛЬНО: используем prefetch_related для оптимизации groups = ProductVariantGroup.objects.prefetch_related( 'items__product' ) return render(request, 'variants.html', { 'variant_groups': groups }) ``` ### Фильтрация товаров в наличии ```python from products.models import Product # Получить только товары в наличии in_stock = Product.objects.filter(in_stock=True) # Получить только товары без наличия out_of_stock = Product.objects.filter(in_stock=False) # Комбинированный фильтр available = Product.objects.filter( is_active=True, in_stock=True ).order_by('name') ``` --- ## Логика наличия ### Когда товар считается "в наличии"? Товар в наличии (Product.in_stock = True) когда: - Существует запись в Stock с `quantity_available > 0` - Это может быть на любом из складов - quantity_available = quantity - reserved (свободный остаток) ### Когда он перестаёт быть в наличии? - Все Stock записи удалены - Или всем Stock записям quantity_available = 0 (все проданы или зарезервированы) ### Как это обновляется автоматически? 1. При создании приходного документа (Incoming) 2. При продаже товара (Sale) 3. При списании товара (WriteOff) 4. При изменении резервирования (Reservation) **Вы не должны вручную обновлять Product.in_stock!** --- ## Цена варианта - логика ### Порядок определения цены ProductVariantGroup: 1. **Есть товары в наличии?** - Да → берём цену товара с **наименьшим приоритетом** среди доступных - Пример: приоритет 1 доступен → его цена 2. **Нет товаров в наличии?** - Все недоступны → берём **максимальную цену** из всех товаров - Пример: цены 50, 60, 70 → показываем 70 (самая дорогая) ### Пример расчёта: ``` Группа "Роза красная Freedom" ├─ Приоритет 1: Роза 50см, цена 50 руб, в наличии ✓ ├─ Приоритет 2: Роза 60см, цена 60 руб, в наличии ✓ └─ Приоритет 3: Роза 70см, цена 70 руб, нет в наличии ✗ Цена группы = 50 руб (первый в наличии) ``` ``` Группа "Роза красная Freedom" ├─ Приоритет 1: Роза 50см, цена 50 руб, нет ✗ ├─ Приоритет 2: Роза 60см, цена 60 руб, нет ✗ └─ Приоритет 3: Роза 70см, цена 70 руб, нет ✗ Цена группы = 70 руб (максимальная из всех) ``` --- ## Типичные ошибки ❌ **Ошибка 1: Попытка обновить Product.in_stock вручную** ```python # НЕПРАВИЛЬНО! product.in_stock = True product.save() # Это будет перезаписано при следующем изменении Stock ``` ✅ **Правильно:** Система сама обновит Product.in_stock при изменении остатков. --- ❌ **Ошибка 2: Не использовать prefetch_related для вариантов** ```python # НЕПРАВИЛЬНО (N+1 query problem)! for group in groups: price = group.price # Это выполнит запрос для каждой группы! ``` ✅ **Правильно:** ```python groups = ProductVariantGroup.objects.prefetch_related('items__product') for group in groups: price = group.price # Всего 2 запроса вместо N+1 ``` --- ❌ **Ошибка 3: Фильтровать по in_stock на ProductVariantGroup** ```python # НЕПРАВИЛЬНО! groups = ProductVariantGroup.objects.filter(in_stock=True) # in_stock это свойство, а не поле БД ``` ✅ **Правильно:** ```python # Если нужны группы где есть хотя бы один товар в наличии groups = ProductVariantGroup.objects.filter( items__product__in_stock=True ).distinct() # Или отфильтровать в Python groups = [g for g in groups if g.in_stock] ``` --- ## Дополнительные полезные запросы ### Все товары без наличия ```python from products.models import Product out_of_stock = Product.objects.filter(in_stock=False) ``` ### Группы вариантов где нет ни одного товара в наличии ```python from django.db.models import Exists, OuterRef ProductVariantGroup.objects.filter( ~Exists(ProductVariantGroupItem.objects.filter( variant_group=OuterRef('pk'), product__in_stock=True )) ) ``` ### Товары которые изменили статус наличия за последний час ```python from django.utils import timezone from datetime import timedelta Product.objects.filter( updated_at__gte=timezone.now() - timedelta(hours=1) ) ``` --- ## Помощь и контакты Если что-то не работает: 1. Проверьте что миграция `0003_add_product_in_stock` применена 2. Убедитесь что сигналы зарегистрированы в `inventory/apps.py` 3. Проверьте логи: есть ли ошибки в сигналах при обновлении Stock 4. Запустите тестовый скрипт: `python test_variant_stock.py`