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:
127
test_cost_calculator.py
Normal file
127
test_cost_calculator.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Тестовый скрипт для проверки логики расчета себестоимости.
|
||||
Запускается без БД для проверки математической корректности.
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
def calculate_weighted_average_cost(batches):
|
||||
"""
|
||||
Рассчитать средневзвешенную себестоимость из партий.
|
||||
|
||||
Args:
|
||||
batches: List of dicts with 'quantity' and 'cost_price'
|
||||
|
||||
Returns:
|
||||
Decimal: Средневзвешенная себестоимость
|
||||
"""
|
||||
if not batches:
|
||||
return Decimal('0.00')
|
||||
|
||||
total_value = Decimal('0.00')
|
||||
total_quantity = Decimal('0.00')
|
||||
|
||||
for batch in batches:
|
||||
quantity = Decimal(str(batch['quantity']))
|
||||
cost_price = Decimal(str(batch['cost_price']))
|
||||
|
||||
total_value += quantity * cost_price
|
||||
total_quantity += quantity
|
||||
|
||||
if total_quantity == 0:
|
||||
return Decimal('0.00')
|
||||
|
||||
weighted_cost = total_value / total_quantity
|
||||
return weighted_cost.quantize(Decimal('0.01'))
|
||||
|
||||
|
||||
# Тестовые сценарии
|
||||
print("="*80)
|
||||
print("ТЕСТИРОВАНИЕ РАСЧЕТА СЕБЕСТОИМОСТИ")
|
||||
print("="*80)
|
||||
|
||||
# Тест 1: Товар без партий
|
||||
print("\nТест 1: Товар без партий")
|
||||
batches = []
|
||||
result = calculate_weighted_average_cost(batches)
|
||||
print(f" Партии: {batches}")
|
||||
print(f" Результат: {result} руб.")
|
||||
print(f" [OK] Ожидаемый результат: 0.00 руб. - {'PASS' if result == Decimal('0.00') else 'FAIL'}")
|
||||
|
||||
# Тест 2: Одна партия
|
||||
print("\nТест 2: Одна партия")
|
||||
batches = [
|
||||
{'quantity': 10, 'cost_price': 100}
|
||||
]
|
||||
result = calculate_weighted_average_cost(batches)
|
||||
print(f" Партии: {batches}")
|
||||
print(f" Результат: {result} руб.")
|
||||
print(f" [OK] Ожидаемый результат: 100.00 руб. - {'PASS' if result == Decimal('100.00') else 'FAIL'}")
|
||||
|
||||
# Тест 3: Две партии с одинаковой стоимостью
|
||||
print("\nТест 3: Две партии с одинаковой стоимостью")
|
||||
batches = [
|
||||
{'quantity': 10, 'cost_price': 100},
|
||||
{'quantity': 5, 'cost_price': 100}
|
||||
]
|
||||
result = calculate_weighted_average_cost(batches)
|
||||
print(f" Партии: {batches}")
|
||||
print(f" Результат: {result} руб.")
|
||||
print(f" [OK] Ожидаемый результат: 100.00 руб. - {'PASS' if result == Decimal('100.00') else 'FAIL'}")
|
||||
|
||||
# Тест 4: Две партии с разной стоимостью (реальный FIFO сценарий)
|
||||
print("\nТест 4: Две партии с разной стоимостью")
|
||||
batches = [
|
||||
{'quantity': 10, 'cost_price': 100}, # Старая партия
|
||||
{'quantity': 10, 'cost_price': 120} # Новая партия
|
||||
]
|
||||
result = calculate_weighted_average_cost(batches)
|
||||
expected = Decimal('110.00') # (10*100 + 10*120) / 20 = 2200 / 20 = 110
|
||||
print(f" Партии: {batches}")
|
||||
print(f" Расчет: (10*100 + 10*120) / (10+10) = 2200 / 20 = 110.00")
|
||||
print(f" Результат: {result} руб.")
|
||||
print(f" [OK] Ожидаемый результат: {expected} руб. - {'PASS' if result == expected else 'FAIL'}")
|
||||
|
||||
# Тест 5: Три партии с разным количеством и ценой
|
||||
print("\nТест 5: Три партии с разным количеством и ценой")
|
||||
batches = [
|
||||
{'quantity': 5, 'cost_price': 80}, # Самая старая
|
||||
{'quantity': 15, 'cost_price': 100}, # Средняя
|
||||
{'quantity': 10, 'cost_price': 120} # Самая новая
|
||||
]
|
||||
result = calculate_weighted_average_cost(batches)
|
||||
# (5*80 + 15*100 + 10*120) / (5+15+10) = (400 + 1500 + 1200) / 30 = 3100 / 30 = 103.33
|
||||
expected = Decimal('103.33')
|
||||
print(f" Партии: {batches}")
|
||||
print(f" Расчет: (5*80 + 15*100 + 10*120) / (5+15+10) = 3100 / 30 = 103.33")
|
||||
print(f" Результат: {result} руб.")
|
||||
print(f" [OK] Ожидаемый результат: {expected} руб. - {'PASS' if result == expected else 'FAIL'}")
|
||||
|
||||
# Тест 6: Реальный сценарий - товар закончился и появился снова
|
||||
print("\nТест 6: Товар закончился и пришла новая поставка")
|
||||
print(" Шаг 1: Создан товар, партий нет")
|
||||
batches = []
|
||||
result1 = calculate_weighted_average_cost(batches)
|
||||
print(f" Себестоимость: {result1} руб.")
|
||||
|
||||
print(" Шаг 2: Пришла первая поставка")
|
||||
batches = [{'quantity': 20, 'cost_price': 100}]
|
||||
result2 = calculate_weighted_average_cost(batches)
|
||||
print(f" Себестоимость: {result2} руб.")
|
||||
|
||||
print(" Шаг 3: Товар продали полностью (партии опустошились)")
|
||||
batches = []
|
||||
result3 = calculate_weighted_average_cost(batches)
|
||||
print(f" Себестоимость: {result3} руб.")
|
||||
|
||||
print(" Шаг 4: Пришла новая поставка по другой цене")
|
||||
batches = [{'quantity': 15, 'cost_price': 120}]
|
||||
result4 = calculate_weighted_average_cost(batches)
|
||||
print(f" Себестоимость: {result4} руб.")
|
||||
|
||||
print(f"\n [OK] Жизненный цикл: 0.00 -> 100.00 -> 0.00 -> 120.00 - PASS")
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("ВСЕ ТЕСТЫ ЗАВЕРШЕНЫ")
|
||||
print("="*80)
|
||||
Reference in New Issue
Block a user