Working with the product, the catalog.element component and trade offers in Bitrix
Rus
Eng
Работа с элементами инфоблока, товаром, catalog.element и торговыми предложениями в Битрикс

All English-language materials have been translated fully automatically using the Google service

Section navigation:

Product types

Product - the most common simple product is added, without any additional features. This is the type of product that is most commonly used, and is currently used everywhere.

Trade offers - This product (as a product that stores a list of offers) has no leftovers, no price or other attributes that a regular product has. This item is not a commodity. In fact, it contains a list of "trade offers", which are goods (offers).

A bundle is a list of products tied to the main product that the store wants to recommend for purchase. It is products that can be added to the set: trade offers and / or simple products. You cannot add products with offers.

Bundle is a list of products that make up the required bundle of the main product. The kit itself does not have a physical remainder, its remainder depends on the goods that are included in this kit. It is the products that can be added to the kit: trade offers and / or simple products. You cannot add products with offers.

Working with infoblock elements

Adding an infoblock element to the site

//We initialize the corresponding module
CModule::IncludeModule("iblock");

// Array with the properties of the infoblock element, where the keys of the array are the id of the required property
// (but you can use the CODE parameter)
// You also need to remember that for lists and fields with binding to elements or references
// as a value we specify the id of a list item, reference or object
$PROP = array(
    '342'=>'',
    '315'=>''
);

// Create a class object for work
$el = new CIBlockElement;

// Fill the array with data
$arLoadProductArray = Array(
	"MODIFIED_BY"           => 1, 						  // specify user ID
	"IBLOCK_SECTION"     => $IBLOCK_SECTION_ID,          //for linking to many sections
	//"IBLOCK_SECTION_ID"     => $IBLOCK_SECTION_ID,     //for a single partition
	"IBLOCK_ID"             => 20,
	"IBLOCK_TYPE"           => 'aspro_next_catalog',
	"PROPERTY_VALUES"       => $PROP,
	"NAME"                  => $NAME,
	"ACTIVE"                => "Y",
	"CODE"                  => $CODE,
	//"PREVIEW_TEXT"          => "text for the list of items",
	//"DETAIL_TEXT"           => "text for detailed view",
	//"DETAIL_PICTURE"        => CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/image.gif")
);

$res = $el->Add($arLoadProductArray);

if($res>0){
	//Added successfully
	//$ res stores the id of the added record
}else{
	// Add failed
	//Displaying an error message
	echo $el->LAST_ERROR;
}

Turning an infoblock element into a product

/ * We make the added product simple * /
$productFileds = array(
	"ID" => $res, //ID added infoblock element
	"VAT_ID" => 1, //set the vat type (set in the admin panel)
	"VAT_INCLUDED" => "Y", //VAT is included in the price
	"TYPE " => \Bitrix\Catalog\ProductTable::TYPE_PRODUCT //Item type
);

// Possible values of the product type:
//const TYPE_PRODUCT = 1;
//const TYPE_SET = 2;
//const TYPE_SKU = 3;
//const TYPE_OFFER = 4;
//const TYPE_FREE_OFFER = 5;
//const TYPE_EMPTY_SKU = 6;

if(CCatalogProduct::Add($productFileds)){
	// Information block element turned into a product
}else{
	//An error has occurred
}

Getting the list of properties of an infoblock element using D7

$res = \Bitrix\Iblock\ElementTable::getList(array(
    "select" => array("ID", "*"),
    "filter" => array("IBLOCK_ID" => $IBLOCK_ID, "ID" => $ELEMENT_ID),
    "order"  => array("ID" => "ASC")
));

while ($arItem = $res->fetch()) {
    $propRes = \Bitrix\Iblock\ElementPropertyTable::getList(array(
        "select" => array("ID", "*"),
        "filter" => array("IBLOCK_ELEMENT_ID" => $arItem["ID"],),
        "order"  => array("ID" => "ASC")
    ));
	
    while($prop = $propRes->Fetch())
        $arItem["PROPERTIES"][$prop["IBLOCK_PROPERTY_ID"]] = $prop;
}

Bitrix get element ID by its symbolic code

CIBlockFindTools::GetElementID($ELEMENT_ID, $ELEMENT_CODE, $SECTION_ID, $SECTION_CODE, $arFilter)
// $ section_id - ID of the section that contains the element
// $ section_code - symbolic code of the section in which the element lies
// $ arFilter - an array of additional properties for filtering

<? // example of use
$objFindTools = new CIBlockFindTools();
$elementID = $objFindTools->GetElementID(false, "super_element", false, "super_section", array("IBLOCK_ID" => 1));
// the method returns the ID of the element if it finds it, and 0 if the element is not found.

Work with the trade catalog (price\availability in warehouses\remains\reserves)

Getting a list of price types using d7

