Добавлены формы для работы с документами списания
- WriteOffDocumentForm - создание/редактирование документа списания - WriteOffDocumentItemForm - добавление/редактирование позиций документа - Автоматическая установка текущей даты и склада по умолчанию - Фильтрация товаров по наличию на выбранном складе - Валидация количества с проверкой доступных остатков - Учет текущего резерва при редактировании позиций
This commit is contained in:
@@ -5,7 +5,7 @@ from decimal import Decimal
|
||||
|
||||
from .models import (
|
||||
Warehouse, Incoming, Sale, WriteOff, Transfer, Reservation, Inventory, InventoryLine, StockBatch,
|
||||
TransferBatch, TransferItem, Showcase
|
||||
TransferBatch, TransferItem, Showcase, WriteOffDocument, WriteOffDocumentItem, Stock
|
||||
)
|
||||
from products.models import Product
|
||||
|
||||
@@ -443,3 +443,110 @@ class TransferBulkForm(forms.Form):
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# WRITEOFF DOCUMENT FORMS - Документы списания
|
||||
# ============================================================================
|
||||
|
||||
class WriteOffDocumentForm(forms.ModelForm):
|
||||
"""
|
||||
Форма создания/редактирования документа списания.
|
||||
"""
|
||||
class Meta:
|
||||
model = WriteOffDocument
|
||||
fields = ['warehouse', 'date', 'notes']
|
||||
widgets = {
|
||||
'warehouse': forms.Select(attrs={'class': 'form-control'}),
|
||||
'date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Примечания к документу'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['warehouse'].queryset = Warehouse.objects.filter(is_active=True)
|
||||
|
||||
# Устанавливаем дату по умолчанию - сегодня
|
||||
if not self.initial.get('date'):
|
||||
from django.utils import timezone
|
||||
self.initial['date'] = timezone.now().date()
|
||||
|
||||
# Если есть склад по умолчанию - предвыбираем его
|
||||
if not self.initial.get('warehouse'):
|
||||
default_warehouse = Warehouse.objects.filter(
|
||||
is_active=True,
|
||||
is_default=True
|
||||
).first()
|
||||
if default_warehouse:
|
||||
self.initial['warehouse'] = default_warehouse.id
|
||||
|
||||
|
||||
class WriteOffDocumentItemForm(forms.ModelForm):
|
||||
"""
|
||||
Форма добавления/редактирования позиции в документ списания.
|
||||
"""
|
||||
class Meta:
|
||||
model = WriteOffDocumentItem
|
||||
fields = ['product', 'quantity', 'reason', 'notes']
|
||||
widgets = {
|
||||
'product': forms.Select(attrs={'class': 'form-control'}),
|
||||
'quantity': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001', 'min': '0.001'}),
|
||||
'reason': forms.Select(attrs={'class': 'form-control'}),
|
||||
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2, 'placeholder': 'Примечания'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, document=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.document = document
|
||||
|
||||
if document:
|
||||
# Фильтруем товары - только те, что есть на складе
|
||||
products_with_stock = Stock.objects.filter(
|
||||
warehouse=document.warehouse,
|
||||
quantity_available__gt=0
|
||||
).values_list('product_id', flat=True)
|
||||
|
||||
self.fields['product'].queryset = Product.objects.filter(
|
||||
id__in=products_with_stock,
|
||||
status='active'
|
||||
).order_by('name')
|
||||
else:
|
||||
self.fields['product'].queryset = Product.objects.filter(
|
||||
status='active'
|
||||
).order_by('name')
|
||||
|
||||
def clean_quantity(self):
|
||||
quantity = self.cleaned_data.get('quantity')
|
||||
if quantity and quantity <= 0:
|
||||
raise ValidationError('Количество должно быть больше нуля')
|
||||
return quantity
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
product = cleaned_data.get('product')
|
||||
quantity = cleaned_data.get('quantity')
|
||||
|
||||
if product and quantity and self.document:
|
||||
stock = Stock.objects.filter(
|
||||
product=product,
|
||||
warehouse=self.document.warehouse
|
||||
).first()
|
||||
|
||||
if not stock:
|
||||
raise ValidationError({
|
||||
'product': f'Товар "{product.name}" отсутствует на складе'
|
||||
})
|
||||
|
||||
available = stock.quantity_available - stock.quantity_reserved
|
||||
|
||||
# Учитываем текущий резерв при редактировании
|
||||
if self.instance.pk and self.instance.reservation:
|
||||
available += self.instance.reservation.quantity
|
||||
|
||||
if quantity > available:
|
||||
raise ValidationError({
|
||||
'quantity': f'Недостаточно свободного товара. '
|
||||
f'Доступно: {available}, запрашивается: {quantity}'
|
||||
})
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user