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:
2025-11-15 00:22:58 +03:00
parent 4a4bd437b9
commit 65a316649b
4 changed files with 216 additions and 9 deletions

View File

@@ -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):
"""