feat(products): реализована система единиц продажи на фронтенде
Добавлена полноценная интеграция единиц измерения (UoM) для продажи товаров в разных единицах с автоматическим пересчётом цен и остатков. ## Основные изменения: ### Backend - Расширен API поиска товаров (api_views.py): добавлена сериализация sales_units - Создан новый endpoint get_product_sales_units_api для загрузки единиц с остатками - Добавлено поле sales_unit в OrderItemForm и SaleForm с валидацией - Созданы CRUD views для управления единицами продажи (uom_views.py) - Обновлена ProductForm: использует base_unit вместо устаревшего unit ### Frontend - Создан модуль sales-units.js с функциями для работы с единицами - Интегрирован в select2-product-search.js: автозагрузка единиц при выборе товара - Добавлены контейнеры для единиц в order_form.html и sale_form.html - Реализовано автоматическое обновление цены при смене единицы продажи - При выборе базовой единицы цена возвращается к базовой цене товара ### UI - Добавлены страницы управления единицами продажи в навбар - Созданы шаблоны: sales_unit_list.html, sales_unit_form.html, sales_unit_delete.html - Добавлены фильтры по товару, единице, активности и дефолтности ## Исправленные ошибки: - Порядок инициализации: обработчики устанавливаются ДО триггера события change - Цена корректно обновляется при выборе единицы продажи - При выборе "Базовая единица" возвращается базовая цена товара 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,16 +45,33 @@ class WarehouseForm(forms.ModelForm):
|
||||
class SaleForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Sale
|
||||
fields = ['product', 'warehouse', 'quantity', 'sale_price', 'order', 'document_number']
|
||||
fields = ['product', 'warehouse', 'sales_unit', 'quantity', 'sale_price', 'order', 'document_number']
|
||||
widgets = {
|
||||
'product': forms.Select(attrs={'class': 'form-control'}),
|
||||
'warehouse': forms.Select(attrs={'class': 'form-control'}),
|
||||
'sales_unit': forms.Select(attrs={'class': 'form-control'}),
|
||||
'quantity': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
|
||||
'sale_price': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
|
||||
'order': forms.Select(attrs={'class': 'form-control'}),
|
||||
'document_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Динамический queryset для sales_unit
|
||||
if self.instance.pk and self.instance.product:
|
||||
from products.models import ProductSalesUnit
|
||||
self.fields['sales_unit'].queryset = ProductSalesUnit.objects.filter(
|
||||
product=self.instance.product,
|
||||
is_active=True
|
||||
).order_by('position', 'id')
|
||||
else:
|
||||
from products.models import ProductSalesUnit
|
||||
self.fields['sales_unit'].queryset = ProductSalesUnit.objects.none()
|
||||
|
||||
self.fields['sales_unit'].required = False
|
||||
|
||||
def clean_quantity(self):
|
||||
quantity = self.cleaned_data.get('quantity')
|
||||
if quantity and quantity <= 0:
|
||||
|
||||
Reference in New Issue
Block a user