Т.е. вот такой код не будет работать, задача, которая решается здесь, реальная и звучит так "Нужно во всем тексте страницы подменить {cache} на какую-то строку, которая берется из базы и это значение нужно закешировать".
Что обязательно для повторения ошибки: - Должен быть подключен prolog_before.php, а не header и footer (т.е это ajax или cron обычно). - Кеш должен отсутствовать, т.е. должнен производиться процесс его сохранения (я в примере сделал случайный cacheId, чтобы наверняка это выполнялось).
Решение: - Отказаться от кеширования внутри OnEndBufferContent. - Заранее подготавливать кеш (вариант с OnBeforeEndBufferContent не сработает). - Внутри OnEndBufferContent проверять константу BX_BUFFER_SHUTDOWN, и если она есть, то пытаться получать данные из кеша через initCache и getVars, т.к. они не вызывают ошибку (т.е. просто не сохранять данные, но получить попробовать можно).
// Это чтобы получить красиво в массиве ARTNUMBER_VALUE значение сразу. Но можете сделать так и посмотреть результат $query->setSelect(['ID', 'NAME', 'ARTNUMBER_VALUE' => 'ARTNUMBER.VALUE']);
// Но можно сделать так, тоже сработает, но это больше для работы с объектами а не массивом (см. fetchObject()) // $query->setSelect(['ID', 'NAME', 'ARTNUMBER']);
Задался тем же вопросом, метод Bitrix\Sale\TradingPlatform\Helper::notifyNewOrder выглядит как устаревший и передавать почту и имя не хотелось. И проверив его работу, обнаружил, что письмо не отправляется. Взглянув на код еще раз понял причину: передаваемые параметры отличаются от стандартных, не передается EMAIL, а именно он используется в качестве получателя (здесь EMAIL_TO в виде массива). Это можно решить конечно, через обработчик перед отправкой/добавлением, но это не наш путь.
Нашел альтернативный вариант \Bitrix\Sale\Notify::sendOrderNew и с ним есть сложность, он внутри проверяет является ли заказ новым, в нашем случае - нет, не является и письмо не отправится. Но это можно решить установкой вручную isNew для заказа, что нельзя сделать в стандартном \Bitrix\Sale\Order. Что тоже можно решить переопределив метод для Order, в итоге работающий код будет выглядеть так:
Код
<?php
// Класс с возможностью ручной установки isNew
class OrderExtended extends \Bitrix\Sale\Order
{
public function setIsNew($value)
{
$this->isNew = ($value === true);
}
}
$orderId = 100;
// Подмена класса для заказа
$registry = \Bitrix\Sale\Registry::getInstance(\Bitrix\Sale\Registry::REGISTRY_TYPE_ORDER);
$registry->set(\Bitrix\Sale\Registry::ENTITY_ORDER, OrderExtended::class);
// Грузим заказ + ставим отметку isNew
$order = \Bitrix\Sale\Order::load($orderId);
$order->setIsNew(true);
// Сама отправка уведомления
$result = \Bitrix\Sale\Notify::sendOrderNew($order);
В итоге заработало, не очень понравилось что $result всегда возвращается успешный, не зависимо отправлено ли письмо или нет, но это уже другая история
P.S. Если разработчики это читают, я был бы рад доработкам (лень пока что создавать идею, а на заявку в ТП не тянет): - объявить устаревшим метод Bitrix\Sale\TradingPlatform\Helper::notifyNewOrder, т.к. он не работает или обновить его код - дать принудительно отправлять уведомление через \Bitrix\Sale\Notify::sendOrderNew, без подстановки isNew (может параметром отдельным?)
Необходимость выводить перечень примененных к товару скидок, Возникла проблема с начислением скидок, как определить какие скидки применились к товарам в заказе?
Дмитрий Буров написал: В новой корзине в массиве $arResult['APPLIED_DISCOUNT_LIST'] содержатся скидки примененные в корзине (вот только точное отношение к какому товару/ам они принадлежат видимо пока не доделали, т.к. в BASKET, пусто(кроме подарка)).
Внутри result_modifier.php для sale.basket.basket можно получить те же данные что и в OnSaleComponentOrderResultPrepared:
Код
/** @var \Bitrix\Sale\BasketBase $basket */
$basket = (\Bitrix\Sale\Basket\Storage::getInstance(
\Bitrix\Sale\Fuser::getId(),
\Bitrix\Main\Context::getCurrent()->getSite()))
->getOrderableBasket();
$order = $basket->getOrder();
$discountApplyResults = $order->getDiscount()->getApplyResult(false); // c false немного больше данных будет, но если не нужно, то можно true передать
Важно: работает именно внутри компонента корзины (т.к. данные там заполняются, а мы берем тот же инстанс просто), в остальных местах не гарантируется.
Можно пойти дальше и через событие OnInitRegistryList задать свой класс вместо \Bitrix\Sale\Basket (наследника), может что из этого выйдет удобное еще, но для решения данной задачи, мне кажется, излишне будет уже.
Алексей Бурлака написал: как грамотно извлекать SECTION_PAGE_URL-ы и DETAIL_PAGE_URL-ы при работе исключительно через D7?
Это не D7, но вместо str_replace можно после получения данных для SECTION_PAGE_URL воспользоваться таким способом:
Код
// $sectionPageUrl - значение из SECTION_PAGE_URL
// $arSection - Массив со свойствами раздела, нужными для шаблона
\CIBlock::ReplaceSectionUrl($sectionPageUrl, $arSection, true, 'S');
Это не сильно проще чем str_replace (нужно знать какие параметры используются в адресе или брать все), зато есть служебные замены автоматически (#LANG#, #SITE_DIR#, #SERVER_NAME#).
Я поэкспериментировал и внешне кажется что почти все есть, надо немного костылей сделать (со службой доставки). В функции \Bitrix\Sale\Order::create третий параметр - валюта заказа, т.е. кажется - указывай валюту и все будет нормально.
Исходные данные: - для оформления используется кастомный компонент (не шаблон, а компонент полностью самописный) - тип платильщика на сайте один - дефолтный, задан в константах - валюта сайта HKD - доступные валюты на сайте HKD, EUR, USD
Какая задача: сохранение заказа в EUR. Зачем: фиксация курса валюты, дальнейшая работа с заказом без изменения цены, возможность оплаты в конкретной валюте, без конвертации на стороне платежных систем (для пользователя).
Реализация:
При создании заказа указываю нужную валюту
Код
// CURRENCY - текущая валюта выбранная пользователем на сайте, в данный момент - EUR. определена в init.php
// DEFAULT_PERSON_TYPE_ID - тип платильщика, задан в init.php, не выбирается пользователем
$siteId = \Bitrix\Main\Context::getCurrent()->getSite(); // идентификатор сайта
$userid = $GLOBALS['USER']->getID(); // ID текущего пользователя
// Создаем заказ
$order = \Bitrix\Sale\Order::create($siteId, $userId, CURRENCY);
// Устанавливаем тип платильщика
$order->setPersonTypeId(DEFAULT_PERSON_TYPE_ID);
// Загружаем в заказ товары и корзины
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(\Bitrix\Sale\Fuser::getId(), $siteId)->getOrderableItems();
$order->setBasket($basket);
Вот до этого этапе все нормально отрабатывает и внешне выглядит хорошо, все функции \Sale\Order работают корректно в соответствии с валютой: - ::getPrice отдает цену соответствующую EUR - ::getCurrency отдает 'EUR'
Оплата вроде автоматом подхватывает валюту, не разбирался в деталях.
С доставкой сложнее, т.к. валюта там указывается в настройках службы доставки и расчет идет в соответствии с этой валютой.
Этот вопрос я решил через событие 'onSaleDeliveryServiceCalculate', примерно так:
Код
$eventManager->addEventHandler("sale", "onSaleDeliveryServiceCalculate", function($event)
{
$parameters = $event->getParameters();
/** @var \Bitrix\Sale\Shipment $shipment */
$shipment = $parameters['SHIPMENT'];
$delivery = $shipment->getDelivery();
if ($shipment->getCurrency() != $delivery->getCurrency())
{
$priceRate = \CCurrencyRates::GetConvertFactorEx($delivery->getCurrency(), $shipment->getCurrency())
/** @var \Bitrix\Sale\Delivery\CalculationResult $parameters['RESULT'] */
$parameters['RESULT']->setDeliveryPrice($price*$priceRate);
return new EventResult(
EventResult::SUCCESS,
$parameters,
'sale'
);
}
// Изменения применяются только при EventResult::SUCCESS
// Остальные игнорируются
return new EventResult(
EventResult::UNDEFINED
);
}
И в целом все вроде работает, но надо проверять сейчас, раз из коробке это не поддерживается - могут быть проблемы. Я проверил сейчас: - Создал заказ (кастомный компонент, с указанием нужной мне валюты) - В админке заказ отображается в EUR - В админке оплата в EUR - В админке доставка в EUR - При пересчете заказа в админке - все остается на местах - При изменении службы доставки у отгрузки - валюта сохраняется, рассчитывается корректно - При добавлении новой отгрузки - валюта также EUR
Кажется что при таком создании через админку все работает хорошо, а это значит что не конфликтует ни с чем и может использоваться в работе.
Вопрос: Есть ли какие-то серьезные причины не поступать таким образом? Или моменты которые я не учел? Кажется через чур просто как то у меня это получилось, не должно было так получиться исходя из того, что "возможность сохранять заказ в валюте отличной от валюты сайта - не поддерживается".