Обучаю битриксу программистов, интеграторов

Товар из комплектующих

В каталоге, надо было отображать, как товар с торг предложениями, а в 1С, хранятся, как комплектующие.
Битрикс вроде умеет работаеть с комплектующими, но они отображаются, как попало на сайте.
Задача:
цена и остатки товара, рассчитываются из комплектующих
на сайте для клиента, отображается, как товар с торг предложениями
в 1С загружается как заказ из комплектующих
комплектующие выгрузила в отдельный инфоблок
в офферсах проставила связь между комплектующими
Единственный момент, у меня по одной комплектующей в товаре, если надо несколько, например 3 болта, то надо допиливать

обработчики в init.php
расчет цены, расчет остатоков, при оформлении заказа создается копия заказа из комплектующих.

AddEventHandler("catalog", "OnStoreProductUpdate", "MyOnStoreProductAddUpdate");
AddEventHandler("catalog", "OnStoreProductAdd", "MyOnStoreProductAddUpdate");
AddEventHandler("catalog", "OnPriceUpdate", "MyOnPriceAddUpdate");
AddEventHandler("catalog", "OnPriceAdd", "MyOnPriceAddUpdate");
function MyOnPriceAddUpdate($id,$arFields){

    if(!empty($arFields["PRODUCT_ID"]) && !empty($arFields["CATALOG_GROUP_ID"]) && isset($arFields["PRICE"])){
        $arFilter = Array(
           "IBLOCK_ID"=>IBLOCK__OFFER_ID, 
           //"ACTIVE"=>"Y",
           "PROPERTY_IS_PARTS"=>5732,//товар из комплектующих равно "да"
           "PROPERTY_PARTS"=>$arFields["PRODUCT_ID"]
        );
        $arSelect = Array("ID");
        $res = CIBlockElement::GetList(Array("sort"=>"asc"), $arFilter, false, Array("nPageSize"=>500,"iNumPage"=>1), $arSelect); 

        $arProducts = array();
        $arPartsIds = array();
        $arProductIds = array();
        while($arItem = $res->GetNext()){
            $res1 = CIBlockElement::GetProperty(IBLOCK__OFFER_ID, $arItem["ID"], "sort", "asc", array("CODE" => "PARTS"));
            $arItem["VALUES"] = array();
            while ($arValue = $res1->Fetch()){
                $arItem["VALUES"][] = $arValue['VALUE'];
                $arPartsIds[] = $arValue['VALUE'];
            }
            $arProducts[$arItem["ID"]] = $arItem;
            $arProductIds[] = $arItem["ID"];
        }
        if(!empty($arPartsIds)){
            $arPartsIds = array_unique($arPartsIds);
            $dbPrices = CPrice::GetList(
                array(),
                array(
                        "PRODUCT_ID" => array_merge($arPartsIds,$arProductIds),
                        "CATALOG_GROUP_ID" => $arFields["CATALOG_GROUP_ID"]
                ),
                false,
                array("nTopCount"=>500),
                array("ID","PRODUCT_ID","PRICE")
            );
            $arPrices = array();
            while($arPrice = $dbPrices->Fetch()){
                $arPrices[$arPrice["PRODUCT_ID"]] = $arPrice;
            }
            // AddMessage2Log($arPrices);
            foreach ($arProducts as $productId => $arProduct) {
                $price = 0;
                foreach ($arProduct["VALUES"] as $partId) {
                    if(!empty($arPrices[$partId])){
                        $price = $price + $arPrices[$partId]["PRICE"];
                    }
                    else{
                        $price = 0;
                        if(!empty($arPrices[$productId]["ID"])){
                            CPrice::Delete($arPrices[$productId]["ID"]);
                        }
                        break;
                    }
                }
                if($price>0){
                   if(!empty($arPrices[$productId]["ID"])){
                        CPrice::Update($arPrices[$productId]["ID"],array("PRICE"=>$price));
                   } 
                   else{
                        CPrice::Add(array(                       
                            "PRODUCT_ID" => $productId,
                            "CATALOG_GROUP_ID" => $arFields["CATALOG_GROUP_ID"],
                            "PRICE" => $price,
                            "CURRENCY" => "RUB",
                            "QUANTITY_FROM" => false,
                            "QUANTITY_TO" => false
                        ));
                   }
                }
            }
        }

    }

}
function MyOnStoreProductAddUpdate($id,$arFields){
    // AddMessage2Log($id);
     
    if(!empty($arFields["PRODUCT_ID"]) && !empty($arFields["STORE_ID"]) && isset($arFields["AMOUNT"])){
        $arFilter = Array(
           "IBLOCK_ID"=>IBLOCK__OFFER_ID, 
           //"ACTIVE"=>"Y",
           "PROPERTY_IS_PARTS"=>5732,//товар из комплектующих равно "да"
           "PROPERTY_PARTS"=>$arFields["PRODUCT_ID"]
        );

        $arSelect = Array("ID");
        $arFields["AMOUNT"] = intval($arFields["AMOUNT"]);
        $res = CIBlockElement::GetList(Array("sort"=>"asc"), $arFilter, false, Array("nPageSize"=>500,"iNumPage"=>1), $arSelect);   
        if($arFields["AMOUNT"] == 0){ //если 0, то не проверяем другие запчасти, а ставим, что 0

            $arProductIds = array();
            while($arItem = $res->Fetch()){
                $arProductIds[$arItem["ID"]] = $arItem["ID"];
            }
            if(!empty($arProductIds)){
                $rsStore = CCatalogStoreProduct::GetList(array(), array('PRODUCT_ID' =>$arProductIds, 'STORE_ID' => $arFields["STORE_ID"]), false, false, array('ID',"PRODUCT_ID")); 
                while($arStore = $rsStore->Fetch()){   
                    CCatalogStoreProduct::Update($arStore["ID"], array("AMOUNT"=>0));
                    unset($arProductIds[$arStore["PRODUCT_ID"]]);                
                    // AddMessage2Log($arStore);
                } 
            }          

        } 
        else{
            //AddMessage2Log($arFields);
            $arProducts = array();
            $arPartsIds = array();
            $arProductIds = array();
            while($arItem = $res->Fetch()){
                $res1 = CIBlockElement::GetProperty(IBLOCK__OFFER_ID, $arItem["ID"], "sort", "asc", array("CODE" => "PARTS"));
                $arItem["VALUES"] = array();
                while ($arValue = $res1->Fetch()){
                    $arItem["VALUES"][] = $arValue['VALUE'];
                    $arPartsIds[] = $arValue['VALUE'];
                }
                $arProducts[$arItem["ID"]] = $arItem;
                $arProductIds[] = $arItem["ID"];
            }
           
            if(!empty($arProductIds) && !empty($arPartsIds)){
                $arPartsIds = array_unique($arPartsIds);
                //AddMessage2Log(array($arProducts,$arPartsIds));

                $rsStore = CCatalogStoreProduct::GetList(array(), array('PRODUCT_ID' =>array_merge($arPartsIds,$arProductIds), 'STORE_ID' => $arFields["STORE_ID"]), false, false, array("ID","PRODUCT_ID","AMOUNT")); 
                
                $arStoreInfo = array();
                while($arStore = $rsStore->Fetch()){   
                    $arStoreInfo[$arStore["PRODUCT_ID"]] = array(
                        "ID" => $arStore["ID"],
                        "AMOUNT"=>intval($arStore["AMOUNT"])
                    );
                }
                //AddMessage2Log($arStoreInfo);
                foreach ($arProducts as $productId => $arProduct) {
                    $min = 1000;
                    foreach ($arProduct["VALUES"] as $partId) {                        
                        if(!empty($arStoreInfo[$partId])){
                            $min = ($arStoreInfo[$partId]["AMOUNT"]<$min) ? $arStoreInfo[$partId]["AMOUNT"] : $min;
                        }
                        else{
                            $min = 0;
                        }
                        if($min == 0){                        
                            if(!empty($arStoreInfo[$productId])){
                                CCatalogStoreProduct::Update($arStoreInfo[$productId]["ID"], array("AMOUNT"=>0));
                            }
                            break;
                        }
                    } 
                   // AddMessage2Log($min);
                    if($min>0){
                        if(isset($arStoreInfo[$productId]["ID"])){
                            //AddMessage2Log('upd');
                            //AddMessage2Log(array($arStoreInfo[$productId]["ID"],array("AMOUNT"=>$min)));
                            if($min!=$arStoreInfo[$productId]["AMOUNT"]){
                                CCatalogStoreProduct::Update($arStoreInfo[$productId]["ID"], array("AMOUNT"=>$min));
                            }
                        }
                        else{
                           // AddMessage2Log('add');
                            
                            CCatalogStoreProduct::Add(array(
                                "PRODUCT_ID" => $productId,
                                "STORE_ID" => $arFields["STORE_ID"],
                                "AMOUNT" => $min,
                            ));
                        }
                    }                   
                }
            }
        }
    }
}
function  DeactivateProducts(&$arFields){
    if(($arFields["IBLOCK_ID"] == IBLOCK__OFFER_ID)&&($arFields["ACTIVE"]=="N")){ 
        $PROP_PRODUCT_FROM_PART = 2965;   //свойство товар из комплектующих   
        $PROP_VALUE_YES = 5733;//значение свойства "товар из комплектующих" = "да""
        
        if(isset($arFields["PROPERTY_VALUES"][$PROP_PRODUCT_FROM_PART])){           
            foreach ($arFields["PROPERTY_VALUES"][$PROP_PRODUCT_FROM_PART] as $propValue) {
                if(!empty($propValue["VALUE"])&&($propValue["VALUE"]==$PROP_VALUE_YES)){
                    $arFields["ACTIVE"] = "Y";
                    break;
                }
            }
        }
        elseif(!empty($arFields["ID"])){
            $arFilter = Array(
              "IBLOCK_ID"=>$arFields["IBLOCK_ID"], 
              "ID"=>$arFields["ID"],
              "PROPERTY_IS_PART"=>$PROP_VALUE_YES
            );
            $arSelect = Array("ID", "IBLOCK_ID");
            $res = CIBlockElement::GetList(Array("sort"=>"asc"), $arFilter, false, Array("nPageSize"=>50,"iNumPage"=>1), $arSelect);
            if($arItem = $res->Fetch()){
                $arFields["ACTIVE"] = "Y";
            }

        }        
    }

}
Main\EventManager::getInstance()->addEventHandler(
    'sale',
    'OnSaleOrderSaved',
    'MyOnSaleOrderSaved'
);

