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:
2026-01-02 12:35:01 +03:00
parent 5b68f14bb4
commit e831c4fb6e
19 changed files with 1574 additions and 52 deletions

View File

@@ -208,6 +208,7 @@
{{ item_form.id }}
{{ item_form.product }} <!-- Hidden field -->
{{ item_form.product_kit }} <!-- Hidden field -->
{{ item_form.sales_unit }} <!-- Hidden field -->
{{ item_form.is_custom_price }} <!-- Hidden field -->
<div class="row align-items-end">
@@ -229,6 +230,9 @@
{% endif %}
</select>
</div>
<!-- Контейнер для единиц продажи (управляется JS) -->
<div class="sales-unit-container mb-2" style="display: none;"></div>
</div>
<div class="col-md-2">
<div class="mb-2">
@@ -295,6 +299,7 @@
<input type="hidden" name="items-__prefix__-id" id="id_items-__prefix__-id">
<input type="hidden" name="items-__prefix__-product" id="id_items-__prefix__-product">
<input type="hidden" name="items-__prefix__-product_kit" id="id_items-__prefix__-product_kit">
<input type="hidden" name="items-__prefix__-sales_unit" id="id_items-__prefix__-sales_unit">
<input type="hidden" name="items-__prefix__-is_custom_price" id="id_items-__prefix__-is_custom_price" value="false">
<div class="row align-items-end">
@@ -308,6 +313,9 @@
<option value=""></option>
</select>
</div>
<!-- Контейнер для единиц продажи (управляется JS) -->
<div class="sales-unit-container mb-2" style="display: none;"></div>
</div>
<div class="col-md-2">
<div class="mb-2">
@@ -1809,7 +1817,8 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
</div>
<!-- Подключение модуля Select2 для поиска товаров/комплектов -->
<!-- Подключение модулей для работы с единицами продажи и поиском товаров -->
<script src="{% static 'products/js/sales-units.js' %}"></script>
<script src="{% static 'products/js/select2-product-search.js' %}"></script>
<script>