Меню

Bitrix sale ajax locations is not a component ошибка

 

Пользователь 300902

Заглянувший

Сообщений: 3
Авторитет:

1

Рейтинг пользователя:

0

Регистрация: 24.12.2014

При выборе местоположения во время оформлении заказа появляется сообщение об ошибке «К сожалению, произошла внутренняя ошибка».

POST-запрос на bitrix/components/bitrix/sale.location.selector.steps/get.php возвращает
{‘result’:true,’errors’:[],’data’:{‘ITEMS’:[{‘DISPLAY’:’Дальний Восток’,’VALUE’:’2907′,’CODE’:’0000028103′,’IS_PARENT’:true},{‘DISPLAY’:’Крымский федеральный округ’,’VALUE’:’3123′,’CODE’:’0000028113′,’IS_PARENT’:true},{‘DISPLAY’:’Поволжье’,’VALUE’:’1505′,’CODE’:’0000028068′,’IS_PARENT’:true},{‘DISPLAY’:’РФ’,’VALUE’:’3159′,’CODE’:’rf’,’IS_PARENT’:false},{‘DISPLAY’:’Северный Кавказ’,’VALUE’:’1326′,’CODE’:’0000028060′,’IS_PARENT’:true},{‘DISPLAY’:’Северо-Запад’,’VALUE’:’767′,’CODE’:’0000028042′,’IS_PARENT’:true},{‘DISPLAY’:’Сибирь’,’VALUE’:’2414′,’CODE’:’0000028090′,’IS_PARENT’:true},{‘DISPLAY’:’Урал’,’VALUE’:’2171′,’CODE’:’0000028083′,’IS_PARENT’:true},{‘DISPLAY’:’Центр’,’VALUE’:’2′,’CODE’:’0000028024′,’IS_PARENT’:true},{‘DISPLAY’:’Юг’,’VALUE’:’1093′,’CODE’:’0000028053′,’IS_PARENT’:true}],’ETC’:[]}}

Я так понимаю, что ошибка выводится из BX.chainedSelectorsSLS.
Вопрос: почему выводится ошибка, если  фактически список местоположений с сервера приходит, и каким образом можно эту проблему можно решить?


3

ответов

— проверить вызов на наличие левых символов
— попробовать разместить компонент на отдельной странице
— проверить физическое наличие компонентов


Alexander Ostrovsky

— проверить вызов на наличие левых символов
— попр…

меня смущает что нет component.php или class.php/
Но посмотрев на другие проекты тоже не обнаружил их


g000d WIN

меня смущает что нет component.php или class.php/

ну вот отсутствие компонент.пхп и выдает эту ошибку.
у меня есть такой файл в этом компоненте.

