61 if ((
string)$this->
getField(
'XML_ID') === (
string)$xmlId)
91 if ($this->getId() === $id)
105 if ($this->internalId ==
null)
107 if ($this->getId() > 0)
109 $this->internalId = $this->getId();
114 $this->internalId =
'n'.static::$idBasket;
134 "MODULE" => $moduleId,
138 "CUSTOM_PRICE" =>
'N',
139 "PRODUCT_ID" => $productId,
140 'XML_ID' => static::generateXmlId(),
143 $basket = $basketItemCollection->
getBasket();
146 $fields[
'LID'] = $basket->getSiteId();
149 $basketItem = static::createBasketItemObject($fields);
151 if ($basketCode !==
null)
153 $basketItem->internalId = $basketCode;
154 if (mb_strpos($basketCode,
'n') === 0)
164 $basketItem->setCollection($basketItemCollection);
174 return uniqid(
'bx_');
183 private static function createBasketItemObject(array $fields = [])
186 $basketItemClassName = $registry->getBasketItemClassName();
188 return new $basketItemClassName($fields);
197 "NAME",
"LID",
"SORT",
"PRODUCT_ID",
"PRODUCT_PRICE_ID",
"PRICE_TYPE_ID",
198 "CATALOG_XML_ID",
"PRODUCT_XML_ID",
"DETAIL_PAGE_URL",
199 "BASE_PRICE",
"PRICE",
"DISCOUNT_PRICE",
"CURRENCY",
"CUSTOM_PRICE",
200 "QUANTITY",
"WEIGHT",
"DIMENSIONS",
"MEASURE_CODE",
"MEASURE_NAME",
201 "DELAY",
"CAN_BUY",
"NOTES",
"VAT_RATE",
"VAT_INCLUDED",
"BARCODE_MULTI",
202 "SUBSCRIBE",
"PRODUCT_PROVIDER_CLASS",
"CALLBACK_FUNC",
"ORDER_CALLBACK_FUNC",
203 "CANCEL_CALLBACK_FUNC",
"PAY_CALLBACK_FUNC",
"TYPE",
"SET_PARENT_ID",
204 "DISCOUNT_NAME",
"DISCOUNT_VALUE",
"DISCOUNT_COUPON",
"RECOMMENDATION",
"XML_ID",
208 return array_merge($result, static::getCalculatedFields());
216 static $fieldsMap =
null;
218 if ($fieldsMap ===
null)
220 $fieldsMap = array_fill_keys(static::getSettableFields(),
true);
232 'DISCOUNT_PRICE_PERCENT',
233 'IGNORE_CALLBACK_FUNC',
244 return static::getSettableFields();
254 'VAT_INCLUDED' =>
'VAT_INCLUDED',
255 'VAT_RATE' =>
'VAT_RATE'
264 return [
'QUANTITY',
'PRICE',
'CUSTOM_PRICE'];
277 parent::__construct($fields);
296 public function delete()
301 if (!$checkResult->isSuccess())
303 $result->addErrors($checkResult->getErrors());
308 $oldEntityValues = $this->fields->getOriginalValues();
311 $event =
new Main\Event(
'sale',
"OnBeforeSaleBasketItemEntityDeleted", [
313 'VALUES' => $oldEntityValues,
317 if ($event->getResults())
320 foreach($event->getResults() as $eventResult)
322 if($eventResult->getType() == Main\EventResult::ERROR)
324 $eventResultData = $eventResult->getParameters();
325 if ($eventResultData instanceof ResultError)
327 $error = $eventResultData;
331 $error =
new ResultError(
332 Localization\
Loc::getMessage(
'SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
333 'SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'
337 $result->addError($error);
341 if (!$result->isSuccess())
350 $r = parent::delete();
351 if (!$r->isSuccess())
353 $result->addErrors($r->getErrors());
358 $oldEntityValues = $this->fields->getOriginalValues();
361 $event =
new Main\Event(
'sale',
"OnSaleBasketItemEntityDeleted", [
363 'VALUES' => $oldEntityValues,
367 if ($event->getResults())
370 foreach($event->getResults() as $eventResult)
372 if($eventResult->getType() == Main\EventResult::ERROR)
374 $eventResultData = $eventResult->getParameters();
375 if ($eventResultData instanceof ResultError)
377 $error = $eventResultData;
381 $error =
new ResultError(
382 Localization\
Loc::getMessage(
'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
383 'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'
387 $result->addError($error);
401 elseif ($name ===
'VAT_RATE')
403 $value = is_numeric($value) ? (float)$value :
null;
406 return parent::normalizeValue($name, $value);
420 $this->calculatedFields->set($name, $value);
424 if ($name ===
'CUSTOM_PRICE')
428 $this->markFieldCustom(
'PRICE');
432 $this->unmarkFieldCustom(
'PRICE');
436 return parent::setField($name, $value);
451 $this->calculatedFields->set($name, $value);
455 if ($name ===
'CUSTOM_PRICE')
459 $this->markFieldCustom(
'PRICE');
463 $this->unmarkFieldCustom(
'PRICE');
467 parent::setFieldNoDemand($name, $value);
488 isset($this->calculatedFields[$name])
489 || (is_array($this->calculatedFields) && array_key_exists($name, $this->calculatedFields))
492 return $this->calculatedFields->get($name);
498 return parent::getField($name);
508 'CURRENCY',
'CUSTOM_PRICE',
'VAT_RATE',
'VAT_INCLUDED',
509 'PRODUCT_PROVIDER_CLASS',
'SUBSCRIBE',
'TYPE',
'LID',
'FUSER_ID'
512 $priorityValues = [];
513 foreach ($priorityFields as $field)
515 if (isset($values[$field]))
517 $priorityValues[$field] = $values[$field];
521 return $priorityValues + $values;
533 foreach ($fields as $name => $value)
537 $this->calculatedFields[$name] = $value;
538 unset($fields[$name]);
542 return parent::setFields($fields);
560 $callbackFunction = $this->
getField(
'CALLBACK_FUNC');
561 if (empty($callbackFunction))
566 $callbackFunction = trim((
string)$callbackFunction);
567 if (empty($callbackFunction) || !function_exists($callbackFunction))
572 return $callbackFunction;
582 $module = $this->
getField(
'MODULE');
583 $productProviderName = $this->
getField(
'PRODUCT_PROVIDER_CLASS');
586 || !isset($productProviderName)
587 || (strval($productProviderName) ==
"")
593 if (!empty($module) && Main\Loader::includeModule($module))
595 return Internals\Catalog\Provider::getProviderEntity($productProviderName);
608 if ($this->provider !==
null)
611 $module = $this->
getField(
'MODULE');
612 $productProviderName = $this->
getField(
'PRODUCT_PROVIDER_CLASS');
615 || !isset($productProviderName)
616 || (strval($productProviderName) ==
"")
622 $providerName = Internals\Catalog\Provider::getProviderName($module, $productProviderName);
623 if ($providerName !==
null)
625 $this->provider = $providerName;
628 return $providerName;
648 if ($name ===
"QUANTITY")
652 $result->addError(
new Main\
Error(
654 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
655 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
661 $value = (float)$value;
662 $oldValue = (float)$oldValue;
663 $deltaQuantity = $value - $oldValue;
667 $context = $basket->getContext();
670 $r = Internals\Catalog\Provider::getAvailableQuantityAndPriceByBasketItem($this, $context);
671 if (!$r->isSuccess())
673 $result->addErrors($r->getErrors());
674 $result->setData($r->getData());
679 $providerData = $r->getData();
681 if ($this->
getField(
'SUBSCRIBE') !==
'Y')
685 if (array_key_exists(
'AVAILABLE_QUANTITY', $providerData) && $providerData[
'AVAILABLE_QUANTITY'] > 0)
687 $availableQuantity = $providerData[
'AVAILABLE_QUANTITY'];
694 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY_2',
695 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
697 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
706 $errorMessageCode =
'SALE_BASKET_PRODUCT_NOT_AVAILABLE';
707 if ($this->isService())
709 $errorMessageCode =
'SALE_BASKET_SERVICE_NOT_AVAILABLE';
716 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
718 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
727 $availableQuantity = $value;
730 if (isset($providerData[
'PRICE_DATA'][
'CUSTOM_PRICE']))
732 $this->markFieldCustom(
'PRICE');
741 && roundEx($availableQuantity, SALE_VALUE_PRECISION) < roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
745 && roundEx($availableQuantity, SALE_VALUE_PRECISION) > roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
750 if ($deltaQuantity > 0)
753 if ($canAddCount > 0)
755 $mess = Localization\Loc::getMessage(
756 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_LESS',
758 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
759 '#QUANTITY#' => $oldValue,
760 '#ADD#' => $canAddCount,
766 $mess = Localization\Loc::getMessage(
767 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_ZERO',
769 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
770 '#QUANTITY#' => $oldValue,
777 $mess = Localization\Loc::getMessage(
778 'SALE_BASKET_AVAILABLE_FOR_DECREASE_QUANTITY',
780 '#PRODUCT_NAME#' => $this->
getField(
'NAME'),
781 '#AVAILABLE_QUANTITY#' => $availableQuantity
786 $result->addError(
new ResultError($mess,
"SALE_BASKET_AVAILABLE_QUANTITY"));
788 "AVAILABLE_QUANTITY" => $availableQuantity,
789 "REQUIRED_QUANTITY" => $deltaQuantity
801 if ((!$basket->getOrder() || $basket->getOrderId() == 0) && !(
$collection instanceof BundleCollection))
803 if (!$this->isMarkedFieldCustom(
'PRICE') && $value > 0)
805 $r = $basket->refresh(RefreshFactory::createSingle($this->
getBasketCode()));
806 if (!$r->isSuccess())
808 $result->addErrors($r->getErrors());
814 if (!$this->isMarkedFieldCustom(
'PRICE'))
817 if (strval($providerName) ==
'')
823 if (!empty($providerData[
'PRICE_DATA']))
825 if (isset($providerData[
'PRICE_DATA'][
'PRICE']))
827 $this->
setField(
'PRICE', $providerData[
'PRICE_DATA'][
'PRICE']);
830 if (isset($providerData[
'PRICE_DATA'][
'BASE_PRICE']))
832 $this->
setField(
'BASE_PRICE', $providerData[
'PRICE_DATA'][
'BASE_PRICE']);
835 if (isset($providerData[
'PRICE_DATA'][
'DISCOUNT_PRICE']))
837 $this->
setField(
'DISCOUNT_PRICE', $providerData[
'PRICE_DATA'][
'DISCOUNT_PRICE']);
840 elseif ($providerName && !$this->
isCustom())
845 'SALE_BASKET_ITEM_WRONG_PRICE',
846 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
848 'SALE_BASKET_ITEM_WRONG_PRICE'
857 $r = parent::onFieldModify($name, $oldValue, $value);
860 if ($r->hasWarnings())
862 $result->addWarnings($r->getWarnings());
866 $name ===
'BASE_PRICE'
867 || $name ===
'DISCOUNT_PRICE'
873 $r = $this->
setField(
'PRICE', $price);
874 if (!$r->isSuccess())
875 $result->addErrors($r->getErrors());
881 $result->addErrors($r->getErrors());
893 return $this->
getField(
'VAT_INCLUDED') ===
'Y';
903 $vat = $calculator->calc(
921 $price -= $this->
getVat();
954 $price += $this->
getPrice() * $vatRate;
969 $price += $this->
getVat();
980 static $calculateFields =
null;
982 if ($calculateFields ===
null)
984 $calculateFields = array_fill_keys(static::getCalculatedFields(),
true);
987 return isset($calculateFields[$field]);
995 return (
int)$this->
getField(
'PRODUCT_ID');
1004 return (
float)$this->
getField(
'PRICE');
1013 return (
float)$this->
getField(
'BASE_PRICE');
1022 return (
float)$this->
getField(
'DISCOUNT_PRICE');
1031 return $this->isMarkedFieldCustom(
'PRICE');
1041 return $this->
getField(
'CURRENCY');
1056 if ($oldCurrency === $currency)
1074 return (
float)$this->
getField(
'QUANTITY');
1082 return (
float)$this->
getField(
'QUANTITY');
1100 return $this->
getField(
'VAT_RATE');
1109 return $this->
getField(
'FUSER_ID');
1119 $this->
setField(
'ORDER_ID', (
int)$id);
1128 return $this->
getField(
'BARCODE_MULTI') ===
"Y";
1138 return $this->
getField(
'CAN_BUY') ===
'Y';
1148 return $this->
getField(
'DELAY') ===
'Y';
1168 return (
string)$this->
getField(
'MARKING_CODE_GROUP');
1178 public function getPropertyCollection()
1180 if ($this->propertyCollection ===
null)
1185 $basketPropertyCollectionClassName = $registry->getBasketPropertiesCollectionClassName();
1187 if ($this->getId() > 0)
1191 $basketPropertyCollectionClassName::loadByCollection(
$collection);
1194 if ($this->propertyCollection ===
null)
1196 $this->propertyCollection = $basketPropertyCollectionClassName::load($this);
1208 return $this->propertyCollection !==
null;
1233 $this->markFieldCustom(
'PRICE');
1237 $this->unmarkFieldCustom(
'PRICE');
1240 $r = $this->
setField(
'PRICE', $value);
1241 if (!$r->isSuccess())
1243 $result->addErrors($r->getErrors());
1258 'DISCOUNT_PRICE_PERCENT',
1268 if (!isset($values[
'BASE_PRICE']) || doubleval($values[
'BASE_PRICE']) == 0)
1269 $values[
'BASE_PRICE'] = $values[
'PRICE'] + $values[
'DISCOUNT_PRICE'];
1271 parent::initFields($values);
1284 $this->checkCallingContext();
1288 $id = $this->getId();
1291 $this->onBeforeSave();
1293 $r = $this->callEventSaleBasketItemBeforeSaved($isNew);
1294 if (!$r->isSuccess())
1311 if ($r->getId() > 0)
1317 if (!$r->isSuccess())
1324 $result->setId($id);
1326 $controller = Internals\CustomFieldsController::getInstance();
1327 $controller->save($this);
1330 $r = $this->callEventSaleBasketItemSaved($isNew);
1331 if (!$r->isSuccess())
1338 if (!$r->isSuccess())
1340 $result->addErrors($r->getErrors());
1343 $this->callEventOnBasketItemEntitySaved();
1351 public function getBasket()
1362 private function checkCallingContext()
1364 $basket = $this->getBasket();
1366 $order = $basket->getOrder();
1370 if (!$order->isSaveRunning())
1372 trigger_error(
"Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1377 if (!$basket->isSaveRunning())
1379 trigger_error(
"Incorrect call to the save process. Use method save() on \Bitrix\Sale\Basket entity", E_USER_WARNING);
1390 protected function onBeforeSave()
1397 if ($this->
getField(
'ORDER_ID') <= 0)
1406 if ($this->getId() <= 0)
1408 if ($this->
getField(
'FUSER_ID') <= 0)
1410 $fUserId = (int)$basket->getFUserId(
true);
1413 throw new Main\ArgumentNullException(
'FUSER_ID');
1435 $fields = $this->fields->getValues();
1438 if (!$r->isSuccess())
1440 $result->addErrors($r->getErrors());
1444 if ($resultData = $r->getData())
1446 $result->setData($resultData);
1451 $result->setId($id);
1468 $fields = $this->fields->getChangedValues();
1470 if (!empty($fields))
1473 if (!$r->isSuccess())
1475 $result->addErrors($r->getErrors());
1479 if ($resultData = $r->getData())
1481 $result->setData($resultData);
1491 protected function callEventOnBasketItemEntitySaved()
1494 $oldEntityValues = $this->fields->getOriginalValues();
1496 if (!empty($oldEntityValues))
1501 'OnSaleBasketItemEntitySaved',
1504 'VALUES' => $oldEntityValues,
1516 protected function callEventSaleBasketItemBeforeSaved($isNewEntity)
1518 $result =
new Result();
1521 $oldEntityValues = $this->fields->getOriginalValues();
1526 'IS_NEW' => $isNewEntity,
1527 'VALUES' => $oldEntityValues,
1531 if ($event->getResults())
1534 foreach($event->getResults() as $eventResult)
1536 if($eventResult->getType() == Main\EventResult::ERROR)
1538 $errorMsg =
new ResultError(
1539 Localization\
Loc::getMessage(
'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'),
1540 'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'
1543 $eventResultData = $eventResult->getParameters();
1544 if ($eventResultData instanceof ResultError)
1545 $errorMsg = $eventResultData;
1547 $result->addError($errorMsg);
1559 protected function callEventSaleBasketItemSaved($isNewEntity)
1561 $result =
new Result();
1564 $oldEntityValues = $this->fields->getOriginalValues();
1569 'IS_NEW' => $isNewEntity,
1570 'VALUES' => $oldEntityValues,
1574 if ($event->getResults())
1577 foreach($event->getResults() as $eventResult)
1579 if($eventResult->getType() == Main\EventResult::ERROR)
1581 $errorMsg =
new ResultError(
1582 Localization\
Loc::getMessage(
'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'),
1583 'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'
1585 $eventResultData = $eventResult->getParameters();
1587 if ($eventResultData instanceof ResultError)
1588 $errorMsg = $eventResultData;
1590 $result->addError($errorMsg);
1622 $isChanged = parent::isChanged();
1624 if ($isChanged ===
false)
1642 $basketItem = static::createBasketItemObject($data);
1643 $basketItem->setCollection($basketItemCollection);
1658 if ((
float)$this->
getField(
'QUANTITY') <= 0)
1660 $result->addError(
new Main\
Error(
1662 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
1663 [
'#PRODUCT_NAME#' => $this->
getField(
'NAME')]
1670 $result->addError(
new Main\
Error(
1675 if ($basketPropertyCollection = $this->getPropertyCollection())
1677 $r = $basketPropertyCollection->verify();
1678 if (!$r->isSuccess())
1680 $result->addErrors($r->getErrors());
1699 $moduleId = trim($this->
getField(
'MODULE'));
1700 $providerClassName = trim($this->
getField(
'PRODUCT_PROVIDER_CLASS'));
1701 $callback = trim($this->
getField(
'CALLBACK_FUNC'));
1703 return (empty($moduleId) && empty($providerClassName) && empty($callback));
1713 return 'SaleBasketItem';
1719 $name ===
'BASE_PRICE'
1720 || $name ===
'PRICE'
1721 || $name ===
'DISCOUNT_PRICE'
1732 $result = parent::toArray();
1734 $result[
'PROPERTIES'] = $this->getPropertyCollection()->toArray();
1748 return (
float)$this->
getField(
'DEFAULT_PRICE');
static getMessage($code, $replace=null, $language=null)