1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
product_provider.php
См. документацию.
1<?php
2
12use Bitrix\Sale;
13
14if (!Loader::includeModule('sale'))
15{
16 return false;
17}
18
24{
25 protected static $errors = array();
26
27 protected static $arOneTimeCoupons = array(); // deprecated
28 protected static $clearAutoCache = array();
29 protected static $catalogList = array();
30 protected static $userCache = array();
31 protected static $priceTitleCache = array();
32 protected static $proxyUserGroups = array();
33 protected static $proxyIblockElementListPermN = array();
34 protected static $proxyIblockElementListPermY = array();
35 protected static $proxyIblockRights = array();
36 protected static $proxyCatalogProduct = array();
37 protected static $proxyStoresCount = array();
38
39 protected static $hitCache = array();
40
42
43 public const CACHE_USER_GROUPS = 'USER_GROUPS';
44 public const CACHE_ITEM_WITHOUT_RIGHTS = 'IBLOCK_ELEMENT_PERM_N';
45 public const CACHE_ITEM_WITH_RIGHTS = 'IBLOCK_ELEMENT_PERM_Y';
46 public const CACHE_IBLOCK_RIGHTS_MODE = 'IBLOCK_RIGHTS_MODE';
47 public const CACHE_USER_RIGHTS = 'USER_RIGHT';
48 public const CACHE_PRODUCT = 'CATALOG_PRODUCT';
49 public const CACHE_VAT = 'VAT_INFO';
50 public const CACHE_IBLOCK_RIGHTS = 'IBLOCK_RIGHTS';
51 public const CACHE_STORE = 'CATALOG_STORE';
52 public const CACHE_STORE_PRODUCT = 'CATALOG_STORE_PRODUCT';
53 public const CACHE_PARENT_PRODUCT_ACTIVE = 'PARENT_PRODUCT_ACTIVE';
54
59 public static function GetProductData($arParams)
60 {
61 $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true);
62
64
65 if (!isset($arParams['QUANTITY']) || (float)$arParams['QUANTITY'] <= 0)
66 $arParams['QUANTITY'] = 0;
67
68 if ($useSaleDiscountOnly && !isset($arParams['CHECK_DISCOUNT']))
69 $arParams['CHECK_DISCOUNT'] = 'N';
70
71 $arParams['RENEWAL'] = (isset($arParams['RENEWAL']) && $arParams['RENEWAL'] == 'Y' ? 'Y' : 'N');
72 $arParams['CHECK_QUANTITY'] = (isset($arParams['CHECK_QUANTITY']) && $arParams["CHECK_QUANTITY"] == 'N' ? 'N' : 'Y');
73 $arParams['CHECK_PRICE'] = (isset($arParams['CHECK_PRICE']) && $arParams['CHECK_PRICE'] == 'N' ? 'N' : 'Y');
74 $arParams['CHECK_DISCOUNT'] = (isset($arParams['CHECK_DISCOUNT']) && $arParams['CHECK_DISCOUNT'] == 'N' ? 'N' : 'Y');
75 $arParams['CHECK_COUPONS'] = (isset($arParams['CHECK_COUPONS']) && $arParams['CHECK_COUPONS'] == 'N' ? 'N' : 'Y');
77 {
78 if ($arParams['CHECK_DISCOUNT'] == 'N')
79 {
80 $arParams['CHECK_COUPONS'] = 'N';
81 }
82 }
83
84 $arParams['AVAILABLE_QUANTITY'] = (isset($arParams['AVAILABLE_QUANTITY']) && $arParams['AVAILABLE_QUANTITY'] == 'Y' ? 'Y' : 'N');
85 $arParams['SELECT_QUANTITY_TRACE'] = (isset($arParams['SELECT_QUANTITY_TRACE']) && $arParams['SELECT_QUANTITY_TRACE'] == 'Y' ? 'Y' : 'N');
86 $arParams['SELECT_CHECK_MAX_QUANTITY'] = (isset($arParams['SELECT_CHECK_MAX_QUANTITY']) && $arParams['SELECT_CHECK_MAX_QUANTITY'] == 'Y' ? 'Y' : 'N');
87 $arParams['BASKET_ID'] = (string)(isset($arParams['BASKET_ID']) ? $arParams['BASKET_ID'] : '0');
88 $arParams['USER_ID'] = (isset($arParams['USER_ID']) ? (int)$arParams['USER_ID'] : 0);
89 if ($arParams['USER_ID'] < 0)
90 $arParams['USER_ID'] = 0;
91 $arParams['SITE_ID'] = (isset($arParams['SITE_ID']) ? $arParams['SITE_ID'] : false);
92 $strSiteID = $arParams['SITE_ID'];
93
94 $arParams['CURRENCY'] = (isset($arParams['CURRENCY']) ? Currency\CurrencyManager::checkCurrencyID($arParams['CURRENCY']) : false);
95 if ($arParams['CURRENCY'] === false)
96 $arParams['CURRENCY'] = CSaleLang::GetLangCurrency($strSiteID ? $strSiteID : SITE_ID);
97
98 $productID = (int)$arParams['PRODUCT_ID'];
99 $quantity = (float)$arParams['QUANTITY'];
100 $intUserID = (int)$arParams['USER_ID'];
101
102 global $USER, $APPLICATION;
103
104 $emptyResult = array();
105
106 if ($adminSection)
107 {
108 if (!$userGroups = static::getHitCache(self::CACHE_USER_GROUPS, $intUserID))
109 {
110 $userGroups = self::getUserGroups($intUserID);
111 static::setHitCache(self::CACHE_USER_GROUPS, $intUserID, $userGroups);
112 }
113
114 if (empty($userGroups))
115 return $emptyResult;
116 }
117 else
118 {
119 //TODO: fix for crm
120 $userGroups = array(2);
121 if (isset($USER) && $USER instanceof CUser)
122 $userGroups = $USER->GetUserGroupArray();
123 }
124
125 if (!$arProduct = static::getHitCache(self::CACHE_ITEM_WITH_RIGHTS, $productID))
126 {
127 $elementFilter = array(
128 'ID' => $productID,
129 'ACTIVE' => 'Y',
130 'ACTIVE_DATE' => 'Y',
131 'CHECK_PERMISSIONS' => 'Y',
132 'MIN_PERMISSION' => 'R'
133 );
134 if ($adminSection)
135 $elementFilter['PERMISSIONS_BY'] = $intUserID;
136
137 $iterator = CIBlockElement::GetList(
138 array(),
139 $elementFilter,
140 false,
141 false,
142 array('ID', 'IBLOCK_ID', 'NAME', 'DETAIL_PAGE_URL', 'XML_ID')
143 );
144 if ($arProduct = $iterator->GetNext())
145 static::setHitCache(self::CACHE_ITEM_WITH_RIGHTS, $productID, $arProduct);
146 unset($dbIBlockElement, $elementFilter);
147 }
148
149 if(empty($arProduct) || !is_array($arProduct))
150 return $emptyResult;
151
152 if (!isset(self::$catalogList[$arProduct['IBLOCK_ID']]))
153 {
154 self::$catalogList[$arProduct['IBLOCK_ID']] = Catalog\CatalogIblockTable::getList(array(
155 'select' => array('IBLOCK_ID', 'SUBSCRIPTION', 'PRODUCT_IBLOCK_ID', 'CATALOG_XML_ID' => 'IBLOCK.XML_ID'),
156 'filter' => array('=IBLOCK_ID' => $arProduct['IBLOCK_ID'])
157 ))->fetch();
158 }
159 if (empty(self::$catalogList[$arProduct['IBLOCK_ID']]) || !is_array(self::$catalogList[$arProduct['IBLOCK_ID']]))
160 return $emptyResult;
161 if (self::$catalogList[$arProduct['IBLOCK_ID']]['SUBSCRIPTION'] == 'Y')
162 $quantity = 1;
163
164 if (self::$catalogList[$arProduct['IBLOCK_ID']]['PRODUCT_IBLOCK_ID'] > 0)
165 {
166 if (!static::checkParentActivity($arProduct['ID'], $arProduct['IBLOCK_ID']))
167 return $emptyResult;
168 }
169
170 if (!$arCatalogProduct = static::getHitCache(self::CACHE_PRODUCT, $productID))
171 {
172 $select = array('ID', 'TYPE', 'AVAILABLE',
173 'QUANTITY', 'QUANTITY_TRACE', 'CAN_BUY_ZERO',
174 'QUANTITY_RESERVED',
175 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH',
176 'BARCODE_MULTI',
177 'MEASURE'
178 );
179 $select = array_merge($select, Catalog\Product\SystemField::getProviderSelectFields());
180 $arCatalogProduct = Catalog\ProductTable::getList(array(
181 'select' => $select,
182 'filter' => array('=ID' => $productID)
183 ))->fetch();
184 if (!empty($arCatalogProduct))
185 {
186 $arCatalogProduct['ID'] = (int)$arCatalogProduct['ID'];
187 $arCatalogProduct['TYPE'] = (int)$arCatalogProduct['TYPE'];
188 $arCatalogProduct['QUANTITY'] = (float)$arCatalogProduct['QUANTITY'];
189 $arCatalogProduct['QUANTITY_RESERVED'] = (float)$arCatalogProduct['QUANTITY_RESERVED'];
190 Catalog\Product\SystemField::prepareRow($arCatalogProduct, Catalog\Product\SystemField::OPERATION_PROVIDER);
191
192 static::setHitCache(self::CACHE_PRODUCT, $productID, $arCatalogProduct);
193 }
194 }
195 if (empty($arCatalogProduct) || !is_array($arCatalogProduct))
196 {
197 $APPLICATION->ThrowException(Loc::getMessage("CATALOG_ERR_NO_PRODUCT"), "CATALOG_NO_QUANTITY_PRODUCT");
198 return $emptyResult;
199 }
200
201 if (
202 ($arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_SKU || $arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
203 && (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
204 )
205 {
206 $APPLICATION->ThrowException(Loc::getMessage("CATALOG_ERR_SKU_PRODUCT"), 'CATALOG_SKU_PRODUCT');
207 return $emptyResult;
208 }
209
210 if (
211 $arParams["CHECK_QUANTITY"] == "Y"
212 && $arCatalogProduct['AVAILABLE'] != Catalog\ProductTable::STATUS_YES
213 )
214 {
215 $APPLICATION->ThrowException(
216 Loc::getMessage("CATALOG_NO_QUANTITY_PRODUCT", array("#NAME#" => $arProduct["NAME"])),
217 "CATALOG_NO_QUANTITY_PRODUCT"
218 );
219 return $emptyResult;
220 }
221
222 if ($arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_SET)
223 {
224 static::$errors = array();
225 if (!static::checkProductSet($productID))
226 {
227 $APPLICATION->ThrowException(implode(', ', static::$errors), 'NO_PRODUCT');
228 static::$errors = array();
229 return $emptyResult;
230 }
231 }
232
233 if ($arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_OFFER)
234 {
235 if (mb_strpos($arProduct["~XML_ID"], '#') === false)
236 {
237 $parent = \CCatalogSku::GetProductInfo($arProduct['ID'], $arProduct['IBLOCK_ID']);
238 if (!empty($parent))
239 {
240 $parentIterator = Iblock\ElementTable::getList([
241 'select' => ['ID', 'XML_ID'],
242 'filter' => ['=ID' => $parent['ID']]
243 ]);
244 $parentData = $parentIterator->fetch();
245 if (!empty($parentData))
246 {
247 $arProduct['~XML_ID'] = $parentData['XML_ID'].'#'.$arProduct['~XML_ID'];
248 }
249 unset($parentData, $parentIterator);
250 }
251 unset($parent);
252 }
253 }
254
255 $dblQuantity = $arCatalogProduct['QUANTITY'];
256 $quantityLimited = ($arCatalogProduct['QUANTITY_TRACE'] == Catalog\ProductTable::STATUS_YES
257 && $arCatalogProduct['CAN_BUY_ZERO'] == Catalog\ProductTable::STATUS_NO);
258 $quantityLimitExceeded = ($quantityLimited && $dblQuantity < $quantity);
259
260 $arCatalogProduct['MEASURE'] = (int)$arCatalogProduct['MEASURE'];
261 $arCatalogProduct['MEASURE_NAME'] = '';
262 $arCatalogProduct['MEASURE_CODE'] = 0;
263 if ($arCatalogProduct['MEASURE'] <= 0)
264 {
265 $arMeasure = CCatalogMeasure::getDefaultMeasure(true, true);
266 $arCatalogProduct['MEASURE_NAME'] = $arMeasure['~SYMBOL_RUS'];
267 $arCatalogProduct['MEASURE_CODE'] = $arMeasure['CODE'];
268 }
269 else
270 {
271 $rsMeasures = CCatalogMeasure::getList(
272 array(),
273 array('ID' => $arCatalogProduct['MEASURE']),
274 false,
275 false,
276 array('ID', 'SYMBOL_RUS', 'CODE')
277 );
278 if ($arMeasure = $rsMeasures->Fetch())
279 {
280 $arCatalogProduct['MEASURE_NAME'] = $arMeasure['SYMBOL_RUS'];
281 $arCatalogProduct['MEASURE_CODE'] = $arMeasure['CODE'];
282 }
283 }
284
286 "NAME" => $arProduct["~NAME"],
287 "CAN_BUY" => "Y",
288 "DETAIL_PAGE_URL" => $arProduct['~DETAIL_PAGE_URL'],
289 "BARCODE_MULTI" => $arCatalogProduct["BARCODE_MULTI"],
290 "WEIGHT" => (float)$arCatalogProduct['WEIGHT'],
291 "DIMENSIONS" => serialize(array(
292 "WIDTH" => $arCatalogProduct["WIDTH"],
293 "HEIGHT" => $arCatalogProduct["HEIGHT"],
294 "LENGTH" => $arCatalogProduct["LENGTH"]
295 )),
296 "VAT_INCLUDED" => "Y",
297 "MEASURE_ID" => $arCatalogProduct['MEASURE'],
298 "MEASURE_NAME" => $arCatalogProduct['MEASURE_NAME'],
299 "MEASURE_CODE" => $arCatalogProduct['MEASURE_CODE'],
300 "CATALOG_XML_ID" => self::$catalogList[$arProduct['IBLOCK_ID']]['CATALOG_XML_ID'],
301 "PRODUCT_XML_ID" => $arProduct['~XML_ID']
302 );
303 switch ($arCatalogProduct['TYPE'])
304 {
307 break;
310 break;
311 default:
312 $arResult['TYPE'] = null;
313 break;
314 }
315 foreach (Catalog\Product\SystemField::getProviderSelectFields() as $index => $value)
316 {
317 $field = is_string($index) ? $index : $value;
318 $arResult[$field] = $arCatalogProduct[$field];
319 }
320
321 if ($arParams['SELECT_QUANTITY_TRACE'] == "Y")
322 $arResult["QUANTITY_TRACE"] = $arCatalogProduct["QUANTITY_TRACE"];
323 if ($arParams['SELECT_CHECK_MAX_QUANTITY'] == 'Y')
324 $arResult['CHECK_MAX_QUANTITY'] = ($quantityLimited ? 'Y' : 'N');
325
326 if ($arParams["CHECK_QUANTITY"] == "Y")
327 {
328 $arResult["QUANTITY"] = ($quantityLimitExceeded ? $dblQuantity : $quantity);
329 if ($quantityLimitExceeded)
330 {
331 $APPLICATION->ThrowException(
333 "CATALOG_QUANTITY_NOT_ENOGH",
334 array(
335 "#NAME#" => $arProduct["NAME"],
336 "#CATALOG_QUANTITY#" => $dblQuantity,
337 "#QUANTITY#" => $quantity,
338 '#MEASURE_NAME#' => $arCatalogProduct['MEASURE_NAME']
339 )
340 ),
341 "CATALOG_QUANTITY_NOT_ENOGH"
342 );
343 }
344 }
345 else
346 {
347 $arResult["QUANTITY"] = $arParams["QUANTITY"];
348 }
349
350 if ($arParams["AVAILABLE_QUANTITY"] == "Y")
351 $arResult["AVAILABLE_QUANTITY"] = ($quantityLimitExceeded ? $dblQuantity : $quantity);
352
353 if ($arParams["CHECK_PRICE"] == "Y")
354 {
355 $productHash = array(
356 'MODULE_ID' => ($useSaleDiscountOnly ? 'sale' : 'catalog'),
357 'PRODUCT_ID' => $productID,
358 'BASKET_ID' => $arParams['BASKET_ID']
359 );
360
361 $arCoupons = array();
362 if ($arParams['CHECK_COUPONS'] == 'Y')
363 {
365 {
366 $arCoupons = DiscountCouponsManager::getForApply(array('MODULE_ID' => 'sale'), $productHash, true);
367 }
368 else
369 {
370 $arCoupons = DiscountCouponsManager::getForApply(array('MODULE_ID' => 'catalog'), $productHash, true);
371 }
372 if (!empty($arCoupons))
373 $arCoupons = array_keys($arCoupons);
374 }
375 if ($adminSection)
376 {
377 if ($intUserID > 0)
379 else
381 }
382
385 'CURRENCY' => $arParams['CURRENCY'],
386 'PRECISION' => (int)Main\Config\Option::get('sale', 'value_precision'),
387 'USE_DISCOUNTS' => $arParams['CHECK_DISCOUNT'] == 'Y',
388 'RESULT_WITH_VAT' => true,
390 ));
391
393 $productID,
394 $quantity,
395 $userGroups,
396 $arParams['RENEWAL'],
397 array(),
398 ($adminSection ? $strSiteID : false),
399 $arCoupons
400 );
401
402 if (empty($arPrice))
403 {
404 if ($nearestQuantity = CCatalogProduct::GetNearestQuantityPrice($productID, $quantity, $userGroups))
405 {
406 $quantity = $nearestQuantity;
408 $productID,
409 $quantity,
410 $userGroups,
411 $arParams['RENEWAL'],
412 array(),
413 ($adminSection ? $strSiteID : false),
414 $arCoupons
415 );
416 }
417 }
418
420
421 unset($userGroups);
422
423 if ($adminSection)
424 {
425 if ($intUserID > 0)
427 else
429 }
430
431 if (empty($arPrice))
432 return $emptyResult;
433
434 $arDiscountList = array();
435 if (empty($arPrice['DISCOUNT_LIST']) && !empty($arPrice['DISCOUNT']) && is_array($arPrice['DISCOUNT']))
436 $arPrice['DISCOUNT_LIST'] = array($arPrice['DISCOUNT']);
437 if (!empty($arPrice['DISCOUNT_LIST']))
438 {
439 $appliedCoupons = array();
440 foreach ($arPrice['DISCOUNT_LIST'] as &$arOneDiscount)
441 {
442 $arDiscountList[] = CCatalogDiscount::getDiscountDescription($arOneDiscount);
443
444 if (!empty($arOneDiscount['COUPON']))
445 $appliedCoupons[] = $arOneDiscount['COUPON'];
446 }
447 unset($arOneDiscount);
448 if (!empty($appliedCoupons))
449 $resultApply = DiscountCouponsManager::setApplyByProduct($productHash, $appliedCoupons);
450 unset($resultApply, $appliedCoupons);
451 }
452
453 if (empty($arPrice['PRICE']['CATALOG_GROUP_NAME']))
454 {
455 if (!empty($arPrice['PRICE']['CATALOG_GROUP_ID']))
456 {
457 $priceName = self::getPriceTitle($arPrice['PRICE']['CATALOG_GROUP_ID']);
458 if ($priceName != '')
459 $arPrice['PRICE']['CATALOG_GROUP_NAME'] = $priceName;
460 unset($priceName);
461 }
462 }
463
464 $arResult['PRODUCT_PRICE_ID'] = $arPrice['RESULT_PRICE']['ID'];
465 $arResult['NOTES'] = $arPrice['PRICE']['CATALOG_GROUP_NAME'];
466 $arResult['VAT_RATE'] = $arPrice['RESULT_PRICE']['VAT_RATE'];
467 $arResult['VAT_INCLUDED'] = $arPrice['RESULT_PRICE']['VAT_INCLUDED'];
468 $arResult['DISCOUNT_NAME'] = null;
469 $arResult['DISCOUNT_COUPON'] = null;
470 $arResult['DISCOUNT_VALUE'] = null;
471 $arResult['DISCOUNT_LIST'] = array();
472
473 if (empty($arPrice['RESULT_PRICE']) || !is_array($arPrice['RESULT_PRICE']))
474 $arPrice['RESULT_PRICE'] = CCatalogDiscount::calculateDiscountList($arPrice['PRICE'], $arParams['CURRENCY'], $arDiscountList, true);
475
476 $arResult['PRICE_TYPE_ID'] = $arPrice['RESULT_PRICE']['PRICE_TYPE_ID'];
477 $arResult['BASE_PRICE'] = $arPrice['RESULT_PRICE']['BASE_PRICE'];
478 $arResult['PRICE'] = $arPrice['RESULT_PRICE']['DISCOUNT_PRICE'];
479 $arResult['CURRENCY'] = $arPrice['RESULT_PRICE']['CURRENCY'];
480 $arResult['DISCOUNT_PRICE'] = $arPrice['RESULT_PRICE']['DISCOUNT'];
481 if ($arParams['CHECK_DISCOUNT'] == 'Y')
482 {
483 if (isset($arPrice['RESULT_PRICE']['PERCENT']))
484 $arResult['DISCOUNT_VALUE'] = ($arPrice['RESULT_PRICE']['PERCENT'] > 0 ? $arPrice['RESULT_PRICE']['PERCENT'] . '%' : null);
485 }
486
487 if (!empty($arDiscountList))
488 $arResult['DISCOUNT_LIST'] = $arDiscountList;
489 if (!empty($arPrice['DISCOUNT']))
490 {
491 $arResult['DISCOUNT_NAME'] = '['.$arPrice['DISCOUNT']['ID'].'] '.$arPrice['DISCOUNT']['NAME'];
492 if (!empty($arPrice['DISCOUNT']['COUPON']))
493 $arResult['DISCOUNT_COUPON'] = $arPrice['DISCOUNT']['COUPON'];
494
495 if (empty($arResult['DISCOUNT_LIST']))
496 $arResult['DISCOUNT_LIST'] = array($arPrice['DISCOUNT']);
497 }
498 }
499 else
500 {
501 $vatRate = null;
502
503 if (!$arVAT = static::getHitCache(self::CACHE_VAT, $productID))
504 {
505 $arVat = CCatalogProduct::GetVATDataByID($productID);
506 if (!empty($arVAT))
507 {
508 static::setHitCache(self::CACHE_VAT, $productID, $arVAT);
509 }
510 }
511
512 if (isset($arVAT) && is_array($arVAT))
513 {
514 if ($arVat['EXCLUDE_VAT'] === 'N')
515 {
516 $vatRate = $arVAT['RATE'] * 0.01;
517 }
518 }
519
520 $arResult['VAT_RATE'] = $vatRate;
521 }
522
523 return $arResult;
524 }
525
530 public static function OrderProduct($arParams)
531 {
532 $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true);
533
535
536 if ($useSaleDiscountOnly && !isset($arParams['CHECK_DISCOUNT']))
537 $arParams['CHECK_DISCOUNT'] = 'N';
538
539 $arParams['RENEWAL'] = (isset($arParams['RENEWAL']) && $arParams['RENEWAL'] == 'Y' ? 'Y' : 'N');
540 $arParams['CHECK_QUANTITY'] = (isset($arParams['CHECK_QUANTITY']) && $arParams['CHECK_QUANTITY'] == 'N' ? 'N' : 'Y');
541 $arParams['CHECK_DISCOUNT'] = (isset($arParams['CHECK_DISCOUNT']) && $arParams['CHECK_DISCOUNT'] == 'N' ? 'N' : 'Y');
542 $arParams['USER_ID'] = (isset($arParams['USER_ID']) ? (int)$arParams['USER_ID'] : 0);
543 if ($arParams['USER_ID'] < 0)
544 $arParams['USER_ID'] = 0;
545 $arParams['SITE_ID'] = (isset($arParams['SITE_ID']) ? $arParams['SITE_ID'] : false);
546 $strSiteID = $arParams['SITE_ID'];
547 $arParams['BASKET_ID'] = (string)(isset($arParams['BASKET_ID']) ? $arParams['BASKET_ID'] : '0');
548
549 $arParams['CURRENCY'] = (isset($arParams['CURRENCY']) ? Currency\CurrencyManager::checkCurrencyID($arParams['CURRENCY']) : false);
550 if ($arParams['CURRENCY'] === false)
551 $arParams['CURRENCY'] = CSaleLang::GetLangCurrency($strSiteID ? $strSiteID : SITE_ID);
552
553 global $USER;
554
555 $productID = (int)$arParams['PRODUCT_ID'];
556 $quantity = (float)$arParams['QUANTITY'];
557 $intUserID = (int)$arParams['USER_ID'];
558
559 $arResult = array();
560
561 if ($adminSection)
562 {
563 if ($intUserID == 0)
564 return $arResult;
565
566 if (!$userGroups = static::getHitCache(self::CACHE_USER_GROUPS, $intUserID))
567 {
568 $userGroups = self::getUserGroups($intUserID);
569 static::setHitCache(self::CACHE_USER_GROUPS, $intUserID, $userGroups);
570 }
571
572 if (empty($userGroups))
573 return $arResult;
574 }
575 else
576 {
577 $userGroups = array(2);
578 if (isset($USER) && $USER instanceof CUser)
579 $userGroups = $USER->GetUserGroupArray();
580 }
581
582 if (!$arProduct = static::getHitCache(self::CACHE_ITEM_WITH_RIGHTS, $productID))
583 {
584 $elementFilter = array(
585 'ID' => $productID,
586 'ACTIVE' => 'Y',
587 'ACTIVE_DATE' => 'Y',
588 'CHECK_PERMISSIONS' => 'Y',
589 'MIN_PERMISSION' => 'R'
590 );
591 if ($adminSection)
592 $elementFilter['PERMISSIONS_BY'] = $intUserID;
593
594 $iterator = CIBlockElement::GetList(
595 array(),
596 $elementFilter,
597 false,
598 false,
599 array('ID', 'IBLOCK_ID', 'NAME', 'DETAIL_PAGE_URL')
600 );
601 if ($arProduct = $iterator->GetNext())
602 static::setHitCache(self::CACHE_ITEM_WITH_RIGHTS, $productID, $arProduct);
603 unset($dbIBlockElement, $elementFilter);
604 }
605
606 if (empty($arProduct) || !is_array($arProduct))
607 return $arResult;
608
609 if (!static::checkParentActivity($arProduct['ID'], $arProduct['IBLOCK_ID']))
610 return $arResult;
611
612 if (!$arCatalogProduct = static::getHitCache(self::CACHE_PRODUCT, $productID))
613 {
614 $select = array('ID', 'TYPE', 'AVAILABLE',
615 'QUANTITY', 'QUANTITY_TRACE', 'CAN_BUY_ZERO',
616 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH',
617 'BARCODE_MULTI',
618 'MEASURE'
619 );
620 $select = array_merge($select, Catalog\Product\SystemField::getProviderSelectFields());
621 $arCatalogProduct = Catalog\ProductTable::getList(array(
622 'select' => $select,
623 'filter' => array('=ID' => $productID)
624 ))->fetch();
625 if (!empty($arCatalogProduct))
626 {
627 Catalog\Product\SystemField::prepareRow($arCatalogProduct, Catalog\Product\SystemField::OPERATION_PROVIDER);
628 $arCatalogProduct['QUANTITY'] = (float)$arCatalogProduct['QUANTITY'];
629 static::setHitCache(self::CACHE_PRODUCT, $productID, $arCatalogProduct);
630 }
631 }
632
633 if (empty($arCatalogProduct) || !is_array($arCatalogProduct))
634 return $arResult;
635
636 if (
637 ($arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_SKU || $arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
638 && (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
639 )
640 return $arResult;
641
642 if (
643 $arParams["CHECK_QUANTITY"] == "Y"
644 && $arCatalogProduct['AVAILABLE'] != Catalog\ProductTable::STATUS_YES
645 )
646 return $arResult;
647
648 if ($arCatalogProduct['TYPE'] == Catalog\ProductTable::TYPE_SET)
649 {
650 static::$errors = array();
651 if (!static::checkProductSet($productID))
652 {
653 static::$errors = array();
654 return $arResult;
655 }
656 }
657
658 if ($adminSection)
660
661 $productHash = array(
662 'MODULE' => ($useSaleDiscountOnly ? 'sale' : 'catalog'),
663 'PRODUCT_ID' => $productID,
664 'BASKET_ID' => $arParams['BASKET_ID']
665 );
666 $arCoupons = DiscountCouponsManager::getForApply(array(), $productHash, true);
667
668 if (!empty($arCoupons))
669 $arCoupons = array_keys($arCoupons);
670
673 'CURRENCY' => $arParams['CURRENCY'],
674 'PRECISION' => (int)Main\Config\Option::get('sale', 'value_precision'),
675 'USE_DISCOUNTS' => $arParams['CHECK_DISCOUNT'] == 'Y',
676 'RESULT_WITH_VAT' => true,
678 ));
679
681 $productID,
682 $quantity,
683 $userGroups,
684 $arParams['RENEWAL'],
685 array(),
686 ($adminSection ? $strSiteID : false),
687 $arCoupons
688 );
689
690 if (empty($arPrice))
691 {
692 if ($nearestQuantity = CCatalogProduct::GetNearestQuantityPrice($productID, $quantity, $userGroups))
693 {
694 $quantity = $nearestQuantity;
696 $productID,
697 $quantity,
698 $userGroups,
699 $arParams['RENEWAL'],
700 array(),
701 ($adminSection ? $strSiteID : false),
702 $arCoupons
703 );
704 }
705 }
706
708
709 unset($userGroups);
710 if ($adminSection)
712
713 if (empty($arPrice))
714 return $arResult;
715
716 $arDiscountList = array();
717 if (empty($arPrice['DISCOUNT_LIST']) && !empty($arPrice['DISCOUNT']) && is_array($arPrice['DISCOUNT']))
718 $arPrice['DISCOUNT_LIST'] = array($arPrice['DISCOUNT']);
719 if (!empty($arPrice['DISCOUNT_LIST']))
720 {
721 $appliedCoupons = array();
722 foreach ($arPrice['DISCOUNT_LIST'] as &$arOneDiscount)
723 {
724 $arDiscountList[] = CCatalogDiscount::getDiscountDescription($arOneDiscount);
725
726 if (!empty($arOneDiscount['COUPON']))
727 $appliedCoupons[] = $arOneDiscount['COUPON'];
728 }
729 unset($arOneDiscount);
730 if (!empty($appliedCoupons))
731 $resultApply = DiscountCouponsManager::setApplyByProduct($productHash, $appliedCoupons);
732 unset($resultApply, $appliedCoupons);
733 }
734
735 if (empty($arPrice['PRICE']['CATALOG_GROUP_NAME']))
736 {
737 if (!empty($arPrice['PRICE']['CATALOG_GROUP_ID']))
738 {
739 $priceName = self::getPriceTitle($arPrice['PRICE']['CATALOG_GROUP_ID']);
740 if ($priceName != '')
741 $arPrice['PRICE']['CATALOG_GROUP_NAME'] = $priceName;
742 unset($priceName);
743 }
744 }
745
746 if (empty($arPrice['RESULT_PRICE']) || !is_array($arPrice['RESULT_PRICE']))
747 $arPrice['RESULT_PRICE'] = CCatalogDiscount::calculateDiscountList($arPrice['PRICE'], $arParams['CURRENCY'], $arDiscountList, true);
748
750 'PRODUCT_PRICE_ID' => $arPrice['RESULT_PRICE']['ID'],
751// "AVAILABLE_QUANTITY" => $arCatalogProduct["QUANTITY"],
752 'PRICE_TYPE_ID' => $arPrice['RESULT_PRICE']['PRICE_TYPE_ID'],
753 'BASE_PRICE' => $arPrice['RESULT_PRICE']['BASE_PRICE'],
754 'PRICE' => $arPrice['RESULT_PRICE']['DISCOUNT_PRICE'],
755 'VAT_RATE' => $arPrice['RESULT_PRICE']['VAT_RATE'],
756 'VAT_INCLUDED' => $arPrice['RESULT_PRICE']['VAT_INCLUDED'],
757 "CURRENCY" => $arPrice['RESULT_PRICE']['CURRENCY'],
758 "WEIGHT" => (float)$arCatalogProduct["WEIGHT"],
759 "DIMENSIONS" => serialize(array(
760 "WIDTH" => $arCatalogProduct["WIDTH"],
761 "HEIGHT" => $arCatalogProduct["HEIGHT"],
762 "LENGTH" => $arCatalogProduct["LENGTH"]
763 )),
764 "NAME" => $arProduct["~NAME"],
765 "CAN_BUY" => "Y",
766 "DETAIL_PAGE_URL" => $arProduct['~DETAIL_PAGE_URL'],
767 "NOTES" => $arPrice["PRICE"]["CATALOG_GROUP_NAME"],
768 "DISCOUNT_PRICE" => $arPrice['RESULT_PRICE']['DISCOUNT'],
769 "DISCOUNT_VALUE" => ($arPrice['RESULT_PRICE']['PERCENT'] > 0 ? $arPrice['RESULT_PRICE']['PERCENT'].'%' : null),
770 "DISCOUNT_NAME" => null,
771 "DISCOUNT_COUPON" => null,
772 "DISCOUNT_LIST" => array()
773 );
774 switch ($arCatalogProduct['TYPE'])
775 {
778 break;
781 break;
782 default:
783 $arResult['TYPE'] = null;
784 break;
785 }
786 foreach (Catalog\Product\SystemField::getProviderSelectFields() as $index => $value)
787 {
788 $field = is_string($index) ? $index : $value;
789 $arResult[$field] = $arCatalogProduct[$field];
790 }
791
792 if ($arParams["CHECK_QUANTITY"] == "Y")
793 $arResult["QUANTITY"] = $quantity;
794 else
795 $arResult["QUANTITY"] = $arParams["QUANTITY"];
796
797 if (!empty($arDiscountList))
798 $arResult['DISCOUNT_LIST'] = $arDiscountList;
799 if (!empty($arPrice['DISCOUNT']))
800 {
801 $arResult['DISCOUNT_NAME'] = '['.$arPrice['DISCOUNT']['ID'].'] '.$arPrice['DISCOUNT']['NAME'];
802 if (!empty($arPrice['DISCOUNT']['COUPON']))
803 $arResult['DISCOUNT_COUPON'] = $arPrice['DISCOUNT']['COUPON'];
804
805 if (empty($arResult['DISCOUNT_LIST']))
806 $arResult['DISCOUNT_LIST'] = array($arPrice['DISCOUNT']);
807 }
808
809 return $arResult;
810 }
811
812 // in case product provider class is used,
813 // instead of this method quantity is changed with ReserveProduct and DeductProduct methods
814 public static function CancelProduct($arParams)
815 {
816 return true;
817 }
818
819 public static function DeliverProduct($arParams)
820 {
822 $arParams["PRODUCT_ID"],
823 $arParams["USER_ID"],
824 $arParams["PAID"],
825 $arParams["ORDER_ID"]
826 );
827 }
828
835 public static function ViewProduct($arParams)
836 {
837 if (!is_set($arParams["SITE_ID"]))
838 $arParams["SITE_ID"] = SITE_ID;
839
841 $arParams["PRODUCT_ID"],
842 $arParams["USER_ID"],
843 $arParams["SITE_ID"]
844 );
845 }
846
847 public static function RecurringOrderProduct($arParams)
848 {
850 $arParams["PRODUCT_ID"],
851 $arParams["USER_ID"]
852 );
853 }
854
855 public static function ReserveProduct($arParams)
856 {
857 global $APPLICATION;
858
859 $arRes = array();
860 $arFields = array();
861
862 $arParams["PRODUCT_ID"] = (int)$arParams["PRODUCT_ID"];
863 if ($arParams["PRODUCT_ID"] <= 0)
864 {
865 $APPLICATION->ThrowException(Loc::getMessage("RSRV_INCORRECT_ID"), "NO_ORDER_ID");
866 $arRes["RESULT"] = false;
867 return $arRes;
868 }
869
870 $disableReservation = !static::isReservationEnabled();
871
872 if ((string)$arParams["UNDO_RESERVATION"] != "Y")
873 $arParams["UNDO_RESERVATION"] = "N";
874
875 $arParams["QUANTITY_ADD"] = (float)$arParams["QUANTITY_ADD"];
876
878 'select' => [
879 'ID',
880 'CAN_BUY_ZERO',
881 'QUANTITY_TRACE',
882 'QUANTITY',
883 'WEIGHT',
884 'WIDTH',
885 'HEIGHT',
886 'LENGTH',
887 'BARCODE_MULTI',
888 'TYPE',
889 'QUANTITY_RESERVED',
890 ],
891 'filter' => ['=ID' => $arParams["PRODUCT_ID"]],
892 ]);
893 $arProduct = $iterator->fetch();
894 unset($iterator);
895 if (empty($arProduct))
896 {
897 $APPLICATION->ThrowException(Loc::getMessage("RSRV_ID_NOT_FOUND", array("#PRODUCT_ID#" => $arParams["PRODUCT_ID"])), "ID_NOT_FOUND");
898 $arRes["RESULT"] = false;
899 return $arRes;
900 }
901
902 if (
903 ($arProduct['TYPE'] == Catalog\ProductTable::TYPE_SKU || $arProduct['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
904 && Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
905 )
906 {
907 $APPLICATION->ThrowException(Loc::getMessage("RSRV_SKU_FOUND", array("#PRODUCT_ID#" => $arParams["PRODUCT_ID"])), "SKU_FOUND");
908 $arRes["RESULT"] = false;
909 return $arRes;
910 }
911 $arProduct["QUANTITY"] = (float)$arProduct["QUANTITY"];
912 $arProduct["QUANTITY_RESERVED"] = (float)$arProduct["QUANTITY_RESERVED"];
913
914 if ($disableReservation)
915 {
916 $startReservedQuantity = 0;
917
918 if ($arParams["UNDO_RESERVATION"] != "Y")
919 $arFields = array("QUANTITY" => $arProduct["QUANTITY"] - $arParams["QUANTITY_ADD"]);
920 else
921 $arFields = array("QUANTITY" => $arProduct["QUANTITY"] + $arParams["QUANTITY_ADD"]);
922
923 $internalResult = Catalog\Model\Product::update(
924 $arParams["PRODUCT_ID"],
926 );
927 $arRes["RESULT"] = $internalResult->isSuccess();
928 unset($internalResult);
929 }
930 else
931 {
932 if ($arProduct["QUANTITY_TRACE"] == "N" || (isset($arParams["ORDER_DEDUCTED"]) && $arParams["ORDER_DEDUCTED"] == "Y"))
933 {
934 $arRes["RESULT"] = true;
935 $arFields["QUANTITY_RESERVED"] = 0;
936 $startReservedQuantity = 0;
937 }
938 else
939 {
940 $startReservedQuantity = $arProduct["QUANTITY_RESERVED"];
941
942 if ($arParams["UNDO_RESERVATION"] == "N")
943 {
944 if ($arProduct["CAN_BUY_ZERO"] == "Y")
945 {
946 $arFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] + $arParams["QUANTITY_ADD"];
947
948 if ($arProduct["QUANTITY"] >= $arParams["QUANTITY_ADD"])
949 {
950 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $arParams["QUANTITY_ADD"];
951 }
952 elseif ($arProduct["QUANTITY"] < $arParams["QUANTITY_ADD"])
953 {
954 //reserve value, quantity will be negative
955 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $arParams["QUANTITY_ADD"];
956 }
957
958 $internalResult = Catalog\Model\Product::update(
959 $arParams["PRODUCT_ID"],
961 );
962 $arRes["RESULT"] = $internalResult->isSuccess();
963 unset($internalResult);
964 }
965 else //CAN_BUY_ZERO = N
966 {
967 if ($arProduct["QUANTITY"] >= $arParams["QUANTITY_ADD"])
968 {
969 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $arParams["QUANTITY_ADD"];
970 $arFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] + $arParams["QUANTITY_ADD"];
971 }
972 elseif ($arProduct["QUANTITY"] < $arParams["QUANTITY_ADD"])
973 {
974 //reserve only possible value, quantity = 0
975
976 $arRes["QUANTITY_NOT_RESERVED"] = $arParams["QUANTITY_ADD"] - $arProduct["QUANTITY"];
977
978 $arFields["QUANTITY"] = 0;
979 $arFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] + $arProduct["QUANTITY"];
980
981 $APPLICATION->ThrowException(Loc::getMessage("RSRV_QUANTITY_NOT_ENOUGH_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "ERROR_NOT_ENOUGH_QUANTITY");
982 }
983 $internalResult = Catalog\Model\Product::update(
984 $arParams["PRODUCT_ID"],
986 );
987 $arRes["RESULT"] = $internalResult->isSuccess();
988 unset($internalResult);
989 }
990 }
991 else //undo reservation
992 {
993 if ($arParams["QUANTITY_ADD"] > $arProduct["QUANTITY_RESERVED"])
994 {
995 $APPLICATION->ThrowException(
997 "DDCT_DEDUCTION_UNDO_ERROR_RESERVE_QUANTITY",
998 self::GetProductCatalogInfo($arParams["PRODUCT_ID"])
999 ),
1000 "RESERVATION_ERROR"
1001 );
1002 $arRes["RESULT"] = false;
1003
1004 static::clearHitCache(self::CACHE_PRODUCT);
1005
1006 $arRes['CAN_RESERVE'] = "Y";
1007
1008 return $arRes;
1009 }
1010 $arFields["QUANTITY"] = $arProduct["QUANTITY"] + $arParams["QUANTITY_ADD"];
1011
1012 $needReserved = $arProduct["QUANTITY_RESERVED"] - $arParams["QUANTITY_ADD"];
1013
1014 $arFields["QUANTITY_RESERVED"] = $needReserved;
1015
1016 $internalResult = Catalog\Model\Product::update(
1017 $arParams["PRODUCT_ID"],
1018 $arFields
1019 );
1020 $arRes["RESULT"] = $internalResult->isSuccess();
1021 unset($internalResult);
1022 }
1023 } //quantity trace
1024 }
1025
1026 if ($arRes["RESULT"])
1027 {
1028 $needReserved = $arFields["QUANTITY_RESERVED"] - $startReservedQuantity;
1029 if ($startReservedQuantity > $arFields["QUANTITY_RESERVED"])
1030 {
1031 $needReserved = $arFields["QUANTITY_RESERVED"];
1032 }
1033
1034 $arRes["QUANTITY_RESERVED"] = $needReserved;
1035 }
1036 else
1037 {
1038 $APPLICATION->ThrowException(Loc::getMessage("RSRV_UNKNOWN_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "UNKNOWN_RESERVATION_ERROR");
1039 }
1040
1041 static::clearHitCache(self::CACHE_PRODUCT);
1042
1043 $arRes['CAN_RESERVE'] = ($disableReservation ? "N" : "Y");
1044
1045 return $arRes;
1046 }
1047
1048 public static function DeductProduct($arParams)
1049 {
1050 global $APPLICATION;
1051
1052 $arRes = array();
1053 $arFields = array();
1054
1055 $basketItem = null;
1056
1058
1059 $disableReservation = !static::isReservationEnabled();
1060
1061
1062 if ((string)$arParams["UNDO_DEDUCTION"] != "Y")
1063 $arParams["UNDO_DEDUCTION"] = "N";
1064
1065
1066 if ((int)$arParams["PRODUCT_ID"] <= 0)
1067 {
1068 $APPLICATION->ThrowException(Loc::getMessage("RSRV_INCORRECT_ID"), "NO_ORDER_ID");
1069 $arRes["RESULT"] = false;
1070 return $arRes;
1071 }
1072
1073 $isOrderConverted = \Bitrix\Main\Config\Option::get("main", "~sale_converted_15", 'Y');
1074
1075 $arParams["QUANTITY"] = doubleval($arParams["QUANTITY"]);
1076
1077
1078
1079 if ((string)$arParams["EMULATE"] != "Y")
1080 $arParams["EMULATE"] = "N";
1081
1082 if ((string)$arParams["PRODUCT_RESERVED"] != "Y")
1083 $arParams["PRODUCT_RESERVED"] = "N";
1084
1085 if (!isset($arParams["STORE_DATA"]))
1086 $arParams["STORE_DATA"] = array();
1087
1088 if (!is_array($arParams["STORE_DATA"]))
1089 $arParams["STORE_DATA"] = array($arParams["STORE_DATA"]);
1090
1091 $basketItem = null;
1092 if (isset($arParams["BASKET_ITEM"]) && $isOrderConverted != 'N')
1093 {
1094 if ($arParams["BASKET_ITEM"] instanceof \Bitrix\Sale\BasketItem)
1095 {
1097 $basketItem = $arParams["BASKET_ITEM"];
1098 }
1099 }
1100
1101 $rsProducts = CCatalogProduct::GetList(
1102 array(),
1103 array('ID' => $arParams["PRODUCT_ID"]),
1104 false,
1105 false,
1106 array('ID', 'QUANTITY', 'QUANTITY_RESERVED', 'QUANTITY_TRACE', 'CAN_BUY_ZERO', 'TYPE')
1107 );
1108
1109 if ($arProduct = $rsProducts->Fetch())
1110 {
1111 if (
1112 ($arProduct['TYPE'] == Catalog\ProductTable::TYPE_SKU || $arProduct['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
1113 && (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
1114 )
1115 {
1116 $arRes["RESULT"] = false;
1117 }
1118 elseif ($arParams["UNDO_DEDUCTION"] == "N")
1119 {
1120 if ($arParams["EMULATE"] == "Y" || $arProduct["QUANTITY_TRACE"] == "N")
1121 {
1122 $arRes["RESULT"] = true;
1123 }
1124 else
1125 {
1126 if ($useStoreControl)
1127 {
1128
1129 if ($isOrderConverted != 'N' && empty($arParams["STORE_DATA"]) && $basketItem)
1130 {
1131 if (static::canProductAutoShip($basketItem))
1132 {
1133 $arParams["STORE_DATA"] = static::getProductStoreData($basketItem, $arParams["QUANTITY"]);
1134 }
1135 }
1136
1137
1138 $barcodeMulti = false;
1139 if ($isOrderConverted != 'N')
1140 {
1141 $barcodeMulti = $basketItem->isBarcodeMulti();
1142 }
1143
1144
1145 if ($barcodeMulti)
1146 {
1147 if (!empty($arParams["STORE_DATA"]))
1148 {
1149 foreach ($arParams["STORE_DATA"] as $id => $arRecord)
1150 {
1151 if (!empty($arRecord["BARCODE"]) && is_array($arRecord["BARCODE"]))
1152 {
1153 foreach ($arRecord["BARCODE"] as $barcodeId => $barcodeValue)
1154 {
1155 if (strval(trim($barcodeValue)) == "")
1156 {
1157 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", array_merge(self::GetProductCatalogInfo($arParams["PRODUCT_ID"]), array("#STORE_ID#" => $arRecord['STORE_ID']))), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY");
1158 $arRes["RESULT"] = false;
1159 return $arRes;
1160
1161 }
1162 }
1163 }
1164 else
1165 {
1166 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_MULTI_BARCODE_EMPTY", array_merge(self::GetProductCatalogInfo($arParams["PRODUCT_ID"]), array("#STORE_ID#" => $arRecord['STORE_ID']))), "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY");
1167 $arRes["RESULT"] = false;
1168 return $arRes;
1169 }
1170 }
1171 }
1172 }
1173
1174
1175 if (!empty($arParams["STORE_DATA"]))
1176 {
1177 foreach ($arParams["STORE_DATA"] as $id => $arRecord)
1178 {
1179 if (!empty($arRecord["BARCODE"]) && is_array($arRecord["BARCODE"]))
1180 {
1181 foreach($arRecord["BARCODE"] as $barcodeValue)
1182 {
1183 $arRes['BARCODE'][$barcodeValue] = false;
1184 }
1185 }
1186 }
1187
1188 $totalAmount = 0;
1189 foreach ($arParams["STORE_DATA"] as $id => $arRecord)
1190 {
1191 if (!isset($arRecord["STORE_ID"]) || intval($arRecord["STORE_ID"]) < 0 || !isset($arRecord["QUANTITY"]) || intval($arRecord["QUANTITY"]) < 0)
1192 {
1193 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DDCT_DEDUCTION_STORE_ERROR");
1194 $arRes["RESULT"] = false;
1195 return $arRes;
1196 }
1197
1199 array(),
1200 array(
1201 "PRODUCT_ID" => $arParams["PRODUCT_ID"],
1202 "STORE_ID" => $arRecord["STORE_ID"]
1203 ),
1204 false,
1205 false,
1206 array('ID', 'AMOUNT')
1207 );
1208 if ($arProp = $rsProps->Fetch())
1209 {
1210 if ($arProp["AMOUNT"] < $arRecord["QUANTITY"])
1211 {
1212 $APPLICATION->ThrowException(
1213 Loc::getMessage(
1214 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2',
1215 array_merge(
1216 self::GetProductCatalogInfo($arParams['PRODUCT_ID']),
1217 [
1218 '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($arRecord['STORE_ID']),
1219 '#STORE_ID#' => $arRecord['STORE_ID'],
1220 ]
1221 )
1222 ),
1223 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR'
1224 );
1225 $arRes["RESULT"] = false;
1226 return $arRes;
1227 }
1228 else
1229 {
1230 $res = CCatalogStoreProduct::Update($arProp["ID"], array("AMOUNT" => $arProp["AMOUNT"] - $arRecord["QUANTITY"]));
1231
1232 if ($res)
1233 {
1234 $arRes["STORES"][$arRecord["STORE_ID"]] = $arRecord["QUANTITY"];
1235 $totalAmount += $arRecord["QUANTITY"];
1236
1237
1238
1239 //deleting barcodes
1240 if (isset($arRecord["BARCODE"]) && is_array($arRecord["BARCODE"]) && count($arRecord["BARCODE"]) > 0)
1241 {
1242
1243 foreach ($arRecord["BARCODE"] as $barcodeId => $barcodeValue)
1244 {
1245 if (strval(trim($barcodeValue)) == "" || !$barcodeMulti)
1246 {
1247 continue;
1248 }
1249
1250 $arFields = array(
1251 "STORE_ID" => $arRecord["STORE_ID"],
1252 "BARCODE" => $barcodeValue,
1253 "PRODUCT_ID" => $arParams["PRODUCT_ID"]
1254 );
1255
1256 $dbres = CCatalogStoreBarcode::GetList(
1257 array(),
1258 $arFields,
1259 false,
1260 false,
1261 array("ID", "STORE_ID", "BARCODE", "PRODUCT_ID")
1262 );
1263
1264 if ($catalogStoreBarcodeRes = $dbres->Fetch())
1265 {
1266 CCatalogStoreBarcode::Delete($catalogStoreBarcodeRes["ID"]);
1267 $arRes['BARCODE'][$barcodeValue] = true;
1268 }
1269 else
1270 {
1271 $APPLICATION->ThrowException(
1272 Loc::getMessage(
1273 "DDCT_DEDUCTION_BARCODE_ERROR",
1274 array_merge(self::GetProductCatalogInfo($arParams["PRODUCT_ID"]), array("#BARCODE#" => $barcodeValue))
1275 ),
1276 "DDCT_DEDUCTION_BARCODE_ERROR"
1277 );
1278 $arRes['BARCODE'][$barcodeValue] = false;
1279 $arRes["RESULT"] = false;
1280 return $arRes;
1281 }
1282 }
1283 }
1284 }
1285 else
1286 {
1287 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_SAVE_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DDCT_DEDUCTION_SAVE_ERROR");
1288 $arRes["RESULT"] = false;
1289 return $arRes;
1290 }
1291
1292 }
1293 }
1294 }
1295
1296 //updating total sum
1297 if ($arParams["PRODUCT_RESERVED"] == "Y")
1298 {
1299 if ($totalAmount <= $arProduct["QUANTITY_RESERVED"])
1300 {
1301 $needReserved = $arProduct["QUANTITY_RESERVED"] - $totalAmount;
1302 if ($totalAmount > $arProduct["QUANTITY_RESERVED"])
1303 {
1304 $needReserved = $arProduct["QUANTITY_RESERVED"];
1305 }
1306
1307 $arFields["QUANTITY_RESERVED"] = $needReserved;
1308 }
1309 else if ($totalAmount <= $arProduct["QUANTITY_RESERVED"] + $arProduct["QUANTITY"])
1310 {
1311 $arFields["QUANTITY_RESERVED"] = 0;
1312 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - ($totalAmount - $arProduct["QUANTITY_RESERVED"]);
1313 }
1314 else //not enough products - don't deduct anything
1315 {
1316 $arRes["RESULT"] = false;
1317 return $arRes;
1318 }
1319 }
1320 else //product not reserved, use main quantity field to deduct from, quantity_reserved only if there is shortage in the main field
1321 {
1322 if ($totalAmount <= $arProduct["QUANTITY"])
1323 {
1324 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $totalAmount;
1325 }
1326 else if ($totalAmount <= $arProduct["QUANTITY_RESERVED"] + $arProduct["QUANTITY"])
1327 {
1328 $arFields["QUANTITY"] = 0;
1329
1330 $minusQuantity = ($totalAmount - $arProduct["QUANTITY"]);
1331
1332 $needReserved = $arProduct["QUANTITY_RESERVED"] - $minusQuantity;
1333 if ($minusQuantity > $arProduct["QUANTITY_RESERVED"])
1334 {
1335 $needReserved = $arProduct["QUANTITY_RESERVED"];
1336 }
1337
1338 $arFields["QUANTITY_RESERVED"] = $needReserved;
1339
1340 }
1341 else //not enough products - don't deduct anything
1342 {
1343 $arRes["RESULT"] = false;
1344 return $arRes;
1345 }
1346 }
1347
1349
1350 $arRes["RESULT"] = true;
1351 }
1352 else
1353 {
1354 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DEDUCTION_STORE_ERROR1");
1355 $arRes["RESULT"] = false;
1356 return $arRes;
1357 }
1358 }
1359 else // store control not used
1360 {
1361 if (($disableReservation && ($arParams['UNDO_DEDUCTION'] == "Y" || $arParams["PRODUCT_RESERVED"] == "N")) || !$disableReservation)
1362 {
1363 if ($arParams["QUANTITY"] <= $arProduct["QUANTITY_RESERVED"] + $arProduct["QUANTITY"])
1364 {
1365 if ($arParams["PRODUCT_RESERVED"] == "Y")
1366 {
1367 if ($arParams["QUANTITY"] <= $arProduct["QUANTITY_RESERVED"])
1368 {
1369
1370 $needReserved = $arProduct["QUANTITY_RESERVED"] - $arParams["QUANTITY"];
1371 if ($arParams["QUANTITY"] > $arProduct["QUANTITY_RESERVED"])
1372 {
1373 $needReserved = $arProduct["QUANTITY_RESERVED"];
1374 }
1375
1376 $arFields["QUANTITY_RESERVED"] = $needReserved;
1377 }
1378 else
1379 {
1380 $arFields["QUANTITY_RESERVED"] = 0;
1381 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - ($arParams["QUANTITY"] - $arProduct["QUANTITY_RESERVED"]);
1382 }
1383 }
1384 else //product not reserved, use main quantity field to deduct from, quantity_reserved only if there is shortage in the main field
1385 {
1386 if ($arParams["QUANTITY"] <= $arProduct["QUANTITY"])
1387 {
1388 $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $arParams["QUANTITY"];
1389 }
1390 else
1391 {
1392 $arFields["QUANTITY"] = 0;
1393
1394 $minusQuantity = ($arParams["QUANTITY"] - $arProduct["QUANTITY"]);
1395
1396 $needReserved = $arProduct["QUANTITY_RESERVED"] - $minusQuantity;
1397 if ($minusQuantity > $arProduct["QUANTITY_RESERVED"])
1398 {
1399 $needReserved = $arProduct["QUANTITY_RESERVED"];
1400 }
1401
1402 $arFields["QUANTITY_RESERVED"] = $needReserved;
1403 }
1404 }
1405
1406 $arRes["RESULT"] = CCatalogProduct::Update($arParams["PRODUCT_ID"], $arFields);
1407 }
1408 else //not enough products - don't deduct anything
1409 {
1410 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_QUANTITY_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DDCT_DEDUCTION_QUANTITY_ERROR");
1411 $arRes["RESULT"] = false;
1412 return $arRes;
1413 }
1414 }
1415 else
1416 {
1417 $arRes["RESULT"] = true;
1418 }
1419
1420 } //store control
1421 } //emulate /quantity trace
1422 }
1423 else //undo deduction
1424 {
1425 if ($arParams["EMULATE"] == "Y" || $arProduct["QUANTITY_TRACE"] == "N")
1426 {
1427 $arRes["RESULT"] = true;
1428 }
1429 else
1430 {
1431 if ($useStoreControl)
1432 {
1433 if ($isOrderConverted != 'N' && empty($arParams["STORE_DATA"]) && $basketItem)
1434 {
1435 if (static::canProductAutoShip($basketItem))
1436 {
1437 $arParams["STORE_DATA"] = static::getProductStoreData($basketItem, $arParams["QUANTITY"]);
1438 }
1439
1440 if (empty($arParams["STORE_DATA"]))
1441 {
1442 $arParams["STORE_DATA"] = static::getProductOneStoreData($basketItem, $arParams["QUANTITY"]);
1443 }
1444 }
1445
1446 if (!empty($arParams["STORE_DATA"]))
1447 {
1448 $totalAddedAmount = 0;
1449 foreach ($arParams["STORE_DATA"] as $id => $arRecord)
1450 {
1452 array(),
1453 array(
1454 "PRODUCT_ID" => $arParams["PRODUCT_ID"],
1455 "STORE_ID" => $arRecord["STORE_ID"]
1456 ),
1457 false,
1458 false,
1459 array('ID', 'AMOUNT')
1460 );
1461
1462 if ($arProp = $rsProps->Fetch())
1463 {
1465 $arProp["ID"],
1466 array("AMOUNT" => $arProp["AMOUNT"] + $arRecord["QUANTITY"])
1467 );
1468
1469 if ($res)
1470 {
1471 $arRes["STORES"][$arRecord["STORE_ID"]] = $arRecord["QUANTITY"];
1472 $totalAddedAmount += $arRecord["QUANTITY"];
1473
1474 $barcodeMulti = false;
1475 if ($isOrderConverted != 'N')
1476 {
1477 $barcodeMulti = $basketItem->isBarcodeMulti();
1478 }
1479
1480 //adding barcodes
1481 if (isset($arRecord["BARCODE"]))
1482 {
1483 if (!empty($arRecord["BARCODE"]) && is_array($arRecord["BARCODE"]))
1484 {
1485
1486 foreach ($arRecord["BARCODE"] as $barcodeValue)
1487 {
1488 if (strval(trim($barcodeValue)) == '' || (strval(trim($barcodeValue)) != '' && !$barcodeMulti))
1489 continue;
1490
1491 $arFields = array(
1492 "STORE_ID" => $arRecord["STORE_ID"],
1493 "BARCODE" => $barcodeValue,
1494 "PRODUCT_ID" => $arParams["PRODUCT_ID"]
1495 );
1496
1497 CCatalogStoreBarcode::Add($arFields);
1498 }
1499 }
1500 elseif (!is_array($arRecord["BARCODE"]))
1501 {
1502 $arFields = array(
1503 "STORE_ID" => $arRecord["STORE_ID"],
1504 "BARCODE" => $arRecord["BARCODE"],
1505 "PRODUCT_ID" => $arParams["PRODUCT_ID"]
1506 );
1507
1508 CCatalogStoreBarcode::Add($arFields);
1509 }
1510 }
1511 }
1512 else
1513 {
1514 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_SAVE_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DDCT_DEDUCTION_SAVE_ERROR");
1515 $arRes["RESULT"] = false;
1516 return $arRes;
1517 }
1518 }
1519 }
1520
1521 // $dbAmount = $DB->Query("SELECT SUM(AMOUNT) as AMOUNT FROM b_catalog_store_product WHERE PRODUCT_ID = ".$arParams["PRODUCT_ID"]." ", true);
1522 // if ($totalAddedAmount = $dbAmount->Fetch())
1523 // {
1524 // }
1525 if ($arParams["PRODUCT_RESERVED"] == "Y")
1526 {
1527 $arUpdateFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] + $totalAddedAmount;
1528 }
1529 else
1530 {
1531 $arUpdateFields["QUANTITY"] = $arProduct["QUANTITY"] + $totalAddedAmount;
1532 }
1533
1534 CCatalogProduct::Update($arParams["PRODUCT_ID"], $arUpdateFields);
1535
1536 $arRes["RESULT"] = true;
1537 }
1538 else
1539 {
1540 $APPLICATION->ThrowException(Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "DEDUCTION_STORE_ERROR2");
1541 $arRes["RESULT"] = false;
1542 return $arRes;
1543 }
1544 }
1545 else //store control not used
1546 {
1547 if ($arParams["PRODUCT_RESERVED"] == "Y" && !$disableReservation)
1548 {
1549 $arFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] + $arParams["QUANTITY"];
1550 // $arFields["QUANTITY"] = $arProduct["QUANTITY"] - $arParams["QUANTITY_RESERVED"];
1551 }
1552 else
1553 {
1554 $arFields["QUANTITY"] = $arProduct["QUANTITY"] + $arParams["QUANTITY"];
1555 // $arFields["QUANTITY_RESERVED"] = $arProduct["QUANTITY_RESERVED"] - $arParams["QUANTITY_RESERVED"];
1556 }
1557
1558 $arRes["RESULT"] = CCatalogProduct::Update($arParams["PRODUCT_ID"], $arFields);
1559 }
1560 } //emulate or quantity trace
1561 }
1562 }
1563 else
1564 {
1565 $arRes["RESULT"] = false;
1566 }
1567
1568 if (!$arRes["RESULT"])
1569 {
1570 $APPLICATION->ThrowException(Loc::getMessage("DDCT_UNKNOWN_ERROR", self::GetProductCatalogInfo($arParams["PRODUCT_ID"])), "UNKNOWN_DEDUCTION_ERROR");
1571 }
1572
1573 if ($arRes['RESULT'] === true)
1574 {
1575 static::clearHitCache(self::CACHE_PRODUCT);
1576 }
1577
1578 return $arRes;
1579 }
1580
1588 public static function tryShipmentProduct(\Bitrix\Sale\BasketItem $basketItem, $reserved = 'N', array $basketStoreData = array(), $quantity = null)
1589 {
1590 $result = new \Bitrix\Sale\Result();
1591
1592 $storesList = array();
1593
1595
1596 $productId = $basketItem->getProductId();
1597
1599 'select' => array('ID', 'TYPE', 'QUANTITY', 'QUANTITY_RESERVED', 'QUANTITY_TRACE', 'CAN_BUY_ZERO'),
1600 'filter' => array('=ID' => $productId)
1601 ))->fetch();
1602 if (empty($arProduct))
1603 {
1604 $result->addError( new \Bitrix\Sale\ResultError(Loc::getMessage("DDCT_DEDUCTION_PRODUCT_NOT_FOUND_ERROR", self::GetProductCatalogInfo($productId)), "DDCT_DEDUCTION_PRODUCT_NOT_FOUND_ERROR") );
1605 return $result;
1606 }
1607
1608 if (
1609 ($arProduct['TYPE'] == Catalog\ProductTable::TYPE_SKU || $arProduct['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
1610 && (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') != 'Y'
1611 )
1612 return $result;
1613
1614 if ($useStoreControl)
1615 {
1616 if (empty($basketStoreData))
1617 {
1618 if (static::canProductAutoShip($basketItem))
1619 {
1620 $basketStoreData = static::getProductStoreData($basketItem, $quantity);
1621 }
1622 }
1623
1624 if (!empty($basketStoreData))
1625 {
1626 $totalAmount = 0;
1627 foreach ($basketStoreData as $storeId => $basketStore)
1628 {
1629
1630 if (intval($storeId) < -1 || intval($storeId) == 0
1631 || !isset($basketStore["QUANTITY"]) || intval($basketStore["QUANTITY"]) < 0)
1632 {
1633 $result->addError( new \Bitrix\Sale\ResultError(Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::GetProductCatalogInfo($productId)), "DDCT_DEDUCTION_STORE_ERROR") );
1634
1635 return $result;
1636 }
1637
1638 if (intval($storeId) == -1)
1639 {
1640 $totalAmount = intval($basketStore["QUANTITY"]);
1641 }
1642 else
1643 {
1644
1645 $rsProps = CCatalogStoreProduct::GetList(
1646 array(),
1647 array(
1648 "PRODUCT_ID" => $productId,
1649 "STORE_ID" => $storeId
1650 ),
1651 false,
1652 false,
1653 array('ID', 'AMOUNT')
1654 );
1655 if ($arProp = $rsProps->Fetch())
1656 {
1657 if ($arProp["AMOUNT"] < $basketStore["QUANTITY"])
1658 {
1659 $result->addError( new \Bitrix\Sale\ResultError(
1661 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR_2',
1662 array_merge(
1663 self::GetProductCatalogInfo($productId),
1664 [
1665 '#STORE_NAME#' => \CCatalogStoreControlUtil::getStoreName($storeId),
1666 '#STORE_ID#' => $storeId,
1667 ]
1668 )
1669 ),
1670 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR'
1671 ));
1672 return $result;
1673 }
1674 else
1675 {
1676
1677 $storesList[$storeId] = $basketStore["QUANTITY"];
1678 $totalAmount += $basketStore["QUANTITY"];
1679
1680 //check barcodes
1681 if (isset($basketStore["BARCODE"]) && is_array($basketStore["BARCODE"]) && count($basketStore["BARCODE"]) > 0)
1682 {
1683 foreach ($basketStore["BARCODE"] as $barcodeId => $barcodeValue)
1684 {
1685 if (strval(trim($barcodeValue)) == "")
1686 {
1687 if ($basketItem->isBarcodeMulti())
1688 {
1689 $result->addError( new \Bitrix\Sale\ResultError(
1691 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY",
1692 array_merge(self::GetProductCatalogInfo($productId), array("#STORE_ID#" => $basketStore['STORE_ID']))
1693 ),
1694 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY"
1695 ));
1696 }
1697 continue;
1698 }
1699
1700 $arFields = array(
1701 "STORE_ID" => static::CATALOG_PROVIDER_EMPTY_STORE_ID,
1702 "BARCODE" => $barcodeValue,
1703 "PRODUCT_ID" => $productId
1704 );
1705
1706 if ($basketItem->isBarcodeMulti())
1707 {
1708 $arFields['STORE_ID'] = $storeId;
1709 }
1710
1711 $dbres = CCatalogStoreBarcode::GetList(
1712 array(),
1713 $arFields,
1714 false,
1715 false,
1716 array("ID", "STORE_ID", "BARCODE", "PRODUCT_ID")
1717 );
1718
1719 if (!$arRes = $dbres->Fetch())
1720 {
1721 $result->addError( new \Bitrix\Sale\ResultError(
1723 "DDCT_DEDUCTION_BARCODE_ERROR",
1724 array_merge(self::GetProductCatalogInfo($productId), array("#BARCODE#" => $barcodeValue))
1725 ),
1726 "DDCT_DEDUCTION_BARCODE_ERROR"
1727 ) );
1728 }
1729 }
1730 }
1731 elseif($basketItem->isBarcodeMulti())
1732 {
1733 $result->addError( new \Bitrix\Sale\ResultError(
1735 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY",
1736 array_merge(self::GetProductCatalogInfo($productId), array("#STORE_ID#" => $basketStore['STORE_ID']))
1737 ),
1738 "DDCT_DEDUCTION_MULTI_BARCODE_EMPTY"
1739 ));
1740 }
1741 }
1742 }
1743 }
1744
1745 if (!$result->isSuccess(true))
1746 {
1747 return $result;
1748 }
1749
1750 if ($reserved == 'Y')
1751 {
1752 $reservedPoolQuantity = static::getProductPoolQuantityByBasketItem($basketItem);
1753 $reservedQuantity = $arProduct["QUANTITY_RESERVED"] + floatval($reservedPoolQuantity);
1754 }
1755
1756
1757 $productQuantity = ($reserved == 'Y' ? $reservedQuantity : $arProduct["QUANTITY"]);
1758
1759 /*if (($totalAmount > $productQuantity)
1760 || ($totalAmount > $reservedQuantity + $arProduct["QUANTITY"]))*/
1761 if ($totalAmount > $arProduct["QUANTITY_RESERVED"] + $arProduct["QUANTITY"])
1762 {
1763 $result->addError( new \Bitrix\Sale\ResultError(
1764 Loc::getMessage("DDCT_DEDUCTION_SHIPMENT_QUANTITY_NOT_ENOUGH", self::GetProductCatalogInfo($productId)),
1765 "SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH"
1766 ));
1767 return $result;
1768 }
1769
1770 }
1771 }
1772 else
1773 {
1774 $result->addError( new \Bitrix\Sale\ResultError(
1775 Loc::getMessage("DDCT_DEDUCTION_STORE_ERROR", self::GetProductCatalogInfo($productId)),
1776 "DEDUCTION_STORE_ERROR1"
1777 ) );
1778 return $result;
1779 }
1780 }
1781 else // store control not used
1782 {
1783
1784 $reservedPoolQuantity = static::getProductPoolQuantityByBasketItem($basketItem);
1785 $reservedQuantity = $arProduct["QUANTITY_RESERVED"] + floatval($reservedPoolQuantity);
1786
1787 if ($arProduct["CAN_BUY_ZERO"] != "Y" && $arProduct["QUANTITY_TRACE"] == "Y")
1788 {
1789 if ($quantity > $reservedQuantity + $arProduct["QUANTITY"])
1790 {
1791 $result->addError( new \Bitrix\Sale\ResultError(
1792 Loc::getMessage("DDCT_DEDUCTION_QUANTITY_ERROR", self::GetProductCatalogInfo($productId)),
1793 "DDCT_DEDUCTION_QUANTITY_ERROR"
1794 ) );
1795 return $result;
1796 }
1797 }
1798
1799// $arRes["RESULT"] = true;
1800
1801 } //store control
1802
1803 return $result;
1804 }
1805
1806 public static function tryUnshipmentProduct($productId)
1807 {
1808 $result = new \Bitrix\Sale\Result();
1809 $fields = array();
1810
1811 $rsProducts = CCatalogProduct::GetList(
1812 array(),
1813 array('ID' => $productId),
1814 false,
1815 false,
1816 array('ID', 'QUANTITY', 'QUANTITY_RESERVED', 'QUANTITY_TRACE', 'CAN_BUY_ZERO', 'TYPE')
1817 );
1818
1819 if ($arProduct = $rsProducts->Fetch())
1820 {
1821 if (
1822 ($arProduct['TYPE'] != Catalog\ProductTable::TYPE_SKU && $arProduct['TYPE'] != Catalog\ProductTable::TYPE_EMPTY_SKU)
1823 || (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') == 'Y'
1824 )
1825 {
1826 $fields["QUANTITY_TRACE"] = ($arProduct["QUANTITY_TRACE"] == "Y");
1827 }
1828 }
1829
1830 if (!empty($fields))
1831 {
1832 $result->addData($fields);
1833 }
1834
1835
1836 return $result;
1837 }
1838
1839 public static function GetStoresCount($arParams = array())
1840 {
1841 //without store control stores are used for information purposes only
1842 if (!Catalog\Config\State::isUsedInventoryManagement())
1843 return -1;
1844
1845 return count(static::getStoreIds($arParams));
1846 }
1847
1848 public static function GetProductStores($arParams)
1849 {
1850 //without store control stores are used for information purposes only
1851 if (!Catalog\Config\State::isUsedInventoryManagement())
1852 return false;
1853
1854 $arParams['PRODUCT_ID'] = (isset($arParams['PRODUCT_ID']) ? (int)$arParams['PRODUCT_ID'] : 0);
1855 if ($arParams['PRODUCT_ID'] <= 0)
1856 return false;
1857
1858 $arParams['ENUM_BY_ID'] = (isset($arParams['ENUM_BY_ID']) && $arParams['ENUM_BY_ID'] == true);
1859
1860 $storeIds = static::getStoreIds($arParams);
1861 if (empty($storeIds))
1862 return false;
1863
1864 $cacheId = md5(serialize($arParams));
1865 if (!($result = static::getHitCache(self::CACHE_STORE_PRODUCT, $cacheId)))
1866 {
1867 $result = array();
1869 'select' => array('PRODUCT_ID', 'AMOUNT', 'STORE_ID', 'STORE_NAME' => 'STORE.TITLE'),
1870 'filter' => array('=PRODUCT_ID' => $arParams['PRODUCT_ID'], '@STORE_ID' => $storeIds),
1871 'order' => array('STORE_ID' => 'ASC')
1872 ));
1873 while ($row = $iterator->fetch())
1874 $result[$row['STORE_ID']] = $row;
1875 unset($row, $iterator);
1876 if (!empty($result))
1877 {
1878 if (!$arParams['ENUM_BY_ID'])
1879 $result = array_values($result);
1880 static::setHitCache(self::CACHE_STORE_PRODUCT, $cacheId, $result);
1881 }
1882 }
1883
1884 return (!empty($result) ? $result : false);
1885 }
1886
1887 public static function CheckProductBarcode($arParams)
1888 {
1889 $result = false;
1890
1891 $arFilter = array(
1892 "PRODUCT_ID" => $arParams["PRODUCT_ID"],
1893 "BARCODE" => $arParams["BARCODE"]
1894 );
1895
1896 if (isset($arParams["STORE_ID"]))
1897 $arFilter["STORE_ID"] = intval($arParams["STORE_ID"]);
1898
1899 $dbres = CCatalogStoreBarcode::GetList(
1900 array(),
1901 $arFilter
1902 );
1903 if ($res = $dbres->GetNext())
1904 $result = true;
1905
1906 return $result;
1907 }
1908
1909 private static function GetProductCatalogInfo($productID)
1910 {
1911 $productID = (int)$productID;
1912 if ($productID <= 0)
1913 return array();
1914
1915
1916 if (!$arProduct = static::getHitCache('IBLOCK_ELEMENT', $productID))
1917 {
1918 $dbProduct = CIBlockElement::GetList(array(), array("ID" => $productID), false, false, array('ID', 'IBLOCK_ID', 'NAME', 'IBLOCK_SECTION_ID'));
1919 if ($arProduct = $dbProduct->Fetch())
1920 {
1921 static::setHitCache('IBLOCK_ELEMENT', $productID, $arProduct);
1922 }
1923 }
1924
1925 return array(
1926 "#PRODUCT_ID#" => $arProduct["ID"],
1927 "#PRODUCT_NAME#" => $arProduct["NAME"],
1928 );
1929 }
1930
1931 public static function GetSetItems($productID, $intType, $arProducInfo = array())
1932 {
1933 static $proxyCatalogProductSet = array();
1934 static $proxyCatalogSkuData = array();
1935
1936 $arProductId = array();
1937 $proxyCatalogProductSetKey = $productID."|".$intType;
1938
1939 if (!isset($proxyCatalogProductSet[$proxyCatalogProductSetKey]))
1940 $proxyCatalogProductSet[$proxyCatalogProductSetKey] = CCatalogProductSet::getAllSetsByProduct($productID, $intType);
1941 $arSets = $proxyCatalogProductSet[$proxyCatalogProductSetKey];
1942
1943 if (is_array($arSets))
1944 {
1945 foreach ($arSets as $k => $arSet)
1946 {
1947 foreach ($arSet["ITEMS"] as $k1 => $item)
1948 {
1949 $arItem = self::GetProductData(array("PRODUCT_ID" => $item["ITEM_ID"], "QUANTITY" => $item["QUANTITY"], "CHECK_QUANTITY" => "N", "CHECK_PRICE" => "N"));
1950 if (array_key_exists('QUANTITY_TRACE', $arItem))
1951 unset($arItem['QUANTITY_TRACE']);
1952
1953 $arItem["PRODUCT_ID"] = $item["ITEM_ID"];
1954 $arItem["MODULE"] = "catalog";
1955 $arItem["PRODUCT_PROVIDER_CLASS"] = "CCatalogProductProvider";
1956 if ($intType == CCatalogProductSet::TYPE_SET)
1957 {
1958 $arItem['SET_DISCOUNT_PERCENT'] = ($item['DISCOUNT_PERCENT'] == '' ? false : (float)$item['DISCOUNT_PERCENT']);
1959 }
1960
1961 $arProductId[] = $item["ITEM_ID"];
1962
1963 $arItem["PROPS"] = array();
1964
1965 if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && is_array($proxyCatalogSkuData[$item["ITEM_ID"]]))
1966 {
1967 $arParentSku = $proxyCatalogSkuData[$item["ITEM_ID"]];
1968 }
1969 else
1970 {
1971 if ($arParentSku = CCatalogSku::GetProductInfo($item["ITEM_ID"]))
1972 {
1973 $proxyCatalogSkuData[$item["ITEM_ID"]] = $arParentSku;
1974 }
1975
1976 }
1977
1978 if (!empty($arParentSku))
1979 {
1980 $arPropsSku = array();
1981
1982 if (!$arProduct = static::getHitCache('IBLOCK_ELEMENT', $item["ITEM_ID"]))
1983 {
1984 $dbProduct = CIBlockElement::GetList(array(), array("ID" => $item["ITEM_ID"]), false, false, array('ID', 'IBLOCK_ID', 'NAME', 'IBLOCK_SECTION_ID'));
1985 if ($arProduct = $dbProduct->Fetch())
1986 {
1987 static::setHitCache('IBLOCK_ELEMENT', $item["ITEM_ID"], $arProduct);
1988 }
1989 }
1990
1991 if (!$arPropsSku = static::getHitCache('IBLOCK_PROPERTY', $arParentSku['OFFER_IBLOCK_ID']))
1992 {
1994 'select' => array('ID', 'IBLOCK_ID', 'CODE'),
1995 'filter' => array(
1996 '=IBLOCK_ID' => $arParentSku['OFFER_IBLOCK_ID'],
1997 '=ACTIVE' => 'Y',
1998 '=MULTIPLE' => 'N',
1999 '!=ID' => $arParentSku['SKU_PROPERTY_ID']
2000 ),
2001 'order' => array('SORT' => 'ASC')
2002 ));
2003 while ($row = $iterator->fetch())
2004 $arPropsSku[] = $row['CODE'];
2005 unset($row, $iterator);
2006
2007 static::setHitCache('IBLOCK_PROPERTY', $arParentSku['OFFER_IBLOCK_ID'], $arPropsSku);
2008 }
2009
2010 $proxyProductPropertyKey = $item["ITEM_ID"]."_".$arParentSku["IBLOCK_ID"]."_".md5(join('|', $arPropsSku));
2011
2012 if (!$product_properties = static::getHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey))
2013 {
2014 $product_properties = CIBlockPriceTools::GetOfferProperties(
2015 $item["ITEM_ID"],
2016 $arParentSku["IBLOCK_ID"],
2017 $arPropsSku
2018 );
2019
2020 static::setHitCache('PRODUCT_PROPERTY', $proxyProductPropertyKey, $product_properties);
2021 }
2022
2023 foreach ($product_properties as $propData)
2024 {
2025 $arItem["PROPS"][] = array(
2026 "NAME" => $propData["NAME"],
2027 "CODE" => $propData["CODE"],
2028 "VALUE" => $propData["VALUE"],
2029 "SORT" => $propData["SORT"]
2030 );
2031 }
2032 }
2033
2034 $arSets[$k]["ITEMS"][$k1] = array_merge($item, $arItem);
2035 }
2036 }
2037
2038 if (!$productList = static::getHitCache('IBLOCK_ELEMENT_LIST', $productID))
2039 {
2040 $rsProducts = CIBlockElement::GetList(
2041 array(),
2042 array('ID' => $arProductId),
2043 false,
2044 false,
2045 array("ID", "IBLOCK_ID", "IBLOCK_SECTION_ID", "PREVIEW_PICTURE", "DETAIL_PICTURE", "IBLOCK_TYPE_ID", "XML_ID")
2046 );
2047 while ($arProduct = $rsProducts->GetNext())
2048 {
2049 $productList[] = $arProduct;
2050 }
2051
2052 if (!empty($productList) && is_array($productList))
2053 {
2054 static::setHitCache('IBLOCK_ELEMENT_LIST', $productID, $productList);
2055 }
2056 }
2057
2058 if (!empty($productList) && is_array($productList))
2059 {
2060 foreach ($productList as $arProduct)
2061 {
2062 foreach ($arSets as $k => $arSet)
2063 {
2064 foreach ($arSet["ITEMS"] as $k1 => $item)
2065 {
2066 if ($item["ITEM_ID"] == $arProduct["ID"])
2067 {
2068 $arProps = array();
2069 $strIBlockXmlID = strval(CIBlock::GetArrayByID($arProduct['IBLOCK_ID'], 'XML_ID'));
2070 if ($strIBlockXmlID != "")
2071 {
2072 $arProps[] = array(
2073 "NAME" => "Catalog XML_ID",
2074 "CODE" => "CATALOG.XML_ID",
2075 "VALUE" => $strIBlockXmlID
2076 );
2077 }
2078
2079 if (!empty($proxyCatalogSkuData[$item["ITEM_ID"]]) && mb_strpos($arProduct["XML_ID"], '#') === false)
2080 {
2081 $arParentSku = $proxyCatalogSkuData[$item["ITEM_ID"]];
2082 if (!empty($proxyParentData[$arParentSku['ID']]) && is_array($proxyParentData[$arParentSku['ID']]))
2083 {
2084 $parentData = $proxyParentData[$arParentSku['ID']];
2085 }
2086 else
2087 {
2089 'select' => array('ID', 'XML_ID'),
2090 'filter' => array('ID' => $arParentSku['ID'])
2091 ));
2092 if ($parentData = $parentIterator->fetch())
2093 $proxyParentData[$arParentSku['ID']] = $parentData;
2094 unset($parentIterator);
2095 }
2096
2097 $arProduct["XML_ID"] = $parentData['XML_ID'].'#'.$arProduct["XML_ID"];
2098 unset($parentData);
2099 }
2100
2101 $arProps[] = array(
2102 "NAME" => "Product XML_ID",
2103 "CODE" => "PRODUCT.XML_ID",
2104 "VALUE" => $arProduct["XML_ID"]
2105 );
2106
2107 $arSets["$k"]["ITEMS"][$k1]["IBLOCK_ID"] = $arProduct["IBLOCK_ID"];
2108 $arSets["$k"]["ITEMS"][$k1]["IBLOCK_SECTION_ID"] = $arProduct["IBLOCK_SECTION_ID"];
2109 $arSets["$k"]["ITEMS"][$k1]["PREVIEW_PICTURE"] = $arProduct["PREVIEW_PICTURE"];
2110 $arSets["$k"]["ITEMS"][$k1]["DETAIL_PICTURE"] = $arProduct["DETAIL_PICTURE"];
2111 $arSets["$k"]["ITEMS"][$k1]["PRODUCT_XML_ID"] = $arProduct["XML_ID"];
2112 $arSets["$k"]["ITEMS"][$k1]["PROPS"] = array_merge($arSets["$k"]["ITEMS"][$k1]["PROPS"], $arProps);
2113 }
2114 }
2115 }
2116 }
2117 }
2118 }
2119
2120 foreach(GetModuleEvents("sale", "OnGetSetItems", true) as $arEvent)
2121 ExecuteModuleEventEx($arEvent, array(&$arSets));
2122
2123 return $arSets;
2124 }
2125
2126 protected static function isNeedClearPublicCache($currentQuantity, $newQuantity, $quantityTrace, $canBuyZero, $ratio = 1): bool
2127 {
2128 return false;
2129 }
2130
2131 protected static function clearPublicCache($productID, $productInfo = array()) {}
2132
2139 public static function getProductAvailableQuantity($productId, $userId = null)
2140 {
2141 $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true);
2142
2143 $userId = (isset($userId) ? (int)$userId : 0);
2144
2145 if ($userId < 0)
2146 $userId = 0;
2147
2148 static $arUserCache = array();
2149 if ($adminSection)
2150 {
2151 if ($userId == 0)
2152 return false;
2153
2154 if (!isset($arUserCache[$userId]))
2155 {
2156 $userIterator = Main\UserTable::getList(array(
2157 'select' => array('ID'),
2158 'filter' => array('=ID' => $userId)
2159 ));
2160 if ($userDat = $userIterator->fetch())
2161 {
2162 $userDat['ID'] = (int)$userDat['ID'];
2163 $arUserCache[$userDat['ID']] = CUser::GetUserGroup($userDat['ID']);
2164 }
2165 else
2166 {
2167 return false;
2168 }
2169 }
2170
2171 $dbIBlockElement = CIBlockElement::GetList(
2172 array(),
2173 array(
2174 'ID' => $productId,
2175 'ACTIVE' => 'Y',
2176 'ACTIVE_DATE' => 'Y',
2177 'CHECK_PERMISSION' => 'N'
2178 ),
2179 false,
2180 false,
2181 array('ID', 'IBLOCK_ID', 'NAME', 'DETAIL_PAGE_URL')
2182 );
2183 if (!($arProduct = $dbIBlockElement->GetNext()))
2184 return false;
2185
2186
2187 $iblockRights = null;
2188
2189 if (!$iblockRights = static::getHitCache(self::CACHE_IBLOCK_RIGHTS_MODE, $arProduct['IBLOCK_ID']))
2190 {
2191 if ($iblockRights = CIBlock::GetArrayByID($arProduct['IBLOCK_ID'], 'RIGHTS_MODE'))
2192 {
2193 static::setHitCache(self::CACHE_IBLOCK_RIGHTS_MODE, $arProduct['IBLOCK_ID'], $iblockRights);
2194 }
2195 }
2196
2197 if ($iblockRights == 'E')
2198 {
2199 $proxyUserPermissionKey = $productId."|".$userId;
2200
2201 if (!$arUserRights = static::getHitCache(self::CACHE_USER_RIGHTS, $proxyUserPermissionKey))
2202 {
2203 if ($arUserRights = CIBlockElementRights::GetUserOperations($productId, $userId))
2204 {
2205 static::setHitCache(self::CACHE_USER_RIGHTS, $proxyUserPermissionKey, $arUserRights);
2206 }
2207 }
2208
2209 if (empty($arUserRights) || !isset($arUserRights['element_read']))
2210 return false;
2211
2212 unset($arUserRights);
2213 }
2214 else
2215 {
2216 if (CIBlock::GetPermission($arProduct['IBLOCK_ID'], $userId) < 'R')
2217 return false;
2218 }
2219 }
2220 else
2221 {
2222 $dbIBlockElement = CIBlockElement::GetList(
2223 array(),
2224 array(
2225 'ID' => $productId,
2226 'ACTIVE' => 'Y',
2227 'ACTIVE_DATE' => 'Y',
2228 'CHECK_PERMISSIONS' => 'Y',
2229 'MIN_PERMISSION' => 'R'
2230 ),
2231 false,
2232 false,
2233 array('ID', 'IBLOCK_ID', 'NAME', 'DETAIL_PAGE_URL')
2234 );
2235 if (!($arProduct = $dbIBlockElement->GetNext()))
2236 return false;
2237 }
2238
2239 $rsProducts = CCatalogProduct::GetList(
2240 array(),
2241 array('ID' => $productId),
2242 false,
2243 false,
2244 array(
2245 'ID',
2246 'QUANTITY',
2247 'TYPE'
2248 )
2249 );
2250
2251 if ($arCatalogProduct = $rsProducts->Fetch())
2252 {
2253 if (
2254 ($arCatalogProduct['TYPE'] != Catalog\ProductTable::TYPE_SKU && $arCatalogProduct['TYPE'] != Catalog\ProductTable::TYPE_EMPTY_SKU)
2255 || (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') == 'Y'
2256 )
2257 {
2258 return $arCatalogProduct['QUANTITY'];
2259 }
2260 }
2261
2262 return false;
2263 }
2264
2265 protected static function getUserGroups($userId)
2266 {
2267 $userId = (int)$userId;
2268 if ($userId < 0)
2269 return false;
2270
2271 if (!isset(self::$userCache[$userId]))
2272 self::$userCache[$userId] = Main\UserTable::getUserGroupIds($userId);
2273
2274 return self::$userCache[$userId];
2275 }
2276
2277 protected static function getProductPoolQuantityByBasketItem(\Bitrix\Sale\BasketItem $basketItem)
2278 {
2280 if (!$basket = $basketItem->getCollection())
2281 return false;
2282
2284 if (!$order = $basket->getOrder())
2285 return false;
2286
2287 return \Bitrix\Sale\Provider::getReservationPoolItem($order->getInternalId(), $basketItem);
2288 }
2289
2290 protected static function getPriceTitle($priceType)
2291 {
2292 $priceType = (int)$priceType;
2293 if ($priceType <= 0)
2294 return '';
2295 if (!isset(self::$priceTitleCache[$priceType]))
2296 {
2297 self::$priceTitleCache[$priceType] = '';
2299 'select' => array('ID', 'NAME', 'NAME_LANG' => 'CURRENT_LANG.NAME'),
2300 'filter' => array('=ID' => $priceType)
2301 ))->fetch();
2302 if (!empty($group))
2303 {
2304 $group['NAME_LANG'] = (string)$group['NAME_LANG'];
2305 self::$priceTitleCache[$priceType] = ($group['NAME_LANG'] != '' ? $group['NAME_LANG'] : $group['NAME']);
2306 }
2307 unset($group);
2308 }
2309 return self::$priceTitleCache[$priceType];
2310 }
2311
2319 protected static function checkParentActivity($productId, $iblockId = 0)
2320 {
2321 $cacheKey = $productId.'|'.$iblockId;
2322 if (!static::isExistsHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey))
2323 {
2324 $result = 'Y';
2325 $parent = CCatalogSku::GetProductInfo($productId, $iblockId);
2326 if (!empty($parent))
2327 {
2328 $itemList = CIBlockElement::GetList(
2329 array(),
2330 array(
2331 'ID' => $parent['ID'],
2332 'IBLOCK_ID' => $parent['IBLOCK_ID'],
2333 'ACTIVE' => 'Y',
2334 'ACTIVE_DATE' => 'Y',
2335 'CHECK_PERMISSIONS' => 'N'
2336 ),
2337 false,
2338 false,
2339 array('ID')
2340 );
2341 $item = $itemList->Fetch();
2342 unset($itemList);
2343 if (empty($item))
2344 $result = 'N';
2345 }
2346 static::setHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey, $result);
2347 unset($result);
2348 }
2349 return (static::getHitCache(self::CACHE_PARENT_PRODUCT_ACTIVE, $cacheKey) != 'N');
2350 }
2351
2358 public static function getHitCache($type, $key)
2359 {
2360 if (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key]))
2361 return self::$hitCache[$type][$key];
2362
2363 return false;
2364 }
2365
2372 public static function isExistsHitCache($type, $key)
2373 {
2374 return (!empty(self::$hitCache[$type]) && !empty(self::$hitCache[$type][$key]));
2375 }
2376
2384 public static function setHitCache($type, $key, $value)
2385 {
2386 if (empty(self::$hitCache[$type]))
2387 self::$hitCache[$type] = array();
2388
2389 if (empty(self::$hitCache[$type][$key]))
2390 self::$hitCache[$type][$key] = array();
2391
2392 self::$hitCache[$type][$key] = $value;
2393 }
2394
2400 public static function clearHitCache($type = null)
2401 {
2402 if ($type === null)
2403 self::$hitCache = array();
2404 elseif (!empty(self::$hitCache[$type]))
2405 unset(self::$hitCache[$type]);
2406 }
2407
2414 protected static function canProductAutoShip(\Bitrix\Sale\BasketItem $basketItem)
2415 {
2416 $countStores = static::GetStoresCount(array('SITE_ID' => $basketItem->getField('LID')));
2417 $defaultDeductionStore = Main\Config\Option::get("sale", "deduct_store_id", "", $basketItem->getField('LID'));
2418
2419 $canAutoDeduct = (($countStores == 1 || $countStores == -1 || $defaultDeductionStore > 0) && !$basketItem->isBarcodeMulti());
2420
2421 $countProductStores = 0;
2422
2423 if ($canAutoDeduct === true)
2424 return true;
2425
2426 if ($productStore = static::GetProductStores(array(
2427 'PRODUCT_ID' => $basketItem->getProductId(),
2428 'SITE_ID' => $basketItem->getField('LID')
2429 )))
2430 {
2431 foreach ($productStore as $productStoreItem)
2432 {
2433 if ($productStoreItem['AMOUNT'] > 0)
2434 {
2435 $countProductStores++;
2436 }
2437 }
2438 }
2439
2440 return ($countProductStores == 1);
2441 }
2442
2449 protected static function getProductStoreData(\Bitrix\Sale\BasketItem $basketItem, $quantity)
2450 {
2451 $productStoreData = array();
2452
2453 if ($productStore = static::GetProductStores(array(
2454 'PRODUCT_ID' => $basketItem->getProductId(),
2455 'SITE_ID' => $basketItem->getField('LID')
2456 )))
2457 {
2458 foreach ($productStore as $productStoreItem)
2459 {
2460 if ($productStoreItem['AMOUNT'] > 0)
2461 {
2462 $productStoreData = array(
2463 $productStoreItem['STORE_ID'] => array(
2464 'STORE_ID' => $productStoreItem['STORE_ID'],
2465 'QUANTITY' => $quantity
2466 )
2467 );
2468 break;
2469 }
2470 }
2471 }
2472
2473 return (!empty($productStoreData) ? $productStoreData : false);
2474 }
2475
2482 protected static function getProductOneStoreData(\Bitrix\Sale\BasketItem $basketItem, $quantity)
2483 {
2484 $productStoreData = array();
2485
2486 if ($productStore = static::GetProductStores(array(
2487 'PRODUCT_ID' => $basketItem->getProductId(),
2488 'SITE_ID' => $basketItem->getField('LID')
2489 )))
2490 {
2491 if (count($productStore) != 1)
2492 {
2493 return false;
2494 }
2495
2496 foreach ($productStore as $productStoreItem)
2497 {
2498 $productStoreData = array(
2499 $productStoreItem['STORE_ID'] => array(
2500 'STORE_ID' => $productStoreItem['STORE_ID'],
2501 'QUANTITY' => $quantity,
2502 )
2503 );
2504 }
2505 }
2506
2507 return (!empty($productStoreData) ? $productStoreData : false);
2508 }
2509
2510 protected static function getStoreIds(array $params)
2511 {
2512 $filterId = array('ACTIVE' => 'Y');
2513 if (isset($params['SITE_ID']) && $params['SITE_ID'] != '')
2514 $filterId['+SITE_ID'] = $params['SITE_ID'];
2515
2516 $cacheId = md5(serialize($filterId));
2517 $storeIds = static::getHitCache(self::CACHE_STORE, $cacheId);
2518 if (empty($storeIds))
2519 {
2520 $storeIds = array();
2521
2522 $filter = Main\Entity\Query::filter();
2523 $filter->where('ACTIVE', '=', 'Y');
2524 if (isset($params['SITE_ID']) && $params['SITE_ID'] != '')
2525 {
2526 $subFilter = Main\Entity\Query::filter();
2527 $subFilter->logic('or')->where('SITE_ID', '=', $params['SITE_ID'])->where('SITE_ID', '=', '')->whereNull('SITE_ID');
2528 $filter->where($subFilter);
2529 unset($subFilter);
2530 }
2531
2532 $iterator = Catalog\StoreTable::getList(array(
2533 'select' => array('ID'),
2534 'filter' => $filter,
2535 'order' => array('ID' => 'ASC')
2536 ));
2537 while ($row = $iterator->fetch())
2538 $storeIds[] = (int)$row['ID'];
2539 unset($row, $iterator, $filter);
2540 if (!empty($storeIds))
2541 static::setHitCache(self::CACHE_STORE, $cacheId, $storeIds);
2542 }
2543 unset($cacheId, $filterId);
2544
2545 return $storeIds;
2546 }
2547
2555 protected static function checkProductSet($productId)
2556 {
2558 if (empty($allSets))
2559 {
2560 static::$errors[] = Loc::getMessage('CATALOG_ERR_NO_PRODUCT_SET');
2561 return false;
2562 }
2563 reset($allSets);
2564 $set = current($allSets);
2565 unset($allSets);
2566 $itemIds = array();
2567 foreach ($set['ITEMS'] as $item)
2568 {
2569 if ($item['ITEM_ID'] != $item['OWNER_ID'])
2570 $itemIds[$item['ITEM_ID']] = $item['ITEM_ID'];
2571 }
2572 if (empty($itemIds))
2573 {
2574 static::$errors[] = Loc::getMessage('CATALOG_ERR_NO_PRODUCT_SET');
2575 return false;
2576 }
2577 $iterator = CIBlockElement::GetList(
2578 array(),
2579 array(
2580 'ID' => $itemIds,
2581 'ACTIVE' => 'Y',
2582 'ACTIVE_DATE' => 'Y',
2583 'CHECK_PERMISSIONS' => 'N' // permission check in GetSetItems
2584 ),
2585 false,
2586 false,
2587 array('ID', 'IBLOCK_ID')
2588 );
2589 while ($row = $iterator->Fetch())
2590 unset($itemIds[$row['ID']]);
2591 unset($row, $iterator);
2592
2593 if (!empty($itemIds))
2594 {
2595 static::$errors[] = Loc::getMessage('CATALOG_ERR_NO_PRODUCT_SET_ITEM');
2596 return false;
2597 }
2598
2599 return true;
2600 }
2601
2605 public static function isReservationEnabled()
2606 {
2607 return !((string)Main\Config\Option::get("catalog", "enable_reservation") == "N"
2608 && (string)Main\Config\Option::get("sale", "product_reserve_condition") != "S"
2610 );
2611 }
2612
2616 public static function isNeedShip()
2617 {
2618 return static::isReservationEnabled();
2619 }
2620}
$arParams
Определения access_dialog.php:21
$type
Определения options.php:106
CatalogViewedProductCallback($productID, $UserID, $strSiteID=SITE_ID)
Определения include.php:370
CatalogPayOrderCallback($productID, $userID, $bPaid, $orderID)
Определения include.php:566
global $APPLICATION
Определения include.php:80
CatalogRecurringCallback($productID, $userID)
Определения include.php:849
$useSaleDiscountOnly
Определения options.php:32
$arResult
Определения generate_coupon.php:16
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static isUsedInventoryManagement()
Определения state.php:42
static update($id, array $data)
Определения entity.php:229
static getList(array $parameters)
Определения entity.php:78
static setConfig(array $config)
Определения calculation.php:46
const STATUS_YES
Определения product.php:66
const TYPE_EMPTY_SKU
Определения product.php:75
const TYPE_SET
Определения product.php:71
const TYPE_SKU
Определения product.php:72
const STATUS_NO
Определения product.php:67
const TYPE_SERVICE
Определения product.php:76
const TYPE_OFFER
Определения product.php:73
static checkCurrencyID($currency)
Определения currencymanager.php:35
static get($moduleId, $name, $default="", $siteId=false)
Определения option.php:30
Определения loader.php:13
static getMessage($code, $replace=null, $language=null)
Определения loc.php:30
static getList(array $parameters=array())
Определения datamanager.php:431
static getUserGroupIds($userId)
Определения user.php:250
const TYPE_SET
Определения basketitem.php:28
const TYPE_SERVICE
Определения basketitem.php:29
static getForApply($filter, $product=[], $uniqueDiscount=false)
Определения discountcouponsmanagerbase.php:986
static setApplyByProduct($product, $couponsList, $oldMode=false)
static isUsedSaleDiscountOnly()
Определения discount.php:4246
static getDiscountDescription(array $discount)
Определения discount.php:2449
static calculateDiscountList($priceData, $currency, &$discountList, $getWithVat=true)
Определения discount.php:2311
static ClearDiscountUserID()
Определения discount_save.php:55
static Disable()
Определения discount_save.php:38
static SetDiscountUserID($intUserID)
Определения discount_save.php:48
static Enable()
Определения discount_save.php:33
static GetNearestQuantityPrice($productID, $quantity=1, $arUserGroups=array())
Определения product.php:851
static Update($id, $fields)
Определения product.php:557
static GetOptimalPrice($intProductID, $quantity=1, $arUserGroups=array(), $renewal="N", $priceList=array(), $siteID=false, $arDiscountCoupons=false)
Определения product.php:973
static GetVATDataByID($id)
Определения product.php:220
static GetList($arOrder=array(), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения product.php:23
const CACHE_USER_RIGHTS
Определения product_provider.php:47
const CACHE_PARENT_PRODUCT_ACTIVE
Определения product_provider.php:53
static getUserGroups($userId)
Определения product_provider.php:2265
static $priceTitleCache
Определения product_provider.php:31
const CACHE_IBLOCK_RIGHTS
Определения product_provider.php:50
static getPriceTitle($priceType)
Определения product_provider.php:2290
const CACHE_ITEM_WITH_RIGHTS
Определения product_provider.php:45
static GetProductStores($arParams)
Определения product_provider.php:1848
static RecurringOrderProduct($arParams)
Определения product_provider.php:847
static isNeedClearPublicCache($currentQuantity, $newQuantity, $quantityTrace, $canBuyZero, $ratio=1)
Определения product_provider.php:2126
const CACHE_STORE_PRODUCT
Определения product_provider.php:52
static $proxyIblockRights
Определения product_provider.php:35
static checkProductSet($productId)
Определения product_provider.php:2555
static CancelProduct($arParams)
Определения product_provider.php:814
const CACHE_VAT
Определения product_provider.php:49
static isReservationEnabled()
Определения product_provider.php:2605
static getProductAvailableQuantity($productId, $userId=null)
Определения product_provider.php:2139
static $proxyUserGroups
Определения product_provider.php:32
static $hitCache
Определения product_provider.php:39
static $arOneTimeCoupons
Определения product_provider.php:27
static GetSetItems($productID, $intType, $arProducInfo=array())
Определения product_provider.php:1931
static GetStoresCount($arParams=array())
Определения product_provider.php:1839
static $proxyIblockElementListPermN
Определения product_provider.php:33
static $proxyCatalogProduct
Определения product_provider.php:36
static $catalogList
Определения product_provider.php:29
static GetProductData($arParams)
Определения product_provider.php:59
static $clearAutoCache
Определения product_provider.php:28
static $userCache
Определения product_provider.php:30
const CACHE_USER_GROUPS
Определения product_provider.php:43
static checkParentActivity($productId, $iblockId=0)
Определения product_provider.php:2319
const CACHE_PRODUCT
Определения product_provider.php:48
static $errors
Определения product_provider.php:25
static isExistsHitCache($type, $key)
Определения product_provider.php:2372
static getStoreIds(array $params)
Определения product_provider.php:2510
static tryShipmentProduct(\Bitrix\Sale\BasketItem $basketItem, $reserved='N', array $basketStoreData=array(), $quantity=null)
Определения product_provider.php:1588
static OrderProduct($arParams)
Определения product_provider.php:530
const CACHE_ITEM_WITHOUT_RIGHTS
Определения product_provider.php:44
static ReserveProduct($arParams)
Определения product_provider.php:855
static clearHitCache($type=null)
Определения product_provider.php:2400
const CATALOG_PROVIDER_EMPTY_STORE_ID
Определения product_provider.php:41
static CheckProductBarcode($arParams)
Определения product_provider.php:1887
static getProductOneStoreData(\Bitrix\Sale\BasketItem $basketItem, $quantity)
Определения product_provider.php:2482
const CACHE_STORE
Определения product_provider.php:51
static ViewProduct($arParams)
Определения product_provider.php:835
static canProductAutoShip(\Bitrix\Sale\BasketItem $basketItem)
Определения product_provider.php:2414
const CACHE_IBLOCK_RIGHTS_MODE
Определения product_provider.php:46
static $proxyStoresCount
Определения product_provider.php:37
static $proxyIblockElementListPermY
Определения product_provider.php:34
static getHitCache($type, $key)
Определения product_provider.php:2358
static isNeedShip()
Определения product_provider.php:2616
static DeliverProduct($arParams)
Определения product_provider.php:819
static setHitCache($type, $key, $value)
Определения product_provider.php:2384
static clearPublicCache($productID, $productInfo=array())
Определения product_provider.php:2131
static tryUnshipmentProduct($productId)
Определения product_provider.php:1806
static getProductStoreData(\Bitrix\Sale\BasketItem $basketItem, $quantity)
Определения product_provider.php:2449
const TYPE_SET
Определения product_set.php:9
static getAllSetsByProduct($intProductID, $intSetType)
Определения product_set.php:341
static getStoreName($storeId)
Определения store_utility.php:12
static Update($id, $arFields)
Определения store_product.php:56
static GetList($arOrder=array(), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения store_product.php:51
static GetOfferProperties($offerID, $iblockID, $propertiesList, $skuTreeProps='')
Определения comp_pricetools.php:1189
static GetLangCurrency($siteId)
Определения settings.php:52
Определения user.php:6037
$arFields
Определения dblapprove.php:5
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$iblockId
Определения iblock_catalog_edit.php:30
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$useStoreControl
Определения iblock_subelement_generator.php:49
static DeductProduct($arFields)
global $USER
Определения csv_new_run.php:40
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
is_set($a, $k=false)
Определения tools.php:2133
Определения basket.php:2
$order
Определения payment.php:8
return false
Определения prolog_main_admin.php:185
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
const ADMIN_SECTION
Определения rss.php:2
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$arRes
Определения options.php:104
const SITE_ID
Определения sonet_set_content_view.php:12
$k
Определения template_pdf.php:567
$arFilter
Определения user_search.php:106
$rsProps
Определения yandex_run.php:584
$iterator
Определения yandex_run.php:610
$fields
Определения yandex_run.php:501