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

@@ -44,6 +44,7 @@ urlpatterns = [
# API endpoints
path('api/search-products-variants/', views.search_products_and_variants, name='api-search-products-variants'),
path('api/products/<int:product_id>/sales-units/', api_views.get_product_sales_units_api, name='api-product-sales-units'),
path('api/kits/temporary/create/', views.create_temporary_kit_api, name='api-temporary-kit-create'),
path('api/tags/create/', api_views.create_tag_api, name='api-tag-create'),
path('api/tags/<int:pk>/toggle/', api_views.toggle_tag_status_api, name='api-tag-toggle'),
@@ -110,4 +111,11 @@ urlpatterns = [
path('configurable/<int:pk>/options/add/', views.add_option_to_configurable, name='configurableproduct-add-option'),
path('configurable/<int:pk>/options/<int:option_id>/remove/', views.remove_option_from_configurable, name='configurableproduct-remove-option'),
path('configurable/<int:pk>/options/<int:option_id>/set-default/', views.set_option_as_default, name='configurableproduct-set-default-option'),
# Управление единицами измерения
path('units/', views.unit_of_measure_list, name='unit-list'),
path('sales-units/', views.product_sales_unit_list, name='sales-unit-list'),
path('sales-units/create/', views.product_sales_unit_create, name='sales-unit-create'),
path('sales-units/<int:pk>/edit/', views.product_sales_unit_update, name='sales-unit-update'),
path('sales-units/<int:pk>/delete/', views.product_sales_unit_delete, name='sales-unit-delete'),
]