Каталог

Незадуваемые свечи на День рождения

По вашему запросу ничего не найдено

Незадуваемые свечи: идеальное решение для ваших праздников

Когда речь заходит о праздниках, особенно о днях рождения, юбилеях и детских праздниках, одним из важнейших атрибутов являются свечи. Свечи не только создают атмосферу торжества, но и становятся центром внимания, когда приходит время задувать огонь. Однако, что делать, если свечи не здуваются? Ответ прост — незадуваемые свечи. В нашем интернет-магазине вы найдете разнообразные незадуваемые свечи, которые помогут сделать ваш праздник незабываемым.

Почему стоит купить незадуваемые свечи?

  • Удобство: Незадуваемые свечи позволяют избежать лишнего беспокойства о том, что свечи потухнут до того, как вы успеете их задувать. Эти свечи горят до тех пор, пока их не потушат специально. Это особенно важно для детских праздников, когда малыши могут не справиться с задачей задувания огня.
  • Безопасность: Мы понимаем, что безопасность — это главный приоритет, особенно на детских праздниках. Наши незадуваемые свечи изготовлены из безопасных материалов, что делает их идеальным выбором для ваших торжеств.
  • Разнообразие форматов: В нашем ассортименте вы найдете незадуваемые свечи для любого торта — от классических до уникальных форматов, подходящих для тематических праздников.
  • Качество по низкой цене: Мы предлагаем наши свечи по доступной цене, что позволяет вам закупить их оптом недорого. При этом вы можете быть уверены в высоком качестве наших товаров.

Как заказать незадуваемые свечи?

Покупка незадуваемых свечей у нас — это просто и удобно! Вы можете заказать их оптом, что станет отличным решением для организаторов мероприятий, детских садов и частных праздников. Мы предлагаем доставку по всей России, включая Москву, что обеспечивает быстрый и надежный сервис. Заказать незадуваемые свечи можно прямо со склада, что гарантирует быструю отправку и получение товара.

Для каких праздников использовать незадуваемые свечи?

  • День рождения: Незадуваемые свечи — это отличный выбор для дня рождения. Они добавят яркие эмоции и сделают момент задувания свечей особенным.
  • Детские праздники: На детских праздниках важна каждая деталь. Незадуваемые свечи не только безопасны, но и обеспечивают увлекательное времяпрепровождение для детей.
  • Юбилей: На юбилеях, где собирается множество гостей, незадуваемые свечи помогут избежать лишней суеты и создадут комфортную атмосферу для всех присутствующих.

Сопутствующие категории товаров

Кроме незадуваемых свечей, в нашем интернет-магазине вы также найдете множество сопутствующих товаров, которые помогут сделать ваш праздник еще ярче и интереснее:

  • Свечи для торта: Классические и тематические свечи для тортов — обязательный атрибут любого праздника.
  • Бенгальские огни: Добавьте немного феерии с помощью бенгальских огней, которые создадут романтичную атмосферу и станут отличным дополнением к вашему торжеству.
  • Светодиодные свечи: Для тех, кто предпочитает современные решения, светодиодные свечи — идеальный выбор. Они безопасны и прекрасно смотрятся в любое время.
  • Свечи-фонтаны: Эти уникальные свечи добавят впечатляющий эффект к вашему торту, создавая настоящий фейерверк из огня.
  • Свечи-цифры: Поздравьте именинника с очередной вехой с помощью свечей в виде цифр! Это станет ярким акцентом на любом торте.
  • Наборы и музыкальные свечи: У нас также есть наборы незадуваемых свечей и музыкальные свечи, которые добавят веселья и радости на ваш праздник.
  • Ароматизированные свечи: Создайте уникальную атмосферу с помощью ароматизированных свечей, которые наполнят пространство приятным запахом.
  • Свечи с цветным пламенем: Эти свечи удивят ваших гостей разнообразием цветов и создадут незабываемую атмосферу.

Незадуваемые свечи — это идеальный способ сделать ваше торжество ярче и безопаснее. В нашем интернет-магазине вы найдете широкий выбор различных свечей и аксессуаров для праздников. Не упустите возможность закупить наши товары оптом по низкой цене с доставкой по всей России. Закажите незадуваемые свечи уже сегодня и сделайте ваш праздник незабываемым!

