17 private static $ignoreErrors =
false;
27 public static function getProductData($basketList,
array $context)
36 if (!is_array($basketList) && !($basketList instanceof
Sale\
BasketBase))
43 foreach ($basketList as $basketItem)
45 $creator->addBasketItem($basketItem);
48 $r = $creator->getProductData();
51 $data = $r->getData();
52 if (array_key_exists(
'PRODUCT_DATA_LIST',
$data))
59 $result->addErrors($r->getErrors());
83 $creator->addBasketItem($basketItem);
85 $r = $creator->getBundleItems();
92 $result->addErrors($r->getErrors());
104 public static function getAvailableQuantityAndPriceByBasketItem(Sale\BasketItemBase $basketItem,
array $context =
array())
109 $basket = $basketItem->getCollection();
111 $order = $basket->getOrder();
124 if (!$r->isSuccess())
129 $resultData =
array();
131 $creator = Sale\Internals\ProviderCreator::create(
$context);
134 $creator->addBasketItem($basketItem);
136 $r = $creator->getAvailableQuantityAndPrice();
139 $providerName = $basketItem->getProviderName();
141 if (strval($providerName) ==
'')
143 $providerName = $basketItem->getCallbackFunction();
146 $providerName = self::clearProviderName($providerName);
148 $checkProviderName = $providerName;
149 $data = $r->getData();
150 if (array_key_exists(
'PRODUCT_DATA_LIST',
$data) && isset(
$data[
'PRODUCT_DATA_LIST'][$checkProviderName]))
152 $productData =
$data[
'PRODUCT_DATA_LIST'][$checkProviderName];
154 if (isset($productData[
'PRICE_LIST'][$basketItem->getProductId()][$basketItem->getBasketCode()]))
156 $resultData[
'PRICE_DATA'] = $productData[
'PRICE_LIST'][$basketItem->getProductId()][$basketItem->getBasketCode()];
159 if (isset($productData[
'AVAILABLE_QUANTITY_LIST'][$basketItem->getProductId()]))
161 $resultData[
'AVAILABLE_QUANTITY'] = $productData[
'AVAILABLE_QUANTITY_LIST'][$basketItem->getProductId()];
167 $result->addErrors($r->getErrors());
170 if (isset($resultData))
188 public static function tryReserveShipment(Sale\Shipment $shipment)
190 $context = self::prepareContext($shipment->getOrder());
192 if (!$r->isSuccess())
197 $shipmentItemList = [];
199 foreach ($shipment->getShipmentItemCollection() as $item)
201 $basketItem = $item->getBasketItem();
202 if ($basketItem->isReservableItem())
204 $shipmentItemList[] = $item;
208 return self::tryReserveShipmentItemArray($shipmentItemList,
$context);
219 protected static function tryReserveShipmentItemArray(
array $shipmentItemList,
array $context)
224 $shipmentItem = current($shipmentItemList);
230 $availableQuantityList = [];
232 $needQuantityList = self::getNeedQuantityByShipmentItemList($shipmentItemList);
233 if (!$needQuantityList)
238 $creator = Sale\Internals\ProviderCreator::create(
$context);
240 foreach ($shipmentItemList as $shipmentItem)
242 $productData = $creator->createItemForReserveByShipmentItem($shipmentItem);
243 $creator->addProductData($productData);
246 $r = $creator->getAvailableQuantityByStore();
249 $data = $r->getData();
250 if (array_key_exists(
'AVAILABLE_QUANTITY_LIST_BY_STORE',
$data))
252 $availableQuantityList =
$data[
'AVAILABLE_QUANTITY_LIST_BY_STORE'];
257 $result->addErrors($r->getErrors());
260 if ($r->hasWarnings())
262 $result->addWarnings($r->getWarnings());
270 $applyItemsList = [];
274 foreach ($availableQuantityList as $providerName => $productAvailableQuantityList)
276 $providerName = trim($providerName);
277 foreach ($productAvailableQuantityList as $productId => $quantityByStore)
279 if (array_key_exists($productId, $needQuantityList[$providerName]))
281 if (Sale\Configuration::getProductReservationCondition() !== Sale\Reservation\Configuration\ReserveCondition::ON_SHIP)
283 if (!isset($applyItemsList[$providerName][$productId]))
285 $applyItemsList[$providerName][
$productId] = [];
288 foreach ($quantityByStore as $storeId => $quantity)
290 if ($quantity < $needQuantityList[$providerName][$productId][$storeId])
292 $poolQuantity = (float)$pool->getByStore(
300 if (
$delta < $poolQuantity)
304 elseif ($poolQuantity > 0)
323 foreach ($shipmentItemList as $shipmentItem)
325 $basketItem = $shipmentItem->getBasketItem();
327 if ($basketItem->getProductId() === $productId)
330 new Sale\ResultWarning(
331 Main\Localization\Loc::getMessage(
332 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY',
333 [
'#PRODUCT_NAME#' => $basketItem->getField(
'NAME')]
335 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY'
346 if (!empty($applyItemsList))
348 $shipmentItemMap = self::createProductShipmentItemMap($shipmentItemList);
350 self::setQuantityAfterReserve($shipmentItemMap, $applyItemsList);
365 public static function tryReserveShipmentItem(Sale\ShipmentItem $shipmentItem,
array $context =
array())
368 $order = $shipmentItem->getCollection()->getShipment()->getOrder();
372 if (!$r->isSuccess())
377 return self::tryReserveShipmentItemArray([$shipmentItem],
$context);
391 $basketItem = $reserveQuantity->getCollection()->getBasketItem();
393 $order = $basketItem->getBasket()->getOrder();
397 if (!$r->isSuccess())
404 $productData = $creator->createItemForReserve($reserveQuantity);
405 $creator->addProductData($productData);
407 $availableQuantityList = [];
409 $r = $creator->getAvailableQuantityByStore();
412 $data = $r->getData();
413 if (array_key_exists(
'AVAILABLE_QUANTITY_LIST_BY_STORE',
$data))
415 $availableQuantityList =
$data[
'AVAILABLE_QUANTITY_LIST_BY_STORE'];
420 return $result->addErrors($r->getErrors());
423 if ($r->hasWarnings())
425 $result->addWarnings($r->getWarnings());
430 $providerName = $basketItem->getProviderName();
431 $providerName = self::clearProviderName($providerName);
433 $storeId = $reserveQuantity->getStoreId();
434 $availableQuantity = $availableQuantityList[$providerName][$basketItem->getProductId()][$storeId] ?? 0;
436 if ($availableQuantity < $productData[
'QUANTITY'])
440 Main\
Localization\Loc::getMessage(
'SALE_PROVIDER_RESERVE_WRONG_AVAILABLE_QUANTITY'),
441 'SALE_PROVIDER_RESERVE_WRONG_AVAILABLE_QUANTITY'
449 $basketItem->getProductId(),
451 $productData[
'QUANTITY']
456 if (!empty($poolItems))
458 foreach ($poolItems as $poolItem)
463 && $poolItem->getCollection()->getBasketItem()->getInternalIndex() === $reserveQuantity->getCollection()->getBasketItem()->getInternalIndex()
476 $basketItem->getProductId(),
497 $order = $shipmentItem->getCollection()->getShipment()->getOrder();
502 $basketItem = $shipmentItem->getBasketItem();
504 $productId = $basketItem->getProductId();
506 $reservedQuantity = $shipmentItem->getReservedQuantity();
508 if ($reservedQuantity == 0)
517 if (!empty($poolItems))
520 foreach ($poolItems as $poolItem)
535 Sale\Internals\ItemsPool::add(
$order->getInternalId(), $productId, $shipmentItem);
538 $r = $shipmentItem->setField(
'RESERVED_QUANTITY', $shipmentItem->getReservedQuantity() + -1 * $reservedQuantity);
539 if (!$r->isSuccess())
541 $result->addErrors($r->getErrors());
552 private static function getNeedQuantityByShipmentItemList(
array $shipmentItemList) :
array
554 $needQuantityList = [];
557 foreach ($shipmentItemList as $shipmentItem)
559 $quantityList = self::getNeedQuantityByShipmentItem($shipmentItem);
560 $providerName = key($quantityList);
561 $productId = key($quantityList[$providerName]);
563 foreach ($quantityList[$providerName][$productId] as $storeId => $quantity)
565 if (!isset($needQuantityList[$providerName][$productId][$storeId]))
574 return $needQuantityList;
583 private static function getNeedQuantityByShipmentItem(Sale\ShipmentItem $shipmentItem) :
array
585 $basketItem = $shipmentItem->getBasketItem();
589 $providerName = $basketItem->getProviderName();
590 $providerName = self::clearProviderName($providerName);
592 $quantity = $shipmentItem->getQuantity();
598 Sale\Configuration::getDefaultStoreId() => -$shipmentItem->getReservedQuantity(),
605 $quantityByStore = [];
608 $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection();
609 if ($shipmentItemStoreCollection)
612 foreach ($shipmentItemStoreCollection as $shipmentItemStore)
614 if (!isset($quantityByStore[$shipmentItemStore->getStoreId()]))
616 $quantityByStore[$shipmentItemStore->getStoreId()] = 0;
619 $quantityByStore[$shipmentItemStore->getStoreId()] += $shipmentItemStore->getQuantity();
621 $quantity -= $shipmentItemStore->getQuantity();
627 $storeId = Sale\Configuration::getDefaultStoreId();
629 if (!isset($quantityByStore[$storeId]))
634 $quantityByStore[
$storeId] += $quantity;
653 public static function tryUnreserveShipment(Sale\Shipment $shipment)
658 $order = $shipment->getOrder();
663 foreach ($shipment->getShipmentItemCollection() as $shipmentItem)
666 if (!$basketItem = $shipmentItem->getBasketItem())
671 if ((
int)$shipmentItem->getReservedQuantity() === 0)
676 $shipmentItemReserveQuantity = $shipmentItem->getReservedQuantity();
679 $reserveCollection = $basketItem->getReserveQuantityCollection();
680 if (!$reserveCollection)
686 $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection();
687 if ($shipmentItemStoreCollection)
690 foreach ($shipmentItemStoreCollection as $itemStore)
692 if ($shipmentItemReserveQuantity == 0)
698 foreach ($reserveCollection as $reserve)
700 if ($reserve->getStoreId() !== $itemStore->getStoreId())
706 if ($shipmentItemReserveQuantity > $itemStore->getQuantity())
708 $reserveStoreQuantity = $itemStore->getQuantity();
712 $reserveStoreQuantity = $shipmentItemReserveQuantity;
715 if ($reserveStoreQuantity >= $reserve->getQuantity())
717 $poolQuantity = $reserve->getQuantity();
719 $reserve->deleteNoDemand();
723 $poolQuantity = $reserveStoreQuantity;
725 $reserve->setFieldNoDemand(
'QUANTITY', $reserve->getQuantity() - $reserveStoreQuantity);
728 $shipmentItemReserveQuantity -= $reserveStoreQuantity;
731 $basketItem->getProductId(),
732 $itemStore->getStoreId(),
740 if ($shipmentItemReserveQuantity > 0)
742 $storeId = Sale\Configuration::getDefaultStoreId();
744 foreach ($reserveCollection as $reserve)
746 if ($shipmentItemReserveQuantity == 0)
751 if ($reserve->getStoreId() !== $storeId)
756 if ($shipmentItemReserveQuantity >= $reserve->getQuantity())
758 $poolQuantity = $reserve->getQuantity();
759 $shipmentItemReserveQuantity -= $poolQuantity;
761 $reserve->deleteNoDemand();
765 $reserve->setFieldNoDemand(
'QUANTITY', $reserve->getQuantity() - $shipmentItemReserveQuantity);
766 $shipmentItemReserveQuantity = 0;
768 $poolQuantity = $shipmentItemReserveQuantity;
773 $basketItem->getProductId(),
780 $shipmentItem->getFields()->set(
'RESERVED_QUANTITY', 0);
782 if (!Sale\Internals\ActionEntity::isTypeExists(
788 Sale\Internals\ActionEntity::add(
792 'METHOD' =>
'Bitrix\Sale\Shipment::updateReservedFlag',
793 'PARAMS' => [$shipment],
799 $poolItems = Sale\Internals\ItemsPool::get(
$order->getInternalId(), $basketItem->getProductId());
800 if (!empty($poolItems))
803 foreach ($poolItems as $poolItem)
806 $poolItem instanceof Sale\ShipmentItem
807 && $poolItem->getInternalIndex() == $shipmentItem->getInternalIndex()
818 Sale\Internals\ItemsPool::add(
$order->getInternalId(), $basketItem->getProductId(), $shipmentItem);
830 private static function createProductShipmentItemMap(
array $shipmentItemList)
835 foreach ($shipmentItemList as $shipmentItem)
837 $basketItem = $shipmentItem->getBasketItem();
839 $providerName = self::clearProviderName($basketItem->getProviderName());
841 $result[$providerName][$basketItem->getProductId()][$shipmentItem->getInternalIndex()] = $shipmentItem;
855 private static function setQuantityAfterReserve(
array $shipmentItemMap,
array $availableQuantityList)
859 foreach ($availableQuantityList as $providerName => $productsList)
861 foreach ($productsList as $productId => $reservedQuantityByStore)
863 foreach ($reservedQuantityByStore as $storeId => $reservedQuantity)
865 $r = self::setReserveQuantityByProduct($shipmentItemMap[$providerName][$productId], $storeId, $reservedQuantity);
866 if (!$r->isSuccess())
868 $result->addErrors($r->getErrors());
877 private static function setReserveQuantityByProduct(
array $shipmentItemList, $storeId, $quantity) : Sale\Result
887 foreach ($shipmentItemList as $shipmentItem)
889 $basketItem = $shipmentItem->getBasketItem();
890 if (!$basketItem->isReservableItem())
896 $reserveQuantityCollection = $basketItem->getReserveQuantityCollection();
897 if (!$reserveQuantityCollection)
905 $order = $basketItem->getBasket()->getOrder();
912 foreach ($reserveQuantityCollection as $item)
914 if ($item->getStoreId() === $storeId)
921 if ($reserve ===
null)
923 $reserve = $reserveQuantityCollection->create();
924 $reserve->setStoreId($storeId);
929 $settableQuantity = $quantity;
930 if ($shipmentItem->getReservedQuantity() < $quantity)
932 $settableQuantity = $shipmentItem->getReservedQuantity();
937 $needQuantity = $basketItem->getQuantity() - $reserve->getQuantity();
938 $settableQuantity = (abs($needQuantity) >= abs($quantity)) ? $quantity : $needQuantity;
941 $reserve->setFieldNoDemand(
'QUANTITY', $reserve->getQuantity() + $settableQuantity);
943 self::applyReserveToShipmentItem($shipmentItem, $settableQuantity);
945 $quantity -= $settableQuantity;
950 $poolItems = Sale\Internals\ItemsPool::get(
$order->getInternalId(), $productId);
951 if (!empty($poolItems))
954 foreach ($poolItems as $poolItem)
957 $poolItem instanceof Sale\ShipmentItem
958 && $poolItem->getInternalIndex() == $shipmentItem->getInternalIndex()
969 Sale\Internals\ItemsPool::add(
$order->getInternalId(), $productId, $shipmentItem);
978 $item->getFields()->set(
'RESERVED_QUANTITY', $item->getReservedQuantity() + $quantity);
980 $shipment = $item->getCollection()->getShipment();
981 $order = $shipment->getOrder();
993 'METHOD' =>
'Bitrix\Sale\Shipment::updateReservedFlag',
994 'PARAMS' => [$shipment],
1012 $order = $shipment->getOrder();
1019 $shipmentItemCollection = $shipment->getShipmentItemCollection();
1021 $needShipList =
array();
1024 foreach ($shipmentItemCollection as $shipmentItem)
1026 $creator->addShipmentItem($shipmentItem);
1029 $r = $creator->isNeedShip();
1030 if ($r->isSuccess())
1032 $data = $r->getData();
1033 if (array_key_exists(
'IS_NEED_SHIP',
$data))
1035 $needShipList =
$data[
'IS_NEED_SHIP'] + $needShipList;
1041 foreach ($shipmentItemCollection as $shipmentItem)
1043 $shipmentProductData = $creator->createItemForShip($shipmentItem, $needShipList);
1044 $creator->addProductData($shipmentProductData);
1047 $tryShipProductList =
array();
1049 $isIgnoreErrors =
false;
1051 $r = $creator->tryShip();
1053 $needSetAfterResult =
false;
1054 if ($r->isSuccess())
1056 if ($r->hasWarnings())
1058 $result->addWarnings($r->getWarnings());
1062 $needSetAfterResult =
true;
1067 $result->addWarnings($r->getErrors());
1069 if (self::isIgnoreErrors())
1071 $isIgnoreErrors =
true;
1072 $needSetAfterResult =
true;
1076 $result->addErrors($r->getErrors());
1081 $data = $r->getData();
1082 if (array_key_exists(
'TRY_SHIP_PRODUCTS_LIST',
$data))
1084 $tryShipProductList =
$data[
'TRY_SHIP_PRODUCTS_LIST'] + $tryShipProductList;
1087 if ($needSetAfterResult && !empty($tryShipProductList))
1090 if ($isIgnoreErrors)
1092 foreach ($tryShipProductList as &$productList)
1094 $productList = array_fill_keys(array_keys($productList),
true);
1098 $creator->setItemsResultAfterTryShip($pool, $tryShipProductList);
1108 $order = $shipment->getOrder();
1111 'USER_ID' =>
$order->getUserId(),
1112 'SITE_ID' =>
$order->getSiteId(),
1117 foreach ($shipment->getShipmentItemCollection() as $shipmentItem)
1119 $creator->addShipmentItem($shipmentItem);
1122 $r = $creator->deliver();
1123 if ($r->isSuccess())
1125 $r = $creator->createItemsResultAfterDeliver($r);
1126 if ($r->isSuccess())
1128 $data = $r->getData();
1130 !empty(
$data[
'RESULT_AFTER_DELIVER_LIST'])
1131 && is_array(
$data[
'RESULT_AFTER_DELIVER_LIST'])
1140 $result->addErrors($r->getErrors());
1150 $order = $shipment->getOrder();
1153 'USER_ID' =>
$order->getUserId(),
1154 'SITE_ID' =>
$order->getSiteId(),
1159 foreach ($shipment->getShipmentItemCollection() as $shipmentItem)
1161 $creator->addShipmentItem($shipmentItem);
1165 $shipment->isShipped()
1166 ? $creator->writeOffProductBatches()
1167 : $creator->returnProductBatches()
1170 if (!$r->isSuccess())
1172 $result->addErrors($r->getErrors());
1185 public static function createMapShipmentItemStoreData(
Sale\
ShipmentItem $shipmentItem, $needUseReserve =
true)
1187 $resultList =
array();
1190 $basketItem = $shipmentItem->getBasketItem();
1191 if (!$basketItem->isReservableItem())
1197 $reserveQuantityCollection = $basketItem->getReserveQuantityCollection();
1198 if (!$reserveQuantityCollection)
1203 $reserveQuantityStoreList = [];
1208 $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection();
1209 if ($shipmentItemStoreCollection)
1212 foreach ($shipmentItemStoreCollection as $shipmentItemStore)
1214 $productId = $basketItem->getProductId();
1216 $storeId = $shipmentItemStore->getStoreId();
1218 if (!isset($reserveQuantityStoreList[$storeId]))
1220 $reserveQuantityStoreList[$storeId] = $reserveQuantityCollection->getQuantityByStoreId($shipmentItemStore->getStoreId());
1223 if (!isset($resultList[$storeId]))
1225 $resultList[$storeId] = [
1226 'PRODUCT_ID' => $productId,
1228 'RESERVED_QUANTITY' => 0,
1229 'STORE_ID' => $storeId,
1230 'IS_BARCODE_MULTI' => $basketItem->isBarcodeMulti(),
1235 $barcodeId = ($shipmentItemStore->getId() > 0)? $shipmentItemStore->getId() :
'n'.$countBarcode;
1237 $resultList[$storeId][
'QUANTITY'] += $basketItem->isBarcodeMulti()? 1 : $shipmentItemStore->getQuantity();
1238 $resultList[$storeId][
'BARCODE'][$barcodeId] = $shipmentItemStore->getBarcode();
1240 if ($needUseReserve)
1242 if ($reserveQuantityStoreList[$storeId] > $resultList[$storeId][
'QUANTITY'])
1244 $resultList[$storeId][
'RESERVED_QUANTITY'] = $resultList[$storeId][
'QUANTITY'];
1246 elseif ($reserveQuantityStoreList[$storeId] > 0)
1248 $resultList[
$storeId][
'RESERVED_QUANTITY'] = $reserveQuantityStoreList[
$storeId];
1251 $reserveQuantityStoreList[
$storeId] -= $resultList[
$storeId][
'RESERVED_QUANTITY'];
1267 public static function createMapShipmentItemCollectionStoreData($shipmentItemList)
1269 $resultList =
array();
1272 foreach ($shipmentItemList as $shipmentItem)
1274 $basketCode = $shipmentItem->getBasketCode();
1276 if (!isset($resultList[$basketCode]))
1278 $resultList[$basketCode] =
array();
1281 $map = self::createMapShipmentItemStoreData($shipmentItem);
1282 if (!empty(
$map) && is_array(
$map))
1284 $resultList[$basketCode] =
$map + $resultList[$basketCode];
1306 if (!$r->isSuccess())
1308 $result->addErrors($r->getErrors());
1316 if (empty($rulesMap))
1322 if (!$r->isSuccess())
1324 $result->addErrors($r->getErrors());
1327 if ($r->hasWarnings())
1329 $result->addWarnings($r->getWarnings());
1349 'SITE_ID' =>
$order->getSiteId(),
1350 'USER_ID' =>
$order->getUserId(),
1351 'CURRENCY' =>
$order->getCurrency(),
1388 throw new Main\ArgumentNullException(
'SITE_ID');
1393 throw new Main\ArgumentNullException(
'CURRENCY');
1408 static $providerProxy =
array();
1411 if (array_key_exists(
$code, $providerProxy))
1413 return $providerProxy[
$code];
1416 $providerName =
null;
1417 if (strval($module) !=
'' &&
Main\Loader::includeModule($module) && class_exists(
$name))
1422 $providerName =
$name;
1426 if ($providerName !==
null)
1428 $providerProxy[
$code] = $providerName;
1431 return $providerName;
1441 static $providerEntityProxy =
array();
1442 if (array_key_exists(
$name, $providerEntityProxy))
1444 return $providerEntityProxy[
$name];
1450 is_subclass_of(
$name,
Sale\SaleProviderBase::class)
1451 || is_subclass_of(
$name, \IBXSaleProductProvider::class)
1456 return $providerEntityProxy[
$name];
1468 private static function clearProviderName($providerName)
1470 if (empty($providerName) || !is_string($providerName))
1475 if (mb_substr($providerName, 0, 1) ===
"\\")
1477 $providerName = mb_substr($providerName, 1);
1480 return trim($providerName);
1489 self::$ignoreErrors = ($value ===
true);
1498 return self::$ignoreErrors;