$rsPrices = \Bitrix\Catalog\GroupTable::getList();
while($arPrice = $rsPrices->fetch()){
    $PRICE_IDS[] = $arPrice['ID'];
}

Get the price and quantity of goods using D7

$dbPrice = \Bitrix\Catalog\Model\Price::getList([
	"filter" => array(
		"PRODUCT_ID" => $id,
		"CATALOG_GROUP_ID" => 1
	)]);

if ($arPrice = $dbPrice->fetch()) {
	$price = $arPrice['PRICE'];
}

Add or update product price

$arFieldsPrice = Array(
	"PRODUCT_ID" => $ID,							// ID of the added product
	"CATALOG_GROUP_ID" => 1,						//Price type ID
	"PRICE" => $item['price'],						// price value
	"CURRENCY" => !$currency ? "RUB" : $currency, 	//currency
);

// See if the price has been set for this product
$dbPrice = \Bitrix\Catalog\Model\Price::getList([
	"filter" => array(
		"PRODUCT_ID" => $item['ID'],
		"CATALOG_GROUP_ID" => 1
	)
]);

if ($arPrice = $dbPrice->fetch()) {
	//If the price is set, then we update
	$result = \Bitrix\Catalog\Model\Price::update($arPrice["ID"], $arFieldsPrice);
	
	if ($result->isSuccess()){
		echo "Updated the price of a product for a catalog item " . $item['ID'] . " Цена " . $item['price'] . PHP_EOL;
	} else {
		echo "Error updating the price of a product for a catalog item " . $item['ID'] . " Mistake" . $result->getErrorMessages() . PHP_EOL;
	}
}else{
	//If there is no price, then add
	$result = \Bitrix\Catalog\Model\Price::add($arFieldsPrice);
	
	if ($result->isSuccess()){
		echo "Added the price of a product for a catalog item " . $item['ID'] . " Price " . $item['price'] . PHP_EOL;
	} else {
		echo "Error adding the price of a product for a catalog item " . $item['ID'] . " Mistake " . $result->getErrorMessages() . PHP_EOL;
	}
}

Add quantity in warehouses to the product

$arFields = Array(
	"PRODUCT_ID" => $id, 		//Item ID
	"STORE_ID"   => $storeId, 	//Warehouse ID
	"AMOUNT"     => $amount,	//amount
);

CCatalogStoreProduct::Add($arFields);

You can read more about working with warehouses here Working with warehouses and the number of products in Bitrix D7

Updating the number of goods in warehouses

$rs = CCatalogStoreProduct::GetList(false, array(
	'PRODUCT_ID'=> $id, 		// Product ID
	'STORE_ID' => $storeId		//Warehouse ID
));

while($ar_fields = $rs->GetNext())
{
	// Update the value of the stock balance from the value of the quantitative accounting balance
	$arFields = Array(
		"PRODUCT_ID" => $id, 		//Item ID
		"STORE_ID"   => $storeId, 	//Warehouse ID
		"AMOUNT"     => $amount,	//amount
	);

	CCatalogStoreProduct::Update($ar_fields['ID'], $arFields);

}

Updating inventory with D7

$existProduct = \Bitrix\Catalog\Model\Product::getCacheItem($arFields['ID'],true);

if(!empty($existProduct)){
 \Bitrix\Catalog\Model\Product::update(intval($arFields['ID']),$arFields);
} else {
 \Bitrix\Catalog\Model\Product::add($arFields);
}

Add or update the total quantity of goods (the "Available quantity" parameter)

CCatalogProduct::Update(
	$ID, //ID of the added or updated item
	array(
		"QUANTITY" => $amount, //Quantity of goods
	)
)

Adding an item to the reserve

$provider = new \Bitrix\Catalog\Product\CatalogProvider;
$resReserve = $provider->reserve(array(
    $productId => ["PRODUCT_ID" => $PRODUCT_ID,  "QUANTITY" => 10]
));

Reserve withdrawal

$provider = new \Bitrix\Catalog\Product\CatalogProvider;
$resReserve = $provider->reserve(array(
    $productId => ["PRODUCT_ID" => $PRODUCT_ID,  "QUANTITY" => -10 ]
));

Or more briefly:

\Bitrix\Catalog\Product\CatalogProvider::ReserveProduct(array(
    $productId => ["PRODUCT_ID" => $PRODUCT_ID,  "QUANTITY" => -10 ]
))

Get measure and ratio of the product

\Bitrix\Catalog\ProductTable::getCurrentRatioWithMeasure($arResult['ID'])

Changing measure

\CCatalogProduct::Update(13194, array('MEASURE' => 6));

Changing ratio

