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:
@@ -47,7 +47,7 @@
|
||||
</label>
|
||||
<textarea class="form-control {% if form.description.errors %}is-invalid{% endif %}"
|
||||
id="{{ form.description.id_for_label }}" name="{{ form.description.html_name }}"
|
||||
rows="4">{{ form.description.value|default:'' }}</textarea>
|
||||
rows="3">{{ form.description.value|default:'' }}</textarea>
|
||||
{% if form.description.errors %}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.description.errors %}
|
||||
@@ -57,6 +57,72 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4 mb-3">Адрес</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<label for="{{ form.street.id_for_label }}" class="form-label">
|
||||
{{ form.street.label }}
|
||||
</label>
|
||||
{{ form.street }}
|
||||
{% if form.street.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.street.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="{{ form.building_number.id_for_label }}" class="form-label">
|
||||
{{ form.building_number.label }}
|
||||
</label>
|
||||
{{ form.building_number }}
|
||||
{% if form.building_number.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.building_number.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4 mb-3">Контакты</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.phone.id_for_label }}" class="form-label">
|
||||
{{ form.phone.label }}
|
||||
</label>
|
||||
{{ form.phone }}
|
||||
{% if form.phone.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.phone.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label">
|
||||
{{ form.email.label }}
|
||||
</label>
|
||||
{{ form.email }}
|
||||
{% if form.email.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.email.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4 mb-3">Настройки</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input"
|
||||
@@ -76,7 +142,21 @@
|
||||
<label class="form-check-label" for="{{ form.is_default.id_for_label }}">
|
||||
{{ form.is_default.label }}
|
||||
<small class="text-muted d-block">
|
||||
Отмечьте, чтобы использовать этот склад по умолчанию при создании новых документов
|
||||
{{ form.is_default.help_text }}
|
||||
</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input"
|
||||
id="{{ form.is_pickup_point.id_for_label }}" name="{{ form.is_pickup_point.html_name }}"
|
||||
{% if form.is_pickup_point.value %}checked{% endif %}>
|
||||
<label class="form-check-label" for="{{ form.is_pickup_point.id_for_label }}">
|
||||
{{ form.is_pickup_point.label }}
|
||||
<small class="text-muted d-block">
|
||||
{{ form.is_pickup_point.help_text }}
|
||||
</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user