Похожие вопросы

  • Маркетплейс
  • Документация
  • Блог
  • Новости
  • Софт
  • Профиль
  • 30.05.2018

    Периодически на проектах встречается задача внедрить не стандартную логику определения местоположения в компоненте оформления заказа.

    Большинство решений предлагают, по сути, эмулировать смену уже на отображенной странице с последующей перегрузкой при помощи JavaScrip. Вот еще один вариант, но без лишних перегрузок. Решение работает на основе обработчика события компонента bitrix:sale.order.ajax OnSaleComponentOrderProperties.

    В первую очередь выносим идентификаторы свойств, которые нам понадобятся в доступные переменные

    class ProjectConfig
    {
        /**
         * ИД свойства заказа Почтовый индекс
         */
        const PROPERTY_ZIP_ID = 4;
        /**
         * ИД свойства заказа Местоположение
         */
        const PROPERTY_LOCATION_ID = 6;
    }
    

    Далее добавляем метод обработчика события. Для примера «нестандартной» логики я взял хранение местоположение в свойстве UF_LOCATION пользователя сайта. Компонент оперирует кодом местоположения из-за этого первым делом по значению UF_LOCATION получаем код нужного местоположения. Следующим шагом, необходимо проверить почтовый индекс на соответствие местоположению. Полученные значения местоположения и индекса заносим в соответствующие переменные массива $arUserResult[‘ORDER_PROP’].

    class SaleHandlers
    {
    	private static $cacheZipTimout = 300000000;
    
        /**
         * Проверяем почтовый индекс по местоположению. Если не входит в список - возвращаем первый из списка
         * @param $locationId int ИД местоположения
         * @param $zip int индекс
         */
        public static function checkZip ($locationId,$zip){
            $arResult = array();
            $cache = BitrixMainDataCache::createInstance();
            if ($cache->initCache(self::$cacheZipTimout, 'location_index_'.$locationId, 'zip_locations'))
            {
                $arResult = $cache->getVars();
            }
            elseif ($cache->startDataCache())
            {
                $rsZip = CSaleLocation::GetLocationZIP($locationId);
                while ($arZip = $rsZip->fetch()) {
                    $arResult[$arZip['ZIP']] = true;
                }
                $cache->endDataCache($arResult);
            }
            reset($arResult);
            return (array_key_exists($zip,$arResult)) ? $zip : key($arResult);
        }
    
        public static function OnSaleComponentOrderProperties(&$arUserResult, $request, &$arParams, &$arResult)
        {
            global $USER;
            if ($USER->IsAuthorized()) {
                $arUser= MainUserTable::getList([
                    'filter' => ['ID' => $USER->GetID()],
                    'select' => [
                        'UF_LOCATION',
                        'LOCATION_CODE'=>'LOCATION.CODE'
                    ],
                    'runtime' => [
                        new MainEntityReferenceField(
                            'LOCATION',
                            'BitrixSaleLocationLocationTable',
                            [
                                'this.UF_LOCATION' => 'ref.ID'
                            ]
    
                        )
                    ]
                ])->fetch();
                $arUserResult['ORDER_PROP'][Opt::PROPERTY_LOCATION_ID] = $arUser['LOCATION_CODE'];
                $arUserResult['ORDER_PROP'][Opt::PROPERTY_ZIP_ID] = self::checkZip(
    				$arUser['UF_LOCATION'],
    				$arUserResult['ORDER_PROP'][Opt::PROPERTY_ZIP_ID]
    			);
            }
        }
    }
    

    Следующий шаг зарегистрировать классы и обработчик в init.php

    use BitrixMainEventManager;
    
    BitrixMainLoader::registerAutoLoadClasses(null, array(
    	'ProjectConfig' => '/local/php_interface/classes/projectconfig.php',
    	'SaleHandlers' => '/local/php_interface/handlers/salehandlers.php',
    ));
    
    $eventManager = EventManager::getInstance();
    $eventManager->addEventHandler(
    	"sale", 
    	'OnSaleComponentOrderProperties', 
    	array("SaleHandlers", "OnSaleComponentOrderProperties")
    );
    

    Следующая задача изменить страницу оформления заказа. Стандартный компонент оформления заказа выполнен практически полностью на JavaScript. Нам необходимо запретить редактирование местоположения. Для этого необходимо переопределить метод объекта BX.Sale.OrderAjaxComponent сохраняем метод editActiveRegionBlock. И создаем альтернативный, в котором сначала проверяем необходимость отображения блока. Если вывод полей редактирование то просто выходим, иначе вызываем родительский editActiveRegionBlock.

    Для этого в каталоге с шаблоном компонента создаем скрипт (например order_ajax_ext.js) содержащий следующий код:

    (function () {
    	'use strict';
    
    	BX.namespace('BX.Sale.OrderAjaxComponentExt');
    
    	BX.Sale.OrderAjaxComponentExt = BX.Sale.OrderAjaxComponent;
    	BX.Sale.OrderAjaxComponentExt.parentEditActiveRegionBlock = BX.Sale.OrderAjaxComponent.editActiveRegionBlock;
    	BX.Sale.OrderAjaxComponentExt.editActiveRegionBlock =function (activeNodeMode) {
    		if (this.initialized.region) {
    			return;
    		}
    		BX.Sale.OrderAjaxComponent.parentEditActiveRegionBlock(activeNodeMode);
    	};
    })();
    

    И подключаем его в template.php после подключения штатного order_ajax.js командой

    $this->addExternalJs($templateFolder.'/order_ajax_ext.js');
    

    Обратите внимание: предотвращается редактирование всей информации блока «Регион доставки», т.е. если вам необходимо дать доступ на редактирование части полей этого блока — надо более детально разобрать и исправить метод BX.Sale.OrderAjaxComponent.editActiveRegionBlock. Для этого скопировать его код в новый метод из примера выше, и внести необходимые правки. В этом случае возможно родительский метод вызывать нет необходимости (зависит от внесенных правок)

    Все записи


    Перед использованием этой компоненты создайте поле корзины LOCATION — тип местоположение

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    <?$APPLICATION->IncludeComponent(

    «bitrix:sale.location.selector.search»,

    «basket»,

    Array(

    «COMPONENT_TEMPLATE» => «.default»,

    «ID» => »,

    «CODE» => $_REQUEST[‘LOCATION’],

    «INPUT_NAME» => «LOCATION»,

    «PROVIDE_LINK_BY» => «code», //эта настройка отвечает за нули перед

    «JSCONTROL_GLOBAL_ID» => «»,

    «JS_CALLBACK» => «»,

    «FILTER_BY_SITE» => «Y»,

    «SHOW_DEFAULT_LOCATIONS» => «Y»,

    «CACHE_TYPE» => «N»,

    «CACHE_TIME» => «36000000»,

    «FILTER_SITE_ID» => «s1»,

    «INITIALIZE_BY_GLOBAL_EVENT» => «»,

    «SUPPRESS_ERRORS» => «N»

    )

    );?>

    CODE =>$_REQUEST[‘LOCATION’] — эта строка важная.

    если мы его обновляем через аjax — то в теле будет приходить js скрипт. Он должне выполниться такой командой

    $(document).on(‘click’, ‘.quick-location-tag’, function(){

        setTimeout(function() { ajaxUpdate(); }, 300);

    });

    Несколько полезных ссылок, в котором есть работа с локацией:
    работа с локациями — полноценный компонент оформления заказа (самописный)

    Делаем собственный sale.order.ajax на D7

    еще код — тоже есть локации в кастомнок мом
    https://phpjs.ru/2018/07/08/bitrix-d7-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%BA%D0%B0%D0%B7%D0%B0/

    Либо

    На чтение 6 мин. Опубликовано 15.12.2019

    Данный пост будет посвящено новому компоненту sale.order.ajax и его шаблоны с js-файлом на 7,5+ тыс. строк. Пост будет собирательным и обновляемым — все, что буду находить в сети, и буду считать интересным и полезным — пуду публиковать тут.

    С переходом битрикса на новое ядро, D7 для многих разработчиков начались непростые времена. С одной стороны, новое ядро предназначено для ускорения работы сайтов, с другой стороны, многие механизмы, отлично работавшие на старом ядре, перестали работать.

    Компонент bitrx:sale.order.ajax был полностью переработан под использование в новом ядре. В том числе был полностью переработан и шаблон данного компонента.

    Вся логика отрисовки шаблона нового компонента возложена на клиентскую часть и выполняется через файл order_ajax.js (более 7700 строк некомментированного кода:().

    Юрий Волошин на форуме разработчиков писал:

    Т.к. «отрисовка» в новом шаблоне происходит через js, кастомизировать следует файл order_ajax.js.

    У каждого блока существует два состояния — активное и скрытое.

    Активная секция должна всегда находиться в DOM на странице, т.к. в ней находятся нужные нам input’ы для рассчета заказа. Неактивный блок формируется на основе активного, и его нахождение на странице не обязательно.

    Основной метод — это editOrder, который вызывает редактирование каждого блока (секции — editSection). editSection — определяет текущую активность блока и вызывает конкретный метод для конкретного блока, напр. для блока оплат — editPaySystemBlock.

    Если блок активен — в видимой части отрисовывается выбор платежных систем (editActivePaySystemBlock — на отрисовку пс можно повлиять здесь), если блок неактивен — editFadePaySystemBlock, который в свою очередь вызовет отрисовку в скрытой области на странице блока с платежными системами, и на основе его сформирует в видимой части информацию о выбранной пс (скрытый/пройденный шаг).

    Это как «в общем» работает js и как его можно кастомизировать.

    Можно добавить, что расположение блоков можно поменять в файле template.php (блоки выделены комментариями)

    В принципе, идея тут заложена верная, но для решения конкретных задач очень много не хватает. И об этом свидетельствуют переписки разработчиков на партнерском форуме.

    Вот несколько решений конкретных задач кастомизации шаблона компонента.

    Содержание

    1. Определение местоположения пользователя в автоматическом режиме
    2. Скрыть какое-то свойство
    3. Исключить из показа нулевой цены за доставку
    4. Вариант кастомизации компонента оформления от Александры Плотниковой
    5. Местоположение — простое заполнение
    6. Описание
    7. Установка компонента

    Определение местоположения пользователя в автоматическом режиме

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

    А вот такая модификация позволяет определить местоположение только по названию города:

    Скрыть какое-то свойство

    Если необходимо скрыть какое-то свойство, например, свойство индекс — задать значение по умолчанию и не показывать пользователям это поле, то можно внести корректировку в JS. В функции getPropertyRowNode после switch (propertyType) добавляем скрытие данного свойства:

    Исключить из показа нулевой цены за доставку

    Очень распространена ситуация, когда при покупке в интернет-магазине пользователь оплачивает именно товар, а доставку оплачивает уже непосредственно транспортной компании. Но если поставить стоимость доставки равной нулю, то визуально будет выглядеть так, что доставка пользователю ничего не будет стоить, что не корректно. Для исключения такой ситуации можно просто скрыть этот ноль из выдачи. Для этого нужно внести небольшие правки в файл order_ajax.js шаблона.

    В функции getDeliveryPriceNodes: function(delivery) в блоке «else» заменяем. Вместо:

    Так мы спрячем нулевую цену из свернутого блока с выбранной доставкой.

    Дальше нужно скрыть нули в списке служб доставки. Для этого в функции createDeliveryItem: function(item) делаем строгую проверку на ноль. Вместо:

    И последним нужно скрыть нулевую доставку из итоговых сумм. Для этого в функции editTotalBlock: function() также ставим строгую проверку на ноль. Вместо

    В результате нулевая доставка не будет показана пользователю.

    Вариант кастомизации компонента оформления от Александры Плотниковой

    Александра написала мне в обратную связь о своем интересном кейсе по доработке одношагового заказа. Пока делюсь ссылкой, позже проработаю и выпишу текстом

    Благодарю за внимание! Делитесь вашими замечаниями в комментариях ниже.

    Периодически на проектах встречается задача внедрить не стандартную логику определения местоположения в компоненте оформления заказа.

    Большинство решений предлагают, по сути, эмулировать смену уже на отображенной странице с последующей перегрузкой при помощи JavaScrip. Вот еще один вариант, но без лишних перегрузок. Решение работает на основе обработчика события компонента bitrix:sale.order.ajax OnSaleComponentOrderProperties.

    В первую очередь выносим идентификаторы свойств, которые нам понадобятся в доступные переменные

    Далее добавляем метод обработчика события. Для примера «нестандартной» логики я взял хранение местоположение в свойстве UF_LOCATION пользователя сайта. Компонент оперирует кодом местоположения из-за этого первым делом по значению UF_LOCATION получаем код нужного местоположения. Следующим шагом, необходимо проверить почтовый индекс на соответствие местоположению. Полученные значения местоположения и индекса заносим в соответствующие переменные массива $arUserResult[‘ORDER_PROP’].

    Новые технологии — это просто

    пр. Обуховской обороны, д. 70, корп 2,оф. 488 newit@newit.ru

    Местоположение — простое заполнение

    Описание

    Компонент позволяет настроить удобный выбор города из списка местоположений.


    Есть возможность автоматического заполнения индекса города если он заполнен у выбранного города, индекс берется последний из списка в городе .

    Данный компонент можно использовать в любом месте где необходимо выбрать город из списка местопложений, например в компоненте создания профиля покупателя. Настраивать его можно через визуальный редактор.

    Установка компонента

    Устанваливаем модуль через marketplace.
    Для настройки компонента ‘простой выбор местоположения’ необходимо заменить стандартный компонент sale.ajax.locations.
    Рассмотрим на примере одношагового оформления заказа sale.order.ajax.
    Необходимо найти где в шаблоных sale.order.ajax выводится компонент sale.ajax.locations , в данном компоненте вывод находится в файле props.php
    находим строчки

    Значение переменных компонента NewIt:sale.locations.autocomplete.
    CITY_INPUT_NAME — Имя поля формы города
    LOCATION_VALUE — Выбранное значение города
    JQUERY — Если в проекте не используется JQUERY то надо поставить «Y» .
    FORM_ID_TEXT_CITY — ID текстового поля формы для введения города.
    MAXITEMSTOSHOW — Максимальное отображение городов в списке
    CITY_EMPTY_ID — Страна для «другого города» по умолчанию
    CITY_EMPTY_ID_NAME — Название в списке для «другого города»
    MINCHARS — Минимальное количество символов для отправки запроса
    ONCITYCHANGE — функция которая срабатывает при выборе города
    INDEX_ON — Включить заполнение поля индекса

    INDEX_PLACE — Служит для назначения id или class поля с индексом.

    Так же необходимо удалить компонент sale.ajax.locations в конце файла props.php

    По аналогии можно заменить выбор города в других компонента.
    Обятельным условием работы компонента является заполненные местоположения, для того что бы заполняло индексы необходимы заполненные индексы у городов.


    Работа с sale.order.ajax в Битрикс

    Заказать сайт

    Обратившись ко мне вы получите надежного и сведущего исполнителя, который быстро и качественно реализует любую задачу для Вас и Вашего бизнеса.

    Навигация по разделам:

    • Кастомизация sale.order.ajax
    • Открыть все блоки и убрать лишнее
    • Определение местоположения пользователя в автоматическом режиме
    • Скрыть какое-то свойство
    • Исключить из показа нулевой цены за доставку
    • Перенос полей «Адрес доставки», «Комментарии», «Местоположения» в sale.order.ajax
    • Перенос поля «Индекс» в блок пользовательских свойств в sale.order.ajax
    • Запрет Битриксу выбирать доставку по умолчанию
    • order_ajax_ext.js
    • Краткое описание функций ошибок sale.order.ajax
    • Выполнение кода после перезагрузки страницы
    • Программная смена города в sale.order.ajax на javascript
    • Расчет стоимости доставки для всех служб доставки
    • Получение стоимости доставки для продукта после применения скидок и правил корзины
    • Делаем бесплатную доставку по России с условием
    • Делаем собственный валидатор поля на примере кастомной валидации поля «Телефон»
    • Полезные переменные order_ajax.js
    • Массив соответствия международных двухбуквенных кодов стран
    • Альтернативные (кастомные) sale.order.ajax
    • Ошибка в Битрикс при оформлении заказа window.__jsonp_ymaps_map is not a function

    Данный материал является агрегативным и написан более для себя. По мере работы и накопления опыта обновляю его.

    Нужно четко понимать, что не все описанные в материале способы и методы сработают у вас. Версий Битрикс много и код довольно значительно меняется. Однажды сработавшее решение не обязательно сработает в следующих обновлениях. Например, у меня часть методов работавших в 18 версии перестали работать в 20.

    В процессе работ заметил, что использование приведенных методов может вызывать ошибки исполнения в случаях, если изменен порядок следования блоков, включен режим быстрого редактирования или в параметрах компонента установлены опции «Автозаполнение оплаты и доставки по предыдущему заказу» или «Режим совместимости для предыдущего шаблона»

    Кастомизация sale.order.ajax

    Практически никого из моих клиентов не устраивает внешний вид sale.order.ajax. Здесь я опишу большую часть приемов, которыми пользуюсь сам при кастомизации данного непростого компонента.

    В файле template.php блоки заказа отмечены комментариями:

    BASKET ITEMS BLOCK - корзина заказа - таблица товаров
    REGION BLOCK - выбор типа плательщика и ввод местоположения - города покупателя
    DELIVERY BLOCK - выбор службы доставки
    PAY SYSTEMS BLOCK - выбор способа оплаты
    BUYER PROPS BLOCK - форма с полями для ввода данных покупателя
    ORDER SAVE BLOCK - итог и кнопка подтверждения заказа

    Чтобы все блоки были активны, а не только первый комментируем вторую строчку в данном блоке

    initFirstSection: function()
    {
    	var firstSection = this.orderBlockNode.querySelector('.bx-soa-section.bx-active');
    	//BX.addClass(firstSection, 'bx-selected');
    	this.activeSectionId = firstSection.id;
    },

    Если доставки выдают ошибку ошибку вычислений, то блоки с доставкой скрываются. Отключить это можно закомментировав следующий код:

    /*if (this.result.DELIVERY.length > 0)
    {
    	BX.addClass(this.deliveryBlockNode, 'bx-active');
    	this.deliveryBlockNode.removeAttribute('style');
    }
    else
    {
    	BX.removeClass(this.deliveryBlockNode, 'bx-active');
    	this.deliveryBlockNode.style.display = 'none';
    }*/

    Убираем сокрытие блоков при авторизации (при выключенной в настройках опции «регистрировать вместе с оформлением заказа»).

    if (this.result.SHOW_AUTH && section.id != this.authBlockNode.id && section.id != this.basketBlockNode.id)
    	section.style.display = 'none';
    else if (section.id != this.pickUpBlockNode.id)
    	section.style.display = '';

    Открыть все блоки и убрать лишнее

    Чтобы раскрыть все скрытые блоки можно воспользоваться следующими методами (лично использовал на версиях до 20):

    Ищем строку var active = section.id == this.activeSectionId и меняем ее на

    var active = true

    Далее отключаем реагирование на клик по заголовку блока

    BX.unbindAll(titleNode);
    
    if (this.result.SHOW_AUTH)
    {
    	BX.bind(titleNode, 'click', BX.delegate(function(){
    		this.animateScrollTo(this.authBlockNode);
    		this.addAnimationEffect(this.authBlockNode, 'bx-step-good');
    	}, this));
    }
    else
    {
    	BX.bind(titleNode, 'click', BX.proxy(this.showByClick, this));
    	editButton = titleNode.querySelector('.bx-soa-editstep');
    	editButton && BX.bind(editButton, 'click', BX.proxy(this.showByClick, this));
    }

    Чтобы всегда были открыты Регион и Пользователь

    if (this.activeSectionId !== this.regionBlockNode.id)
    	this.editFadeRegionContent(this.regionBlockNode.querySelector('.bx-soa-section-content'));
    
    if (this.activeSectionId != this.propsBlockNode.id)
    	this.editFadePropsContent(this.propsBlockNode.querySelector('.bx-soa-section-content'));

    Чтобы убрать кнопки Далее/Назад

    node.appendChild(
    	BX.create('DIV', {
    		props: {className: 'row bx-soa-more'},
    		children: [
    			BX.create('DIV', {
    				props: {className: 'bx-soa-more-btn col-xs-12'},
    				children: buttons
    			})
    		]
    	})
    );

    Чтобы убрать ссылки «изменить» у всех блоков в editOrder (~2222 стр.)

    var editSteps = this.orderBlockNode.querySelectorAll('.bx-soa-editstep'), i;
     for (i in editSteps) {
    	if (editSteps.hasOwnProperty(i)) {
    	   BX.remove(editSteps[i]);
    	}
     }

    Чтобы скрыть уведомление о том, что данный заполнены автоматически добавляем в конец файла style.css

    .alert.alert-warning{display:none;}

    Определение местоположения пользователя в автоматическом режиме

    use BitrixMainEventManager; 
    $eventManager = EventManager::getInstance();
    $eventManager->addEventHandler("sale", "OnSaleComponentOrderProperties", Array("Example", "OnSaleComponentOrderProperties"));
    
    class Example
    {
       /**
       * У меня по условию задачи известны ID и NAME местоположения
       */
       static $curCityId = XX;  // числовое значение идентификатора местоположения
       static $curCityName = 'Название города';
       
       /**
       * ID свойств заказа   
       */
       const PROP_LOCATION = 6; 
       const PROP_ZIP = 4; 
       const PROP_LOCATION_NAME = 5;
    
    
       static function OnSaleComponentOrderProperties(&$arFields)
       {
          $rsLocaction = CSaleLocation::GetLocationZIP(self::$curCityId); 
          $arLocation = $rsLocaction->Fetch(); 
          $arFields['ORDER_PROP'][self::PROP_ZIP] = $arLocation['ZIP'];
          $arFields['ORDER_PROP'][self::PROP_LOCATION_NAME] = self::$curCityName;
          $arFields['ORDER_PROP'][self::PROP_LOCATION] = CSaleLocation::getLocationCODEbyID(self::$curCityId);
       }
    }

    А вот такая модификация позволяет определить местоположение только по названию города:

    class Example {
       static function OnSaleComponentOrderProperties(&$arFields)
       {
          static $curCityName = 'Название города';
          const PROP_LOCATION = 6;  // - Идентификатор свойства с местоположением
          static function OnSaleComponentOrderProperties(&$arFields)
          {
             $res = BitrixSaleLocationLocationTable::getList(array(
             'filter' => array('=NAME.NAME' => self::$curCityName, '=NAME.LANGUAGE_ID' => LANGUAGE_ID),
             'select' => array('CODE' => 'CODE', 'NAME_RU' => 'NAME.NAME', 'TYPE_CODE' => 'TYPE.CODE') //'*', 
             ));
             while($item = $res->fetch())
             {            
                $code = $item["CODE"];
             }
             $arFields['ORDER_PROP'][self::PROP_LOCATION] = $code;
          }
       }
    }

    Скрыть какое-то свойство

    Если необходимо скрыть какое-то свойство, например, свойство индекс — задать значение по умолчанию и не показывать пользователям это поле, то можно внести корректировку в JS. В функции getPropertyRowNode после switch (propertyType) добавляем скрытие данного свойства:

    if(property.getId()==6){// идентификатор скрываемого свойства
       var addressInput=propsItemNode.querySelector('textarea');
       propsItemNode.style='display:none;';
       addressInput.value='нужное значение';
    }

    Скрываем сообщение «Вы заказывали в нашем интернет-магазине, поэтому мы заполнили все данные автоматически»

    Идем в функцию checkNotifications на ~750 стр и находим код

    informer.appendChild(
    	BX.create('DIV', {
    		props: {className: 'row'},
    		children: [
    			BX.create('DIV', {
    				props: {className: 'col-xs-12'},
    				style: {position: 'relative', paddingLeft: '48px'},
    				children: [
    					BX.create('DIV', {props: {className: 'icon-' + className}}),
    					BX.create('DIV', {html: text})
    				]
    			})
    		]
    	})
    );
    BX.addClass(informer, 'alert alert-' + className);

    И обрамляем данный код доп. проверкой

    if(this.params.MESS_SUCCESS_PRELOAD_TEXT.indexOf('Вы заказывали в нашем инте') === false) {
    	//Код выше ставим сюда
    }

    Скрываем сообщение «Выберите свой город в списке. Если вы не нашли свой город, выберите «другое местоположение», а город впишите в поле «Город»

    Идем в функцию getDeliveryLocationInput и комментируем код:

    /*
    if (location && location[0])
    {
    	node.appendChild(
    		BX.create('DIV', {
    			props: {className: 'bx-soa-reference'},
    			html: this.params.MESS_REGION_REFERENCE
    		})
    	);
    }
    */

    Или с помощью стилей скрываем класс bx-soa-reference

    #bx-soa-region .bx_soa_location .bx-soa-reference{display: none}

    Исключить из показа нулевой цены за доставку

    В функции getDeliveryPriceNodes: function(delivery) в блоке «else» заменяем. Вместо:

    priceNodesArray = [delivery.PRICE_FORMATED];

    пишем:

    if(delivery.PRICE>0) priceNodesArray = [delivery.PRICE_FORMATED];

    Так мы спрячем нулевую цену из свернутого блока с выбранной доставкой.

    Дальше нужно скрыть нули в списке служб доставки. Для этого в функции createDeliveryItem: function(item) делаем строгую проверку на ноль. Вместо:

    if (item.PRICE >= 0 || typeof item.DELIVERY_DISCOUNT_PRICE !== 'undefined')

    пишем:

    if (item.PRICE > 0 || typeof item.DELIVERY_DISCOUNT_PRICE !== 'undefined')

    А также вместо:

    else if (deliveryCached && (deliveryCached.PRICE >= 0 || typeof deliveryCached.DELIVERY_DISCOUNT_PRICE !== 'undefined'))

    пишем:

    else if (deliveryCached && (deliveryCached.PRICE > 0 || typeof deliveryCached.DELIVERY_DISCOUNT_PRICE !== 'undefined'))

    И последним нужно скрыть нулевую доставку из итоговых сумм. Для этого в функции editTotalBlock: function() также ставим строгую проверку на ноль. Вместо

    if (parseFloat(total.DELIVERY_PRICE) >= 0 && this.result.DELIVERY.length)

    пишем:

    if (parseFloat(total.DELIVERY_PRICE) > 0 && this.result.DELIVERY.length)

    В результате нулевая доставка не будет показана пользователю.

    Перенос полей «Адрес доставки», «Комментарии», «Местоположения» в sale.order.ajax

    Убираем поле «Адрес доставки» из вывода блока «Пользователь»

    Описанные у моих коллег способы в моем случае не сработали. Решил пойти некрасивым, но действенным способом

    Идем в функцию editPropsItemsи находим код:

    if (
    	this.deliveryLocationInfo.loc == property.getId()
    	|| this.deliveryLocationInfo.zip == property.getId()
    	|| this.deliveryLocationInfo.city == property.getId()
    )

    Меняем его на:

    if (
    	this.deliveryLocationInfo.loc == property.getId()
    	|| this.deliveryLocationInfo.zip == property.getId()
    	|| this.deliveryLocationInfo.city == property.getId()
    	|| property.getName()=='Адрес доставки (улица, дом, квартира)' //где property.getName() приравниваем к названию поля адреса в вашей системе
    )

    Перенос поля «Индекс» в блок пользовательских свойств в sale.order.ajax

    В базовом стандартном шаблоне поле индекс достаточно легко перенести. Давайте перенесем поле Индекс из блока местоположений в блок пользовательских свойств

    Находим функцию getDeliveryLocationInput (~4451 стр.) и комментируем код this.getZipLocationInput(node);

    Идем в функцию editPropsItems (~6728 стр.) и перед propsNode.appendChild(propsItemsContainer) добавляем код this.getZipLocationInput(propsItemsContainer);.

    По идее этого должно хватить. Но если все равно не получается, то перейдите по адресу /bitrix/admin/sale_order_props.php?lang=ru и в поле Индекс переведите его а группу свойств Личные данные

    Вывод поля «Адрес доставки» в блоке «Доставка»

    Идем в функцию editDeliveryInfo и в самый конец добавляем код:

    var deliveryItemsContainer = BX.create('DIV', {props: {className: 'col-sm-12 bx-soa-delivery'}}),
    	group, property, groupIterator = this.propertyCollection.getGroupIterator(), propsIterator;
    
    if (!deliveryItemsContainer)
    	deliveryItemsContainer = this.propsBlockNode.querySelector('.col-sm-12.bx-soa-delivery');
    
    while (group = groupIterator())
    {
    	propsIterator =  group.getIterator();
    	while (property = propsIterator())
    	{
    		if (property.getName()=='Адрес доставки (улица, дом, квартира)') { //Если свойство совпадает с названием поля адреса в вашей системе
    
    			this.getPropertyRowNode(property, deliveryItemsContainer, false); //вставляем свойство в подготовленный контейнер
    			deliveryNode.appendChild(deliveryItemsContainer); //контейнер вместе со свойством в нём добавляем в конце блока с описанием (deliveryInfoContainer)
    
    		}
    	}
    }

    Переносим поле «Адрес доставки» в отдельный новый(!) блок

    Данная необходимость возникает, например, в шаблонах Аспро, где поле «Адрес доставки» в блоке с выбором доставок выглядит чужеродно и некрасиво

    В шаблоне template.php компонента sale.order.ajax добавляем новый блок в наиболее удобное нам место

    <!--	ADRESS BLOCK	-->
    <div id="bx-soa-adress-dostavki" data-visited="false" class="bx-soa-section bx-active">
    	<div class="bx-soa-section-title-container">
    		<h2 class="bx-soa-section-title col-sm-9">
    			<span class="bx-soa-section-title-count"></span>Данные для доставки
    		</h2>
    	</div>
    	<div class="bx-soa-section-content container-fluid"></div>
    </div>
    

    Идем в файл order_ajax.js и в функции init на ~77 стр добавляем вызов вновь добавленного блока

    this.orderAdresBlockNode = BX('bx-soa-adress-dostavki');

    Скрываем вывод поля «Адрес доставки» как показано выше

    if (
    	this.deliveryLocationInfo.loc == property.getId()
    	|| this.deliveryLocationInfo.zip == property.getId()
    	|| this.deliveryLocationInfo.city == property.getId()
    	|| property.getName()=='Адрес доставки' //где property.getName() приравниваем к названию поля адреса в вашей системе
    )

    Реализуем вывод нового поля в дополнительном блоке. В стандартном случае придется учесть, что поле «Адрес доставки» генерируется отдельно для каждого из типов плательщиков, имеет разный id свойства, а сам наш блок надо скрывать, если выбраны оплаты и доставки при которых поле «Адрес доставки» не выводится. Поэтому код будет таким.

    Идем в функцию editDeliveryInfo (любую аналогичную или свою) и после строки deliveryNode.appendChild(deliveryInfoContainer) или в конце функции добавляем:

    var deliveryItemsContainer = BX.create('DIV', {props: {className: 'col-sm-12 bx-soa-delivery'}}),
    	group, property, groupIterator = this.propertyCollection.getGroupIterator(), propsIterator;
    
    if (!deliveryItemsContainer)
    	deliveryItemsContainer = this.propsBlockNode.querySelector('.col-sm-12.bx-soa-delivery');
    
    while (group = groupIterator())
    {
    	propsIterator =  group.getIterator();
    	while (property = propsIterator())
    	{
    
    		var personType = this.getSelectedPersonType();
    
    		this.orderAdresBlockNode.querySelector('.bx-soa-section-content').innerHTML = '';
    
    		if (property.getName()=='Адрес доставки' && personType.ID === '1' && property.getId() == '7') {
    			this.getPropertyRowNode(property, deliveryItemsContainer, false); //вставляем свойство в подготовленный контейнер
    			this.orderAdresBlockNode.querySelector('.bx-soa-section-content').appendChild(deliveryItemsContainer); 
    
    			this.orderAdresBlockNode.classList.remove('hidden');
    		}else if (property.getName()=='Адрес доставки' && personType.ID === '2' && property.getId() == '19') { 
    			this.getPropertyRowNode(property, deliveryItemsContainer, false); //вставляем свойство в подготовленный контейнер
    			this.orderAdresBlockNode.querySelector('.bx-soa-section-content').appendChild(deliveryItemsContainer);
    
    			this.orderAdresBlockNode.classList.remove('hidden');
    		}else{
    			this.orderAdresBlockNode.classList.add('hidden');
    		}
    
    	}
    }

    Код не самый красивый и правильный, но работает

    Переносим поле «Комментарии к заказу» в конец формы

    Идем в функцию editActivePropsBlock и комментируем строчку:

    this.editPropsComment(propsNode);

    Для вывода поля ищем в шаблоне функцию последнего блока. В моем случае это вывод состава заказа. Ищем функцию editBasketItems и в самый конец дописываем:

    this.editPropsComment(basketItemsNode);

    Переносим поле «Местоположения» в блок пользовательских свойств

    По умолчанию данное поле искуственно исключено из блока пользовательских свойств. Чтобы его вернуть на место идем в функцию editPropsItems и удаляем код:

    if (
    	this.deliveryLocationInfo.loc == property.getId()
    	|| this.deliveryLocationInfo.zip == property.getId()
    	|| this.deliveryLocationInfo.city == property.getId()
    )
    	continue;

    Запрет Битриксу выбирать доставку по умолчанию

    В случае, если у пользователя есть сохраненный профиль, ему автоматически выберется последняя выбранная им доставка, но битрикс ничего не знает о том, что у нас там еще и обязательные поля. Поэтому убираем дефолтный выбор доставки в обработчике OnSaleComponentOrderJsDataHandler. Он у нас уже есть, дописываем в него:

    if (isset($arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY'])
     && $arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY']!='') {
        $arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY'] = '';}

    В данном случае блок с доставками всегда будет открыт, и пользователь сразу обратит внимание на необходимость заполнения полей. Но! Если пользователь в кабинете удаляет профиль, поле с местоположением будет у него незаполнено, и после его заполнения блок с доставками автоматически закроется без возможности его отредактировать (пропадет кнопка «Изменить»). Это очень трудно пофиксить, чтобы не посыпалось всё остальное, поэтому мы приняли решение убрать возможность редактирования профилей в кабинете пользователя (делается снятием галочки в настройках компонента личного кабинета)

    На данный момент у меня всё. Конечно, этот код был написан для конкретного проекта и с определенными допущениями. Но надеюсь, что данная заметка оказалась вам полезной и наведет вас на путь истинный при решении вашей задачи. Ибо документации по методам класса OrderAjaxComponent нет и не будет. Если Вам есть что добавить или поправить — буду рада комментариям.

    order_ajax_ext.js

    Создаём файл order_ajax_ext.js в папке с шаблоном компонента sale.order.ajax (там же, где лежит файл order_ajax.js) с содержимым:

    (function () {
        'use strict'; 
     
        var initParent = BX.Sale.OrderAjaxComponent.init,
            getBlockFooterParent = BX.Sale.OrderAjaxComponent.getBlockFooter,
            editOrderParent = BX.Sale.OrderAjaxComponent.editOrder
            ;
     
        BX.namespace('BX.Sale.OrderAjaxComponentExt');    
     
        BX.Sale.OrderAjaxComponentExt = BX.Sale.OrderAjaxComponent;
     
    	//Пример перехвата стандартной функции
        BX.Sale.OrderAjaxComponentExt.init = function (parameters) {
            initParent.apply(this, arguments);
     
            var editSteps = this.orderBlockNode.querySelectorAll('.bx-soa-editstep'), i;
            for (i in editSteps) {
                if (editSteps.hasOwnProperty(i)) {
                    BX.remove(editSteps[i]);
                }
            }
     
        }; 
    })();

    В отдельных переменных определяем функции-методы родительского BX.Sale.OrderAjaxComponent, чтобы их можно было вызвать в дочерних функциях и не получить ошибку Maximum call stack size exceeded.

    Копируем ссылку с BX.Sale.OrderAjaxComponent в BX.Sale.OrderAjaxComponentExt.

    В методе BX.Sale.OrderAjaxComponentExt.init вызываем родительский init, следом прибиваем ссылки «изменить» у всех блоков. Они нам не нужны.

    В методе BX.Sale.OrderAjaxComponentExt.getBlockFooter прибиваем кнопки «Назад» и «Вперед» у блоков. Они нам тоже не понадобятся — все блоки у нас развёрнуты.

    В методе BX.Sale.OrderAjaxComponentExt.editOrder ненужным блокам-секциям добавляем css-класс bx-soa-section-hide. По нему мы и будем скрывать ненужные блоки. А так же в этом методе раскрываем только нужные нам блоки: «Покупатель» и «Товары в заказе».

    Метод BX.Sale.OrderAjaxComponentExt.initFirstSection оставляем просто пустым. Если этого не сделать, то у анонимов при попытке оформления будет вываливаться эксепшен, по поводу отсутствия необходимых обязательных полей.

    Идем дальше.

    В файле template.php нашего шаблона нового оформления добавляем подключение нашего скрипта order_ajax_ext.js

    После строчки:

    $this->addExternalJs($templateFolder.'/order_ajax.js');

    добавляем:

    $this->addExternalJs($templateFolder.'/order_ajax_ext.js');

    А так же в файле template.php меняем все вызовы BX.Sale.OrderAjaxComponent на BX.Sale.OrderAjaxComponentExt

    Ну и не забываем добавить в файл стилей, чтобы ненужные блоки скрылись

    .bx-soa-section-hide {
        display: none;
    }

    Краткое описание функций sale.order.ajax

    showValidationResult: function(inputs, errors) — функция в которой полям с ошибкой добавляется класс hasError, который помечает ошибкой(в стандартном варианте добавляет обводку красным).

    showErrorTooltip: function(tooltipId, targetNode, text) — функция в которой добавляются тултипы для полей с ошибкой.

    showError: function(node, msg, border) — функция в которой выводятся ошибки в «групповой контейнер»

    refreshOrder: function(result) — функция в которой происходит разбор ошибок, которые приходят от сервера. Там есть ветка result.error

    Первые 3 функции отвечают за валидацию на форме без перезагрузки, а четвёртая за обработку результатов от сервера.

    Выполнение кода после перезагрузки страницы

    Бывает, что нужно регулярно выполнить код после перезагрузки страницы (изменения опций заказа). Например, требуется перерисовать селект. Это просто. Откройте файл order_ajax.js и в самый конец допишите:

    (function ($) {
    	$(function() {
    		BX.addCustomEvent('onAjaxSuccess', function(){
    			$(function() {
    				$('select').ikSelect({
    					autoWidth:false,
    					ddFullWidth:false
    				});
    			});
    		});
    
    		$('select').ikSelect({
    			autoWidth:false,
    			ddFullWidth:false
    		});
    
    		window.onresize = function() {
    			$('select').ikSelect('redraw');
    		}
    	});
    })(jQuery);
    

    Штатный ajax возвращает данные во фрейм-контейнер. Соответственно, весь js, уже загруженный на странице, находится на уровень выше. Соотв, чтобы обратиться к нему из html, загруженном через ajax, необходимо это указать (top.имя_функции или top.имя_переменной). Спасибо Евгению Жукову

    top.$

    Для стилизации селектов, которые по умолчанию выглядят ужасно лучше всего использовать замечательную библиотеку ikSelect

    Программная смена города в sale.order.ajax на javascript

    Часто, по техническому заданию, требуется программная смена города на javascript с ajax перезагрузкой страницы и перерасчетом формы. Покажу как сделать это.

    Будем считать, что у вас уже есть code локации из местоположений Битрикс. Тогда в части кода где необходимо произвести смену локации пишем:

    //Данный код будет работать не только внутри sale.order.ajax, но и во внешних скриптах, поскольку мы обращается к его функциям через пространство имен BX.Sale.OrderAjaxComponent
    var code = '0000000001';//Ваш код локации. В данном случае это Белоруссия
    
    $('.dropdown-field').val(code);//Обновляем код в скрытом инпуте
    //$('.bx-ui-sls-fake').val(code);
    
    BX.Sale.OrderAjaxComponent.params.newCity=code; //Записываем в параметры компонента необходимый нам город
    BX.Sale.OrderAjaxComponent.sendRequest();//Обновляем форму

    Здесь мы заполнили скрытый input нужным нам кодом и записали его в переменную, которой воспользуемся при перестроении формы. Идем в функцию prepareLocations (~1557 стр.).Находим код:

    temporaryLocations.push({

    И выше него пишем:

    //Делаем двойную проверку.
    if(typeof this.params!='undefined'){//В первом случае на то, что параметры вообще установлены, поскольку код выполняется первый раз до их инициализации
    	if(typeof this.params.newCity!='undefined'){//Вторая проверка - установлена ли наша переменная
    		locations[i].lastValue = this.params.newCity;//Если переменная установлена, то подставляем ее в локацию
    		delete this.params.newCity;//Обнуляем нашу переменную
    	}
    }

    В итоге у меня получилось так:

    for (k in output)
    {
    	if (output.hasOwnProperty(k))
    	{
    		//Делаем двойную проверку.
    		if(typeof this.params!='undefined'){//В первом случае на то, что параметры вообще установлены, поскольку код выполняется первый раз до их инициализации
    			if(typeof this.params.newCity!='undefined'){//Вторая проверка - установлена ли наша переменная
    				locations[i].lastValue = this.params.newCity;//Если переменная установлена, то подставляем ее в локацию
    				delete this.params.newCity;//Обнуляем нашу переменную
    			}
    		}
    
    
    		temporaryLocations.push({
    			output: BX.processHTML(output[k], false),
    			showAlt: locations[i].showAlt,
    			lastValue: locations[i].lastValue,
    			coordinates: locations[i].coordinates || false
    		});
    	}
    }

    Расчет стоимости доставки для всех служб доставки

    Будем полагать, что компонент sale.order.ajax вынесен у вас в отдельную папку

    Тогда идем в файл /local/components/YOUR_NAMESPACE/sale.order.ajax/class.php и находим функцию protected function calculateDeliveries(Order $order) (~4289 стр)

    Находим условие if ((int)$shipment->getDeliveryId() === $deliveryId) и в области else сразу после кода:

    $mustBeCalculated = $this->arParams['DELIVERY_NO_AJAX'] === 'Y'
    						|| ($this->arParams['DELIVERY_NO_AJAX'] === 'H' && $deliveryObj->isCalculatePriceImmediately());

    пишем:

    $mustBeCalculated = true;
    $calcResult = $deliveryObj->calculate($shipment);
    $calcOrder = $order;

    Теперь после обращения к серверу в наш order_ajax.js приходят службы доставки с рассчитанными стоимостями. Остается их только обработать и вывести.

    В скрипте находим функциюcreateDeliveryItem: function(item) и работаем с параметром item.PRICE или item.PRICE_FORMATED и выводим его куда нужно.

    Получение стоимости доставки для продукта после применения скидок и правил корзины

    За скрипт спасибо Денису Мягкову

    /**
     * Получение стоимости доставки для продукта после применения скидок, правил корзины и ...
     *
     * @param string|int $bitrixProductId Id битриксового продукта
     * @param string     $siteId          Id битриксового сайта, например "s1"
     * @param string|int $userId          Id битриксового пользователя
     * @param string|int $personTypeId    Id битриксового "Тип плательщика" /bitrix/admin/sale_person_type.php?lang=ru
     * @param string|int $deliveryId      Id битриксового "Службы доставки" /bitrix/admin/sale_delivery_service_list.php?lang=ru&filter_group=0
     * @param string|int $paySystemId     Id битриксового "Платежные системы" /bitrix/admin/sale_pay_system.php?lang=ru
     * @param array      $userCityId      Id битриксового города ("куда доставлять")
     *
     * @return null|float null - не удалось получить; float - стоимость (может быть 0 (после применения скидок на доставку))
     *
     * @throws BitrixMainArgumentException
     * @throws BitrixMainArgumentNullException
     * @throws BitrixMainArgumentOutOfRangeException
     * @throws BitrixMainArgumentTypeException
     * @throws BitrixMainLoaderException
     * @throws BitrixMainNotImplementedException
     * @throws BitrixMainNotSupportedException
     * @throws BitrixMainObjectException
     * @throws BitrixMainObjectNotFoundException
     * @throws BitrixMainSystemException
     */
    function getDeliveryPriceForProduct($bitrixProductId, $siteId, $userId, $personTypeId, $deliveryId, $paySystemId, $userCityId)
    {
        $result = null;
    
        BitrixMainLoader::includeModule('catalog');
        BitrixMainLoader::includeModule('sale');
    
        $products = array(
            array(
                'PRODUCT_ID' => $bitrixProductId,
                'QUANTITY'   => 1,
                // 'NAME'       => 'Товар 1', 
                // 'PRICE' => 500,
                // 'CURRENCY' => 'RUB',
            ),
        );
        /** @var BitrixSaleBasket $basket */
        $basket = BitrixSaleBasket::create($siteId);
        foreach ($products as $product) {
            $item = $basket->createItem("catalog", $product["PRODUCT_ID"]);
            unset($product["PRODUCT_ID"]);
            $item->setFields($product);
        }
    
        /** @var BitrixSaleOrder $order */
        $order = BitrixSaleOrder::create($siteId, $userId);
        $order->setPersonTypeId($personTypeId);
        $order->setBasket($basket);
    
        /** @var BitrixSalePropertyValueCollection $orderProperties */
        $orderProperties = $order->getPropertyCollection();
        /** @var BitrixSalePropertyValue $orderDeliveryLocation */
        $orderDeliveryLocation = $orderProperties->getDeliveryLocation();
        $orderDeliveryLocation->setValue($userCityId); // В какой город "доставляем" (куда доставлять).
    
        /** @var BitrixSaleShipmentCollection $shipmentCollection */
        $shipmentCollection = $order->getShipmentCollection();
    
        $delivery = BitrixSaleDeliveryServicesManager::getObjectById($deliveryId);
        /** @var BitrixSaleShipment $shipment */
        $shipment = $shipmentCollection->createItem($delivery);
    
        /** @var BitrixSaleShipmentItemCollection $shipmentItemCollection */
        $shipmentItemCollection = $shipment->getShipmentItemCollection();
        /** @var BitrixSaleBasketItem $basketItem */
        foreach ($basket as $basketItem) {
            $item = $shipmentItemCollection->createItem($basketItem);
            $item->setQuantity($basketItem->getQuantity());
        }
    
        /** @var BitrixSalePaymentCollection $paymentCollection */
        $paymentCollection = $order->getPaymentCollection();
        /** @var BitrixSalePayment $payment */
        $payment = $paymentCollection->createItem(
            BitrixSalePaySystemManager::getObjectById($paySystemId)
        );
        $payment->setField("SUM", $order->getPrice());
        $payment->setField("CURRENCY", $order->getCurrency());
    
        // $result = $order->save(); // НЕ сохраняем заказ в битриксе - нам нужны только применённые "скидки" и "правила корзины" на заказ.
        // if (!$result->isSuccess()) {
        //     //$result->getErrors();
        // }
    
        $deliveryPrice = $order->getDeliveryPrice();
        if ($deliveryPrice === '') {
            $deliveryPrice = null;
        }
        $result = $deliveryPrice;
    
        return $result;
    }
    
    // Использование
    $deliveryPriceForProductCourier = getDeliveryPriceForProduct(
        $bitrixProductId,
        SITE_ID,
        $USER->GetID(),
        '1', // Юридическое лицо  /bitrix/admin/sale_person_type.php?lang=ru
        '1386', // Доставка курьером до дома (в случае наличия "профиля" - указываем его id)  /bitrix/admin/sale_delivery_service_edit.php?lang=ru
        '37', // Наличными или картой при получении  /bitrix/admin/sale_pay_system.php?lang=ru
        $userCity['ID'] // Город пользователя
    );

    Делаем бесплатную доставку по России с условием

    Допустим у нас есть задача сделать доставку по России бесплатной, если стоимость товаров в корзине превышает определенную сумму.

    В моем случае в свойствах заказа есть отдельное поле «Страна», поэтому задача упрощается и весь код можно свести к паре проверок

    <?php
    use BitrixMainEventManager;
    use BitrixMainEvent;
    use BitrixMainEventResult;
    
    // Получить цену товаров при создании корзины
    EventManager::getInstance()->addEventHandler(
        'sale',
        'OnSaleComponentOrderCreated',
        'OnSaleComponentOrderCreated'
    );
    
    function OnSaleComponentOrderCreated($order, &$arUserResult, $request, &$arParams, &$arResult, &$arDeliveryServiceAll, &$arPaySystemServiceAll)
    {
    	//Записываем стоимость товаров
        $_SESSION['ORDER_BASKET_PRICE'] = $order->getBasket()->getPrice();
    	
    	//Записываем код текущей страны из свойства заказа с id = 24
        $_SESSION['ORDER_LOCATION'] = $arUserResult['ORDER_PROP']['24'];
    }
    
    //Изменяем стоимость доставки во время каждого расчета стоимости доставки
    EventManager::getInstance()->addEventHandler(
        'sale',
        'onSaleDeliveryServiceCalculate',
        'onSaleDeliveryServiceCalculate'
    );
    
    function onSaleDeliveryServiceCalculate(BitrixMainEvent $event)
    {
    
        $baseResult = $event->getParameter('RESULT');
        $shipment = $event->getParameter('SHIPMENT');
    
        //Если установлена цена, страна Россия и служба доставки не EMS
        if(isset($_SESSION['ORDER_BASKET_PRICE'])
            && $_SESSION['ORDER_LOCATION']=='0000073738'
            && $event->getParameter('DELIVERY_ID') != 45
            && $event->getParameter('DELIVERY_ID') != 31
            && $event->getParameter('DELIVERY_ID') != 33){
    
    		//Смотрим, если настройки доставки мы уже в текущем сеансе получали, то пропускаем блок
            if(!$_SESSION['DELIVERY_PRICE_WHILE_RUSSIA']){
                //Получаем настройки стоимости товара при котором доставка должна быть бесплатной
                $arSelect = Array('ID','PROPERTY_VAL');
                $arFilter = Array(
                    '=IBLOCK_ID' => 27, //Настройки
                    '=IBLOCK_SECTION_ID' => 86, //Настройки бесплатной доставки по России
                    '=ACTIVE' => 'Y',
                    '=ID' => array(1776), //Доставка по России бесплатна при стоимости цены больше
                );
    
                $res = CIBlockElement::GetList(Array(), $arFilter, false, Array(), $arSelect);
    
                //Ищем настройки бесплатной доставки
                while($ob = $res->GetNextElement())
                {
                    $arFields = $ob->GetFields();
    
                    $_SESSION['DELIVERY_PRICE_WHILE_RUSSIA'] = (int)$arFields['PROPERTY_VAL_VALUE'];
                }
            }
    
            $basketPrice = $_SESSION['ORDER_BASKET_PRICE'];
    
    		//Если цена товаров больше значения из настроек
            if($basketPrice > $_SESSION['DELIVERY_PRICE_WHILE_RUSSIA']) {
                //делаем доставку бесплатной
                $baseResult->setDeliveryPrice(0);
            }
        }
    
        //Пересохраняем результат
        $event->addResult(
            new BitrixMainEventResult(
                BitrixMainEventResult::SUCCESS, array('RESULT' => $baseResult)
            )
        );
    }
    
    
    /*
    // Так же обработчик можно вызвать старым способом
    AddEventHandler("sale", "onSaleDeliveryServiceCalculate", "onSaleDeliveryServiceCalculate");
    function onSaleDeliveryServiceCalculate($result, $shipment, $deliveryID){
        // Проверка id службы доставки
        //17 - Почта России
        //20 - Доставка курьером
        //21 - Пункт выдачи СДЭК
        //24 - Пункт выдачи Boxberry
        //33 - EMS Почта России
    	
        if($deliveryID == 20 ){
            if(isset($_SESSION['ORDER_BASKET_PRICE']) )
            {
                $basketPrice = $_SESSION['ORDER_BASKET_PRICE'];
    
                if($basketPrice > 1000){
                    // Записываем новое значение цены на доставку
                    $shipment->setBasePriceDelivery(0, true);
                }
            }
        }
    	
        if($deliveryID == 31 || $deliveryID == 33){
            $shipment->setBasePriceDelivery(2500, true);
        }
    }*/

    Не забываем, что код нужно разместить в init.php

    Если вы знаете более изящные способы, то буду рад если вы ими поделитесь.

    Делаем собственный валидатор поля на примере кастомной валидации поля «Телефон»

    Для валидации любого поля в sale.order.ajax можно пойти двумя путями.

    — Воспользоваться регулярными выражениями

    — Подписаться на OnSaleOrderBeforeSaved и реализовать собственную логику валидации

    Опишу оба способа

    1. Идем в Магазин->Настройки->Свойства заказа->Список свойств и открываем нужное свойство. В нашем случае это телефон.

    Ищем поле Регулярное выражение для проверки: и вписываем его:

    /^(s*)?(+)?([- _():=+]?d[- _():=+]?){10,14}(s*)?$/

    Данная регулярка подойдет для проверки таких вариантов написания телефона:

    +7(903)888-88-88
    8(999)99-999-99
    +380(67)777-7-777
    001-541-754-3010
    +1-541-754-3010
    19-49-89-636-48018
    +233 205599853

    2. В большинстве случаев этого достаточно, но мы можем реализовать собственное правило проверки.

    $eventManager = BitrixMainEventManager::getInstance();
    $eventManager->addEventHandler('sale', 'OnSaleOrderBeforeSaved', ['ClassMyEvent', 'OnSaleOrderBeforeSavedHandler']);
    
    ...
    class MyEvent {
    	public static function OnSaleOrderBeforeSavedHandler(BitrixMainEvent $event)
        {
            /** @var BitrixSaleOrder $order */
            $order = $event->getParameter("ENTITY");
    
            if ($order->isNew()) {//Если проверка требуется только для нового заказа, а не при его редактировании
                $propertyCollection = $order->getPropertyCollection();
                $phoneProperty = $propertyCollection->getPhone();
                $isPhoneValid = MyEvent::checkPhoneNumber($phoneProperty->getValue());
    
                if ($isPhoneValid != 1) {
                    $event->addResult(new BitrixMainEventResult(
                        BitrixMainEventResult::ERROR,
                        new BitrixSaleResultError('Введите корректный номер телефона', 'PHONE_PROPERTY_INVALID')
                    ));
                }
            }
        }
    	
        public static function checkPhoneNumber($phone)    {
            if(preg_match("#^([0-9+]{0,3})s?(([0-9]{1,6}))s?([0-9-]{1,9})$#", $phone))
                return 1;
            else
                return 0;
        }
    }

    Полезные переменные order_ajax.js

    this.result.TOTAL //Данные о текущей цене и стоимости корзины

    Массив соответствия международных двухбуквенных кодов стран

    const C_COUNTRIES = [
        "RU" => 1,  //Россия
        "AZ" => 2,  //Азербайджан
        "AM" => 3,  //Армения
        "BY" => 4,  //Беларусь
        "GE" => 5,  //Грузия
        "KZ" => 6,  //Казахстан
        "KG" => 7,  //Киргизия
        "LV" => 8,  //Латвия
        "LT" => 9,  //Литва
        "MD" => 10,  //Молдавия
        "TJ" => 11,  //Таджикистан
        "TM" => 12,  //Туркменистан
        "UZ" => 13,  //Узбекистан
        "UA" => 14,  //Украина
        "EE" => 15,  //Эстония
        "AU" => 16,  //Австралия
        "AT" => 17,  //Австрия
        "AL" => 18,  //Албания
        "DZ" => 19,  //Алжир
        "AO" => 20,  //Ангола
        "AE" => 21,  //Арабские Эмираты
        "AR" => 22,  //Аргентина
        "AW" => 23,  //Аруба
        "AF" => 24,  //Афганистан
        "BS" => 25,  //Багамские острова
        "BD" => 26,  //Бангладеш
        "BB" => 27,  //Барбадос
        "BE" => 28,  //Бельгия
        "BJ" => 29,  //Бенин
        "BM" => 30,  //Бермудские острова
        "BG" => 31,  //Болгария
        "BO" => 32,  //Боливия
        "BA" => 33,  //Босния и Герцеговина
        "BR" => 34,  //Бразилия
        "BN" => 35,  //Бруней
        "GB" => 36,  //Великобритания
        "HU" => 37,  //Венгрия
        "VE" => 38,  //Венесуэлла
        "VN" => 39,  //Вьетнам
        "HT" => 40,  //Гаити
        "GM" => 41,  //Гамбия
        "HN" => 42,  //Гондурас
        "GP" => 43,  //Гваделупа
        "GT" => 44,  //Гватемала
        "GN" => 45,  //Гвинея
        "DE" => 46,  //Германия
        "GI" => 47,  //Гибралтар
        "NL" => 48,  //Нидерланды
        "HK" => 49,  //Гонконг
        "GD" => 50,  //Гренада
        "GL" => 51,  //Гренландия
        "GR" => 52,  //Греция
        "GU" => 53,  //Гуана
        "DK" => 54,  //Дания
        "DO" => 55,  //Доминиканская Республика
        "EG" => 56,  //Египет
        "CD" => 57,  //Демократическая республика Конго
        "ZM" => 58,  //Замбия
        "ZW" => 59,  //Зимбабве
        "IL" => 60,  //Израиль
        "IN" => 61,  //Индия
        "ID" => 62,  //Индонезия
        "JO" => 63,  //Иордания
        "IQ" => 64,  //Ирак
        "IR" => 65,  //Иран
        "IE" => 66,  //Ирландия
        "IS" => 67,  //Исландия
        "ES" => 68,  //Испания
        "IT" => 69,  //Италия
        "YE" => 70,  //Йемен
        "KY" => 71,  //Каймановы острова
        "CM" => 72,  //Камерун
        "CA" => 73,  //Канада
        "KE" => 74,  //Кения
        "CY" => 75,  //Кипр
        "CN" => 76,  //Китай
        "CO" => 77,  //Колумбия
        "KH" => 78,  //Камбоджа
        "CG" => 79,  //Конго
        "KR" => 80,  //Корея (Южная)
        "CR" => 81,  //Коста Рика
        "CU" => 82,  //Куба
        "KW" => 83,  //Кувейт
        "LR" => 84,  //Либерия
        "LI" => 85,  //Лихтенштейн
        "LU" => 86,  //Люксембург
        "MR" => 87,  //Мавритания
        "MG" => 88,  //Мадагаскар
        "MK" => 89,  //Македония
        "MY" => 90,  //Малайзия
        "ML" => 91,  //Мали
        "MT" => 92,  //Мальта
        "MX" => 93,  //Мексика
        "MZ" => 94,  //Мозамбик
        "MC" => 95,  //Монако
        "MN" => 96,  //Монголия
        "MA" => 97,  //Морокко
        "NA" => 98,  //Намибия
        "NP" => 99,  //Непал
        "NG" => 100,  //Нигерия
        "NI" => 102,  //Никарагуа
        "NZ" => 103,  //Новая Зеландия
        "NO" => 104,  //Норвегия
        "PK" => 105,  //Пакистан
        "PA" => 106,  //Панама
        "PG" => 107,  //Папуа Новая Гвинея
        "PY" => 108,  //Парагвай
        "PE" => 109,  //Перу
        "PL" => 110,  //Польша
        "PT" => 111,  //Португалия
        "PR" => 112,  //Пуэрто Рико
        "RO" => 113,  //Румыния
        "SA" => 114,  //Саудовская Аравия
        "SN" => 115,  //Сенегал
        "SG" => 116,  //Сингапур
        "SY" => 117,  //Сирия
        "SK" => 118,  //Словакия
        "SI" => 119,  //Словения
        "SO" => 120,  //Сомали
        "SD" => 121,  //Судан
        "US" => 122,  //США
        "TW" => 123,  //Тайвань
        "TH" => 124,  //Таиланд
        "TT" => 125,  //Тринидад и Тобаго
        "TN" => 126,  //Тунис
        "TR" => 127,  //Турция
        "UG" => 128,  //Уганда
        "UY" => 129,  //Уругвай
        "PH" => 130,  //Филиппины
        "FI" => 131,  //Финляндия
        "FR" => 132,  //Франция
        "TD" => 133,  //Чад
        "CZ" => 134,  //Чехия
        "CL" => 135,  //Чили
        "CH" => 136,  //Швейцария
        "SE" => 137,  //Швеция
        "LK" => 138,  //Шри-Ланка
        "EC" => 139,  //Эквадор
        "ET" => 140,  //Эфиопия
        "ZA" => 141,  //ЮАР
        "RS" => 142,  //Сербия
        "JM" => 143,  //Ямайка
        "JP" => 144,  //Япония
        "BH" => 145,  //Бахрейн
        "AD" => 146,  //Андорра
        "BZ" => 147,  //Белиз
        "BT" => 148,  //Бутан
        "BW" => 149,  //Ботсвана
        "BF" => 150,  //Буркина Фасо
        "BI" => 151,  //Бурунди
        "CF" => 152,  //Центральноафриканская Республика
        "KM" => 153,  //Коморос
        "CI" => 154,  //Кот-Д`ивуар
        "DJ" => 155,  //Джибути
        "TL" => 156,  //Восточный Тимор
        "SV" => 157,  //Эль Сальвадор
        "GQ" => 158,  //Экваториальная Гвинея
        "ER" => 159,  //Эритрея
        "FJ" => 160,  //Фижи
        "GA" => 161,  //Габон
        "GH" => 162,  //Гана
        "GW" => 163,  //Гвинея-Биссау
        "KP" => 164,  //Корея (Северная)
        "LB" => 165,  //Ливан
        "LS" => 166,  //Лесото
        "LY" => 167,  //Ливия
        "MV" => 168,  //Мальдивы
        "MH" => 169,  //Маршалские острова
        "NE" => 170,  //Нигер
        "OM" => 171,  //Оман
        "QA" => 172,  //Катар
        "RW" => 173,  //Руанда
        "WS" => 174,  //Самоа
        "SC" => 175,  //Сейшеллы
        "SL" => 176,  //Сьерра-Леоне
        "SR" => 177,  //Суринам
        "SZ" => 178,  //Свазиленд
        "TZ" => 179,  //Танзания
        "EH" => 180,  //Западная Сахара
        "HR" => 181,  //Хорватия
        "AI" => 182,  //Ангилья
        "AQ" => 183,  //Антарктида
        "AG" => 184,  //Антигуа и Барбуда
        "BV" => 185,  //Остров Буве
        "IO" => 186,  //Британские территории в Индийском Океане
        "VG" => 187,  //Британские Виргинские острова
        "MM" => 188,  //Мьянма
        "CV" => 189,  //Кабо-Верде
        "CX" => 190,  //Остров Рождества
        "CC" => 191,  //Кокосовые острова
        "CK" => 192,  //Острова Кука
        "DM" => 193,  //Доминика
        "FK" => 194,  //Фолклендские острова
        "FO" => 195,  //Фарерские острова
        "GF" => 196,  //Гвиана
        "PF" => 197,  //Французская Полинезия
        "TF" => 198,  //Южные Французские территории
        "HM" => 199,  //Острова Херд и Макдоналд
        "KI" => 200,  //Кирибати
        "LA" => 201,  //Лаос
        "MO" => 202,  //Макао
        "MW" => 203,  //Малави
        "MQ" => 204,  //Мартиника
        "MU" => 205,  //Маврикий
        "YT" => 206,  //Майотта
        "FM" => 207,  //Микронезия
        "MS" => 208,  //Монтсеррат
        "NR" => 209,  //Науру
    //  210  Антильские острова - они же Карибы, относятся к нескольким государствам, каое имеется ввиду в Битриксе - хз
        "NC" => 211,  //Новая Каледония
        "NU" => 212,  //Ниуэ
        "NF" => 213,  //Остров Норфолк
        "PW" => 214,  //Палау
        "PS" => 215,  //Палестина
        "PN" => 216,  //Остров Питкэрн
        "RE" => 217,  //Реюньон
        "SH" => 218,  //Остров Св.Елены
        "KN" => 219,  //Острова Сент-Киттс и Невис
        "LC" => 220,  //Санта-Лючия
        "PM" => 221,  //Острова Сен-Пьер и Микелон
        "VC" => 222,  //Сент-Винсент и Гренадины
        "SM" => 223,  //Сан-Марино
        "SB" => 224,  //Соломоновы острова
        "LK" => 225,  //Южная Георгия и Южные Сандвичевы острова
        "SJ" => 226,  //Острова Шпицберген и Ян-Майен
        "TG" => 227,  //Того
        "TK" => 228,  //Токелау
        "TO" => 229,  //Тонга
        "TC" => 230,  //Острова Тёркс и Кайкос
        "TV" => 231,  //Тувалу
        "VI" => 232,  //Американские Виргинские острова
        "VU" => 233,  //Вануату
        "VA" => 234,  //Ватикан
        "WF" => 235,  //Острова Уоллис и Футуна
        "ME" => 236,  //Черногория    
    ];

    Альтернативные (кастомные) sale.order.ajax

    Интересный кастомный sale.order.ajax предложил alorian в компоненте Opensource Bitrix Order

    Ошибка в Битрикс при оформлении заказа window.__jsonp_ymaps_map is not a function

    Скорей всего вы используете несколько служб доставки, таких как СДЭК или Boxberry. Зайдите в настройки этих служб и в одной из них отключите использование Яндекс карт. Для работы будет достаточно одного подключения API Яндекс

    Баг при автозаполнении телефона. После любого действия начальная цифра (7) дублируется, стирая актуальную цифру телефона

    С проблемой столкнулся на одном из шаблонов Аспро Оптимус. Решил обходным путем.

    Идем в функцию alterProperty и отключаем штатный механизм маскирования закомментировав данные строки:

    //if (settings.IS_PHONE == 'Y')
    	//textNode.setAttribute('autocomplete', 'tel');

    Подключил библиотеку Inputmask и добавил в конец файла order_ajax.js следующие строки, где #soa-property-3 это идентификатор нашего свойства с телефоном

    $(function() {
    	$('#soa-property-3').inputmask({"mask": "7 (999) 999-99-99"});
    
    	BX.addCustomEvent('onAjaxSuccess', function(){
    		$('#soa-property-3').inputmask({"mask": "7 (999) 999-99-99"});
    	});
    
    });

    Стоит ознакомиться:

    • Новый sale.order.ajax
    • Полезности для sale.order.ajax
    • Новый шаблон sale.order.ajax: кастомизация
    • 1С-Битрикс Управление сайтом: Интеграция верстки в компонент оформление заказа
    • 1C-Битрикс. Кастомизация нового шаблона
    • bitrix Кастомное оформление заказа- странная методология
    • Делаем собственный sale.order.ajax на D7

    Теги:

    1С БитриксjQueryJavascriptphpJSONsale.order.ajax

    Просмотров: 22151

    Если нужно получить список городов из модуля «Местоположения» можно воспользоваться API Битрикс. Данные местоположения можно использовать в любом месте сайта или в любом компоненте. Например в профиле пользователя или формах обратной связи.

    Получение местоположений и вывод их в select

    Для примера, буду выводить города в профиле пользователя. Данные должны записаться в стандартное поле «Город» с кодом «PERSONAL_CITY»

    Сначала импортируем местоположения. Для этого проходим в административную панель по пути Магазин -> Настройки -> Местоположения -> Импорт местоположений и выбираем нужные данные. Для примера, я выберу регион Россия->Центр

    Дожидаемся окончания импорта. Затем идем в шаблон компонента main.profile и открываем файл template.php в нужном месте шаблона вызываем местоположения с помощью API BitrixSaleLocationLocationTable::getList пока выводим все города, просто списком

    <?
    $res = BitrixSaleLocationLocationTable::getList(array(
        'filter' => array('=TYPE.ID' => '5', '=NAME.LANGUAGE_ID' => LANGUAGE_ID),
        'select' => array('NAME_RU' => 'NAME.NAME')
        ));
        while ($item = $res->fetch()) {
            echo $item['NAME_RU'].'<br>'; 
        }
    ?>
    

    Где в массивах:
    ‘filter’ — передали ID типа местоположения. В моем случае это 5-ый, города. И языковые настройки. Сюда можете предать любые типы: Улицы, Села, Страны, Регионы.
    ‘select’ — передали нужные для вывода данные, в моем случае русские названия (городов).

    В итоге, получили список всех городов импортированных в местоположения. Теперь оформляем это в список c выбором select, где первым option (по умолчанию) будет ранее выбранный и сохраненный город. Самому селекту назначаем name=»PERSONAL_CITY», что бы он сохранялся в стандартное поле пользователя- Город

    <select name="PERSONAL_CITY">
        <option value="<?= $arResult["arUser"]["PERSONAL_CITY"] ?>">
            <?= $arResult["arUser"]["PERSONAL_CITY"] ?>
        </option>
        <?
        $res = BitrixSaleLocationLocationTable::getList(array(
            'filter' => array('=TYPE.ID' => '5', '=NAME.LANGUAGE_ID' => LANGUAGE_ID),
            'select' => array('NAME_RU' => 'NAME.NAME')
        ));
        while ($item = $res->fetch()) {?>
            <option value="<? print_r($item['NAME_RU']); ?>">
                <? print_r($item['NAME_RU']); ?>
            </option>
         <?} ?>
    </select>

    Все, теперь пользователь может выбрать город из списка и он сохранится в нужное поле (Город). Но если городов очень много, например вы загрузили всю Россию, выбирать из такого списка будет очень тяжело. Поэтому…

    Автопоиск по первым буквам из выпадающего списка select

    Реализуем удобный поиск по select, который будет подбирать значения в процессе ввода данных. выглядет например, так:

    Для этого воспользуемся готовым плагином SELECT2. Просто подключаем приложенные скрипты и файлы стилей. Можно подключить их прямо в template.php, если используете данный способ только в кабинете пользователя

    Скачать SELECT2

    Для select задаем любой удобный id. Например id=»my_sity». И указываем плагину, какой select он должен обработать. Целиком это будет выглять так (комментарии прямо в коде):

    
    <select name="PERSONAL_CITY"  id="my_sity">
        <option value="<?= $arResult["arUser"]["PERSONAL_CITY"] ?>">
            <?= $arResult["arUser"]["PERSONAL_CITY"] ?>
        </option>
        <?
        $res = BitrixSaleLocationLocationTable::getList(array(
            'filter' => array('=TYPE.ID' => '5', '=NAME.LANGUAGE_ID' => LANGUAGE_ID),
            'select' => array('NAME_RU' => 'NAME.NAME')
        ));
        while ($item = $res->fetch()) {?>
            <option value="<? print_r($item['NAME_RU']); ?>">
                <? print_r($item['NAME_RU']); ?>
            </option>
         <?} ?>
    </select>
    
    <!-- ПОДКЛЮЧАЕМ ОФОРМЛЕНИЕ И JS ПЛАГИНА -->
    <link href="/local/templates/.default/js/select/select2.min.css" type="text/css" rel="stylesheet"/>
    <script src="/local/templates/.default/js/select/select2.full.min.js"></script>
    
    <!-- УКАЗЫВАЕМ ID НУЖНОГО SELECT-а -->
    <script>
    $(document).ready(function () {
       $("#my_sity").select2();
    });
    </script>

    Само собой, стили и скрипты плагина, лучше всего подключать через API Битрикс, для оптимизации работы. В примере оставил «по старинке», что бы не усложнять объяснение.

    В общем-то и все. Использовать данный плагин можно с любым select-ом. Так же и получение местоположений, можете использовать в любом месте сайта, в любом компоненте или шаблоне.

    Поблагодарить и поддержать!

    Поддержать выпуски видео уроков, поблагодарить за полезную инструкцию или заметку можно через:

    Юманей

    Мои каналы:

    Каналы на которые я выкладываю видео. Контент дублируется, смотрите где удобнее.

    Видео на Ютубе

    Видео на VK

    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии

    А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Bitrix env логи ошибок
  • Bitrix batch json выдает ошибку