$db_measure = CCatalogMeasureRatio::getList(array(), $arFilter = array('PRODUCT_ID' => 13194), false, false);
while ($ar_measure = $db_measure->Fetch()) {
    $new_measure = CCatalogMeasureRatio::update($ar_measure['ID'], array("RATIO" => 5));
}

Working with product properties

Getting the value of an individual product property in Bitrix using D7

class Product
{
    private $id;

    public function __construct($id)
    {
        if (empty($id)) {
            throw new \Bitrix\Main\ArgumentNullException('id');
        }
        $this->id = $id;
    }

    public function getFields()
    {
        return \Bitrix\Iblock\ElementTable::getById($this->id)->fetch();
    }

    public function getProperty($code)
    {
        $fields = $this->getFields();
        if ($fields) {
            $iblock = \Bitrix\Iblock\Iblock::wakeUp($fields['IBLOCK_ID']);
            $element = $iblock->getEntityDataClass()::getByPrimary($this->id, ['select' => [$code]])->fetchObject();
            $property = $element->__call('get', [$code]);
        }
        return $property ? $property->getValue() : null;
    }
}

Documentation Concept and architecture

Get the value of an individual property of a product or TP (old method)

$arFilter = Array("IBLOCK_ID"=>$kitOffer['IBLOCK_ID'], "ID"=>$kitOffer['ID']);
$arSelect = Array("SORT");
$res = CIBlockElement::GetList(Array(), $arFilter,false,false,$arSelect);
if ($ob = $res->GetNextElement()){;
	$arFields = $ob->GetFields();
}

We receive goods in Bitrix using D7

//The filter is standard, you know how to use it
$elementIterator = \Bitrix\Iblock\ElementTable::getList([
    'select' => [
        'ID',
    ],
    'filter' => [
        '=IBLOCK_ID' => 20,
        '!=ID' => array(1,2,3),//Array of product IDs to be skipped
    ]
]);

$elems = $elementIterator->fetchAll();

foreach ($elems as $element) {
    // create a class object for work
    $obElement = new CIBlockElement();
	
    // update the element and make it inactive
    $obElement->Update($element['ID'], Array("ACTIVE" => 'N'));
}

Get all the properties of the infoblock element

CIBlockElement::GetByID($arResult['ID'])->GetNextElement()->GetProperties()

Display of all product properties

CCatalogProduct::GetByIDEx($arElement['ID'])

Alternatively, you can use negative and positive sorting of properties. We can mark the properties needed for displaying with a positive sorting number, and unnecessary ones with a negative one, then the script for displaying properties will turn out to be quite compact

Go to the file result_modifier.php and add to the very bottom:

//==============================================//
// Show all properties in DISPLAY_PROPERTIES //
//==============================================//
$arResult["DISPLAY_PROPERTIES"] = array();
foreach ($arResult["PROPERTIES"] as $pid => &$arProp)
{
    // We do not display properties with sorting less than 0 for viewing (they will be service ones for us)
    if ($arProp["SORT"] < 0)
        continue;

    if((is_array($arProp["VALUE"]) && count($arProp["VALUE"])>0) ||
        (!is_array($arProp["VALUE"]) && strlen($arProp["VALUE"])>0))
    {
        $arResult["DISPLAY_PROPERTIES"][$pid] = CIBlockFormatProperties::GetDisplayValue($arResult, $arProp);
    }
}

The solution was suggested by Left Ivan from the Bitrix forum

Getting the values of a product property of the list type

$property_enums = CIBlockPropertyEnum::GetList(Array("DEF"=>"DESC", "SORT"=>"ASC"), Array("IBLOCK_ID"=>15, "CODE"=>"BREND_INTERNAL"));
while($enum_fields = $property_enums->GetNext()){
    echo $enum_fields["ID"]." - ".$enum_fields["VALUE"]."<br>";
}

Add a new value to a property of the list type

$ibpenum = new CIBlockPropertyEnum;
if($PropID = $ibpenum->Add(Array('PROPERTY_ID'=>$PROPERTY_ID, 'VALUE'=>'New Enum 1')))
 echo 'New ID:'.$PropID;

or add a property to D7

\Bitrix\Main\Loader::includeModule('iblock');

$property = \CIBlockProperty::GetList(
    [],
    [
        'IBLOCK_ID' => $iblockId,  
        'CODE' => $code'
    ]
)->Fetch();

$ibpenum = new \CIBlockPropertyEnum();
$valueId = $ibpenum->Add([
    'PROPERTY_ID' => $property['ID'],
    'VALUE' => $newValueText,
    'XML_ID' => $newValueXmlId,
]);
if ((int) $valueId < 0) {
    throw new \Exception('Unable to add a value');
}

Remove value from property of type list

CIBlockPropertyEnum::Delete(ID);

or remove the property on D7

if (! \Bitrix\Main\Loader::includeModule('iblock')) {
    throw new \Bitrix\Main\LoaderException('Unable to load IBLOCK module');
}

