62 if ((
string)$this->
getField(
'XML_ID') === (
string)$xmlId)
92 if ($this->getId() === $id)
106 if ($this->internalId ==
null)
108 if ($this->getId() > 0)
110 $this->internalId = $this->getId();
115 $this->internalId =
'n'.static::$idBasket;
139 "CUSTOM_PRICE" =>
'N',
140 "PRODUCT_ID" => $productId,
141 'XML_ID' => static::generateXmlId(),
144 $basket = $basketItemCollection->
getBasket();
147 $fields[
'LID'] = $basket->getSiteId();
150 $basketItem = static::createBasketItemObject($fields);
152 if ($basketCode !==
null)
154 $basketItem->internalId = $basketCode;
155 if (mb_strpos($basketCode,
'n') === 0)
165 $basketItem->setCollection($basketItemCollection);
175 return uniqid(
'bx_');
184 private static function createBasketItemObject(
array $fields = [])
187 $basketItemClassName = $registry->getBasketItemClassName();
189 return new $basketItemClassName(
$fields);
198 "NAME",
"LID",
"SORT",
"PRODUCT_ID",
"PRODUCT_PRICE_ID",
"PRICE_TYPE_ID",
199 "CATALOG_XML_ID",
"PRODUCT_XML_ID",
"DETAIL_PAGE_URL",
200 "BASE_PRICE",
"PRICE",
"DISCOUNT_PRICE",
"CURRENCY",
"CUSTOM_PRICE",
201 "QUANTITY",
"WEIGHT",
"DIMENSIONS",
"MEASURE_CODE",
"MEASURE_NAME",
202 "DELAY",
"CAN_BUY",
"NOTES",
"VAT_RATE",
"VAT_INCLUDED",
"BARCODE_MULTI",
203 "SUBSCRIBE",
"PRODUCT_PROVIDER_CLASS",
"CALLBACK_FUNC",
"ORDER_CALLBACK_FUNC",
204 "CANCEL_CALLBACK_FUNC",
"PAY_CALLBACK_FUNC",
"TYPE",
"SET_PARENT_ID",
205 "DISCOUNT_NAME",
"DISCOUNT_VALUE",
"DISCOUNT_COUPON",
"RECOMMENDATION",
"XML_ID",
209 return array_merge($result, static::getCalculatedFields());
217 static $fieldsMap =
null;
219 if ($fieldsMap ===
null)
221 $fieldsMap = array_fill_keys(static::getSettableFields(),
true);
233 'DISCOUNT_PRICE_PERCENT',
234 'IGNORE_CALLBACK_FUNC',
245 return static::getSettableFields();
255 'BASE_PRICE' =>
'BASE_PRICE',
256 'VAT_INCLUDED' =>
'VAT_INCLUDED',
257 'VAT_RATE' =>
'VAT_RATE'
266 return [
'QUANTITY',
'PRICE',
'CUSTOM_PRICE'];
279 parent::__construct($fields);
298 public function delete()
303 if (!$checkResult->isSuccess())
305 $result->addErrors($checkResult->getErrors());
310 $oldEntityValues = $this->fields->getOriginalValues();
315 'VALUES' => $oldEntityValues,
322 foreach(
$event->getResults() as $eventResult)
324 if($eventResult->getType() ==
Main\EventResult::ERROR)
326 $eventResultData = $eventResult->getParameters();
327 if ($eventResultData instanceof ResultError)
329 $error = $eventResultData;
334 Localization\Loc::getMessage(
'SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
335 'SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'
339 $result->addError(
$error);
343 if (!$result->isSuccess())
352 $r = parent::delete();
353 if (!$r->isSuccess())
355 $result->addErrors($r->getErrors());
360 $oldEntityValues = $this->fields->getOriginalValues();
363 $event =
new Main\Event(
'sale',
"OnSaleBasketItemEntityDeleted", [
365 'VALUES' => $oldEntityValues,
372 foreach(
$event->getResults() as $eventResult)
374 if($eventResult->getType() == Main\EventResult::ERROR)
376 $eventResultData = $eventResult->getParameters();
377 if ($eventResultData instanceof ResultError)
379 $error = $eventResultData;
384 Localization\Loc::getMessage(
'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
385 'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'
389 $result->addError(
$error);
405 $value = is_numeric($value) ? (float)$value :
null;
408 return parent::normalizeValue(
$name, $value);
422 $this->calculatedFields->set(
$name, $value);
426 if (
$name ===
'CUSTOM_PRICE')
430 $this->markFieldCustom(
'PRICE');
434 $this->unmarkFieldCustom(
'PRICE');
438 return parent::setField(
$name, $value);
453 $this->calculatedFields->set(
$name, $value);
457 if (
$name ===
'CUSTOM_PRICE')
461 $this->markFieldCustom(
'PRICE');
465 $this->unmarkFieldCustom(
'PRICE');
469 parent::setFieldNoDemand(
$name, $value);
490 isset($this->calculatedFields[
$name])
491 || (is_array($this->calculatedFields) && array_key_exists(
$name, $this->calculatedFields))
494 return $this->calculatedFields->get(
$name);
500 return parent::getField(
$name);
510 'CURRENCY',
'CUSTOM_PRICE',
'VAT_RATE',
'VAT_INCLUDED',
511 'PRODUCT_PROVIDER_CLASS',
'SUBSCRIBE',
'TYPE',
'LID',
'FUSER_ID'
514 $priorityValues = [];
515 foreach ($priorityFields as $field)
517 if (isset($values[$field]))
519 $priorityValues[$field] = $values[$field];
523 return $priorityValues + $values;
535 foreach ($fields as
$name => $value)
539 $this->calculatedFields[
$name] = $value;
540 unset($fields[
$name]);
544 return parent::setFields($fields);
562 $callbackFunction = $this->
getField(
'CALLBACK_FUNC');
563 if (empty($callbackFunction))
568 $callbackFunction = trim((
string)$callbackFunction);
569 if (empty($callbackFunction) || !function_exists($callbackFunction))
574 return $callbackFunction;
584 $module = $this->
getField(
'MODULE');
585 $productProviderName = $this->
getField(
'PRODUCT_PROVIDER_CLASS');
588 || !isset($productProviderName)
589 || (strval($productProviderName) ==
"")
595 if (!empty($module) &&
Main\Loader::includeModule($module))
610 if ($this->provider !==
null)
613 $module = $this->
getField(
'MODULE');
614 $productProviderName = $this->
getField(
'PRODUCT_PROVIDER_CLASS');
617 || !isset($productProviderName)
618 || (strval($productProviderName) ==
"")
625 if ($providerName !==
null)
627 $this->provider = $providerName;
630 return $providerName;
650 if (
$name ===
"QUANTITY")
656 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
657 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
663 $value = (float)$value;
664 $oldValue = (float)$oldValue;
665 $deltaQuantity = $value - $oldValue;
672 $r = Internals\Catalog\Provider::getAvailableQuantityAndPriceByBasketItem($this,
$context);
673 if (!$r->isSuccess())
675 $result->addErrors($r->getErrors());
676 $result->setData($r->getData());
681 $providerData = $r->getData();
683 if ($this->
getField(
'SUBSCRIBE') !==
'Y')
687 if (array_key_exists(
'AVAILABLE_QUANTITY', $providerData) && $providerData[
'AVAILABLE_QUANTITY'] > 0)
689 $availableQuantity = $providerData[
'AVAILABLE_QUANTITY'];
695 Localization\Loc::getMessage(
696 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY_2',
697 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
699 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
708 $errorMessageCode =
'SALE_BASKET_PRODUCT_NOT_AVAILABLE';
709 if ($this->isService())
711 $errorMessageCode =
'SALE_BASKET_SERVICE_NOT_AVAILABLE';
716 Localization\Loc::getMessage(
718 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
720 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
729 $availableQuantity =
$value;
732 if (isset($providerData[
'PRICE_DATA'][
'CUSTOM_PRICE']))
734 $this->markFieldCustom(
'PRICE');
752 if ($deltaQuantity > 0)
755 if ($canAddCount > 0)
757 $mess = Localization\Loc::getMessage(
758 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_LESS',
760 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
761 '#QUANTITY#' => $oldValue,
762 '#ADD#' => $canAddCount,
768 $mess = Localization\Loc::getMessage(
769 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_ZERO',
771 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
772 '#QUANTITY#' => $oldValue,
779 $mess = Localization\Loc::getMessage(
780 'SALE_BASKET_AVAILABLE_FOR_DECREASE_QUANTITY',
782 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
783 '#AVAILABLE_QUANTITY#' => $availableQuantity
788 $result->addError(
new ResultError($mess,
"SALE_BASKET_AVAILABLE_QUANTITY"));
790 "AVAILABLE_QUANTITY" => $availableQuantity,
791 "REQUIRED_QUANTITY" => $deltaQuantity
803 if ((!$basket->getOrder() || $basket->getOrderId() == 0) && !(
$collection instanceof BundleCollection))
805 if (!$this->isMarkedFieldCustom(
'PRICE') && $value > 0)
807 $r = $basket->refresh(RefreshFactory::createSingle($this->
getBasketCode()));
808 if (!$r->isSuccess())
810 $result->addErrors($r->getErrors());
816 if (!$this->isMarkedFieldCustom(
'PRICE'))
819 if (strval($providerName) ==
'')
825 if (!empty($providerData[
'PRICE_DATA']))
827 if (isset($providerData[
'PRICE_DATA'][
'PRICE']))
829 $this->
setField(
'PRICE', $providerData[
'PRICE_DATA'][
'PRICE']);
832 if (isset($providerData[
'PRICE_DATA'][
'BASE_PRICE']))
834 $this->
setField(
'BASE_PRICE', $providerData[
'PRICE_DATA'][
'BASE_PRICE']);
837 if (isset($providerData[
'PRICE_DATA'][
'DISCOUNT_PRICE']))
839 $this->
setField(
'DISCOUNT_PRICE', $providerData[
'PRICE_DATA'][
'DISCOUNT_PRICE']);
846 Localization\Loc::getMessage(
847 'SALE_BASKET_ITEM_WRONG_PRICE',
848 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
850 'SALE_BASKET_ITEM_WRONG_PRICE'
859 $r = parent::onFieldModify(
$name, $oldValue, $value);
862 if ($r->hasWarnings())
864 $result->addWarnings($r->getWarnings());
868 $name ===
'BASE_PRICE'
869 ||
$name ===
'DISCOUNT_PRICE'
875 $r = $this->
setField(
'PRICE', $price);
876 if (!$r->isSuccess())
877 $result->addErrors($r->getErrors());
883 $result->addErrors($r->getErrors());
895 return $this->
getField(
'VAT_INCLUDED') ===
'Y';
912 public function getVatUnit(
bool $withRound =
true): float|int
916 return $calculator->calc(
932 $price -= $this->
getVat();
965 $price += $this->
getPrice() * $vatRate;
980 $price += $this->
getVat();
991 static $calculateFields =
null;
993 if ($calculateFields ===
null)
995 $calculateFields = array_fill_keys(static::getCalculatedFields(),
true);
998 return isset($calculateFields[$field]);
1006 return (
int)$this->
getField(
'PRODUCT_ID');
1015 return (
float)$this->
getField(
'PRICE');
1024 return (
float)$this->
getField(
'BASE_PRICE');
1033 return (
float)$this->
getField(
'DISCOUNT_PRICE');
1042 return $this->isMarkedFieldCustom(
'PRICE');
1052 return $this->
getField(
'CURRENCY');
1085 return (
float)$this->
getField(
'QUANTITY');
1093 return (
float)$this->
getField(
'QUANTITY');
1111 return $this->
getField(
'VAT_RATE');
1120 return $this->
getField(
'FUSER_ID');
1130 $this->
setField(
'ORDER_ID', (
int)$id);
1139 return $this->
getField(
'BARCODE_MULTI') ===
"Y";
1149 return $this->
getField(
'CAN_BUY') ===
'Y';
1159 return $this->
getField(
'DELAY') ===
'Y';
1179 return (
string)$this->
getField(
'MARKING_CODE_GROUP');
1189 public function getPropertyCollection()
1191 if ($this->propertyCollection ===
null)
1196 $basketPropertyCollectionClassName = $registry->getBasketPropertiesCollectionClassName();
1198 if ($this->getId() > 0)
1202 $basketPropertyCollectionClassName::loadByCollection(
$collection);
1205 if ($this->propertyCollection ===
null)
1207 $this->propertyCollection = $basketPropertyCollectionClassName::load($this);
1219 return $this->propertyCollection !==
null;
1244 $this->markFieldCustom(
'PRICE');
1248 $this->unmarkFieldCustom(
'PRICE');
1251 $r = $this->
setField(
'PRICE', $value);
1252 if (!$r->isSuccess())
1254 $result->addErrors($r->getErrors());
1269 'DISCOUNT_PRICE_PERCENT',
1279 if (!isset($values[
'BASE_PRICE']) || doubleval($values[
'BASE_PRICE']) == 0)
1280 $values[
'BASE_PRICE'] = $values[
'PRICE'] + $values[
'DISCOUNT_PRICE'];
1282 parent::initFields($values);
1295 $this->checkCallingContext();
1299 $id = $this->getId();
1302 $this->onBeforeSave();
1304 $r = $this->callEventSaleBasketItemBeforeSaved($isNew);
1305 if (!$r->isSuccess())
1322 if ($r->getId() > 0)
1328 if (!$r->isSuccess())
1335 $result->setId($id);
1338 $controller->save($this);
1341 $r = $this->callEventSaleBasketItemSaved($isNew);
1342 if (!$r->isSuccess())
1349 if (!$r->isSuccess())
1351 $result->addErrors($r->getErrors());
1354 $this->callEventOnBasketItemEntitySaved();
1362 public function getBasket()
1373 private function checkCallingContext()
1375 $basket = $this->getBasket();
1377 $order = $basket->getOrder();
1381 if (!
$order->isSaveRunning())
1383 trigger_error(
"Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1388 if (!$basket->isSaveRunning())
1390 trigger_error(
"Incorrect call to the save process. Use method save() on \Bitrix\Sale\Basket entity", E_USER_WARNING);
1401 protected function onBeforeSave()
1408 if ($this->
getField(
'ORDER_ID') <= 0)
1417 if ($this->
getId() <= 0)
1419 if ($this->
getField(
'FUSER_ID') <= 0)
1421 $fUserId = (int)$basket->getFUserId(
true);
1424 throw new Main\ArgumentNullException(
'FUSER_ID');
1446 $fields = $this->fields->getValues();
1449 if (!$r->isSuccess())
1451 $result->addErrors($r->getErrors());
1455 if ($resultData = $r->getData())
1457 $result->setData($resultData);
1462 $result->setId($id);
1479 $fields = $this->fields->getChangedValues();
1481 if (!empty($fields))
1484 if (!$r->isSuccess())
1486 $result->addErrors($r->getErrors());
1490 if ($resultData = $r->getData())
1492 $result->setData($resultData);
1502 protected function callEventOnBasketItemEntitySaved()
1505 $oldEntityValues = $this->fields->getOriginalValues();
1507 if (!empty($oldEntityValues))
1512 'OnSaleBasketItemEntitySaved',
1515 'VALUES' => $oldEntityValues,
1527 protected function callEventSaleBasketItemBeforeSaved($isNewEntity)
1532 $oldEntityValues = $this->fields->getOriginalValues();
1537 'IS_NEW' => $isNewEntity,
1538 'VALUES' => $oldEntityValues,
1542 if (
$event->getResults())
1545 foreach(
$event->getResults() as $eventResult)
1547 if($eventResult->getType() == Main\EventResult::ERROR)
1550 Localization\Loc::getMessage(
'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'),
1551 'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'
1554 $eventResultData = $eventResult->getParameters();
1555 if ($eventResultData instanceof ResultError)
1570 protected function callEventSaleBasketItemSaved($isNewEntity)
1572 $result =
new Result();
1575 $oldEntityValues = $this->fields->getOriginalValues();
1580 'IS_NEW' => $isNewEntity,
1581 'VALUES' => $oldEntityValues,
1585 if (
$event->getResults())
1588 foreach(
$event->getResults() as $eventResult)
1590 if($eventResult->getType() == Main\EventResult::ERROR)
1593 Localization\Loc::getMessage(
'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'),
1594 'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'
1596 $eventResultData = $eventResult->getParameters();
1598 if ($eventResultData instanceof ResultError)
1633 $isChanged = parent::isChanged();
1635 if ($isChanged ===
false)
1653 $basketItem = static::createBasketItemObject(
$data);
1654 $basketItem->setCollection($basketItemCollection);
1669 if ((
float)$this->
getField(
'QUANTITY') <= 0)
1673 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
1674 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
1682 Localization\Loc::getMessage(
'SALE_BASKET_ITEM_ERR_CURRENCY_EMPTY')
1686 if ($basketPropertyCollection = $this->getPropertyCollection())
1688 $r = $basketPropertyCollection->verify();
1689 if (!$r->isSuccess())
1691 $result->addErrors($r->getErrors());
1711 $providerClassName = trim($this->
getField(
'PRODUCT_PROVIDER_CLASS'));
1712 $callback = trim($this->
getField(
'CALLBACK_FUNC'));
1714 return (empty(
$moduleId) && empty($providerClassName) && empty($callback));
1724 return 'SaleBasketItem';
1730 $name ===
'BASE_PRICE'
1731 ||
$name ===
'PRICE'
1732 ||
$name ===
'DISCOUNT_PRICE'
1743 $result = parent::toArray();
1745 $result[
'PROPERTIES'] = $this->getPropertyCollection()->toArray();
1759 return (
float)$this->
getField(
'DEFAULT_PRICE');
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)