static function add(SaleBasketBase $basket, array $fields, array $context, array $options = [])
{
$result = new MainResult();
if (!isset($options['CHECK_CRAWLERS']) || $options['CHECK_CRAWLERS'] == 'Y')
{
$validBuyer = static::checkCurrentUser();
if (!$validBuyer->isSuccess())
{
$result->addErrors($validBuyer->getErrors());
return $result;
}
unset($validBuyer);
}
if (empty($fields['PRODUCT_ID']))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT')));
return $result;
}
$productId = (int)$fields['PRODUCT_ID'];
if ($productId <= 0)
{
$result->addError(new MainError(MainLocalizationLoc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT')));
return $result;
}
unset($fields['PRODUCT_ID']);
if (empty($fields['QUANTITY']))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_EMPTY_QUANTITY')));
return $result;
}
$quantity = (float)$fields['QUANTITY'];
if ($quantity <= 0)
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_EMPTY_QUANTITY')));
return $result;
}
if (self::$saleIncluded === null)
self::$saleIncluded = Loader::includeModule('sale');
if (!self::$saleIncluded)
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_SALE')));
return $result;
}
$module = 'catalog';
$presets = ['PRODUCT_ID' => $productId];
if (array_key_exists('MODULE', $fields))
{
$module = $fields['MODULE'];
unset($fields['MODULE']);
}
if (
$module === 'catalog'
&& !isset($fields['PRODUCT_PROVIDER_CLASS'])
)
{
$fields['PRODUCT_PROVIDER_CLASS'] = self::getDefaultProviderName();
}
$transferFields = [
'PRODUCT_PROVIDER_CLASS' => true,
'CALLBACK_FUNC' => true,
'PAY_CALLBACK_FUNC' => true,
'SUBSCRIBE' => true
];
$presets = array_merge($presets, array_intersect_key($fields, $transferFields));
$fields = array_diff_key($fields, $transferFields);
unset($transferFields);
$propertyList = (!empty($fields['PROPS']) && is_array($fields['PROPS']) ? $fields['PROPS'] : []);
if (array_key_exists('PROPS', $fields))
unset($fields['PROPS']);
if ($module == 'catalog')
{
$elementFilter = array(
'ID' => $productId,
'ACTIVE' => 'Y',
'ACTIVE_DATE' => 'Y',
'CHECK_PERMISSIONS' => 'N'
);
if (!empty($options['CHECK_PERMISSIONS']) && $options['CHECK_PERMISSIONS'] == "Y")
{
$elementFilter['CHECK_PERMISSIONS'] = 'Y';
$elementFilter['MIN_PERMISSION'] = 'R';
if (isset($context['USER_ID']))
$elementFilter['PERMISSIONS_BY'] = $context['USER_ID'];
}
$iterator = CIBlockElement::GetList(
array(),
$elementFilter,
false,
false,
array(
"ID",
"IBLOCK_ID",
"XML_ID",
"NAME",
"DETAIL_PAGE_URL",
)
);
if (!($elementFields = $iterator->GetNext()))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_IBLOCK_ELEMENT')));
return $result;
}
$iterator = CatalogProductTable::getList(array(
'select' => array(
'ID', 'TYPE', 'AVAILABLE', 'CAN_BUY_ZERO', 'QUANTITY_TRACE', 'QUANTITY',
'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH',
'MEASURE', 'BARCODE_MULTI'
),
'filter' => array('=ID' => $productId)
));
$productFields = $iterator->fetch();
unset($iterator);
if (empty($productFields))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT')));
return $result;
}
if (
($productFields['TYPE'] == CatalogProductTable::TYPE_SKU || $productFields['TYPE'] == CatalogProductTable::TYPE_EMPTY_SKU)
&& (string)MainConfigOption::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
)
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_CANNOT_ADD_SKU')));
return $result;
}
if ($productFields['AVAILABLE'] != CatalogProductTable::STATUS_YES)
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_PRODUCT_RUN_OUT')));
return $result;
}
if ($productFields['TYPE'] == CatalogProductTable::TYPE_OFFER)
{
$skuInfo = CCatalogSku::GetProductInfo($productId, $elementFields['IBLOCK_ID']);
if (empty($skuInfo))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_PRODUCT_BAD_TYPE')));
return $result;
}
else
{
$parentIterator = CIBlockElement::GetList(
array(),
array(
'ID' => $skuInfo['ID'],
'IBLOCK_ID' => $skuInfo['IBLOCK_ID'],
'ACTIVE' => 'Y',
'ACTIVE_DATE' => 'Y',
'CHECK_PERMISSIONS' => 'N'
),
false,
false,
array('ID', 'IBLOCK_ID', 'XML_ID')
);
$parent = $parentIterator->Fetch();
if (empty($parent))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT')));
return $result;
}
elseif (mb_strpos($elementFields["~XML_ID"], '#') === false)
{
$elementFields["~XML_ID"] = $parent['XML_ID'].'#'.$elementFields["~XML_ID"];
}
unset($parent, $parentIterator);
}
}
if ($productFields['TYPE'] == CatalogProductTable::TYPE_SET)
{
$allSets = CCatalogProductSet::getAllSetsByProduct($productId, CCatalogProductSet::TYPE_SET);
if (empty($allSets))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT_SET')));
return $result;
}
$set = current($allSets);
unset($allSets);
$itemIds = array();
foreach ($set['ITEMS'] as $item)
{
if ($item['ITEM_ID'] != $item['OWNER_ID'])
$itemIds[$item['ITEM_ID']] = $item['ITEM_ID'];
}
if (empty($itemIds))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT_SET')));
return $result;
}
$setFilter = array(
'ID' => $itemIds,
'ACTIVE' => 'Y',
'ACTIVE_DATE' => 'Y',
'CHECK_PERMISSIONS' => 'N'
);
if (!empty($options['CHECK_PERMISSIONS']) && $options['CHECK_PERMISSIONS'] == "Y")
{
$setFilter['CHECK_PERMISSIONS'] = 'Y';
$setFilter['MIN_PERMISSION'] = 'R';
if (isset($context['USER_ID']))
$setFilter['PERMISSIONS_BY'] = $context['USER_ID'];
}
$iterator = CIBlockElement::GetList(
array(),
$setFilter,
false,
false,
array('ID', 'IBLOCK_ID')
);
while ($row = $iterator->Fetch())
{
if (isset($itemIds[$row['ID']]))
unset($itemIds[$row['ID']]);
}
unset($row, $iterator);
if (!empty($itemIds))
{
$result->addError(new MainError(Loc::getMessage('BX_CATALOG_PRODUCT_BASKET_ERR_NO_PRODUCT_SET_ITEMS')));
return $result;
}
}
$propertyIndex = self::getPropertyIndex('CATALOG.XML_ID', $propertyList);
if (!isset($fields['CATALOG_XML_ID']) || $propertyIndex === null)
{
$iBlockXmlID = (string)CIBlock::GetArrayByID($elementFields['IBLOCK_ID'], 'XML_ID');
if ($iBlockXmlID !== '')
{
$fields['CATALOG_XML_ID'] = $iBlockXmlID;
$propertyData = array(
'NAME' => 'Catalog XML_ID',
'CODE' => 'CATALOG.XML_ID',
'VALUE' => $iBlockXmlID
);
if ($propertyIndex !== null)
$propertyList[$propertyIndex] = $propertyData;
else
$propertyList[] = $propertyData;
unset($propertyData);
}
unset($iBlockXmlID);
}
$propertyIndex = self::getPropertyIndex('PRODUCT.XML_ID', $propertyList);
if (!isset($fields['PRODUCT_XML_ID']) || $propertyIndex === null)
{
$fields['PRODUCT_XML_ID'] = $elementFields['~XML_ID'];
$propertyData = array(
'NAME' => 'Product XML_ID',
'CODE' => 'PRODUCT.XML_ID',
'VALUE' => $elementFields['~XML_ID']
);
if ($propertyIndex !== null)
$propertyList[$propertyIndex] = $propertyData;
else
$propertyList[] = $propertyData;
unset($propertyData);
}
unset($propertyIndex);
//TODO: change to d7 measure class
$productFields['MEASURE'] = (int)$productFields['MEASURE'];
$productFields['MEASURE_NAME'] = '';
$productFields['MEASURE_CODE'] = 0;
if ($productFields['MEASURE'] <= 0)
{
$measure = CCatalogMeasure::getDefaultMeasure(true, true);
$productFields['MEASURE_NAME'] = $measure['~SYMBOL_RUS'];
$productFields['MEASURE_CODE'] = $measure['CODE'];
unset($measure);
}
else
{
$measureIterator = CCatalogMeasure::getList(
[],
['ID' => $productFields['MEASURE']],
false,
false,
['ID', 'SYMBOL_RUS', 'CODE']
);
$measure = $measureIterator->Fetch();
unset($measureIterator);
if (!empty($measure))
{
$productFields['MEASURE_NAME'] = $measure['SYMBOL_RUS'];
$productFields['MEASURE_CODE'] = $measure['CODE'];
}
unset($measure);
}
if (isset($options['FILL_PRODUCT_PROPERTIES']) && $options['FILL_PRODUCT_PROPERTIES'] === 'Y')
{
if ($productFields['TYPE'] == CatalogProductTable::TYPE_OFFER)
{
self::fillOfferProperties($propertyList, $productId, $elementFields['IBLOCK_ID']);
}
}
$fields['TYPE'] = SaleInternalsCatalogProductTypeMapper::getType((int)$productFields['TYPE']);
$fields += [
'DETAIL_PAGE_URL' => $elementFields['~DETAIL_PAGE_URL'],
'BARCODE_MULTI' => $productFields['BARCODE_MULTI'],
'WEIGHT' => (float)$productFields['WEIGHT'],
'DIMENSIONS' => [
'WIDTH' => $productFields['WIDTH'],
'HEIGHT' => $productFields['HEIGHT'],
'LENGTH' => $productFields['LENGTH']
],
'MEASURE_ID' => $productFields['MEASURE'],
'MEASURE_NAME' => $productFields['MEASURE_NAME'],
'MEASURE_CODE' => $productFields['MEASURE_CODE']
];
unset($productFields);
}
if (static::isCompatibilityEventAvailable())
{
$eventFields = array_merge($presets, $fields);
$eventFields['MODULE'] = $module;
$eventFields['PROPS'] = $propertyList;
$eventResult = static::runCompatibilityEvent($eventFields);
if ($eventResult === false)
{
return $result;
}
foreach ($eventResult as $key => $value)
{
if (isset($presets[$key]))
{
if ($presets[$key] !== $value)
{
$presets[$key] = $value;
}
}
elseif (!isset($fields[$key]) || $fields[$key] !== $value)
{
$fields[$key] = $value;
}
}
unset($key, $value);
$propertyList = $eventResult['PROPS'];
unset($eventResult);
}
$basketItem = null;
// using merge by default
if (!isset($options['USE_MERGE']) || $options['USE_MERGE'] === 'Y')
{
$basketItem = $basket->getExistsItem($module, $productId, $propertyList);
}
if ($basketItem)
{
$fields['QUANTITY'] = $basketItem->isDelay() ? $quantity : $basketItem->getQuantity() + $quantity;
}
else
{
$fields['QUANTITY'] = $quantity;
$fields['DELAY'] ??= 'N';
$basketCode = !empty($fields['BASKET_CODE']) ? $fields['BASKET_CODE'] : null;
$basketItem = $basket->createItem($module, $productId, $basketCode);
}
if (!$basketItem)
{
throw new MainObjectNotFoundException('BasketItem');
}
/** @var SaleBasketPropertiesCollection $propertyCollection */
$propertyCollection = $basketItem->getPropertyCollection();
if ($propertyCollection)
{
$propertyCollection->redefine($propertyList);
}
$r = $basketItem->setFields($presets);
if (!$r->isSuccess())
{
$result->addErrors($r->getErrors());
return $result;
}
//If error happend while setting quantity field we will know the name of product.
if(!empty($elementFields['~NAME']))
{
$basketItem->setField('NAME', $elementFields['~NAME']);
}
$r = $basketItem->setField('QUANTITY', $fields['QUANTITY']);
if (!$r->isSuccess())
{
$result->addErrors($r->getErrors());
return $result;
}
unset($fields['QUANTITY']);
$settableFields = array_fill_keys($basketItem::getSettableFields(), true);
$basketFields = array_intersect_key($fields, $settableFields);
if (!empty($basketFields))
{
$r = $basketItem->setFields($basketFields);
if (!$r->isSuccess())
{
$result->addErrors($r->getErrors());
return $result;
}
}
$result->setData(['BASKET_ITEM' => $basketItem]);
return $result;
}