feat: Add signal handler for synchronizing Incoming edits with StockBatch
## Changes ### 1. Fixed missing signal handler for Incoming edit (inventory/signals.py) - Added new signal handler `update_stock_batch_on_incoming_edit()` that: - Triggers when Incoming is edited (created=False) - Synchronizes StockBatch with new quantity and cost_price values - Automatically triggers cost price recalculation for the product - Updates Stock (inventory balance) for the warehouse - Includes proper logging and error handling ### 2. Created IncomingModelForm for editing individual incoming items (inventory/forms.py) - New ModelForm: `IncomingModelForm` that: - Inherits from forms.ModelForm (accepts 'instance' parameter required by UpdateView) - Allows editing: product, quantity, cost_price, notes - Includes validation for positive quantity and non-negative cost_price - Filters only active products ### 3. Updated IncomingUpdateView (inventory/views/incoming.py) - Changed form_class from IncomingForm to IncomingModelForm - Updated imports to include IncomingModelForm - Removed obsolete comments from form_valid method ## Architecture When editing an Incoming item: 1. User submits form with new quantity/cost_price 2. form.save() triggers post_save signal (created=False) 3. update_stock_batch_on_incoming_edit() synchronizes StockBatch 4. StockBatch.save() triggers update_product_cost_on_batch_change() 5. Product.cost_price is recalculated with weighted average ## Problem Solved Previously, editing an Incoming item would NOT: - Update the related StockBatch - Recalculate product cost_price - Update warehouse inventory balance - Maintain data consistency between Incoming and StockBatch Now all these operations happen automatically through the signal chain. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -232,6 +232,81 @@ def create_stock_batch_on_incoming(sender, instance, created, **kwargs):
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Sale)
|
||||
def process_sale_fifo(sender, instance, created, **kwargs):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user