refactor: подготовка к стандартизации Transfer моделей
Текущее состояние перед рефакторингом Transfer → TransferDocument. Все изменения с последнего коммита по улучшению системы поступлений. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ from decimal import Decimal
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from orders.models import Order, OrderItem
|
||||
from inventory.models import Reservation, Warehouse, Incoming, StockBatch, Sale, SaleBatchAllocation, Inventory, WriteOff, Stock, WriteOffDocumentItem, Transformation, TransformationInput, TransformationOutput
|
||||
from inventory.models import Reservation, Warehouse, StockBatch, Sale, SaleBatchAllocation, Inventory, WriteOff, Stock, WriteOffDocumentItem, Transformation, TransformationInput, TransformationOutput
|
||||
from inventory.services import SaleProcessor
|
||||
from inventory.services.batch_manager import StockBatchManager
|
||||
# InventoryProcessor больше не используется в сигналах - обработка вызывается явно через view
|
||||
@@ -1046,131 +1046,7 @@ def update_reservation_on_item_change(sender, instance, created, **kwargs):
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Incoming)
|
||||
def create_stock_batch_on_incoming(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Сигнал: При создании товара в приходе (Incoming) автоматически создается StockBatch и обновляется Stock.
|
||||
|
||||
Архитектура:
|
||||
- IncomingBatch: одна партия поступления (IN-0000-0001) содержит несколько товаров
|
||||
- Incoming: один товар в партии поступления
|
||||
- StockBatch: одна партия товара на складе (создается для каждого товара в приходе)
|
||||
Для FIFO: каждый товар имеет свою partия, чтобы можно было списывать отдельно
|
||||
|
||||
Процесс:
|
||||
1. Проверяем, новый ли товар в приходе
|
||||
2. Если stock_batch еще не создан - создаем StockBatch для этого товара
|
||||
3. Связываем Incoming с созданной StockBatch
|
||||
4. Обновляем остатки на складе (Stock)
|
||||
"""
|
||||
if not created:
|
||||
return # Только для новых приходов
|
||||
|
||||
# Если stock_batch уже установлен - не создаем новый
|
||||
if instance.stock_batch:
|
||||
return
|
||||
|
||||
# Получаем данные из партии поступления
|
||||
incoming_batch = instance.batch
|
||||
warehouse = incoming_batch.warehouse
|
||||
|
||||
# Создаем новую партию товара на складе
|
||||
# Каждый товар в партии поступления → отдельная StockBatch
|
||||
stock_batch = StockBatch.objects.create(
|
||||
product=instance.product,
|
||||
warehouse=warehouse,
|
||||
quantity=instance.quantity,
|
||||
cost_price=instance.cost_price,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
# Связываем Incoming с созданной StockBatch
|
||||
instance.stock_batch = stock_batch
|
||||
instance.save(update_fields=['stock_batch'])
|
||||
|
||||
# Обновляем или создаем запись в Stock
|
||||
stock, created_stock = Stock.objects.get_or_create(
|
||||
product=instance.product,
|
||||
warehouse=warehouse
|
||||
)
|
||||
# Пересчитываем остаток из всех активных партий
|
||||
# refresh_from_batches() уже вызывает save(), поэтому не вызываем ещё раз
|
||||
stock.refresh_from_batches()
|
||||
|
||||
|
||||
@receiver(post_save, sender=Incoming)
|
||||
def update_stock_batch_on_incoming_edit(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Сигнал: При редактировании товара в приходе (Incoming) автоматически
|
||||
обновляется связанная партия товара на складе (StockBatch).
|
||||
|
||||
Это обеспечивает синхронизацию данных между Incoming и StockBatch.
|
||||
|
||||
Архитектура:
|
||||
- Если Incoming редактируется - обновляем StockBatch с новыми значениями
|
||||
- Обновление StockBatch автоматически пересчитывает себестоимость товара (Product.cost_price)
|
||||
через сигнал update_product_cost_on_batch_change()
|
||||
|
||||
Процесс:
|
||||
1. Проверяем, это редактирование (created=False), а не создание
|
||||
2. Получаем связанный StockBatch
|
||||
3. Проверяем, изменились ли quantity или cost_price
|
||||
4. Если да - обновляем StockBatch
|
||||
5. Сохраняем StockBatch (запускает цепь пересчета себестоимости)
|
||||
6. Обновляем остатки на складе (Stock)
|
||||
"""
|
||||
if created:
|
||||
return # Только для редактирования (не для создания)
|
||||
|
||||
# Получаем связанный StockBatch
|
||||
if not instance.stock_batch:
|
||||
return # Если нет связи со StockBatch - нечего обновлять
|
||||
|
||||
stock_batch = instance.stock_batch
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Проверяем, отличаются ли значения в StockBatch от Incoming
|
||||
# Это говорит нам о том, что произошло редактирование
|
||||
needs_update = (
|
||||
stock_batch.quantity != instance.quantity or
|
||||
stock_batch.cost_price != instance.cost_price
|
||||
)
|
||||
|
||||
if not needs_update:
|
||||
return # Никаких изменений
|
||||
|
||||
# Обновляем StockBatch с новыми значениями из Incoming
|
||||
stock_batch.quantity = instance.quantity
|
||||
stock_batch.cost_price = instance.cost_price
|
||||
stock_batch.save()
|
||||
|
||||
logger.info(
|
||||
f"✓ StockBatch #{stock_batch.id} обновлён при редактировании Incoming: "
|
||||
f"quantity={instance.quantity}, cost_price={instance.cost_price} "
|
||||
f"(товар: {instance.product.sku})"
|
||||
)
|
||||
|
||||
# Обновляем Stock (остатки на складе)
|
||||
warehouse = stock_batch.warehouse
|
||||
stock, _ = Stock.objects.get_or_create(
|
||||
product=instance.product,
|
||||
warehouse=warehouse
|
||||
)
|
||||
stock.refresh_from_batches()
|
||||
|
||||
logger.info(
|
||||
f"✓ Stock обновлён для товара {instance.product.sku} "
|
||||
f"на складе {warehouse.name}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Ошибка при обновлении StockBatch при редактировании Incoming #{instance.id}: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
# Сигналы для Incoming удалены - теперь StockBatch создается напрямую в IncomingDocumentService
|
||||
|
||||
|
||||
@receiver(post_save, sender=Sale)
|
||||
|
||||
Reference in New Issue
Block a user