From 711b35488fd3e5919b66dff34a26c6df67a30740 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Wed, 10 Dec 2025 23:35:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=84=D0=BE=D1=80=D0=BC=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D0=BC=D0=B8=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WriteOffDocumentForm - создание/редактирование документа списания - WriteOffDocumentItemForm - добавление/редактирование позиций документа - Автоматическая установка текущей даты и склада по умолчанию - Фильтрация товаров по наличию на выбранном складе - Валидация количества с проверкой доступных остатков - Учет текущего резерва при редактировании позиций --- myproject/inventory/forms.py | 109 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/myproject/inventory/forms.py b/myproject/inventory/forms.py index 7fb826c..40b8c1e 100644 --- a/myproject/inventory/forms.py +++ b/myproject/inventory/forms.py @@ -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 +