Исправлены 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>
199 lines
7.7 KiB
Markdown
199 lines
7.7 KiB
Markdown
# Быстрый гид: Динамическая себестоимость товаров
|
||
|
||
## Как это работает
|
||
|
||
Себестоимость товара теперь **автоматически рассчитывается** на основе партий товара (StockBatch) по формуле средневзвешенной стоимости:
|
||
|
||
```
|
||
cost_price = Σ(количество × стоимость) / Σ(количество)
|
||
```
|
||
|
||
## Автоматическое обновление
|
||
|
||
Себестоимость обновляется **автоматически** при:
|
||
- ✅ Создании новой партии (поступление товара)
|
||
- ✅ Изменении количества в партии
|
||
- ✅ Изменении стоимости партии
|
||
- ✅ Удалении партии
|
||
|
||
**Никаких дополнительных действий не требуется!**
|
||
|
||
## Просмотр деталей
|
||
|
||
### На странице товара
|
||
|
||
1. Откройте страницу товара: `http://grach.localhost:8000/products/1/`
|
||
2. Найдите строку "Себестоимость"
|
||
3. Нажмите кнопку **"Детали расчета"**
|
||
4. Увидите:
|
||
- Кешированную стоимость (из БД)
|
||
- Рассчитанную стоимость (из партий)
|
||
- Таблицу с разбивкой по партиям
|
||
- Дату создания каждой партии
|
||
|
||
## Примеры сценариев
|
||
|
||
### Сценарий 1: Новый товар
|
||
```
|
||
Товар создан → cost_price = 0.00 (нет партий)
|
||
```
|
||
|
||
### Сценарий 2: Первая поставка
|
||
```
|
||
Поступление: 10 шт по 100 руб
|
||
→ Автоматически: cost_price = 100.00
|
||
```
|
||
|
||
### Сценарий 3: Вторая поставка
|
||
```
|
||
Текущее: 10 шт по 100 руб (cost_price = 100.00)
|
||
Поступление: 10 шт по 120 руб
|
||
→ Автоматически: cost_price = 110.00
|
||
Расчет: (10×100 + 10×120) / 20 = 110.00
|
||
```
|
||
|
||
### Сценарий 4: Товар закончился
|
||
```
|
||
Продажа: весь товар продан
|
||
→ Автоматически: cost_price = 0.00
|
||
```
|
||
|
||
### Сценарий 5: Новая поставка после опустошения
|
||
```
|
||
Поступление: 15 шт по 130 руб
|
||
→ Автоматически: cost_price = 130.00
|
||
```
|
||
|
||
## Ручной пересчет (если нужно)
|
||
|
||
Если по какой-то причине себестоимость "слетела", можно пересчитать вручную:
|
||
|
||
```bash
|
||
# Пересчитать для тенанта grach
|
||
python manage.py recalculate_product_costs --schema=grach
|
||
|
||
# С подробным выводом
|
||
python manage.py recalculate_product_costs --schema=grach --verbose
|
||
|
||
# Предварительный просмотр без сохранения
|
||
python manage.py recalculate_product_costs --schema=grach --dry-run --verbose
|
||
|
||
# Показать только изменившиеся товары
|
||
python manage.py recalculate_product_costs --schema=grach --only-changed
|
||
```
|
||
|
||
## Влияние на комплекты (ProductKit)
|
||
|
||
Стоимость комплектов теперь автоматически учитывает актуальную себестоимость компонентов!
|
||
|
||
```python
|
||
# Раньше: использовалась статическая стоимость
|
||
# Теперь: использует динамическую стоимость из партий
|
||
kit_cost = sum(component.cost_price × quantity)
|
||
```
|
||
|
||
## Проверка синхронизации
|
||
|
||
На странице товара в секции "Детали расчета":
|
||
- 🟢 **Зеленый статус** - все синхронизировано
|
||
- 🟡 **Желтый статус** - требуется синхронизация (запустите команду пересчета)
|
||
|
||
## API для разработчиков
|
||
|
||
### Получить детали расчета
|
||
|
||
```python
|
||
from products.models import Product
|
||
|
||
product = Product.objects.get(id=1)
|
||
|
||
# Получить детали
|
||
details = product.cost_price_details
|
||
|
||
print(f"Кешированная стоимость: {details['cached_cost']}")
|
||
print(f"Рассчитанная стоимость: {details['calculated_cost']}")
|
||
print(f"Синхронизировано: {details['is_synced']}")
|
||
print(f"Всего в партиях: {details['total_quantity']}")
|
||
|
||
# Перебрать партии
|
||
for batch in details['batches']:
|
||
print(f"Склад: {batch['warehouse_name']}")
|
||
print(f"Количество: {batch['quantity']}")
|
||
print(f"Стоимость: {batch['cost_price']}")
|
||
```
|
||
|
||
### Ручное обновление стоимости
|
||
|
||
```python
|
||
from products.services.cost_calculator import ProductCostCalculator
|
||
|
||
# Рассчитать новую стоимость
|
||
new_cost = ProductCostCalculator.calculate_weighted_average_cost(product)
|
||
|
||
# Обновить в БД
|
||
old_cost, new_cost, was_updated = ProductCostCalculator.update_product_cost(product)
|
||
|
||
if was_updated:
|
||
print(f"Стоимость обновлена: {old_cost} → {new_cost}")
|
||
```
|
||
|
||
## Логирование
|
||
|
||
Все операции логируются в стандартный Django logger:
|
||
|
||
```python
|
||
import logging
|
||
logger = logging.getLogger('products.services.cost_calculator')
|
||
```
|
||
|
||
Примеры сообщений:
|
||
- `INFO: Обновлена себестоимость товара SKU-001: 100.00 -> 110.00`
|
||
- `ERROR: Ошибка при расчете себестоимости для товара SKU-001: ...`
|
||
|
||
## Производительность
|
||
|
||
### Чтение cost_price
|
||
- **0 дополнительных запросов** - значение читается из БД
|
||
|
||
### Создание/изменение партии
|
||
- **1 дополнительный UPDATE** - автоматическое обновление cost_price
|
||
|
||
### Просмотр деталей (cost_price_details)
|
||
- **1 SELECT** - запрос партий товара
|
||
|
||
## FAQ
|
||
|
||
**Q: Нужно ли что-то делать после создания партии?**
|
||
A: Нет! Себестоимость обновляется автоматически через Django signals.
|
||
|
||
**Q: Что если у товара нет партий?**
|
||
A: cost_price = 0.00 (автоматически)
|
||
|
||
**Q: Можно ли вручную установить себестоимость?**
|
||
A: Можно, но при следующем изменении партий значение пересчитается автоматически.
|
||
|
||
**Q: Как проверить правильность расчета?**
|
||
A: Откройте "Детали расчета" на странице товара - там видна вся математика.
|
||
|
||
**Q: Влияет ли это на ProductKit?**
|
||
A: Да! Стоимость комплектов теперь использует актуальную себестоимость компонентов.
|
||
|
||
**Q: Что если синхронизация нарушилась?**
|
||
A: Запустите `python manage.py recalculate_product_costs --schema=grach`
|
||
|
||
## Техническая документация
|
||
|
||
Подробная техническая документация доступна в файле:
|
||
`DYNAMIC_COST_PRICE_IMPLEMENTATION.md`
|
||
|
||
## Контакты и поддержка
|
||
|
||
При возникновении проблем проверьте:
|
||
1. Логи Django (ошибки при расчете)
|
||
2. Страницу товара (секция "Детали расчета")
|
||
3. Запустите команду с --dry-run для проверки
|
||
|
||
---
|
||
Версия: 1.0
|
||
Дата: 2025-01-01
|