function GetBasketPropertyByCode($basketPropertyCollection, $code){
    $return = false;
    foreach ($basketPropertyCollection as $basketProperty){ 
        if($basketProperty->getField('CODE') == $code){
            $return = $basketProperty;
            break;
        } 
    }
    return $return;
}

function MyOnSaleOrderSaved(Main\Event $event){   
    $order = $event->getParameter("ENTITY");
    if ($event->getParameter("IS_NEW") && CModule::IncludeModule('iblock')){
       $basket = $order->getBasket();
       $arProductIds = array();
      
       $arProductIds = array();
       foreach ($basket as $item) {
            $arProductIds[] = $item->getProductId();
        }        
        $arFilter = Array(          
           "ID"=>$arProductIds,
           "PROPERTY_IS_PARTS"=>5732,//товар из комплектующих равно "да"
           "IBLOCK_ID"=>IBLOCK__OFFER_ID
        );
        $arSelect = Array("ID","IBLOCK_ID");
        $res = CIBlockElement::GetList(Array("sort"=>"asc"), $arFilter, false, Array("nPageSize"=>100,"iNumPage"=>1), $arSelect);
        $arProductsFromParts = array();
        while($arItem = $res->fetch()){
            $arProductsFromParts[$arItem["ID"]] = $arItem["ID"];            
            
        }        
        if(!empty( $arProductsFromParts) && CModule::IncludeModule('sale') ){
            $orderPropertyCollection = $order->getPropertyCollection();
            $orderProperty = $orderPropertyCollection->getItemByOrderPropertyCode("IS_PART");
            $orderProperty->setValue('Y');
            $order->setField('EXTERNAL_ORDER',"Y");
            $order->save();

            DiscountCouponsManager::init();
            $propertyCollection = $order->getPropertyCollection();

            $orderNew = \Bitrix\Sale\Order::create("s3", $order->getUserId(),$order->getCurrency());
            $fuser = Sale\Fuser::getIdByUserId($order->getUserId());
            $basketNew = \Bitrix\Sale\Basket::create("s3");
            $basketNew->setFUserId($fuser); //привязать к покупателю
            
            $orderNew->setPersonTypeId($order->getPersonTypeId());  
            
            foreach ($basket as $item) {
                $basketPropertyCollection = $item->getPropertyCollection(); // \Bitrix\Sale\BasketPropertyItem
                // $basketPropertyIsPart = GetBasketPropertyByCode($basketPropertyCollection,"IS_PART");
                if(isset($arProductsFromParts[$item->getProductId()])){
                    $res = CIBlockElement::GetProperty(IBLOCK__OFFER_ID, $item->getProductId(), "sort", "asc", array("CODE" => "PARTS"));
                    $arParts = [];
                    while ($arValue = $res->Fetch()){
                        $arParts[] = $arValue['VALUE'];
                    }
                    if(!empty($arParts)){
                        $arFilter = Array(
                           "ID"=>$arParts,                 
                        );
                        $arSelect = Array("ID", "NAME","XML_ID");
                        $res = CIBlockElement::GetList(Array("sort"=>"asc"), $arFilter, false, Array("nPageSize"=>100,"iNumPage"=>1), $arSelect);               
                        $arNames = [];
                        while($arItem = $res->GetNext()){
                            $arNames[$arItem["ID"]] = array("NAME"=>$arItem["NAME"],"XML_ID"=>$arItem["XML_ID"]);                
                        }
               
                        $dbProductPrice = CPrice::GetListEx(
                            array("ID"=>"ASC"),
                            array("PRODUCT_ID" => $arParts,"CATALOG_GROUP_ID"=>$item->getField("PRICE_TYPE_ID")),
                            false,
                            false,
                            array("PRODUCT_ID", "PRICE")
                        );
                        $arPrices = [];
                        while($arPrice = $dbProductPrice->Fetch()){
                            $arPrices[$arPrice["PRODUCT_ID"]] = $arPrice["PRICE"];
                        }
                       
                        foreach ($arParts as $productId) {
                            $itemNew = $basketNew->createItem('catalog', $productId); 
                            $itemNew->setFields(array(
                                'NAME'=> $arNames[$productId]["NAME"] ?? "",
                                'PRODUCT_XML_ID'=> $arNames[$productId]["XML_ID"] ?? "",
                                'QUANTITY' => $item->getQuantity(),
                                'CURRENCY' => $item->getCurrency(),
                                'PRICE_TYPE_ID'=>$item->getField("PRICE_TYPE_ID"),
                                'LID' => "s3",
                                'PRICE' => $arPrices[$productId]??1,
                                'CUSTOM_PRICE' => 'Y', 
                                'PRICE_TYPE_ID' => $item->getField('PRICE_TYPE_ID'),           
                            )); 
                        }
                    }
                }
                else{
                    $itemNew = $basketNew->createItem('catalog', $item->getProductId()); 
                    $itemNew->setFields(array(
                        'NAME'=> $item->getField("NAME"),
                        'PRODUCT_XML_ID'=>$item->getField("PRODUCT_XML_ID"),
                        'PRICE_TYPE_ID'=>$item->getField("PRICE_TYPE_ID"),
                        'QUANTITY' => $item->getQuantity(),
                        'CURRENCY' => $item->getCurrency(),
                        'LID' => "s3",
                        'PRICE' => $item->getPrice(),
                        'CUSTOM_PRICE' => 'Y',    
                        'PRICE_TYPE_ID' => $item->getField('PRICE_TYPE_ID'),     
                    )); 
                }
            }
            $basketNew->save();
            $orderNew->setBasket($basketNew);
            $orderNew->save();
            $orderPropertyCollection = $order->getPropertyCollection();
            $orderNewPropertyCollection = $orderNew->getPropertyCollection();

            foreach ($orderPropertyCollection as $orderProperty) {
                if($orderProperty->getField("CODE")!="IS_PART"){ 
                    foreach ($orderNewPropertyCollection as $orderNewProperty) {
                        if($orderNewProperty->getPropertyId() == $orderProperty->getPropertyId()){
                            $orderNewProperty->setValue($orderProperty->getValue());
                            break;      
                        }
                    }           
                }
            }
            
            //доставка
            $shipmentCollection = $orderNew->getShipmentCollection();
            $shipment = $shipmentCollection->createItem();  
            $service = Delivery\Services\Manager::getById($order->getField('DELIVERY_ID'));     
            $shipment->setFields(array(
                'DELIVERY_ID' => $service['ID'],
                'DELIVERY_NAME' => $service['NAME'],                              
            ));
            $shipmentItemCollection = $shipment->getShipmentItemCollection();
            foreach ($basketNew as $item){
                $shipmentItem = $shipmentItemCollection->createItem($item);
                $shipmentItem->setQuantity($item->getQuantity());
            }
            
            //оплата
            $paymentCollection = $orderNew->getPaymentCollection();
            $payment = $paymentCollection->createItem(\Bitrix\Sale\PaySystem\Manager::getObjectById($order->getField('PAY_SYSTEM_ID'))); //$paySystemId  - ИД платежной системы
            $payment->setField('SUM', $orderNew->getPrice());
            $order->doFinalAction(true);

            $result = $orderNew->save();
            if ($result->isSuccess()) {               
                CSaleOrder::Update($orderNew->getId(), array("PRICE_DELIVERY"=>$order->getDeliveryPrice()));
            } 
            else {
                AddMessage2Log($result->getError());
            }
        }

        
    }
}
Если блог был полезным, можете угостить меня "чашечкой кофе" :)

Сбер по номеру телефона +7 (953) 585-13-09 Вероника.
Спасибо!