diff --git a/myproject/products/templates/products/includes/select2-product-init.html b/myproject/products/templates/products/includes/select2-product-init.html index c6ea803..5d0ebd2 100644 --- a/myproject/products/templates/products/includes/select2-product-init.html +++ b/myproject/products/templates/products/includes/select2-product-init.html @@ -40,10 +40,32 @@ * @param {Element} element - DOM элемент select * @param {string} type - Тип поиска ('product' или 'variant') * @param {string} apiUrl - URL API для поиска + * @returns {boolean} - true если инициализация прошла успешно, false иначе */ window.initProductSelect2 = function(element, type, apiUrl) { - if (!element || $(element).data('select2')) { - return; // Уже инициализирован + // Валидация входных параметров + if (!element) { + console.error('initProductSelect2: element is null or undefined'); + return false; + } + + if (!apiUrl || typeof apiUrl !== 'string') { + console.error('initProductSelect2: invalid apiUrl', apiUrl); + return false; + } + + // Проверка загрузки jQuery и Select2 + if (typeof $ === 'undefined' || typeof $.fn.select2 === 'undefined') { + console.error('initProductSelect2: jQuery or Select2 not loaded'); + return false; + } + + var $element = $(element); + + // Улучшенная проверка инициализации + if ($element.hasClass('select2-hidden-accessible')) { + console.log('initProductSelect2: already initialized, skipping', element.name); + return true; // Уже инициализирован - это не ошибка } var placeholders = { @@ -51,38 +73,45 @@ 'variant': 'Начните вводить название группы...' }; - $(element).select2({ - theme: 'bootstrap-5', - placeholder: placeholders[type] || 'Выберите...', - allowClear: true, - width: '100%', - language: 'ru', - minimumInputLength: 0, - dropdownAutoWidth: false, - ajax: { - url: apiUrl, - dataType: 'json', - delay: 250, - data: function (params) { - return { - q: params.term || '', - type: type, - page: params.page || 1 - }; + try { + $element.select2({ + theme: 'bootstrap-5', + placeholder: placeholders[type] || 'Выберите...', + allowClear: true, + width: '100%', + language: 'ru', + minimumInputLength: 0, + dropdownAutoWidth: false, + ajax: { + url: apiUrl, + dataType: 'json', + delay: 250, + data: function (params) { + return { + q: params.term || '', + type: type, + page: params.page || 1 + }; + }, + processResults: function (data) { + return { + results: data.results, + pagination: { + more: data.pagination.more + } + }; + }, + cache: true }, - processResults: function (data) { - return { - results: data.results, - pagination: { - more: data.pagination.more - } - }; - }, - cache: true - }, - templateResult: formatSelectResult, - templateSelection: formatSelectSelection - }); + templateResult: formatSelectResult, + templateSelection: formatSelectSelection + }); + console.log('initProductSelect2: successfully initialized for', element.name); + return true; + } catch (error) { + console.error('initProductSelect2: initialization error', error); + return false; + } }; /** diff --git a/myproject/products/templates/products/variantgroup_form.html b/myproject/products/templates/products/variantgroup_form.html index c0e0c6d..e46e8ad 100644 --- a/myproject/products/templates/products/variantgroup_form.html +++ b/myproject/products/templates/products/variantgroup_form.html @@ -215,12 +215,43 @@ document.addEventListener('DOMContentLoaded', function() { const totalFormsInput = document.querySelector('[name$="TOTAL_FORMS"]'); const apiUrl = '{% url "products:api-search-products-variants" %}'; + // Валидация + if (!container) { + console.error('items-tbody container not found'); + return; + } + + if (!totalFormsInput) { + console.error('TOTAL_FORMS input not found'); + return; + } + + if (!apiUrl || apiUrl.includes('undefined')) { + console.error('Invalid API URL:', apiUrl); + return; + } + + console.log('Initializing variant group form with API URL:', apiUrl); + // Инициализируем Select2 для всех селектов товаров function initSelect2ForRow(row) { - const productSelect = row.querySelector('[name$="-product"]'); - if (productSelect) { - window.initProductSelect2(productSelect, 'product', apiUrl); + if (!row) { + console.error('initSelect2ForRow: row is null'); + return false; + } + const productSelect = row.querySelector('[name$="-product"]'); + if (!productSelect) { + console.warn('initSelect2ForRow: product select not found in row'); + return false; + } + + // Убираем старые обработчики перед повторной инициализацией + $(productSelect).off('select2:select'); + + const success = window.initProductSelect2(productSelect, 'product', apiUrl); + + if (success) { // Обработчик события при выборе товара $(productSelect).on('select2:select', function(e) { // Извлекаем числовой ID из формата "product_123" @@ -239,13 +270,20 @@ document.addEventListener('DOMContentLoaded', function() { } updateRowData(row); }); + return true; + } else { + console.error('initSelect2ForRow: failed to initialize Select2'); + return false; } } // Функция для обновления данных строки при выборе товара function updateRowData(row) { + if (!row) return; + const productSelect = row.querySelector('[name$="-product"]'); if (!productSelect || !productSelect.value) { + // Сброс данных row.querySelector('[data-product-sku]').textContent = '-'; row.querySelector('[data-product-price]').textContent = '-'; row.querySelector('[data-product-stock]').innerHTML = '-'; @@ -262,13 +300,26 @@ document.addEventListener('DOMContentLoaded', function() { // Получаем цену и статус через AJAX (используем числовой ID) const numericId = productSelect.value; - fetch(`{% url "products:api-search-products-variants" %}?id=${numericId}`) - .then(response => response.json()) + + // Добавляем timeout для fetch + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + fetch(`${apiUrl}?id=${numericId}`, { signal: controller.signal }) + .then(response => { + clearTimeout(timeoutId); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) .then(data => { if (data.results && data.results.length > 0) { const product = data.results[0]; row.querySelector('[data-product-sku]').textContent = product.sku || sku; - row.querySelector('[data-product-price]').innerHTML = `${product.price} руб.` || '-'; + + const price = product.price || product.actual_price || '0'; + row.querySelector('[data-product-price]').innerHTML = `${price} руб.`; // Отображаем статус наличия const stockCell = row.querySelector('[data-product-stock]'); @@ -277,21 +328,33 @@ document.addEventListener('DOMContentLoaded', function() { } else { stockCell.innerHTML = ' Нет'; } + } else { + // Fallback: используем данные из опции + row.querySelector('[data-product-sku]').textContent = sku; + row.querySelector('[data-product-price]').textContent = '-'; + row.querySelector('[data-product-stock]').textContent = '-'; } }) - .catch(() => { + .catch(error => { + clearTimeout(timeoutId); + console.error('updateRowData: fetch error', error); + // Fallback: данные из опции row.querySelector('[data-product-sku]').textContent = sku; row.querySelector('[data-product-price]').textContent = '-'; row.querySelector('[data-product-stock]').textContent = '-'; }); } - // Инициализируем для существующих строк - container.querySelectorAll('.item-row').forEach(row => { - initSelect2ForRow(row); - // Загружаем данные товара при загрузке страницы (с небольшой задержкой) - setTimeout(() => updateRowData(row), 100); - }); + // Инициализируем для существующих строк с небольшой задержкой + setTimeout(function() { + container.querySelectorAll('.item-row').forEach(row => { + const success = initSelect2ForRow(row); + if (success) { + // Загружаем данные только если Select2 инициализирован + setTimeout(() => updateRowData(row), 200); + } + }); + }, 100); // ДОБАВЛЕНИЕ НОВОГО ТОВАРА document.getElementById('add-item-btn').addEventListener('click', function(e) { @@ -342,8 +405,17 @@ document.addEventListener('DOMContentLoaded', function() { // Увеличиваем счетчик форм totalFormsInput.value = newFormIndex + 1; - // Инициализируем Select2 для нового селекта - initSelect2ForRow(newRow); + // Даем время браузеру отрисовать элемент перед инициализацией Select2 + setTimeout(function() { + const success = initSelect2ForRow(newRow); + if (!success) { + console.error('Failed to initialize Select2 for new row, retrying...'); + // Повторная попытка через 500ms + setTimeout(function() { + initSelect2ForRow(newRow); + }, 500); + } + }, 50); // Обновляем приоритеты updatePriorities();