Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/catalog/lib/product/ |
Upload File : |
<?php namespace Bitrix\Catalog\Product; use Bitrix\Catalog\Config\State; use Bitrix\Catalog\Product\Store\BatchManager; use Bitrix\Catalog\Product\Store\CostPriceCalculator; use Bitrix\Main; use Bitrix\Catalog; use Bitrix\Iblock; use Bitrix\Sale; use Bitrix\Sale\SaleProviderBase as Base; use Bitrix\Currency; use Bitrix\Catalog\Product\Store\DistributionStrategy; if (Main\Loader::includeModule('sale')) { /** * Class CatalogProvider * * @package Bitrix\Catalog\Product */ class CatalogProvider extends Base { private static $userCache = array(); protected static $hitCache = array(); protected static $priceTitleCache = array(); protected static $clearAutoCache = array(); protected $enableCache = true; protected const CACHE_USER_GROUPS = 'USER_GROUPS'; protected const CACHE_ITEM_WITHOUT_RIGHTS = 'IBLOCK_ELEMENT_PERM_N'; protected const CACHE_ITEM_RIGHTS = 'IBLOCK_ELEMENT'; protected const CACHE_ITEM_WITH_RIGHTS = 'IBLOCK_ELEMENT_PERM_Y'; protected const CACHE_ELEMENT_RIGHTS_MODE = 'ELEMENT_RIGHTS_MODE'; protected const CACHE_ELEMENT_SHORT_DATA = 'IBLOCK_ELEMENT_SHORT'; protected const CACHE_PRODUCT = 'CATALOG_PRODUCT'; protected const CACHE_VAT = 'VAT_INFO'; protected const CACHE_IBLOCK_RIGHTS = 'IBLOCK_RIGHTS'; protected const CACHE_STORE = 'CATALOG_STORE'; protected const CACHE_STORE_PRODUCT = 'CATALOG_STORE_PRODUCT'; protected const CACHE_PARENT_PRODUCT_ACTIVE = 'PARENT_PRODUCT_ACTIVE'; protected const CACHE_CATALOG_IBLOCK_LIST = 'CATALOG_IBLOCK_LIST'; protected const CACHE_PRODUCT_STORE_LIST = 'CACHE_PRODUCT_STORE_LIST'; protected const CACHE_PRODUCT_AVAILABLE_LIST = 'CACHE_PRODUCT_AVAILABLE_LIST'; protected const CATALOG_PROVIDER_EMPTY_STORE_ID = Base::EMPTY_STORE_ID; protected const BUNDLE_TYPE = 1; /** @deprecated */ protected const RESULT_PRODUCT_LIST = Base::SUMMMARY_PRODUCT_LIST; protected const RESULT_CATALOG_LIST = 'CATALOG_DATA_LIST'; protected const USE_GATALOG_DATA = 'CATALOG_DATA'; protected const AMOUNT_SRC_QUANTITY = 'QUANTITY'; protected const AMOUNT_SRC_QUANTITY_LIST = Base::FLAT_QUANTITY_LIST; protected const AMOUNT_SRC_PRICE_LIST = 'PRICE_LIST'; protected const AMOUNT_SRC_STORE_QUANTITY_LIST = Base::STORE_QUANTITY_LIST; protected const AMOUNT_SRC_RESERVED_LIST = Base::FLAT_RESERVED_QUANTITY_LIST; protected const AMOUNT_SRC_STORE_RESERVED_LIST = Base::STORE_RESERVED_QUANTITY_LIST; private const QUANTITY_FORMAT_STORE = 1; private const QUANTITY_FORMAT_SHIPMENT = 2; /** * @param array $products * * @return Sale\Result */ public function getProductData(array $products) { return $this->getData($products); } /** * @param array $products * * @return Sale\Result */ public function getCatalogData(array $products) { return $this->getData( $products, [self::USE_GATALOG_DATA] ); } /** * @param array $products * @param array $options * * @return Sale\Result */ private function getData(array $products, array $options = array()): Sale\Result { $context = $this->getContext(); $resultProductList = array_fill_keys(array_keys($products), false); $result = new Sale\Result(); $userId = (int)($context['USER_ID'] ?? 0); if ($userId < 0) { $userId = 0; } $siteId = $context['SITE_ID'] ?? false; $currency = $context['CURRENCY'] ?? false; $currency = (is_string($currency) ? Currency\CurrencyManager::checkCurrencyID($context['CURRENCY']) : false); if ($currency === false) { $currency = Sale\Internals\SiteCurrencyTable::getSiteCurrency($siteId ?: SITE_ID); } $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true); if (in_array('DISABLE_CACHE', $options)) { $this->enableCache = false; } $catalogDataEnabled = self::isCatalogDataEnabled($options); $outputVariable = static::getOutputVariable($options); $productGetIdList = array(); $correctProductIds = []; $iblockElementSelect = array('ID', 'IBLOCK_ID', 'IBLOCK_SECTION_ID', 'ACTIVE', 'ACTIVE_DATE', 'XML_ID'); if (!$catalogDataEnabled) { $iblockElementSelect = array_merge($iblockElementSelect, array('NAME', 'DETAIL_PAGE_URL')); } $resultList = array(); foreach ($products as $productId => $itemData) { $resultList[$productId] = false; if (!isset($itemData['ITEM_CODE'])) { $itemData['ITEM_CODE'] = $productId; $products[$productId]['ITEM_CODE'] = $productId; } if (!isset($itemData['BASKET_CODE'])) { $itemData['BASKET_CODE'] = $productId; $products[$productId]['BASKET_CODE'] = $productId; } $hash = $productId."|".$userId; $productCachedData = static::getHitCache(self::CACHE_ITEM_RIGHTS, $hash, $iblockElementSelect); if ($this->enableCache && !empty($productCachedData)) { $products[$productId]['PRODUCT_DATA'] = $productCachedData; $correctProductIds[$productId] = true; } else { $productGetIdList[] = $productId; } } if (!empty($productGetIdList)) { $productDataList = $this->getElements( $productGetIdList, $iblockElementSelect, ($adminSection ? $userId : null) ); foreach ($productDataList as $productId => $productData) { $products[$productId]['PRODUCT_DATA'] = $productData; $hash = $productId."|".$userId; static::setHitCache(self::CACHE_ITEM_RIGHTS, $hash, $productData); $correctProductIds[$productId] = true; } $products = array_intersect_key( $products, $correctProductIds ); if (empty($products)) { return static::getResultProvider($result, $outputVariable, $resultList); } } unset($correctProductIds); $iblockList = array(); $iblockDataList = array(); $iblockGetIdList = array(); foreach ($products as $productId => $productData) { $iblockId = $productData['PRODUCT_DATA']['IBLOCK_ID']; $iblockList[$iblockId][] = $productId; } foreach ($iblockList as $iblockId => $iblockProductIdList) { $iblockData = static::getHitCache(self::CACHE_CATALOG_IBLOCK_LIST, $iblockId); if ($this->enableCache && !empty($iblockData)) { $iblockDataList[$iblockId] = $iblockData; } else { $iblockGetIdList[] = $iblockId; } } if (!empty($iblockGetIdList)) { $iblockDataList = $this->getIblockData($iblockGetIdList) + $iblockDataList; } $iblockList = array_intersect_key( $iblockList, $iblockDataList ); $iblockProductMap = static::createIblockProductMap($iblockList, $iblockDataList); $correctProductList = static::checkSkuPermission($iblockProductMap); $products = array_intersect_key( $products, array_fill_keys($correctProductList, true) ); if (empty($products)) { return static::getResultProvider($result, $outputVariable, $resultList); } $products = static::changeSubscribeProductQuantity($products, $iblockProductMap); // catalog product $catalogSelect = array( 'ID', 'CAN_BUY_ZERO', 'QUANTITY_TRACE', 'QUANTITY', 'QUANTITY_RESERVED', 'MEASURE', 'TYPE', 'AVAILABLE', ); if (!$catalogDataEnabled) { $catalogSelect = array_merge($catalogSelect, array( 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH', 'BARCODE_MULTI', )); } $catalogSelect = array_merge($catalogSelect, Catalog\Product\SystemField::getProviderSelectFields()); $catalogProductDataList = static::getCatalogProducts(array_keys($products), $catalogSelect); $products = array_intersect_key($products, $catalogProductDataList); if (empty($products)) { return static::getResultProvider($result, $outputVariable, $resultList); } // fill catalog xml id $products = self::fillCatalogXmlId($products, $iblockProductMap); // prepare offers xml id $products = self::fillOfferXmlId($products, $catalogProductDataList); // get prices and discounts $priceDataList = self::getPriceDataList( $products, [ 'IS_ADMIN_SECTION' => $adminSection, 'USER_ID' => $userId, 'SITE_ID' => $siteId, 'CURRENCY' => $currency, ] ); $discountList = self::getDiscountList($priceDataList); $productQuantityList = array(); $productPriceList = array(); $fullQuantityMode = in_array('FULL_QUANTITY', $options); foreach ($products as $productId => $productData) { $catalogProductData = $catalogProductDataList[$productId]; $quantityList = array(); if (array_key_exists('QUANTITY', $productData)) { $quantityList = array($productData['BASKET_CODE'] => $productData['QUANTITY']); } if (!empty($productData[Base::FLAT_QUANTITY_LIST])) { $quantityList = $productData[Base::FLAT_QUANTITY_LIST]; } $productQuantityList[$productData['BASKET_CODE']]['QUANTITY_RESERVED'] = $catalogProductData['QUANTITY_RESERVED']; $baseCatalogQuantity = (float)$catalogProductData['QUANTITY']; $allCount = count($quantityList); $sumQuantity = 0; foreach ($quantityList as $quantity) { $sumQuantity += (float)abs($quantity); } $catalogQuantityForAvaialable = $baseCatalogQuantity; $checkCatalogQuantity = $baseCatalogQuantity; $isEnough = !($catalogProductData['CHECK_QUANTITY'] && $catalogQuantityForAvaialable < $sumQuantity); $setQuantity = $baseCatalogQuantity; foreach ($quantityList as $basketCode => $quantity) { $quantity = (float)abs($quantity); if (!$isEnough) { if ($catalogQuantityForAvaialable - $quantity < 0) { $quantity = $catalogQuantityForAvaialable; } $catalogQuantityForAvaialable -= $quantity; } $productQuantityList[$basketCode]['AVAILABLE_QUANTITY'] = ( $baseCatalogQuantity >= $quantity || !$catalogProductData['CHECK_QUANTITY'] ? $quantity : $baseCatalogQuantity ); if ($fullQuantityMode) { $checkCatalogQuantity -= $quantity; $setQuantity = $quantity; $allCount--; if ($allCount == 0) { $setQuantity = $checkCatalogQuantity + $quantity; } } else { if ($baseCatalogQuantity - $quantity > 0 || !$catalogProductData['CHECK_QUANTITY']) { $setQuantity = $quantity; } } $productQuantityList[$basketCode]['QUANTITY'] = $setQuantity; } unset($basketCode, $quantity); foreach (array_keys($quantityList) as $basketCode) { if (isset($priceDataList[$productId][$basketCode])) { $productPriceList[$basketCode] = $priceDataList[$productId][$basketCode]; } } unset($basketCode); $measure = isset($catalogProductData['MEASURE']) ? (int)$catalogProductData['MEASURE'] : null; $measureFields = static::getMeasure($measure); if (!empty($measureFields)) { $catalogProductDataList[$productId] = $measureFields + $catalogProductDataList[$productId]; } } unset($fullQuantityMode); $resultData = static::setCatalogDataToProducts($products, $catalogProductDataList, $options); $priceResultList = static::createProductPriceList($products, $productPriceList, $discountList); $resultList = static::createProductResult($products, $resultData, $priceResultList, $productQuantityList); $resultList = $resultList + $resultProductList; return static::getResultProvider($result, $outputVariable, $resultList); } private static function getOutputVariable(array $options = array()): string { return (self::isCatalogDataEnabled($options) ? static::RESULT_CATALOG_LIST : Base::SUMMMARY_PRODUCT_LIST ); } private static function getResultProvider(Sale\Result $result, $outputVariable, array $resultList = array()): Sale\Result { $result->setData( array( $outputVariable => $resultList, ) ); return $result; } /** * @param array $list * @param array $select * @param int|null $userId * * @return array */ private function getElements(array $list, array $select, ?int $userId = null): array { $filter = array( 'ID' => $list, 'ACTIVE_DATE' => 'Y', 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', ); if ($userId !== null) { $filter['PERMISSIONS_BY'] = $userId; } $resultList = array(); $dbIBlockElement = \CIBlockElement::GetList( array(), $filter, false, false, $select ); while ($productData = $dbIBlockElement->GetNext()) { $resultList[$productData['ID']] = $productData; } unset($dbIBlockElement); return $resultList; } /** * @param array $products * * @return Sale\Result */ public function getBundleItems(array $products) { $result = new Sale\Result(); $resultList = array(); $productIdList = array(); static $proxyCatalogProductSet = array(); static $proxyCatalogSkuData = array(); $bundleItemList = array(); $bundleIndex = array(); foreach ($products as $productId => $productData) { $proxyCatalogProductSetKey = $productId."|".static::BUNDLE_TYPE; if (!empty($proxyCatalogProductSet[$proxyCatalogProductSetKey]) && is_array($proxyCatalogProductSet[$proxyCatalogProductSetKey])) { $childItemList = $proxyCatalogProductSet[$proxyCatalogProductSetKey]; } else { $childItemList = \CCatalogProductSet::getAllSetsByProduct($productId, static::BUNDLE_TYPE); if (!empty($childItemList) && is_array($childItemList)) { $proxyCatalogProductSet[$proxyCatalogProductSetKey] = $childItemList; } } if (!empty($childItemList)) { $bundleItemList = $childItemList + $bundleItemList; $bundleItemListIds = array_keys($childItemList); $bundleItemId = reset($bundleItemListIds); unset($bundleItemListIds); $bundleIndex[$bundleItemId] = $productId; } } $childIdList = array(); if (empty($bundleItemList)) return $result; $bundleChildList = array(); $childProducts = array(); $productIndexList = array(); foreach ($bundleItemList as $parentItemid => $bundleItemData) { $productId = $bundleIndex[$parentItemid]; foreach ($bundleItemData["ITEMS"] as $childItemid => $item) { if (!isset($childIdList[$item['ITEM_ID']])) $childIdList[$item['ITEM_ID']] = true; $bundleChildList[$item['ITEM_ID']] = $item; $childProducts[$item['ITEM_ID']] = array( 'ITEM_CODE' => $item['ITEM_ID'], 'PRODUCT_ID' => $item['ITEM_ID'], Base::FLAT_QUANTITY_LIST => [$item['ITEM_ID'] => $item['QUANTITY']], 'BUNDLE_CHILD' => true, ); $productIndexList[$item['ITEM_ID']] = array( 'PRODUCT_ID' => $productId, 'PARENT_ID' => $parentItemid, 'CHILD_ID' => $childItemid, ); } } $r = $this->getProductData($childProducts); if ($r->isSuccess()) { $resultData = $r->getData(); if ( !empty($resultData[Base::SUMMMARY_PRODUCT_LIST]) && is_array($resultData[Base::SUMMMARY_PRODUCT_LIST]) ) { $resultDataList = $resultData[Base::SUMMMARY_PRODUCT_LIST]; foreach ($resultDataList as $itemCode => $itemData) { $item = $bundleChildList[$itemCode]; if (array_key_exists('QUANTITY_TRACE', $itemData)) unset($itemData['QUANTITY_TRACE']); $itemData["PRODUCT_ID"] = $item["ITEM_ID"]; $itemData["MODULE"] = 'catalog'; $itemData["PRODUCT_PROVIDER_CLASS"] = Basket::getDefaultProviderName(); $productIdList[] = $item["ITEM_ID"]; $itemData["PROPS"] = array(); if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && is_array($proxyCatalogSkuData[$item["ITEM_ID"]])) { $parentSkuData = $proxyCatalogSkuData[$item["ITEM_ID"]]; } else { $parentSkuData = \CCatalogSku::GetProductInfo($item["ITEM_ID"]); if ($parentSkuData) { $proxyCatalogSkuData[$item["ITEM_ID"]] = $parentSkuData; } } if (!empty($parentSkuData)) { $childDataList = array(); $childIdGetList = array(); $iblockPropertyDataList = array(); $iblockPropertyIdList = array(); $propsSku = array(); foreach ($childIdList as $childId => $parentValue) { $productData = static::getHitCache(self::CACHE_ELEMENT_SHORT_DATA, $item["ITEM_ID"]); if (!empty($productData)) { $childDataList[$childId] = $productData; if (!isset($iblockPropertyIdList[$productData['IBLOCK_ID']])) { $iblockPropertyIdList[$productData['IBLOCK_ID']] = true; } } else { $childIdGetList[] = $childId; } } if (!empty($childIdGetList)) { $iterator = Iblock\ElementTable::getList([ 'select' => [ 'ID', 'IBLOCK_ID', 'NAME', 'IBLOCK_SECTION_ID', ], 'filter' => \CIBlockElement::getPublicElementsOrmFilter(['@ID' => $childIdGetList]), ]); while ($productData = $iterator->fetch()) { static::setHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productData["ID"], $productData); $childDataList[$productData["ID"]] = $productData; if (!isset($iblockPropertyIdList[$productData['IBLOCK_ID']])) { $iblockPropertyIdList[$productData['IBLOCK_ID']] = true; } } } foreach ($iblockPropertyIdList as $iblockPropertyId => $iblockPropertyValue) { if ($propsSku = static::getHitCache('IBLOCK_PROPERTY', $iblockPropertyId)) { $iblockPropertyDataList[$iblockPropertyId] = $propsSku; } else { $dbOfferProperties = \CIBlock::GetProperties($iblockPropertyId, array(), array("!XML_ID" => "CML2_LINK")); while($offerProperties = $dbOfferProperties->Fetch()) { $propsSku[] = $offerProperties["CODE"]; } static::setHitCache('IBLOCK_PROPERTY', $iblockPropertyId, $propsSku); } } $propSkuHash = (!empty($propsSku)) ? md5(join('|', $propsSku)): md5($item["ITEM_ID"]); $proxyProductPropertyKey = $item["ITEM_ID"]."_".$parentSkuData["IBLOCK_ID"]."_".$propSkuHash; $productProperties = static::getHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey); if (empty($productProperties)) { $productProperties = \CIBlockPriceTools::GetOfferProperties( $item["ITEM_ID"], $parentSkuData["IBLOCK_ID"], $propsSku ); static::setHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey, $productProperties); } if (!empty($productProperties)) { foreach ($productProperties as $propData) { $itemData["PROPS"][] = array( "NAME" => $propData["NAME"], "CODE" => $propData["CODE"], "VALUE" => $propData["VALUE"], "SORT" => $propData["SORT"], ); } } } $parentProductIndexData = $productIndexList[$itemCode]; $priceData = array(); if (!empty($itemData['PRICE_LIST'])) { $priceData = reset($itemData['PRICE_LIST']); unset($itemData['PRICE_LIST']); } if (array_key_exists('PRODUCT', $itemData)) { unset($itemData['PRODUCT']); } $bundleItemList[$parentProductIndexData['PARENT_ID']]["ITEMS"][$parentProductIndexData['CHILD_ID']] = array_merge($item, $itemData, $priceData); } } } $elementList = static::getHitCache('IBLOCK_ELEMENT_LIST', $productId); if (empty($elementList)) { $productRes = \CIBlockElement::GetList( array(), array('ID' => $productIdList), false, false, array("ID", "IBLOCK_ID", "IBLOCK_SECTION_ID", "PREVIEW_PICTURE", "DETAIL_PICTURE", "IBLOCK_TYPE_ID", "XML_ID") ); while ($productData = $productRes->GetNext()) { $elementList[$productData['ID']] = $productData; } if (!empty($elementList) && is_array($elementList)) { static::setHitCache('IBLOCK_ELEMENT_LIST', $productId, $elementList); } } if (!empty($elementList) && is_array($elementList)) { foreach ($bundleItemList as $bundleParentId => $bundleItemData) { foreach ($bundleItemData["ITEMS"] as $bundleChildId => $item) { if (!$elementList[$item["ITEM_ID"]]) continue; $elementData = $elementList[$item["ITEM_ID"]]; $properties = array(); $strIBlockXmlID = (string)\CIBlock::GetArrayByID($elementData['IBLOCK_ID'], 'XML_ID'); if ($strIBlockXmlID != "") { $properties[] = array( "NAME" => "Catalog XML_ID", "CODE" => "CATALOG.XML_ID", "VALUE" => $strIBlockXmlID, ); $elementData['CATALOG_XML_ID'] = $strIBlockXmlID; } if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && strpos($elementData["XML_ID"], '#') === false) { $parentSkuData = $proxyCatalogSkuData[$item["ITEM_ID"]]; if (!empty($proxyParentData[$parentSkuData['ID']]) && is_array($proxyParentData[$parentSkuData['ID']])) { $parentData = $proxyParentData[$parentSkuData['ID']]; } else { $parentIterator = Iblock\ElementTable::getList( array( 'select' => array('ID', 'XML_ID'), 'filter' => array('ID' => $parentSkuData['ID']), ) ); $parentData = $parentIterator->fetch(); if (!empty($parentData)) { $proxyParentData[$parentSkuData['ID']] = $parentData; } unset($parentIterator); } $elementData["XML_ID"] = $parentData['XML_ID'].'#'.$elementData["XML_ID"]; unset($parentData); } $properties[] = array( "NAME" => "Product XML_ID", "CODE" => "PRODUCT.XML_ID", "VALUE" => $elementData["XML_ID"], ); $bundleItemData = $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId]; $bundleItemProps = array(); if (is_array($elementData["PROPS"]) && !empty($elementData["PROPS"])) { $bundleItemProps = $elementData["PROPS"]; } if (!empty($properties)) { $bundleItemProps = $bundleItemProps + $properties; } $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId] = $bundleItemData + array( 'IBLOCK_ID' => $elementData["IBLOCK_ID"], 'IBLOCK_SECTION_ID' => $elementData["IBLOCK_SECTION_ID"], 'PREVIEW_PICTURE' => $elementData["PREVIEW_PICTURE"], 'DETAIL_PICTURE' => $elementData["DETAIL_PICTURE"], 'CATALOG_XML_ID' => $elementData["CATALOG_XML_ID"], 'PRODUCT_XML_ID' => $elementData["XML_ID"], ); $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId]['PROPS'] = $bundleItemProps; } } } foreach(GetModuleEvents("sale", "OnGetSetItems", true) as $eventData) { ExecuteModuleEventEx($eventData, array(&$bundleItemList)); } if (!empty($bundleItemList)) { foreach ($bundleItemList as $bundleParentId => $bundleData) { if (empty($bundleIndex[$bundleParentId])) continue; $productId = $bundleIndex[$bundleParentId]; $resultList[$productId] = $bundleData; } $result->setData( array( 'BUNDLE_LIST' => $resultList, ) ); } return $result; } /** * @param $userId * * @return bool|array */ protected static function getUserGroups($userId) { $userId = (int)$userId; if ($userId < 0) return false; if (!isset(self::$userCache[$userId])) self::$userCache[$userId] = Main\UserTable::getUserGroupIds($userId); return self::$userCache[$userId]; } /** * @param array $products * * @return Sale\Result */ public function ship(array $products) { return $this->shipProducts($products); } /** * @param array $products * * @return Sale\Result */ public function unship(array $products) { $result = new Sale\Result(); $r = $this->tryUnship($products); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); return $result; } $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_SHIPPED'])) { $productsList = array(); foreach ($data['PRODUCTS_LIST_SHIPPED'] as $productId => $value) { if ($value && !empty($products[$productId])) { $productsList[$productId] = $products[$productId]; } } if (!empty($productsList)) { $this->shipProducts($productsList); } } return $result; } /** * @param array $products * * @return Sale\Result * @throws Main\ObjectNotFoundException */ public function deliver(array $products) { $result = new Sale\Result(); $resultList = array(); $productOrderList = static::createOrderListFromProducts($products); $deliverProductList = array(); foreach ($products as $productId => $productData) { $userId = null; $orderPaid = null; $orderId = null; if (isset($productData['USER_ID'])) { $userId = $productData['USER_ID']; } if (isset($productData['ORDER_ID'])) { $orderId = $productData['ORDER_ID']; } if (isset($productData['PAID'])) { $orderPaid = $productData['PAID']; } /** * @var int $orderId * @var Sale\Order $order */ if (isset($productOrderList[$productId])) { foreach ($productOrderList[$productId] as $orderId => $order) { if (!isset($resultList[$productId])) { $deliverProductList[] = array( 'PRODUCT_ID' => $productId, 'USER_ID' => $order->getUserId(), 'PAID' => $order->isPaid(), 'ORDER_ID' => $orderId, ); } } } else { if (isset($productData['USER_ID'])) { $userId = $productData['USER_ID']; } if (isset($productData['ORDER_ID'])) { $orderId = $productData['ORDER_ID']; } if (isset($productData['PAID'])) { $orderPaid = $productData['PAID']; } $deliverProductList[] = array( 'PRODUCT_ID' => $productId, 'USER_ID' => $userId, 'PAID' => $orderPaid, 'ORDER_ID' => $orderId, ); } } if (!empty($deliverProductList)) { foreach ($deliverProductList as $productData) { $productId = $productData['PRODUCT_ID']; $resultList[$productId] = \CatalogPayOrderCallback( $productId, $productData['USER_ID'], $productData['PAID'], $productData['ORDER_ID'] ); } } if (!empty($resultList)) { $result->setData( array( 'DELIVER_PRODUCTS_LIST' => $resultList, ) ); } return $result; } /** * @param array $products * * @return Sale\Result */ public function viewProduct(array $products) { $result = new Sale\Result(); $resultList = array(); foreach ($products as $productId => $itemData) { if (!isset($resultList[$productId])) { $context = $this->getContext(); $resultList[$productId] = \CatalogViewedProductCallback( $productId, $context['USER_ID'], $context['SITE_ID'] ); } } if (!empty($resultList)) { $result->setData( array( 'VIEW_PRODUCTS_LIST' => $resultList, ) ); } return $result; } /** * @param array $items * * @return Sale\Result */ public function recurring(array $items) { $result = new Sale\Result(); $resultList = array(); foreach ($items as $productId => $itemData) { if (!isset($resultList[$productId])) { $context = $this->getContext(); $resultList[$productId] = \CatalogRecurringCallback( $productId, $context['USER_ID'] ); } } if (!empty($resultList)) { $result->setData( array( 'RECURRING_PRODUCTS_LIST' => $resultList, ) ); } return $result; } /** * @param array $items * * @return Sale\Result */ public function checkBarcode(array $items) { $result = new Sale\Result(); $resultList = array(); foreach ($items as $barcodeParams) { $resultList[$barcodeParams['BARCODE']] = false; $dbres = \CCatalogStoreBarcode::GetList( array(), $barcodeParams ); $resultList[$barcodeParams['BARCODE']] = (bool)($dbres->GetNext()); } if (!empty($resultList)) { $result->setData( array( 'BARCODE_CHECK_LIST' => $resultList, ) ); } return $result; } /** * @param array $products * * @return Sale\Result */ protected function shipProducts(array $products) { $result = new Sale\Result(); $resultList = array_fill_keys(array_keys($products), false); $availableItems = $this->createProductsListWithCatalogData($products); $productStoreDataList = []; if (Catalog\Config\State::isUsedInventoryManagement()) { $r = $this->getProductListStores($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCT_STORES_LIST'])) { $productStoreDataList = $data['PRODUCT_STORES_LIST']; } unset($data); } unset($r); } foreach ($availableItems as $productId => $productData) { $r = static::shipProduct( $productData, (!empty($productStoreDataList[$productId]) ? $productStoreDataList[$productId] : [] ) ); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); $result->addWarnings($r->getErrors()); } $resultList[$productId] = $r->isSuccess(); } $result->setData([ 'SHIPPED_PRODUCTS_LIST' => $resultList, ]); return $result; } // private function /** * @param array $quantityList * * @return Sale\Result */ private static function updateCatalogStoreAmount(array $quantityList): Sale\Result { $result = new Sale\Result(); $resultList = array(); if (empty($quantityList)) { return $result; } foreach ($quantityList as $catalogStoreId => $amount) { $fields = [ 'AMOUNT' => $amount['AMOUNT'], ]; if (isset($amount['QUANTITY_RESERVED'])) { $fields['QUANTITY_RESERVED'] = $amount['QUANTITY_RESERVED']; } $internalResult = Catalog\StoreProductTable::update($catalogStoreId, $fields); $resultList[$catalogStoreId] = $internalResult->isSuccess(); } $result->setData( array( 'AMOUNT_UPDATED_LIST' => $resultList, ) ); return $result; } /** * @param array $productData * @param array $productStoreDataList * * @return Sale\Result */ private static function shipProduct(array $productData, array $productStoreDataList = array()): Sale\Result { $result = new Sale\Result(); $productId = $productData['PRODUCT_ID']; $productQuantity = self::getTotalAmountFromQuantityList($productData); $needShip = ($productQuantity < 0); if ( //Catalog\Config\State::isUsedInventoryManagement() $productData['PRODUCT']['USED_STORE_INVENTORY'] ) { if (empty($productStoreDataList) && $needShip) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_STORE_ERROR", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "DDCT_DEDUCTION_STORE_ERROR" ) ); return $result; } $setQuantityList = array(); $r = static::getSetableStoreQuantityProduct($productData, $productStoreDataList); if ($r->isSuccess()) { $resultData = $r->getData(); if (!empty($resultData[Base::FLAT_QUANTITY_LIST])) { $setQuantityList = $resultData[Base::FLAT_QUANTITY_LIST]; } } else { return $r; } /*if (!$productData['PRODUCT']['USED_STORE_INVENTORY']) // product types without stores { $setQuantityList = []; } */ $r = static::updateCatalogStoreAmount($setQuantityList); if ($r->isSuccess()) { $resultData = $r->getData(); if (!empty($resultData['AMOUNT_UPDATED_LIST'])) { foreach($resultData['AMOUNT_UPDATED_LIST'] as $catalogStoreIsUpdated) { if ($catalogStoreIsUpdated === true) { static::clearHitCache(self::CACHE_STORE_PRODUCT); if ($needShip) { $r = static::deleteBarcodes($productData); } else { $r = static::addBarcodes($productData); } if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } } } } else { return $r; } return static::shipQuantityWithStoreControl($productData); } elseif (isset($productData["CATALOG"])) { if ($productData["CATALOG"]["QUANTITY_TRACE"] == "N") { return $result; } } return static::shipQuantityWithoutStoreControl($productData); } /** * @param array $productData * * @return Sale\Result */ private static function shipQuantityWithStoreControl(array $productData): Sale\Result { $result = new Sale\Result(); $productId = (int)$productData['PRODUCT_ID']; $productQuantity = self::getTotalAmountFromQuantityList($productData); $catalogData = $productData['CATALOG']; $isExistsReserve = static::isExistsCommonStoreReserve($productData) && static::isReservationEnabled(); $isNeedShip = ($productQuantity < 0); $productQuantity = abs($productQuantity); $fields = array(); $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED']; $catalogQuantity = self::getTotalAmountFromPriceList($catalogData); $sumCatalogQuantity = $catalogReservedQuantity + $catalogQuantity; if ($isNeedShip) { if ($isExistsReserve) { if ($catalogReservedQuantity >= $productQuantity) { $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity - $productQuantity; } elseif ($sumCatalogQuantity >= $productQuantity) { $fields["QUANTITY_RESERVED"] = 0; $fields["QUANTITY"] = $catalogQuantity - ($productQuantity - $catalogReservedQuantity); } else { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT" ) ); return $result; } } else { if ($productQuantity <= $catalogQuantity) { $fields["QUANTITY"] = $catalogQuantity - $productQuantity; } elseif ($productQuantity <= $sumCatalogQuantity) { $fields["QUANTITY"] = 0; $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity - ($productQuantity - $catalogQuantity); } else { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT" ) ); return $result; } } } else { if ($isExistsReserve) { $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity; } else { $fields["QUANTITY"] = $catalogQuantity + $productQuantity; } } if (!$productData['PRODUCT']['USED_RESERVATION']) { if (isset($fields['QUANTITY_RESERVED'])) { unset($fields['QUANTITY_RESERVED']); } } $isUpdated = false; if (!empty($fields)) { $internalResult = Catalog\Model\Product::update($productId, $fields); if ($internalResult->isSuccess()) { $isUpdated = true; $quantityValues = array(); if (isset($fields['QUANTITY'])) { $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY']; QuantityControl::resetAvailableQuantity($productId); } if (isset($fields['QUANTITY_RESERVED'])) { $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED']; } if (!empty($quantityValues)) { QuantityControl::setValues($productId, $quantityValues); } } else { self::convertErrors($internalResult); } unset($internalResult); } $result->setData( array( 'IS_UPDATED' => $isUpdated, ) ); return $result; } /** * @param array $productData * * @return Sale\Result */ private static function shipQuantityWithoutStoreControl(array $productData): Sale\Result { $result = new Sale\Result(); $productId = (int)$productData['PRODUCT_ID']; $catalogData = $productData['CATALOG']; $productQuantity = self::getTotalAmountFromQuantityList($productData); $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED']; $catalogQuantity = self::getTotalAmountFromPriceList($catalogData); $fields = array(); $isExistsReserve = static::isExistsCommonStoreReserve($productData) && static::isReservationEnabled(); $isNeedShip = ($productQuantity < 0); if ($isNeedShip) { $productQuantity = abs($productQuantity); if (($productQuantity <= $catalogReservedQuantity + $catalogQuantity) || $catalogData["CAN_BUY_ZERO"] == "Y") { if ($isExistsReserve) { if ($productQuantity <= $catalogReservedQuantity) { $needReservedQuantity = $catalogReservedQuantity - $productQuantity; $fields["QUANTITY_RESERVED"] = $needReservedQuantity; } else { $fields["QUANTITY_RESERVED"] = 0; $fields["QUANTITY"] = $catalogQuantity - ($productQuantity - $catalogReservedQuantity); } } else { $fields["QUANTITY"] = $catalogQuantity - $productQuantity; } } else //not enough products - don't deduct anything { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_QUANTITY_ERROR", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "DDCT_DEDUCTION_QUANTITY_ERROR" ) ); } } else { if ($isExistsReserve) { $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity; } else { $fields["QUANTITY"] = $catalogQuantity + $productQuantity; } } if (!$productData['PRODUCT']['USED_RESERVATION']) { if (isset($fields['QUANTITY_RESERVED'])) { unset($fields['QUANTITY_RESERVED']); } } if (!empty($fields)) { $internalResult = Catalog\Model\Product::update($productId, $fields); if ($internalResult-> isSuccess()) { $quantityValues = array(); if (isset($fields['QUANTITY'])) { $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY']; QuantityControl::resetAvailableQuantity($productId); } if (isset($fields['QUANTITY_RESERVED'])) { $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED']; } if (!empty($quantityValues)) { QuantityControl::setValues($productId, $quantityValues); } } else { self::convertErrors($internalResult); } unset($internalResult); } return $result; } private static function isExistsCommonStoreReserve(array $productData): bool { if ( empty($productData['NEED_RESERVE_BY_STORE_LIST']) || !is_array($productData['NEED_RESERVE_BY_STORE_LIST']) ) { return false; } foreach ($productData['NEED_RESERVE_BY_STORE_LIST'] as $block) { if (empty($block) || !is_array($block)) { continue; } if (in_array(true, $block, true)) { return true; } } return false; } /** * @param array $productData * @param array $productStoreDataList * * @return Sale\Result */ private static function getSetableStoreQuantityProduct(array $productData, array $productStoreDataList): Sale\Result { $result = new Sale\Result(); $setQuantityList = array(); $productQuantity = self::getTotalAmountFromQuantityList($productData); $isNeedShip = ($productQuantity < 0); $quantityByStore = self::getQuantityDataFromStore($productData); $needQuantityList = $quantityByStore['AMOUNT']; if (empty($needQuantityList)) { $autoShipStore = static::getAutoShipStoreData($productData, $productStoreDataList); if (!empty($autoShipStore)) { $needQuantityList[$autoShipStore['STORE_ID']] = ($productQuantity > $autoShipStore['AMOUNT'] ? $autoShipStore['AMOUNT'] : abs($productQuantity)); $shipmentItemList = $productData['SHIPMENT_ITEM_LIST']; /** @var Sale\ShipmentItem $shipmentItem */ foreach ($shipmentItemList as $index => $shipmentItem) { $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection(); if ($shipmentItemStoreCollection && $shipmentItemStoreCollection->count() === 0) { $item = $shipmentItemStoreCollection->createItem($shipmentItem->getBasketItem()); $item->setField('STORE_ID', $autoShipStore['STORE_ID']); $item->setField('QUANTITY', abs($productData['SHIPMENT_ITEM_QUANTITY_LIST'][$index])); } } } } if (!empty($productStoreDataList)) { $isReservationEnabled = Main\Config\Option::get("sale", "product_reserve_condition") != "S"; $compileReserve = self::getCompileReserve($productData); foreach ($productStoreDataList as $storeId => $productStoreData) { $productId = $productStoreData['PRODUCT_ID']; if ($isNeedShip && (isset($needQuantityList[$storeId]) && $productStoreData['AMOUNT'] < $needQuantityList[$storeId])) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2', array_merge( self::getProductCatalogInfo($productId), [ '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($storeId), '#STORE_ID#' => $storeId, '#PRODUCT_ID#' => $productId, ] ) ), 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR' ) ); } else { $storeConfig = self::getUpdateStoreConfig( $storeId, $needQuantityList, $compileReserve, [ 'RESERVATION_ENABLED' => $isReservationEnabled, ] ); if (!$storeConfig['AMOUNT'] && !$storeConfig['QUANTITY_RESERVED']) { continue; } $storeUpdate = []; if ($storeConfig['AMOUNT']) { $setQuantity = $productQuantity; if (isset($needQuantityList[$storeId])) { $setQuantity = ($isNeedShip ? -1 : 1) * $needQuantityList[$storeId]; } $storeUpdate['AMOUNT'] = $productStoreData['AMOUNT'] + $setQuantity; $storeUpdate['DELTA'] = $setQuantity; $storeUpdate['OLD_AMOUNT'] = $productStoreData['AMOUNT']; unset($setQuantity); } if ($storeConfig['QUANTITY_RESERVED']) { $setReserveQuantity = 0; if (isset($needQuantityList[$storeId])) { $setReserveQuantity = ($isNeedShip ? -1 : 1) * $needQuantityList[$storeId]; } if (isset($quantityByStore['QUANTITY_RESERVED'][$storeId])) { $setReserveQuantity = ($isNeedShip ? -1 : 1) * $quantityByStore['QUANTITY_RESERVED'][$storeId]; } if ($setReserveQuantity != 0) { $storeUpdate['QUANTITY_RESERVED'] = $productStoreData['QUANTITY_RESERVED'] + $setReserveQuantity; $storeUpdate['OLD_QUANTITY_RESERVED'] = $productStoreData['QUANTITY_RESERVED']; $storeUpdate['QUANTITY_RESERVED_DELTA'] = $setReserveQuantity; } unset($setReserveQuantity); } if (!empty($storeUpdate)) { $setQuantityList[$productStoreData['ID']] = $storeUpdate; } unset($storeUpdate, $storeConfig); } } } if (!empty($setQuantityList)) { $result->addData( array( Base::FLAT_QUANTITY_LIST => $setQuantityList, ) ); } return $result; } private static function getUpdateStoreConfig(int $storeId, array $quantityList, array $reserveList, array $config): array { $result = [ 'AMOUNT' => isset($quantityList[$storeId]), 'QUANTITY_RESERVED' => false, ]; if ($config['RESERVATION_ENABLED']) { $result['QUANTITY_RESERVED'] = isset($reserveList[$storeId]); } return $result; } private static function getCompileReserve(array $product): array { if (empty($product['NEED_RESERVE_BY_STORE_LIST']) || !is_array($product['NEED_RESERVE_BY_STORE_LIST'])) { return []; } $result = []; foreach ($product['NEED_RESERVE_BY_STORE_LIST'] as $shipment) { if (empty($shipment) || !is_array($shipment)) { continue; } foreach ($shipment as $storeId => $flag) { if ($flag === true) { $result[$storeId] = true; } } } return $result; } private static function getQuantityDataFromStore(array $product): array { $result = [ 'AMOUNT' => [], 'QUANTITY_RESERVED' => [], ]; $storeDataExists = ( !empty($product['STORE_DATA_LIST']) && is_array($product['STORE_DATA_LIST']) ); $reserveDataExists = ( !empty($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]) && is_array($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]) ); if (!$storeDataExists && !$reserveDataExists) { return $result; } $found = false; if ($storeDataExists) { foreach ($product['STORE_DATA_LIST'] as $storeList) { if (!is_array($storeList)) { continue; } $found = true; foreach ($storeList as $storeId => $store) { if (!isset($result['AMOUNT'][$storeId])) { $result['AMOUNT'][$storeId] = 0; } $result['AMOUNT'][$storeId] += (float)$store['QUANTITY']; if (isset($store['RESERVED_QUANTITY'])) { if (!isset($result['QUANTITY_RESERVED'][$storeId])) { $result['QUANTITY_RESERVED'][$storeId] = 0; } $result['QUANTITY_RESERVED'][$storeId] += (float)$store['RESERVED_QUANTITY']; } } } } if (!$found && $reserveDataExists) { switch (self::getQuantityFormat($product[self::AMOUNT_SRC_STORE_RESERVED_LIST])) { case self::QUANTITY_FORMAT_STORE: $internalResult = self::calculateQuantityFromStores($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]); break; case self::QUANTITY_FORMAT_SHIPMENT: $internalResult = self::calculateQuantityFromShipments($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]); break; default: $internalResult = null; break; } if ($internalResult !== null) { $result['QUANTITY_RESERVED'] = $internalResult; } unset($internalResult); } return $result; } /** * @param array $productData * * @return Sale\Result */ private static function deleteBarcodes(array $productData): Sale\Result { $result = new Sale\Result(); $storeData = $productData['STORE_DATA_LIST']; if (!empty($storeData)) { foreach ($storeData as $storeDataList) { foreach($storeDataList as $storeDataValue) { $r = static::deleteBarcode($storeDataValue); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } } } return $result; } /** * @param array $storeData * * @return Sale\Result */ private static function deleteBarcode(array $storeData): Sale\Result { $result = new Sale\Result(); $storeId = $storeData["STORE_ID"]; $productId = $storeData["PRODUCT_ID"]; $barcodeMulti = $storeData['IS_BARCODE_MULTI']; $barcodeList = $storeData['BARCODE']; foreach ($barcodeList as $barcodeValue) { if (trim($barcodeValue) == "" || !$barcodeMulti) { continue; } $result = new Sale\Result(); $barcodeFields = array( "STORE_ID" => $storeId, "BARCODE" => $barcodeValue, "PRODUCT_ID" => $productId, ); $dbres = \CCatalogStoreBarcode::GetList( array(), $barcodeFields, false, false, array("ID", "STORE_ID", "BARCODE", "PRODUCT_ID") ); $catalogStoreBarcodeRes = $dbres->Fetch(); if ($catalogStoreBarcodeRes) { \CCatalogStoreBarcode::Delete($catalogStoreBarcodeRes["ID"]); } else { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_BARCODE_ERROR", array_merge( self::getProductCatalogInfo($productId), array("#BARCODE#" => $barcodeValue) ) ), "DDCT_DEDUCTION_BARCODE_ERROR" ) ); } } return $result; } /** * @param array $productData * * @return Sale\Result */ private static function addBarcodes(array $productData): Sale\Result { $result = new Sale\Result(); $storeData = $productData['STORE_DATA_LIST']; if (!empty($storeData)) { foreach ($storeData as $storeDataList) { foreach($storeDataList as $storeDataValue) { $r = static::addBarcode($storeDataValue); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } } } return $result; } /** * @param array $storeData * * @return Sale\Result */ private static function addBarcode(array $storeData): Sale\Result { $result = new Sale\Result(); $storeId = $storeData["STORE_ID"]; $productId = $storeData["PRODUCT_ID"]; $barcodeMulti = $storeData['IS_BARCODE_MULTI']; $barcodeList = $storeData['BARCODE']; foreach ($barcodeList as $barcodeValue) { if (trim($barcodeValue) == "" || !$barcodeMulti) { continue; } $result = new Sale\Result(); $barcodeFields = array( "STORE_ID" => $storeId, "BARCODE" => $barcodeValue, "PRODUCT_ID" => $productId, ); \CCatalogStoreBarcode::Add($barcodeFields); } return $result; } /** * @param array $products * * @return Sale\Result */ public function reserve(array $products) { $result = new Sale\Result(); $resultList = array(); $availableItems = $this->createProductsListWithCatalogData($products); foreach ($availableItems as $productId => $productData) { $resultList[$productId] = false; $r = static::reserveProduct($productData); if ($r->isSuccess()) { $resultData = $r->getData(); if (!empty($resultData)) { $resultList[$productId] = $resultData; } } else { $result->addErrors($r->getErrors()); } } $result->setData(array( 'RESERVED_PRODUCTS_LIST' => $resultList, )); return $result; } /** * @param array $productData * * @return Sale\Result */ private static function reserveProduct(array $productData): Sale\Result { if ( !static::isReservationEnabled() || !$productData['PRODUCT']['USED_RESERVATION'] ) { return static::reserveQuantityWithDisabledReservation($productData); } return self::reserveStoreQuantityWithEnabledReservation($productData); } /** * @param array $productData * * @return Sale\Result */ private static function reserveStoreQuantityWithEnabledReservation(array $productData): Sale\Result { $result = new Sale\Result(); $enableStoreControl = Catalog\Config\State::isUsedInventoryManagement(); $resultFields = []; $fields = []; // fields for update products $storeFields = []; // rows for update reserve in store $needShipList = []; $productId = $productData['PRODUCT_ID']; $storeProductQuantity = self::getStoreQuantityFromQuantityList($productData); // quantity with stores // empty store can't updated if used inventory managment if ($enableStoreControl && isset($storeProductQuantity[Base::EMPTY_STORE_ID])) { return $result; } $productQuantity = self::getTotalAmountFromQuantityList($productData); $isNeedReserve = ($productQuantity > 0); $catalogData = $productData['CATALOG']; $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED']; $catalogQuantity = self::getTotalAmountFromPriceList($catalogData); $sumCatalogQuantity = $catalogQuantity + $catalogReservedQuantity; if (isset($productData['NEED_SHIP'])) { $needShipList = $productData['NEED_SHIP']; } $setQuantityReserved = $catalogReservedQuantity; if (!empty($needShipList) && !empty($productData['SHIPMENT_ITEM_DATA_LIST'])) { $shipmentItemList = $productData['SHIPMENT_ITEM_DATA_LIST']; foreach ($needShipList as $shipmentItemIndex => $isNeedShip) { if ($setQuantityReserved <= 0) { $setQuantityReserved = 0; break; } if ($isNeedShip === true) { $shipmentItemQuantity = $shipmentItemList[$shipmentItemIndex]; $setQuantityReserved -= $shipmentItemQuantity; } } } if ($catalogData["QUANTITY_TRACE"] == "N") { $fields["QUANTITY_RESERVED"] = $setQuantityReserved; $resultFields['IS_UPDATED'] = true; $resultFields['QUANTITY_RESERVED'] = 0; } else { $resultFields['QUANTITY_RESERVED'] = $catalogReservedQuantity; if ( $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_PRODUCT || $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_OFFER ) { $storeFields = $enableStoreControl ? self::loadCurrentStoreReserve($productId, $storeProductQuantity) : []; } if ($isNeedReserve) { if ($catalogData["CAN_BUY_ZERO"] == "Y") { $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity; $fields['QUANTITY'] = $catalogQuantity - $productQuantity; } else { if ($catalogQuantity >= $productQuantity) { $fields["QUANTITY"] = $catalogQuantity - $productQuantity; $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity; } else { $resultFields["QUANTITY_NOT_RESERVED"] = $productQuantity - $catalogQuantity; $fields["QUANTITY"] = 0; $fields["QUANTITY_RESERVED"] = $sumCatalogQuantity; $result->addWarning( new Sale\ResultWarning( Main\Localization\Loc::getMessage( "RSRV_QUANTITY_NOT_ENOUGH_ERROR", self::getProductCatalogInfo($productId) ), "ERROR_NOT_ENOUGH_QUANTITY" ) ); } } } else //undo reservation { $correctReserve = 0; if ($enableStoreControl) { foreach (array_keys($storeFields) as $storeId) { if ($storeFields[$storeId]['ID'] === null) { continue; } $storeProductFields = $storeFields[$storeId]; $newReserve = $storeProductFields['QUANTITY_RESERVED'] + $storeProductFields['ADD_QUANTITY_RESERVED']; if ($newReserve < 0) { $correctReserve -= $newReserve; $storeFields[$storeId]['ADD_QUANTITY_RESERVED'] -= $newReserve; } } } $needQuantity = abs($productQuantity) - $correctReserve; $fields["QUANTITY"] = $catalogQuantity + $needQuantity; $needReservedQuantity = $catalogReservedQuantity - $needQuantity; if ($needQuantity > $catalogReservedQuantity) { $needReservedQuantity = $catalogReservedQuantity; } $fields["QUANTITY_RESERVED"] = $needReservedQuantity; if ($enableStoreControl) { foreach (array_keys($storeFields) as $storeId) { if ($storeFields[$storeId]['ID'] === null) { unset($storeFields[$storeId]); } } } } } //quantity trace if (!$productData['PRODUCT']['USED_RESERVATION']) { if (isset($fields['QUANTITY_RESERVED'])) { unset($fields['QUANTITY_RESERVED']); } } if (!empty($fields) && is_array($fields)) { $storeSuccess = true; if ($enableStoreControl) { foreach (array_keys($storeFields) as $index) { if ($index === Base::EMPTY_STORE_ID) { $storeSuccess = false; } else { $storeProductFields = $storeFields[$index]; $id = $storeProductFields['ID']; $storeProductFields['QUANTITY_RESERVED'] += $storeProductFields['ADD_QUANTITY_RESERVED']; unset($storeProductFields['ID'], $storeProductFields['ADD_QUANTITY_RESERVED']); if ($id === null) { $storeProductFields['AMOUNT'] = 0; $internalResult = Catalog\StoreProductTable::add($storeProductFields); } else { unset($storeProductFields['STORE_ID'], $storeProductFields['PRODUCT_ID']); $internalResult = Catalog\StoreProductTable::update($id, $storeProductFields); } if ($internalResult->isSuccess()) { $storeFields[$index]['ID'] = (int)$internalResult->getId(); } else { $storeFields[$index]['ERROR'] = true; $storeFields[$index]['ERROR_MESSAGES'] = $internalResult->getErrorMessages(); $storeSuccess = false; } } if (!$storeSuccess) { break; } } } if (!$storeSuccess) { return $result; } $resultFields['IS_UPDATED'] = false; $internalResult = Catalog\Model\Product::update($productId, $fields); if ($internalResult->isSuccess()) { $resultFields['IS_UPDATED'] = true; $quantityValues = array(); if (isset($fields['QUANTITY'])) { $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY']; QuantityControl::resetAvailableQuantity($productId); } if (isset($fields['QUANTITY_RESERVED'])) { $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED']; } if (!empty($quantityValues)) { QuantityControl::setValues($productId, $quantityValues); } } else { self::convertErrors($internalResult); } unset($internalResult); } if (isset($resultFields['IS_UPDATED'])) { if (isset($fields['QUANTITY_RESERVED'])) { $needReserved = $fields["QUANTITY_RESERVED"] - $resultFields['QUANTITY_RESERVED']; if ($resultFields['QUANTITY_RESERVED'] > $fields["QUANTITY_RESERVED"]) { $needReserved = $fields["QUANTITY_RESERVED"]; } $resultFields["QUANTITY_RESERVED"] = $needReserved; } if (!empty($resultFields)) { $result->setData($resultFields); } } return $result; } /** * @param array $productData * * @return Sale\Result */ private static function reserveQuantityWithDisabledReservation(array $productData): Sale\Result { $result = new Sale\Result(); $catalogData = $productData['CATALOG']; $isQuantityTrace = $catalogData["QUANTITY_TRACE"] == 'Y'; $productQuantity = self::getTotalAmountFromQuantityList($productData); $catalogQuantity = self::getTotalAmountFromPriceList($catalogData); $isUpdated = true; $fields = array( 'QUANTITY' => $catalogQuantity, ); if ($isQuantityTrace) { $productId = $productData['PRODUCT_ID']; $fields['QUANTITY'] -= $productQuantity; if ($catalogData["CAN_BUY_ZERO"] != "Y" && ($catalogQuantity < $productQuantity)) { $result->addWarning( new Sale\ResultWarning( Main\Localization\Loc::getMessage( "RESERVE_QUANTITY_NOT_ENOUGH_ERROR", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "RESERVE_QUANTITY_NOT_ENOUGH_ERROR" ) ); $fields['QUANTITY'] = 0; } $internalResult = Catalog\Model\Product::update($productId, $fields); if (!$internalResult->isSuccess()) { $isUpdated = false; self::convertErrors($internalResult); } unset($internalResult); } if ($isUpdated) { $result->setData($fields); } return $result; } /** * Checks offers parent products existence and activity. * * @param array $productIds * @param int $iblockId * * @return array */ private static function checkParentActivity(array $productIds, int $iblockId = 0): array { $resultList = array(); $productIdsToLoad = array(); foreach ($productIds as $productId) { $cacheKey = $productId.'|'.$iblockId; if (static::isExistsHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey)) { if (static::getHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey) === 'Y') { $resultList[] = $productId; } } else { $productIdsToLoad[] = $productId; } } if (!empty($productIdsToLoad)) { $productToOfferMap = array(); $parentIds = array(); $cacheResult = array_fill_keys($productIdsToLoad, 'N'); $productList = \CCatalogSku::getProductList($productIdsToLoad); if (!empty($productList)) { foreach ($productList as $offerId => $productInfo) { $productToOfferMap[$productInfo['ID']][] = $offerId; $parentIds[] = $productInfo['ID']; } $itemList = \CIBlockElement::GetList( array(), array( 'ID' => array_unique($parentIds), 'IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y', 'ACTIVE_DATE' => 'Y', 'CHECK_PERMISSIONS' => 'N', ), false, false, array('ID') ); while ($item = $itemList->Fetch()) { if (!empty($productToOfferMap[$item['ID']])) { foreach ($productToOfferMap[$item['ID']] as $productId) { $cacheResult[$productId] = 'Y'; $resultList[] = $productId; } } } } foreach ($cacheResult as $productId => $value) { static::setHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $productId.'|'.$iblockId, $value); } } return $resultList; } /** * @param $priceType * * @return string */ protected static function getPriceTitle($priceType) { $priceType = (int)$priceType; if ($priceType <= 0) return ''; if (!isset(self::$priceTitleCache[$priceType])) { self::$priceTitleCache[$priceType] = ''; $priceTypeList = Catalog\GroupTable::getTypeList(); if (isset($priceTypeList[$priceType])) { $groupName = (string)$priceTypeList[$priceType]['NAME_LANG']; self::$priceTitleCache[$priceType] = ($groupName != '' ? $groupName : $priceTypeList[$priceType]['NAME']); unset($groupName); } unset($priceTypeList); } return self::$priceTitleCache[$priceType]; } /** * @param array $products * * @return Sale\Result */ public function tryShip(array $products) { $result = new Sale\Result(); $resultList = array(); $filteredProducts = $this->createQuantityFilteredProducts($products); if (empty($filteredProducts)) { $result->setData( array( 'TRY_SHIP_PRODUCTS_LIST' => array_fill_keys(array_keys($products), true), ) ); return $result; } $availableItems = $this->createProductsListWithCatalogData($filteredProducts); if (empty($availableItems)) { $productIdList = array_keys($products); foreach($productIdList as $productId) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE" ) ); } $result->setData( array( 'TRY_SHIP_PRODUCTS_LIST' => array_fill_keys($productIdList, false), ) ); return $result; } else { foreach ($availableItems as $productId => $productData) { $messageId = null; if ( isset($productData['PRODUCT']['TYPE']) && $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_SERVICE ) { if ( (!isset($productData['CATALOG']['ACTIVE']) || $productData['CATALOG']['ACTIVE'] !== 'Y') || (!isset($productData['PRODUCT']['AVAILABLE']) || $productData['PRODUCT']['AVAILABLE'] !== 'Y') ) { $messageId = 'SALE_PROVIDER_PRODUCT_SERVICE_NOT_AVAILABLE'; } } else { if (!isset($productData['CATALOG']['ACTIVE']) || $productData['CATALOG']['ACTIVE'] !== 'Y') { $messageId = 'SALE_PROVIDER_PRODUCT_NOT_AVAILABLE'; } } if ($messageId !== null) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( $messageId, array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE" ) ); $resultList[$productId] = false; unset($availableItems[$productId]); } } } if (!empty($availableItems)) { if (Catalog\Config\State::isUsedInventoryManagement()) { $r = $this->checkProductsInStore($availableItems); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_IN_STORE'])) { $resultList = $resultList + $data['PRODUCTS_LIST_IN_STORE']; } } else { $result->addErrors($r->getErrors()); } } else { $r = $this->checkProductsQuantity($availableItems); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY'])) { $resultList = $resultList + $data['PRODUCTS_LIST_REQUIRED_QUANTITY']; } } else { $result->addErrors($r->getErrors()); } } } if (!empty($resultList)) { $result->setData( array( 'TRY_SHIP_PRODUCTS_LIST' => $resultList, ) ); } return $result; } /** * @param array $products * * @return Sale\Result */ public function isNeedShip(array $products) { $result = new Sale\Result(); $result->setData( array( 'IS_NEED_SHIP' => static::isReservationEnabled(), ) ); return $result; } /** * @param array $products * * @return array */ private function createQuantityFilteredProducts(array $products): array { $resultList = array(); foreach ($products as $productId => $productData) { $resultList[$productId] = $productData; if (array_key_exists('QUANTITY', $productData)) { if ($productData['QUANTITY'] > 0) { unset($resultList[$productId]); } else { $resultList[$productId] *= -1; } } elseif (!empty($productData[Base::FLAT_QUANTITY_LIST])) { foreach ($productData[Base::FLAT_QUANTITY_LIST] as $basketCode => $quantity) { if ($quantity > 0) { unset($resultList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode]); } else { $resultList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode] *= -1; } } if (empty($resultList[$productId][Base::FLAT_QUANTITY_LIST])) { unset($resultList[$productId]); } } } return $resultList; } /** * @param array $products * * @return Sale\Result */ public function tryUnship(array $products) { $result = new Sale\Result(); $resultList = array(); $availableItems = $this->createProductsListWithCatalogData($products); if (Catalog\Config\State::isUsedInventoryManagement()) { $r = $this->checkProductsInStore($availableItems); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_IN_STORE'])) { $resultList = $data['PRODUCTS_LIST_IN_STORE']; } } } else { $r = $this->checkProductsQuantity($availableItems); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY'])) { $resultList = $data['PRODUCTS_LIST_REQUIRED_QUANTITY']; } } else { return $r; } } if (!empty($resultList)) { $result->setData(array( 'PRODUCTS_LIST_SHIPPED' => $resultList, )); } return $result; } /** * @param array $products * * @return Sale\Result */ public function checkProductsInStore(array $products) { $result = new Sale\Result(); $resultList = array(); $r = $this->checkProductInStores($products); if (!$r->isSuccess()) { return $r; } $storeProductMap = $this->createStoreProductMap($products); $r = $this->checkExistsProductsInStore($products, $storeProductMap); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_EXISTS_IN_STORE'])) { $resultList = $data['PRODUCTS_LIST_EXISTS_IN_STORE']; } } else { return $r; } $r = $this->checkProductQuantityInStore($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE'])) { $resultList = $data['PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE']; } } else { $result->addErrors($r->getErrors()); } if (!empty($resultList)) { $result->addData( array( 'PRODUCTS_LIST_IN_STORE' => $resultList, ) ); } return $result; } /** * @param array $products * * @return Sale\Result */ private function checkProductsQuantity(array $products): Sale\Result { $result = new Sale\Result(); $resultList = array(); $availableQuantityList = array(); $r = $this->getAvailableQuantity($products); if ($r->isSuccess()) { $resultData = $r->getData(); if (!empty($resultData['AVAILABLE_QUANTITY_LIST'])) { $availableQuantityList = $resultData['AVAILABLE_QUANTITY_LIST']; } } else { return $r; } $enabledReservation = static::isReservationEnabled(); foreach ($products as $productId => $productData) { if (empty($productData['CATALOG'])) { $resultList[$productId] = false; $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE" ) ); continue; } $resultList[$productId] = true; $catalogData = $productData['CATALOG']; if ($catalogData["CHECK_QUANTITY"]) { $productQuantity = self::getTotalAmountFromQuantityList($productData); $availableQuantity = 0; if (isset($availableQuantityList[$productId])) { $availableQuantity = $availableQuantityList[$productId]; } $availableQuantity += (float)$catalogData['QUANTITY_RESERVED']; if ($enabledReservation && $productQuantity > $availableQuantity) { $resultList[$productId] = false; $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_QUANTITY_ERROR", array_merge( self::getProductCatalogInfo($productId), array("#PRODUCT_ID#" => $productId) ) ), "DDCT_DEDUCTION_QUANTITY_ERROR" ) ); } } } if (!empty($resultList)) { $result->setData( array( 'PRODUCTS_LIST_REQUIRED_QUANTITY' => $resultList, ) ); } return $result; } /** * @param array $products * * @return array */ private function createProductsListWithCatalogData(array $products): array { $productDataList = array(); $productIdList = array_fill_keys(array_keys($products), true); $r = $this->getData($products, [self::USE_GATALOG_DATA, 'FULL_QUANTITY']); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data[static::RESULT_CATALOG_LIST])) { $productDataList = $data[static::RESULT_CATALOG_LIST]; } } $resultList = array(); $availableListId = array_intersect_key($productIdList, $productDataList); if (!empty($availableListId)) { foreach (array_keys($availableListId) as $productId) { if (empty($productDataList[$productId]) || !is_array($productDataList[$productId])) { continue; } $resultList[$productId] = $products[$productId]; $resultList[$productId]['PRODUCT'] = $productDataList[$productId]['PRODUCT']; unset($productDataList[$productId]['PRODUCT']); $resultList[$productId]['CATALOG'] = $productDataList[$productId]; } } return $resultList; } /** * @param array $products * * @return array */ protected function createStoreProductMap(array $products) { $productStoreDataList = array(); $r = $this->getProductListStores($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCT_STORES_LIST'])) { $productStoreDataList = $data['PRODUCT_STORES_LIST']; } } $canAutoShipList = array(); $r = $this->canProductListAutoShip($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCT_CAN_AUTOSHIP_LIST'])) { $canAutoShipList = $data['PRODUCT_CAN_AUTOSHIP_LIST']; } } $storeProductList = array(); foreach ($products as $productId => $productData) { if (!empty($productData['STORE_DATA_LIST']) && static::isExistsBarcode($productData['STORE_DATA_LIST'])) { $storeProductList[$productId] = $productData['STORE_DATA_LIST']; } elseif (!empty($canAutoShipList[$productId]) && !empty($productStoreDataList[$productId])) { $productQuantity = self::getTotalAmountFromQuantityList($productData); foreach ($productData['SHIPMENT_ITEM_DATA_LIST'] as $shipmentItemIndex => $shipmentItemQuantity) { foreach ($productStoreDataList[$productId] as $productStoreData) { $storeId = $productStoreData['STORE_ID']; $storeProductList[$productId][$shipmentItemIndex][$storeId] = array( 'PRODUCT_ID' => $productId, 'STORE_ID' => $storeId, 'IS_BARCODE_MULTI' => false, 'QUANTITY' => abs($productQuantity), ); } } } } return $storeProductList; } private function checkProductInStores($products): Sale\Result { $result = new Sale\Result(); $productStoreDataList = array(); $canAutoShipList = array(); $r = $this->canProductListAutoShip($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCT_CAN_AUTOSHIP_LIST'])) { $canAutoShipList = $data['PRODUCT_CAN_AUTOSHIP_LIST']; } } $r = $this->getProductListStores($products); if ($r->isSuccess()) { $data = $r->getData(); if (!empty($data['PRODUCT_STORES_LIST'])) { $productStoreDataList = $data['PRODUCT_STORES_LIST']; } } foreach ($products as $productId => $productData) { if (!empty($productData['STORE_DATA_LIST'])) { if (!static::isExistsBarcode($productData['STORE_DATA_LIST'])) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", self::getProductCatalogInfo($productId) ), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY" ) ); } } elseif ($canAutoShipList[$productId] === false) { if (!isset($productStoreDataList[$productId])) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_STORE_EMPTY_ERROR", self::getProductCatalogInfo($productId) ), "DEDUCTION_STORE_ERROR1" ) ); } elseif (count($productStoreDataList[$productId]) > 1) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_STORE_ERROR", self::getProductCatalogInfo($productId) ), "DEDUCTION_STORE_ERROR1" ) ); } } } return $result; } private static function isExistsBarcode(array $list): bool { $resultValue = false; foreach ($list as $storeDataList) { foreach ($storeDataList as $storeValue) { if (is_array($storeValue['BARCODE']) && $storeValue['IS_BARCODE_MULTI'] === true) { foreach ($storeValue["BARCODE"] as $barcodeValue) { if (trim($barcodeValue) == "") { return $resultValue; } } $resultValue = true; } else { return (!empty($storeValue['BARCODE'])); } } } return $resultValue; } /** * @param array $products * * @return Sale\Result */ protected function checkProductQuantityInStore(array $products) { $result = new Sale\Result(); $resultList = array(); $productQuantityList = array(); $usedProductStoreQuantity = []; $productStoreDataList = self::loadCurrentProductStores(array_keys($products)); foreach ($products as $productId => $productData) { if (empty($productData['CATALOG'])) continue; if ( isset($productData['PRODUCT']) && !$productData['PRODUCT']['USED_STORE_INVENTORY'] //product types without stores ) { continue; } $productStoreData = $productStoreDataList[$productId] ?? []; $storeDataList = $productData['STORE_DATA_LIST']; if (!empty($storeDataList)) { foreach ($storeDataList as $barcodeList) { foreach($barcodeList as $storeId => $storeDataValue) { if (!empty($storeDataValue)) { if ( !isset($productStoreData[$storeId]) || ($productStoreData[$storeId]["AMOUNT"] < $storeDataValue["QUANTITY"]) ) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2', array_merge( self::getProductCatalogInfo($productId), [ '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($storeId), '#STORE_ID#' => $storeId, ] ) ), 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR' ) ); } else { if (!isset($productQuantityList[$productId])) { $productQuantityList[$productId] = 0; } $productQuantityList[$productId] += $storeDataValue["QUANTITY"]; if (!isset($usedProductStoreQuantity[$productId])) { $usedProductStoreQuantity[$productId] = []; } $usedProductStoreQuantity[$productId][$storeId] = true; $r = static::checkProductBarcodes($productData, $productStoreData[$storeId], $storeDataValue); if ($r->isSuccess()) { if (!array_key_exists($productId, $resultList)) { $resultList[$productId] = true; } } else { $result->addErrors($r->getErrors()); } } } else { if (!array_key_exists($productId, $resultList)) { $resultList[$productId] = true; } } } } } else { $resultList[$productId] = true; if (!isset($productQuantityList[$productId])) { $productQuantityList[$productId] = 0; } if ( !empty($productData[Base::FLAT_QUANTITY_LIST]) && is_array($productData[Base::FLAT_QUANTITY_LIST]) ) { $productQuantityList[$productId] = array_sum( $productData[Base::FLAT_QUANTITY_LIST] ); } } } if (!empty($productQuantityList)) { foreach ($productQuantityList as $amountProductId => $amountValue) { $product = $products[$amountProductId]; $catalogData = $product['CATALOG']; $catalogQuantity = self::getTotalAmountFromPriceList($catalogData); $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED']; if ($product[Base::FLAT_RESERVED_QUANTITY_LIST][$product['BASKET_CODE']] > 0) { $catalogQuantity += $catalogReservedQuantity; } else { $unusedReserve = 0.0; if (isset($usedProductStoreQuantity[$amountProductId])) { $usedProductStores = $usedProductStoreQuantity[$amountProductId]; $productStores = $productStoreDataList[$amountProductId]; foreach (array_keys($productStores) as $storeId) { if (isset($usedProductStores[$storeId])) { continue; } $unusedReserve += $productStores[$storeId]['QUANTITY_RESERVED']; } unset($storeId); unset($productStores, $usedProductStores); } $catalogQuantity += $unusedReserve; unset($unusedReserve); } if ($amountValue > $catalogQuantity) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_SHIPMENT_QUANTITY_NOT_ENOUGH", self::getProductCatalogInfo($amountProductId) ), "SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH" ) ); } } } if (!empty($resultList)) { $result->setData( array( 'PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE' => $resultList, ) ); } return $result; } /** * @param array * @param array $storeDataList * * @return Sale\Result */ protected function checkExistsProductItemInStore(array $productData, array $storeDataList = array()) { $result = new Sale\Result(); if (!empty($storeDataList)) { foreach ($storeDataList as $storeData) { foreach ($storeData as $storeDataValue) { $storeId = $storeDataValue['STORE_ID']; if ((int)$storeId < -1 || (int)$storeId == 0 || !isset($storeDataValue["QUANTITY"]) || (int)$storeDataValue["QUANTITY"] < 0) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_STORE_ERROR", self::getProductCatalogInfo($productData['PRODUCT_ID']) ), "DDCT_DEDUCTION_STORE_ERROR" ) ); return $result; } } } } else { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::getProductCatalogInfo($productData['PRODUCT_ID'])), "DEDUCTION_STORE_ERROR1" )); } return $result; } /** * @param array $products * @param array $storeData * * @return Sale\Result */ protected function checkExistsProductsInStore(array $products, array $storeData = array()) { $result = new Sale\Result(); $resultList = array(); if (!empty($storeData)) { foreach ($products as $productId => $productData) { $productStoreData = array(); if (!empty($storeData[$productId])) { $productStoreData = $storeData[$productId]; } $resultList[$productId] = true; if ( ( isset($productData['BUNDLE_PARENT']) && $productData['BUNDLE_PARENT'] === true ) || ( isset($productData['PRODUCT']['USED_STORE_INVENTORY']) && !$productData['PRODUCT']['USED_STORE_INVENTORY'] ) ) { continue; } $r = $this->checkExistsProductItemInStore($productData, $productStoreData); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); $resultList[$productId] = false; } } } if (!empty($resultList)) { $result->setData( array( 'PRODUCTS_LIST_EXISTS_IN_STORE' => $resultList, ) ); } return $result; } /** * @param array $productData * @param array $productStoreData * @param array $storeData * * @return Sale\Result */ protected static function checkProductBarcodes(array $productData, array $productStoreData, array $storeData = array()) { $result = new Sale\Result(); $productId = $productData['PRODUCT_ID']; $storeId = $productStoreData['STORE_ID']; if (isset($storeData['BARCODE']) && count($storeData['BARCODE']) > 0) { foreach ($storeData['BARCODE'] as $barcodeValue) { if (trim($barcodeValue) == "" && $storeData['IS_BARCODE_MULTI'] === true) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", array_merge( self::getProductCatalogInfo($productId), array("#STORE_ID#" => $storeId) ) ), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY" ) ); continue; } if (!empty($barcodeValue)) { $fields = [ '=STORE_ID' => static::CATALOG_PROVIDER_EMPTY_STORE_ID, '=BARCODE' => $barcodeValue, '=PRODUCT_ID' => $productId, ]; if ($storeData['IS_BARCODE_MULTI'] === true) { $fields['=STORE_ID'] = $storeId; } $iterator = Catalog\StoreBarcodeTable::getList([ 'select' => ['ID'], 'filter' => $fields, 'limit' => 1, ]); $row = $iterator->fetch(); unset($iterator); if (empty($row)) { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_BARCODE_ERROR", array_merge(self::getProductCatalogInfo($productId), array("#BARCODE#" => $barcodeValue)) ), "DDCT_DEDUCTION_BARCODE_ERROR" )); } } } } else { $result->addError( new Sale\ResultError( Main\Localization\Loc::getMessage( "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", array_merge(self::getProductCatalogInfo($productId), array("#STORE_ID#" => $storeId)) ), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY" )); } return $result; } /** * @param array $products * * @return Sale\Result * @throws Main\ArgumentNullException */ private function canProductListAutoShip(array $products): Sale\Result { $context = $this->getContext(); if (empty($context['SITE_ID'])) { throw new Main\ArgumentNullException('SITE_ID'); } static $canAutoList = array(); $resultList = array(); $hasNew = false; $countStores = 0; $countStoresResult = $this->getStoresCount(); if ($countStoresResult->isSuccess()) { $countStores = $countStoresResult->get('STORES_COUNT'); } $countStoresResult->getData(); $defaultDeductionStore = (int)Main\Config\Option::get("sale", "deduct_store_id", "", $context['SITE_ID']); $isDefaultStore = ($defaultDeductionStore > 0); foreach ($products as $productId => $productData) { if (isset($canAutoList[$productId])) { $resultList[$productId] = $canAutoList[$productId]; continue; } if (!$productData['PRODUCT']['USED_STORE_INVENTORY']) // product types without stores { $canAutoList[$productId] = true; $resultList[$productId] = true; continue; } $isOneStore = ($countStores == 1 || $countStores == -1); $isOnlyOneStore = ($isOneStore || $isDefaultStore); $isMulti = false; if (isset($productData['STORE_DATA_LIST'])) { $storeData = array(); $shipmentItemStoreData = reset($productData['STORE_DATA_LIST']); if (!empty($shipmentItemStoreData)) { $storeData = reset($shipmentItemStoreData); } if (!empty($storeData)) { $isMulti = isset($storeData['IS_BARCODE_MULTI']) && $storeData['IS_BARCODE_MULTI'] === true; } } elseif (isset($productData['IS_BARCODE_MULTI'])) { $isMulti = $productData['IS_BARCODE_MULTI'] === true; } $resultList[$productId] = ($isOnlyOneStore && !$isMulti); $hasNew = true; if ($isMulti) { $hasNew = false; } } if ($hasNew) { $productStoreList = []; $r = $this->getProductListStores($products); if ($r->isSuccess()) { $productStoreData = $r->getData(); if ( !empty($productStoreData['PRODUCT_STORES_LIST']) && is_array($productStoreData['PRODUCT_STORES_LIST']) ) { $productStoreList = $productStoreData['PRODUCT_STORES_LIST']; } } if (!empty($productStoreList)) { foreach ($products as $productId => $productData) { if (!empty($productStoreList[$productId])) { $countProductInStore = 0; foreach ($productStoreList[$productId] as $storeData) { if ((float)$storeData['AMOUNT'] > 0) { $countProductInStore++; } } $resultList[$productId] = ($countProductInStore == 1); $canAutoList[$productId] = $resultList[$productId]; } } } } $result = new Sale\Result(); if (!empty($resultList)) { $result->setData( array( 'PRODUCT_CAN_AUTOSHIP_LIST' => $resultList, ) ); } return $result; } /** * @param array $product * @param array $productStoreDataList * * @return bool|array */ private static function getAutoShipStoreData(array $product, array $productStoreDataList) { $isMulti = false; if (isset($product['STORE_DATA_LIST'])) { $storeData = []; $shipmentItemStoreData = reset($product['STORE_DATA_LIST']); if (!empty($shipmentItemStoreData)) { $storeData = reset($shipmentItemStoreData); } if (!empty($storeData)) { $isMulti = isset($storeData['IS_BARCODE_MULTI']) && $storeData['IS_BARCODE_MULTI'] === true; } } elseif (isset($product['IS_BARCODE_MULTI'])) { $isMulti = $product['IS_BARCODE_MULTI'] === true; } if ($isMulti) { return false; } $outputStoreData = false; if (!empty($productStoreDataList)) { $countProductInStore = 0; $storeProductData = false; foreach ($productStoreDataList as $storeData) { if ((float)$storeData['AMOUNT'] > 0) { $countProductInStore++; if (!$storeProductData) { $storeProductData = $storeData; } } } if ($countProductInStore == 1 && !empty($storeProductData)) { $outputStoreData = $storeProductData; } } return $outputStoreData; } /** * @param array $products * * @return Sale\Result */ protected function getCountProductsInStore(array $products) { $result = new Sale\Result(); $productStoreList = array(); $productStoreResult = $this->getProductListStores($products); if ($productStoreResult->isSuccess()) { $productStoreData = $productStoreResult->getData(); if (array_key_exists('PRODUCT_STORES_LIST', $productStoreData)) { $productStoreList = $productStoreData['PRODUCT_STORES_LIST']; } } if (empty($productStoreList)) { return $result; } $resultList = array(); foreach ($productStoreList as $productStoreDataList) { foreach ($productStoreDataList as $storeId =>$productStoreData) { $productId = $productStoreData['PRODUCT_ID']; if ($productStoreData['AMOUNT'] > 0) { if (!isset($resultList[$productId])) { $resultList[$productId] = []; } $resultList[$productId][$storeId] = $productStoreData['AMOUNT']; } } } if (!empty($resultList)) { $result->setData( array( 'RESULT_LIST' => $resultList, ) ); } return $result; } /** * @internal * @return Sale\Result */ public function getStoresCount() { $result = new Sale\Result(); $count = -1; if (Catalog\Config\State::isUsedInventoryManagement()) { $count = count($this->getStoreIds()); } $result->setData( array( 'STORES_COUNT' => $count, ) ); return $result; } /** * @return array */ private function getStoreIds(): array { $context = $this->getContext(); $filterId = [ 'ACTIVE' => 'Y', ]; if (isset($context['SITE_ID']) && $context['SITE_ID'] !== '') { $filterId['+SITE_ID'] = $context['SITE_ID']; } $cacheId = md5(serialize($filterId)); $storeIds = static::getHitCache(self::CACHE_STORE, $cacheId); if (empty($storeIds)) { $storeIds = []; $filter = Main\Entity\Query::filter(); $filter->where('ACTIVE', '=', 'Y'); if (isset($context['SITE_ID']) && $context['SITE_ID'] != '') { $subFilter = Main\Entity\Query::filter(); $subFilter->logic('or')->where('SITE_ID', '=', $context['SITE_ID'])->where('SITE_ID', '=', '')->whereNull('SITE_ID'); $filter->where($subFilter); unset($subFilter); } $iterator = Catalog\StoreTable::getList([ 'select' => ['ID', 'SORT'], 'filter' => $filter, 'order' => ['SORT' => 'ASC', 'ID' => 'ASC'], ]); while ($row = $iterator->fetch()) { $storeIds[] = (int)$row['ID']; } unset($row, $iterator, $filter); if (!empty($storeIds)) { static::setHitCache(self::CACHE_STORE, $cacheId, $storeIds); } } unset($cacheId, $filterId); return $storeIds; } /** * @param array $products * * @return Sale\Result */ public function getProductListStores(array $products) { $result = new Sale\Result(); //without store control stores are used for information purposes only if (!Catalog\Config\State::isUsedInventoryManagement()) { return $result; } $storeIds = $this->getStoreIds(); if (empty($storeIds)) { return $result; } $resultList = []; $productGetIdList = []; foreach (array_keys($products) as $productId) { /* $cacheId = md5($productId); $storeProductDataList = static::getHitCache(self::CACHE_STORE_PRODUCT, $cacheId); if (!empty($storeProductDataList)) { $resultList[$productId] = $storeProductDataList; } else { $productGetIdList[$productId] = $productId; } */ // remove cache because need clear cache after modify stores if ( isset($products[$productId]['PRODUCT']['USED_STORE_INVENTORY']) && !$products[$productId]['PRODUCT']['USED_STORE_INVENTORY'] ) // product types without stores { continue; } $productGetIdList[$productId] = $productId; } if (!empty($productGetIdList)) { $emptyProductStores = []; $iterator = Catalog\StoreTable::getList([ 'select' => [ 'ID', 'TITLE', ], 'filter' => [ '@ID' => $storeIds, ], 'order' => [ 'ID' => 'ASC', ], ]); while ($row = $iterator->fetch()) { $id = (int)$row['ID']; $emptyProductStores[$id] = [ 'ID' => 0, 'PRODUCT_ID' => 0, 'STORE_ID' => $row['ID'], 'AMOUNT' => 0, 'QUANTITY_RESERVED' => 0, 'STORE_NAME' => $row['TITLE'], ]; } unset($row, $iterator); foreach (array_chunk($productGetIdList, 500) as $pageIds) { foreach ($pageIds as $productId) { $rows = $emptyProductStores; foreach (array_keys($rows) as $storeId) { $rows[$storeId]['PRODUCT_ID'] = $productId; } $resultList[$productId] = $rows; unset($rows); } unset($productId); $iterator = Catalog\StoreProductTable::getList([ 'select' => [ 'ID', 'PRODUCT_ID', 'STORE_ID', 'AMOUNT', 'QUANTITY_RESERVED', ], 'filter' => [ '=PRODUCT_ID' => $pageIds, '@STORE_ID' => $storeIds, ], 'order' => [ 'PRODUCT_ID' => 'ASC', 'STORE_ID' => 'ASC', ], ]); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $row['PRODUCT_ID'] = (int)$row['PRODUCT_ID']; $row['STORE_ID'] = (int)$row['STORE_ID']; if (!isset($resultList[$row['PRODUCT_ID']])) { $resultList[$row['PRODUCT_ID']] = []; } $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['ID'] = $row['ID']; $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['AMOUNT'] = (float)$row['AMOUNT']; $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED']; } unset($iterator, $row); } unset($pageIds); /* foreach ($productGetIdList as $productId) { if (!empty($resultList[$productId])) { $cacheId = md5($productId); static::setHitCache(self::CACHE_STORE_PRODUCT, $cacheId, $resultList[$productId]); } } */ } if (!empty($resultList)) { $result->setData([ 'PRODUCT_STORES_LIST' => $resultList, ]); } return $result; } /** * @param $type * @param $key * @param array $fields * * @return bool|mixed */ protected static function getHitCache($type, $key, array $fields = array()) { if (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key])) { if (static::isExistsHitCache($type, $key, $fields)) { return self::$hitCache[$type][$key]; } } return false; } /** * @param $type * @param $key * @param array $fields * * @return bool */ protected static function isExistsHitCache($type, $key, array $fields = []): bool { $isExists = false; if (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key])) { $isExists = true; if (!empty($fields) && is_array($fields) && is_array(self::$hitCache[$type][$key])) { foreach ($fields as $name) { if (!array_key_exists($name, self::$hitCache[$type][$key])) { $isExists = false; break; } } } } return $isExists; } /** * @param string $type * @param string|int $key * @param mixed $value */ protected static function setHitCache(string $type, $key, $value): void { if (!isset(self::$hitCache[$type])) { self::$hitCache[$type] = []; } if (!isset(self::$hitCache[$type][$key])) { self::$hitCache[$type][$key] = []; } self::$hitCache[$type][$key] = $value; } /** * @param string|null $type */ protected static function clearHitCache(?string $type = null): void { if ($type === null) { self::$hitCache = []; } elseif (isset(self::$hitCache[$type])) { unset(self::$hitCache[$type]); } } /** * @param $fields * * @return array */ protected static function clearNotCacheFields($fields) { $resultFields = array(); $clearFields = static::getNotCacheFields(); foreach ($fields as $name => $value) { $clearName = $name; if (mb_substr($clearName, 0, 1) == '~') { $clearName = mb_substr($clearName, 1, mb_strlen($clearName)); } if (!in_array($clearName, $clearFields)) { $resultFields[$name] = $value; } } return $resultFields; } /** * @return array */ protected static function getNotCacheFields() { return array( 'CAN_BUY_ZERO', 'QUANTITY_TRACE', 'QUANTITY', 'CAN_BUY', ); } protected static function checkNeedFields(array $fields, array $need) { foreach ($need as $name => $value) { if (!array_key_exists($name, $fields)) { return false; } } return true; } /** * @deprecated deprecated since 21.700.0 * * @param $currentQuantity * @param $newQuantity * @param $quantityTrace * @param $canBuyZero * @param float|int $ratio * @return bool * @noinspection PhpUnusedParameterInspection */ protected static function isNeedClearPublicCache($currentQuantity, $newQuantity, $quantityTrace, $canBuyZero, $ratio = 1): bool { return false; } /** * @deprecated deprecated since 21.700.0 * * @param $productID * @param array|false $productInfo * @return void */ protected static function clearPublicCache($productID, $productInfo = array()): void {} /** * @param array $products * * @return Sale\Result */ public function getAvailableQuantity(array $products) { $result = $this->getAvailableQuantityByStore($products); if (!$result->isSuccess()) { return $result; } $data = $result->getData(); if (empty($data[Base::STORE_AVAILABLE_QUANTITY_LIST])) { return $result; } $reservedList = $data[Base::STORE_AVAILABLE_QUANTITY_LIST]; $resultList = []; foreach ($reservedList as $productId => $rows) { $resultList[$productId] = reset($rows); } unset($productId, $rows); unset($reservedList, $data); $result->setData([ Base::FLAT_AVAILABLE_QUANTITY_LIST => $resultList, ]); return $result; } public function getAvailableQuantityByStore(array $products): Sale\Result { $result = new Sale\Result(); $resultList = []; $isGotQuantityDataList = []; foreach ($products as $productId => $productData) { $catalogAvailableQuantity = QuantityControl::getAvailableQuantity($productId); $catalogQuantity = QuantityControl::getQuantity($productId); if ($catalogQuantity === null || $catalogAvailableQuantity === null) { continue; } $productQuantity = self::getStoreAmountFromQuantityList($productData); if ($productQuantity === null) { continue; } if ($catalogAvailableQuantity < array_sum($productQuantity)) { continue; } $isGotQuantityDataList[$productId] = true; $resultList[$productId] = $productQuantity; } if (count($resultList) != count($products)) { if ($this->isExistsCatalogData($products)) { $items = $products; } else { $items = $this->createProductsListWithCatalogData($products); } foreach ($items as $productId => $productData) { if (isset($isGotQuantityDataList[$productId])) { continue; } if (empty($productData['CATALOG']) || !is_array($productData['CATALOG'])) { continue; } $catalogData = $productData['CATALOG']; $productQuantity = self::getStoreAmountFromQuantityList($productData); if ($productQuantity === null) { $resultList[$productId] = []; continue; } $resultList[$productId] = $productQuantity; $catalogQuantity = self::getTotalAmountFromPriceList($catalogData, false); QuantityControl::setQuantity($productId, $catalogQuantity); if ($catalogData['CHECK_QUANTITY']) { $totalReservedQuantity = 0; $reservedQuantity = self::getStoreReservedQuantityFromProduct($productData); if ($reservedQuantity !== null) { $totalReservedQuantity = array_sum($reservedQuantity); } $needQuantity = (array_sum($productQuantity) - $totalReservedQuantity); if ($catalogQuantity < $needQuantity) { $limitQuantity = $catalogQuantity; $availableList = $resultList[$productId]; arsort($availableList, SORT_NUMERIC); foreach (array_keys($availableList) as $storeId) { $storeQuantity = $resultList[$productId][$storeId]; if ($limitQuantity > $storeQuantity) { $limitQuantity -= $storeQuantity; } else { $storeQuantity = $limitQuantity; if ($limitQuantity > 0) { $limitQuantity = 0; } } $resultList[$productId][$storeId] = $storeQuantity; } } } } } if (!empty($resultList)) { $result->setData([ Base::STORE_AVAILABLE_QUANTITY_LIST => $resultList, ]); } return $result; } /** * @param array $products * * @return Sale\Result */ public function getAvailableQuantityAndPrice(array $products) { $result = new Sale\Result(); $availableQuantityListResult = $this->getAvailableQuantity($products); if ($this->isExistsCatalogData($products)) { $items = $products; } else { $items = $this->createProductsListWithCatalogData($products); } $priceDataList = array(); foreach ($items as $productId => $productData) { $catalogData = $productData['CATALOG']; if (!empty($catalogData['PRICE_LIST'])) { $priceDataList[$productId] = $catalogData['PRICE_LIST']; } } $availableQuantityData = array(); if ($availableQuantityListResult->isSuccess()) { $availableQuantityList = $availableQuantityListResult->getData(); if (isset($availableQuantityList[Base::FLAT_AVAILABLE_QUANTITY_LIST])) { $availableQuantityData = $availableQuantityList[Base::FLAT_AVAILABLE_QUANTITY_LIST]; } } $result->setData([ Base::SUMMMARY_PRODUCT_LIST => [ Base::FLAT_PRICE_LIST => $priceDataList, Base::FLAT_AVAILABLE_QUANTITY_LIST => $availableQuantityData, ], ]); return $result; } public function writeOffProductBatches(array $products): Sale\Result { $result = new Sale\Result(); if (!Catalog\Config\State::isUsedInventoryManagement() || !State::isProductBatchMethodSelected()) { return $result; } foreach ($products as $productId => $productData) { if (empty($productData['SHIPMENT_ITEM_LIST']) || !is_array($productData['SHIPMENT_ITEM_LIST'])) { continue; } $productBatch = new BatchManager($productId); /** @var Sale\ShipmentItem $item */ foreach ($productData['SHIPMENT_ITEM_LIST'] as $item) { /** @var Sale\ShipmentItemStore $storeItem */ foreach ($item->getShipmentItemStoreCollection() as $storeItem) { $quantity = $storeItem->getQuantity(); if ($quantity <= 0) { continue; } $distributor = new DistributionStrategy\ShipmentStore($productBatch, $storeItem); $r = $distributor->writeOff($quantity); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } } } return $result; } public function returnProductBatches(array $products): Sale\Result { $result = new Sale\Result(); if (!Catalog\Config\State::isUsedInventoryManagement() || !State::isProductBatchMethodSelected()) { return $result; } foreach ($products as $productId => $productData) { if (empty($productData['SHIPMENT_ITEM_LIST']) || !is_array($productData['SHIPMENT_ITEM_LIST'])) { continue; } $productBatch = new BatchManager($productId); /** @var Sale\ShipmentItem $item */ foreach ($productData['SHIPMENT_ITEM_LIST'] as $item) { foreach ($item->getShipmentItemStoreCollection() as $storeItem) { $distributor = new DistributionStrategy\ShipmentStore($productBatch, $storeItem); $r = $distributor->return(); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } } } return $result; } /** * @param $products * * @return bool */ private function isExistsCatalogData($products): bool { foreach ($products as $productData) { if (empty($productData['CATALOG'])) { return false; } } return true; } /** * @param array $list * * @return array */ private function getIblockData(array $list): array { $resultList = []; $res = Catalog\CatalogIblockTable::getList([ 'select' => [ 'IBLOCK_ID', 'SUBSCRIPTION', 'PRODUCT_IBLOCK_ID', 'CATALOG_XML_ID' => 'IBLOCK.XML_ID', ], 'filter' => ['@IBLOCK_ID' => $list], ]); while($iblockData = $res->fetch()) { $resultList[$iblockData['IBLOCK_ID']] = $iblockData; if ($this->enableCache) { static::setHitCache(self::CACHE_CATALOG_IBLOCK_LIST, $iblockData['IBLOCK_ID'], $iblockData); } } unset($res, $iblockData); return $resultList; } /** * @param array $iblockProductMap * * @return array */ private static function checkSkuPermission(array $iblockProductMap): array { $resultList = array(); foreach ($iblockProductMap as $iblockData) { if ($iblockData['PRODUCT_IBLOCK_ID'] > 0 && !empty($iblockData['PRODUCT_LIST'])) { $resultList = array_merge( $resultList, static::checkParentActivity($iblockData['PRODUCT_LIST'], (int)$iblockData['PRODUCT_IBLOCK_ID']) ); } else { foreach ($iblockData['PRODUCT_LIST'] as $productId) { $resultList[] = $productId; } } } return $resultList; } /** * @param array $iblockList * @param array $iblockDataList * * @return array */ private static function createIblockProductMap(array $iblockList, array $iblockDataList): array { $resultList = $iblockDataList; foreach ($iblockList as $iblockId => $iblockProductList) { if (isset($iblockDataList[$iblockId])) { $resultList[$iblockId]['PRODUCT_LIST'] = $iblockProductList; } } return $resultList; } /** * @param array $products * @param array $iblockProductMap * * @return array */ private static function changeSubscribeProductQuantity(array $products, array $iblockProductMap): array { $resultList = $products; foreach ($iblockProductMap as $iblockData) { if ($iblockData['SUBSCRIPTION'] != 'Y') continue; if (empty($iblockData['PRODUCT_LIST'])) continue; foreach($iblockData['PRODUCT_LIST'] as $productId) { if (isset($resultList[$productId])) { if ( !empty($resultList[$productId][Base::FLAT_QUANTITY_LIST]) && is_array($resultList[$productId][Base::FLAT_QUANTITY_LIST]) ) { foreach (array_keys($resultList[$productId][Base::FLAT_QUANTITY_LIST]) as $index) { $resultList[$productId][Base::FLAT_QUANTITY_LIST][$index] = 1; } } } } } return $resultList; } /** * @param array $list * @param array $select * * @return array */ private static function getCatalogProducts(array $list, array $select): array { $usedStoreInventory = Catalog\Config\State::isUsedInventoryManagement(); $typesWithoutStores = [ Catalog\ProductTable::TYPE_SET => true, Catalog\ProductTable::TYPE_SKU => true, Catalog\ProductTable::TYPE_SERVICE => true, ]; $typesWithoutReservation = [ Catalog\ProductTable::TYPE_SET => true, Catalog\ProductTable::TYPE_SKU => true, Catalog\ProductTable::TYPE_SERVICE => true, ]; if (empty($select)) { $select = ['*']; } else { $select[] = 'ID'; $select[] = 'TYPE'; $select[] = 'AVAILABLE'; $select = array_unique($select); } Main\Type\Collection::normalizeArrayValuesByInt($list, true); if (empty($list)) { return []; } $resultList = []; foreach (array_chunk($list, 500) as $pageIds) { $iterator = Catalog\Model\Product::getList([ 'select' => $select, 'filter' => [ '@ID' => $pageIds, ], ]); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $row['TYPE'] = (int)$row['TYPE']; $row['QUANTITY'] = (float)$row['QUANTITY']; $row['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED']; $row['CHECK_QUANTITY'] = ( $row['TYPE'] !== Catalog\ProductTable::TYPE_SERVICE && $row['QUANTITY_TRACE'] === Catalog\ProductTable::STATUS_YES && $row['CAN_BUY_ZERO'] === Catalog\ProductTable::STATUS_NO ); Catalog\Product\SystemField::prepareRow($row, Catalog\Product\SystemField::OPERATION_PROVIDER); if (isset($typesWithoutStores[$row['TYPE']])) { $row['USED_STORE_INVENTORY'] = false; } else { $row['USED_STORE_INVENTORY'] = $usedStoreInventory; } if (isset($typesWithoutReservation[$row['TYPE']])) { $row['USED_RESERVATION'] = false; $row['QUANTITY_RESERVED'] = 0; } else { $row['USED_RESERVATION'] = true; } $resultList[$row['ID']] = $row; } unset($row, $iterator); } unset($pageIds); return $resultList; } /** * @param null $id * * @return array */ private static function getMeasure($id = null): array { static $measureList = array(); if (!empty($measureList[$id])) { return $measureList[$id]; } $fields = array( 'MEASURE' => $id, 'MEASURE_NAME' => $id, 'MEASURE_CODE' => 0, ); if ((int)$id <= 0) { $measure = \CCatalogMeasure::getDefaultMeasure(true, true); $fields['MEASURE_NAME'] = $measure['~SYMBOL_RUS']; $fields['MEASURE_CODE'] = $measure['CODE']; } else { $resMeasures = \CCatalogMeasure::getList( array(), array('ID' => $id), false, false, array('ID', 'SYMBOL_RUS', 'CODE') ); $measure = $resMeasures->fetch(); if (!empty($measure)) { $fields['MEASURE_NAME'] = $measure['SYMBOL_RUS']; $fields['MEASURE_CODE'] = $measure['CODE']; } } $measureList[$id] = $fields; return $fields; } /** * @param array $products * @param array $productPriceList * @param array $discountList * * @return array */ private static function createProductPriceList(array $products, array $productPriceList, array $discountList = array()): array { $priceResultList = array(); foreach ($productPriceList as $basketCode => $priceData) { if (!$priceData) continue; $priceResultList[$basketCode]['PRODUCT_PRICE_ID'] = $priceData['RESULT_PRICE']['ID']; $priceResultList[$basketCode]['NOTES'] = $priceData['PRICE']['CATALOG_GROUP_NAME']; $priceResultList[$basketCode]['DISCOUNT_NAME'] = null; $priceResultList[$basketCode]['DISCOUNT_COUPON'] = null; $priceResultList[$basketCode]['DISCOUNT_VALUE'] = null; $priceResultList[$basketCode]['DISCOUNT_LIST'] = array(); $discount = array(); if (!empty($discountList[$priceData['PRODUCT_ID']][$basketCode])) { $discount = $discountList[$priceData['PRODUCT_ID']][$basketCode]; } $priceResultList[$basketCode]['PRICE_TYPE_ID'] = $priceData['RESULT_PRICE']['PRICE_TYPE_ID']; $priceResultList[$basketCode]['BASE_PRICE'] = $priceData['RESULT_PRICE']['BASE_PRICE']; $priceResultList[$basketCode]['PRICE'] = $priceData['RESULT_PRICE']['DISCOUNT_PRICE']; $priceResultList[$basketCode]['CURRENCY'] = $priceData['RESULT_PRICE']['CURRENCY']; $priceResultList[$basketCode]['DISCOUNT_PRICE'] = $priceData['RESULT_PRICE']['DISCOUNT']; if (isset($priceData['RESULT_PRICE']['PERCENT'])) { $priceResultList[$basketCode]['DISCOUNT_VALUE'] = ($priceData['RESULT_PRICE']['PERCENT'] > 0 ? $priceData['RESULT_PRICE']['PERCENT'] . '%' : null); } $priceResultList[$basketCode]['VAT_RATE'] = $priceData['RESULT_PRICE']['VAT_RATE']; $priceResultList[$basketCode]['VAT_INCLUDED'] = $priceData['RESULT_PRICE']['VAT_INCLUDED']; if (!empty($discount)) { $priceResultList[$basketCode]['DISCOUNT_LIST'] = $discount; } if (!empty($priceData['DISCOUNT'])) { $priceResultList[$basketCode]['DISCOUNT_NAME'] = '[' . $priceData['DISCOUNT']['ID'] . '] ' . $priceData['DISCOUNT']['NAME']; if (!empty($priceData['DISCOUNT']['COUPON'])) { $priceResultList[$basketCode]['DISCOUNT_COUPON'] = $priceData['DISCOUNT']['COUPON']; } if (empty($priceResultList[$basketCode]['DISCOUNT_LIST'])) { $priceResultList[$basketCode]['DISCOUNT_LIST'] = array($priceData['DISCOUNT']); } } } $resultList = array(); if (!empty($priceResultList)) { foreach ($products as $productId => $productData) { if (!empty($products[$productId])) { $productData = $products[$productId]; $quantityList = array(); if (array_key_exists('QUANTITY', $productData)) { $quantityList = array( $productData['BASKET_CODE'] => $productData['QUANTITY'], ); } if (!empty($productData[Base::FLAT_QUANTITY_LIST])) { $quantityList = $productData[Base::FLAT_QUANTITY_LIST]; } foreach($quantityList as $basketCode => $quantity) { $resultList[$basketCode] = $priceResultList[$basketCode]; } } } } return $resultList; } /** * @param array $products * @param array $items * @param array $priceList * @param array $productQuantityList * * @return array */ private static function createProductResult(array $products, array $items, array $priceList, array $productQuantityList): array { $resultList = array(); foreach ($products as $productId => $productData) { $itemCode = $productData['ITEM_CODE']; $basketCode = $productData['BASKET_CODE']; $resultList[$productId] = $items[$productId]; if (isset($productData['PRODUCT_DATA']['ACTIVE'])) { $resultList[$productId]['ACTIVE'] = $productData['PRODUCT_DATA']['ACTIVE']; } $resultList[$productId]['ITEM_CODE'] = $itemCode; QuantityControl::resetAllQuantity($productId); QuantityControl::setReservedQuantity($productId, $productQuantityList[$basketCode]['QUANTITY_RESERVED']); if (!isset($priceList[$basketCode])) { $priceList[$basketCode] = array(); } if (!empty($productData[Base::FLAT_QUANTITY_LIST])) { foreach($productData[Base::FLAT_QUANTITY_LIST] as $basketCode => $quantity) { QuantityControl::addQuantity($productId, $productQuantityList[$basketCode]['QUANTITY']); QuantityControl::addAvailableQuantity($productId, $productQuantityList[$basketCode]['AVAILABLE_QUANTITY']); if (empty($priceList[$basketCode])) { continue; } $resultList[$productId]['PRICE_LIST'][$basketCode] = array_merge( array( 'QUANTITY' => $productQuantityList[$basketCode]['QUANTITY'], 'AVAILABLE_QUANTITY' => $productQuantityList[$basketCode]['AVAILABLE_QUANTITY'], "ITEM_CODE" => $itemCode, "BASKET_CODE" => $basketCode, ), $priceList[$basketCode] ); } } else { $resultList[$productId]['QUANTITY'] = $productQuantityList[$basketCode]['QUANTITY']; QuantityControl::addQuantity($productId, $productQuantityList[$basketCode]['QUANTITY']); QuantityControl::addAvailableQuantity($productId, $productQuantityList[$basketCode]['AVAILABLE_QUANTITY']); if (!empty($resultList[$productId])) { if (empty($priceList[$basketCode])) { continue; } $resultList[$productId] = $priceList[$basketCode] + $resultList[$productId]; } } } return $resultList; } /** * @param array $products * @param array $catalogDataList * @param array $options * * @return array */ private static function setCatalogDataToProducts(array $products, array $catalogDataList, array $options = array()): array { $catalogDataEnabled = self::isCatalogDataEnabled($options); $specialFields = []; foreach (Catalog\Product\SystemField::getProviderSelectFields() as $index => $value) { $specialFields[] = is_string($index) ? $index : $value; } $result = []; foreach ($products as $productId => $productData) { if (!isset($catalogDataList[$productId])) { continue; } $row = $catalogDataList[$productId]; $result[$productId] = [ 'CAN_BUY' => ( $productData['PRODUCT_DATA']['ACTIVE'] === 'Y' && $row['AVAILABLE'] === 'Y' ? 'Y' : 'N' ), 'CAN_BUY_ZERO' => $row['CAN_BUY_ZERO'], 'QUANTITY_TRACE' => $row['QUANTITY_TRACE'], 'CHECK_QUANTITY' => $row['CHECK_QUANTITY'], 'QUANTITY_RESERVED' => (float)$row['QUANTITY_RESERVED'], 'CATALOG_XML_ID' => $productData['PRODUCT_DATA']['CATALOG_XML_ID'], 'PRODUCT_XML_ID' => $productData['PRODUCT_DATA']['~XML_ID'], 'PRODUCT' => $row, ]; if (!$catalogDataEnabled) { $basketRow = [ 'NAME' => $productData['PRODUCT_DATA']['~NAME'], 'DETAIL_PAGE_URL' => $productData['PRODUCT_DATA']['~DETAIL_PAGE_URL'], 'MEASURE_ID' => $row['MEASURE'], 'MEASURE_NAME' => $row['MEASURE_NAME'], 'MEASURE_CODE' => $row['MEASURE_CODE'], 'BARCODE_MULTI' => $row['BARCODE_MULTI'], 'WEIGHT' => (float)$row['WEIGHT'], 'DIMENSIONS' => serialize( [ 'WIDTH' => $row['WIDTH'], 'HEIGHT' => $row['HEIGHT'], 'LENGTH' => $row['LENGTH'], ] ), ]; switch ($row['TYPE']) { case Catalog\ProductTable::TYPE_SET: $basketRow['TYPE'] = Sale\BasketItem::TYPE_SET; break; case Catalog\ProductTable::TYPE_SERVICE: $basketRow['TYPE'] = Sale\BasketItem::TYPE_SERVICE; break; default: $basketRow['TYPE'] = null; break; } foreach ($specialFields as $index) { $basketRow[$index] = $row[$index]; } $result[$productId] = array_merge( $result[$productId], $basketRow ); } $result[$productId]["VAT_INCLUDED"] = "Y"; } return $result; } /** * @return bool */ protected static function isReservationEnabled() { return !(Main\Config\Option::get("catalog", "enable_reservation") == "N" && Main\Config\Option::get("sale", "product_reserve_condition") != "S" && !Catalog\Config\State::isUsedInventoryManagement()); } /** * @param array $products * * @return array * @throws Main\ObjectNotFoundException */ public static function createOrderListFromProducts(array $products) { $productOrderList = array(); foreach ($products as $productId => $productData) { if (!empty($productData['SHIPMENT_ITEM_LIST'])) { /** * @var $shipmentItemIndex * @var Sale\ShipmentItem $shipmentItem */ foreach ($productData['SHIPMENT_ITEM_LIST'] as $shipmentItem) { $shipmentItemCollection = $shipmentItem->getCollection(); if (!$shipmentItemCollection) { throw new Main\ObjectNotFoundException('Entity "ShipmentItemCollection" not found'); } $shipment = $shipmentItemCollection->getShipment(); if (!$shipment) { throw new Main\ObjectNotFoundException('Entity "Shipment" not found'); } /** @var Sale\ShipmentCollection $shipmentCollection */ $shipmentCollection = $shipment->getCollection(); if (!$shipmentCollection) { throw new Main\ObjectNotFoundException('Entity "ShipmentCollection" not found'); } $order = $shipmentCollection->getOrder(); if (!$order) { throw new Main\ObjectNotFoundException('Entity "Order" not found'); } if (empty($productOrderList[$productId][$order->getId()])) { $productOrderList[$productId][$order->getId()] = $order; } } } } return $productOrderList; } /** * @param $productId * * @return array */ private static function getProductCatalogInfo($productId): array { $productId = (int)$productId; if ($productId <= 0) { return []; } $product = static::getHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productId); if (empty($product)) { $iterator = Iblock\ElementTable::getList([ 'select' => [ 'ID', 'IBLOCK_ID', 'NAME', 'IBLOCK_SECTION_ID', ], 'filter' => \CIBlockElement::getPublicElementsOrmFilter(['=ID' => $productId]), ]); $product = $iterator->fetch(); if ($product) { static::setHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productId, $product); } } return (empty($product) ? [] : [ "#PRODUCT_ID#" => $product['ID'], "#PRODUCT_NAME#" => $product['NAME'], ] ); } private static function getTotalAmountFromQuantityList(array $data): float { return self::getAmountFromSource( $data, [ self::AMOUNT_SRC_STORE_QUANTITY_LIST, self::AMOUNT_SRC_QUANTITY, self::AMOUNT_SRC_QUANTITY_LIST, ] ); } private static function getTotalAmountFromPriceList(array $product, bool $direction = true): float { if ($direction) { $list = [ self::AMOUNT_SRC_QUANTITY, self::AMOUNT_SRC_PRICE_LIST, ]; } else { $list = [ self::AMOUNT_SRC_PRICE_LIST, self::AMOUNT_SRC_QUANTITY, ]; } return self::getAmountFromSource($product, $list); } private static function isCatalogDataEnabled(array $options): bool { return in_array(self::USE_GATALOG_DATA, $options); } private static function fillCatalogXmlId(array $products, array $iblockProductMap): array { foreach ($iblockProductMap as $entityData) { if (empty($entityData['PRODUCT_LIST']) || !is_array($entityData['PRODUCT_LIST'])) { continue; } foreach ($entityData['PRODUCT_LIST'] as $index) { if (!isset($products[$index])) { continue; } $products[$index]['PRODUCT_DATA']['CATALOG_XML_ID'] = $entityData['CATALOG_XML_ID']; } unset($index); } unset($entityData); return $products; } private static function fillOfferXmlId(array $products, array $catalogProductDataList): array { $offerList = []; foreach ($catalogProductDataList as $entityData) { if ($entityData['TYPE'] != Catalog\ProductTable::TYPE_OFFER) { continue; } if (strpos($products[$entityData['ID']]['PRODUCT_DATA']['~XML_ID'], '#') !== false) { continue; } $offerList[] = $entityData['ID']; } unset($entityData); if (!empty($offerList)) { $parentMap = []; $parentIdList = []; $parentList = \CCatalogSku::getProductList($offerList, 0); foreach ($parentList as $offerId => $offerData) { $parentId = (int)$offerData['ID']; if (!isset($parentMap[$parentId])) { $parentMap[$parentId] = []; } $parentMap[$parentId][] = $offerId; $parentIdList[$parentId] = $parentId; } unset($offerId, $offerData, $parentList); if (!empty($parentMap)) { sort($parentIdList); foreach (array_chunk($parentIdList, 500) as $pageIds) { $iterator = Iblock\ElementTable::getList([ 'select' => [ 'ID', 'XML_ID', ], 'filter' => ['@ID' => $pageIds], ]); while ($row = $iterator->fetch()) { $parentId = (int)$row['ID']; if (empty($parentMap[$parentId])) { continue; } foreach ($parentMap[$parentId] as $index) { $products[$index]['PRODUCT_DATA']['~XML_ID'] = $row['XML_ID'] . '#' . $products[$index]['PRODUCT_DATA']['~XML_ID'] ; } } unset($parentId, $index); unset($row, $iterator); } unset($pageIds); } unset($parentIdList, $parentMap); } unset($offerList); return $products; } private static function getPriceDataList(array $products, array $config): array { /* 'IS_ADMIN_SECTION' => $adminSection, 'USER_ID' => $userId, 'SITE_ID' => $siteId, 'CURRENCY' => $currency, */ $userGroups = self::getUserGroups($config['USER_ID']); \CCatalogProduct::GetVATDataByIDList(array_keys($products)); if ($config['IS_ADMIN_SECTION']) { if ($config['USER_ID'] > 0) { \CCatalogDiscountSave::SetDiscountUserID($config['USER_ID']); } else { \CCatalogDiscountSave::Disable(); } } Price\Calculation::pushConfig(); Price\Calculation::setConfig([ 'CURRENCY' => $config['CURRENCY'], 'PRECISION' => (int)Main\Config\Option::get('sale', 'value_precision'), 'RESULT_WITH_VAT' => true, 'RESULT_MODE' => Catalog\Product\Price\Calculation::RESULT_MODE_RAW, ]); $priceDataList = \CCatalogProduct::GetOptimalPriceList( $products, $userGroups, 'N', [], ($config['IS_ADMIN_SECTION'] ? $config['SITE_ID'] : false) ); if (empty($priceDataList)) { $productsQuantityList = $products; $quantityCorrected = false; foreach ($productsQuantityList as $productId => $productData) { $quantityList = array($productData['BASKET_CODE'] => $productData['QUANTITY']); if (empty($productData[Base::FLAT_QUANTITY_LIST])) { $quantityList = $productData[Base::FLAT_QUANTITY_LIST]; } if (empty($quantityList)) { continue; } foreach ($quantityList as $basketCode => $quantity) { $nearestQuantity = \CCatalogProduct::GetNearestQuantityPrice($productId, $quantity, $userGroups); if (!empty($nearestQuantity)) { if (!empty($productData[Base::FLAT_QUANTITY_LIST])) { $productsQuantityList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode]['QUANTITY'] = $nearestQuantity; } else { $productsQuantityList[$productId]['QUANTITY'] = $nearestQuantity; } $quantityCorrected = true; } } } if ($quantityCorrected) { $priceDataList = \CCatalogProduct::GetOptimalPriceList( $productsQuantityList, $userGroups, 'N', [], ($config['IS_ADMIN_SECTION'] ? $config['SITE_ID'] : false) ); } } Price\Calculation::popConfig(); if ($config['IS_ADMIN_SECTION']) { if ($config['USER_ID'] > 0) { \CCatalogDiscountSave::ClearDiscountUserID(); } else { \CCatalogDiscountSave::Enable(); } } if (!empty($priceDataList)) { foreach ($priceDataList as $productId => $priceBasketDataList) { foreach ($priceBasketDataList as $basketCode => $priceData) { if ($priceData === false) { continue; } if (empty($priceData['DISCOUNT_LIST']) && !empty($priceData['DISCOUNT']) && is_array($priceData['DISCOUNT'])) { $priceDataList[$productId][$basketCode]['DISCOUNT_LIST'] = [$priceData['DISCOUNT']]; } if (empty($priceData['PRICE']['CATALOG_GROUP_NAME'])) { if (!empty($priceData['PRICE']['CATALOG_GROUP_ID'])) { $priceName = self::getPriceTitle($priceData['PRICE']['CATALOG_GROUP_ID']); if ($priceName !== '') { $priceDataList[$productId][$basketCode]['PRICE']['CATALOG_GROUP_NAME'] = $priceName; } unset($priceName); } } } } } return $priceDataList; } private static function getDiscountList(array $priceDataList): array { $discountList = array(); if (!empty($priceDataList)) { foreach ($priceDataList as $productId => $priceBasketDataList) { foreach ($priceBasketDataList as $basketCode => $priceData) { if ($priceData === false) { continue; } if (empty($priceData['DISCOUNT_LIST']) && !empty($priceData['DISCOUNT']) && is_array($priceData['DISCOUNT'])) { $priceDataList[$productId][$basketCode]['DISCOUNT_LIST'] = [$priceData['DISCOUNT']]; } if (!empty($priceData['DISCOUNT_LIST'])) { if (!isset($discountList[$productId])) { $discountList[$productId] = []; } if (!isset($discountList[$productId][$basketCode])) { $discountList[$productId][$basketCode] = []; } foreach ($priceData['DISCOUNT_LIST'] as $discountItem) { $discountList[$productId][$basketCode][] = \CCatalogDiscount::getDiscountDescription($discountItem); } unset($discountItem); } if (empty($priceData['PRICE']['CATALOG_GROUP_NAME'])) { if (!empty($priceData['PRICE']['CATALOG_GROUP_ID'])) { $priceName = self::getPriceTitle($priceData['PRICE']['CATALOG_GROUP_ID']); if ($priceName != '') { $priceDataList[$productId][$basketCode]['PRICE']['CATALOG_GROUP_NAME'] = $priceName; } unset($priceName); } } } } } return $discountList; } public static function getDefaultStoreId(): int { $result = parent::getDefaultStoreId(); if (Catalog\Config\State::isUsedInventoryManagement()) { $storeId = Catalog\StoreTable::getDefaultStoreId(); if ($storeId !== null) { $result = $storeId; } } return $result; } private static function getAmountFromSource(array $product, array $sourceList): float { if (empty($product) || empty($sourceList)) { return 0; } $result = 0; $found = false; foreach ($sourceList as $source) { switch ($source) { case self::AMOUNT_SRC_QUANTITY: if (array_key_exists($source, $product)) { $result = $product[$source]; $found = true; } break; case self::AMOUNT_SRC_QUANTITY_LIST: case self::AMOUNT_SRC_RESERVED_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { $result = array_sum($product[$source]); $found = true; } break; case self::AMOUNT_SRC_PRICE_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { foreach ($product[$source] as $row) { if (!is_array($row) || !isset($row['QUANTITY'])) { continue; } $result += (float)$row['QUANTITY']; } unset($row); $found = true; } break; case self::AMOUNT_SRC_STORE_QUANTITY_LIST: case self::AMOUNT_SRC_STORE_RESERVED_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { switch (self::getQuantityFormat($product[$source])) { case self::QUANTITY_FORMAT_STORE: $internalResult = self::calculateQuantityFromStores($product[$source]); break; case self::QUANTITY_FORMAT_SHIPMENT: $internalResult = self::calculateQuantityFromShipments($product[$source]); break; default: $internalResult = null; break; } if ($internalResult !== null) { $result += array_sum($internalResult); $found = true; } unset($internalResult); } break; } if ($found) { break; } } return (float)$result; } private static function getStoreAmountFromQuantityList(array $data): ?array { return self::getStoreAmountFromSource( $data, [ self::AMOUNT_SRC_STORE_QUANTITY_LIST, self::AMOUNT_SRC_QUANTITY_LIST, self::AMOUNT_SRC_QUANTITY, ] ); } private static function getStoreReservedQuantityFromProduct(array $product): ?array { return self::getStoreAmountFromSource( $product, [ self::AMOUNT_SRC_STORE_RESERVED_LIST, self::AMOUNT_SRC_RESERVED_LIST, ] ); } private static function getStoreAmountFromPriceList(array $product, bool $direction = true): ?array { if ($direction) { $list = [ self::AMOUNT_SRC_QUANTITY, self::AMOUNT_SRC_PRICE_LIST, ]; } else { $list = [ self::AMOUNT_SRC_PRICE_LIST, self::AMOUNT_SRC_QUANTITY, ]; } return self::getStoreAmountFromSource($product, $list); } private static function getStoreAmountFromSource(array $product, array $sourceList): ?array { if (empty($product) || empty($sourceList)) { return null; } $result = []; $found = false; foreach ($sourceList as $source) { switch ($source) { case self::AMOUNT_SRC_STORE_QUANTITY_LIST: case self::AMOUNT_SRC_STORE_RESERVED_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { switch (self::getQuantityFormat($product[$source])) { case self::QUANTITY_FORMAT_STORE: $internalResult = self::calculateQuantityFromStores($product[$source]); break; case self::QUANTITY_FORMAT_SHIPMENT: $internalResult = self::calculateQuantityFromShipments($product[$source]); break; default: $internalResult = null; break; } if ($internalResult !== null) { $result = $internalResult; $found = true; } unset($internalResult); } break; case self::AMOUNT_SRC_QUANTITY_LIST: case self::AMOUNT_SRC_RESERVED_LIST: /* 'QUANTITY_LIST' => array ( 289 => 1.0, 290 => 3.0, 291 => 4.0, ), */ if ( !empty($product[$source]) && is_array($product[$source]) ) { $result[static::getDefaultStoreId()] = array_sum($product[$source]); $found = true; } break; case self::AMOUNT_SRC_QUANTITY: if (array_key_exists($source, $product)) { $result[static::getDefaultStoreId()] = (float)$product[$source]; $found = true; } break; } if ($found) { break; } } return (!empty($result) ? $result : null); } private static function getQuantityFormat(array $list): ?int { /* first variant 'RESERVED_QUANTITY_LIST_BY_STORE' => array ( 20 => basket code array ( '0_0' => shipment index array ( 3 => 10.0, store id -> quantity ), ), ), second variant 'RESERVED_QUANTITY_LIST_BY_STORE' => array ( 20 => basket code array ( 3 => 10.0, store id -> quantity ), ), 'QUANTITY_LIST_BY_STORE' => array ( 289 => basket code array ( 5 => 1.0, store id => quantity ), 290 => array ( 5 => 3.0, ), 291 => array ( 5 => 4.0, ), ), ), */ $basketRow = reset($list); if ( empty($basketRow) || !is_array($basketRow) ) { return null; } $row = reset($basketRow); if (is_array($row)) { return self::QUANTITY_FORMAT_SHIPMENT; } return self::QUANTITY_FORMAT_STORE; } private static function calculateQuantityFromStores(array $list): ?array { $result = []; $found = false; foreach ($list as $basketItemStores) { if ( empty($basketItemStores) || !is_array($basketItemStores) ) { continue; } foreach ($basketItemStores as $storeId => $quantity) { if (!isset($result[$storeId])) { $result[$storeId] = 0.0; } $result[$storeId] += (float)$quantity; $found = true; } unset($storeId, $quantity); } unset($basketItemStores); return ($found ? $result : null); } private static function calculateQuantityFromShipments(array $list): ?array { $result = []; $found = false; foreach ($list as $basketItemShipments) { if ( empty($basketItemShipments) || !is_array($basketItemShipments) ) { continue; } foreach ($basketItemShipments as $basketItemStores) { foreach ($basketItemStores as $storeId => $quantity) { if (!isset($result[$storeId])) { $result[$storeId] = 0; } $result[$storeId] += (float)$quantity; $found = true; } } } return ($found ? $result : null); } private static function getStoreQuantityFromQuantityList(array $product): array { return self::getStoreQuantityFromSource( $product, [ self::AMOUNT_SRC_STORE_QUANTITY_LIST, self::AMOUNT_SRC_QUANTITY_LIST, ] ); } private static function getStoreQuantityFromSource(array $product, array $sourceList): array { if (empty($product) || empty($sourceList)) { return [static::getDefaultStoreId() => 0.0]; } $result = []; $found = false; foreach ($sourceList as $source) { switch ($source) { case self::AMOUNT_SRC_QUANTITY_LIST: case self::AMOUNT_SRC_RESERVED_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { $result = [ static::getDefaultStoreId() => (float)array_sum($product[$source]), ]; $found = true; } break; /*case self::AMOUNT_SRC_PRICE_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { foreach ($product[$source] as $row) { if (!is_array($row) || !isset($row['QUANTITY'])) { continue; } $result += (float)$row['QUANTITY']; } unset($row); $found = true; } break; */ case self::AMOUNT_SRC_STORE_QUANTITY_LIST: case self::AMOUNT_SRC_STORE_RESERVED_LIST: if ( !empty($product[$source]) && is_array($product[$source]) ) { switch (self::getQuantityFormat($product[$source])) { case self::QUANTITY_FORMAT_STORE: $internalResult = self::calculateQuantityFromStores($product[$source]); break; case self::QUANTITY_FORMAT_SHIPMENT: $internalResult = self::calculateQuantityFromShipments($product[$source]); break; default: $internalResult = null; break; } if ($internalResult !== null) { $result = $internalResult; $found = true; } } break; } if ($found) { break; } } return (!empty($result) ? $result : [static::getDefaultStoreId() => 0.0] ); } private static function loadCurrentStoreReserve(int $productId, array $reserve): array { $result = []; foreach ($reserve as $storeId => $quantity) { $result[$storeId] = [ 'ID' => null, 'PRODUCT_ID' => $productId, 'STORE_ID' => $storeId, 'ADD_QUANTITY_RESERVED' => $quantity, 'QUANTITY_RESERVED' => 0.0, ]; } $iterator = Catalog\StoreProductTable::getList([ 'select' => [ 'ID', 'STORE_ID', 'QUANTITY_RESERVED', ], 'filter' => [ '=PRODUCT_ID' => $productId, '@STORE_ID' => array_keys($reserve), ], ]); while ($row = $iterator->fetch()) { $storeId = (int)$row['STORE_ID']; $result[$storeId]['ID'] = (int)$row['ID']; $result[$storeId]['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED']; } unset($row, $iterator); return $result; } private static function loadCurrentProductStores(array $list): array { Main\Type\Collection::normalizeArrayValuesByInt($list, true); if (empty($list)) { return []; } $result = []; foreach (array_chunk($list, 500) as $pageIds) { $iterator = Catalog\StoreProductTable::getList([ 'select' => [ 'ID', 'STORE_ID', 'PRODUCT_ID', 'AMOUNT', 'QUANTITY_RESERVED', ], 'filter' => [ '@PRODUCT_ID' => $pageIds, '=STORE.ACTIVE' => 'Y', ], 'order' => [ 'PRODUCT_ID' => 'ASC', 'STORE_ID' => 'ASC', ], ]); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $row['PRODUCT_ID'] = (int)$row['PRODUCT_ID']; $row['STORE_ID'] = (int)$row['STORE_ID']; $row['AMOUNT'] = (float)$row['AMOUNT']; $row['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED']; $productId = $row['PRODUCT_ID']; $storeId = $row['STORE_ID']; if (!isset($result[$productId])) { $result[$productId] = []; } $result[$productId][$storeId] = $row; } unset($productId, $storeId); unset($row, $iterator); } unset($pageIds); return $result; } private static function convertErrors(Main\Entity\Result $result): void { global $APPLICATION; $oldMessages = []; foreach ($result->getErrorMessages() as $errorText) { $oldMessages[] = [ 'text' => $errorText, ]; } unset($errorText); if (!empty($oldMessages)) { $error = new \CAdminException($oldMessages); $APPLICATION->ThrowException($error); unset($error); } unset($oldMessages); } } }