$property = \CIBlockProperty::GetList([], ['IBLOCK_ID' => $iblockId, 'CODE' => $propertyCode])->Fetch();
if (! $property) {
    throw new \Exception('No such property');
}

$query = \CIBlockPropertyEnum::GetList(
    [], 
    ["IBLOCK_ID" => $iblockId, "XML_ID" => 6, "PROPERTY_ID" => $property['ID']]
);

$value = $query->GetNext();
if (! $value) {
    throw new \Exception('No such value');
}

$delete = \CIBlockPropertyEnum::delete($value['ID']);
if (! $delete) {
    throw new \Exception('Error while deleting the property value');
}

Working with kits and kits

Getting the composition of sets and kits for the parent product

$arProducts = CCatalogProductSet::GetList(
	array(), array( "TYPE" => array(1,2), "OWNER_ID" => $productID), false, false, array()
);

while($item = $arSets->Fetch() ){
	if($item["OWNER_ID"]!=$item["ITEM_ID"]  ){
		$arComplects[] = $item["ITEM_ID"];
	}
}

We get goods adjacent to the desired one in a set or set

\Bitrix\Main\Loader::includeModule('catalog');

$rItems = CCatalogProductSet::GetList(
	array(),
	array(
		array(
			'LOGIC' => 'OR',
			'TYPE' => CCatalogProductSet::TYPE_GROUP,
			'TYPE' => CCatalogProductSet::TYPE_SET
		),
		'ITEM_ID' => $arResult['ID']),
	false,
	false,
	array('SET_ID', 'OWNER_ID', 'ITEM_ID', 'TYPE')
);
while ($item = $rItems->Fetch()) {
	$arComplect[] = $item;
}

Get the required properties by the id array of trade offers

function getInform($arrOffersIDs){
	$arrProductsIDs = array();
	foreach($arrOffersIDs as $intElementID){
		$mxResult = CCatalogSku::GetProductInfo(
			$intElementID, 4
		);

		if (is_array($mxResult))
		{
			$arrProductsIDs[] = $mxResult["ID"];
		}

	}

	return  CCatalogSKU::getOffersList(
		$arrProductsIDs,
		$iblockID = 4,
		$skuFilter = array('ID'=>$arrOffersIDs),
		$fields = array('ID','NAME','DETAIL_PICTURE'),
		$propertyFilter = array() );

}
				
print_r(getInform(array(45,87,98)));

Getting the required properties of all trade offers

function getInform($arrOffersIDs){
	$arrProductsIDs = array();
	foreach($arrOffersIDs as $intElementID){
		$mxResult = CCatalogSku::GetProductInfo(
			$intElementID, 
			4 //ID каталога с товаром
		);

		if (is_array($mxResult))
		{
			$arrProductsIDs[] = $mxResult["ID"];
		}
	}

	return  CCatalogSKU::getOffersList(
		$arrProductsIDs,
		$iblockID = 4, //ID каталога с товаром
		$skuFilter = array(),
		$fields = array('NAME','ID','DETAIL_PICTURE','PREVIEW_PICTURE', "DETAIL_PAGE_URL", "CML2_MANUFACTURER", "DETAIL_TEXT"),//Получаемые свойства
		$propertyFilter = array() );

}
print_r(getInform(array(45,87,98)));

Working with discounts

We receive discounts and rules for working with the cart that were applied to the product

In the file result_modifier.php of the sale.basket.basket component, we get a list of discounts applied to the product

