Заметка по кастомизации шаблона оформления заказа. До сих пор... он переделывается через заднее место 🙄
В общем по большей части все актуально до сих пор.. ищем в тысячах строк несколько функций и правим, что-то добавляем или удаляем.
В файле 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 = '';
Открыть все блоки и убрать лишнее
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
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 Bitrix\Main\EventManager;
$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 = Bitrix\Sale\Location\LocationTable::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;
}
}
}
Скрыть какое-то свойство
if(property.getId()==6){// идентификатор скрываемого свойства
var addressInput=propsItemNode.querySelector('textarea');
propsItemNode.style='display:none;';
addressInput.value='нужное значение';
}
Скрываем сообщение «Вы заказывали в нашем интернет-магазине, поэтому мы заполнили все данные автоматически»
checkNotifications
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)
priceNodesArray = [delivery.PRICE_FORMATED];
if(delivery.PRICE>0) priceNodesArray = [delivery.PRICE_FORMATED];
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'))
if (parseFloat(total.DELIVERY_PRICE) >= 0 && this.result.DELIVERY.length)
if (parseFloat(total.DELIVERY_PRICE) > 0 && this.result.DELIVERY.length)
Убираем поле «Адрес доставки» из вывода блока «Пользователь»
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() приравниваем к названию поля адреса в вашей системе
)
Вывод поля «Адрес доставки» в блоке «Доставка»
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
<!-- 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
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() приравниваем к названию поля адреса в вашей системе
)
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;
Запрет Битриксу выбирать доставку по умолчанию
if (isset($arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY'])
&& $arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY']!='') {
$arResult['JS_DATA']['LAST_ORDER_DATA']['DELIVERY'] = '';
}
order_ajax_ext.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
BX.Sale.OrderAjaxComponent
BX.Sale.OrderAjaxComponentExt
BX.Sale.OrderAjaxComponentExt.init
BX.Sale.OrderAjaxComponentExt.getBlockFooter
BX.Sale.OrderAjaxComponentExt.editOrder
BX.Sale.OrderAjaxComponentExt.initFirstSection
$this->addExternalJs($templateFolder.'/order_ajax.js');
$this->addExternalJs($templateFolder.'/order_ajax_ext.js');
.bx-soa-section-hide {
display: none;
}
Выполнение кода после перезагрузки страницы
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);
top.$
Получение стоимости доставки для продукта после применения скидок, правил корзины
/**
* Получение стоимости доставки для продукта после применения скидок, правил корзины и ...
*
* @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 \Bitrix\Main\ArgumentException
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Bitrix\Main\ArgumentOutOfRangeException
* @throws \Bitrix\Main\ArgumentTypeException
* @throws \Bitrix\Main\LoaderException
* @throws \Bitrix\Main\NotImplementedException
* @throws \Bitrix\Main\NotSupportedException
* @throws \Bitrix\Main\ObjectException
* @throws \Bitrix\Main\ObjectNotFoundException
* @throws \Bitrix\Main\SystemException
*/
function getDeliveryPriceForProduct($bitrixProductId, $siteId, $userId, $personTypeId, $deliveryId, $paySystemId, $userCityId)
{
$result = null;
\Bitrix\Main\Loader::includeModule('catalog');
\Bitrix\Main\Loader::includeModule('sale');
$products = array(
array(
'PRODUCT_ID' => $bitrixProductId,
'QUANTITY' => 1,
// 'NAME' => 'Товар 1',
// 'PRICE' => 500,
// 'CURRENCY' => 'RUB',
),
);
/** @var \Bitrix\Sale\Basket $basket */
$basket = \Bitrix\Sale\Basket::create($siteId);
foreach ($products as $product) {
$item = $basket->createItem("catalog", $product["PRODUCT_ID"]);
unset($product["PRODUCT_ID"]);
$item->setFields($product);
}
/** @var \Bitrix\Sale\Order $order */
$order = \Bitrix\Sale\Order::create($siteId, $userId);
$order->setPersonTypeId($personTypeId);
$order->setBasket($basket);
/** @var \Bitrix\Sale\PropertyValueCollection $orderProperties */
$orderProperties = $order->getPropertyCollection();
/** @var \Bitrix\Sale\PropertyValue $orderDeliveryLocation */
$orderDeliveryLocation = $orderProperties->getDeliveryLocation();
$orderDeliveryLocation->setValue($userCityId); // В какой город "доставляем" (куда доставлять).
/** @var \Bitrix\Sale\ShipmentCollection $shipmentCollection */
$shipmentCollection = $order->getShipmentCollection();
$delivery = \Bitrix\Sale\Delivery\Services\Manager::getObjectById($deliveryId);
/** @var \Bitrix\Sale\Shipment $shipment */
$shipment = $shipmentCollection->createItem($delivery);
/** @var \Bitrix\Sale\ShipmentItemCollection $shipmentItemCollection */
$shipmentItemCollection = $shipment->getShipmentItemCollection();
/** @var \Bitrix\Sale\BasketItem $basketItem */
foreach ($basket as $basketItem) {
$item = $shipmentItemCollection->createItem($basketItem);
$item->setQuantity($basketItem->getQuantity());
}
/** @var \Bitrix\Sale\PaymentCollection $paymentCollection */
$paymentCollection = $order->getPaymentCollection();
/** @var \Bitrix\Sale\Payment $payment */
$payment = $paymentCollection->createItem(
\Bitrix\Sale\PaySystem\Manager::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 Bitrix\Main\EventManager;
use Bitrix\Main\Event;
use \Bitrix\Main\EventResult;
// Получить цену товаров при создании корзины
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(\Bitrix\Main\Event $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 \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::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);
}
}*/
Краткое описание функций sale.order.ajax
showValidationResult: function(inputs, errors)
— функция в которой полям с ошибкой добавляется класс hasError, который помечает ошибкой(в стандартном варианте добавляет обводку красным).
showErrorTooltip: function(tooltipId, targetNode, text)
— функция в которой добавляются тултипы для полей с ошибкой.
showError: function(node, msg, border)
— функция в которой выводятся ошибки в «групповой контейнер»
refreshOrder: function(result)
— функция в которой происходит разбор ошибок, которые приходят от сервера. Там есть ветка result.error
Первые 3 функции отвечают за валидацию на форме без перезагрузки, а четвёртая за обработку результатов от сервера.
Баг при автозаполнении телефона. После любого действия начальная цифра (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"});
});
});
Количество просмотров: 5