Обратный звонок
Запрос успешно отправлен!
Имя *
Телефон *
Предзаказ
Предзаказ успешно отправлен!
Имя *
Телефон *
Добавить в корзину
Название товара
100 ₽
1 шт.
Перейти в корзину
Заявка

Я ознакомлен и согласен с условиями оферты и политики конфиденциальности.

Заявка

Я ознакомлен и согласен с условиями оферты и политики конфиденциальности.

`; } /** * Показ модального окна ошибки оформления заказа */ showOrderErrorModal(result) { // Скрываем успешное окно и показываем ошибку $('.order-success-content').hide(); $('.order-error-content').show(); // Заполняем сообщение об ошибке $('#error-message').text(result.details || result.error || 'Произошла ошибка при оформлении заказа'); this.showModal('Ошибка оформления заказа', '', 'error'); } /** * Создание содержимого модального окна успеха */ createSuccessModalContent(result) { const { orderNumber, paymentMethod, clientType, paymentButton } = result; // Получаем HTML ответ для поиска ключа заказа const $html = $(result.htmlResponse || ''); // Извлекаем информацию о заказе из HTML const orderInfo = this.extractOrderInfo($html); const orderItems = this.extractOrderItems($html); let content = `

Заказ №${orderNumber}

Товары в заказе
${this.createOrderItemsHTML(orderItems)}
${this.createOrderTotalHTML(orderItems)}
Информация о заказе
${this.createOrderInfoHTML(orderInfo, paymentMethod, clientType)} ${paymentMethod === 'Юкасса' && paymentButton ? this.createPaymentSectionHTML(paymentButton, $html) : ''}
`; return content; } /** * Извлечение информации о заказе из HTML */ extractOrderInfo($html) { const info = { orderDate: new Date().toLocaleDateString('ru-RU'), deliveryMethod: 'Доставка курьером', deliveryAddress: '', recipient: '', phone: '', email: '' }; // Извлекаем адрес доставки const addressSelectors = [ '.delivery-address', '.shipping-address', '[class*="address"]' ]; for (const selector of addressSelectors) { const $address = $html.find(selector); if ($address.length > 0) { info.deliveryAddress = $address.text().trim(); break; } } // Извлекаем получателя const recipientSelectors = [ '.recipient-name', '.client-name', '[class*="recipient"]' ]; for (const selector of recipientSelectors) { const $recipient = $html.find(selector); if ($recipient.length > 0) { info.recipient = $recipient.text().trim(); break; } } // Извлекаем телефон и email из формы info.phone = $('#phone').val() || ''; info.email = $('#email').val() || ''; // Если не нашли получателя, используем данные из формы if (!info.recipient) { const firstName = $('#contact_name').val() || ''; const lastName = $('#surname').val() || ''; const middleName = $('#middlename').val() || ''; info.recipient = `${lastName} ${firstName} ${middleName}`.trim(); } // Если не нашли адрес, используем данные из формы if (!info.deliveryAddress) { const city = $('#shipping_address_full_locality_name').val() || ''; const street = $('#shipping_address_street').val() || ''; const house = $('#shipping_address_house').val() || ''; const flat = $('#shipping_address_flat').val() || ''; info.deliveryAddress = `${city}, ${street}, д.${house}${flat ? ', кв.' + flat : ''}`.trim(); } return info; } /** * Извлечение товаров заказа из HTML */ extractOrderItems($html) { const items = []; // Сначала пытаемся получить товары из текущей корзины const $cartItems = $('.cart-item'); const self = this; // Сохраняем контекст if ($cartItems.length > 0) { $cartItems.each(function() { const $item = $(this); const imageSrc = $item.find('img').attr('src') || ''; const item = { name: $item.find('.item-title').text().trim() || 'Товар', sku: $item.find('.item-sku').text().trim() || '', quantity: $item.find('input[name*="quantity"]').val() || '1', price: $item.find('.item-total').text().trim() || '0 ₽', image: self.isValidImageUrl(imageSrc) ? imageSrc : '' }; if (item.name && item.name !== 'Товар') { items.push(item); } }); } // Если не нашли в корзине, ищем в HTML ответе if (items.length === 0) { const itemSelectors = [ '.order-item', '.cart-item', '.product-item', '[class*="item"]' ]; for (const selector of itemSelectors) { const $items = $html.find(selector); if ($items.length > 0) { $items.each(function() { const $item = $(this); const imageSrc = $item.find('img').attr('src') || ''; const item = { name: $item.find('.item-name, .product-name, [class*="name"]').text().trim() || 'Товар', sku: $item.find('.item-sku, .product-sku, [class*="sku"]').text().trim() || '', quantity: $item.find('.item-quantity, .product-quantity, [class*="quantity"]').text().trim() || '1', price: $item.find('.item-price, .product-price, [class*="price"]').text().trim() || '0 ₽', image: self.isValidImageUrl(imageSrc) ? imageSrc : '' }; if (item.name && item.name !== 'Товар') { items.push(item); } }); break; } } } // Если все еще не нашли товары, создаем заглушку if (items.length === 0) { items.push({ name: 'Товары в заказе', sku: '', quantity: '1', price: '0 ₽', image: '' }); } return items; } /** * Создание HTML для товаров заказа */ createOrderItemsHTML(items) { return items.map(item => { const imageHTML = (item.image && this.isValidImageUrl(item.image)) ? `
${item.name || ''}
` : ''; return `
${imageHTML}
${item.name || ''}
${item.sku ? `
Артикул: ${item.sku}
` : ''}
Количество: ${item.quantity || 1}
${item.price || 0}
`; }).join(''); } /** * Создание HTML для итоговой суммы */ createOrderTotalHTML(items) { // Получаем данные из корзины const $cartTotal = $('[data-cart-total-price]'); const $cartDiscount = $('.discount .insales-ui-discounts-errors'); let totalPrice = '0 ₽'; let discountPrice = '0 ₽'; if ($cartTotal.length > 0) { totalPrice = $cartTotal.text().trim() || '0 ₽'; } if ($cartDiscount.length > 0) { const discountText = $cartDiscount.text().trim(); const discountMatch = discountText.match(/(\d+[\s,]*\d*)\s*₽/); if (discountMatch) { discountPrice = discountMatch[0]; } } return `
Скидка: ${discountPrice}
Итого: ${totalPrice}
`; } /** * Создание HTML для информации о заказе */ createOrderInfoHTML(orderInfo, paymentMethod, clientType) { return `
Дата оформления:
${orderInfo.orderDate}
Способ оплаты:
${paymentMethod}
Способ доставки:
${orderInfo.deliveryMethod}
Адрес доставки:
${orderInfo.deliveryAddress || 'Не указан'}
Получатель:
${orderInfo.recipient || 'Не указан'}
Телефон:
${orderInfo.phone || 'Не указан'}
Email:
${orderInfo.email || 'Не указан'}
`; } /** * Создание HTML для секции оплаты */ createPaymentSectionHTML(paymentButton, $html) { // Проверяем, что ссылка корректная let paymentUrl = paymentButton.href; if (!paymentUrl || paymentUrl === 'https://канцопт24.рф/page/payment') { // Если ссылка неправильная, создаем правильную const orderKey = this.extractOrderKey($html); if (orderKey) { paymentUrl = `/payments/external/6146185/create?key=${orderKey}`; } } return `