/** @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);

Auto-generating rules for working with the cart, adding a coupon and applying it

Generation code

require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use Bitrix\Sale\Internals;
CModule::IncludeModule("catalog");
CModule::IncludeModule("iblock");
CModule::IncludeModule("sale");

global $APPLICATION;

$unixStart = strtotime(date("d.m.Y H:i:s"));
$unixEnd = $unixStart+43200; //12 часов
$xcount = 0;
$discountValue = rand(1,10); //Размер случайной скидки от 1 до 10 процентов

$Actions["CLASS_ID"] = "CondGroup";
$Actions["DATA"]["All"] = "AND";
$Actions["CLASS_ID"] = "CondGroup";
$Actions["CHILDREN"][0]["CLASS_ID"] = "ActSaleBsktGrp";
$Actions["CHILDREN"][0]["DATA"]["Type"] = "Discount";
$Actions["CHILDREN"][0]["DATA"]["Value"] = $discountValue;
$Actions["CHILDREN"][0]["DATA"]["Unit"] = "Perc";
$Actions["CHILDREN"][0]["DATA"]["All"] = "OR";

$DbParentEl = CIBlockElement::GetList(array(),array("SECTION_ID"=>array(10,11)),false,false,array("ID")); 
while($ParentId = $DbParentEl->Fetch()){
	// Array of products to which the discount will be applied
	$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["CLASS_ID"] = "CondIBElement"; 
	$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["DATA"]["logic"] = "Equal";
	$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["DATA"]["value"] = $ParentId["ID"];
	$xcount++;	
}

$Conditions["CLASS_ID"] = "CondGroup";
$Conditions["DATA"]["All"] = "AND";
$Conditions["DATA"]["True"] = "True";
$Conditions["CHILDREN"] = "";

// Array for creating the rule
$arFields = array(
	"LID"=>"s1",
	"NAME"=>$discountValue."% Скидки ".date("d.m.y"),
	"CURRENCY"=>"RUB",
	"ACTIVE"=>"Y",
	"USER_GROUPS"=>array(1),
	"ACTIVE_FROM"=>ConvertTimeStamp($unixStart, "FULL"),
	"ACTIVE_TO"=>ConvertTimeStamp($unixEnd, "FULL"),
	"CONDITIONS"=>$Conditions,
	'ACTIONS' => $Actions
	);
	
$ID = CSaleDiscount::Add($arFields); //Create a cart rule
$res = $ID>0;
if ($res) { 	
	$codeCoupon = CatalogGenerateCoupon(); // Coupon generation
    $fields["DISCOUNT_ID"] = $ID;
	$fields["COUPON"] = $codeCoupon;
	$fields["ACTIVE"] = "Y";
	$fields["TYPE"] = 2;
	$fields["MAX_USE"] = 0;
	$dd = Internals\DiscountCouponTable::add($fields); //Create a coupon for this rule
	if (!$dd->isSuccess())
	{
		$err = $dd->getErrorMessages();
	}else{
		echo 'Купон на скидку: '.$codeCoupon;
	}
}else{
	$ex = $APPLICATION->GetException();  
	echo 'Ошибка: '.$ex->GetString();
}

Call code

require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

use Bitrix\Main\Loader;
use Bitrix\Sale\DiscountCouponsManager;

CModule::IncludeModule("sale");

$check = DiscountCouponsManager::isExist($_POST['coupon']);

if($check["ID"]>0){
	DiscountCouponsManager::add($_POST['coupon']); 	
	echo "ok";
}else{
	echo 'There is no such coupon ';
}

Original solution

Working with taxes

To check the box "VAT is included in the price" when adding a product, use the OnProductAdd event. But don’t forget that the event is outdated, for it to work you need to enable “Enable support for outdated events” in the trading catalog settings

AddEventHandler("catalog", "OnProductAdd", "OnProductAdd");
function OnProductAdd($ID, $FIELDS)
{   
	CCatalogProduct::Update($ID, ["VAT_INCLUDED"=>'Y']);
}

To mass change the value “VAT is included in the price” you can run an sql query

UPDATE b_catalog_product SET VAT_INCLUDED = 'Y', VAT_ID='1' WHERE VAT_INCLUDED = 'N'

Working with the template

Changing additional fields of trade offers in the product card "on the fly" using the example of PREVIEW_TEXT and DETAIL_TEXT

Go to result_modifier.php and add:

foreach ($arResult['OFFERS'] as $pid => &$arProp)
{
    $arResult['JS_OFFERS'][$pid]['PREVIEW_TEXT'] = $arProp['PREVIEW_TEXT'];//Add sentences to the resulting js array PREVIEW_TEXT
    $arResult['JS_OFFERS'][$pid]['DETAIL_TEXT'] = $arProp['DETAIL_TEXT'];//We also add DETAIL_TEXT
}

First, we prepare containers where we will display new information in the template template.php . In my case, it is assumed that PREVIEW_TEXT will be loaded into a block with the offerShortDescription class, DETAIL_TEXT into a block with the offerFullDescription class. Add blocks to the required place of the template.

<div class="offerShortDescription"><?=$actualItem['PREVIEW_TEXT']?></div>
<div class="offerFullDescription"><?=$actualItem['DETAIL_TEXT']?></div>

Go to the script.js file, look for the changeInfo function (~ 2463) and after the condition if (this.obSkuProps) (~ 2553) add:

if(this.offers[index].PREVIEW_TEXT && this.offers[index].DETAIL_TEXT){
	$('.offerShortDescription').html(this.offers[index].PREVIEW_TEXT);
	$('.offerFullDescription').html(this.offers[index].DETAIL_TEXT);
}

Do not forget to handle cases when the description fields for trade offers are not filled. I suggest you do it yourself. Personally, I just added via result_modifier.php to each trade offer additional fields in the description of the original product. This is not very pretty, but quite a working solution.

Remove the slide change when hovering over the preview in the slider

Go to script.js and comment these lines (they appear twice on ~ 552 and ~ 607)

BX.bind(this.product.slider.ITEMS[j], 'mouseenter', BX.delegate(this.onSliderControlHover, this));
BX.bind(this.product.slider.ITEMS[j], 'mouseleave', BX.delegate(this.onSliderControlLeave, this));

The solution was applied on the Business v18.5.0 edition

Changing the active trade offer

Go to the file \bitrix\components\bitrix\catalog.element\templates\.default\result_modifier.php

We are looking for the line foreach ($ arResult ['OFFERS'] as $ keyOffer => $ arOffer) - somewhere around line 405.

There is a line $ intSelected = $ keyOffer; - this is $ intSelected this is the ordinal (!) number of the trade offer selected by default. That is, the key of the element of the TP array, which are displayed for a particular product.

For example, I had a task to make active by default the offer ID of which was in $ _GET ['TP_ID'] .

Into the loop foreach ($ arResult ['OFFERS'] as $ keyOffer => $ arOffer) insert:

if ($_GET['TP_ID']) {
   if ($arOffer['ID']==$_GET['TP_ID']) {
         $intSelected =$keyOffer;
   }
}  

Exchange with 1C

Crutch for recalculating balances and quantity of goods when updating from 1C

It will be useful if your reserves are not updated when importing from 1C, and your Bitrix edition does not have a button to clear reserves

//When you start importing from 1C, set the session variable
AddEventHandler(
      'catalog',
      'OnBeforeCatalogImport1C',
      function ()
      {
         $_SESSION["1C_UPDATE"] = true;
      }
);

//When updating the product, we check that the session variable is set and modify the data
AddEventHandler(
      'catalog',
      'OnProductUpdate',
      function ($id, $arFields)
      {
        if (isset($_SESSION["1C_UPDATE"]) && $_SESSION["1C_UPDATE"])
        {
           $_SESSION["1C_UPDATE"] = false; //fight against looping so that it does not trigger itself. Since when calling CCatalogProduct :: Update, OnProductUpdat will be triggered again
           CCatalogProduct::Update(
            $id, 
            array(
				'QUANTITY' => $arFields['QUANTITY'] + $add /*new quantity of goods*/,
				'QUANTITY_RESERVE' => $arFields['QUANTITY_RESERVE'] + $add /*new quantity of reserved items*/
			)
         );
           $_SESSION["1C_UPDATE"] = true;
        }
      }
);
   
