This commit is contained in:
2025-11-04 11:00:05 +03:00
parent 706ee5d8e8
commit b24d5bcdee
13 changed files with 1383 additions and 72 deletions

View File

@@ -3,7 +3,10 @@ from django import forms
from django.core.exceptions import ValidationError
from decimal import Decimal
from .models import Warehouse, Incoming, Sale, WriteOff, Transfer, Reservation, Inventory, InventoryLine, StockBatch
from .models import (
Warehouse, Incoming, Sale, WriteOff, Transfer, Reservation, Inventory, InventoryLine, StockBatch,
TransferBatch, TransferItem
)
from products.models import Product
@@ -82,42 +85,6 @@ class WriteOffForm(forms.ModelForm):
return cleaned_data
class TransferForm(forms.ModelForm):
class Meta:
model = Transfer
fields = ['batch', 'from_warehouse', 'to_warehouse', 'quantity', 'document_number']
widgets = {
'batch': forms.Select(attrs={'class': 'form-control'}),
'from_warehouse': forms.Select(attrs={'class': 'form-control'}),
'to_warehouse': forms.Select(attrs={'class': 'form-control'}),
'quantity': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
'document_number': forms.TextInput(attrs={'class': 'form-control'}),
}
def clean(self):
cleaned_data = super().clean()
batch = cleaned_data.get('batch')
quantity = cleaned_data.get('quantity')
from_warehouse = cleaned_data.get('from_warehouse')
if batch and quantity:
if quantity > batch.quantity:
raise ValidationError(
f'Невозможно перенести {quantity} шт, доступно {batch.quantity} шт'
)
if quantity <= 0:
raise ValidationError('Количество должно быть больше нуля')
# Проверяем что складской источник совпадает с складом партии
if from_warehouse and batch.warehouse_id != from_warehouse.id:
raise ValidationError(
f'Партия находится на складе "{batch.warehouse.name}", '
f'а вы выбрали "{from_warehouse.name}"'
)
return cleaned_data
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservation
@@ -339,3 +306,103 @@ class IncomingForm(forms.Form):
'Оставьте поле пустым для автогенерации или используйте другой формат.'
)
return document_number
# ============================================================================
# TRANSFER FORMS - Перемещение товаров между складами
# ============================================================================
class TransferHeaderForm(forms.ModelForm):
"""
Форма заголовка документа перемещения товара между складами.
Содержит информацию о складах-источнике и складе-назначении, примечания.
"""
class Meta:
model = TransferBatch
fields = ['from_warehouse', 'to_warehouse', 'notes']
widgets = {
'from_warehouse': forms.Select(attrs={'class': 'form-control'}),
'to_warehouse': forms.Select(attrs={'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Примечания к перемещению'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Фильтруем только активные склады
self.fields['from_warehouse'].queryset = Warehouse.objects.filter(is_active=True)
self.fields['to_warehouse'].queryset = Warehouse.objects.filter(is_active=True)
def clean(self):
cleaned_data = super().clean()
from_warehouse = cleaned_data.get('from_warehouse')
to_warehouse = cleaned_data.get('to_warehouse')
if from_warehouse and to_warehouse:
if from_warehouse.id == to_warehouse.id:
raise ValidationError('Склад-источник и склад-назначение должны быть разными')
return cleaned_data
class TransferLineForm(forms.Form):
"""
Форма для одной строки товара при массовом перемещении.
Используется в динамической таблице для ввода нескольких товаров.
"""
product = forms.ModelChoiceField(
queryset=Product.objects.filter(is_active=True).order_by('name'),
widget=forms.Select(attrs={'class': 'form-control'}),
label="Товар",
required=True
)
quantity = forms.DecimalField(
max_digits=10,
decimal_places=3,
widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
label="Количество",
required=True
)
def clean_quantity(self):
quantity = self.cleaned_data.get('quantity')
if quantity and quantity <= 0:
raise ValidationError('Количество должно быть больше нуля')
return quantity
class TransferBulkForm(forms.Form):
"""
Комбинированная форма для ввода перемещения товаров.
Содержит header информацию (склад-источник, склад-назначение, примечания) + динамический набор товаров.
"""
from_warehouse = forms.ModelChoiceField(
queryset=Warehouse.objects.filter(is_active=True),
widget=forms.Select(attrs={'class': 'form-control'}),
label="Склад-отгрузки",
required=True
)
to_warehouse = forms.ModelChoiceField(
queryset=Warehouse.objects.filter(is_active=True),
widget=forms.Select(attrs={'class': 'form-control'}),
label="Склад-приемки",
required=True
)
notes = forms.CharField(
widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Примечания к перемещению'}),
label="Примечания",
required=False
)
def clean(self):
cleaned_data = super().clean()
from_warehouse = cleaned_data.get('from_warehouse')
to_warehouse = cleaned_data.get('to_warehouse')
if from_warehouse and to_warehouse:
if from_warehouse.id == to_warehouse.id:
raise ValidationError('Склад-источник и склад-назначение должны быть разными')
return cleaned_data