Нам часто требуется различные функциональные штуки при обмене, например: изменение названий и урлов у товаров или категорий, удаление товаров и категорий, разбить множественные свойства на отдельные свойства, перенести значение свойств под основные характеристики, работа с ценами и многое-многое другое.
Для этого мы решили опубликовать гайд некоторых решений, который мы будем постепенно обновлять, а всеми этими штуками могут пользоваться все желающие.
Пример 1
// файл init.php прописываем куда ссылаться
if(file_exists($_SERVER[‘DOCUMENT_ROOT’] . ‘/bitrix/php_interface/include/1c_update.php’))
require $_SERVER[‘DOCUMENT_ROOT’] . ‘/bitrix/php_interface/include/1c_update.php’;
if(file_exists($_SERVER[‘DOCUMENT_ROOT’] . ‘/bitrix/php_interface/include/tovar_vid_price.php’))
require $_SERVER[‘DOCUMENT_ROOT’] . ‘/bitrix/php_interface/include/tovar_vid_price.php’;
Пример 2
// Запретить менять значения свойств инфоблока в 1С Битрикс
AddEventHandler(«iblock», «OnBeforeIBlockPropertyUpdate»,»DoNotUpdate»);
AddEventHandler(«iblock», «OnBeforeIBlockSectionUpdate», «DoNotUpdateSection»);
function DoNotUpdate(&$arFields){
if ($_REQUEST[‘mode’]==’import’)
{
unset($arFields[‘NAME’]);
}
}
function DoNotUpdateSection(&$arFields){
if ($_REQUEST[‘mode’]==’import’)
{
unset($arFields[‘NAME’]);
unset($arFields[‘CODE’]);
}
}
Пример 3
// Запрет на обновение категорий товара
AddEventHandler(«iblock», «OnBeforeIBlockSectionUpdate», «MyOnBeforeIBlockSectionUpdate»);
function MyOnBeforeIBlockSectionUpdate(&$arFields){
if(($_GET[‘type’] == ‘catalog’) && ($_GET[‘mode’] == ‘import’) && strstr($_GET[‘filename’], ‘import’) &&($arFields[«IBLOCK_ID»]==10)){
unset($arFields[«NAME»]);
unset($arFields[«CODE»]);
unset($arFields[«IBLOCK_SECTION_ID»]);
}
}
Пример 4
// реализуем возможность загрузки из 1С множественной строки и разбиваем на отдельные свойства
AddEventHandler(«iblock», «OnAfterIBlockElementAdd», «UpdatePluralToProp_1с»);
AddEventHandler(«iblock», «OnAfterIBlockElementUpdate», «UpdatePluralToProp_1с»);
function UpdatePluralToProp_1с(&$arFields)
{
// соберем массив свойств элемента и сделаем обработку строки которая может быть множественной
// переменная для разделителей
$spacer = array(«,», «\\»);
$properties = array();
$db_props = CIBlockElement::GetProperty($arFields[‘IBLOCK_ID’], $arFields[‘ID’], array(«sort» => «asc»), Array());
while ($ob = $db_props->GetNext())
{
// получим свойство в котором обнаружили разделитель
if($ob[‘CODE’] != ‘PROPERTY_КОДСВОЙСТВА’) {
$properties[$ob[‘ID’]][] = $ob;
}
}
foreach ($properties as $keyprops => $prop) {
// удалим пустые значения
foreach($prop as $key_v => $values) {
if(empty($values[‘VALUE’])){
unset($prop[$key_v]);
}
}
if ((!empty($prop)) && (count($prop) <= 2)) {
foreach ($prop as $key_pro => $v) {
// пройдемся по массиву с разделителями
foreach($spacer as $spacer_value) {
if (strpos($v[‘VALUE’], $spacer_value) !== false) {
// разделим строку на массив
//$v[‘VALUE’] = str_replace(» «,»»,$v[‘VALUE’]);
$v[‘VALUE’] = trim($v[‘VALUE’]);
$PROPERTY_VALUE = array();
$arr_element_prop = array();
$arr_element_prop = explode($spacer_value, $v[‘VALUE’]);
foreach($arr_element_prop as $key_elem => $element_prop) {
$element_prop = trim($element_prop);
$PROPERTY_VALUE[‘n’.$key_elem] = array(
‘VALUE’=>$element_prop
);
}
CIBlockElement::SetPropertyValuesEx($arFields[‘ID’], $arFields[‘IBLOCK_ID’], array($keyprops => $PROPERTY_VALUE));
}
}
}
}
}
// printr($arFields);die;
}
Пример 5
// Реквизиты разбиваем на отдельные свойства
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandlerCompatible(‘iblock’, ‘OnAfterIBlockElementUpdate’,
array(‘ext1cHandler’, ‘attributeFieldToProps’));
$eventManager->addEventHandlerCompatible(‘iblock’, ‘OnAfterIBlockElementAdd’,
array(‘ext1cHandler’, ‘attributeFieldToProps’));
class ext1cHandler
{
const CML2_ATTRIBUTES_NAME = ‘CML2_TRAITS’;
protected static $iblockProps = null;
/**
* @param $iblockId
* @return array|null
*/
protected static function getIblockProps($iblockId)
{
if (self::$iblockProps === null) {
$resProps = CIBlock::GetProperties($iblockId, Array(), Array());
if (intval($resProps->SelectedRowsCount()) > 0) {
self::$iblockProps = array();
while ($arProp = $resProps->Fetch()) {
self::$iblockProps[$arProp[‘CODE’]] = $arProp[‘ID’];
}
}
}
return self::$iblockProps;
}
/**
* @param $arFields
*/
public static function attributeFieldToProps($arFields)
{
if (!self::is1cSync()) return true;
self::getIblockProps($arFields[‘IBLOCK_ID’]);
if (empty(self::$iblockProps) || !is_array(self::$iblockProps)) return;
//получаем массив значений множественного свойства CML2_ATTRIBUTES в которое стандартно выгружаются характеристики ТП из 1С
$resCml2Attributes = CIBlockElement::GetProperty($arFields[‘IBLOCK_ID’], $arFields[‘ID’], array(‘sort’ => ‘asc’), array(‘CODE’ => self::CML2_ATTRIBUTES_NAME));
while ($arCml2Attribute = $resCml2Attributes->GetNext()) {
$cml2AttributeName = $arCml2Attribute[‘DESCRIPTION’]; //название характеристики
$cml2AttributeValue = $arCml2Attribute[‘VALUE’]; //значение характеристики
// создание свойства
$codeNewProp = self::getTranslit($cml2AttributeName);
if (!isset(self::$iblockProps[$codeNewProp])) {
$arFieldsProp = array(
‘NAME’ => $cml2AttributeName,
‘ACTIVE’ => ‘Y’,
‘SORT’ => ‘500’,
‘CODE’ => $codeNewProp,
‘PROPERTY_TYPE’ => ‘L’,
‘IBLOCK_ID’ => $arFields[‘IBLOCK_ID’],
‘VALUES’ => array(),
);
$ibp = new CIBlockProperty;
if ($propId = $ibp->Add($arFieldsProp)) {
self::$iblockProps[$codeNewProp] = $propId;
}
}
if (isset(self::$iblockProps[$codeNewProp])) {
self::getEnumListProp($arFields[‘IBLOCK_ID’], self::$iblockProps[$codeNewProp]);
$xmlIdPropValue = self::getTranslit($cml2AttributeValue);
if (!isset(self::$enumListProps[self::$iblockProps[$codeNewProp]][$xmlIdPropValue])) {
$ibpenum = new CIBlockPropertyEnum;
$arFieldsEnum = array(
‘XML_ID’ => $xmlIdPropValue,
‘PROPERTY_ID’ => self::$iblockProps[$codeNewProp],
‘VALUE’ => $cml2AttributeValue
);
if ($enumPropValueId = $ibpenum->Add($arFieldsEnum)) {
self::$enumListProps[self::$iblockProps[$codeNewProp]][$xmlIdPropValue] = $enumPropValueId;
}
}
if (isset(self::$enumListProps[self::$iblockProps[$codeNewProp]][$xmlIdPropValue])) {
CIBlockElement::SetPropertyValues($arFields[‘ID’], $arFields[‘IBLOCK_ID’], array(
‘VALUE’ => self::$enumListProps[self::$iblockProps[$codeNewProp]][$xmlIdPropValue]
), self::$iblockProps[$codeNewProp]);
}
}
}
}
private static $enumListProps = array();
/**
* @param $iblockId
* @param $propId
* @return array
*/
protected static function getEnumListProp($iblockId, $propId)
{
if (!isset(self::$enumListProps[$propId])) {
$resEnumField = CIBlockPropertyEnum::GetList(array(‘SORT’ => ‘ASC’), array(‘IBLOCK_ID’ => $iblockId, ‘PROPERTY_ID’ => $propId));
if (intval($resEnumField->SelectedRowsCount()) > 0) {
self::$enumListProps[$propId] = array();
while ($arEnumField = $resEnumField->Fetch()) {
self::$enumListProps[$propId][$arEnumField[‘XML_ID’]] = $arEnumField[‘ID’];
}
}
}
return self::$enumListProps;
}
/**
* @param $text
* @param string $lang
* @return string
*/
private static function getTranslit($text, $lang = ‘ru’)
{
$resultString = CUtil::translit($text, $lang, array(
‘max_len’ => 50,
‘change_case’ => ‘U’,
‘replace_space’ => ‘_’,
‘replace_other’ => ‘_’,
‘delete_repeat_replace’ => true,
)
);
if (preg_match(‘/^[0-9]/’, $resultString)) {
$resultString = ‘_’ . $resultString;
}
return $resultString;
}
/**
* @return bool
*/
private static function is1cSync()
{
static $is1C = null;
if ($is1C === null) {
$is1C = (isset($_GET[‘type’], $_GET[‘mode’]) && $_GET[‘type’] === ‘catalog’ && $_GET[‘mode’] === ‘import’);
}
return $is1C;
}
}
Пример 6
// деактивируем товары у которых нет картинок и цен
AddEventHandler(«catalog», «OnCompleteCatalogImport1C», «myCustomImportDeactiv»);
function myCustomImportDeactiv($arg1, $arg2 = false){
CModule::IncludeModule(«iblock»);
CModule::IncludeModule(«catalog»);
CModule::IncludeModule(«sales»);
$el = new CIBlockElement;
$arSelect = Array(«ID», «ACTIVE», «DETAIL_PICTURE», «CATALOG_PRICE_1»);
/**
* Выключаем товары у которых нет картинок
* @return [type] [description]
*/
function deactivationItemWhereNoPicture()
{
global $el, $arSelect;
$arFilter = array(«IBLOCK_ID» => 51, «DETAIL_PICTURE» => false);
$res = CIBlockElement::GetList(array(), $arFilter, false, false, $arSelect);
while ($r = $res->GetNext()) {
$el->Update($r[‘ID’], array(«ACTIVE» => «N»));
}
}
deactivationItemWhereNoPicture();
function deactivationItemWhereNoPrices()
{
$arProds = Array(); // Массив со всеми товарами
$resOffers = CIBlockElement::GetList( // Делаем выборку офероф
Array(),
Array(«IBLOCK_ID» => $IBLOCK_OFFERS_ID),
false,
false,
Array(«IBLOCK_ID», «ID», «ACTIVE», «PROPERTY_CML2_LINK.ID»)
);
while($arOffer = $resOffers->GetNext()) { // Записываем активность офферов товара в массив $arProds
$iProdId = $arOffer[«PROPERTY_CML2_LINK_ID»];
$iOfferId = $arOffer[«ID»];
$arProds[$iProdId][$iOfferId] = $arOffer[«ACTIVE»];
}
$arProdsToDeact = Array(); // Массив товаров для деактивации
foreach($arProds as $iProdId=>$arOffers) { // Перебор всех товаров
$bDeact = true; // По умолчанию товар декативируем
foreach($arOffers as $iOfferId=>$sOfferActive) { // Но саначала посмотрим оферы товара
if($sOfferActive == «Y») { // Если хоть один активен
$bDeact = false; // Отменяем деактивацию
break; // И выходим из перебора офероф
}
}
if($bDeact) { // Если товар все таки нужно деактивировать
$arProdsToDeact[] = $iProdId; // Запишем его в массив $arProdsToDeact
}
}
$elProd = new CIBlockElement;
$arProd = Array(«ACTIVE» => «N»); // Каждому указываем новое значение активности
foreach($arProdsToDeact as $keyProd=>$iProdId) { // Пройдем массив товаров для деактивации по порядку
$res = $elProd->Update($iProdId, $arProd);
}
}
deactivationItemWhereNoPrices();
}
Пример 7
// удаляем — деактивируемые товары
AddEventHandler( «catalog», «OnCompleteCatalogImport1C» , array( «mdf_import», «DeleteDeactivatedElement» ) );
class mdf_import {
function DeleteDeactivatedElement( $arParams, $arFields ){
// Поиск инфоблоков, в которых были выполнены изменения с момента последней выгрузки.
$curr_date = mktime(0,0,0);
$rsImportedIBlocks = \Bitrix\IBlock\IblockFieldTable::getList(array(
«select» => array(«IBLOCK_ID», «DEFAULT_VALUE»),
«filter» => array(
//»=IBLOCK_ID» => $arCatalogID,
«=FIELD_ID» => «XML_IMPORT_START_TIME»,
«>=DEFAULT_VALUE» => date(«Y-m-d H:i:s», $curr_date),
),
));
// Обходим в цикле инфоблоки
while ($arField = $rsImportedIBlocks->fetch())
{
// Ищем неактивные товары
$element = new CIBlockElement;
$rsElements = CIBlockElement::GetList(array(), array(
«IBLOCK_ID» => $arField[«IBLOCK_ID»],
«ACTIVE» => «N»,
), false, false, array(«ID»));
// в цикле удаляем эти товары
while ($arElement = $rsElements->Fetch())
{
$element->Delete($arElement[«ID»]);
}
}
echo «success\n», «Удаление деактивированных элементов завершено\n»;
}
}
Пример 8
// забираем вес из свойства и передаем в нужную характеристику
AddEventHandler(«catalog», «OnCompleteCatalogImport1C», «myCustomImportMod»);
function myCustomImportMod($arg1, $arg2 = false){
Cmodule::IncludeModule(‘catalog’);
CModule::IncludeModule(‘iblock’);
$addProps = CIBlockElement::GetList (
Array(«ID» => «ASC»),
Array(«IBLOCK_ID» => 52),
false,
false,
Array(
‘ID’,
‘PROPERTY_WEIGHT’
)
);
while($ar_fields = $addProps->GetNext())
{
echo ‘Товару с ID-‘ . $ar_fields[‘ID’] .’ установлены параметры’;
echo ‘Вес:’ . $ar_fields[‘PROPERTY_WEIGHT_VALUE’];
Cmodule::IncludeModule(‘catalog’);
$PRODUCT_ID = $ar_fields[‘ID’];
$arFields = array(
‘WEIGHT’ => $ar_fields[‘PROPERTY_WEIGHT_VALUE’],
);
CCatalogProduct::Update($PRODUCT_ID, $arFields);
}
}
Пример 9
// несколько видов цен в зависимости от суммы
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(«catalog», «OnGetOptimalPrice», [«amountForPrice», «onGetPriceEvent»]);
global $basketPrice;
$basketPrice = [];
class amountForPrice
{
/**
* [BASE ID: 1] при сумме заказа до 3 000 рублей
* [SALE_SMAL ID: 2] Мелкий опт от 3.000 руб.
* [SALE_MIDDLE ID: 4] Крупный опт от 35.000 руб.
* [SALE_LARGE ID: 5] Крупный опт от 35.000 руб.
*/
// Проставляем ID типов цен
protected static $baseGroup = 1;
protected static $smallGroup = 6;
protected static $largeGroup = 7;
public function onGetPriceEvent($productID, $quantity = 1, $arUserGroups = [], $renewal = «N», $arPrices = array(), $siteID = false, $arDiscountCoupons = false)
{
// Через global, чтоб не вызывалось по несколько раз
// OnGetOptimalPrice вызывается для каждого товара
global $basketPrice;
if (empty($basketPrice)) {
$basketPrice = self::getBasketUser();
}
// Получаем доступные цены для товара
$resOptPrices = \Bitrix\Catalog\PriceTable::getList([
‘filter’ => [‘=PRODUCT_ID’ => $productID],
‘select’ => [‘CATALOG_GROUP_ID’, ‘PRICE’, ‘CURRENCY’],
]);
while($optPrice = $resOptPrices->fetch()){
$arOptPrices[$optPrice[‘CATALOG_GROUP_ID’]] = $optPrice;
}
if(empty($arOptPrices)){
return false;
}
$arPrice = self::getPrice($basketPrice, $arOptPrices);
$result = [
‘PRICE’ => [
«ID» => $productID,
‘CATALOG_GROUP_ID’ => $arPrice[‘group’],
‘PRICE’ => $arPrice[‘price’],
‘CURRENCY’ => $arPrice[‘currency’],
‘ELEMENT_IBLOCK_ID’ => $productID,
‘VAT_INCLUDED’ => «Y»,
],
‘DISCOUNT’ => [‘VALUE’ => $discount, ‘CURRENCY’ => $arPrice[‘currency’]],
];
return $result;
}
/**
* Функция смотрит цену корзины и выбирает подходящую цену
* Функции цен уже сам перебразывают цену в зависимости от доступности
*/
public function getPrice($sum = [], $arPrices = [])
{
if ($sum[self::$smallGroup] >= 35000) {
$result = self::priceLarge($arPrices);
} elseif ($sum[self::$baseGroup] >= 3000) {
$result = self::priceSmall($arPrices);
} else {
$result = self::priceBase($arPrices);
}
return $result;
}
public function priceLarge(array $arPrices)
{
if (empty($arPrices[self::$largeGroup])) {
$result = self::priceMiddle($arPrices);
} else {
$result = [
‘group’ => self::$largeGroup,
‘price’ => $arPrices[self::$largeGroup][‘PRICE’],
‘currency’ => $arPrices[self::$largeGroup][‘CURRENCY’]
];
}
return $result;
}
public function priceSmall(array $arPrices)
{
if (empty($arPrices[self::$smallGroup])) {
$result = self::priceBase($arPrices);
} else {
$result = [
‘group’ => self::$smallGroup,
‘price’ => $arPrices[self::$smallGroup][‘PRICE’],
‘currency’ => $arPrices[self::$smallGroup][‘CURRENCY’]
];
}
return $result;
}
public function priceBase(array $arPrices)
{
$result = [
‘group’ => 1,
‘price’ => $arPrices[self::$baseGroup][‘PRICE’],
‘currency’ => $arPrices[self::$baseGroup][‘CURRENCY’]
];
return $result;
}
/**
* Получение суммы корзины текущего пользователя
* Получаем суммы всех типов цен
*/
public function getBasketUser()
{
$resultPrice = [];
$resultPrice[self::$baseGroup] = 0;
$resultPrice[self::$smallGroup] = 0;
//$resultPrice[self::$largeGroup] = 0; // large мы не считаем т.к он уже не учавствует в выборке
$obBasket = \Bitrix\Sale\Basket::getList([
‘filter’ => [
‘FUSER_ID’ => \Bitrix\Sale\Fuser::getId(),
‘LID’ => SITE_ID,
‘ORDER_ID’ => NULL // Т.к корзина связана с заказами, то нам нужна корзина у которой нет заказа
],
‘select’ => [‘QUANTITY’, ‘PRODUCT_ID’, ‘CAN_BUY’, ‘DELAY’]
]);
while($arItem = $obBasket->Fetch()){
// Нам нужны только доступные товары остальные не считаем
if ($arItem[‘DELAY’] == ‘N’ && $arItem[‘CAN_BUY’] == ‘Y’) {
$resPrices = \Bitrix\Catalog\PriceTable::getList([
‘filter’ => [‘=PRODUCT_ID’ => $arItem[‘PRODUCT_ID’]],
‘select’ => [‘CATALOG_GROUP_ID’, ‘PRICE’],
]);
while ($price = $resPrices->fetch()) {
$arItem[‘PRICES’][$price[‘CATALOG_GROUP_ID’]] = $price[‘PRICE’];
}
// Если вдруг нет у товара цены, то берем следующую т.к нам нужна именно сумма
$resultPrice[self::$smallGroup] += (($arItem[‘PRICES’][self::$smallGroup] ?? $arItem[‘PRICES’][self::$baseGroup]) * $arItem[‘QUANTITY’]);
$resultPrice[self::$baseGroup] += ($arItem[‘PRICES’][self::$baseGroup] * $arItem[‘QUANTITY’]);
}
}
return $resultPrice;
}
}