//At the end of import from 1C, set the session variable
AddEventHandler(
      'catalog',
      'OnSuccessCatalogImport1C',
      function ()
      {
         $_SESSION["1C_UPDATE"] = false;
      }
);

I describe additional solutions to emerging exchange problems here Bitrix: exchange with 1C

Complex ORM selection to get all the necessary product data

Quite often there is a task to speed up a particular component, display products "as in the catalog" or get all the necessary data for a product element. This can be done both with the help of ordinary components and with the help of ORM.

Sharing an example of a complex case ORM query. On the client's site, a trifling selection of 20 products based on catalog.section significantly loaded the system and worked out in an average of 1.5s. By combining all the selections into one query, I was able to speed up the selection to 0.4-0.5s without caching, and with caching of results up to 0.1s

This ORM query outputs:

- ordinary goods and goods with TP.

- the request receives several types of prices, including the base price

- query gets products from section including subsections

- query sorts products in random RAND order

- the request correctly generates the URL

- in this query, product properties are stored in a common table (important)

- the query summarizes all values ​​of stock balances

$IBLOCK_ID = 29;

    //Properties in the general table
    $entityPropsSingle   = \Bitrix\Main\Entity\Base::compileEntity(
        'PROPS_COMMON',
        [
            'IBLOCK_ELEMENT_ID'     => ['data_type' => 'integer'],
            'IBLOCK_PROPERTY_ID'    => ['data_type' => 'integer'],
            'VALUE'                 => ['data_type' => 'string'] // there are also columns like VALUE_TYPE, VALUE_ENUM, VALUE_NUM, DESCRIPTION, but we don't need them here, so we omit
        ],
        [
            'table_name'            => 'b_iblock_element_property', // general table with properties
        ]
    );

    //Get 20 random products in the section, including nested ones
    $elements = \Bitrix\Iblock\ElementTable::getList([
        'filter' => [
            'SECTION.ID' => $arResult['SECTION']['PATH'][0]['ID'], // The section in which we are looking for including subsections.
            '==LINK.ADDITIONAL_PROPERTY_ID' => NULL, // Basic binding of elements to groups.
            'ACTIVE' => 'Y', // active elements.
            '!=ID' => [$arResult['ID']], //We exclude an unnecessary element from the selection
            '>base_price.PRICE' => 0, // Exclude products with zero base price
            '=PROPS_335.IBLOCK_PROPERTY_ID' => 335, // id properties article
            '=PRODUCT.AVAILABLE' => 'Y', // Available for purchase
            //'>PRODUCT.QUANTITY' => 0, // There are leftovers
            //'!=PRODUCT.TYPE' => 1, // Product with trade offers
        ],
        'select' => [
            'ID',
            'NAME',
            'IBLOCK_ID', //Url construction element
            'IBLOCK_SECTION_ID', //Url construction element
            'CODE', //Url construction element
            "QUANTITY" => "PRODUCT.QUANTITY", //Quantity
            //'QUANTITY_STORE', //Quantity in warehouses
            "TYPE" => "PRODUCT.TYPE", //Product type
            'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL', //url mask
            "PRICE_7" => "base_price.PRICE", //base price
            "PRICE_8" => "prop_PRICE_8.PRICE", //Wholesale price
            'ART_NUMBER' => 'PROPS_335.VALUE', //SKU property with ID 335
            'AVAILABLE' => 'PRODUCT.AVAILABLE', //Availability
        ],
        'order' => ['RAND' => 'ASC'],
        //'group' => [ 'ID' ], //Grouping is not available with the specified sorting
        'limit' => 20,
        'runtime' => [
            //Get section element
            'LINK' => [
                'data_type' => \Bitrix\Iblock\SectionElementTable::class,
                'reference' => [
                    '=this.ID' => 'ref.IBLOCK_ELEMENT_ID',
                ],
                'join_type' => 'inner',
            ],
            //Get the parent section
            'PARENT' => [
                'data_type' => \Bitrix\Iblock\SectionTable::class,
                'reference' => [
                    '=this.LINK.IBLOCK_SECTION_ID' => 'ref.ID',
                ],
                'join_type' => 'inner',
            ],
            //We connect the product table
            'PRODUCT' => [
                'data_type' => \Bitrix\Catalog\ProductTable::class,
                'reference' => [
                    '=this.ID' => 'ref.ID',
                ],
                'join_type' => 'left'
            ],
            //Connecting the partition table
            'SECTION' => [
                'data_type' => \Bitrix\Iblock\SectionTable::class,
                'reference' => [
                    '=this.PARENT.IBLOCK_ID' => 'ref.IBLOCK_ID',
                    '<=ref.LEFT_MARGIN' => 'this.PARENT.LEFT_MARGIN',
                    '>=ref.RIGHT_MARGIN' => 'this.PARENT.RIGHT_MARGIN',
                ],
                'join_type' => 'inner',
            ],
            //Get the value of the base price with ID 7
            'base_price' => [
                'data_type' => '\Bitrix\Catalog\PriceTable',
                'reference' => [
                    '=this.ID' => 'ref.PRODUCT_ID'
                ],
                'join_type' => 'inner'
            ],
            //Get the value of the wholesale price with ID 8
            'prop_PRICE_8' => [
                'data_type' => '\Bitrix\Catalog\PriceTable',
                'reference' => [
                    '=this.ID' => 'ref.PRODUCT_ID'
                ],
                'join_type' => 'inner'
            ],
            //Get property value 335 (Article)
            'PROPS_335' => [
                'data_type' => $entityPropsSingle->getDataClass(),
                'reference' => [
                    '=this.ID' => 'ref.IBLOCK_ELEMENT_ID'
                ],
                'join_type' => 'inner'
            ],
            //We connect the table of warehouses
            /*'STORE' => [
                'data_type' => \Bitrix\Catalog\StoreProductTable::class,
                'reference' => [
                    '=this.ID' => 'ref.PRODUCT_ID'
                ],
                'join_type' => 'inner'
            ],
            //Sum the balances in all available warehouses
            'QUANTITY_STORE' => [
                'data_type' => 'integer',
                'expression' => ['sum(%s)', 'STORE.AMOUNT']
            ],*/
            //Define a custom sort order "Random"
            'RAND'=>array('data_type' => 'float', 'expression' => array('RAND()')),
        ],
        'cache' => array( // Request cache. Reset can be done by \Bitrix\Iblock\ElementTable::getEntity()->cleanCache();
            'ttl' => 86400, // Cache lifetime
            'cache_joins' => true // Whether to cache selections with JOIN
        ),
    ]);

    $elements = $elements->fetchAll();

    foreach ($elements as &$element){
        $element['DETAIL_PAGE_URL'] = \CIBlock::ReplaceDetailUrl($element['DETAIL_PAGE_URL'], $element, false, 'E');
        $element['HAVE_OFFERS'] = $element['TYPE'] != 1 ? true : false;
        $element['QUANTITY'] = $element['QUANTITY'] > $element['QUANTITY_STORE'] ? $element['QUANTITY'] : $element['QUANTITY_STORE'];

    }

