Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
catalogprovider.php
1<?php
3
10use Bitrix\Sale;
14
15if (Main\Loader::includeModule('sale'))
16{
22 class CatalogProvider extends Base
23 {
24 private static $userCache = array();
25
26 protected static $hitCache = array();
27 protected static $priceTitleCache = array();
28 protected static $clearAutoCache = array();
29
30 protected $enableCache = true;
31
32 protected const CACHE_USER_GROUPS = 'USER_GROUPS';
33 protected const CACHE_ITEM_WITHOUT_RIGHTS = 'IBLOCK_ELEMENT_PERM_N';
34 protected const CACHE_ITEM_RIGHTS = 'IBLOCK_ELEMENT';
35 protected const CACHE_ITEM_WITH_RIGHTS = 'IBLOCK_ELEMENT_PERM_Y';
36 protected const CACHE_ELEMENT_RIGHTS_MODE = 'ELEMENT_RIGHTS_MODE';
37 protected const CACHE_ELEMENT_SHORT_DATA = 'IBLOCK_ELEMENT_SHORT';
38 protected const CACHE_PRODUCT = 'CATALOG_PRODUCT';
39 protected const CACHE_VAT = 'VAT_INFO';
40 protected const CACHE_IBLOCK_RIGHTS = 'IBLOCK_RIGHTS';
41 protected const CACHE_STORE = 'CATALOG_STORE';
42 protected const CACHE_STORE_PRODUCT = 'CATALOG_STORE_PRODUCT';
43 protected const CACHE_PARENT_PRODUCT_ACTIVE = 'PARENT_PRODUCT_ACTIVE';
44 protected const CACHE_CATALOG_IBLOCK_LIST = 'CATALOG_IBLOCK_LIST';
45 protected const CACHE_PRODUCT_STORE_LIST = 'CACHE_PRODUCT_STORE_LIST';
46 protected const CACHE_PRODUCT_AVAILABLE_LIST = 'CACHE_PRODUCT_AVAILABLE_LIST';
47
48 protected const CATALOG_PROVIDER_EMPTY_STORE_ID = Base::EMPTY_STORE_ID;
49 protected const BUNDLE_TYPE = 1;
50
52 protected const RESULT_PRODUCT_LIST = Base::SUMMMARY_PRODUCT_LIST;
53 protected const RESULT_CATALOG_LIST = 'CATALOG_DATA_LIST';
54
55 protected const USE_GATALOG_DATA = 'CATALOG_DATA';
56
57 protected const AMOUNT_SRC_QUANTITY = 'QUANTITY';
58 protected const AMOUNT_SRC_QUANTITY_LIST = Base::FLAT_QUANTITY_LIST;
59 protected const AMOUNT_SRC_PRICE_LIST = 'PRICE_LIST';
60 protected const AMOUNT_SRC_STORE_QUANTITY_LIST = Base::STORE_QUANTITY_LIST;
61 protected const AMOUNT_SRC_RESERVED_LIST = Base::FLAT_RESERVED_QUANTITY_LIST;
62 protected const AMOUNT_SRC_STORE_RESERVED_LIST = Base::STORE_RESERVED_QUANTITY_LIST;
63
64 private const QUANTITY_FORMAT_STORE = 1;
65 private const QUANTITY_FORMAT_SHIPMENT = 2;
66
72 public function getProductData(array $products)
73 {
74 return $this->getData($products);
75 }
76
82 public function getCatalogData(array $products)
83 {
84 return $this->getData(
85 $products,
86 [self::USE_GATALOG_DATA]
87 );
88 }
89
96 private function getData(array $products, array $options = array()): Sale\Result
97 {
98 $context = $this->getContext();
99
100 $resultProductList = array_fill_keys(array_keys($products), false);
101
102 $result = new Sale\Result();
103
104 $userId = (int)($context['USER_ID'] ?? 0);
105 if ($userId < 0)
106 {
107 $userId = 0;
108 }
109 $siteId = $context['SITE_ID'] ?? false;
110 $currency = $context['CURRENCY'] ?? false;
111 $currency = (is_string($currency) ? Currency\CurrencyManager::checkCurrencyID($context['CURRENCY']) : false);
112 if ($currency === false)
113 {
114 $currency = Sale\Internals\SiteCurrencyTable::getSiteCurrency($siteId ?: SITE_ID);
115 }
116 $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true);
117
118 if (in_array('DISABLE_CACHE', $options))
119 {
120 $this->enableCache = false;
121 }
122
123 $catalogDataEnabled = self::isCatalogDataEnabled($options);
124
125 $outputVariable = static::getOutputVariable($options);
126
127 $productGetIdList = array();
128 $correctProductIds = [];
129
130 $iblockElementSelect = array('ID', 'IBLOCK_ID', 'IBLOCK_SECTION_ID', 'ACTIVE', 'ACTIVE_DATE', 'XML_ID');
131 if (!$catalogDataEnabled)
132 {
133 $iblockElementSelect = array_merge($iblockElementSelect, array('NAME', 'DETAIL_PAGE_URL'));
134 }
135
136 $resultList = array();
137 foreach ($products as $productId => $itemData)
138 {
139 $resultList[$productId] = false;
140 if (!isset($itemData['ITEM_CODE']))
141 {
142 $itemData['ITEM_CODE'] = $productId;
143 $products[$productId]['ITEM_CODE'] = $productId;
144 }
145
146 if (!isset($itemData['BASKET_CODE']))
147 {
148 $itemData['BASKET_CODE'] = $productId;
149 $products[$productId]['BASKET_CODE'] = $productId;
150 }
151
152 $hash = $productId."|".$userId;
153 $productCachedData = static::getHitCache(self::CACHE_ITEM_RIGHTS, $hash, $iblockElementSelect);
154 if ($this->enableCache && !empty($productCachedData))
155 {
156 $products[$productId]['PRODUCT_DATA'] = $productCachedData;
157 $correctProductIds[$productId] = true;
158 }
159 else
160 {
161 $productGetIdList[] = $productId;
162 }
163 }
164
165 if (!empty($productGetIdList))
166 {
167 $productDataList = $this->getElements(
168 $productGetIdList,
169 $iblockElementSelect,
170 ($adminSection ? $userId : null)
171 );
172
173 foreach ($productDataList as $productId => $productData)
174 {
175 $products[$productId]['PRODUCT_DATA'] = $productData;
176 $hash = $productId."|".$userId;
177 static::setHitCache(self::CACHE_ITEM_RIGHTS, $hash, $productData);
178 $correctProductIds[$productId] = true;
179 }
180
181 $products = array_intersect_key(
182 $products,
183 $correctProductIds
184 );
185 if (empty($products))
186 {
187 return static::getResultProvider($result, $outputVariable, $resultList);
188 }
189 }
190 unset($correctProductIds);
191
192 $iblockList = array();
193 $iblockDataList = array();
194 $iblockGetIdList = array();
195 foreach ($products as $productId => $productData)
196 {
197 $iblockId = $productData['PRODUCT_DATA']['IBLOCK_ID'];
198 $iblockList[$iblockId][] = $productId;
199 }
200
201 foreach ($iblockList as $iblockId => $iblockProductIdList)
202 {
203 $iblockData = static::getHitCache(self::CACHE_CATALOG_IBLOCK_LIST, $iblockId);
204 if ($this->enableCache && !empty($iblockData))
205 {
206 $iblockDataList[$iblockId] = $iblockData;
207 }
208 else
209 {
210 $iblockGetIdList[] = $iblockId;
211 }
212 }
213
214 if (!empty($iblockGetIdList))
215 {
216 $iblockDataList = $this->getIblockData($iblockGetIdList) + $iblockDataList;
217 }
218
219 $iblockList = array_intersect_key(
220 $iblockList,
221 $iblockDataList
222 );
223
224 $iblockProductMap = static::createIblockProductMap($iblockList, $iblockDataList);
225
226 $correctProductList = static::checkSkuPermission($iblockProductMap);
227
228 $products = array_intersect_key(
229 $products,
230 array_fill_keys($correctProductList, true)
231 );
232 if (empty($products))
233 {
234 return static::getResultProvider($result, $outputVariable, $resultList);
235 }
236
237 $products = static::changeSubscribeProductQuantity($products, $iblockProductMap);
238
239 // catalog product
240
241 $catalogSelect = array(
242 'ID',
243 'CAN_BUY_ZERO',
244 'QUANTITY_TRACE',
245 'QUANTITY',
246 'QUANTITY_RESERVED',
247 'MEASURE',
248 'TYPE',
249 'AVAILABLE',
250 );
251 if (!$catalogDataEnabled)
252 {
253 $catalogSelect = array_merge($catalogSelect, array(
254 'WEIGHT',
255 'WIDTH',
256 'HEIGHT',
257 'LENGTH',
258 'BARCODE_MULTI',
259 ));
260 }
261 $catalogSelect = array_merge($catalogSelect, Catalog\Product\SystemField::getProviderSelectFields());
262
263 $catalogProductDataList = static::getCatalogProducts(array_keys($products), $catalogSelect);
264
265 $products = array_intersect_key($products, $catalogProductDataList);
266 if (empty($products))
267 {
268 return static::getResultProvider($result, $outputVariable, $resultList);
269 }
270
271 // fill catalog xml id
272 $products = self::fillCatalogXmlId($products, $iblockProductMap);
273 // prepare offers xml id
274 $products = self::fillOfferXmlId($products, $catalogProductDataList);
275
276 // get prices and discounts
277 $priceDataList = self::getPriceDataList(
278 $products,
279 [
280 'IS_ADMIN_SECTION' => $adminSection,
281 'USER_ID' => $userId,
282 'SITE_ID' => $siteId,
283 'CURRENCY' => $currency,
284 ]
285 );
286 $discountList = self::getDiscountList($priceDataList);
287
288 $productQuantityList = array();
289 $productPriceList = array();
290
291 $fullQuantityMode = in_array('FULL_QUANTITY', $options);
292
293 foreach ($products as $productId => $productData)
294 {
295 $catalogProductData = $catalogProductDataList[$productId];
296
297 $quantityList = array();
298
299 if (array_key_exists('QUANTITY', $productData))
300 {
301 $quantityList = array($productData['BASKET_CODE'] => $productData['QUANTITY']);
302 }
303
304 if (!empty($productData[Base::FLAT_QUANTITY_LIST]))
305 {
306 $quantityList = $productData[Base::FLAT_QUANTITY_LIST];
307 }
308
309 $productQuantityList[$productData['BASKET_CODE']]['QUANTITY_RESERVED'] = $catalogProductData['QUANTITY_RESERVED'];
310
311 $baseCatalogQuantity = (float)$catalogProductData['QUANTITY'];
312
313 $allCount = count($quantityList);
314 $sumQuantity = 0;
315 foreach ($quantityList as $quantity)
316 {
317 $sumQuantity += (float)abs($quantity);
318 }
319
320 $catalogQuantityForAvaialable = $baseCatalogQuantity;
321 $checkCatalogQuantity = $baseCatalogQuantity;
322
323 $isEnough = !($catalogProductData['CHECK_QUANTITY'] && $catalogQuantityForAvaialable < $sumQuantity);
324 $setQuantity = $baseCatalogQuantity;
325 foreach ($quantityList as $basketCode => $quantity)
326 {
327 $quantity = (float)abs($quantity);
328
329 if (!$isEnough)
330 {
331 if ($catalogQuantityForAvaialable - $quantity < 0)
332 {
333 $quantity = $catalogQuantityForAvaialable;
334 }
335
336 $catalogQuantityForAvaialable -= $quantity;
337 }
338
339 $productQuantityList[$basketCode]['AVAILABLE_QUANTITY'] = (
340 $baseCatalogQuantity >= $quantity || !$catalogProductData['CHECK_QUANTITY']
341 ? $quantity
342 : $baseCatalogQuantity
343 );
344
345 if ($fullQuantityMode)
346 {
347 $checkCatalogQuantity -= $quantity;
348 $setQuantity = $quantity;
349 $allCount--;
350
351 if ($allCount == 0)
352 {
353 $setQuantity = $checkCatalogQuantity + $quantity;
354 }
355 }
356 else
357 {
358 if ($baseCatalogQuantity - $quantity > 0 || !$catalogProductData['CHECK_QUANTITY'])
359 {
360 $setQuantity = $quantity;
361 }
362 }
363
364 $productQuantityList[$basketCode]['QUANTITY'] = $setQuantity;
365 }
366 unset($basketCode, $quantity);
367
368 foreach (array_keys($quantityList) as $basketCode)
369 {
370 if (isset($priceDataList[$productId][$basketCode]))
371 {
372 $productPriceList[$basketCode] = $priceDataList[$productId][$basketCode];
373 }
374 }
375 unset($basketCode);
376
377 $measure = isset($catalogProductData['MEASURE']) ? (int)$catalogProductData['MEASURE'] : null;
378 $measureFields = static::getMeasure($measure);
379 if (!empty($measureFields))
380 {
381 $catalogProductDataList[$productId] = $measureFields + $catalogProductDataList[$productId];
382 }
383 }
384
385 unset($fullQuantityMode);
386
387 $resultData = static::setCatalogDataToProducts($products, $catalogProductDataList, $options);
388
389 $priceResultList = static::createProductPriceList($products, $productPriceList, $discountList);
390
391 $resultList = static::createProductResult($products, $resultData, $priceResultList, $productQuantityList);
392
393 $resultList = $resultList + $resultProductList;
394
395 return static::getResultProvider($result, $outputVariable, $resultList);
396 }
397
398 private static function getOutputVariable(array $options = array()): string
399 {
400 return (self::isCatalogDataEnabled($options)
401 ? static::RESULT_CATALOG_LIST
402 : Base::SUMMMARY_PRODUCT_LIST
403 );
404 }
405
406 private static function getResultProvider(Sale\Result $result, $outputVariable, array $resultList = array()): Sale\Result
407 {
408 $result->setData(
409 array(
410 $outputVariable => $resultList,
411 )
412 );
413
414 return $result;
415 }
416
424 private function getElements(array $list, array $select, ?int $userId = null): array
425 {
426 $filter = array(
427 'ID' => $list,
428 'ACTIVE_DATE' => 'Y',
429 'CHECK_PERMISSIONS' => 'Y',
430 'MIN_PERMISSION' => 'R',
431 );
432 if ($userId !== null)
433 {
434 $filter['PERMISSIONS_BY'] = $userId;
435 }
436
437 $resultList = array();
438 $dbIBlockElement = \CIBlockElement::GetList(
439 array(),
440 $filter,
441 false,
442 false,
443 $select
444 );
445 while ($productData = $dbIBlockElement->GetNext())
446 {
447 $resultList[$productData['ID']] = $productData;
448 }
449 unset($dbIBlockElement);
450
451 return $resultList;
452 }
453
459 public function getBundleItems(array $products)
460 {
461 $result = new Sale\Result();
462
463 $resultList = array();
464
465 $productIdList = array();
466 static $proxyCatalogProductSet = array();
467 static $proxyCatalogSkuData = array();
468 $bundleItemList = array();
469
470 $bundleIndex = array();
471
472 foreach ($products as $productId => $productData)
473 {
474 $proxyCatalogProductSetKey = $productId."|".static::BUNDLE_TYPE;
475 if (!empty($proxyCatalogProductSet[$proxyCatalogProductSetKey]) && is_array($proxyCatalogProductSet[$proxyCatalogProductSetKey]))
476 {
477 $childItemList = $proxyCatalogProductSet[$proxyCatalogProductSetKey];
478 }
479 else
480 {
481 $childItemList = \CCatalogProductSet::getAllSetsByProduct($productId, static::BUNDLE_TYPE);
482 if (!empty($childItemList) && is_array($childItemList))
483 {
484 $proxyCatalogProductSet[$proxyCatalogProductSetKey] = $childItemList;
485 }
486 }
487
488 if (!empty($childItemList))
489 {
490 $bundleItemList = $childItemList + $bundleItemList;
491 $bundleItemListIds = array_keys($childItemList);
492 $bundleItemId = reset($bundleItemListIds);
493 unset($bundleItemListIds);
494 $bundleIndex[$bundleItemId] = $productId;
495 }
496 }
497
498 $childIdList = array();
499
500 if (empty($bundleItemList))
501 return $result;
502
503 $bundleChildList = array();
504 $childProducts = array();
505 $productIndexList = array();
506 foreach ($bundleItemList as $parentItemid => $bundleItemData)
507 {
508 $productId = $bundleIndex[$parentItemid];
509 foreach ($bundleItemData["ITEMS"] as $childItemid => $item)
510 {
511 if (!isset($childIdList[$item['ITEM_ID']]))
512 $childIdList[$item['ITEM_ID']] = true;
513
514 $bundleChildList[$item['ITEM_ID']] = $item;
515 $childProducts[$item['ITEM_ID']] = array(
516 'ITEM_CODE' => $item['ITEM_ID'],
517 'PRODUCT_ID' => $item['ITEM_ID'],
518 Base::FLAT_QUANTITY_LIST => [$item['ITEM_ID'] => $item['QUANTITY']],
519 'BUNDLE_CHILD' => true,
520 );
521
522 $productIndexList[$item['ITEM_ID']] = array(
523 'PRODUCT_ID' => $productId,
524 'PARENT_ID' => $parentItemid,
525 'CHILD_ID' => $childItemid,
526 );
527 }
528 }
529
530 $r = $this->getProductData($childProducts);
531 if ($r->isSuccess())
532 {
533 $resultData = $r->getData();
534 if (
535 !empty($resultData[Base::SUMMMARY_PRODUCT_LIST])
536 && is_array($resultData[Base::SUMMMARY_PRODUCT_LIST])
537 )
538 {
539 $resultDataList = $resultData[Base::SUMMMARY_PRODUCT_LIST];
540 foreach ($resultDataList as $itemCode => $itemData)
541 {
542 $item = $bundleChildList[$itemCode];
543 if (array_key_exists('QUANTITY_TRACE', $itemData))
544 unset($itemData['QUANTITY_TRACE']);
545
546 $itemData["PRODUCT_ID"] = $item["ITEM_ID"];
547 $itemData["MODULE"] = 'catalog';
548 $itemData["PRODUCT_PROVIDER_CLASS"] = Basket::getDefaultProviderName();
549
550 $productIdList[] = $item["ITEM_ID"];
551
552 $itemData["PROPS"] = array();
553
554 if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && is_array($proxyCatalogSkuData[$item["ITEM_ID"]]))
555 {
556 $parentSkuData = $proxyCatalogSkuData[$item["ITEM_ID"]];
557 }
558 else
559 {
560 $parentSkuData = \CCatalogSku::GetProductInfo($item["ITEM_ID"]);
561 if ($parentSkuData)
562 {
563 $proxyCatalogSkuData[$item["ITEM_ID"]] = $parentSkuData;
564 }
565 }
566
567 if (!empty($parentSkuData))
568 {
569 $childDataList = array();
570 $childIdGetList = array();
571
572 $iblockPropertyDataList = array();
573 $iblockPropertyIdList = array();
574
575 $propsSku = array();
576
577 foreach ($childIdList as $childId => $parentValue)
578 {
579 $productData = static::getHitCache(self::CACHE_ELEMENT_SHORT_DATA, $item["ITEM_ID"]);
580 if (!empty($productData))
581 {
582 $childDataList[$childId] = $productData;
583 if (!isset($iblockPropertyIdList[$productData['IBLOCK_ID']]))
584 {
585 $iblockPropertyIdList[$productData['IBLOCK_ID']] = true;
586 }
587 }
588 else
589 {
590 $childIdGetList[] = $childId;
591 }
592 }
593
594 if (!empty($childIdGetList))
595 {
596 $iterator = Iblock\ElementTable::getList([
597 'select' => [
598 'ID',
599 'IBLOCK_ID',
600 'NAME',
601 'IBLOCK_SECTION_ID',
602 ],
603 'filter' => \CIBlockElement::getPublicElementsOrmFilter(['@ID' => $childIdGetList]),
604 ]);
605 while ($productData = $iterator->fetch())
606 {
607 static::setHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productData["ID"], $productData);
608 $childDataList[$productData["ID"]] = $productData;
609
610 if (!isset($iblockPropertyIdList[$productData['IBLOCK_ID']]))
611 {
612 $iblockPropertyIdList[$productData['IBLOCK_ID']] = true;
613 }
614 }
615 }
616
617 foreach ($iblockPropertyIdList as $iblockPropertyId => $iblockPropertyValue)
618 {
619 if ($propsSku = static::getHitCache('IBLOCK_PROPERTY', $iblockPropertyId))
620 {
621 $iblockPropertyDataList[$iblockPropertyId] = $propsSku;
622 }
623 else
624 {
625 $dbOfferProperties = \CIBlock::GetProperties($iblockPropertyId, array(), array("!XML_ID" => "CML2_LINK"));
626 while($offerProperties = $dbOfferProperties->Fetch())
627 {
628 $propsSku[] = $offerProperties["CODE"];
629 }
630 static::setHitCache('IBLOCK_PROPERTY', $iblockPropertyId, $propsSku);
631 }
632 }
633
634 $propSkuHash = (!empty($propsSku)) ? md5(join('|', $propsSku)): md5($item["ITEM_ID"]);
635
636 $proxyProductPropertyKey = $item["ITEM_ID"]."_".$parentSkuData["IBLOCK_ID"]."_".$propSkuHash;
637
638 $productProperties = static::getHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey);
639 if (empty($productProperties))
640 {
641 $productProperties = \CIBlockPriceTools::GetOfferProperties(
642 $item["ITEM_ID"],
643 $parentSkuData["IBLOCK_ID"],
644 $propsSku
645 );
646
647 static::setHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey, $productProperties);
648 }
649
650 if (!empty($productProperties))
651 {
652 foreach ($productProperties as $propData)
653 {
654 $itemData["PROPS"][] = array(
655 "NAME" => $propData["NAME"],
656 "CODE" => $propData["CODE"],
657 "VALUE" => $propData["VALUE"],
658 "SORT" => $propData["SORT"],
659 );
660 }
661 }
662
663 }
664
665 $parentProductIndexData = $productIndexList[$itemCode];
666
667 $priceData = array();
668 if (!empty($itemData['PRICE_LIST']))
669 {
670 $priceData = reset($itemData['PRICE_LIST']);
671 unset($itemData['PRICE_LIST']);
672 }
673
674 if (array_key_exists('PRODUCT', $itemData))
675 {
676 unset($itemData['PRODUCT']);
677 }
678
679 $bundleItemList[$parentProductIndexData['PARENT_ID']]["ITEMS"][$parentProductIndexData['CHILD_ID']] = array_merge($item, $itemData, $priceData);
680 }
681 }
682 }
683
684 $elementList = static::getHitCache('IBLOCK_ELEMENT_LIST', $productId);
685 if (empty($elementList))
686 {
687 $productRes = \CIBlockElement::GetList(
688 array(),
689 array('ID' => $productIdList),
690 false,
691 false,
692 array("ID", "IBLOCK_ID", "IBLOCK_SECTION_ID", "PREVIEW_PICTURE", "DETAIL_PICTURE", "IBLOCK_TYPE_ID", "XML_ID")
693 );
694 while ($productData = $productRes->GetNext())
695 {
696 $elementList[$productData['ID']] = $productData;
697 }
698
699 if (!empty($elementList) && is_array($elementList))
700 {
701 static::setHitCache('IBLOCK_ELEMENT_LIST', $productId, $elementList);
702 }
703 }
704
705 if (!empty($elementList) && is_array($elementList))
706 {
707 foreach ($bundleItemList as $bundleParentId => $bundleItemData)
708 {
709 foreach ($bundleItemData["ITEMS"] as $bundleChildId => $item)
710 {
711 if (!$elementList[$item["ITEM_ID"]])
712 continue;
713
714 $elementData = $elementList[$item["ITEM_ID"]];
715
716 $properties = array();
717 $strIBlockXmlID = (string)\CIBlock::GetArrayByID($elementData['IBLOCK_ID'], 'XML_ID');
718 if ($strIBlockXmlID != "")
719 {
720 $properties[] = array(
721 "NAME" => "Catalog XML_ID",
722 "CODE" => "CATALOG.XML_ID",
723 "VALUE" => $strIBlockXmlID,
724 );
725
726 $elementData['CATALOG_XML_ID'] = $strIBlockXmlID;
727
728 }
729
730 if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && strpos($elementData["XML_ID"], '#') === false)
731 {
732 $parentSkuData = $proxyCatalogSkuData[$item["ITEM_ID"]];
733 if (!empty($proxyParentData[$parentSkuData['ID']]) && is_array($proxyParentData[$parentSkuData['ID']]))
734 {
735 $parentData = $proxyParentData[$parentSkuData['ID']];
736 }
737 else
738 {
739 $parentIterator = Iblock\ElementTable::getList(
740 array(
741 'select' => array('ID', 'XML_ID'),
742 'filter' => array('ID' => $parentSkuData['ID']),
743 )
744 );
745
746 $parentData = $parentIterator->fetch();
747 if (!empty($parentData))
748 {
749 $proxyParentData[$parentSkuData['ID']] = $parentData;
750 }
751
752 unset($parentIterator);
753 }
754
755 $elementData["XML_ID"] = $parentData['XML_ID'].'#'.$elementData["XML_ID"];
756 unset($parentData);
757 }
758
759 $properties[] = array(
760 "NAME" => "Product XML_ID",
761 "CODE" => "PRODUCT.XML_ID",
762 "VALUE" => $elementData["XML_ID"],
763 );
764
765 $bundleItemData = $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId];
766
767 $bundleItemProps = array();
768 if (is_array($elementData["PROPS"]) && !empty($elementData["PROPS"]))
769 {
770 $bundleItemProps = $elementData["PROPS"];
771 }
772
773 if (!empty($properties))
774 {
775 $bundleItemProps = $bundleItemProps + $properties;
776 }
777
778 $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId] = $bundleItemData + array(
779 'IBLOCK_ID' => $elementData["IBLOCK_ID"],
780 'IBLOCK_SECTION_ID' => $elementData["IBLOCK_SECTION_ID"],
781 'PREVIEW_PICTURE' => $elementData["PREVIEW_PICTURE"],
782 'DETAIL_PICTURE' => $elementData["DETAIL_PICTURE"],
783
784 'CATALOG_XML_ID' => $elementData["CATALOG_XML_ID"],
785 'PRODUCT_XML_ID' => $elementData["XML_ID"],
786 );
787
788 $bundleItemList[$bundleParentId]["ITEMS"][$bundleChildId]['PROPS'] = $bundleItemProps;
789 }
790 }
791
792 }
793
794 foreach(GetModuleEvents("sale", "OnGetSetItems", true) as $eventData)
795 {
796 ExecuteModuleEventEx($eventData, array(&$bundleItemList));
797 }
798
799 if (!empty($bundleItemList))
800 {
801 foreach ($bundleItemList as $bundleParentId => $bundleData)
802 {
803 if (empty($bundleIndex[$bundleParentId]))
804 continue;
805
806 $productId = $bundleIndex[$bundleParentId];
807
808 $resultList[$productId] = $bundleData;
809 }
810
811 $result->setData(
812 array(
813 'BUNDLE_LIST' => $resultList,
814 )
815 );
816 }
817
818 return $result;
819 }
820
826 protected static function getUserGroups($userId)
827 {
828 $userId = (int)$userId;
829 if ($userId < 0)
830 return false;
831
832 if (!isset(self::$userCache[$userId]))
833 self::$userCache[$userId] = Main\UserTable::getUserGroupIds($userId);
834
835 return self::$userCache[$userId];
836 }
837
843 public function ship(array $products)
844 {
845 return $this->shipProducts($products);
846 }
847
853 public function unship(array $products)
854 {
855 $result = new Sale\Result();
856
857 $r = $this->tryUnship($products);
858 if (!$r->isSuccess())
859 {
860 $result->addErrors($r->getErrors());
861 return $result;
862 }
863
864 $data = $r->getData();
865
866 if (!empty($data['PRODUCTS_LIST_SHIPPED']))
867 {
868 $productsList = array();
869 foreach ($data['PRODUCTS_LIST_SHIPPED'] as $productId => $value)
870 {
871 if ($value && !empty($products[$productId]))
872 {
873 $productsList[$productId] = $products[$productId];
874 }
875 }
876
877 if (!empty($productsList))
878 {
879 $this->shipProducts($productsList);
880 }
881 }
882
883 return $result;
884 }
885
892 public function deliver(array $products)
893 {
894 $result = new Sale\Result();
895
896 $resultList = array();
897
898 $productOrderList = static::createOrderListFromProducts($products);
899
900 $deliverProductList = array();
901 foreach ($products as $productId => $productData)
902 {
903 $userId = null;
904 $orderPaid = null;
905 $orderId = null;
906
907 if (isset($productData['USER_ID']))
908 {
909 $userId = $productData['USER_ID'];
910 }
911
912 if (isset($productData['ORDER_ID']))
913 {
914 $orderId = $productData['ORDER_ID'];
915 }
916
917 if (isset($productData['PAID']))
918 {
919 $orderPaid = $productData['PAID'];
920 }
921
927 if (isset($productOrderList[$productId]))
928 {
929 foreach ($productOrderList[$productId] as $orderId => $order)
930 {
931 if (!isset($resultList[$productId]))
932 {
933 $deliverProductList[] = array(
934 'PRODUCT_ID' => $productId,
935 'USER_ID' => $order->getUserId(),
936 'PAID' => $order->isPaid(),
937 'ORDER_ID' => $orderId,
938 );
939 }
940 }
941 }
942 else
943 {
944 if (isset($productData['USER_ID']))
945 {
946 $userId = $productData['USER_ID'];
947 }
948
949 if (isset($productData['ORDER_ID']))
950 {
951 $orderId = $productData['ORDER_ID'];
952 }
953
954 if (isset($productData['PAID']))
955 {
956 $orderPaid = $productData['PAID'];
957 }
958
959 $deliverProductList[] = array(
960 'PRODUCT_ID' => $productId,
961 'USER_ID' => $userId,
962 'PAID' => $orderPaid,
963 'ORDER_ID' => $orderId,
964 );
965 }
966 }
967
968 if (!empty($deliverProductList))
969 {
970 foreach ($deliverProductList as $productData)
971 {
972 $productId = $productData['PRODUCT_ID'];
973 $resultList[$productId] = \CatalogPayOrderCallback(
974 $productId,
975 $productData['USER_ID'],
976 $productData['PAID'],
977 $productData['ORDER_ID']
978 );
979 }
980 }
981
982 if (!empty($resultList))
983 {
984 $result->setData(
985 array(
986 'DELIVER_PRODUCTS_LIST' => $resultList,
987 )
988 );
989 }
990
991 return $result;
992 }
993
999 public function viewProduct(array $products)
1000 {
1001 $result = new Sale\Result();
1002
1003 $resultList = array();
1004
1005 foreach ($products as $productId => $itemData)
1006 {
1007 if (!isset($resultList[$productId]))
1008 {
1009 $context = $this->getContext();
1010
1011 $resultList[$productId] = \CatalogViewedProductCallback(
1012 $productId,
1013 $context['USER_ID'],
1014 $context['SITE_ID']
1015 );
1016 }
1017
1018 }
1019
1020 if (!empty($resultList))
1021 {
1022 $result->setData(
1023 array(
1024 'VIEW_PRODUCTS_LIST' => $resultList,
1025 )
1026 );
1027 }
1028
1029 return $result;
1030 }
1031
1037 public function recurring(array $items)
1038 {
1039 $result = new Sale\Result();
1040
1041 $resultList = array();
1042
1043 foreach ($items as $productId => $itemData)
1044 {
1045 if (!isset($resultList[$productId]))
1046 {
1047 $context = $this->getContext();
1048
1049 $resultList[$productId] = \CatalogRecurringCallback(
1050 $productId,
1051 $context['USER_ID']
1052 );
1053 }
1054
1055 }
1056
1057 if (!empty($resultList))
1058 {
1059 $result->setData(
1060 array(
1061 'RECURRING_PRODUCTS_LIST' => $resultList,
1062 )
1063 );
1064 }
1065
1066 return $result;
1067 }
1068
1074 public function checkBarcode(array $items)
1075 {
1076 $result = new Sale\Result();
1077
1078 $resultList = array();
1079
1080 foreach ($items as $barcodeParams)
1081 {
1082 $resultList[$barcodeParams['BARCODE']] = false;
1083 $dbres = \CCatalogStoreBarcode::GetList(
1084 array(),
1085 $barcodeParams
1086 );
1087 $resultList[$barcodeParams['BARCODE']] = (bool)($dbres->GetNext());
1088
1089 }
1090
1091 if (!empty($resultList))
1092 {
1093 $result->setData(
1094 array(
1095 'BARCODE_CHECK_LIST' => $resultList,
1096 )
1097 );
1098 }
1099
1100 return $result;
1101 }
1102
1108 protected function shipProducts(array $products)
1109 {
1110 $result = new Sale\Result();
1111
1112 $resultList = array_fill_keys(array_keys($products), false);
1113
1114 $availableItems = $this->createProductsListWithCatalogData($products);
1115
1116 $productStoreDataList = [];
1117 if (Catalog\Config\State::isUsedInventoryManagement())
1118 {
1119 $r = $this->getProductListStores($products);
1120 if ($r->isSuccess())
1121 {
1122 $data = $r->getData();
1123 if (!empty($data['PRODUCT_STORES_LIST']))
1124 {
1125 $productStoreDataList = $data['PRODUCT_STORES_LIST'];
1126 }
1127 unset($data);
1128 }
1129 unset($r);
1130 }
1131
1132 foreach ($availableItems as $productId => $productData)
1133 {
1134 $r = static::shipProduct(
1135 $productData,
1136 (!empty($productStoreDataList[$productId])
1137 ? $productStoreDataList[$productId]
1138 : []
1139 )
1140 );
1141 if (!$r->isSuccess())
1142 {
1143 $result->addErrors($r->getErrors());
1144 $result->addWarnings($r->getErrors());
1145 }
1146
1147 $resultList[$productId] = $r->isSuccess();
1148 }
1149
1150 $result->setData([
1151 'SHIPPED_PRODUCTS_LIST' => $resultList,
1152 ]);
1153
1154 return $result;
1155 }
1156
1157 // private function
1158
1164 private static function updateCatalogStoreAmount(array $quantityList): Sale\Result
1165 {
1166 $result = new Sale\Result();
1167 $resultList = array();
1168
1169 if (empty($quantityList))
1170 {
1171 return $result;
1172 }
1173
1174 foreach ($quantityList as $catalogStoreId => $amount)
1175 {
1176 $fields = [
1177 'AMOUNT' => $amount['AMOUNT'],
1178 ];
1179 if (isset($amount['QUANTITY_RESERVED']))
1180 {
1181 $fields['QUANTITY_RESERVED'] = $amount['QUANTITY_RESERVED'];
1182 }
1183
1184 $internalResult = Catalog\StoreProductTable::update($catalogStoreId, $fields);
1185
1186 $resultList[$catalogStoreId] = $internalResult->isSuccess();
1187 }
1188
1189 $result->setData(
1190 array(
1191 'AMOUNT_UPDATED_LIST' => $resultList,
1192 )
1193 );
1194
1195 return $result;
1196 }
1197
1204 private static function shipProduct(array $productData, array $productStoreDataList = array()): Sale\Result
1205 {
1206 $result = new Sale\Result();
1207
1208 $productId = $productData['PRODUCT_ID'];
1209
1210 $productQuantity = self::getTotalAmountFromQuantityList($productData);
1211
1212 $needShip = ($productQuantity < 0);
1213
1214 if (
1215 //Catalog\Config\State::isUsedInventoryManagement()
1216 $productData['PRODUCT']['USED_STORE_INVENTORY']
1217 )
1218 {
1219 if (empty($productStoreDataList) && $needShip)
1220 {
1221 $result->addError(
1222 new Sale\ResultError(
1223 Main\Localization\Loc::getMessage(
1224 "DDCT_DEDUCTION_STORE_ERROR",
1225 array_merge(
1226 self::getProductCatalogInfo($productId),
1227 array("#PRODUCT_ID#" => $productId)
1228 )
1229 ), "DDCT_DEDUCTION_STORE_ERROR"
1230 )
1231 );
1232 return $result;
1233 }
1234
1235 $setQuantityList = array();
1236 $r = static::getSetableStoreQuantityProduct($productData, $productStoreDataList);
1237 if ($r->isSuccess())
1238 {
1239 $resultData = $r->getData();
1240 if (!empty($resultData[Base::FLAT_QUANTITY_LIST]))
1241 {
1242 $setQuantityList = $resultData[Base::FLAT_QUANTITY_LIST];
1243 }
1244 }
1245 else
1246 {
1247 return $r;
1248 }
1249
1250 /*if (!$productData['PRODUCT']['USED_STORE_INVENTORY']) // product types without stores
1251 {
1252 $setQuantityList = [];
1253 } */
1254
1255 $r = static::updateCatalogStoreAmount($setQuantityList);
1256 if ($r->isSuccess())
1257 {
1258 $resultData = $r->getData();
1259 if (!empty($resultData['AMOUNT_UPDATED_LIST']))
1260 {
1261 foreach($resultData['AMOUNT_UPDATED_LIST'] as $catalogStoreIsUpdated)
1262 {
1263 if ($catalogStoreIsUpdated === true)
1264 {
1265 static::clearHitCache(self::CACHE_STORE_PRODUCT);
1266 if ($needShip)
1267 {
1268 $r = static::deleteBarcodes($productData);
1269 }
1270 else
1271 {
1272 $r = static::addBarcodes($productData);
1273 }
1274
1275 if (!$r->isSuccess())
1276 {
1277 $result->addErrors($r->getErrors());
1278 }
1279 }
1280 }
1281 }
1282 }
1283 else
1284 {
1285 return $r;
1286 }
1287
1288 return static::shipQuantityWithStoreControl($productData);
1289 }
1290 elseif (isset($productData["CATALOG"]))
1291 {
1292 if ($productData["CATALOG"]["QUANTITY_TRACE"] == "N")
1293 {
1294 return $result;
1295 }
1296 }
1297
1298 return static::shipQuantityWithoutStoreControl($productData);
1299
1300 }
1301
1307 private static function shipQuantityWithStoreControl(array $productData): Sale\Result
1308 {
1309 $result = new Sale\Result();
1310
1311 $productId = (int)$productData['PRODUCT_ID'];
1312
1313 $productQuantity = self::getTotalAmountFromQuantityList($productData);
1314
1315 $catalogData = $productData['CATALOG'];
1316
1317 $isExistsReserve = static::isExistsCommonStoreReserve($productData) && static::isReservationEnabled();
1318 $isNeedShip = ($productQuantity < 0);
1319
1320 $productQuantity = abs($productQuantity);
1321
1322 $fields = array();
1323
1324 $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED'];
1325 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData);
1326
1327 $sumCatalogQuantity = $catalogReservedQuantity + $catalogQuantity;
1328
1329 if ($isNeedShip)
1330 {
1331 if ($isExistsReserve)
1332 {
1333 if ($catalogReservedQuantity >= $productQuantity)
1334 {
1335 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity - $productQuantity;
1336 }
1337 elseif ($sumCatalogQuantity >= $productQuantity)
1338 {
1339 $fields["QUANTITY_RESERVED"] = 0;
1340 $fields["QUANTITY"] = $catalogQuantity - ($productQuantity - $catalogReservedQuantity);
1341 }
1342 else
1343 {
1344 $result->addError(
1345 new Sale\ResultError(
1346 Main\Localization\Loc::getMessage(
1347 "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT",
1348 array_merge(
1349 self::getProductCatalogInfo($productId),
1350 array("#PRODUCT_ID#" => $productId)
1351 )
1352 ), "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT"
1353 )
1354 );
1355 return $result;
1356 }
1357 }
1358 else
1359 {
1360 if ($productQuantity <= $catalogQuantity)
1361 {
1362 $fields["QUANTITY"] = $catalogQuantity - $productQuantity;
1363 }
1364 elseif ($productQuantity <= $sumCatalogQuantity)
1365 {
1366 $fields["QUANTITY"] = 0;
1367 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity - ($productQuantity - $catalogQuantity);
1368 }
1369 else
1370 {
1371 $result->addError(
1372 new Sale\ResultError(
1373 Main\Localization\Loc::getMessage(
1374 "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT",
1375 array_merge(
1376 self::getProductCatalogInfo($productId),
1377 array("#PRODUCT_ID#" => $productId)
1378 )
1379 ), "DDCT_DEDUCTION_NOT_ENOUGHT_QUANTITY_PRODUCT"
1380 )
1381 );
1382 return $result;
1383 }
1384 }
1385 }
1386 else
1387 {
1388 if ($isExistsReserve)
1389 {
1390 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity;
1391 }
1392 else
1393 {
1394 $fields["QUANTITY"] = $catalogQuantity + $productQuantity;
1395 }
1396 }
1397
1398 if (!$productData['PRODUCT']['USED_RESERVATION'])
1399 {
1400 if (isset($fields['QUANTITY_RESERVED']))
1401 {
1402 unset($fields['QUANTITY_RESERVED']);
1403 }
1404 }
1405
1406 $isUpdated = false;
1407 if (!empty($fields))
1408 {
1409 $internalResult = Catalog\Model\Product::update($productId, $fields);
1410
1411 if ($internalResult->isSuccess())
1412 {
1413 $isUpdated = true;
1414 $quantityValues = array();
1415
1416 if (isset($fields['QUANTITY']))
1417 {
1418 $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY'];
1419 QuantityControl::resetAvailableQuantity($productId);
1420 }
1421
1422 if (isset($fields['QUANTITY_RESERVED']))
1423 {
1424 $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED'];
1425 }
1426
1427 if (!empty($quantityValues))
1428 {
1429 QuantityControl::setValues($productId, $quantityValues);
1430 }
1431 }
1432 else
1433 {
1434 self::convertErrors($internalResult);
1435 }
1436 unset($internalResult);
1437 }
1438
1439 $result->setData(
1440 array(
1441 'IS_UPDATED' => $isUpdated,
1442 )
1443 );
1444
1445 return $result;
1446 }
1447
1453 private static function shipQuantityWithoutStoreControl(array $productData): Sale\Result
1454 {
1455 $result = new Sale\Result();
1456 $productId = (int)$productData['PRODUCT_ID'];
1457
1458 $catalogData = $productData['CATALOG'];
1459
1460 $productQuantity = self::getTotalAmountFromQuantityList($productData);
1461
1462 $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED'];
1463 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData);
1464
1465 $fields = array();
1466
1467 $isExistsReserve = static::isExistsCommonStoreReserve($productData) && static::isReservationEnabled();
1468 $isNeedShip = ($productQuantity < 0);
1469
1470 if ($isNeedShip)
1471 {
1472 $productQuantity = abs($productQuantity);
1473 if (($productQuantity <= $catalogReservedQuantity + $catalogQuantity) || $catalogData["CAN_BUY_ZERO"] == "Y")
1474 {
1475 if ($isExistsReserve)
1476 {
1477 if ($productQuantity <= $catalogReservedQuantity)
1478 {
1479 $needReservedQuantity = $catalogReservedQuantity - $productQuantity;
1480 $fields["QUANTITY_RESERVED"] = $needReservedQuantity;
1481 }
1482 else
1483 {
1484 $fields["QUANTITY_RESERVED"] = 0;
1485 $fields["QUANTITY"] = $catalogQuantity - ($productQuantity - $catalogReservedQuantity);
1486 }
1487 }
1488 else
1489 {
1490 $fields["QUANTITY"] = $catalogQuantity - $productQuantity;
1491 }
1492
1493 }
1494 else //not enough products - don't deduct anything
1495 {
1496 $result->addError(
1497 new Sale\ResultError(
1498 Main\Localization\Loc::getMessage(
1499 "DDCT_DEDUCTION_QUANTITY_ERROR",
1500 array_merge(
1501 self::getProductCatalogInfo($productId),
1502 array("#PRODUCT_ID#" => $productId)
1503 )
1504 ), "DDCT_DEDUCTION_QUANTITY_ERROR"
1505 )
1506 );
1507 }
1508 }
1509 else
1510 {
1511 if ($isExistsReserve)
1512 {
1513 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity;
1514 }
1515 else
1516 {
1517 $fields["QUANTITY"] = $catalogQuantity + $productQuantity;
1518 }
1519 }
1520
1521 if (!$productData['PRODUCT']['USED_RESERVATION'])
1522 {
1523 if (isset($fields['QUANTITY_RESERVED']))
1524 {
1525 unset($fields['QUANTITY_RESERVED']);
1526 }
1527 }
1528
1529 if (!empty($fields))
1530 {
1531 $internalResult = Catalog\Model\Product::update($productId, $fields);
1532
1533 if ($internalResult-> isSuccess())
1534 {
1535 $quantityValues = array();
1536
1537 if (isset($fields['QUANTITY']))
1538 {
1539 $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY'];
1540 QuantityControl::resetAvailableQuantity($productId);
1541 }
1542
1543 if (isset($fields['QUANTITY_RESERVED']))
1544 {
1545 $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED'];
1546 }
1547
1548 if (!empty($quantityValues))
1549 {
1550 QuantityControl::setValues($productId, $quantityValues);
1551 }
1552 }
1553 else
1554 {
1555 self::convertErrors($internalResult);
1556 }
1557 unset($internalResult);
1558 }
1559
1560 return $result;
1561 }
1562
1563 private static function isExistsCommonStoreReserve(array $productData): bool
1564 {
1565 if (
1566 empty($productData['NEED_RESERVE_BY_STORE_LIST'])
1567 || !is_array($productData['NEED_RESERVE_BY_STORE_LIST'])
1568 )
1569 {
1570 return false;
1571 }
1572
1573 foreach ($productData['NEED_RESERVE_BY_STORE_LIST'] as $block)
1574 {
1575 if (empty($block) || !is_array($block))
1576 {
1577 continue;
1578 }
1579 if (in_array(true, $block, true))
1580 {
1581 return true;
1582 }
1583 }
1584
1585 return false;
1586 }
1587
1594 private static function getSetableStoreQuantityProduct(array $productData, array $productStoreDataList): Sale\Result
1595 {
1596 $result = new Sale\Result();
1597
1598 $setQuantityList = array();
1599 $productQuantity = self::getTotalAmountFromQuantityList($productData);
1600 $isNeedShip = ($productQuantity < 0);
1601
1602 $quantityByStore = self::getQuantityDataFromStore($productData);
1603 $needQuantityList = $quantityByStore['AMOUNT'];
1604
1605 if (empty($needQuantityList))
1606 {
1607 $autoShipStore = static::getAutoShipStoreData($productData, $productStoreDataList);
1608
1609 if (!empty($autoShipStore))
1610 {
1611 $needQuantityList[$autoShipStore['STORE_ID']] = ($productQuantity > $autoShipStore['AMOUNT'] ? $autoShipStore['AMOUNT'] : abs($productQuantity));
1612
1613 $shipmentItemList = $productData['SHIPMENT_ITEM_LIST'];
1615 foreach ($shipmentItemList as $index => $shipmentItem)
1616 {
1617 $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection();
1618 if ($shipmentItemStoreCollection && $shipmentItemStoreCollection->count() === 0)
1619 {
1620 $item = $shipmentItemStoreCollection->createItem($shipmentItem->getBasketItem());
1621 $item->setField('STORE_ID', $autoShipStore['STORE_ID']);
1622 $item->setField('QUANTITY', abs($productData['SHIPMENT_ITEM_QUANTITY_LIST'][$index]));
1623 }
1624 }
1625 }
1626 }
1627
1628 if (!empty($productStoreDataList))
1629 {
1630 $isReservationEnabled = Main\Config\Option::get("sale", "product_reserve_condition") != "S";
1631 $compileReserve = self::getCompileReserve($productData);
1632 foreach ($productStoreDataList as $storeId => $productStoreData)
1633 {
1634 $productId = $productStoreData['PRODUCT_ID'];
1635
1636 if ($isNeedShip && (isset($needQuantityList[$storeId]) && $productStoreData['AMOUNT'] < $needQuantityList[$storeId]))
1637 {
1638 $result->addError(
1639 new Sale\ResultError(
1640 Main\Localization\Loc::getMessage(
1641 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2',
1642 array_merge(
1643 self::getProductCatalogInfo($productId),
1644 [
1645 '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($storeId),
1646 '#STORE_ID#' => $storeId,
1647 '#PRODUCT_ID#' => $productId,
1648 ]
1649 )
1650 ), 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR'
1651 )
1652 );
1653 }
1654 else
1655 {
1656 $storeConfig = self::getUpdateStoreConfig(
1657 $storeId,
1658 $needQuantityList,
1659 $compileReserve,
1660 [
1661 'RESERVATION_ENABLED' => $isReservationEnabled,
1662 ]
1663 );
1664 if (!$storeConfig['AMOUNT'] && !$storeConfig['QUANTITY_RESERVED'])
1665 {
1666 continue;
1667 }
1668
1669 $storeUpdate = [];
1670 if ($storeConfig['AMOUNT'])
1671 {
1672 $setQuantity = $productQuantity;
1673
1674 if (isset($needQuantityList[$storeId]))
1675 {
1676 $setQuantity = ($isNeedShip ? -1 : 1) * $needQuantityList[$storeId];
1677 }
1678
1679 $storeUpdate['AMOUNT'] = $productStoreData['AMOUNT'] + $setQuantity;
1680 $storeUpdate['DELTA'] = $setQuantity;
1681 $storeUpdate['OLD_AMOUNT'] = $productStoreData['AMOUNT'];
1682 unset($setQuantity);
1683 }
1684 if ($storeConfig['QUANTITY_RESERVED'])
1685 {
1686 $setReserveQuantity = 0;
1687 if (isset($needQuantityList[$storeId]))
1688 {
1689 $setReserveQuantity = ($isNeedShip ? -1 : 1) * $needQuantityList[$storeId];
1690 }
1691 if (isset($quantityByStore['QUANTITY_RESERVED'][$storeId]))
1692 {
1693 $setReserveQuantity = ($isNeedShip ? -1 : 1) * $quantityByStore['QUANTITY_RESERVED'][$storeId];
1694 }
1695 if ($setReserveQuantity != 0)
1696 {
1697 $storeUpdate['QUANTITY_RESERVED'] = $productStoreData['QUANTITY_RESERVED']
1698 + $setReserveQuantity;
1699 $storeUpdate['OLD_QUANTITY_RESERVED'] = $productStoreData['QUANTITY_RESERVED'];
1700 $storeUpdate['QUANTITY_RESERVED_DELTA'] = $setReserveQuantity;
1701 }
1702 unset($setReserveQuantity);
1703 }
1704 if (!empty($storeUpdate))
1705 {
1706 $setQuantityList[$productStoreData['ID']] = $storeUpdate;
1707 }
1708 unset($storeUpdate, $storeConfig);
1709 }
1710 }
1711 }
1712
1713 if (!empty($setQuantityList))
1714 {
1715 $result->addData(
1716 array(
1717 Base::FLAT_QUANTITY_LIST => $setQuantityList,
1718 )
1719 );
1720 }
1721
1722 return $result;
1723 }
1724
1725 private static function getUpdateStoreConfig(int $storeId, array $quantityList, array $reserveList, array $config): array
1726 {
1727 $result = [
1728 'AMOUNT' => isset($quantityList[$storeId]),
1729 'QUANTITY_RESERVED' => false,
1730 ];
1731
1732 if ($config['RESERVATION_ENABLED'])
1733 {
1734 $result['QUANTITY_RESERVED'] = isset($reserveList[$storeId]);
1735 }
1736
1737 return $result;
1738 }
1739
1740 private static function getCompileReserve(array $product): array
1741 {
1742 if (empty($product['NEED_RESERVE_BY_STORE_LIST']) || !is_array($product['NEED_RESERVE_BY_STORE_LIST']))
1743 {
1744 return [];
1745 }
1746
1747 $result = [];
1748 foreach ($product['NEED_RESERVE_BY_STORE_LIST'] as $shipment)
1749 {
1750 if (empty($shipment) || !is_array($shipment))
1751 {
1752 continue;
1753 }
1754 foreach ($shipment as $storeId => $flag)
1755 {
1756 if ($flag === true)
1757 {
1758 $result[$storeId] = true;
1759 }
1760 }
1761 }
1762
1763 return $result;
1764 }
1765
1766 private static function getQuantityDataFromStore(array $product): array
1767 {
1768 $result = [
1769 'AMOUNT' => [],
1770 'QUANTITY_RESERVED' => [],
1771 ];
1772
1773 $storeDataExists = (
1774 !empty($product['STORE_DATA_LIST'])
1775 && is_array($product['STORE_DATA_LIST'])
1776 );
1777 $reserveDataExists = (
1778 !empty($product[self::AMOUNT_SRC_STORE_RESERVED_LIST])
1779 && is_array($product[self::AMOUNT_SRC_STORE_RESERVED_LIST])
1780 );
1781 if (!$storeDataExists && !$reserveDataExists)
1782 {
1783 return $result;
1784 }
1785
1786 $found = false;
1787 if ($storeDataExists)
1788 {
1789 foreach ($product['STORE_DATA_LIST'] as $storeList)
1790 {
1791 if (!is_array($storeList))
1792 {
1793 continue;
1794 }
1795 $found = true;
1796 foreach ($storeList as $storeId => $store)
1797 {
1798 if (!isset($result['AMOUNT'][$storeId]))
1799 {
1800 $result['AMOUNT'][$storeId] = 0;
1801 }
1802 $result['AMOUNT'][$storeId] += (float)$store['QUANTITY'];
1803 if (isset($store['RESERVED_QUANTITY']))
1804 {
1805 if (!isset($result['QUANTITY_RESERVED'][$storeId]))
1806 {
1807 $result['QUANTITY_RESERVED'][$storeId] = 0;
1808 }
1809 $result['QUANTITY_RESERVED'][$storeId] += (float)$store['RESERVED_QUANTITY'];
1810 }
1811 }
1812 }
1813 }
1814
1815 if (!$found && $reserveDataExists)
1816 {
1817 switch (self::getQuantityFormat($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]))
1818 {
1819 case self::QUANTITY_FORMAT_STORE:
1820 $internalResult = self::calculateQuantityFromStores($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]);
1821 break;
1822 case self::QUANTITY_FORMAT_SHIPMENT:
1823 $internalResult = self::calculateQuantityFromShipments($product[self::AMOUNT_SRC_STORE_RESERVED_LIST]);
1824 break;
1825 default:
1826 $internalResult = null;
1827 break;
1828 }
1829 if ($internalResult !== null)
1830 {
1831 $result['QUANTITY_RESERVED'] = $internalResult;
1832 }
1833 unset($internalResult);
1834 }
1835
1836 return $result;
1837 }
1838
1844 private static function deleteBarcodes(array $productData): Sale\Result
1845 {
1846 $result = new Sale\Result();
1847
1848 $storeData = $productData['STORE_DATA_LIST'];
1849 if (!empty($storeData))
1850 {
1851 foreach ($storeData as $storeDataList)
1852 {
1853 foreach($storeDataList as $storeDataValue)
1854 {
1855 $r = static::deleteBarcode($storeDataValue);
1856 if (!$r->isSuccess())
1857 {
1858 $result->addErrors($r->getErrors());
1859 }
1860 }
1861 }
1862 }
1863
1864 return $result;
1865 }
1866
1872 private static function deleteBarcode(array $storeData): Sale\Result
1873 {
1874 $result = new Sale\Result();
1875
1876 $storeId = $storeData["STORE_ID"];
1877 $productId = $storeData["PRODUCT_ID"];
1878 $barcodeMulti = $storeData['IS_BARCODE_MULTI'];
1879
1880 $barcodeList = $storeData['BARCODE'];
1881
1882 foreach ($barcodeList as $barcodeValue)
1883 {
1884 if (trim($barcodeValue) == "" || !$barcodeMulti)
1885 {
1886 continue;
1887 }
1888
1889 $result = new Sale\Result();
1890 $barcodeFields = array(
1891 "STORE_ID" => $storeId,
1892 "BARCODE" => $barcodeValue,
1893 "PRODUCT_ID" => $productId,
1894 );
1895
1896 $dbres = \CCatalogStoreBarcode::GetList(
1897 array(),
1898 $barcodeFields,
1899 false,
1900 false,
1901 array("ID", "STORE_ID", "BARCODE", "PRODUCT_ID")
1902 );
1903
1904 $catalogStoreBarcodeRes = $dbres->Fetch();
1905 if ($catalogStoreBarcodeRes)
1906 {
1907 \CCatalogStoreBarcode::Delete($catalogStoreBarcodeRes["ID"]);
1908 }
1909 else
1910 {
1911 $result->addError(
1912 new Sale\ResultError(
1913 Main\Localization\Loc::getMessage(
1914 "DDCT_DEDUCTION_BARCODE_ERROR",
1915 array_merge(
1916 self::getProductCatalogInfo($productId),
1917 array("#BARCODE#" => $barcodeValue)
1918 )
1919 ), "DDCT_DEDUCTION_BARCODE_ERROR"
1920 )
1921 );
1922 }
1923 }
1924
1925 return $result;
1926 }
1927
1933 private static function addBarcodes(array $productData): Sale\Result
1934 {
1935 $result = new Sale\Result();
1936 $storeData = $productData['STORE_DATA_LIST'];
1937 if (!empty($storeData))
1938 {
1939 foreach ($storeData as $storeDataList)
1940 {
1941 foreach($storeDataList as $storeDataValue)
1942 {
1943 $r = static::addBarcode($storeDataValue);
1944 if (!$r->isSuccess())
1945 {
1946 $result->addErrors($r->getErrors());
1947 }
1948 }
1949 }
1950 }
1951
1952 return $result;
1953 }
1954
1960 private static function addBarcode(array $storeData): Sale\Result
1961 {
1962 $result = new Sale\Result();
1963
1964 $storeId = $storeData["STORE_ID"];
1965 $productId = $storeData["PRODUCT_ID"];
1966 $barcodeMulti = $storeData['IS_BARCODE_MULTI'];
1967
1968 $barcodeList = $storeData['BARCODE'];
1969
1970 foreach ($barcodeList as $barcodeValue)
1971 {
1972 if (trim($barcodeValue) == "" || !$barcodeMulti)
1973 {
1974 continue;
1975 }
1976
1977 $result = new Sale\Result();
1978 $barcodeFields = array(
1979 "STORE_ID" => $storeId,
1980 "BARCODE" => $barcodeValue,
1981 "PRODUCT_ID" => $productId,
1982 );
1983 \CCatalogStoreBarcode::Add($barcodeFields);
1984 }
1985
1986 return $result;
1987 }
1988
1994 public function reserve(array $products)
1995 {
1996 $result = new Sale\Result();
1997
1998 $resultList = array();
1999
2000 $availableItems = $this->createProductsListWithCatalogData($products);
2001 foreach ($availableItems as $productId => $productData)
2002 {
2003 $resultList[$productId] = false;
2004
2005 $r = static::reserveProduct($productData);
2006 if ($r->isSuccess())
2007 {
2008 $resultData = $r->getData();
2009 if (!empty($resultData))
2010 {
2011 $resultList[$productId] = $resultData;
2012 }
2013 }
2014 else
2015 {
2016 $result->addErrors($r->getErrors());
2017 }
2018
2019 }
2020
2021 $result->setData(array(
2022 'RESERVED_PRODUCTS_LIST' => $resultList,
2023 ));
2024
2025 return $result;
2026 }
2027
2033 private static function reserveProduct(array $productData): Sale\Result
2034 {
2035 if (
2036 !static::isReservationEnabled()
2037 || !$productData['PRODUCT']['USED_RESERVATION']
2038 )
2039 {
2040 return static::reserveQuantityWithDisabledReservation($productData);
2041 }
2042
2043 return self::reserveStoreQuantityWithEnabledReservation($productData);
2044
2045 }
2046
2052 private static function reserveStoreQuantityWithEnabledReservation(array $productData): Sale\Result
2053 {
2054 $result = new Sale\Result();
2055
2056 $enableStoreControl = Catalog\Config\State::isUsedInventoryManagement();
2057
2058 $resultFields = [];
2059 $fields = []; // fields for update products
2060 $storeFields = []; // rows for update reserve in store
2061 $needShipList = [];
2062
2063 $productId = $productData['PRODUCT_ID'];
2064 $storeProductQuantity = self::getStoreQuantityFromQuantityList($productData); // quantity with stores
2065 // empty store can't updated if used inventory managment
2066 if ($enableStoreControl && isset($storeProductQuantity[Base::EMPTY_STORE_ID]))
2067 {
2068 return $result;
2069 }
2070
2071 $productQuantity = self::getTotalAmountFromQuantityList($productData);
2072
2073 $isNeedReserve = ($productQuantity > 0);
2074 $catalogData = $productData['CATALOG'];
2075
2076 $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED'];
2077 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData);
2078
2079 $sumCatalogQuantity = $catalogQuantity + $catalogReservedQuantity;
2080
2081 if (isset($productData['NEED_SHIP']))
2082 {
2083 $needShipList = $productData['NEED_SHIP'];
2084 }
2085
2086 $setQuantityReserved = $catalogReservedQuantity;
2087
2088 if (!empty($needShipList) && !empty($productData['SHIPMENT_ITEM_DATA_LIST']))
2089 {
2090 $shipmentItemList = $productData['SHIPMENT_ITEM_DATA_LIST'];
2091 foreach ($needShipList as $shipmentItemIndex => $isNeedShip)
2092 {
2093 if ($setQuantityReserved <= 0)
2094 {
2095 $setQuantityReserved = 0;
2096 break;
2097 }
2098
2099 if ($isNeedShip === true)
2100 {
2101 $shipmentItemQuantity = $shipmentItemList[$shipmentItemIndex];
2102
2103 $setQuantityReserved -= $shipmentItemQuantity;
2104 }
2105 }
2106 }
2107
2108 if ($catalogData["QUANTITY_TRACE"] == "N")
2109 {
2110 $fields["QUANTITY_RESERVED"] = $setQuantityReserved;
2111 $resultFields['IS_UPDATED'] = true;
2112 $resultFields['QUANTITY_RESERVED'] = 0;
2113 }
2114 else
2115 {
2116 $resultFields['QUANTITY_RESERVED'] = $catalogReservedQuantity;
2117
2118 if (
2119 $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_PRODUCT
2120 || $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_OFFER
2121 )
2122 {
2123 $storeFields = $enableStoreControl
2124 ? self::loadCurrentStoreReserve($productId, $storeProductQuantity)
2125 : [];
2126 }
2127
2128 if ($isNeedReserve)
2129 {
2130 if ($catalogData["CAN_BUY_ZERO"] == "Y")
2131 {
2132 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity;
2133 $fields['QUANTITY'] = $catalogQuantity - $productQuantity;
2134 }
2135 else
2136 {
2137 if ($catalogQuantity >= $productQuantity)
2138 {
2139 $fields["QUANTITY"] = $catalogQuantity - $productQuantity;
2140 $fields["QUANTITY_RESERVED"] = $catalogReservedQuantity + $productQuantity;
2141 }
2142 else
2143 {
2144 $resultFields["QUANTITY_NOT_RESERVED"] = $productQuantity - $catalogQuantity;
2145
2146 $fields["QUANTITY"] = 0;
2147 $fields["QUANTITY_RESERVED"] = $sumCatalogQuantity;
2148
2149 $result->addWarning(
2150 new Sale\ResultWarning(
2151 Main\Localization\Loc::getMessage(
2152 "RSRV_QUANTITY_NOT_ENOUGH_ERROR",
2153 self::getProductCatalogInfo($productId)
2154 ), "ERROR_NOT_ENOUGH_QUANTITY"
2155 )
2156 );
2157 }
2158 }
2159 }
2160 else //undo reservation
2161 {
2162 $correctReserve = 0;
2163 if ($enableStoreControl)
2164 {
2165 foreach (array_keys($storeFields) as $storeId)
2166 {
2167 if ($storeFields[$storeId]['ID'] === null)
2168 {
2169 continue;
2170 }
2171 $storeProductFields = $storeFields[$storeId];
2172 $newReserve = $storeProductFields['QUANTITY_RESERVED'] + $storeProductFields['ADD_QUANTITY_RESERVED'];
2173 if ($newReserve < 0)
2174 {
2175 $correctReserve -= $newReserve;
2176 $storeFields[$storeId]['ADD_QUANTITY_RESERVED'] -= $newReserve;
2177 }
2178 }
2179 }
2180
2181 $needQuantity = abs($productQuantity) - $correctReserve;
2182
2183 $fields["QUANTITY"] = $catalogQuantity + $needQuantity;
2184
2185 $needReservedQuantity = $catalogReservedQuantity - $needQuantity;
2186 if ($needQuantity > $catalogReservedQuantity)
2187 {
2188 $needReservedQuantity = $catalogReservedQuantity;
2189 }
2190
2191 $fields["QUANTITY_RESERVED"] = $needReservedQuantity;
2192
2193 if ($enableStoreControl)
2194 {
2195 foreach (array_keys($storeFields) as $storeId)
2196 {
2197 if ($storeFields[$storeId]['ID'] === null)
2198 {
2199 unset($storeFields[$storeId]);
2200 }
2201 }
2202 }
2203 }
2204
2205 } //quantity trace
2206
2207 if (!$productData['PRODUCT']['USED_RESERVATION'])
2208 {
2209 if (isset($fields['QUANTITY_RESERVED']))
2210 {
2211 unset($fields['QUANTITY_RESERVED']);
2212 }
2213 }
2214
2215 if (!empty($fields) && is_array($fields))
2216 {
2217 $storeSuccess = true;
2218 if ($enableStoreControl)
2219 {
2220 foreach (array_keys($storeFields) as $index)
2221 {
2222 if ($index === Base::EMPTY_STORE_ID)
2223 {
2224 $storeSuccess = false;
2225 }
2226 else
2227 {
2228 $storeProductFields = $storeFields[$index];
2229 $id = $storeProductFields['ID'];
2230 $storeProductFields['QUANTITY_RESERVED'] += $storeProductFields['ADD_QUANTITY_RESERVED'];
2231 unset($storeProductFields['ID'], $storeProductFields['ADD_QUANTITY_RESERVED']);
2232 if ($id === null)
2233 {
2234 $storeProductFields['AMOUNT'] = 0;
2235 $internalResult = Catalog\StoreProductTable::add($storeProductFields);
2236 }
2237 else
2238 {
2239 unset($storeProductFields['STORE_ID'], $storeProductFields['PRODUCT_ID']);
2240 $internalResult = Catalog\StoreProductTable::update($id, $storeProductFields);
2241 }
2242 if ($internalResult->isSuccess())
2243 {
2244 $storeFields[$index]['ID'] = (int)$internalResult->getId();
2245 }
2246 else
2247 {
2248 $storeFields[$index]['ERROR'] = true;
2249 $storeFields[$index]['ERROR_MESSAGES'] = $internalResult->getErrorMessages();
2250 $storeSuccess = false;
2251 }
2252 }
2253 if (!$storeSuccess)
2254 {
2255 break;
2256 }
2257 }
2258 }
2259
2260 if (!$storeSuccess)
2261 {
2262 return $result;
2263 }
2264
2265 $resultFields['IS_UPDATED'] = false;
2266 $internalResult = Catalog\Model\Product::update($productId, $fields);
2267 if ($internalResult->isSuccess())
2268 {
2269 $resultFields['IS_UPDATED'] = true;
2270 $quantityValues = array();
2271 if (isset($fields['QUANTITY']))
2272 {
2273 $quantityValues[QuantityControl::QUANTITY] = $fields['QUANTITY'];
2274 QuantityControl::resetAvailableQuantity($productId);
2275 }
2276
2277 if (isset($fields['QUANTITY_RESERVED']))
2278 {
2279 $quantityValues[QuantityControl::RESERVED_QUANTITY] = $fields['QUANTITY_RESERVED'];
2280 }
2281
2282 if (!empty($quantityValues))
2283 {
2284 QuantityControl::setValues($productId, $quantityValues);
2285 }
2286 }
2287 else
2288 {
2289 self::convertErrors($internalResult);
2290 }
2291 unset($internalResult);
2292 }
2293
2294 if (isset($resultFields['IS_UPDATED']))
2295 {
2296 if (isset($fields['QUANTITY_RESERVED']))
2297 {
2298 $needReserved = $fields["QUANTITY_RESERVED"] - $resultFields['QUANTITY_RESERVED'];
2299 if ($resultFields['QUANTITY_RESERVED'] > $fields["QUANTITY_RESERVED"])
2300 {
2301 $needReserved = $fields["QUANTITY_RESERVED"];
2302 }
2303 $resultFields["QUANTITY_RESERVED"] = $needReserved;
2304 }
2305
2306 if (!empty($resultFields))
2307 {
2308 $result->setData($resultFields);
2309 }
2310 }
2311
2312 return $result;
2313 }
2314
2320 private static function reserveQuantityWithDisabledReservation(array $productData): Sale\Result
2321 {
2322 $result = new Sale\Result();
2323
2324 $catalogData = $productData['CATALOG'];
2325
2326 $isQuantityTrace = $catalogData["QUANTITY_TRACE"] == 'Y';
2327
2328 $productQuantity = self::getTotalAmountFromQuantityList($productData);
2329 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData);
2330
2331 $isUpdated = true;
2332
2333 $fields = array(
2334 'QUANTITY' => $catalogQuantity,
2335 );
2336
2337 if ($isQuantityTrace)
2338 {
2339 $productId = $productData['PRODUCT_ID'];
2340 $fields['QUANTITY'] -= $productQuantity;
2341 if ($catalogData["CAN_BUY_ZERO"] != "Y" && ($catalogQuantity < $productQuantity))
2342 {
2343 $result->addWarning(
2344 new Sale\ResultWarning(
2345 Main\Localization\Loc::getMessage(
2346 "RESERVE_QUANTITY_NOT_ENOUGH_ERROR",
2347 array_merge(
2348 self::getProductCatalogInfo($productId),
2349 array("#PRODUCT_ID#" => $productId)
2350 )
2351 ), "RESERVE_QUANTITY_NOT_ENOUGH_ERROR"
2352 )
2353 );
2354
2355 $fields['QUANTITY'] = 0;
2356 }
2357
2358 $internalResult = Catalog\Model\Product::update($productId, $fields);
2359 if (!$internalResult->isSuccess())
2360 {
2361 $isUpdated = false;
2362 self::convertErrors($internalResult);
2363 }
2364 unset($internalResult);
2365 }
2366
2367 if ($isUpdated)
2368 {
2369 $result->setData($fields);
2370 }
2371
2372 return $result;
2373 }
2374
2383 private static function checkParentActivity(array $productIds, int $iblockId = 0): array
2384 {
2385 $resultList = array();
2386
2387 $productIdsToLoad = array();
2388
2389 foreach ($productIds as $productId)
2390 {
2391 $cacheKey = $productId.'|'.$iblockId;
2392
2393 if (static::isExistsHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey))
2394 {
2395 if (static::getHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey) === 'Y')
2396 {
2397 $resultList[] = $productId;
2398 }
2399 }
2400 else
2401 {
2402 $productIdsToLoad[] = $productId;
2403 }
2404 }
2405
2406 if (!empty($productIdsToLoad))
2407 {
2408 $productToOfferMap = array();
2409 $parentIds = array();
2410
2411 $cacheResult = array_fill_keys($productIdsToLoad, 'N');
2412
2413 $productList = \CCatalogSku::getProductList($productIdsToLoad);
2414 if (!empty($productList))
2415 {
2416 foreach ($productList as $offerId => $productInfo)
2417 {
2418 $productToOfferMap[$productInfo['ID']][] = $offerId;
2419 $parentIds[] = $productInfo['ID'];
2420 }
2421
2422 $itemList = \CIBlockElement::GetList(
2423 array(),
2424 array(
2425 'ID' => array_unique($parentIds),
2426 'IBLOCK_ID' => $iblockId,
2427 'ACTIVE' => 'Y',
2428 'ACTIVE_DATE' => 'Y',
2429 'CHECK_PERMISSIONS' => 'N',
2430 ),
2431 false,
2432 false,
2433 array('ID')
2434 );
2435 while ($item = $itemList->Fetch())
2436 {
2437 if (!empty($productToOfferMap[$item['ID']]))
2438 {
2439 foreach ($productToOfferMap[$item['ID']] as $productId)
2440 {
2441 $cacheResult[$productId] = 'Y';
2442 $resultList[] = $productId;
2443 }
2444 }
2445 }
2446 }
2447
2448 foreach ($cacheResult as $productId => $value)
2449 {
2450 static::setHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $productId.'|'.$iblockId, $value);
2451 }
2452 }
2453
2454 return $resultList;
2455 }
2456
2462 protected static function getPriceTitle($priceType)
2463 {
2464 $priceType = (int)$priceType;
2465 if ($priceType <= 0)
2466 return '';
2467 if (!isset(self::$priceTitleCache[$priceType]))
2468 {
2469 self::$priceTitleCache[$priceType] = '';
2470 $priceTypeList = Catalog\GroupTable::getTypeList();
2471 if (isset($priceTypeList[$priceType]))
2472 {
2473 $groupName = (string)$priceTypeList[$priceType]['NAME_LANG'];
2474 self::$priceTitleCache[$priceType] = ($groupName != '' ? $groupName : $priceTypeList[$priceType]['NAME']);
2475 unset($groupName);
2476 }
2477 unset($priceTypeList);
2478 }
2479 return self::$priceTitleCache[$priceType];
2480 }
2481
2487 public function tryShip(array $products)
2488 {
2489 $result = new Sale\Result();
2490 $resultList = array();
2491
2492 $filteredProducts = $this->createQuantityFilteredProducts($products);
2493
2494 if (empty($filteredProducts))
2495 {
2496 $result->setData(
2497 array(
2498 'TRY_SHIP_PRODUCTS_LIST' => array_fill_keys(array_keys($products), true),
2499 )
2500 );
2501
2502 return $result;
2503 }
2504
2505 $availableItems = $this->createProductsListWithCatalogData($filteredProducts);
2506 if (empty($availableItems))
2507 {
2508 $productIdList = array_keys($products);
2509 foreach($productIdList as $productId)
2510 {
2511 $result->addError(
2512 new Sale\ResultError(
2513 Main\Localization\Loc::getMessage(
2514 "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE",
2515 array_merge(
2516 self::getProductCatalogInfo($productId),
2517 array("#PRODUCT_ID#" => $productId)
2518 )
2519 ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE"
2520 )
2521 );
2522 }
2523
2524 $result->setData(
2525 array(
2526 'TRY_SHIP_PRODUCTS_LIST' => array_fill_keys($productIdList, false),
2527 )
2528 );
2529
2530 return $result;
2531 }
2532 else
2533 {
2534 foreach ($availableItems as $productId => $productData)
2535 {
2536 $messageId = null;
2537 if (
2538 isset($productData['PRODUCT']['TYPE'])
2539 && $productData['PRODUCT']['TYPE'] === Catalog\ProductTable::TYPE_SERVICE
2540 )
2541 {
2542 if (
2543 (!isset($productData['CATALOG']['ACTIVE']) || $productData['CATALOG']['ACTIVE'] !== 'Y')
2544 || (!isset($productData['PRODUCT']['AVAILABLE']) || $productData['PRODUCT']['AVAILABLE'] !== 'Y')
2545 )
2546 {
2547 $messageId = 'SALE_PROVIDER_PRODUCT_SERVICE_NOT_AVAILABLE';
2548 }
2549 }
2550 else
2551 {
2552 if (!isset($productData['CATALOG']['ACTIVE']) || $productData['CATALOG']['ACTIVE'] !== 'Y')
2553 {
2554 $messageId = 'SALE_PROVIDER_PRODUCT_NOT_AVAILABLE';
2555 }
2556 }
2557 if ($messageId !== null)
2558 {
2559 $result->addError(
2560 new Sale\ResultError(
2561 Main\Localization\Loc::getMessage(
2562 $messageId,
2563 array_merge(
2564 self::getProductCatalogInfo($productId),
2565 array("#PRODUCT_ID#" => $productId)
2566 )
2567 ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE"
2568 )
2569 );
2570
2571 $resultList[$productId] = false;
2572 unset($availableItems[$productId]);
2573 }
2574 }
2575 }
2576
2577 if (!empty($availableItems))
2578 {
2579 if (Catalog\Config\State::isUsedInventoryManagement())
2580 {
2581 $r = $this->checkProductsInStore($availableItems);
2582 if ($r->isSuccess())
2583 {
2584 $data = $r->getData();
2585 if (!empty($data['PRODUCTS_LIST_IN_STORE']))
2586 {
2587 $resultList = $resultList + $data['PRODUCTS_LIST_IN_STORE'];
2588 }
2589 }
2590 else
2591 {
2592 $result->addErrors($r->getErrors());
2593 }
2594 }
2595 else
2596 {
2597 $r = $this->checkProductsQuantity($availableItems);
2598 if ($r->isSuccess())
2599 {
2600 $data = $r->getData();
2601 if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY']))
2602 {
2603 $resultList = $resultList + $data['PRODUCTS_LIST_REQUIRED_QUANTITY'];
2604 }
2605 }
2606 else
2607 {
2608 $result->addErrors($r->getErrors());
2609 }
2610
2611 }
2612 }
2613
2614 if (!empty($resultList))
2615 {
2616 $result->setData(
2617 array(
2618 'TRY_SHIP_PRODUCTS_LIST' => $resultList,
2619 )
2620 );
2621 }
2622
2623 return $result;
2624 }
2625
2631 public function isNeedShip(array $products)
2632 {
2633 $result = new Sale\Result();
2634
2635 $result->setData(
2636 array(
2637 'IS_NEED_SHIP' => static::isReservationEnabled(),
2638 )
2639 );
2640 return $result;
2641 }
2642
2648 private function createQuantityFilteredProducts(array $products): array
2649 {
2650 $resultList = array();
2651 foreach ($products as $productId => $productData)
2652 {
2653 $resultList[$productId] = $productData;
2654 if (array_key_exists('QUANTITY', $productData))
2655 {
2656 if ($productData['QUANTITY'] > 0)
2657 {
2658 unset($resultList[$productId]);
2659 }
2660 else
2661 {
2662 $resultList[$productId] *= -1;
2663 }
2664 }
2665 elseif (!empty($productData[Base::FLAT_QUANTITY_LIST]))
2666 {
2667 foreach ($productData[Base::FLAT_QUANTITY_LIST] as $basketCode => $quantity)
2668 {
2669 if ($quantity > 0)
2670 {
2671 unset($resultList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode]);
2672 }
2673 else
2674 {
2675 $resultList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode] *= -1;
2676 }
2677 }
2678
2679 if (empty($resultList[$productId][Base::FLAT_QUANTITY_LIST]))
2680 {
2681 unset($resultList[$productId]);
2682 }
2683 }
2684 }
2685
2686 return $resultList;
2687 }
2688
2694 public function tryUnship(array $products)
2695 {
2696 $result = new Sale\Result();
2697 $resultList = array();
2698 $availableItems = $this->createProductsListWithCatalogData($products);
2699
2700 if (Catalog\Config\State::isUsedInventoryManagement())
2701 {
2702 $r = $this->checkProductsInStore($availableItems);
2703 if ($r->isSuccess())
2704 {
2705 $data = $r->getData();
2706 if (!empty($data['PRODUCTS_LIST_IN_STORE']))
2707 {
2708 $resultList = $data['PRODUCTS_LIST_IN_STORE'];
2709 }
2710 }
2711 }
2712 else
2713 {
2714 $r = $this->checkProductsQuantity($availableItems);
2715 if ($r->isSuccess())
2716 {
2717 $data = $r->getData();
2718 if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY']))
2719 {
2720 $resultList = $data['PRODUCTS_LIST_REQUIRED_QUANTITY'];
2721 }
2722 }
2723 else
2724 {
2725 return $r;
2726 }
2727
2728 }
2729
2730 if (!empty($resultList))
2731 {
2732 $result->setData(array(
2733 'PRODUCTS_LIST_SHIPPED' => $resultList,
2734 ));
2735 }
2736
2737 return $result;
2738 }
2739
2745 public function checkProductsInStore(array $products)
2746 {
2747 $result = new Sale\Result();
2748 $resultList = array();
2749
2750 $r = $this->checkProductInStores($products);
2751 if (!$r->isSuccess())
2752 {
2753 return $r;
2754 }
2755
2756 $storeProductMap = $this->createStoreProductMap($products);
2757
2758 $r = $this->checkExistsProductsInStore($products, $storeProductMap);
2759 if ($r->isSuccess())
2760 {
2761 $data = $r->getData();
2762 if (!empty($data['PRODUCTS_LIST_EXISTS_IN_STORE']))
2763 {
2764 $resultList = $data['PRODUCTS_LIST_EXISTS_IN_STORE'];
2765 }
2766 }
2767 else
2768 {
2769 return $r;
2770 }
2771
2772 $r = $this->checkProductQuantityInStore($products);
2773 if ($r->isSuccess())
2774 {
2775 $data = $r->getData();
2776 if (!empty($data['PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE']))
2777 {
2778 $resultList = $data['PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE'];
2779 }
2780 }
2781 else
2782 {
2783 $result->addErrors($r->getErrors());
2784 }
2785
2786 if (!empty($resultList))
2787 {
2788 $result->addData(
2789 array(
2790 'PRODUCTS_LIST_IN_STORE' => $resultList,
2791 )
2792 );
2793 }
2794
2795 return $result;
2796 }
2797
2803 private function checkProductsQuantity(array $products): Sale\Result
2804 {
2805 $result = new Sale\Result();
2806
2807 $resultList = array();
2808 $availableQuantityList = array();
2809 $r = $this->getAvailableQuantity($products);
2810 if ($r->isSuccess())
2811 {
2812 $resultData = $r->getData();
2813 if (!empty($resultData['AVAILABLE_QUANTITY_LIST']))
2814 {
2815 $availableQuantityList = $resultData['AVAILABLE_QUANTITY_LIST'];
2816 }
2817
2818 }
2819 else
2820 {
2821 return $r;
2822 }
2823
2824 $enabledReservation = static::isReservationEnabled();
2825
2826 foreach ($products as $productId => $productData)
2827 {
2828 if (empty($productData['CATALOG']))
2829 {
2830 $resultList[$productId] = false;
2831 $result->addError(
2832 new Sale\ResultError(
2833 Main\Localization\Loc::getMessage(
2834 "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE",
2835 array_merge(
2836 self::getProductCatalogInfo($productId),
2837 array("#PRODUCT_ID#" => $productId)
2838 )
2839 ), "SALE_PROVIDER_PRODUCT_NOT_AVAILABLE"
2840 )
2841 );
2842
2843 continue;
2844 }
2845
2846 $resultList[$productId] = true;
2847 $catalogData = $productData['CATALOG'];
2848
2849
2850 if ($catalogData["CHECK_QUANTITY"])
2851 {
2852 $productQuantity = self::getTotalAmountFromQuantityList($productData);
2853
2854 $availableQuantity = 0;
2855
2856 if (isset($availableQuantityList[$productId]))
2857 {
2858 $availableQuantity = $availableQuantityList[$productId];
2859 }
2860
2861 $availableQuantity += (float)$catalogData['QUANTITY_RESERVED'];
2862
2863 if ($enabledReservation && $productQuantity > $availableQuantity)
2864 {
2865 $resultList[$productId] = false;
2866 $result->addError(
2867 new Sale\ResultError(
2868 Main\Localization\Loc::getMessage(
2869 "DDCT_DEDUCTION_QUANTITY_ERROR",
2870 array_merge(
2871 self::getProductCatalogInfo($productId),
2872 array("#PRODUCT_ID#" => $productId)
2873 )
2874 ), "DDCT_DEDUCTION_QUANTITY_ERROR"
2875 )
2876 );
2877 }
2878 }
2879 }
2880
2881 if (!empty($resultList))
2882 {
2883 $result->setData(
2884 array(
2885 'PRODUCTS_LIST_REQUIRED_QUANTITY' => $resultList,
2886 )
2887 );
2888 }
2889
2890 return $result;
2891 }
2892
2898 private function createProductsListWithCatalogData(array $products): array
2899 {
2900 $productDataList = array();
2901 $productIdList = array_fill_keys(array_keys($products), true);
2902 $r = $this->getData($products, [self::USE_GATALOG_DATA, 'FULL_QUANTITY']);
2903 if ($r->isSuccess())
2904 {
2905 $data = $r->getData();
2906 if (!empty($data[static::RESULT_CATALOG_LIST]))
2907 {
2908 $productDataList = $data[static::RESULT_CATALOG_LIST];
2909 }
2910 }
2911
2912 $resultList = array();
2913 $availableListId = array_intersect_key($productIdList, $productDataList);
2914 if (!empty($availableListId))
2915 {
2916 foreach (array_keys($availableListId) as $productId)
2917 {
2918 if (empty($productDataList[$productId]) || !is_array($productDataList[$productId]))
2919 {
2920 continue;
2921 }
2922 $resultList[$productId] = $products[$productId];
2923 $resultList[$productId]['PRODUCT'] = $productDataList[$productId]['PRODUCT'];
2924 unset($productDataList[$productId]['PRODUCT']);
2925 $resultList[$productId]['CATALOG'] = $productDataList[$productId];
2926 }
2927 }
2928
2929 return $resultList;
2930 }
2931
2937 protected function createStoreProductMap(array $products)
2938 {
2939 $productStoreDataList = array();
2940 $r = $this->getProductListStores($products);
2941 if ($r->isSuccess())
2942 {
2943 $data = $r->getData();
2944 if (!empty($data['PRODUCT_STORES_LIST']))
2945 {
2946 $productStoreDataList = $data['PRODUCT_STORES_LIST'];
2947 }
2948 }
2949
2950 $canAutoShipList = array();
2951 $r = $this->canProductListAutoShip($products);
2952 if ($r->isSuccess())
2953 {
2954 $data = $r->getData();
2955 if (!empty($data['PRODUCT_CAN_AUTOSHIP_LIST']))
2956 {
2957 $canAutoShipList = $data['PRODUCT_CAN_AUTOSHIP_LIST'];
2958 }
2959 }
2960
2961 $storeProductList = array();
2962 foreach ($products as $productId => $productData)
2963 {
2964 if (!empty($productData['STORE_DATA_LIST']) && static::isExistsBarcode($productData['STORE_DATA_LIST']))
2965 {
2966 $storeProductList[$productId] = $productData['STORE_DATA_LIST'];
2967 }
2968 elseif (!empty($canAutoShipList[$productId]) && !empty($productStoreDataList[$productId]))
2969 {
2970 $productQuantity = self::getTotalAmountFromQuantityList($productData);
2971 foreach ($productData['SHIPMENT_ITEM_DATA_LIST'] as $shipmentItemIndex => $shipmentItemQuantity)
2972 {
2973 foreach ($productStoreDataList[$productId] as $productStoreData)
2974 {
2975 $storeId = $productStoreData['STORE_ID'];
2976 $storeProductList[$productId][$shipmentItemIndex][$storeId] = array(
2977 'PRODUCT_ID' => $productId,
2978 'STORE_ID' => $storeId,
2979 'IS_BARCODE_MULTI' => false,
2980 'QUANTITY' => abs($productQuantity),
2981 );
2982 }
2983 }
2984 }
2985 }
2986
2987 return $storeProductList;
2988 }
2989
2990 private function checkProductInStores($products): Sale\Result
2991 {
2992 $result = new Sale\Result();
2993 $productStoreDataList = array();
2994 $canAutoShipList = array();
2995 $r = $this->canProductListAutoShip($products);
2996 if ($r->isSuccess())
2997 {
2998 $data = $r->getData();
2999 if (!empty($data['PRODUCT_CAN_AUTOSHIP_LIST']))
3000 {
3001 $canAutoShipList = $data['PRODUCT_CAN_AUTOSHIP_LIST'];
3002 }
3003 }
3004
3005 $r = $this->getProductListStores($products);
3006 if ($r->isSuccess())
3007 {
3008 $data = $r->getData();
3009 if (!empty($data['PRODUCT_STORES_LIST']))
3010 {
3011 $productStoreDataList = $data['PRODUCT_STORES_LIST'];
3012 }
3013 }
3014
3015 foreach ($products as $productId => $productData)
3016 {
3017 if (!empty($productData['STORE_DATA_LIST']))
3018 {
3019 if (!static::isExistsBarcode($productData['STORE_DATA_LIST']))
3020 {
3021 $result->addError(
3022 new Sale\ResultError(
3023 Main\Localization\Loc::getMessage(
3024 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", self::getProductCatalogInfo($productId)
3025 ), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY"
3026 )
3027 );
3028 }
3029 }
3030 elseif ($canAutoShipList[$productId] === false)
3031 {
3032 if (!isset($productStoreDataList[$productId]))
3033 {
3034 $result->addError(
3035 new Sale\ResultError(
3036 Main\Localization\Loc::getMessage(
3037 "DDCT_DEDUCTION_STORE_EMPTY_ERROR",
3038 self::getProductCatalogInfo($productId)
3039 ), "DEDUCTION_STORE_ERROR1"
3040 )
3041 );
3042 }
3043 elseif (count($productStoreDataList[$productId]) > 1)
3044 {
3045 $result->addError(
3046 new Sale\ResultError(
3047 Main\Localization\Loc::getMessage(
3048 "DDCT_DEDUCTION_STORE_ERROR",
3049 self::getProductCatalogInfo($productId)
3050 ), "DEDUCTION_STORE_ERROR1"
3051 )
3052 );
3053 }
3054 }
3055 }
3056
3057 return $result;
3058
3059 }
3060
3061 private static function isExistsBarcode(array $list): bool
3062 {
3063 $resultValue = false;
3064 foreach ($list as $storeDataList)
3065 {
3066 foreach ($storeDataList as $storeValue)
3067 {
3068 if (is_array($storeValue['BARCODE']) && $storeValue['IS_BARCODE_MULTI'] === true)
3069 {
3070 foreach ($storeValue["BARCODE"] as $barcodeValue)
3071 {
3072 if (trim($barcodeValue) == "")
3073 {
3074 return $resultValue;
3075 }
3076 }
3077
3078 $resultValue = true;
3079
3080 }
3081 else
3082 {
3083 return (!empty($storeValue['BARCODE']));
3084 }
3085
3086 }
3087 }
3088
3089 return $resultValue;
3090 }
3091
3097 protected function checkProductQuantityInStore(array $products)
3098 {
3099 $result = new Sale\Result();
3100
3101 $resultList = array();
3102 $productQuantityList = array();
3103 $usedProductStoreQuantity = [];
3104
3105 $productStoreDataList = self::loadCurrentProductStores(array_keys($products));
3106
3107 foreach ($products as $productId => $productData)
3108 {
3109 if (empty($productData['CATALOG']))
3110 continue;
3111 if (
3112 isset($productData['PRODUCT'])
3113 && !$productData['PRODUCT']['USED_STORE_INVENTORY'] //product types without stores
3114 )
3115 {
3116 continue;
3117 }
3118
3119 $productStoreData = $productStoreDataList[$productId] ?? [];
3120
3121 $storeDataList = $productData['STORE_DATA_LIST'];
3122
3123 if (!empty($storeDataList))
3124 {
3125 foreach ($storeDataList as $barcodeList)
3126 {
3127 foreach($barcodeList as $storeId => $storeDataValue)
3128 {
3129 if (!empty($storeDataValue))
3130 {
3131 if (
3132 !isset($productStoreData[$storeId])
3133 || ($productStoreData[$storeId]["AMOUNT"] < $storeDataValue["QUANTITY"])
3134 )
3135 {
3136 $result->addError(
3137 new Sale\ResultError(
3138 Main\Localization\Loc::getMessage(
3139 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2',
3140 array_merge(
3141 self::getProductCatalogInfo($productId),
3142 [
3143 '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($storeId),
3144 '#STORE_ID#' => $storeId,
3145 ]
3146 )
3147 ), 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR'
3148 )
3149 );
3150 }
3151 else
3152 {
3153 if (!isset($productQuantityList[$productId]))
3154 {
3155 $productQuantityList[$productId] = 0;
3156 }
3157
3158 $productQuantityList[$productId] += $storeDataValue["QUANTITY"];
3159
3160 if (!isset($usedProductStoreQuantity[$productId]))
3161 {
3162 $usedProductStoreQuantity[$productId] = [];
3163 }
3164 $usedProductStoreQuantity[$productId][$storeId] = true;
3165
3166 $r = static::checkProductBarcodes($productData, $productStoreData[$storeId], $storeDataValue);
3167 if ($r->isSuccess())
3168 {
3169 if (!array_key_exists($productId, $resultList))
3170 {
3171 $resultList[$productId] = true;
3172 }
3173 }
3174 else
3175 {
3176 $result->addErrors($r->getErrors());
3177 }
3178 }
3179 }
3180 else
3181 {
3182 if (!array_key_exists($productId, $resultList))
3183 {
3184 $resultList[$productId] = true;
3185 }
3186 }
3187 }
3188 }
3189 }
3190 else
3191 {
3192 $resultList[$productId] = true;
3193
3194 if (!isset($productQuantityList[$productId]))
3195 {
3196 $productQuantityList[$productId] = 0;
3197 }
3198
3199 if (
3200 !empty($productData[Base::FLAT_QUANTITY_LIST])
3201 && is_array($productData[Base::FLAT_QUANTITY_LIST])
3202 )
3203 {
3204 $productQuantityList[$productId] = array_sum(
3205 $productData[Base::FLAT_QUANTITY_LIST]
3206 );
3207 }
3208
3209 }
3210 }
3211
3212 if (!empty($productQuantityList))
3213 {
3214 foreach ($productQuantityList as $amountProductId => $amountValue)
3215 {
3216 $product = $products[$amountProductId];
3217 $catalogData = $product['CATALOG'];
3218
3219 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData);
3220 $catalogReservedQuantity = (float)$catalogData['QUANTITY_RESERVED'];
3221
3222 if ($product[Base::FLAT_RESERVED_QUANTITY_LIST][$product['BASKET_CODE']] > 0)
3223 {
3224 $catalogQuantity += $catalogReservedQuantity;
3225 }
3226 else
3227 {
3228 $unusedReserve = 0.0;
3229 if (isset($usedProductStoreQuantity[$amountProductId]))
3230 {
3231 $usedProductStores = $usedProductStoreQuantity[$amountProductId];
3232 $productStores = $productStoreDataList[$amountProductId];
3233 foreach (array_keys($productStores) as $storeId)
3234 {
3235 if (isset($usedProductStores[$storeId]))
3236 {
3237 continue;
3238 }
3239 $unusedReserve += $productStores[$storeId]['QUANTITY_RESERVED'];
3240 }
3241 unset($storeId);
3242 unset($productStores, $usedProductStores);
3243 }
3244 $catalogQuantity += $unusedReserve;
3245 unset($unusedReserve);
3246 }
3247
3248 if ($amountValue > $catalogQuantity)
3249 {
3250 $result->addError(
3251 new Sale\ResultError(
3252 Main\Localization\Loc::getMessage(
3253 "DDCT_DEDUCTION_SHIPMENT_QUANTITY_NOT_ENOUGH",
3254 self::getProductCatalogInfo($amountProductId)
3255 ), "SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH"
3256 )
3257 );
3258 }
3259 }
3260 }
3261
3262 if (!empty($resultList))
3263 {
3264 $result->setData(
3265 array(
3266 'PRODUCTS_LIST_REQUIRED_QUANTITY_IN_STORE' => $resultList,
3267 )
3268 );
3269 }
3270
3271 return $result;
3272 }
3273
3280 protected function checkExistsProductItemInStore(array $productData, array $storeDataList = array())
3281 {
3282 $result = new Sale\Result();
3283
3284 if (!empty($storeDataList))
3285 {
3286 foreach ($storeDataList as $storeData)
3287 {
3288 foreach ($storeData as $storeDataValue)
3289 {
3290 $storeId = $storeDataValue['STORE_ID'];
3291
3292 if ((int)$storeId < -1 || (int)$storeId == 0
3293 || !isset($storeDataValue["QUANTITY"]) || (int)$storeDataValue["QUANTITY"] < 0)
3294 {
3295 $result->addError(
3296 new Sale\ResultError(
3297 Main\Localization\Loc::getMessage(
3298 "DDCT_DEDUCTION_STORE_ERROR",
3299 self::getProductCatalogInfo($productData['PRODUCT_ID'])
3300 ), "DDCT_DEDUCTION_STORE_ERROR"
3301 )
3302 );
3303 return $result;
3304 }
3305 }
3306 }
3307 }
3308 else
3309 {
3310 $result->addError( new Sale\ResultError(
3311 Main\Localization\Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::getProductCatalogInfo($productData['PRODUCT_ID'])),
3312 "DEDUCTION_STORE_ERROR1"
3313 ));
3314 }
3315
3316 return $result;
3317 }
3318
3325 protected function checkExistsProductsInStore(array $products, array $storeData = array())
3326 {
3327 $result = new Sale\Result();
3328
3329 $resultList = array();
3330 if (!empty($storeData))
3331 {
3332 foreach ($products as $productId => $productData)
3333 {
3334 $productStoreData = array();
3335 if (!empty($storeData[$productId]))
3336 {
3337 $productStoreData = $storeData[$productId];
3338 }
3339
3340 $resultList[$productId] = true;
3341
3342 if (
3343 (
3344 isset($productData['BUNDLE_PARENT'])
3345 && $productData['BUNDLE_PARENT'] === true
3346 )
3347 || (
3348 isset($productData['PRODUCT']['USED_STORE_INVENTORY'])
3349 && !$productData['PRODUCT']['USED_STORE_INVENTORY']
3350 )
3351 )
3352 {
3353 continue;
3354 }
3355
3356 $r = $this->checkExistsProductItemInStore($productData, $productStoreData);
3357 if (!$r->isSuccess())
3358 {
3359 $result->addErrors($r->getErrors());
3360 $resultList[$productId] = false;
3361 }
3362 }
3363 }
3364
3365 if (!empty($resultList))
3366 {
3367 $result->setData(
3368 array(
3369 'PRODUCTS_LIST_EXISTS_IN_STORE' => $resultList,
3370 )
3371 );
3372 }
3373
3374 return $result;
3375 }
3376
3384 protected static function checkProductBarcodes(array $productData, array $productStoreData, array $storeData = array())
3385 {
3386 $result = new Sale\Result();
3387
3388 $productId = $productData['PRODUCT_ID'];
3389 $storeId = $productStoreData['STORE_ID'];
3390
3391 if (isset($storeData['BARCODE']) && count($storeData['BARCODE']) > 0)
3392 {
3393 foreach ($storeData['BARCODE'] as $barcodeValue)
3394 {
3395 if (trim($barcodeValue) == "" && $storeData['IS_BARCODE_MULTI'] === true)
3396 {
3397 $result->addError(
3398 new Sale\ResultError(
3399 Main\Localization\Loc::getMessage(
3400 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY",
3401 array_merge(
3402 self::getProductCatalogInfo($productId),
3403 array("#STORE_ID#" => $storeId)
3404 )
3405 ), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY"
3406 )
3407 );
3408 continue;
3409 }
3410 if (!empty($barcodeValue))
3411 {
3412 $fields = [
3413 '=STORE_ID' => static::CATALOG_PROVIDER_EMPTY_STORE_ID,
3414 '=BARCODE' => $barcodeValue,
3415 '=PRODUCT_ID' => $productId,
3416 ];
3417
3418 if ($storeData['IS_BARCODE_MULTI'] === true)
3419 {
3420 $fields['=STORE_ID'] = $storeId;
3421 }
3422 $iterator = Catalog\StoreBarcodeTable::getList([
3423 'select' => ['ID'],
3424 'filter' => $fields,
3425 'limit' => 1,
3426 ]);
3427 $row = $iterator->fetch();
3428 unset($iterator);
3429
3430 if (empty($row))
3431 {
3432 $result->addError( new Sale\ResultError(
3433 Main\Localization\Loc::getMessage(
3434 "DDCT_DEDUCTION_BARCODE_ERROR",
3435 array_merge(self::getProductCatalogInfo($productId), array("#BARCODE#" => $barcodeValue))
3436 ),
3437 "DDCT_DEDUCTION_BARCODE_ERROR"
3438 ));
3439 }
3440 }
3441 }
3442 }
3443 else
3444 {
3445 $result->addError( new Sale\ResultError(
3446 Main\Localization\Loc::getMessage(
3447 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY",
3448 array_merge(self::getProductCatalogInfo($productId), array("#STORE_ID#" => $storeId))
3449 ),
3450 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY"
3451 ));
3452 }
3453 return $result;
3454 }
3455
3462 private function canProductListAutoShip(array $products): Sale\Result
3463 {
3464 $context = $this->getContext();
3465 if (empty($context['SITE_ID']))
3466 {
3467 throw new Main\ArgumentNullException('SITE_ID');
3468 }
3469
3470 static $canAutoList = array();
3471
3472 $resultList = array();
3473 $hasNew = false;
3474
3475 $countStores = 0;
3476
3477 $countStoresResult = $this->getStoresCount();
3478 if ($countStoresResult->isSuccess())
3479 {
3480 $countStores = $countStoresResult->get('STORES_COUNT');
3481 }
3482
3483 $countStoresResult->getData();
3484 $defaultDeductionStore = (int)Main\Config\Option::get("sale", "deduct_store_id", "", $context['SITE_ID']);
3485 $isDefaultStore = ($defaultDeductionStore > 0);
3486 foreach ($products as $productId => $productData)
3487 {
3488 if (isset($canAutoList[$productId]))
3489 {
3490 $resultList[$productId] = $canAutoList[$productId];
3491 continue;
3492 }
3493
3494 if (!$productData['PRODUCT']['USED_STORE_INVENTORY']) // product types without stores
3495 {
3496 $canAutoList[$productId] = true;
3497 $resultList[$productId] = true;
3498 continue;
3499 }
3500
3501 $isOneStore = ($countStores == 1 || $countStores == -1);
3502
3503 $isOnlyOneStore = ($isOneStore || $isDefaultStore);
3504 $isMulti = false;
3505 if (isset($productData['STORE_DATA_LIST']))
3506 {
3507 $storeData = array();
3508 $shipmentItemStoreData = reset($productData['STORE_DATA_LIST']);
3509 if (!empty($shipmentItemStoreData))
3510 {
3511 $storeData = reset($shipmentItemStoreData);
3512 }
3513
3514 if (!empty($storeData))
3515 {
3516 $isMulti = isset($storeData['IS_BARCODE_MULTI']) && $storeData['IS_BARCODE_MULTI'] === true;
3517 }
3518 }
3519 elseif (isset($productData['IS_BARCODE_MULTI']))
3520 {
3521 $isMulti = $productData['IS_BARCODE_MULTI'] === true;
3522 }
3523
3524 $resultList[$productId] = ($isOnlyOneStore && !$isMulti);
3525 $hasNew = true;
3526
3527 if ($isMulti)
3528 {
3529 $hasNew = false;
3530 }
3531 }
3532
3533 if ($hasNew)
3534 {
3535 $productStoreList = [];
3536 $r = $this->getProductListStores($products);
3537 if ($r->isSuccess())
3538 {
3539 $productStoreData = $r->getData();
3540 if (
3541 !empty($productStoreData['PRODUCT_STORES_LIST'])
3542 && is_array($productStoreData['PRODUCT_STORES_LIST'])
3543 )
3544 {
3545 $productStoreList = $productStoreData['PRODUCT_STORES_LIST'];
3546 }
3547 }
3548
3549 if (!empty($productStoreList))
3550 {
3551 foreach ($products as $productId => $productData)
3552 {
3553 if (!empty($productStoreList[$productId]))
3554 {
3555 $countProductInStore = 0;
3556 foreach ($productStoreList[$productId] as $storeData)
3557 {
3558 if ((float)$storeData['AMOUNT'] > 0)
3559 {
3560 $countProductInStore++;
3561 }
3562 }
3563 $resultList[$productId] = ($countProductInStore == 1);
3564 $canAutoList[$productId] = $resultList[$productId];
3565 }
3566 }
3567 }
3568 }
3569
3570 $result = new Sale\Result();
3571 if (!empty($resultList))
3572 {
3573 $result->setData(
3574 array(
3575 'PRODUCT_CAN_AUTOSHIP_LIST' => $resultList,
3576 )
3577 );
3578 }
3579
3580 return $result;
3581 }
3582
3589 private static function getAutoShipStoreData(array $product, array $productStoreDataList)
3590 {
3591 $isMulti = false;
3592 if (isset($product['STORE_DATA_LIST']))
3593 {
3594 $storeData = [];
3595 $shipmentItemStoreData = reset($product['STORE_DATA_LIST']);
3596 if (!empty($shipmentItemStoreData))
3597 {
3598 $storeData = reset($shipmentItemStoreData);
3599 }
3600
3601 if (!empty($storeData))
3602 {
3603 $isMulti = isset($storeData['IS_BARCODE_MULTI']) && $storeData['IS_BARCODE_MULTI'] === true;
3604 }
3605 }
3606 elseif (isset($product['IS_BARCODE_MULTI']))
3607 {
3608 $isMulti = $product['IS_BARCODE_MULTI'] === true;
3609 }
3610
3611 if ($isMulti)
3612 {
3613 return false;
3614 }
3615
3616 $outputStoreData = false;
3617
3618 if (!empty($productStoreDataList))
3619 {
3620 $countProductInStore = 0;
3621
3622 $storeProductData = false;
3623 foreach ($productStoreDataList as $storeData)
3624 {
3625 if ((float)$storeData['AMOUNT'] > 0)
3626 {
3627 $countProductInStore++;
3628 if (!$storeProductData)
3629 {
3630 $storeProductData = $storeData;
3631 }
3632 }
3633 }
3634
3635 if ($countProductInStore == 1 && !empty($storeProductData))
3636 {
3637 $outputStoreData = $storeProductData;
3638 }
3639 }
3640
3641 return $outputStoreData;
3642 }
3643
3649 protected function getCountProductsInStore(array $products)
3650 {
3651 $result = new Sale\Result();
3652
3653 $productStoreList = array();
3654 $productStoreResult = $this->getProductListStores($products);
3655 if ($productStoreResult->isSuccess())
3656 {
3657 $productStoreData = $productStoreResult->getData();
3658
3659 if (array_key_exists('PRODUCT_STORES_LIST', $productStoreData))
3660 {
3661 $productStoreList = $productStoreData['PRODUCT_STORES_LIST'];
3662 }
3663 }
3664
3665 if (empty($productStoreList))
3666 {
3667 return $result;
3668 }
3669
3670 $resultList = array();
3671 foreach ($productStoreList as $productStoreDataList)
3672 {
3673 foreach ($productStoreDataList as $storeId =>$productStoreData)
3674 {
3675 $productId = $productStoreData['PRODUCT_ID'];
3676 if ($productStoreData['AMOUNT'] > 0)
3677 {
3678 if (!isset($resultList[$productId]))
3679 {
3680 $resultList[$productId] = [];
3681 }
3682
3683 $resultList[$productId][$storeId] = $productStoreData['AMOUNT'];
3684 }
3685 }
3686 }
3687
3688 if (!empty($resultList))
3689 {
3690 $result->setData(
3691 array(
3692 'RESULT_LIST' => $resultList,
3693 )
3694 );
3695 }
3696
3697 return $result;
3698 }
3699
3704 public function getStoresCount()
3705 {
3706 $result = new Sale\Result();
3707
3708 $count = -1;
3709
3710 if (Catalog\Config\State::isUsedInventoryManagement())
3711 {
3712 $count = count($this->getStoreIds());
3713 }
3714
3715 $result->setData(
3716 array(
3717 'STORES_COUNT' => $count,
3718 )
3719 );
3720
3721 return $result;
3722 }
3723
3727 private function getStoreIds(): array
3728 {
3729 $context = $this->getContext();
3730
3731 $filterId = [
3732 'ACTIVE' => 'Y',
3733 ];
3734 if (isset($context['SITE_ID']) && $context['SITE_ID'] !== '')
3735 {
3736 $filterId['+SITE_ID'] = $context['SITE_ID'];
3737 }
3738
3739 $cacheId = md5(serialize($filterId));
3740 $storeIds = static::getHitCache(self::CACHE_STORE, $cacheId);
3741 if (empty($storeIds))
3742 {
3743 $storeIds = [];
3744
3745 $filter = Main\Entity\Query::filter();
3746 $filter->where('ACTIVE', '=', 'Y');
3747 if (isset($context['SITE_ID']) && $context['SITE_ID'] != '')
3748 {
3749 $subFilter = Main\Entity\Query::filter();
3750 $subFilter->logic('or')->where('SITE_ID', '=', $context['SITE_ID'])->where('SITE_ID', '=', '')->whereNull('SITE_ID');
3751 $filter->where($subFilter);
3752 unset($subFilter);
3753 }
3754
3755 $iterator = Catalog\StoreTable::getList([
3756 'select' => ['ID', 'SORT'],
3757 'filter' => $filter,
3758 'order' => ['SORT' => 'ASC', 'ID' => 'ASC'],
3759 ]);
3760 while ($row = $iterator->fetch())
3761 {
3762 $storeIds[] = (int)$row['ID'];
3763 }
3764 unset($row, $iterator, $filter);
3765 if (!empty($storeIds))
3766 {
3767 static::setHitCache(self::CACHE_STORE, $cacheId, $storeIds);
3768 }
3769 }
3770 unset($cacheId, $filterId);
3771
3772 return $storeIds;
3773 }
3774
3780 public function getProductListStores(array $products)
3781 {
3782 $result = new Sale\Result();
3783
3784 //without store control stores are used for information purposes only
3785 if (!Catalog\Config\State::isUsedInventoryManagement())
3786 {
3787 return $result;
3788 }
3789
3790 $storeIds = $this->getStoreIds();
3791 if (empty($storeIds))
3792 {
3793 return $result;
3794 }
3795
3796 $resultList = [];
3797 $productGetIdList = [];
3798 foreach (array_keys($products) as $productId)
3799 {
3800/* $cacheId = md5($productId);
3801
3802 $storeProductDataList = static::getHitCache(self::CACHE_STORE_PRODUCT, $cacheId);
3803 if (!empty($storeProductDataList))
3804 {
3805 $resultList[$productId] = $storeProductDataList;
3806 }
3807 else
3808 {
3809 $productGetIdList[$productId] = $productId;
3810 } */
3811 // remove cache because need clear cache after modify stores
3812 if (
3813 isset($products[$productId]['PRODUCT']['USED_STORE_INVENTORY'])
3814 && !$products[$productId]['PRODUCT']['USED_STORE_INVENTORY']
3815 ) // product types without stores
3816 {
3817 continue;
3818 }
3819 $productGetIdList[$productId] = $productId;
3820 }
3821
3822 if (!empty($productGetIdList))
3823 {
3824 $emptyProductStores = [];
3825 $iterator = Catalog\StoreTable::getList([
3826 'select' => [
3827 'ID',
3828 'TITLE',
3829 ],
3830 'filter' => [
3831 '@ID' => $storeIds,
3832 ],
3833 'order' => [
3834 'ID' => 'ASC',
3835 ],
3836 ]);
3837 while ($row = $iterator->fetch())
3838 {
3839 $id = (int)$row['ID'];
3840 $emptyProductStores[$id] = [
3841 'ID' => 0,
3842 'PRODUCT_ID' => 0,
3843 'STORE_ID' => $row['ID'],
3844 'AMOUNT' => 0,
3845 'QUANTITY_RESERVED' => 0,
3846 'STORE_NAME' => $row['TITLE'],
3847 ];
3848 }
3849 unset($row, $iterator);
3850
3851 foreach (array_chunk($productGetIdList, 500) as $pageIds)
3852 {
3853 foreach ($pageIds as $productId)
3854 {
3855 $rows = $emptyProductStores;
3856 foreach (array_keys($rows) as $storeId)
3857 {
3858 $rows[$storeId]['PRODUCT_ID'] = $productId;
3859 }
3860 $resultList[$productId] = $rows;
3861 unset($rows);
3862 }
3863 unset($productId);
3864 $iterator = Catalog\StoreProductTable::getList([
3865 'select' => [
3866 'ID',
3867 'PRODUCT_ID',
3868 'STORE_ID',
3869 'AMOUNT',
3870 'QUANTITY_RESERVED',
3871 ],
3872 'filter' => [
3873 '=PRODUCT_ID' => $pageIds,
3874 '@STORE_ID' => $storeIds,
3875 ],
3876 'order' => [
3877 'PRODUCT_ID' => 'ASC',
3878 'STORE_ID' => 'ASC',
3879 ],
3880 ]);
3881 while ($row = $iterator->fetch())
3882 {
3883 $row['ID'] = (int)$row['ID'];
3884 $row['PRODUCT_ID'] = (int)$row['PRODUCT_ID'];
3885 $row['STORE_ID'] = (int)$row['STORE_ID'];
3886 if (!isset($resultList[$row['PRODUCT_ID']]))
3887 {
3888 $resultList[$row['PRODUCT_ID']] = [];
3889 }
3890 $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['ID'] = $row['ID'];
3891 $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['AMOUNT'] = (float)$row['AMOUNT'];
3892 $resultList[$row['PRODUCT_ID']][$row['STORE_ID']]['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED'];
3893 }
3894 unset($iterator, $row);
3895 }
3896 unset($pageIds);
3897
3898/* foreach ($productGetIdList as $productId)
3899 {
3900 if (!empty($resultList[$productId]))
3901 {
3902 $cacheId = md5($productId);
3903 static::setHitCache(self::CACHE_STORE_PRODUCT, $cacheId, $resultList[$productId]);
3904 }
3905 } */
3906 }
3907
3908 if (!empty($resultList))
3909 {
3910 $result->setData([
3911 'PRODUCT_STORES_LIST' => $resultList,
3912 ]);
3913 }
3914
3915 return $result;
3916 }
3917
3925 protected static function getHitCache($type, $key, array $fields = array())
3926 {
3927 if (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key]))
3928 {
3929 if (static::isExistsHitCache($type, $key, $fields))
3930 {
3931 return self::$hitCache[$type][$key];
3932 }
3933 }
3934
3935 return false;
3936 }
3937
3945 protected static function isExistsHitCache($type, $key, array $fields = []): bool
3946 {
3947 $isExists = false;
3948 if (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key]))
3949 {
3950 $isExists = true;
3951 if (!empty($fields) && is_array($fields) && is_array(self::$hitCache[$type][$key]))
3952 {
3953 foreach ($fields as $name)
3954 {
3955 if (!array_key_exists($name, self::$hitCache[$type][$key]))
3956 {
3957 $isExists = false;
3958 break;
3959 }
3960 }
3961 }
3962 }
3963
3964 return $isExists;
3965 }
3966
3972 protected static function setHitCache(string $type, $key, $value): void
3973 {
3974 if (!isset(self::$hitCache[$type]))
3975 {
3976 self::$hitCache[$type] = [];
3977 }
3978
3979 if (!isset(self::$hitCache[$type][$key]))
3980 {
3981 self::$hitCache[$type][$key] = [];
3982 }
3983
3984 self::$hitCache[$type][$key] = $value;
3985 }
3986
3990 protected static function clearHitCache(?string $type = null): void
3991 {
3992 if ($type === null)
3993 {
3994 self::$hitCache = [];
3995 }
3996 elseif (isset(self::$hitCache[$type]))
3997 {
3998 unset(self::$hitCache[$type]);
3999 }
4000 }
4001
4007 protected static function clearNotCacheFields($fields)
4008 {
4009 $resultFields = array();
4010 $clearFields = static::getNotCacheFields();
4011 foreach ($fields as $name => $value)
4012 {
4013 $clearName = $name;
4014 if (mb_substr($clearName, 0, 1) == '~')
4015 {
4016 $clearName = mb_substr($clearName, 1, mb_strlen($clearName));
4017 }
4018
4019 if (!in_array($clearName, $clearFields))
4020 {
4021 $resultFields[$name] = $value;
4022 }
4023 }
4024
4025 return $resultFields;
4026 }
4027
4031 protected static function getNotCacheFields()
4032 {
4033 return array(
4034 'CAN_BUY_ZERO',
4035 'QUANTITY_TRACE',
4036 'QUANTITY',
4037 'CAN_BUY',
4038 );
4039 }
4040
4041 protected static function checkNeedFields(array $fields, array $need)
4042 {
4043 foreach ($need as $name => $value)
4044 {
4045 if (!array_key_exists($name, $fields))
4046 {
4047 return false;
4048 }
4049 }
4050
4051 return true;
4052 }
4053
4065 protected static function isNeedClearPublicCache($currentQuantity, $newQuantity, $quantityTrace, $canBuyZero, $ratio = 1): bool
4066 {
4067 return false;
4068 }
4069
4077 protected static function clearPublicCache($productID, $productInfo = array()): void {}
4078
4084 public function getAvailableQuantity(array $products)
4085 {
4086 $result = $this->getAvailableQuantityByStore($products);
4087 if (!$result->isSuccess())
4088 {
4089 return $result;
4090 }
4091 $data = $result->getData();
4092 if (empty($data[Base::STORE_AVAILABLE_QUANTITY_LIST]))
4093 {
4094 return $result;
4095 }
4096
4097 $reservedList = $data[Base::STORE_AVAILABLE_QUANTITY_LIST];
4098 $resultList = [];
4099 foreach ($reservedList as $productId => $rows)
4100 {
4101 $resultList[$productId] = reset($rows);
4102 }
4103 unset($productId, $rows);
4104 unset($reservedList, $data);
4105
4106 $result->setData([
4108 ]);
4109
4110 return $result;
4111 }
4112
4113 public function getAvailableQuantityByStore(array $products): Sale\Result
4114 {
4115 $result = new Sale\Result();
4116 $resultList = [];
4117
4118 $isGotQuantityDataList = [];
4119
4120 foreach ($products as $productId => $productData)
4121 {
4122 $catalogAvailableQuantity = QuantityControl::getAvailableQuantity($productId);
4123 $catalogQuantity = QuantityControl::getQuantity($productId);
4124
4125 if ($catalogQuantity === null || $catalogAvailableQuantity === null)
4126 {
4127 continue;
4128 }
4129
4130 $productQuantity = self::getStoreAmountFromQuantityList($productData);
4131 if ($productQuantity === null)
4132 {
4133 continue;
4134 }
4135 if ($catalogAvailableQuantity < array_sum($productQuantity))
4136 {
4137 continue;
4138 }
4139
4140 $isGotQuantityDataList[$productId] = true;
4141
4142 $resultList[$productId] = $productQuantity;
4143 }
4144
4145 if (count($resultList) != count($products))
4146 {
4147 if ($this->isExistsCatalogData($products))
4148 {
4149 $items = $products;
4150 }
4151 else
4152 {
4153 $items = $this->createProductsListWithCatalogData($products);
4154 }
4155
4156 foreach ($items as $productId => $productData)
4157 {
4158 if (isset($isGotQuantityDataList[$productId]))
4159 {
4160 continue;
4161 }
4162
4163 if (empty($productData['CATALOG']) || !is_array($productData['CATALOG']))
4164 {
4165 continue;
4166 }
4167
4168 $catalogData = $productData['CATALOG'];
4169
4170 $productQuantity = self::getStoreAmountFromQuantityList($productData);
4171 if ($productQuantity === null)
4172 {
4173 $resultList[$productId] = [];
4174 continue;
4175 }
4176 $resultList[$productId] = $productQuantity;
4177
4178 $catalogQuantity = self::getTotalAmountFromPriceList($catalogData, false);
4179
4180 QuantityControl::setQuantity($productId, $catalogQuantity);
4181
4182 if ($catalogData['CHECK_QUANTITY'])
4183 {
4184 $totalReservedQuantity = 0;
4185 $reservedQuantity = self::getStoreReservedQuantityFromProduct($productData);
4186 if ($reservedQuantity !== null)
4187 {
4188 $totalReservedQuantity = array_sum($reservedQuantity);
4189 }
4190
4191 $needQuantity = (array_sum($productQuantity) - $totalReservedQuantity);
4192 if ($catalogQuantity < $needQuantity)
4193 {
4194 $limitQuantity = $catalogQuantity;
4195 $availableList = $resultList[$productId];
4196 arsort($availableList, SORT_NUMERIC);
4197 foreach (array_keys($availableList) as $storeId)
4198 {
4199 $storeQuantity = $resultList[$productId][$storeId];
4200 if ($limitQuantity > $storeQuantity)
4201 {
4202 $limitQuantity -= $storeQuantity;
4203 }
4204 else
4205 {
4206 $storeQuantity = $limitQuantity;
4207 if ($limitQuantity > 0)
4208 {
4209 $limitQuantity = 0;
4210 }
4211 }
4212 $resultList[$productId][$storeId] = $storeQuantity;
4213 }
4214 }
4215 }
4216 }
4217 }
4218
4219 if (!empty($resultList))
4220 {
4221 $result->setData([
4223 ]);
4224 }
4225
4226 return $result;
4227 }
4228
4234 public function getAvailableQuantityAndPrice(array $products)
4235 {
4236 $result = new Sale\Result();
4237 $availableQuantityListResult = $this->getAvailableQuantity($products);
4238
4239 if ($this->isExistsCatalogData($products))
4240 {
4241 $items = $products;
4242 }
4243 else
4244 {
4245 $items = $this->createProductsListWithCatalogData($products);
4246 }
4247
4248 $priceDataList = array();
4249
4250 foreach ($items as $productId => $productData)
4251 {
4252 $catalogData = $productData['CATALOG'];
4253
4254 if (!empty($catalogData['PRICE_LIST']))
4255 {
4256 $priceDataList[$productId] = $catalogData['PRICE_LIST'];
4257 }
4258
4259 }
4260
4261 $availableQuantityData = array();
4262
4263 if ($availableQuantityListResult->isSuccess())
4264 {
4265 $availableQuantityList = $availableQuantityListResult->getData();
4266
4267 if (isset($availableQuantityList[Base::FLAT_AVAILABLE_QUANTITY_LIST]))
4268 {
4269 $availableQuantityData = $availableQuantityList[Base::FLAT_AVAILABLE_QUANTITY_LIST];
4270 }
4271 }
4272
4273 $result->setData([
4275 Base::FLAT_PRICE_LIST => $priceDataList,
4276 Base::FLAT_AVAILABLE_QUANTITY_LIST => $availableQuantityData,
4277 ],
4278 ]);
4279
4280 return $result;
4281 }
4282
4283 public function writeOffProductBatches(array $products): Sale\Result
4284 {
4285 $result = new Sale\Result();
4286
4287 if (!Catalog\Config\State::isUsedInventoryManagement() || !State::isProductBatchMethodSelected())
4288 {
4289 return $result;
4290 }
4291
4292 foreach ($products as $productId => $productData)
4293 {
4294 if (empty($productData['SHIPMENT_ITEM_LIST']) || !is_array($productData['SHIPMENT_ITEM_LIST']))
4295 {
4296 continue;
4297 }
4298
4299 $productBatch = new BatchManager($productId);
4301 foreach ($productData['SHIPMENT_ITEM_LIST'] as $item)
4302 {
4304 foreach ($item->getShipmentItemStoreCollection() as $storeItem)
4305 {
4306 $quantity = $storeItem->getQuantity();
4307 if ($quantity <= 0)
4308 {
4309 continue;
4310 }
4311
4312 $distributor = new DistributionStrategy\ShipmentStore($productBatch, $storeItem);
4313 $r = $distributor->writeOff($quantity);
4314 if (!$r->isSuccess())
4315 {
4316 $result->addErrors($r->getErrors());
4317 }
4318 }
4319 }
4320 }
4321
4322 return $result;
4323 }
4324
4325 public function returnProductBatches(array $products): Sale\Result
4326 {
4327 $result = new Sale\Result();
4328
4329 if (!Catalog\Config\State::isUsedInventoryManagement() || !State::isProductBatchMethodSelected())
4330 {
4331 return $result;
4332 }
4333
4334 foreach ($products as $productId => $productData)
4335 {
4336 if (empty($productData['SHIPMENT_ITEM_LIST']) || !is_array($productData['SHIPMENT_ITEM_LIST']))
4337 {
4338 continue;
4339 }
4340
4341 $productBatch = new BatchManager($productId);
4343 foreach ($productData['SHIPMENT_ITEM_LIST'] as $item)
4344 {
4345 foreach ($item->getShipmentItemStoreCollection() as $storeItem)
4346 {
4347 $distributor = new DistributionStrategy\ShipmentStore($productBatch, $storeItem);
4348 $r = $distributor->return();
4349 if (!$r->isSuccess())
4350 {
4351 $result->addErrors($r->getErrors());
4352 }
4353 }
4354 }
4355 }
4356
4357 return $result;
4358 }
4359
4365 private function isExistsCatalogData($products): bool
4366 {
4367 foreach ($products as $productData)
4368 {
4369 if (empty($productData['CATALOG']))
4370 {
4371 return false;
4372 }
4373 }
4374 return true;
4375 }
4376
4382 private function getIblockData(array $list): array
4383 {
4384 $resultList = [];
4385 $res = Catalog\CatalogIblockTable::getList([
4386 'select' => [
4387 'IBLOCK_ID',
4388 'SUBSCRIPTION',
4389 'PRODUCT_IBLOCK_ID',
4390 'CATALOG_XML_ID' => 'IBLOCK.XML_ID',
4391 ],
4392 'filter' => ['@IBLOCK_ID' => $list],
4393 ]);
4394 while($iblockData = $res->fetch())
4395 {
4396 $resultList[$iblockData['IBLOCK_ID']] = $iblockData;
4397 if ($this->enableCache)
4398 {
4399 static::setHitCache(self::CACHE_CATALOG_IBLOCK_LIST, $iblockData['IBLOCK_ID'], $iblockData);
4400 }
4401 }
4402 unset($res, $iblockData);
4403
4404 return $resultList;
4405 }
4406
4412 private static function checkSkuPermission(array $iblockProductMap): array
4413 {
4414 $resultList = array();
4415
4416 foreach ($iblockProductMap as $iblockData)
4417 {
4418 if ($iblockData['PRODUCT_IBLOCK_ID'] > 0 && !empty($iblockData['PRODUCT_LIST']))
4419 {
4420 $resultList = array_merge(
4421 $resultList,
4422 static::checkParentActivity($iblockData['PRODUCT_LIST'], (int)$iblockData['PRODUCT_IBLOCK_ID'])
4423 );
4424 }
4425 else
4426 {
4427 foreach ($iblockData['PRODUCT_LIST'] as $productId)
4428 {
4429 $resultList[] = $productId;
4430 }
4431 }
4432 }
4433
4434 return $resultList;
4435 }
4436
4443 private static function createIblockProductMap(array $iblockList, array $iblockDataList): array
4444 {
4445 $resultList = $iblockDataList;
4446 foreach ($iblockList as $iblockId => $iblockProductList)
4447 {
4448 if (isset($iblockDataList[$iblockId]))
4449 {
4450 $resultList[$iblockId]['PRODUCT_LIST'] = $iblockProductList;
4451 }
4452 }
4453
4454 return $resultList;
4455 }
4456
4463 private static function changeSubscribeProductQuantity(array $products, array $iblockProductMap): array
4464 {
4465 $resultList = $products;
4466
4467 foreach ($iblockProductMap as $iblockData)
4468 {
4469 if ($iblockData['SUBSCRIPTION'] != 'Y')
4470 continue;
4471
4472 if (empty($iblockData['PRODUCT_LIST']))
4473 continue;
4474
4475 foreach($iblockData['PRODUCT_LIST'] as $productId)
4476 {
4477 if (isset($resultList[$productId]))
4478 {
4479 if (
4480 !empty($resultList[$productId][Base::FLAT_QUANTITY_LIST])
4481 && is_array($resultList[$productId][Base::FLAT_QUANTITY_LIST])
4482 )
4483 {
4484 foreach (array_keys($resultList[$productId][Base::FLAT_QUANTITY_LIST]) as $index)
4485 {
4486 $resultList[$productId][Base::FLAT_QUANTITY_LIST][$index] = 1;
4487 }
4488 }
4489 }
4490 }
4491 }
4492
4493 return $resultList;
4494 }
4495
4502 private static function getCatalogProducts(array $list, array $select): array
4503 {
4504 $usedStoreInventory = Catalog\Config\State::isUsedInventoryManagement();
4505
4506 $typesWithoutStores = [
4507 Catalog\ProductTable::TYPE_SET => true,
4508 Catalog\ProductTable::TYPE_SKU => true,
4509 Catalog\ProductTable::TYPE_SERVICE => true,
4510 ];
4511 $typesWithoutReservation = [
4512 Catalog\ProductTable::TYPE_SET => true,
4513 Catalog\ProductTable::TYPE_SKU => true,
4514 Catalog\ProductTable::TYPE_SERVICE => true,
4515 ];
4516
4517 if (empty($select))
4518 {
4519 $select = ['*'];
4520 }
4521 else
4522 {
4523 $select[] = 'ID';
4524 $select[] = 'TYPE';
4525 $select[] = 'AVAILABLE';
4526
4527 $select = array_unique($select);
4528 }
4529 Main\Type\Collection::normalizeArrayValuesByInt($list, true);
4530 if (empty($list))
4531 {
4532 return [];
4533 }
4534 $resultList = [];
4535 foreach (array_chunk($list, 500) as $pageIds)
4536 {
4537 $iterator = Catalog\Model\Product::getList([
4538 'select' => $select,
4539 'filter' => [
4540 '@ID' => $pageIds,
4541 ],
4542 ]);
4543 while ($row = $iterator->fetch())
4544 {
4545 $row['ID'] = (int)$row['ID'];
4546 $row['TYPE'] = (int)$row['TYPE'];
4547 $row['QUANTITY'] = (float)$row['QUANTITY'];
4548 $row['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED'];
4549 $row['CHECK_QUANTITY'] = (
4550 $row['TYPE'] !== Catalog\ProductTable::TYPE_SERVICE
4551 && $row['QUANTITY_TRACE'] === Catalog\ProductTable::STATUS_YES
4552 && $row['CAN_BUY_ZERO'] === Catalog\ProductTable::STATUS_NO
4553 );
4554 Catalog\Product\SystemField::prepareRow($row, Catalog\Product\SystemField::OPERATION_PROVIDER);
4555
4556 if (isset($typesWithoutStores[$row['TYPE']]))
4557 {
4558 $row['USED_STORE_INVENTORY'] = false;
4559 }
4560 else
4561 {
4562 $row['USED_STORE_INVENTORY'] = $usedStoreInventory;
4563 }
4564 if (isset($typesWithoutReservation[$row['TYPE']]))
4565 {
4566 $row['USED_RESERVATION'] = false;
4567 $row['QUANTITY_RESERVED'] = 0;
4568 }
4569 else
4570 {
4571 $row['USED_RESERVATION'] = true;
4572 }
4573
4574 $resultList[$row['ID']] = $row;
4575 }
4576 unset($row, $iterator);
4577 }
4578 unset($pageIds);
4579
4580 return $resultList;
4581 }
4582
4588 private static function getMeasure($id = null): array
4589 {
4590 static $measureList = array();
4591
4592 if (!empty($measureList[$id]))
4593 {
4594 return $measureList[$id];
4595 }
4596
4597 $fields = array(
4598 'MEASURE' => $id,
4599 'MEASURE_NAME' => $id,
4600 'MEASURE_CODE' => 0,
4601 );
4602
4603 if ((int)$id <= 0)
4604 {
4605 $measure = \CCatalogMeasure::getDefaultMeasure(true, true);
4606 $fields['MEASURE_NAME'] = $measure['~SYMBOL_RUS'];
4607 $fields['MEASURE_CODE'] = $measure['CODE'];
4608 }
4609 else
4610 {
4611 $resMeasures = \CCatalogMeasure::getList(
4612 array(),
4613 array('ID' => $id),
4614 false,
4615 false,
4616 array('ID', 'SYMBOL_RUS', 'CODE')
4617 );
4618 $measure = $resMeasures->fetch();
4619
4620 if (!empty($measure))
4621 {
4622 $fields['MEASURE_NAME'] = $measure['SYMBOL_RUS'];
4623 $fields['MEASURE_CODE'] = $measure['CODE'];
4624 }
4625 }
4626
4627 $measureList[$id] = $fields;
4628
4629 return $fields;
4630 }
4631
4639 private static function createProductPriceList(array $products, array $productPriceList, array $discountList = array()): array
4640 {
4641 $priceResultList = array();
4642
4643 foreach ($productPriceList as $basketCode => $priceData)
4644 {
4645 if (!$priceData)
4646 continue;
4647 $priceResultList[$basketCode]['PRODUCT_PRICE_ID'] = $priceData['RESULT_PRICE']['ID'];
4648 $priceResultList[$basketCode]['NOTES'] = $priceData['PRICE']['CATALOG_GROUP_NAME'];
4649 $priceResultList[$basketCode]['DISCOUNT_NAME'] = null;
4650 $priceResultList[$basketCode]['DISCOUNT_COUPON'] = null;
4651 $priceResultList[$basketCode]['DISCOUNT_VALUE'] = null;
4652 $priceResultList[$basketCode]['DISCOUNT_LIST'] = array();
4653
4654 $discount = array();
4655 if (!empty($discountList[$priceData['PRODUCT_ID']][$basketCode]))
4656 {
4657 $discount = $discountList[$priceData['PRODUCT_ID']][$basketCode];
4658 }
4659
4660 $priceResultList[$basketCode]['PRICE_TYPE_ID'] = $priceData['RESULT_PRICE']['PRICE_TYPE_ID'];
4661 $priceResultList[$basketCode]['BASE_PRICE'] = $priceData['RESULT_PRICE']['BASE_PRICE'];
4662 $priceResultList[$basketCode]['PRICE'] = $priceData['RESULT_PRICE']['DISCOUNT_PRICE'];
4663 $priceResultList[$basketCode]['CURRENCY'] = $priceData['RESULT_PRICE']['CURRENCY'];
4664 $priceResultList[$basketCode]['DISCOUNT_PRICE'] = $priceData['RESULT_PRICE']['DISCOUNT'];
4665 if (isset($priceData['RESULT_PRICE']['PERCENT']))
4666 {
4667 $priceResultList[$basketCode]['DISCOUNT_VALUE'] = ($priceData['RESULT_PRICE']['PERCENT'] > 0
4668 ? $priceData['RESULT_PRICE']['PERCENT'] . '%' : null);
4669 }
4670 $priceResultList[$basketCode]['VAT_RATE'] = $priceData['RESULT_PRICE']['VAT_RATE'];
4671 $priceResultList[$basketCode]['VAT_INCLUDED'] = $priceData['RESULT_PRICE']['VAT_INCLUDED'];
4672
4673 if (!empty($discount))
4674 {
4675 $priceResultList[$basketCode]['DISCOUNT_LIST'] = $discount;
4676 }
4677
4678 if (!empty($priceData['DISCOUNT']))
4679 {
4680 $priceResultList[$basketCode]['DISCOUNT_NAME'] = '[' .
4681 $priceData['DISCOUNT']['ID'] .
4682 '] ' .
4683 $priceData['DISCOUNT']['NAME'];
4684 if (!empty($priceData['DISCOUNT']['COUPON']))
4685 {
4686 $priceResultList[$basketCode]['DISCOUNT_COUPON'] = $priceData['DISCOUNT']['COUPON'];
4687 }
4688
4689 if (empty($priceResultList[$basketCode]['DISCOUNT_LIST']))
4690 {
4691 $priceResultList[$basketCode]['DISCOUNT_LIST'] = array($priceData['DISCOUNT']);
4692 }
4693 }
4694 }
4695
4696 $resultList = array();
4697 if (!empty($priceResultList))
4698 {
4699 foreach ($products as $productId => $productData)
4700 {
4701 if (!empty($products[$productId]))
4702 {
4703 $productData = $products[$productId];
4704
4705 $quantityList = array();
4706
4707 if (array_key_exists('QUANTITY', $productData))
4708 {
4709 $quantityList = array(
4710 $productData['BASKET_CODE'] => $productData['QUANTITY'],
4711 );
4712 }
4713 if (!empty($productData[Base::FLAT_QUANTITY_LIST]))
4714 {
4715 $quantityList = $productData[Base::FLAT_QUANTITY_LIST];
4716 }
4717
4718 foreach($quantityList as $basketCode => $quantity)
4719 {
4720 $resultList[$basketCode] = $priceResultList[$basketCode];
4721 }
4722 }
4723 }
4724 }
4725
4726 return $resultList;
4727 }
4728
4737 private static function createProductResult(array $products, array $items, array $priceList, array $productQuantityList): array
4738 {
4739 $resultList = array();
4740 foreach ($products as $productId => $productData)
4741 {
4742 $itemCode = $productData['ITEM_CODE'];
4743 $basketCode = $productData['BASKET_CODE'];
4744 $resultList[$productId] = $items[$productId];
4745
4746 if (isset($productData['PRODUCT_DATA']['ACTIVE']))
4747 {
4748 $resultList[$productId]['ACTIVE'] = $productData['PRODUCT_DATA']['ACTIVE'];
4749 }
4750
4751 $resultList[$productId]['ITEM_CODE'] = $itemCode;
4752
4753 QuantityControl::resetAllQuantity($productId);
4754 QuantityControl::setReservedQuantity($productId, $productQuantityList[$basketCode]['QUANTITY_RESERVED']);
4755
4756 if (!isset($priceList[$basketCode]))
4757 {
4758 $priceList[$basketCode] = array();
4759 }
4760
4761 if (!empty($productData[Base::FLAT_QUANTITY_LIST]))
4762 {
4763 foreach($productData[Base::FLAT_QUANTITY_LIST] as $basketCode => $quantity)
4764 {
4765 QuantityControl::addQuantity($productId, $productQuantityList[$basketCode]['QUANTITY']);
4766 QuantityControl::addAvailableQuantity($productId, $productQuantityList[$basketCode]['AVAILABLE_QUANTITY']);
4767
4768 if (empty($priceList[$basketCode]))
4769 {
4770 continue;
4771 }
4772
4773 $resultList[$productId]['PRICE_LIST'][$basketCode] = array_merge(
4774 array(
4775 'QUANTITY' => $productQuantityList[$basketCode]['QUANTITY'],
4776 'AVAILABLE_QUANTITY' => $productQuantityList[$basketCode]['AVAILABLE_QUANTITY'],
4777 "ITEM_CODE" => $itemCode,
4778 "BASKET_CODE" => $basketCode,
4779 ),
4780 $priceList[$basketCode]
4781 );
4782 }
4783 }
4784 else
4785 {
4786 $resultList[$productId]['QUANTITY'] = $productQuantityList[$basketCode]['QUANTITY'];
4787
4788 QuantityControl::addQuantity($productId, $productQuantityList[$basketCode]['QUANTITY']);
4789 QuantityControl::addAvailableQuantity($productId, $productQuantityList[$basketCode]['AVAILABLE_QUANTITY']);
4790 if (!empty($resultList[$productId]))
4791 {
4792 if (empty($priceList[$basketCode]))
4793 {
4794 continue;
4795 }
4796
4797 $resultList[$productId] = $priceList[$basketCode] + $resultList[$productId];
4798 }
4799 }
4800 }
4801
4802 return $resultList;
4803 }
4804
4812 private static function setCatalogDataToProducts(array $products, array $catalogDataList, array $options = array()): array
4813 {
4814 $catalogDataEnabled = self::isCatalogDataEnabled($options);
4815 $specialFields = [];
4816 foreach (Catalog\Product\SystemField::getProviderSelectFields() as $index => $value)
4817 {
4818 $specialFields[] = is_string($index) ? $index : $value;
4819 }
4820
4821 $result = [];
4822 foreach ($products as $productId => $productData)
4823 {
4824 if (!isset($catalogDataList[$productId]))
4825 {
4826 continue;
4827 }
4828
4829 $row = $catalogDataList[$productId];
4830
4831 $result[$productId] = [
4832 'CAN_BUY' => (
4833 $productData['PRODUCT_DATA']['ACTIVE'] === 'Y'
4834 && $row['AVAILABLE'] === 'Y'
4835 ? 'Y'
4836 : 'N'
4837 ),
4838 'CAN_BUY_ZERO' => $row['CAN_BUY_ZERO'],
4839 'QUANTITY_TRACE' => $row['QUANTITY_TRACE'],
4840 'CHECK_QUANTITY' => $row['CHECK_QUANTITY'],
4841 'QUANTITY_RESERVED' => (float)$row['QUANTITY_RESERVED'],
4842 'CATALOG_XML_ID' => $productData['PRODUCT_DATA']['CATALOG_XML_ID'],
4843 'PRODUCT_XML_ID' => $productData['PRODUCT_DATA']['~XML_ID'],
4844 'PRODUCT' => $row,
4845 ];
4846
4847 if (!$catalogDataEnabled)
4848 {
4849 $basketRow = [
4850 'NAME' => $productData['PRODUCT_DATA']['~NAME'],
4851 'DETAIL_PAGE_URL' => $productData['PRODUCT_DATA']['~DETAIL_PAGE_URL'],
4852 'MEASURE_ID' => $row['MEASURE'],
4853 'MEASURE_NAME' => $row['MEASURE_NAME'],
4854 'MEASURE_CODE' => $row['MEASURE_CODE'],
4855 'BARCODE_MULTI' => $row['BARCODE_MULTI'],
4856 'WEIGHT' => (float)$row['WEIGHT'],
4857 'DIMENSIONS' => serialize(
4858 [
4859 'WIDTH' => $row['WIDTH'],
4860 'HEIGHT' => $row['HEIGHT'],
4861 'LENGTH' => $row['LENGTH'],
4862 ]
4863 ),
4864 ];
4865 switch ($row['TYPE'])
4866 {
4867 case Catalog\ProductTable::TYPE_SET:
4868 $basketRow['TYPE'] = Sale\BasketItem::TYPE_SET;
4869 break;
4870 case Catalog\ProductTable::TYPE_SERVICE:
4871 $basketRow['TYPE'] = Sale\BasketItem::TYPE_SERVICE;
4872 break;
4873 default:
4874 $basketRow['TYPE'] = null;
4875 break;
4876 }
4877 foreach ($specialFields as $index)
4878 {
4879 $basketRow[$index] = $row[$index];
4880 }
4881
4882 $result[$productId] = array_merge(
4883 $result[$productId],
4884 $basketRow
4885 );
4886 }
4887
4888 $result[$productId]["VAT_INCLUDED"] = "Y";
4889 }
4890
4891 return $result;
4892 }
4893
4897 protected static function isReservationEnabled()
4898 {
4899 return !(Main\Config\Option::get("catalog", "enable_reservation") == "N"
4900 && Main\Config\Option::get("sale", "product_reserve_condition") != "S"
4901 && !Catalog\Config\State::isUsedInventoryManagement());
4902 }
4903
4910 public static function createOrderListFromProducts(array $products)
4911 {
4912 $productOrderList = array();
4913 foreach ($products as $productId => $productData)
4914 {
4915 if (!empty($productData['SHIPMENT_ITEM_LIST']))
4916 {
4921 foreach ($productData['SHIPMENT_ITEM_LIST'] as $shipmentItem)
4922 {
4923 $shipmentItemCollection = $shipmentItem->getCollection();
4924 if (!$shipmentItemCollection)
4925 {
4926 throw new Main\ObjectNotFoundException('Entity "ShipmentItemCollection" not found');
4927 }
4928
4929 $shipment = $shipmentItemCollection->getShipment();
4930 if (!$shipment)
4931 {
4932 throw new Main\ObjectNotFoundException('Entity "Shipment" not found');
4933 }
4934
4936 $shipmentCollection = $shipment->getCollection();
4937 if (!$shipmentCollection)
4938 {
4939 throw new Main\ObjectNotFoundException('Entity "ShipmentCollection" not found');
4940 }
4941
4942 $order = $shipmentCollection->getOrder();
4943 if (!$order)
4944 {
4945 throw new Main\ObjectNotFoundException('Entity "Order" not found');
4946 }
4947
4948 if (empty($productOrderList[$productId][$order->getId()]))
4949 {
4950 $productOrderList[$productId][$order->getId()] = $order;
4951 }
4952 }
4953 }
4954 }
4955
4956 return $productOrderList;
4957 }
4958
4964 private static function getProductCatalogInfo($productId): array
4965 {
4966 $productId = (int)$productId;
4967 if ($productId <= 0)
4968 {
4969 return [];
4970 }
4971
4972 $product = static::getHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productId);
4973 if (empty($product))
4974 {
4975 $iterator = Iblock\ElementTable::getList([
4976 'select' => [
4977 'ID',
4978 'IBLOCK_ID',
4979 'NAME',
4980 'IBLOCK_SECTION_ID',
4981 ],
4982 'filter' => \CIBlockElement::getPublicElementsOrmFilter(['=ID' => $productId]),
4983 ]);
4984 $product = $iterator->fetch();
4985 if ($product)
4986 {
4987 static::setHitCache(self::CACHE_ELEMENT_SHORT_DATA, $productId, $product);
4988 }
4989 }
4990
4991 return (empty($product)
4992 ? []
4993 : [
4994 "#PRODUCT_ID#" => $product['ID'],
4995 "#PRODUCT_NAME#" => $product['NAME'],
4996 ]
4997 );
4998 }
4999
5000 private static function getTotalAmountFromQuantityList(array $data): float
5001 {
5002 return self::getAmountFromSource(
5003 $data,
5004 [
5005 self::AMOUNT_SRC_STORE_QUANTITY_LIST,
5006 self::AMOUNT_SRC_QUANTITY,
5007 self::AMOUNT_SRC_QUANTITY_LIST,
5008 ]
5009 );
5010 }
5011
5012 private static function getTotalAmountFromPriceList(array $product, bool $direction = true): float
5013 {
5014 if ($direction)
5015 {
5016 $list = [
5017 self::AMOUNT_SRC_QUANTITY,
5018 self::AMOUNT_SRC_PRICE_LIST,
5019 ];
5020 }
5021 else
5022 {
5023 $list = [
5024 self::AMOUNT_SRC_PRICE_LIST,
5025 self::AMOUNT_SRC_QUANTITY,
5026 ];
5027 }
5028
5029 return self::getAmountFromSource($product, $list);
5030 }
5031
5032 private static function isCatalogDataEnabled(array $options): bool
5033 {
5034 return in_array(self::USE_GATALOG_DATA, $options);
5035 }
5036
5037 private static function fillCatalogXmlId(array $products, array $iblockProductMap): array
5038 {
5039 foreach ($iblockProductMap as $entityData)
5040 {
5041 if (empty($entityData['PRODUCT_LIST']) || !is_array($entityData['PRODUCT_LIST']))
5042 {
5043 continue;
5044 }
5045 foreach ($entityData['PRODUCT_LIST'] as $index)
5046 {
5047 if (!isset($products[$index]))
5048 {
5049 continue;
5050 }
5051 $products[$index]['PRODUCT_DATA']['CATALOG_XML_ID'] = $entityData['CATALOG_XML_ID'];
5052 }
5053 unset($index);
5054 }
5055 unset($entityData);
5056
5057 return $products;
5058 }
5059
5060 private static function fillOfferXmlId(array $products, array $catalogProductDataList): array
5061 {
5062 $offerList = [];
5063 foreach ($catalogProductDataList as $entityData)
5064 {
5065 if ($entityData['TYPE'] != Catalog\ProductTable::TYPE_OFFER)
5066 {
5067 continue;
5068 }
5069 if (strpos($products[$entityData['ID']]['PRODUCT_DATA']['~XML_ID'], '#') !== false)
5070 {
5071 continue;
5072 }
5073 $offerList[] = $entityData['ID'];
5074 }
5075 unset($entityData);
5076 if (!empty($offerList))
5077 {
5078 $parentMap = [];
5079 $parentIdList = [];
5080 $parentList = \CCatalogSku::getProductList($offerList, 0);
5081 foreach ($parentList as $offerId => $offerData)
5082 {
5083 $parentId = (int)$offerData['ID'];
5084 if (!isset($parentMap[$parentId]))
5085 {
5086 $parentMap[$parentId] = [];
5087 }
5088 $parentMap[$parentId][] = $offerId;
5089 $parentIdList[$parentId] = $parentId;
5090 }
5091 unset($offerId, $offerData, $parentList);
5092 if (!empty($parentMap))
5093 {
5094 sort($parentIdList);
5095 foreach (array_chunk($parentIdList, 500) as $pageIds)
5096 {
5097 $iterator = Iblock\ElementTable::getList([
5098 'select' => [
5099 'ID',
5100 'XML_ID',
5101 ],
5102 'filter' => ['@ID' => $pageIds],
5103 ]);
5104 while ($row = $iterator->fetch())
5105 {
5106 $parentId = (int)$row['ID'];
5107 if (empty($parentMap[$parentId]))
5108 {
5109 continue;
5110 }
5111 foreach ($parentMap[$parentId] as $index)
5112 {
5113 $products[$index]['PRODUCT_DATA']['~XML_ID'] = $row['XML_ID'] . '#'
5114 . $products[$index]['PRODUCT_DATA']['~XML_ID']
5115 ;
5116 }
5117 }
5118 unset($parentId, $index);
5119 unset($row, $iterator);
5120 }
5121 unset($pageIds);
5122 }
5123 unset($parentIdList, $parentMap);
5124 }
5125 unset($offerList);
5126
5127 return $products;
5128 }
5129
5130 private static function getPriceDataList(array $products, array $config): array
5131 {
5132 /*
5133 'IS_ADMIN_SECTION' => $adminSection,
5134 'USER_ID' => $userId,
5135 'SITE_ID' => $siteId,
5136 'CURRENCY' => $currency,
5137 */
5138 $userGroups = self::getUserGroups($config['USER_ID']);
5139
5140 \CCatalogProduct::GetVATDataByIDList(array_keys($products));
5141
5142 if ($config['IS_ADMIN_SECTION'])
5143 {
5144 if ($config['USER_ID'] > 0)
5145 {
5146 \CCatalogDiscountSave::SetDiscountUserID($config['USER_ID']);
5147 }
5148 else
5149 {
5150 \CCatalogDiscountSave::Disable();
5151 }
5152 }
5153
5154 Price\Calculation::pushConfig();
5155 Price\Calculation::setConfig([
5156 'CURRENCY' => $config['CURRENCY'],
5157 'PRECISION' => (int)Main\Config\Option::get('sale', 'value_precision'),
5158 'RESULT_WITH_VAT' => true,
5159 'RESULT_MODE' => Catalog\Product\Price\Calculation::RESULT_MODE_RAW,
5160 ]);
5161
5162 $priceDataList = \CCatalogProduct::GetOptimalPriceList(
5163 $products,
5164 $userGroups,
5165 'N',
5166 [],
5167 ($config['IS_ADMIN_SECTION'] ? $config['SITE_ID'] : false)
5168 );
5169
5170 if (empty($priceDataList))
5171 {
5172 $productsQuantityList = $products;
5173 $quantityCorrected = false;
5174
5175 foreach ($productsQuantityList as $productId => $productData)
5176 {
5177 $quantityList = array($productData['BASKET_CODE'] => $productData['QUANTITY']);
5178
5179 if (empty($productData[Base::FLAT_QUANTITY_LIST]))
5180 {
5181 $quantityList = $productData[Base::FLAT_QUANTITY_LIST];
5182 }
5183
5184 if (empty($quantityList))
5185 {
5186 continue;
5187 }
5188
5189 foreach ($quantityList as $basketCode => $quantity)
5190 {
5191 $nearestQuantity = \CCatalogProduct::GetNearestQuantityPrice($productId, $quantity, $userGroups);
5192 if (!empty($nearestQuantity))
5193 {
5194 if (!empty($productData[Base::FLAT_QUANTITY_LIST]))
5195 {
5196 $productsQuantityList[$productId][Base::FLAT_QUANTITY_LIST][$basketCode]['QUANTITY'] = $nearestQuantity;
5197 }
5198 else
5199 {
5200 $productsQuantityList[$productId]['QUANTITY'] = $nearestQuantity;
5201 }
5202
5203 $quantityCorrected = true;
5204 }
5205 }
5206 }
5207
5208 if ($quantityCorrected)
5209 {
5210 $priceDataList = \CCatalogProduct::GetOptimalPriceList(
5211 $productsQuantityList,
5212 $userGroups,
5213 'N',
5214 [],
5215 ($config['IS_ADMIN_SECTION'] ? $config['SITE_ID'] : false)
5216 );
5217 }
5218
5219 }
5220
5221 Price\Calculation::popConfig();
5222
5223 if ($config['IS_ADMIN_SECTION'])
5224 {
5225 if ($config['USER_ID'] > 0)
5226 {
5227 \CCatalogDiscountSave::ClearDiscountUserID();
5228 }
5229 else
5230 {
5231 \CCatalogDiscountSave::Enable();
5232 }
5233 }
5234
5235 if (!empty($priceDataList))
5236 {
5237 foreach ($priceDataList as $productId => $priceBasketDataList)
5238 {
5239 foreach ($priceBasketDataList as $basketCode => $priceData)
5240 {
5241 if ($priceData === false)
5242 {
5243 continue;
5244 }
5245
5246 if (empty($priceData['DISCOUNT_LIST']) && !empty($priceData['DISCOUNT']) && is_array($priceData['DISCOUNT']))
5247 {
5248 $priceDataList[$productId][$basketCode]['DISCOUNT_LIST'] = [$priceData['DISCOUNT']];
5249 }
5250
5251 if (empty($priceData['PRICE']['CATALOG_GROUP_NAME']))
5252 {
5253 if (!empty($priceData['PRICE']['CATALOG_GROUP_ID']))
5254 {
5255 $priceName = self::getPriceTitle($priceData['PRICE']['CATALOG_GROUP_ID']);
5256 if ($priceName !== '')
5257 {
5258 $priceDataList[$productId][$basketCode]['PRICE']['CATALOG_GROUP_NAME'] = $priceName;
5259 }
5260 unset($priceName);
5261 }
5262 }
5263 }
5264 }
5265 }
5266
5267 return $priceDataList;
5268 }
5269
5270 private static function getDiscountList(array $priceDataList): array
5271 {
5272 $discountList = array();
5273 if (!empty($priceDataList))
5274 {
5275 foreach ($priceDataList as $productId => $priceBasketDataList)
5276 {
5277 foreach ($priceBasketDataList as $basketCode => $priceData)
5278 {
5279 if ($priceData === false)
5280 {
5281 continue;
5282 }
5283
5284 if (empty($priceData['DISCOUNT_LIST']) && !empty($priceData['DISCOUNT']) && is_array($priceData['DISCOUNT']))
5285 {
5286 $priceDataList[$productId][$basketCode]['DISCOUNT_LIST'] = [$priceData['DISCOUNT']];
5287 }
5288
5289 if (!empty($priceData['DISCOUNT_LIST']))
5290 {
5291 if (!isset($discountList[$productId]))
5292 {
5293 $discountList[$productId] = [];
5294 }
5295 if (!isset($discountList[$productId][$basketCode]))
5296 {
5297 $discountList[$productId][$basketCode] = [];
5298 }
5299 foreach ($priceData['DISCOUNT_LIST'] as $discountItem)
5300 {
5301 $discountList[$productId][$basketCode][] = \CCatalogDiscount::getDiscountDescription($discountItem);
5302 }
5303 unset($discountItem);
5304 }
5305
5306 if (empty($priceData['PRICE']['CATALOG_GROUP_NAME']))
5307 {
5308 if (!empty($priceData['PRICE']['CATALOG_GROUP_ID']))
5309 {
5310 $priceName = self::getPriceTitle($priceData['PRICE']['CATALOG_GROUP_ID']);
5311 if ($priceName != '')
5312 {
5313 $priceDataList[$productId][$basketCode]['PRICE']['CATALOG_GROUP_NAME'] = $priceName;
5314 }
5315 unset($priceName);
5316 }
5317 }
5318 }
5319 }
5320 }
5321
5322 return $discountList;
5323 }
5324
5325 public static function getDefaultStoreId(): int
5326 {
5327 $result = parent::getDefaultStoreId();
5328 if (Catalog\Config\State::isUsedInventoryManagement())
5329 {
5330 $storeId = Catalog\StoreTable::getDefaultStoreId();
5331 if ($storeId !== null)
5332 {
5333 $result = $storeId;
5334 }
5335 }
5336
5337 return $result;
5338 }
5339
5340 private static function getAmountFromSource(array $product, array $sourceList): float
5341 {
5342 if (empty($product) || empty($sourceList))
5343 {
5344 return 0;
5345 }
5346
5347 $result = 0;
5348 $found = false;
5349 foreach ($sourceList as $source)
5350 {
5351 switch ($source)
5352 {
5353 case self::AMOUNT_SRC_QUANTITY:
5354 if (array_key_exists($source, $product))
5355 {
5356 $result = $product[$source];
5357 $found = true;
5358 }
5359 break;
5360 case self::AMOUNT_SRC_QUANTITY_LIST:
5361 case self::AMOUNT_SRC_RESERVED_LIST:
5362 if (
5363 !empty($product[$source])
5364 && is_array($product[$source])
5365 )
5366 {
5367 $result = array_sum($product[$source]);
5368 $found = true;
5369 }
5370 break;
5371 case self::AMOUNT_SRC_PRICE_LIST:
5372 if (
5373 !empty($product[$source])
5374 && is_array($product[$source])
5375 )
5376 {
5377 foreach ($product[$source] as $row)
5378 {
5379 if (!is_array($row) || !isset($row['QUANTITY']))
5380 {
5381 continue;
5382 }
5383 $result += (float)$row['QUANTITY'];
5384 }
5385 unset($row);
5386 $found = true;
5387 }
5388 break;
5389 case self::AMOUNT_SRC_STORE_QUANTITY_LIST:
5390 case self::AMOUNT_SRC_STORE_RESERVED_LIST:
5391 if (
5392 !empty($product[$source])
5393 && is_array($product[$source])
5394 )
5395 {
5396 switch (self::getQuantityFormat($product[$source]))
5397 {
5398 case self::QUANTITY_FORMAT_STORE:
5399 $internalResult = self::calculateQuantityFromStores($product[$source]);
5400 break;
5401 case self::QUANTITY_FORMAT_SHIPMENT:
5402 $internalResult = self::calculateQuantityFromShipments($product[$source]);
5403 break;
5404 default:
5405 $internalResult = null;
5406 break;
5407 }
5408 if ($internalResult !== null)
5409 {
5410 $result += array_sum($internalResult);
5411 $found = true;
5412 }
5413 unset($internalResult);
5414 }
5415 break;
5416 }
5417 if ($found)
5418 {
5419 break;
5420 }
5421 }
5422
5423 return (float)$result;
5424 }
5425
5426 private static function getStoreAmountFromQuantityList(array $data): ?array
5427 {
5428 return self::getStoreAmountFromSource(
5429 $data,
5430 [
5431 self::AMOUNT_SRC_STORE_QUANTITY_LIST,
5432 self::AMOUNT_SRC_QUANTITY_LIST,
5433 self::AMOUNT_SRC_QUANTITY,
5434 ]
5435 );
5436 }
5437
5438 private static function getStoreReservedQuantityFromProduct(array $product): ?array
5439 {
5440 return self::getStoreAmountFromSource(
5441 $product,
5442 [
5443 self::AMOUNT_SRC_STORE_RESERVED_LIST,
5444 self::AMOUNT_SRC_RESERVED_LIST,
5445 ]
5446 );
5447 }
5448
5449 private static function getStoreAmountFromPriceList(array $product, bool $direction = true): ?array
5450 {
5451 if ($direction)
5452 {
5453 $list = [
5454 self::AMOUNT_SRC_QUANTITY,
5455 self::AMOUNT_SRC_PRICE_LIST,
5456 ];
5457 }
5458 else
5459 {
5460 $list = [
5461 self::AMOUNT_SRC_PRICE_LIST,
5462 self::AMOUNT_SRC_QUANTITY,
5463 ];
5464 }
5465
5466 return self::getStoreAmountFromSource($product, $list);
5467 }
5468
5469 private static function getStoreAmountFromSource(array $product, array $sourceList): ?array
5470 {
5471 if (empty($product) || empty($sourceList))
5472 {
5473 return null;
5474 }
5475
5476 $result = [];
5477 $found = false;
5478 foreach ($sourceList as $source)
5479 {
5480 switch ($source)
5481 {
5482 case self::AMOUNT_SRC_STORE_QUANTITY_LIST:
5483 case self::AMOUNT_SRC_STORE_RESERVED_LIST:
5484 if (
5485 !empty($product[$source])
5486 && is_array($product[$source])
5487 )
5488 {
5489 switch (self::getQuantityFormat($product[$source]))
5490 {
5491 case self::QUANTITY_FORMAT_STORE:
5492 $internalResult = self::calculateQuantityFromStores($product[$source]);
5493 break;
5494 case self::QUANTITY_FORMAT_SHIPMENT:
5495 $internalResult = self::calculateQuantityFromShipments($product[$source]);
5496 break;
5497 default:
5498 $internalResult = null;
5499 break;
5500 }
5501 if ($internalResult !== null)
5502 {
5503 $result = $internalResult;
5504 $found = true;
5505 }
5506 unset($internalResult);
5507 }
5508 break;
5509 case self::AMOUNT_SRC_QUANTITY_LIST:
5510 case self::AMOUNT_SRC_RESERVED_LIST:
5511 /*
5512 'QUANTITY_LIST' =>
5513 array (
5514 289 => 1.0,
5515 290 => 3.0,
5516 291 => 4.0,
5517 ),
5518 */
5519 if (
5520 !empty($product[$source])
5521 && is_array($product[$source])
5522 )
5523 {
5524 $result[static::getDefaultStoreId()] = array_sum($product[$source]);
5525 $found = true;
5526 }
5527 break;
5528 case self::AMOUNT_SRC_QUANTITY:
5529 if (array_key_exists($source, $product))
5530 {
5531 $result[static::getDefaultStoreId()] = (float)$product[$source];
5532 $found = true;
5533 }
5534 break;
5535 }
5536 if ($found)
5537 {
5538 break;
5539 }
5540 }
5541
5542 return (!empty($result) ? $result : null);
5543 }
5544
5545 private static function getQuantityFormat(array $list): ?int
5546 {
5547 /*
5548 first variant
5549 'RESERVED_QUANTITY_LIST_BY_STORE' =>
5550 array (
5551 20 => basket code
5552 array (
5553 '0_0' => shipment index
5554 array (
5555 3 => 10.0, store id -> quantity
5556 ),
5557 ),
5558 ),
5559
5560 second variant
5561 'RESERVED_QUANTITY_LIST_BY_STORE' =>
5562 array (
5563 20 => basket code
5564 array (
5565 3 => 10.0, store id -> quantity
5566 ),
5567 ),
5568
5569 'QUANTITY_LIST_BY_STORE' =>
5570 array (
5571 289 => basket code
5572 array (
5573 5 => 1.0, store id => quantity
5574 ),
5575 290 =>
5576 array (
5577 5 => 3.0,
5578 ),
5579 291 =>
5580 array (
5581 5 => 4.0,
5582 ),
5583 ),
5584 ),
5585 */
5586
5587 $basketRow = reset($list);
5588 if (
5589 empty($basketRow)
5590 || !is_array($basketRow)
5591 )
5592 {
5593 return null;
5594 }
5595
5596 $row = reset($basketRow);
5597 if (is_array($row))
5598 {
5599 return self::QUANTITY_FORMAT_SHIPMENT;
5600 }
5601
5602 return self::QUANTITY_FORMAT_STORE;
5603 }
5604
5605 private static function calculateQuantityFromStores(array $list): ?array
5606 {
5607 $result = [];
5608 $found = false;
5609 foreach ($list as $basketItemStores)
5610 {
5611 if (
5612 empty($basketItemStores)
5613 || !is_array($basketItemStores)
5614 )
5615 {
5616 continue;
5617 }
5618 foreach ($basketItemStores as $storeId => $quantity)
5619 {
5620 if (!isset($result[$storeId]))
5621 {
5622 $result[$storeId] = 0.0;
5623 }
5624 $result[$storeId] += (float)$quantity;
5625 $found = true;
5626 }
5627 unset($storeId, $quantity);
5628 }
5629 unset($basketItemStores);
5630
5631 return ($found ? $result : null);
5632 }
5633
5634 private static function calculateQuantityFromShipments(array $list): ?array
5635 {
5636 $result = [];
5637 $found = false;
5638 foreach ($list as $basketItemShipments)
5639 {
5640 if (
5641 empty($basketItemShipments)
5642 || !is_array($basketItemShipments)
5643 )
5644 {
5645 continue;
5646 }
5647 foreach ($basketItemShipments as $basketItemStores)
5648 {
5649 foreach ($basketItemStores as $storeId => $quantity)
5650 {
5651 if (!isset($result[$storeId]))
5652 {
5653 $result[$storeId] = 0;
5654 }
5655 $result[$storeId] += (float)$quantity;
5656 $found = true;
5657 }
5658 }
5659 }
5660
5661 return ($found ? $result : null);
5662 }
5663
5664 private static function getStoreQuantityFromQuantityList(array $product): array
5665 {
5666 return self::getStoreQuantityFromSource(
5667 $product,
5668 [
5669 self::AMOUNT_SRC_STORE_QUANTITY_LIST,
5670 self::AMOUNT_SRC_QUANTITY_LIST,
5671 ]
5672 );
5673 }
5674
5675 private static function getStoreQuantityFromSource(array $product, array $sourceList): array
5676 {
5677 if (empty($product) || empty($sourceList))
5678 {
5679 return [static::getDefaultStoreId() => 0.0];
5680 }
5681
5682 $result = [];
5683 $found = false;
5684 foreach ($sourceList as $source)
5685 {
5686 switch ($source)
5687 {
5688 case self::AMOUNT_SRC_QUANTITY_LIST:
5689 case self::AMOUNT_SRC_RESERVED_LIST:
5690 if (
5691 !empty($product[$source])
5692 && is_array($product[$source])
5693 )
5694 {
5695 $result = [
5696 static::getDefaultStoreId() => (float)array_sum($product[$source]),
5697 ];
5698 $found = true;
5699 }
5700 break;
5701 /*case self::AMOUNT_SRC_PRICE_LIST:
5702 if (
5703 !empty($product[$source])
5704 && is_array($product[$source])
5705 )
5706 {
5707 foreach ($product[$source] as $row)
5708 {
5709 if (!is_array($row) || !isset($row['QUANTITY']))
5710 {
5711 continue;
5712 }
5713 $result += (float)$row['QUANTITY'];
5714 }
5715 unset($row);
5716 $found = true;
5717 }
5718 break; */
5719 case self::AMOUNT_SRC_STORE_QUANTITY_LIST:
5720 case self::AMOUNT_SRC_STORE_RESERVED_LIST:
5721 if (
5722 !empty($product[$source])
5723 && is_array($product[$source])
5724 )
5725 {
5726 switch (self::getQuantityFormat($product[$source]))
5727 {
5728 case self::QUANTITY_FORMAT_STORE:
5729 $internalResult = self::calculateQuantityFromStores($product[$source]);
5730 break;
5731 case self::QUANTITY_FORMAT_SHIPMENT:
5732 $internalResult = self::calculateQuantityFromShipments($product[$source]);
5733 break;
5734 default:
5735 $internalResult = null;
5736 break;
5737 }
5738 if ($internalResult !== null)
5739 {
5740 $result = $internalResult;
5741 $found = true;
5742 }
5743 }
5744 break;
5745 }
5746 if ($found)
5747 {
5748 break;
5749 }
5750 }
5751
5752 return (!empty($result)
5753 ? $result
5754 : [static::getDefaultStoreId() => 0.0]
5755 );
5756 }
5757
5758 private static function loadCurrentStoreReserve(int $productId, array $reserve): array
5759 {
5760 $result = [];
5761 foreach ($reserve as $storeId => $quantity)
5762 {
5763 $result[$storeId] = [
5764 'ID' => null,
5765 'PRODUCT_ID' => $productId,
5766 'STORE_ID' => $storeId,
5767 'ADD_QUANTITY_RESERVED' => $quantity,
5768 'QUANTITY_RESERVED' => 0.0,
5769 ];
5770 }
5771
5772 $iterator = Catalog\StoreProductTable::getList([
5773 'select' => [
5774 'ID',
5775 'STORE_ID',
5776 'QUANTITY_RESERVED',
5777 ],
5778 'filter' => [
5779 '=PRODUCT_ID' => $productId,
5780 '@STORE_ID' => array_keys($reserve),
5781 ],
5782 ]);
5783 while ($row = $iterator->fetch())
5784 {
5785 $storeId = (int)$row['STORE_ID'];
5786 $result[$storeId]['ID'] = (int)$row['ID'];
5787 $result[$storeId]['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED'];
5788 }
5789 unset($row, $iterator);
5790
5791 return $result;
5792 }
5793
5794 private static function loadCurrentProductStores(array $list): array
5795 {
5796 Main\Type\Collection::normalizeArrayValuesByInt($list, true);
5797 if (empty($list))
5798 {
5799 return [];
5800 }
5801
5802 $result = [];
5803 foreach (array_chunk($list, 500) as $pageIds)
5804 {
5805 $iterator = Catalog\StoreProductTable::getList([
5806 'select' => [
5807 'ID',
5808 'STORE_ID',
5809 'PRODUCT_ID',
5810 'AMOUNT',
5811 'QUANTITY_RESERVED',
5812 ],
5813 'filter' => [
5814 '@PRODUCT_ID' => $pageIds,
5815 '=STORE.ACTIVE' => 'Y',
5816 ],
5817 'order' => [
5818 'PRODUCT_ID' => 'ASC',
5819 'STORE_ID' => 'ASC',
5820 ],
5821 ]);
5822 while ($row = $iterator->fetch())
5823 {
5824 $row['ID'] = (int)$row['ID'];
5825 $row['PRODUCT_ID'] = (int)$row['PRODUCT_ID'];
5826 $row['STORE_ID'] = (int)$row['STORE_ID'];
5827 $row['AMOUNT'] = (float)$row['AMOUNT'];
5828 $row['QUANTITY_RESERVED'] = (float)$row['QUANTITY_RESERVED'];
5829
5830 $productId = $row['PRODUCT_ID'];
5831 $storeId = $row['STORE_ID'];
5832 if (!isset($result[$productId]))
5833 {
5834 $result[$productId] = [];
5835 }
5836 $result[$productId][$storeId] = $row;
5837 }
5838 unset($productId, $storeId);
5839 unset($row, $iterator);
5840 }
5841 unset($pageIds);
5842
5843 return $result;
5844 }
5845
5846 private static function convertErrors(Main\Entity\Result $result): void
5847 {
5848 global $APPLICATION;
5849
5850 $oldMessages = [];
5851 foreach ($result->getErrorMessages() as $errorText)
5852 {
5853 $oldMessages[] = [
5854 'text' => $errorText,
5855 ];
5856 }
5857 unset($errorText);
5858
5859 if (!empty($oldMessages))
5860 {
5861 $error = new \CAdminException($oldMessages);
5862 $APPLICATION->ThrowException($error);
5863 unset($error);
5864 }
5865 unset($oldMessages);
5866 }
5867 }
5868}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29