From dc39f56b9a1c65f59a03d488327590584b71aed3 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 1 Dec 2025 10:29:57 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=20=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=80=D0=BE=D0=B2=20=D0=B2=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B5=20=D0=BC=D0=B0=D1=81=D1=81=D0=BE=D0=B2=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BF=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: - На странице /inventory/incoming/create/ не работал поиск товаров - Использовался обычный на Select2 с AJAX автокомплитом - Подключен API endpoint /products/api/search-products-variants/ - Поиск товаров работает в реальном времени (с задержкой 250ms) - Минимальная длина поиска: 0 символов (можно открыть весь список) - Поддержка русского языка - Theme: bootstrap-5 для визуального соответствия Изменения: - Удалён предзагруженный список товаров из контекста шаблона - Добавлена инициализация Select2 для каждой новой строки товара - При удалении строки вызывается select2('destroy') для очистки - Исправлена логика восстановления товаров при ошибке валидации - Используется Option API для программной установки значений Технические детали: - jQuery и Select2 уже подключены в base.html - Не дублируем подключение библиотек - Используем событие 'change' для Select2 вместо 'input' - Кэширование AJAX запросов включено Теперь поиск товаров работает корректно! 🎉 --- .../incoming/incoming_bulk_form.html | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/myproject/inventory/templates/inventory/incoming/incoming_bulk_form.html b/myproject/inventory/templates/inventory/incoming/incoming_bulk_form.html index 0df1645..18ac90b 100644 --- a/myproject/inventory/templates/inventory/incoming/incoming_bulk_form.html +++ b/myproject/inventory/templates/inventory/incoming/incoming_bulk_form.html @@ -168,14 +168,6 @@ document.addEventListener('DOMContentLoaded', function() { const productsJsonInput = document.getElementById('productsJson'); const submitBtn = document.getElementById('submitBtn'); - // Список всех доступных товаров (преобразуем QuerySet в JSON) - const products = [ - {% for product in products %} - { id: {{ product.id }}, name: "{{ product.name }}" }, - {% endfor %} - ]; - const productOptions = products.map(p => ``).join(''); - let rowCounter = 0; // Добавление новой строки товара @@ -193,8 +185,7 @@ document.addEventListener('DOMContentLoaded', function() { row.innerHTML = ` @@ -221,15 +212,49 @@ document.addEventListener('DOMContentLoaded', function() { productsBody.appendChild(row); + // Инициализируем Select2 для нового селекта товара + const productSelect = row.querySelector('.product-select'); + $(productSelect).select2({ + theme: 'bootstrap-5', + placeholder: 'Начните вводить название товара...', + allowClear: true, + width: '100%', + language: 'ru', + minimumInputLength: 0, + ajax: { + url: '/products/api/search-products-variants/', + dataType: 'json', + delay: 250, + data: function (params) { + return { + q: params.term || '', + type: 'product', + page: params.page || 1 + }; + }, + processResults: function (data) { + return { + results: data.results, + pagination: { + more: data.pagination.more + } + }; + }, + cache: true + } + }); + // Добавляем event listeners для новой строки const quantityInput = row.querySelector('.quantity-input'); const priceInput = row.querySelector('.price-input'); const removeBtn = row.querySelector('.btn-remove-row'); + $(productSelect).on('change', updateTotals); quantityInput.addEventListener('input', updateTotals); priceInput.addEventListener('input', updateTotals); removeBtn.addEventListener('click', function(e) { e.preventDefault(); + $(productSelect).select2('destroy'); row.remove(); updateTotals(); }); @@ -298,15 +323,16 @@ document.addEventListener('DOMContentLoaded', function() { // Добавляем восстановленные товары savedProducts.forEach(item => { - const product = products.find(p => p.id === item.product_id); - if (product) { - addProductRow(); - const lastRow = productsBody.querySelector('tr:last-child'); + addProductRow(); + const lastRow = productsBody.querySelector('tr:last-child'); + const lastSelect = lastRow.querySelector('.product-select'); - lastRow.querySelector('.product-select').value = item.product_id; - lastRow.querySelector('.quantity-input').value = item.quantity; - lastRow.querySelector('.price-input').value = item.cost_price; - } + // Создаём option и устанавливаем его в Select2 + const newOption = new Option(item.product_name || `Товар #${item.product_id}`, item.product_id, true, true); + $(lastSelect).append(newOption).trigger('change'); + + lastRow.querySelector('.quantity-input').value = item.quantity; + lastRow.querySelector('.price-input').value = item.cost_price; }); // Обновляем итоги