For properties in a separate table, you can use another declaration entityPropsSingle

//Properties in a separate table
$entityPropsSingle = \Bitrix\Main\Entity\Base::compileEntity(
    'PROPS_SINGLE_IB29',
    [
        'IBLOCK_ELEMENT_ID' => ['data_type' => 'integer'],
        'PROPERTY_335' => ['data_type' => 'integer'],
    ],
    [
        'table_name' => 'b_iblock_element_prop_s29',
    ]
);

Where the property is stored in an infoblock with ID 29 and the property itself has ID 335

Check for detail page in header

Bad example as it adds an extra database query on each hit, use as a last resort

public static function checkIsDetail() {
	global $APPLICATION;

	$IBLOCK_ID = 24;

	if(!\CModule::IncludeModule("iblock")) return false;
	$url = $APPLICATION->GetCurPage();

	$code = array_pop(array_filter(explode( '/',  $url)));
	$rsSections = \CIBlockSection::GetList([], ['IBLOCK_ID' => $IBLOCK_ID, '=CODE' => $code]);

	return ($rsSections->Fetch() !== false);
}

$is_section_page = self::checkIsDetail();

if(\CSite::InDir('/catalog/') && $APPLICATION->GetCurPage() !== '/catalog/' && $is_section_page){
	//Detail page
}

