Исправлен поиск товаров в форме массового поступления
Проблема:
- На странице /inventory/incoming/create/ не работал поиск товаров
- Использовался обычный <select> с предзагруженным списком всех товаров
- При большом количестве товаров список был неудобным
- Невозможно было искать товары по названию в реальном времени
Решение:
- Заменён обычный <select> на 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 запросов включено
Теперь поиск товаров работает корректно! 🎉
This commit is contained in:
@@ -168,14 +168,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const productsJsonInput = document.getElementById('productsJson');
|
const productsJsonInput = document.getElementById('productsJson');
|
||||||
const submitBtn = document.getElementById('submitBtn');
|
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 => `<option value="${p.id}">${p.name}</option>`).join('');
|
|
||||||
|
|
||||||
let rowCounter = 0;
|
let rowCounter = 0;
|
||||||
|
|
||||||
// Добавление новой строки товара
|
// Добавление новой строки товара
|
||||||
@@ -193,8 +185,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>
|
<td>
|
||||||
<select class="form-control form-control-sm product-select" data-row-id="${rowId}">
|
<select class="form-control form-control-sm product-select" data-row-id="${rowId}">
|
||||||
<option value="">Выберите товар...</option>
|
<option value="">Начните вводить название товара...</option>
|
||||||
${productOptions}
|
|
||||||
</select>
|
</select>
|
||||||
<div class="error-message" style="display:none;"></div>
|
<div class="error-message" style="display:none;"></div>
|
||||||
</td>
|
</td>
|
||||||
@@ -221,15 +212,49 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
productsBody.appendChild(row);
|
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 для новой строки
|
// Добавляем event listeners для новой строки
|
||||||
const quantityInput = row.querySelector('.quantity-input');
|
const quantityInput = row.querySelector('.quantity-input');
|
||||||
const priceInput = row.querySelector('.price-input');
|
const priceInput = row.querySelector('.price-input');
|
||||||
const removeBtn = row.querySelector('.btn-remove-row');
|
const removeBtn = row.querySelector('.btn-remove-row');
|
||||||
|
|
||||||
|
$(productSelect).on('change', updateTotals);
|
||||||
quantityInput.addEventListener('input', updateTotals);
|
quantityInput.addEventListener('input', updateTotals);
|
||||||
priceInput.addEventListener('input', updateTotals);
|
priceInput.addEventListener('input', updateTotals);
|
||||||
removeBtn.addEventListener('click', function(e) {
|
removeBtn.addEventListener('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
$(productSelect).select2('destroy');
|
||||||
row.remove();
|
row.remove();
|
||||||
updateTotals();
|
updateTotals();
|
||||||
});
|
});
|
||||||
@@ -298,15 +323,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Добавляем восстановленные товары
|
// Добавляем восстановленные товары
|
||||||
savedProducts.forEach(item => {
|
savedProducts.forEach(item => {
|
||||||
const product = products.find(p => p.id === item.product_id);
|
addProductRow();
|
||||||
if (product) {
|
const lastRow = productsBody.querySelector('tr:last-child');
|
||||||
addProductRow();
|
const lastSelect = lastRow.querySelector('.product-select');
|
||||||
const lastRow = productsBody.querySelector('tr:last-child');
|
|
||||||
|
|
||||||
lastRow.querySelector('.product-select').value = item.product_id;
|
// Создаём option и устанавливаем его в Select2
|
||||||
lastRow.querySelector('.quantity-input').value = item.quantity;
|
const newOption = new Option(item.product_name || `Товар #${item.product_id}`, item.product_id, true, true);
|
||||||
lastRow.querySelector('.price-input').value = item.cost_price;
|
$(lastSelect).append(newOption).trigger('change');
|
||||||
}
|
|
||||||
|
lastRow.querySelector('.quantity-input').value = item.quantity;
|
||||||
|
lastRow.querySelector('.price-input').value = item.cost_price;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Обновляем итоги
|
// Обновляем итоги
|
||||||
|
|||||||
Reference in New Issue
Block a user