Для завершения заказа необходимо произвести оплату:

${paymentButton.text || 'Перейти к оплате'}
`; } /** * Создание содержимого модального окна ошибки */ createErrorModalContent(result) { const { error, details } = result; return `

${error}

${details ? `

${details}

` : ''}
`; } /** * Показ модального окна */ showModal(title, content, type = 'info') { const $modal = $('#order-result-modal'); if ($modal.length === 0) { return; } const $modalTitle = $modal.find('.modal-title'); // Устанавливаем заголовок $modalTitle.text(title); // Показываем нужный контент в зависимости от типа if (type === 'success') { $('.order-success-content').show(); $('.order-error-content').hide(); } else if (type === 'error') { $('.order-success-content').hide(); $('.order-error-content').show(); } // Блокируем прокрутку фона (как в CU0.14) const scrollY = window.scrollY; $('body').addClass('modal-open').css({ 'position': 'fixed', 'top': `-${scrollY}px`, 'width': '100%', 'overflow': 'hidden' }); // Принудительно показываем модальное окно $modal.attr('style', ` position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; background: rgba(0, 0, 0, 0.6) !important; backdrop-filter: blur(4px) !important; z-index: 10000 !important; display: flex !important; align-items: center !important; justify-content: center !important; opacity: 1 !important; pointer-events: auto !important; overflow-y: auto !important; padding: 20px !important; `); // Обработчик закрытия по клику на overlay const self = this; $modal.off('click.modal').on('click.modal', function(e) { // Проверяем, что клик был именно по overlay (не по содержимому) if (e.target === this || $(e.target).hasClass('modal-overlay')) { self.closeModal(); } }); // Обработчик закрытия по клавише Escape $(document).off('keydown.modal').on('keydown.modal', function(e) { if (e.key === 'Escape' || e.keyCode === 27) { self.closeModal(); } }); } /** * Закрытие модального окна */ closeModal() { const $modal = $('#order-result-modal'); if ($modal.length) { // Скрываем модальное окно и отключаем pointer-events $modal.attr('style', 'display: none !important; pointer-events: none !important;'); } // Очищаем обработчики событий $modal.off('click.modal'); $(document).off('keydown.modal'); // Разблокируем прокрутку фона $('body').removeClass('modal-open'); $('body').removeAttr('style'); $('html').removeAttr('style'); // Принудительная перезагрузка с обходом кеша // Используем несколько методов для максимальной надежности на мобильных const currentUrl = window.location.href.split('?')[0].split('#')[0]; const timestamp = new Date().getTime(); const newUrl = currentUrl + '?nocache=' + timestamp; // Используем replace вместо reload для более агрессивной очистки window.location.replace(newUrl); } /** * Сброс виджета */ resetWidget() { // Проверяем, нужно ли сбрасывать состояние // Сбрасываем только для авторизованных + незарегистрированных // Для авторизованных + зарегистрированных НЕ сбрасываем if (this.clientState.isAuthorized && this.clientState.isRegistered) { // Не сбрасываем состояние для авторизованных и зарегистрированных return; } this.clientState = { isAuthorized: false, isRegistered: false, email: null, clientType: 'individual', clientData: null, authCodeSent: false, codeTimer: null }; localStorage.removeItem('client_auth'); $('#order-form')[0].reset(); $('#auth-email-form')[0].reset(); $('#auth-code-form')[0].reset(); $('#order-success-step').removeClass('active'); $('#order-form-step').removeClass('active'); $('#auth-code-step').removeClass('active'); $('#auth-email-step').addClass('active'); this.updateUI(); } /** * Обновление UI */ updateUI() { if (this.clientState.isAuthorized) { this.showAuthSuccess(this.clientState.email); // Скрываем заголовок авторизации для авторизованного клиента $('.auth-header').hide(); // Заполняем email для авторизованного клиента if (this.clientState.email) { $('#email').val(this.clientState.email); $('#email').prop('disabled', true); $('#email').addClass('disabled-field'); } // Проверяем наличие данных клиента (id ИЛИ email + name) const hasValidClientData = this.clientState.clientData && (this.clientState.clientData.id || (this.clientState.clientData.email && this.clientState.clientData.name)); if (this.clientState.isRegistered && hasValidClientData) { this.fillFormWithClientData(); this.showClientStatus(); // Сворачиваем блок организации для авторизованных юр.лиц if (this.clientState.clientType === 'juridical') { this.collapseOrgSectionForAuthUser(); } } } else { // Показываем заголовок авторизации для неавторизованного клиента $('.auth-header').show(); // Разблокируем поле email для неавторизованного клиента $('#email').prop('disabled', false); $('#email').removeClass('disabled-field'); $('#email').val(''); // Очищаем поле // Очищаем статус организации this.setStatusOrgName(null); // Показываем селектор типа клиента this.showClientTypeSelector(); $('#auth-email-step').addClass('active'); $('#auth-code-step').removeClass('active'); $('#auth-success-step').removeClass('active'); } } /** * Таймер для повторной отправки кода */ startCodeTimer() { // Сначала останавливаем предыдущий таймер, если он был запущен this.stopCodeTimer(); // Блокируем кнопку и показываем таймер $('#resend-code-btn').prop('disabled', true).text('Отправить повторно'); $('.code-timer').show(); this.timerSeconds = 60; this.updateTimerDisplay(); // Показываем начальное значение this.codeTimer = setInterval(() => { this.timerSeconds--; if (this.timerSeconds <= 0) { // Разблокируем кнопку повторной отправки и скрываем таймер $('#resend-code-btn').prop('disabled', false).text('Отправить повторно'); $('.code-timer').hide(); this.stopCodeTimer(); } else { this.updateTimerDisplay(); } }, 1000); } /** * Остановка таймера кода */ stopCodeTimer() { if (this.codeTimer) { clearInterval(this.codeTimer); this.codeTimer = null; } } /** * Обновление отображения таймера */ updateTimerDisplay() { const minutes = Math.floor(this.timerSeconds / 60); const seconds = this.timerSeconds % 60; $('#code-timer-text').html(`Повторная отправка через: ${minutes}:${seconds.toString().padStart(2, '0')} сек`); } /** * Показ поля ввода email */ showEmailInput() { $('#auth-code-step').removeClass('active'); $('#auth-email-step').addClass('active'); this.clientState.authCodeSent = false; } /** * Обработка повторной отправки кода */ async handleResendCode() { const email = $('#auth-email').val().trim(); if (!email) { this.showNotification('Нет сохраненного email для повторной отправки', 'error'); return; } // Блокируем кнопку (текст остаётся "Отправить повторно") $('#resend-code-btn').prop('disabled', true); try { const result = await this.sendAuthCode(email); if (result.success) { this.showNotification('Код отправлен повторно', 'success'); // Запускаем таймер (он сам покажет таймер и заблокирует кнопку) this.startCodeTimer(); } else { this.showNotification(result.errors || result.error, 'error'); // При ошибке разблокируем кнопку $('#resend-code-btn').prop('disabled', false); } } catch (error) { this.showNotification('Ошибка повторной отправки кода', 'error'); // При ошибке разблокируем кнопку $('#resend-code-btn').prop('disabled', false); } } /** * Обработка смены email */ handleChangeEmail() { // Останавливаем таймер this.stopCodeTimer(); // Очищаем поля $('#auth-email').val(''); $('#auth-code').val(''); // Возвращаемся к форме ввода email this.showEmailInput(); // Фокусируемся на поле email setTimeout(() => { $('#auth-email').focus(); }, 100); } /** * Показ состояния загрузки */ showLoading(selector) { $(selector).addClass('loading'); } /** * Скрытие состояния загрузки */ hideLoading(selector) { $(selector).removeClass('loading'); } /** * Показ ошибки */ showError(elementId, message) { $(`#${elementId}`).text(message).addClass('show'); } /** * Скрытие ошибки */ hideError(elementId) { $(`#${elementId}`).removeClass('show').text(''); } /** * Отключение стандартной валидации браузера */ disableBrowserValidation() { // Принудительно убираем красную обводку с необязательных полей при загрузке $('#kpp, #okpo').css({ 'border-color': '#d1d5db', 'box-shadow': 'none', 'outline': 'none' }); // Отключаем стандартную валидацию для необязательных полей $('#kpp, #okpo').on('invalid', function(e) { e.preventDefault(); $(this).removeClass('error'); $(this).css({ 'border-color': '#d1d5db', 'box-shadow': 'none', 'outline': 'none' }); }); // Принудительно убираем красную обводку с необязательных полей при любом событии $('#kpp, #okpo').on('blur focus input change', function() { $(this).removeClass('error'); $(this).css({ 'border-color': '#d1d5db', 'box-shadow': 'none', 'outline': 'none' }); }); // Периодически проверяем и убираем красную обводку setInterval(() => { $('#kpp, #okpo').css({ 'border-color': '#d1d5db', 'box-shadow': 'none', 'outline': 'none' }); }, 100); } /** * Обработчики для скрытия ошибок при вводе */ initErrorHandlers() { // Список всех полей с их error ID const fieldErrorMap = { 'contact_name': 'contact_name-error', 'surname': 'surname-error', 'phone': 'phone-error', 'email': 'email-error', 'organization_name': 'organization_name-error', 'legal_address': 'legal_address-error', 'inn': 'inn-error', 'kpp': 'kpp-error', 'ogrn': 'ogrn-error', 'okpo': 'okpo-error', 'bik': 'bik-error', 'bank_name': 'bank_name-error', 'correspondent_account': 'correspondent_account-error', 'settlement_account': 'settlement_account-error', 'delivery_city': 'delivery_city-error', 'delivery_street': 'delivery_street-error', 'delivery_house': 'delivery_house-error' }; // Добавляем обработчики для каждого поля Object.keys(fieldErrorMap).forEach(fieldId => { $(`#${fieldId}`).on('input focus', () => { this.hideError(fieldErrorMap[fieldId]); }); }); } /** * Инициализация обработчиков сворачивания блока организации */ initOrgCollapseHandlers() { // Обработчик клика на заголовок блока организации $(document).on('click', '.org-section-header', () => { this.toggleOrgSection(); }); } /** * Переключение сворачивания/разворачивания блока организации */ toggleOrgSection() { const $orgSection = $('#organization-section'); const $orgContent = $('#org-section-content'); if ($orgSection.hasClass('collapsed')) { // Разворачиваем $orgSection.removeClass('collapsed'); $orgContent.slideDown(300); } else { // Сворачиваем $orgSection.addClass('collapsed'); $orgContent.slideUp(300); } } /** * Установка названия организации в заголовок блока */ setOrgSectionName(orgName) { const $orgName = $('#org-section-name'); const $orgTitle = $('.org-section-title'); if (orgName) { $orgName.text(` - ${orgName}`).show(); $orgTitle.text('Данные организации'); } else { $orgName.hide(); $orgTitle.text('Данные организации'); } } /** * Установка названия организации в статус клиента */ setStatusOrgName(orgName) { const $statusOrgName = $('#status-org-name'); if (orgName) { $statusOrgName.text(` "${orgName}"`).show(); } else { $statusOrgName.hide(); } } /** * Сворачивание блока организации для зарегистрированных юр.лиц */ collapseOrgSectionForAuthUser() { if (this.clientState.clientType === 'juridical') { const $orgSection = $('#organization-section'); $orgSection.addClass('collapsed'); $('#org-section-content').hide(); } } /** * Показ уведомления */ showNotification(message, type = 'info') { const notification = $(`
${message}
`); $('#widget-notifications').append(notification); // Автоматически скрываем через 5 секунд setTimeout(() => { notification.fadeOut(() => notification.remove()); }, 5000); } /** * Проверка заполненности обязательных полей организации и разворачивание блока */ checkOrgFieldsAndExpand() { const requiredFields = [ 'organization_name', 'legal_address', 'inn', 'ogrn', 'bik', 'bank_name', 'correspondent_account', 'settlement_account' ]; // Проверяем, есть ли пустые обязательные поля let hasEmptyFields = false; for (const fieldId of requiredFields) { const value = $(`#${fieldId}`).val(); if (!value || value.trim() === '') { hasEmptyFields = true; break; } } // Если есть пустые поля, разворачиваем блок организации if (hasEmptyFields) { const $orgSection = $('#organization-section'); $orgSection.removeClass('collapsed'); $('#org-section-content').show(); // Показываем уведомление this.showNotification('Заполните недостающие данные организации для оформления заказа', 'info'); } } /** * Получение данных организации по ИНН через DaData API * * ИНСТРУКЦИЯ ПО НАСТРОЙКЕ: * 1. Зарегистрируйтесь на https://dadata.ru * 2. Получите API ключ в личном кабинете * 3. Замените 'YOUR_DADATA_API_KEY' на ваш реальный ключ * 4. Бесплатный тариф: 100 запросов в сутки */ async fetchCompanyDataByINN(inn) { try { // Очищаем ИНН от лишних символов const cleanINN = inn.replace(/\D/g, ''); if (cleanINN.length !== 10 && cleanINN.length !== 12) { throw new Error('ИНН должен содержать 10 или 12 цифр'); } // Показываем индикатор загрузки this.showFieldLoading('inn', true); // Запрос к DaData API const response = await fetch('https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/party', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Token da92b4f24405aa61c91ead2726c34a99f965376a', 'Accept': 'application/json' }, body: JSON.stringify({ query: cleanINN }) }); if (!response.ok) { throw new Error('Ошибка при запросе к DaData API'); } const data = await response.json(); if (data.suggestions && data.suggestions.length > 0) { const company = data.suggestions[0].data; // Заполняем поля организации (используем краткое название) $('#organization_name').val(company.name?.short_with_opf || company.name?.full_with_opf || ''); // Полный юридический адрес - собираем из всех доступных частей let fullAddress = ''; // Сначала пробуем получить полный адрес с индексом if (company.address?.unrestricted_value) { fullAddress = company.address.unrestricted_value; } else if (company.address?.data?.source) { fullAddress = company.address.data.source; } else if (company.address?.value) { fullAddress = company.address.value; } else { // Собираем адрес из частей const parts = []; if (company.address?.data?.postal_code) parts.push(company.address.data.postal_code); if (company.address?.data?.region) parts.push(company.address.data.region); if (company.address?.data?.city) parts.push(company.address.data.city); if (company.address?.data?.street) parts.push(company.address.data.street); if (company.address?.data?.house) parts.push(company.address.data.house); if (company.address?.data?.flat) parts.push('кв. ' + company.address.data.flat); fullAddress = parts.join(', '); } // Для ИП адрес может быть только город - это нормально $('#legal_address').val(fullAddress); $('#ogrn').val(company.ogrn || ''); $('#okpo').val(company.okpo || ''); // КПП всегда заполняем, если есть if (company.kpp) { $('#kpp').val(company.kpp); } else { // Если КПП нет в данных, оставляем поле пустым $('#kpp').val(''); // Для ИП добавляем подсказку, что КПП не нужен if (company.type === 'INDIVIDUAL') { $('#kpp').attr('placeholder', 'КПП не требуется для ИП'); } } // Делаем поля readonly после автозаполнения this.setFieldsReadonly(['organization_name', 'legal_address', 'ogrn', 'okpo', 'kpp'], true); // Принудительно сбрасываем стили для проблемных полей setTimeout(() => { $('#kpp, #okpo').css({ 'border': '2px solid #e1e5e9', 'box-shadow': 'none', 'outline': 'none' }); }, 100); this.showNotification('Данные организации загружены автоматически', 'success'); } else { throw new Error('Организация с таким ИНН не найдена'); } } catch (error) { this.showNotification(`Ошибка загрузки данных: ${error.message}`, 'error'); } finally { this.showFieldLoading('inn', false); } } /** * Получение банковских данных по БИК через DaData API * * ИНСТРУКЦИЯ ПО НАСТРОЙКЕ: * 1. Используйте тот же API ключ, что и для организаций * 2. Замените 'YOUR_DADATA_API_KEY' на ваш реальный ключ * 3. Бесплатный тариф: 100 запросов в сутки */ async fetchBankDataByBIK(bik) { try { // Очищаем БИК от лишних символов const cleanBIK = bik.replace(/\D/g, ''); if (cleanBIK.length !== 9) { throw new Error('БИК должен содержать 9 цифр'); } // Показываем индикатор загрузки this.showFieldLoading('bik', true); // Запрос к DaData API для банков const response = await fetch('https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/bank', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Token da92b4f24405aa61c91ead2726c34a99f965376a', 'Accept': 'application/json' }, body: JSON.stringify({ query: cleanBIK }) }); if (!response.ok) { throw new Error('Ошибка при запросе к DaData API'); } const data = await response.json(); if (data.suggestions && data.suggestions.length > 0) { const bank = data.suggestions[0].data; // Заполняем банковские поля $('#bank_name').val(bank.name?.payment || bank.name?.full || ''); if (bank.correspondent_account) { $('#correspondent_account').val(bank.correspondent_account); } // Делаем поля readonly после автозаполнения this.setFieldsReadonly(['bank_name', 'correspondent_account'], true); this.showNotification('Банковские данные загружены автоматически', 'success'); } else { throw new Error('Банк с таким БИК не найден'); } } catch (error) { this.showNotification(`Ошибка загрузки банковских данных: ${error.message}`, 'error'); } finally { this.showFieldLoading('bik', false); } } /** * Показать/скрыть индикатор загрузки для поля */ showFieldLoading(fieldId, show) { const $field = $(`#${fieldId}`); const $loading = $field.siblings('.field-loading'); if (show) { if ($loading.length === 0) { $field.after('
⏳ Загрузка...
'); } $field.prop('disabled', true); } else { $loading.remove(); $field.prop('disabled', false); } } /** * Установить поля в режим readonly */ setFieldsReadonly(fieldIds, readonly) { fieldIds.forEach(fieldId => { const $field = $(`#${fieldId}`); $field.prop('readonly', readonly); if (readonly) { $field.addClass('auto-filled'); } else { $field.removeClass('auto-filled'); } }); } /** * Инициализация обработчика сообщений о промокодах */ initCouponMessageHandler() { // Флаг для предотвращения повторной обработки let isProcessing = false; // Следим только за изменениями в DOM const observer = new MutationObserver((mutations) => { if (isProcessing) return; mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // Element node const $node = $(node); // Проверяем только точное сообщение о регистрации const text = $node.text().trim(); if (text === 'Для использования купона необходимо зарегистрироваться') { isProcessing = true; const isUnauthorized = !this.clientState.isAuthorized; const isAuthorizedButUnregistered = this.clientState.isAuthorized && !this.clientState.isRegistered; if (isUnauthorized || isAuthorizedButUnregistered) { $node.replaceWith(this.createCouponSuccessMessage()); } setTimeout(() => { isProcessing = false; }, 100); } // Проверяем вложенные элементы $node.find('*').each((index, element) => { const $element = $(element); const text = $element.text().trim(); if (text === 'Для использования купона необходимо зарегистрироваться') { isProcessing = true; const isUnauthorized = !this.clientState.isAuthorized; const isAuthorizedButUnregistered = this.clientState.isAuthorized && !this.clientState.isRegistered; if (isUnauthorized || isAuthorizedButUnregistered) { $element.replaceWith(this.createCouponSuccessMessage()); } setTimeout(() => { isProcessing = false; }, 100); } }); } }); } }); }); // Начинаем наблюдение за изменениями в DOM observer.observe(document.body, { childList: true, subtree: true }); // Перехватываем через наш метод showNotification const originalShowNotification = this.showNotification; this.showNotification = (message, type) => { if (message === 'Для использования купона необходимо зарегистрироваться') { const isUnauthorized = !this.clientState.isAuthorized; const isAuthorizedButUnregistered = this.clientState.isAuthorized && !this.clientState.isRegistered; if (isUnauthorized || isAuthorizedButUnregistered) { this.showCouponSuccessMessage(); return; } } originalShowNotification.call(this, message, type); }; } /** * Создать элемент сообщения об успешном применении промокода */ createCouponSuccessMessage() { return $(`
Промокод будет применен при оформлении заказа
`); } /** * Скрыть предупреждения InSales в консоли */ hideConsoleWarnings() { // Перехватываем console.warn для скрытия предупреждений о купонах const originalWarn = console.warn; console.warn = function(...args) { const message = args.join(' '); // Скрываем предупреждения о купонах if (message.includes('Вы отключили атвоматическое обновление страницы корзины после применения купона') || message.includes('set_coupon:insales:cart')) { return; // Не выводим это сообщение } // Для всех остальных сообщений используем оригинальный warn originalWarn.apply(console, args); }; } /** * Показать сообщение об успешном применении промокода */ showCouponSuccessMessage() { // Удаляем предыдущие сообщения о промокоде $('.coupon-success-message').remove(); // Создаем новое сообщение const $message = this.createCouponSuccessMessage(); // Ищем поле промокода и добавляем сообщение после него const $couponField = $('input[name="coupon_code"], input[name="coupon"], input[placeholder*="промокод"], input[placeholder*="Промокод"]'); if ($couponField.length > 0) { $couponField.after($message); } else { // Если поле не найдено, добавляем в конец формы $('.widget-main').append($message); } // Автоматически скрываем сообщение через 5 секунд setTimeout(() => { $message.fadeOut(300, function() { $(this).remove(); }); }, 5000); } } // Глобальная обработка ошибок для предотвращения поломки сайта window.addEventListener('error', function(event) { console.error('UNIFIED_WIDGET_V3: Глобальная ошибка JavaScript:', event.error); // Предотвращаем поломку сайта из-за внешних ошибок event.preventDefault(); }); // Обработка неправильных ссылок на изображения $(document).ready(function() { // Исправляем неправильные ссылки на изображения $('img[src*="${item.image}"]').each(function() { $(this).attr('src', '').hide(); console.warn('UNIFIED_WIDGET_V3: Исправлена неправильная ссылка на изображение'); }); }); // Инициализация виджета при загрузке DOM $(document).ready(function() { try { window.unifiedWidget = new UnifiedWidgetV3(); } catch (error) { console.error('UNIFIED_WIDGET_V3: Ошибка инициализации виджета:', error); } }); } catch(error) { console.error('Widget "widget-type_CART_ONLY"', error) } } catch(error) { console.error('Widget "widget-type_CART_ONLY"', error) }