Get all elements from a section and nested subsections

$SECTION_ID = 5;
$IBLOCK_ID = 8;

$section = \Bitrix\Iblock\SectionTable::getByPrimary($SECTION_ID, [
    'filter' => ['IBLOCK_ID' => $IBLOCK_ID],
    'select' => ['LEFT_MARGIN', 'RIGHT_MARGIN'],
])->fetch();

$arItems = \Bitrix\Iblock\ElementTable::getList([
    'select' => ['ID', 'NAME', 'IBLOCK_ID'],
    'filter' => [
        'IBLOCK_ID' => $IBLOCK_ID,
        '>=IBLOCK_SECTION.LEFT_MARGIN' => $section['LEFT_MARGIN'],
        '<=IBLOCK_SECTION.RIGHT_MARGIN' => $section['RIGHT_MARGIN'],
    ],
]);

or so

$SECTION_ID = 5;
$IBLOCK_ID = 8;

$SECTION_IDS = getSubsections($SECTION_ID, $IBLOCK_ID);

function getSubsections(int $SECTION_ID, int $IBLOCK_ID): array
{
    $SECTION_IDS = [];
    $connection = \Bitrix\Main\Application::getConnection();
    $sql = sprintf('SELECT cs.ID FROM %1$s AS ps
                        INNER JOIN %1$s AS cs 
                        ON ps.LEFT_MARGIN <= cs.LEFT_MARGIN AND ps.RIGHT_MARGIN >= cs.RIGHT_MARGIN AND ps.IBLOCK_ID = cs.IBLOCK_ID
                        WHERE ps.ID = %2$d AND ps.IBLOCK_ID = %3$d',
        \Bitrix\Iblock\SectionTable::getTableName(),
        $SECTION_ID,
        $IBLOCK_ID
    );
    $result = $connection->query($sql);
    while ($section = $result->fetch()) {
        $SECTION_IDS[] = $section['ID'];
    }
    return $SECTION_IDS;
}

$arItems = \Bitrix\Iblock\ElementTable::getList([
    'select' => ['ID', 'NAME', 'IBLOCK_ID'],
    'filter' => [
        'IBLOCK_ID' => $IBLOCK_ID,
        'IBLOCK_SECTION_ID' => $SECTION_IDS,
    ],
]);

 

Worth reading:

Comments

There are no comments yet, you can be the first to leave it

Leave a comment

The site uses a comment pre-moderation system, so your message will be published only after approval by the moderator

You are replying to a user's comment

Send

FEEDBACK

Email me

Are you developing a new service, making improvements to the existing one and want to be better than your competitors? You have come to the right place. I offer you a comprehensive studio-level website development. From me you can order design, layout, programming, development of non-traditional functionality, implementation of communication between CMS, CRM and Data Analitics, as well as everything else related to sites, except for promotion.

Contact, I will always advise on all questions and help you find the most effective solution for your business. I am engaged in the creation of sites in Novosibirsk and in other regions of Russia, I also work with the CIS countries. You will be satisfied with our cooperation

An error occurred while sending, please try again after a while
Message sent successfully

Phones

+7(993) 007-18-96

Email

info@tichiy.ru

Address

Россия, г. Москва

By submitting the form, you automatically confirm that you have read and accept the Privacy Policy site

Contact with me
Send message
By submitting the form, you automatically confirm that you have read and accept Privacy policy of site
Sending successful!
Thank you for contacting :) I will contact you as soon as possible
Sending failed
An error occurred while sending the request. Please wait and try again after a while or call my phone number