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:
279
test_variant_stock.py
Normal file
279
test_variant_stock.py
Normal file
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Скрипт для тестирования системы наличия товаров и цен вариантов.
|
||||
Проверяет:
|
||||
1. Обновление Product.in_stock при создании Stock
|
||||
2. Свойство ProductVariantGroup.in_stock (вариант в наличии если хотя бы один товар в наличии)
|
||||
3. Свойство ProductVariantGroup.price (берётся цена по приоритету)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
|
||||
# Добавляем путь к myproject
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'myproject'))
|
||||
os.chdir(os.path.join(os.path.dirname(__file__), 'myproject'))
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
|
||||
django.setup()
|
||||
|
||||
from decimal import Decimal
|
||||
from products.models import Product, ProductVariantGroup, ProductVariantGroupItem
|
||||
from inventory.models import Stock, StockBatch, Warehouse, Incoming, IncomingBatch
|
||||
from django.utils import timezone
|
||||
|
||||
def clear_data():
|
||||
"""Очищаем тестовые данные"""
|
||||
Product.objects.all().delete()
|
||||
ProductVariantGroup.objects.all().delete()
|
||||
Stock.objects.all().delete()
|
||||
StockBatch.objects.all().delete()
|
||||
Warehouse.objects.all().delete()
|
||||
IncomingBatch.objects.all().delete()
|
||||
print("OK Данные очищены")
|
||||
|
||||
def create_test_data():
|
||||
"""Создаём тестовые данные"""
|
||||
|
||||
# Создаём склад
|
||||
warehouse = Warehouse.objects.create(
|
||||
name="Основной склад",
|
||||
description="Тестовый склад",
|
||||
is_active=True,
|
||||
is_default=True
|
||||
)
|
||||
print(f"OK Создан склад: {warehouse.name}")
|
||||
|
||||
# Создаём товары (розы разной длины)
|
||||
products = []
|
||||
prices = [Decimal('50.00'), Decimal('60.00'), Decimal('70.00')]
|
||||
|
||||
for i, price in enumerate(prices, 1):
|
||||
product = Product.objects.create(
|
||||
name=f"Роза красная Freedom {50 + i*10}см",
|
||||
sku=f"ROSE-RED-{50 + i*10}",
|
||||
cost_price=Decimal('30.00'),
|
||||
sale_price=price,
|
||||
unit='шт',
|
||||
is_active=True,
|
||||
in_stock=False # По умолчанию нет в наличии
|
||||
)
|
||||
products.append(product)
|
||||
print(f"OK Создан товар: {product.name} (цена: {price}, in_stock={product.in_stock})")
|
||||
|
||||
# Создаём группу вариантов
|
||||
variant_group = ProductVariantGroup.objects.create(
|
||||
name="Роза красная Freedom",
|
||||
description="Розы разной высоты"
|
||||
)
|
||||
print(f"OK Создана группа вариантов: {variant_group.name}")
|
||||
|
||||
# Добавляем товары в группу с приоритетами
|
||||
items = []
|
||||
for priority, product in enumerate(products, 1):
|
||||
item = ProductVariantGroupItem.objects.create(
|
||||
variant_group=variant_group,
|
||||
product=product,
|
||||
priority=priority
|
||||
)
|
||||
items.append(item)
|
||||
print(f" - {product.name} (приоритет {priority})")
|
||||
|
||||
return warehouse, products, variant_group, items
|
||||
|
||||
def test_scenario_1():
|
||||
"""
|
||||
Тест 1: Создание товара в наличии и проверка автоматического обновления in_stock
|
||||
"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 1: Обновление Product.in_stock при создании Stock")
|
||||
print("="*80)
|
||||
|
||||
warehouse, products, variant_group, items = create_test_data()
|
||||
|
||||
# Получаем первый товар
|
||||
product = products[0]
|
||||
print(f"\nПроверяем товар: {product.name}")
|
||||
print(f"Текущий статус in_stock: {product.in_stock}")
|
||||
|
||||
# Создаём приход товара (это должно создать Stock и обновить in_stock)
|
||||
incoming_batch = IncomingBatch.objects.create(
|
||||
warehouse=warehouse,
|
||||
document_number="IN-0001",
|
||||
supplier_name="Тестовый поставщик"
|
||||
)
|
||||
print(f"\nOK Создана партия поступления: {incoming_batch.document_number}")
|
||||
|
||||
# Добавляем товар в приход
|
||||
incoming = Incoming.objects.create(
|
||||
batch=incoming_batch,
|
||||
product=product,
|
||||
quantity=Decimal('100.00'),
|
||||
cost_price=product.cost_price
|
||||
)
|
||||
print(f"OK Добавлен товар в приход: {incoming.quantity} шт")
|
||||
|
||||
# Проверяем что Stock был создан и in_stock обновлён
|
||||
stock = Stock.objects.get(product=product, warehouse=warehouse)
|
||||
print(f"\nOK Stock создан:")
|
||||
print(f" - quantity_available: {stock.quantity_available}")
|
||||
print(f" - quantity_reserved: {stock.quantity_reserved}")
|
||||
print(f" - quantity_free: {stock.quantity_free}")
|
||||
|
||||
# Обновляем товар из БД чтобы получить новое значение
|
||||
product.refresh_from_db()
|
||||
print(f"\nOK Product.in_stock обновлён: {product.in_stock}")
|
||||
|
||||
if product.in_stock:
|
||||
print("PASS: ТЕСТ 1 ПРОЙДЕН: Product.in_stock = True")
|
||||
else:
|
||||
print("FAIL: ТЕСТ 1 ПРОВАЛЕН: Product.in_stock должен быть True")
|
||||
|
||||
return warehouse, products, variant_group, items
|
||||
|
||||
def test_scenario_2(warehouse, products, variant_group, items):
|
||||
"""
|
||||
Тест 2: Проверка свойства ProductVariantGroup.in_stock
|
||||
"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 2: Свойство ProductVariantGroup.in_stock")
|
||||
print("="*80)
|
||||
|
||||
# Обновляем товары из БД
|
||||
for product in products:
|
||||
product.refresh_from_db()
|
||||
|
||||
print(f"\nГруппа вариантов: {variant_group.name}")
|
||||
print(f"Товары в группе:")
|
||||
for item in variant_group.items.all():
|
||||
print(f" - {item.product.name} (приоритет {item.priority}, in_stock={item.product.in_stock})")
|
||||
|
||||
# Первый товар в наличии, поэтому вариант должен быть в наличии
|
||||
print(f"\nСвойство variant_group.in_stock: {variant_group.in_stock}")
|
||||
|
||||
if variant_group.in_stock:
|
||||
print("PASS: ТЕСТ 2 ПРОЙДЕН: Вариант в наличии (хотя бы один товар доступен)")
|
||||
else:
|
||||
print("FAIL: ТЕСТ 2 ПРОВАЛЕН: Вариант должен быть в наличии")
|
||||
|
||||
def test_scenario_3(warehouse, products, variant_group, items):
|
||||
"""
|
||||
Тест 3: Проверка свойства ProductVariantGroup.price
|
||||
"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 3: Свойство ProductVariantGroup.price")
|
||||
print("="*80)
|
||||
|
||||
print(f"\nГруппа вариантов: {variant_group.name}")
|
||||
|
||||
# Обновляем товары из БД
|
||||
for product in products:
|
||||
product.refresh_from_db()
|
||||
|
||||
print(f"Товары в приоритете:")
|
||||
for item in variant_group.items.all().order_by('priority'):
|
||||
status = "OK В наличии" if item.product.in_stock else "NO Нет в наличии"
|
||||
print(f" {item.priority}. {item.product.name} - {item.product.sale_price} руб {status}")
|
||||
|
||||
# Цена должна быть из первого в наличии (приоритет 1, цена 50.00)
|
||||
price = variant_group.price
|
||||
expected_price = Decimal('50.00')
|
||||
|
||||
print(f"\nЦена варианта: {price} руб")
|
||||
print(f"Ожидаемая цена: {expected_price} руб")
|
||||
|
||||
if price == expected_price:
|
||||
print("PASS: ТЕСТ 3 ПРОЙДЕН: Берётся цена товара с приоритетом 1")
|
||||
else:
|
||||
print(f"FAIL: ТЕСТ 3 ПРОВАЛЕН: Цена должна быть {expected_price}, получена {price}")
|
||||
|
||||
def test_scenario_4():
|
||||
"""
|
||||
Тест 4: Проверка цены когда нет товара в наличии (должна быть максимальная)
|
||||
"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 4: Цена варианта когда ни один товар не в наличии")
|
||||
print("="*80)
|
||||
|
||||
# Очищаем данные
|
||||
clear_data()
|
||||
|
||||
# Создаём новые данные без Stock (товары не в наличии)
|
||||
warehouse = Warehouse.objects.create(
|
||||
name="Тестовый склад",
|
||||
is_active=True,
|
||||
is_default=True
|
||||
)
|
||||
|
||||
# Создаём товары с разными ценами
|
||||
products = []
|
||||
prices = [Decimal('100.00'), Decimal('150.00'), Decimal('200.00')]
|
||||
|
||||
for i, price in enumerate(prices, 1):
|
||||
product = Product.objects.create(
|
||||
name=f"Товар {i}",
|
||||
sku=f"PRODUCT-{i}",
|
||||
cost_price=Decimal('50.00'),
|
||||
sale_price=price,
|
||||
unit='шт',
|
||||
is_active=True,
|
||||
in_stock=False # Нет в наличии
|
||||
)
|
||||
products.append(product)
|
||||
|
||||
# Создаём группу вариантов
|
||||
variant_group = ProductVariantGroup.objects.create(
|
||||
name="Группа товаров без наличия"
|
||||
)
|
||||
|
||||
# Добавляем товары в группу
|
||||
for priority, product in enumerate(products, 1):
|
||||
ProductVariantGroupItem.objects.create(
|
||||
variant_group=variant_group,
|
||||
product=product,
|
||||
priority=priority
|
||||
)
|
||||
|
||||
print(f"\nГруппа: {variant_group.name}")
|
||||
print(f"Товары (все без наличия):")
|
||||
for item in variant_group.items.all().order_by('priority'):
|
||||
print(f" {item.priority}. {item.product.name} - {item.product.sale_price} руб (in_stock={item.product.in_stock})")
|
||||
|
||||
# Цена должна быть максимальная = 200.00
|
||||
price = variant_group.price
|
||||
expected_price = Decimal('200.00')
|
||||
|
||||
print(f"\nЦена варианта (максимальная): {price} руб")
|
||||
print(f"Ожидаемая цена (максимальная): {expected_price} руб")
|
||||
|
||||
if price == expected_price:
|
||||
print("PASS: ТЕСТ 4 ПРОЙДЕН: Берётся максимальная цена из товаров")
|
||||
else:
|
||||
print(f"FAIL: ТЕСТ 4 ПРОВАЛЕН: Цена должна быть {expected_price}, получена {price}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("\n" + "="*80)
|
||||
print("= ТЕСТИРОВАНИЕ СИСТЕМЫ НАЛИЧИЯ ТОВАРОВ И ЦЕН ВАРИАНТОВ")
|
||||
print("="*80)
|
||||
|
||||
try:
|
||||
# Очищаем старые данные
|
||||
clear_data()
|
||||
|
||||
# Тесты 1-3
|
||||
warehouse, products, variant_group, items = test_scenario_1()
|
||||
test_scenario_2(warehouse, products, variant_group, items)
|
||||
test_scenario_3(warehouse, products, variant_group, items)
|
||||
|
||||
# Тест 4
|
||||
test_scenario_4()
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("= ТЕСТИРОВАНИЕ ЗАВЕРШЕНО")
|
||||
print("="*80 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nОШИБКА: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
Reference in New Issue
Block a user