Как отличить 1С-ку от обычного пользователя при обновлении элемента инфоблока? Вешаю обработчик на OnBeforeIBlockElementUpdate, нужно отличить 1С от обычного пользователя (администратора) и запрещать 1С-ке внусить некоторые изменения. На сколько знаю определение пользователя $USER происходит после инициализации init.php (порядок выполнения страницы https://dev.1c-bitrix.ru/api_help/main/general/pageplan.php ) и вариант "создать для обмена 1С отдельного пользователя с правами админа" не подходит.
Никак в текущей реализации фреймворка. В связи с этим вспоминается анекдот, перефразируя который его можно рассказать и так: "Приходит к нормальному программисту программист ядра одной известной CMS и говорит: - Слушай, глянь на программу, меня задолбали уже с претензиями, у меня на тестах все работает! Я учел все что можно. Где у меня ошибка?! Нормальный внимательно изучает листинг и мрачно выдает: - В ДНК, @@@@@! "
class ExchangeHandlers
{
private static $inExchange = false;
/**
* Обработчики событий модуля catalog
*/
public function OnBeforeCatalogImport1C($arParams, $file) {
self::$inExchange = true;
)
public function OnSuccessCatalogImport1C($arParams, $file) {
self::$inExchange = false;
}
/**
* Обработчики событий модуля iblock
*/
public function OnBeforeIBlockElementUpdate(&$arParams) {
if (self::$inExchange) {
}
}
}
Александр Воробьев, нет не так. Что будет если обмен прервется? Где вы OnSuccessCatalogImport1C получите? И что будет, если во время обмена я отредактирую свойство элемента инфоблока через админку? Анекдот актуален, к сожалению.
1. Если обмен прервется, то и событие OnBeforeIBlockElementUpdate не будет больше происходить. Следовательно необходимость в этом флаге отпадет.. Так что тут вообще нет ни какой проблемы абсолютно.
2. Ни чего не будет. По скольку сточки зрения сервера это разные хиты. Соответственно $inExchange будет у вас разное. Так что и тут нет ни каких проблем.
Прежде чем бочку катить на битрикс со своим анекдотом, вы бы вспомнили как работает PHP на веб сервере. И подумали как работает вышеприведенный код...
Если коротко: общение с веб сервером это не постоянный канал связи. А передача запроса серверу и получение от него ответа. (Хит). На каждом (абсоютно) хите переменная $inExchange будет равна false. Даже если в параллель будет 100 обменов идти. И лишь при появлении события OnBeforeCatalogImport1C оно будет изменять на true. И значение это будет изменено только и исключительно в рамках данного хита. Это, даже, не переменная сессии, не говоря о том что она ни как не связана между хитами разных клиентов. Если произойдет обрыв...... То будет просто обрыв хита.... все. Данная переменная и ее новое значение (если не будет багов ПО сервера) будет удалено из памяти (или точнееэта область будет помечена как свободная)
Александр Воробьев, написано интересно, можно вас попросить написать не коротко. Как я понял, обуславливать обработку для OnBeforeIBlockElementUpdate в зависимости от пользователя, все равно нельзя (или нет?). Вы говорите, что на хите (в хите, у хита - как правильно?) надо отследить наступление события OnBeforeCatalogImport1C и далее опять же в рамках хита выполнять все нужные действия для OnBeforeIBlockElementUpdate. Если так, то нельзя ли сразу пользователя (считаем, что для обмена у нас отдельный пользователь) на хите определить и задать аналогично $inExchange?
Судя по протоколу обмена OnBeforeCatalogImport1C вызывается между шагом C и D, а событие OnSuccessCatalogImport1C непосредствено по окончании шага D.
Получается, что при пакетной обработке у вас обработается только первая порция товаров? Согласно вашей же логике, ведь это будут разные хиты, и на всех шагах после первого OnBeforeCatalogImport1C не будет запускаться. Поправьте если ошибаюсь, не претендую на истину.
По сабжу, я бы предложил в файлик, на который настроен обмен 1С (можно его переложить в папку local чтобы не затронули обновления) перед вызовом компонента catalog.import.1c определить какую-нибудь константу, или в принципе вначале файла. В обработчике же проверять
нет не проверял. согласно документации OnBeforeCatalogImport1C - событие, вызываемое перед началом процедуры обмена: перед загрузкой XML в базу данных после загрузки файла на сервер. (т.е. это то же этап D) OnSuccessCatalogImport1C - событие, вызываемое после окончания обмена одним XML-файлом
Т.е. вполне логично, что между этими двумя событиями и происходит работа с элементами инфоблока. Т.е пришел следующий файл - опять возникает эта же последовательность событий. Хотя несколько стал менее уверенным в методе - надо проверять - добавить в этиобработчики вывод в логфайл - да убедиться.
Да вполне рабочий вариант и с кастомизацие файла 1c_exchange.php. Сделать переменную ExchangeHandlers::$inExchange Это даже лучше чем мой. Я бы сделал так: создал файл my_1c_exchage.php в нем устанавливал флаг из моего кода и дальше инклуд битриксного. 1Ску настроить на my_1c_excahnge.php
Дмитрий Чебыкин написал: Как я понял, обуславливать обработку для OnBeforeIBlockElementUpdate в зависимости от пользователя, все равно нельзя (или нет?).
Можно... Просто тогда условие видоизменяется: конкретного пользователя всегда считать процессом обмена.... Это отличается от исходного
Цитата
Дмитрий Чебыкин написал: Если так, то нельзя ли сразу пользователя (считаем, что для обмена у нас отдельный пользователь) на хите определить и задать аналогично $inExchange
Если. Конкертный пользователь означает обмен и только обмен, тогда и проблемы то нет. Переменная $inExchage совсем не нужна - просто анаизируем текущего юзера...
Александр Воробьев написал: Если. Конкертный пользователь означает обмен и только обмен, тогда и проблемы то нет. Переменная $inExchage совсем не нужна - просто анаизируем текущего юзера...
А при чем тут init.php? Файл подключается раньше, а обработчик события гораздо позже вызывается, тогда уже есть переменная $GLOBALS['USER'] (она же $USER). То что реализация обработчика описана в init.php (кстати, я бы рекомендовал выносить в отдельные файлы, и пользоваться функцией автозагрузки классов при обращении) не означает что метод выполняется в момент подключения файла.
Александр Воробьев, премного благодарен за разъяснения. От анекдота все равно не откажусь, ибо техподдержке был задан вопрос - можно ли обработать события изменения заказа (корзины заказа), отдельной логикой, если они происходят в результате обмена с 1с. Ответ был отрицательный и пришлось ковырять order_loader.php, чтобы изменять заказ в БУС из 1с так, как нужно мне.
Учебные курсы и документация не помогли решить проблему Я понимаю, что вопросы изменения дизайна и доработка функционала не решаются техподдержкой
Ответ дан исключительно опираясь на данные доступные в документации. Я не знаю как принято в Битрис, но на мой взгляд знать ответы на подобные вопросы не обязательно для ТП первого уровня.
Александр Воробьев, не знаю, что такое "ТП первого уровня", а подсказывать, где в админке найти ту или иную настройку - это не ТП. Вот сижу и думаю, если спрошу, работает ли On***CatalogImport1C в случае, когда идет обмен заказами (или это чисто для каталога прием), не вызовет ли это культурный шок у ТП (обмен заказами можно запускать независимо от заказа в отдельном обмене). Да и в моем случае, могли бы ответить, что обработка событий происходит тогда, когда $USER уже известен и в обработку событий в init.php можно делать разную логику в зависимости от пользователя - отработает нормально.
Ну собственно практически спустя месяц после обсуждения и у меня появилась подобная задача Хочу поделиться решением, мб кому пригодиться. Расчет на то, что 1С авторизуется на сайте под определенным пользователем, собственно и проверяем в коде, что текущей пользователь принадлежит группе "Интеграция 1С".
<?php
class ProcessCatalog
{
/*
* Устанавливается ид группы, которой позволено конвретировать данные товаров
* */
const AVAIL_USER_GROUP_ID = 7;
/*
* Функции обработчиков для изменения кол-ва на складах
* */
public function OnBeforeStoreProductAdd(&$arFields)
{
self::onBeforeStoreProductHandler($arFields);
}
public function OnBeforeStoreProductUpdate($id, &$arFields)
{
self::onBeforeStoreProductHandler($arFields);
}
/*
* Функции обработчиков для изменения цен
* */
public function OnBeforePriceAdd(&$arFields)
{
self::OnBeforePriceHandler($arFields);
}
public function OnBeforePriceUpdate($id, &$arFields)
{
self::OnBeforePriceHandler($arFields);
}
/**
* Конвретирует данные о ценах, согласно настройкам, если пользаку это позволено
* Доступные поля:
* PRODUCT_ID - код товара;
* EXTRA_ID - код наценки;
* CATALOG_GROUP_ID - код типа цены;
* PRICE - цена;
* CURRENCY - валюта цены;
* QUANTITY_FROM - количество товара, начиная с приобретения которого действует эта цена;
* QUANTITY_TO - количество товара, при приобретении которого заканчивает действие эта цена.
*
* @param $arFields - доступные поля
*/
public static function OnBeforePriceHandler(&$arFields)
{
if (self::checkUserAvail() && intval($arFields["PRICE"]) > 0) {
/*
* do something
* */
}
}
/**
* Конвретирует данные о кол-ве на складе, согласно настройкам, если пользаку это позволено
* Доступные поля:
* PRODUCT_ID - ID товара;
* STORE_ID - ID склада;
* AMOUNT - количество товара.
*
* @param $arFields - доступные поля
*/
public static function onBeforeStoreProductHandler(&$arFields)
{
if (self::checkUserAvail() && intval($arFields["AMOUNT"]) > 0) {
/*
* do something
* */
}
}
/**
* Проверяется, что текущий пользователь принадлежит группе, которой позволено конвретировать данные товаров
*
* @return bool
*/
public static function checkUserAvail()
{
global $USER;
if ($USER->IsAuthorized()) {
$arUserGroup = $USER->GetUserGroupArray();
if (in_array(self::AVAIL_USER_GROUP_ID, $arUserGroup)) {
return true;
}
}
return false;
}
}