1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
basketitembase.php
См. документацию.
1<?php
8namespace Bitrix\Sale;
9
10use Bitrix\Main;
11use Bitrix\Main\ArgumentNullException;
12use Bitrix\Main\Localization;
13use Bitrix\Sale\Basket\RefreshFactory;
14use Bitrix\Sale\Internals;
15use Bitrix\Sale\Tax\VatCalculator;
16
18
24{
27
30
32 protected $provider;
33
35 protected $internalId = null;
36
37 protected static $idBasket = 0;
38
44 public function findItemByBasketCode($basketCode)
45 {
46 if ((string)$this->getBasketCode() === (string)$basketCode)
47 {
48 return $this;
49 }
50
51 return null;
52 }
53
60 public function findItemByXmlId($xmlId)
61 {
62 if ((string)$this->getField('XML_ID') === (string)$xmlId)
63 {
64 return $this;
65 }
66
67 return null;
68 }
69
73 public static function getRegistryEntity()
74 {
76 }
77
83 public function findItemById($id)
84 {
85 $id = (int)$id;
86
87 if ($id <= 0)
88 {
89 return null;
90 }
91
92 if ($this->getId() === $id)
93 {
94 return $this;
95 }
96
97 return null;
98 }
99
104 public function getBasketCode()
105 {
106 if ($this->internalId == null)
107 {
108 if ($this->getId() > 0)
109 {
110 $this->internalId = $this->getId();
111 }
112 else
113 {
114 static::$idBasket++;
115 $this->internalId = 'n'.static::$idBasket;
116 }
117 }
118
119 return $this->internalId;
120 }
121
132 public static function create(BasketItemCollection $basketItemCollection, $moduleId, $productId, $basketCode = null)
133 {
134 $fields = [
135 "MODULE" => $moduleId,
136 "BASE_PRICE" => 0,
137 "CAN_BUY" => 'Y',
138 "VAT_RATE" => null,
139 "CUSTOM_PRICE" => 'N',
140 "PRODUCT_ID" => $productId,
141 'XML_ID' => static::generateXmlId(),
142 ];
143
144 $basket = $basketItemCollection->getBasket();
145 if ($basket instanceof BasketBase)
146 {
147 $fields['LID'] = $basket->getSiteId();
148 }
149
150 $basketItem = static::createBasketItemObject($fields);
151
152 if ($basketCode !== null)
153 {
154 $basketItem->internalId = $basketCode;
155 if (mb_strpos($basketCode, 'n') === 0)
156 {
157 $internalId = intval(mb_substr($basketCode, 1));
158 if ($internalId > static::$idBasket)
159 {
160 static::$idBasket = $internalId;
161 }
162 }
163 }
164
165 $basketItem->setCollection($basketItemCollection);
166
167 return $basketItem;
168 }
169
173 protected static function generateXmlId()
174 {
175 return uniqid('bx_');
176 }
177
184 private static function createBasketItemObject(array $fields = [])
185 {
186 $registry = Registry::getInstance(static::getRegistryType());
187 $basketItemClassName = $registry->getBasketItemClassName();
188
189 return new $basketItemClassName($fields);
190 }
191
195 public static function getSettableFields()
196 {
197 $result = [
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",
206 "MARKING_CODE_GROUP"
207 ];
208
209 return array_merge($result, static::getCalculatedFields());
210 }
211
215 public static function getSettableFieldsMap()
216 {
217 static $fieldsMap = null;
218
219 if ($fieldsMap === null)
220 {
221 $fieldsMap = array_fill_keys(static::getSettableFields(), true);
222 }
223
224 return $fieldsMap;
225 }
226
230 public static function getCalculatedFields()
231 {
232 return [
233 'DISCOUNT_PRICE_PERCENT',
234 'IGNORE_CALLBACK_FUNC',
235 'DEFAULT_PRICE',
236 'DISCOUNT_LIST'
237 ];
238 }
239
243 public static function getAvailableFields()
244 {
245 return static::getSettableFields();
246 }
247
251 public static function getCustomizableFields() : array
252 {
253 return [
254 'PRICE' => 'PRICE',
255 'BASE_PRICE' => 'BASE_PRICE',
256 'VAT_INCLUDED' => 'VAT_INCLUDED',
257 'VAT_RATE' => 'VAT_RATE'
258 ];
259 }
260
264 protected static function getMeaningfulFields()
265 {
266 return ['QUANTITY', 'PRICE', 'CUSTOM_PRICE'];
267 }
268
277 protected function __construct(array $fields = [])
278 {
279 parent::__construct($fields);
280
281 $this->calculatedFields = new Internals\Fields();
282 }
283
287 protected function checkBeforeDelete()
288 {
289 return new Result();
290 }
291
298 public function delete()
299 {
300 $result = new Result();
301
302 $checkResult = $this->checkBeforeDelete();
303 if (!$checkResult->isSuccess())
304 {
305 $result->addErrors($checkResult->getErrors());
306 return $result;
307 }
308
310 $oldEntityValues = $this->fields->getOriginalValues();
311
313 $event = new Main\Event('sale', "OnBeforeSaleBasketItemEntityDeleted", [
314 'ENTITY' => $this,
315 'VALUES' => $oldEntityValues,
316 ]);
317 $event->send();
318
319 if ($event->getResults())
320 {
322 foreach($event->getResults() as $eventResult)
323 {
324 if($eventResult->getType() == Main\EventResult::ERROR)
325 {
326 $eventResultData = $eventResult->getParameters();
327 if ($eventResultData instanceof ResultError)
328 {
329 $error = $eventResultData;
330 }
331 else
332 {
333 $error = new ResultError(
334 Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
335 'SALE_EVENT_ON_BEFORE_SALEBASKETITEM_ENTITY_DELETED_ERROR'
336 );
337 }
338
339 $result->addError($error);
340 }
341 }
342
343 if (!$result->isSuccess())
344 {
345 return $result;
346 }
347 }
348
349 $this->setFieldNoDemand("QUANTITY", 0);
350
352 $r = parent::delete();
353 if (!$r->isSuccess())
354 {
355 $result->addErrors($r->getErrors());
356 return $result;
357 }
358
360 $oldEntityValues = $this->fields->getOriginalValues();
361
363 $event = new Main\Event('sale', "OnSaleBasketItemEntityDeleted", [
364 'ENTITY' => $this,
365 'VALUES' => $oldEntityValues,
366 ]);
367 $event->send();
368
369 if ($event->getResults())
370 {
372 foreach($event->getResults() as $eventResult)
373 {
374 if($eventResult->getType() == Main\EventResult::ERROR)
375 {
376 $eventResultData = $eventResult->getParameters();
377 if ($eventResultData instanceof ResultError)
378 {
379 $error = $eventResultData;
380 }
381 else
382 {
383 $error = new ResultError(
384 Localization\Loc::getMessage('SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
385 'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'
386 );
387 }
388
389 $result->addError($error);
390 }
391 }
392 }
393
394 return $result;
395 }
396
397 protected function normalizeValue($name, $value)
398 {
399 if ($this->isPriceField($name))
400 {
401 $value = PriceMaths::roundPrecision($value);
402 }
403 elseif ($name === 'VAT_RATE')
404 {
405 $value = is_numeric($value) ? (float)$value : null;
406 }
407
408 return parent::normalizeValue($name, $value);
409 }
410
418 public function setField($name, $value)
419 {
420 if ($this->isCalculatedField($name))
421 {
422 $this->calculatedFields->set($name, $value);
423 return new Result();
424 }
425
426 if ($name === 'CUSTOM_PRICE')
427 {
428 if ($value == 'Y')
429 {
430 $this->markFieldCustom('PRICE');
431 }
432 else
433 {
434 $this->unmarkFieldCustom('PRICE');
435 }
436 }
437
438 return parent::setField($name, $value);
439 }
440
449 public function setFieldNoDemand($name, $value)
450 {
451 if ($this->isCalculatedField($name))
452 {
453 $this->calculatedFields->set($name, $value);
454 return;
455 }
456
457 if ($name === 'CUSTOM_PRICE')
458 {
459 if ($value === 'Y')
460 {
461 $this->markFieldCustom('PRICE');
462 }
463 else
464 {
465 $this->unmarkFieldCustom('PRICE');
466 }
467 }
468
469 parent::setFieldNoDemand($name, $value);
470 }
471
478 public function getField($name)
479 {
480 static $calculatedFields = null;
481
482 if ($calculatedFields === null)
483 {
484 $calculatedFields = array_fill_keys(static::getCalculatedFields(), true);
485 }
486
487 if (isset($calculatedFields[$name]))
488 {
489 if (
490 isset($this->calculatedFields[$name])
491 || (is_array($this->calculatedFields) && array_key_exists($name, $this->calculatedFields))
492 )
493 {
494 return $this->calculatedFields->get($name);
495 }
496
497 return null;
498 }
499
500 return parent::getField($name);
501 }
502
507 protected function onBeforeSetFields(array $values)
508 {
509 $priorityFields = [
510 'CURRENCY', 'CUSTOM_PRICE', 'VAT_RATE', 'VAT_INCLUDED',
511 'PRODUCT_PROVIDER_CLASS', 'SUBSCRIBE', 'TYPE', 'LID', 'FUSER_ID'
512 ];
513
514 $priorityValues = [];
515 foreach ($priorityFields as $field)
516 {
517 if (isset($values[$field]))
518 {
519 $priorityValues[$field] = $values[$field];
520 }
521 }
522
523 return $priorityValues + $values;
524 }
525
533 public function setFields(array $fields)
534 {
535 foreach ($fields as $name => $value)
536 {
537 if ($this->isCalculatedField($name))
538 {
539 $this->calculatedFields[$name] = $value;
540 unset($fields[$name]);
541 }
542 }
543
544 return parent::setFields($fields);
545 }
546
551 public function getProviderName()
552 {
553 return $this->getProvider();
554 }
555
560 public function getCallbackFunction()
561 {
562 $callbackFunction = $this->getField('CALLBACK_FUNC');
563 if (empty($callbackFunction))
564 {
565 return null;
566 }
567
568 $callbackFunction = trim((string)$callbackFunction);
569 if (empty($callbackFunction) || !function_exists($callbackFunction))
570 {
571 return null;
572 }
573
574 return $callbackFunction;
575 }
576
582 public function getProviderEntity()
583 {
584 $module = $this->getField('MODULE');
585 $productProviderName = $this->getField('PRODUCT_PROVIDER_CLASS');
586 if (
587 !isset($module)
588 || !isset($productProviderName)
589 || (strval($productProviderName) == "")
590 )
591 {
592 return false;
593 }
594
595 if (!empty($module) && Main\Loader::includeModule($module))
596 {
597 return Internals\Catalog\Provider::getProviderEntity($productProviderName);
598 }
599
600 return null;
601 }
602
608 public function getProvider()
609 {
610 if ($this->provider !== null)
611 return $this->provider;
612
613 $module = $this->getField('MODULE');
614 $productProviderName = $this->getField('PRODUCT_PROVIDER_CLASS');
615 if (
616 !isset($module)
617 || !isset($productProviderName)
618 || (strval($productProviderName) == "")
619 )
620 {
621 return null;
622 }
623
624 $providerName = Internals\Catalog\Provider::getProviderName($module, $productProviderName);
625 if ($providerName !== null)
626 {
627 $this->provider = $providerName;
628 }
629
630 return $providerName;
631 }
632
646 protected function onFieldModify($name, $oldValue, $value)
647 {
648 $result = new Result();
649
650 if ($name === "QUANTITY")
651 {
652 if ($value == 0)
653 {
654 $result->addError(new Main\Error(
655 Localization\Loc::getMessage(
656 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
657 ['#PRODUCT_NAME#' => $this->getField('NAME')]
658 )
659 ));
660 return $result;
661 }
662
663 $value = (float)$value;
664 $oldValue = (float)$oldValue;
665 $deltaQuantity = $value - $oldValue;
666
668 $basket = $this->getCollection();
669 $context = $basket->getContext();
670
672 $r = Internals\Catalog\Provider::getAvailableQuantityAndPriceByBasketItem($this, $context);
673 if (!$r->isSuccess())
674 {
675 $result->addErrors($r->getErrors());
676 $result->setData($r->getData());
677
678 return $result;
679 }
680
681 $providerData = $r->getData();
682
683 if ($this->getField('SUBSCRIBE') !== 'Y')
684 {
685 if ($providerData)
686 {
687 if (array_key_exists('AVAILABLE_QUANTITY', $providerData) && $providerData['AVAILABLE_QUANTITY'] > 0)
688 {
689 $availableQuantity = $providerData['AVAILABLE_QUANTITY'];
690 }
691 else
692 {
693 $result->addError(
694 new ResultError(
695 Localization\Loc::getMessage(
696 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY_2',
697 ['#PRODUCT_NAME#' => $this->getField('NAME')]
698 ),
699 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
700 )
701 );
702
703 return $result;
704 }
705 }
706 else
707 {
708 $errorMessageCode = 'SALE_BASKET_PRODUCT_NOT_AVAILABLE';
709 if ($this->isService())
710 {
711 $errorMessageCode = 'SALE_BASKET_SERVICE_NOT_AVAILABLE';
712 }
713
714 $result->addError(
715 new ResultError(
716 Localization\Loc::getMessage(
717 $errorMessageCode,
718 ['#PRODUCT_NAME#' => $this->getField('NAME')]
719 ),
720 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
721 )
722 );
723
724 return $result;
725 }
726 }
727 else
728 {
729 $availableQuantity = $value;
730 }
731
732 if (isset($providerData['PRICE_DATA']['CUSTOM_PRICE']))
733 {
734 $this->markFieldCustom('PRICE');
735 }
736
737 $notPurchasedQuantity = $this->getNotPurchasedQuantity();
738
739 if ($value != 0
740 && (
741 (
742 $deltaQuantity > 0
743 && roundEx($availableQuantity, SALE_VALUE_PRECISION) < roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
744 ) // plus
745 || (
746 $deltaQuantity < 0
747 && roundEx($availableQuantity, SALE_VALUE_PRECISION) > roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
748 ) // minus
749 )
750 )
751 {
752 if ($deltaQuantity > 0)
753 {
754 $canAddCount = $availableQuantity - $this->getReservedQuantity();
755 if ($canAddCount > 0)
756 {
757 $mess = Localization\Loc::getMessage(
758 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_LESS',
759 [
760 '#PRODUCT_NAME#' => $this->getField('NAME'),
761 '#QUANTITY#' => $oldValue,
762 '#ADD#' => $canAddCount,
763 ]
764 );
765 }
766 else
767 {
768 $mess = Localization\Loc::getMessage(
769 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_ZERO',
770 [
771 '#PRODUCT_NAME#' => $this->getField('NAME'),
772 '#QUANTITY#' => $oldValue,
773 ]
774 );
775 }
776 }
777 else
778 {
779 $mess = Localization\Loc::getMessage(
780 'SALE_BASKET_AVAILABLE_FOR_DECREASE_QUANTITY',
781 [
782 '#PRODUCT_NAME#' => $this->getField('NAME'),
783 '#AVAILABLE_QUANTITY#' => $availableQuantity
784 ]
785 );
786 }
787
788 $result->addError(new ResultError($mess, "SALE_BASKET_AVAILABLE_QUANTITY"));
789 $result->setData([
790 "AVAILABLE_QUANTITY" => $availableQuantity,
791 "REQUIRED_QUANTITY" => $deltaQuantity
792 ]);
793
794 return $result;
795 }
796
798 $collection = $this->getCollection();
799
801 $basket = $collection->getBasket();
802
803 if ((!$basket->getOrder() || $basket->getOrderId() == 0) && !($collection instanceof BundleCollection))
804 {
805 if (!$this->isMarkedFieldCustom('PRICE') && $value > 0)
806 {
807 $r = $basket->refresh(RefreshFactory::createSingle($this->getBasketCode()));
808 if (!$r->isSuccess())
809 {
810 $result->addErrors($r->getErrors());
811 return $result;
812 }
813 }
814 }
815
816 if (!$this->isMarkedFieldCustom('PRICE'))
817 {
818 $providerName = $this->getProviderName();
819 if (strval($providerName) == '')
820 {
821 $providerName = $this->getCallbackFunction();
822 }
823
824
825 if (!empty($providerData['PRICE_DATA']))
826 {
827 if (isset($providerData['PRICE_DATA']['PRICE']))
828 {
829 $this->setField('PRICE', $providerData['PRICE_DATA']['PRICE']);
830 }
831
832 if (isset($providerData['PRICE_DATA']['BASE_PRICE']))
833 {
834 $this->setField('BASE_PRICE', $providerData['PRICE_DATA']['BASE_PRICE']);
835 }
836
837 if (isset($providerData['PRICE_DATA']['DISCOUNT_PRICE']))
838 {
839 $this->setField('DISCOUNT_PRICE', $providerData['PRICE_DATA']['DISCOUNT_PRICE']);
840 }
841 }
842 elseif ($providerName && !$this->isCustom())
843 {
844 $result->addError(
845 new ResultError(
846 Localization\Loc::getMessage(
847 'SALE_BASKET_ITEM_WRONG_PRICE',
848 ['#PRODUCT_NAME#' => $this->getField('NAME')]
849 ),
850 'SALE_BASKET_ITEM_WRONG_PRICE'
851 )
852 );
853
854 return $result;
855 }
856 }
857 }
858
859 $r = parent::onFieldModify($name, $oldValue, $value);
860 if ($r->isSuccess())
861 {
862 if ($r->hasWarnings())
863 {
864 $result->addWarnings($r->getWarnings());
865 }
866
867 if (
868 $name === 'BASE_PRICE'
869 || $name === 'DISCOUNT_PRICE'
870 )
871 {
872 if (!$this->isCustomPrice())
873 {
874 $price = $this->getField('BASE_PRICE') - $this->getField('DISCOUNT_PRICE');
875 $r = $this->setField('PRICE', $price);
876 if (!$r->isSuccess())
877 $result->addErrors($r->getErrors());
878 }
879 }
880 }
881 else
882 {
883 $result->addErrors($r->getErrors());
884 }
885
886 return $result;
887 }
888
893 public function isVatInPrice()
894 {
895 return $this->getField('VAT_INCLUDED') === 'Y';
896 }
897
902 public function getVat(): float|int
903 {
904 return PriceMaths::roundPrecision($this->getVatUnit() * $this->getQuantity());
905 }
906
912 public function getVatUnit(bool $withRound = true): float|int
913 {
914 $calculator = new VatCalculator((float)$this->getVatRate());
915
916 return $calculator->calc(
917 $this->getPrice(),
918 $this->isVatInPrice(),
919 $withRound
920 );
921 }
922
927 public function getInitialPrice()
928 {
929 $price = PriceMaths::roundPrecision($this->getPrice() * $this->getQuantity());
930
931 if ($this->isVatInPrice())
932 $price -= $this->getVat();
933
934 return $price;
935 }
936
941 public function getBasePriceWithVat()
942 {
943 $price = $this->getBasePrice();
944
945 if (!$this->isVatInPrice())
946 {
947 $vatRate = (float)$this->getVatRate();
948 $price += $this->getBasePrice() * $vatRate;
949 }
950
951 return PriceMaths::roundPrecision($price);
952 }
953
958 public function getPriceWithVat()
959 {
960 $price = $this->getPrice();
961
962 if (!$this->isVatInPrice())
963 {
964 $vatRate = (float)$this->getVatRate();
965 $price += $this->getPrice() * $vatRate;
966 }
967
968 return PriceMaths::roundPrecision($price);
969 }
970
975 public function getFinalPrice()
976 {
977 $price = PriceMaths::roundPrecision($this->getPrice() * $this->getQuantity());
978
979 if (!$this->isVatInPrice())
980 $price += $this->getVat();
981
982 return $price;
983 }
984
989 protected function isCalculatedField($field)
990 {
991 static $calculateFields = null;
992
993 if ($calculateFields === null)
994 {
995 $calculateFields = array_fill_keys(static::getCalculatedFields(), true);
996 }
997
998 return isset($calculateFields[$field]);
999 }
1000
1004 public function getProductId()
1005 {
1006 return (int)$this->getField('PRODUCT_ID');
1007 }
1008
1013 public function getPrice()
1014 {
1015 return (float)$this->getField('PRICE');
1016 }
1017
1022 public function getBasePrice()
1023 {
1024 return (float)$this->getField('BASE_PRICE');
1025 }
1026
1031 public function getDiscountPrice()
1032 {
1033 return (float)$this->getField('DISCOUNT_PRICE');
1034 }
1035
1040 public function isCustomPrice()
1041 {
1042 return $this->isMarkedFieldCustom('PRICE');
1043 }
1044
1050 public function getCurrency()
1051 {
1052 return $this->getField('CURRENCY');
1053 }
1054
1062 public function changeCurrency(string $currency): Main\Result
1063 {
1064 $result = new Main\Result();
1065
1066 $oldCurrency = $this->getCurrency();
1067 if ($oldCurrency === $currency)
1068 {
1069 return $result;
1070 }
1071
1072 $result->addErrors(
1073 $this->setField('CURRENCY', $currency)->getErrors()
1074 );
1075
1076 return $result;
1077 }
1078
1083 public function getQuantity() : float
1084 {
1085 return (float)$this->getField('QUANTITY');
1086 }
1087
1091 public function getNotPurchasedQuantity() : float
1092 {
1093 return (float)$this->getField('QUANTITY');
1094 }
1095
1100 public function getWeight()
1101 {
1102 return $this->getField('WEIGHT');
1103 }
1104
1109 public function getVatRate()
1110 {
1111 return $this->getField('VAT_RATE');
1112 }
1113
1118 public function getFUserId()
1119 {
1120 return $this->getField('FUSER_ID');
1121 }
1122
1128 public function setOrderId($id)
1129 {
1130 $this->setField('ORDER_ID', (int)$id);
1131 }
1132
1137 public function isBarcodeMulti()
1138 {
1139 return $this->getField('BARCODE_MULTI') === "Y";
1140 }
1141
1147 public function canBuy() : bool
1148 {
1149 return $this->getField('CAN_BUY') === 'Y';
1150 }
1151
1157 public function isDelay() : bool
1158 {
1159 return $this->getField('DELAY') === 'Y';
1160 }
1161
1167 public function isSupportedMarkingCode() : bool
1168 {
1169 return $this->getMarkingCodeGroup() !== '';
1170 }
1171
1177 public function getMarkingCodeGroup() : string
1178 {
1179 return (string)$this->getField('MARKING_CODE_GROUP');
1180 }
1181
1189 public function getPropertyCollection()
1190 {
1191 if ($this->propertyCollection === null)
1192 {
1193 $registry = Registry::getInstance(static::getRegistryType());
1194
1196 $basketPropertyCollectionClassName = $registry->getBasketPropertiesCollectionClassName();
1197
1198 if ($this->getId() > 0)
1199 {
1201 $collection = $this->getCollection();
1202 $basketPropertyCollectionClassName::loadByCollection($collection);
1203 }
1204
1205 if ($this->propertyCollection === null)
1206 {
1207 $this->propertyCollection = $basketPropertyCollectionClassName::load($this);
1208 }
1209 }
1210
1212 }
1213
1218 {
1219 return $this->propertyCollection !== null;
1220 }
1221
1227 {
1228 $this->propertyCollection = $propertyCollection;
1229 }
1230
1238 public function setPrice($value, $custom = false)
1239 {
1240 $result = new Result();
1241
1242 if ($custom)
1243 {
1244 $this->markFieldCustom('PRICE');
1245 }
1246 else
1247 {
1248 $this->unmarkFieldCustom('PRICE');
1249 }
1250
1251 $r = $this->setField('PRICE', $value);
1252 if (!$r->isSuccess())
1253 {
1254 $result->addErrors($r->getErrors());
1255 }
1256
1257 return $result;
1258 }
1259
1264 public static function getRoundFields()
1265 {
1266 return [
1267 'BASE_PRICE',
1268 'DISCOUNT_PRICE',
1269 'DISCOUNT_PRICE_PERCENT',
1270 ];
1271 }
1272
1277 public function initFields(array $values)
1278 {
1279 if (!isset($values['BASE_PRICE']) || doubleval($values['BASE_PRICE']) == 0)
1280 $values['BASE_PRICE'] = $values['PRICE'] + $values['DISCOUNT_PRICE'];
1281
1282 parent::initFields($values);
1283 }
1284
1293 public function save()
1294 {
1295 $this->checkCallingContext();
1296
1297 $result = new Result();
1298
1299 $id = $this->getId();
1300 $isNew = $id === 0;
1301
1302 $this->onBeforeSave();
1303
1304 $r = $this->callEventSaleBasketItemBeforeSaved($isNew);
1305 if (!$r->isSuccess())
1306 {
1307 return $r;
1308 }
1309
1310 if (!$this->isChanged())
1311 {
1312 return $result;
1313 }
1314
1315 if ($id > 0)
1316 {
1317 $r = $this->update();
1318 }
1319 else
1320 {
1321 $r = $this->add();
1322 if ($r->getId() > 0)
1323 {
1324 $id = $r->getId();
1325 }
1326 }
1327
1328 if (!$r->isSuccess())
1329 {
1330 return $r;
1331 }
1332
1333 if ($id > 0)
1334 {
1335 $result->setId($id);
1336
1338 $controller->save($this);
1339 }
1340
1341 $r = $this->callEventSaleBasketItemSaved($isNew);
1342 if (!$r->isSuccess())
1343 {
1344 return $r;
1345 }
1346
1347 $propertyCollection = $this->getPropertyCollection();
1348 $r = $propertyCollection->save();
1349 if (!$r->isSuccess())
1350 {
1351 $result->addErrors($r->getErrors());
1352 }
1353
1354 $this->callEventOnBasketItemEntitySaved();
1355
1356 return $result;
1357 }
1358
1362 public function getBasket()
1363 {
1365 $collection = $this->getCollection();
1366
1367 return $collection->getBasket();
1368 }
1369
1373 private function checkCallingContext()
1374 {
1375 $basket = $this->getBasket();
1376
1377 $order = $basket->getOrder();
1378
1379 if ($order)
1380 {
1381 if (!$order->isSaveRunning())
1382 {
1383 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1384 }
1385 }
1386 else
1387 {
1388 if (!$basket->isSaveRunning())
1389 {
1390 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Basket entity", E_USER_WARNING);
1391 }
1392 }
1393 }
1394
1401 protected function onBeforeSave()
1402 {
1404 $collection = $this->getCollection();
1405
1406 $basket = $collection->getBasket();
1407
1408 if ($this->getField('ORDER_ID') <= 0)
1409 {
1410 $orderId = $collection->getOrderId();
1411 if ($orderId > 0)
1412 {
1413 $this->setFieldNoDemand('ORDER_ID', $orderId);
1414 }
1415 }
1416
1417 if ($this->getId() <= 0)
1418 {
1419 if ($this->getField('FUSER_ID') <= 0)
1420 {
1421 $fUserId = (int)$basket->getFUserId(true);
1422 if ($fUserId <= 0)
1423 {
1424 throw new Main\ArgumentNullException('FUSER_ID');
1425 }
1426
1427 $this->setFieldNoDemand('FUSER_ID', $fUserId);
1428 }
1429 }
1430 }
1431
1437 protected function add()
1438 {
1439 $result = new Result();
1440
1442
1443 $this->setFieldNoDemand('DATE_INSERT', $dateInsert);
1444 $this->setFieldNoDemand('DATE_UPDATE', $dateInsert);
1445
1446 $fields = $this->fields->getValues();
1447
1448 $r = $this->addInternal($fields);
1449 if (!$r->isSuccess())
1450 {
1451 $result->addErrors($r->getErrors());
1452 return $result;
1453 }
1454
1455 if ($resultData = $r->getData())
1456 {
1457 $result->setData($resultData);
1458 }
1459
1460 $id = $r->getId();
1461 $this->setFieldNoDemand('ID', $id);
1462 $result->setId($id);
1463
1464 return $result;
1465 }
1466
1473 protected function update()
1474 {
1475 $result = new Result();
1476
1477 $this->setFieldNoDemand('DATE_UPDATE', new Main\Type\DateTime());
1478
1479 $fields = $this->fields->getChangedValues();
1480
1481 if (!empty($fields))
1482 {
1483 $r = $this->updateInternal($this->getId(), $fields);
1484 if (!$r->isSuccess())
1485 {
1486 $result->addErrors($r->getErrors());
1487 return $result;
1488 }
1489
1490 if ($resultData = $r->getData())
1491 {
1492 $result->setData($resultData);
1493 }
1494 }
1495
1496 return $result;
1497 }
1498
1502 protected function callEventOnBasketItemEntitySaved()
1503 {
1505 $oldEntityValues = $this->fields->getOriginalValues();
1506
1507 if (!empty($oldEntityValues))
1508 {
1510 $event = new Main\Event(
1511 'sale',
1512 'OnSaleBasketItemEntitySaved',
1513 [
1514 'ENTITY' => $this,
1515 'VALUES' => $oldEntityValues,
1516 ]
1517 );
1518
1519 $event->send();
1520 }
1521 }
1522
1527 protected function callEventSaleBasketItemBeforeSaved($isNewEntity)
1528 {
1529 $result = new Result();
1530
1532 $oldEntityValues = $this->fields->getOriginalValues();
1533
1536 'ENTITY' => $this,
1537 'IS_NEW' => $isNewEntity,
1538 'VALUES' => $oldEntityValues,
1539 ]);
1540 $event->send();
1541
1542 if ($event->getResults())
1543 {
1545 foreach($event->getResults() as $eventResult)
1546 {
1547 if($eventResult->getType() == Main\EventResult::ERROR)
1548 {
1549 $errorMsg = new ResultError(
1550 Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'),
1551 'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'
1552 );
1553
1554 $eventResultData = $eventResult->getParameters();
1555 if ($eventResultData instanceof ResultError)
1556 $errorMsg = $eventResultData;
1557
1558 $result->addError($errorMsg);
1559 }
1560 }
1561 }
1562
1563 return $result;
1564 }
1565
1570 protected function callEventSaleBasketItemSaved($isNewEntity)
1571 {
1572 $result = new Result();
1573
1575 $oldEntityValues = $this->fields->getOriginalValues();
1576
1578 $event = new Main\Event('sale', EventActions::EVENT_ON_BASKET_ITEM_SAVED, [
1579 'ENTITY' => $this,
1580 'IS_NEW' => $isNewEntity,
1581 'VALUES' => $oldEntityValues,
1582 ]);
1583 $event->send();
1584
1585 if ($event->getResults())
1586 {
1588 foreach($event->getResults() as $eventResult)
1589 {
1590 if($eventResult->getType() == Main\EventResult::ERROR)
1591 {
1592 $errorMsg = new ResultError(
1593 Localization\Loc::getMessage('SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'),
1594 'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'
1595 );
1596 $eventResultData = $eventResult->getParameters();
1597
1598 if ($eventResultData instanceof ResultError)
1599 $errorMsg = $eventResultData;
1600
1601 $result->addError($errorMsg);
1602 }
1603 }
1604 }
1605
1606 return $result;
1607 }
1608
1613 abstract protected function addInternal(array $fields);
1614
1620 abstract protected function updateInternal($primary, array $fields);
1621
1631 public function isChanged()
1632 {
1633 $isChanged = parent::isChanged();
1634
1635 if ($isChanged === false)
1636 {
1637 $propertyCollection = $this->getPropertyCollection();
1638 $isChanged = $propertyCollection->isChanged();
1639 }
1640
1641 return $isChanged;
1642 }
1643
1651 public static function load(BasketItemCollection $basketItemCollection, $data)
1652 {
1653 $basketItem = static::createBasketItemObject($data);
1654 $basketItem->setCollection($basketItemCollection);
1655
1656 return $basketItem;
1657 }
1658
1665 public function verify()
1666 {
1667 $result = new Result();
1668
1669 if ((float)$this->getField('QUANTITY') <= 0)
1670 {
1671 $result->addError(new Main\Error(
1672 Localization\Loc::getMessage(
1673 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
1674 ['#PRODUCT_NAME#' => $this->getField('NAME')]
1675 )
1676 ));
1677 }
1678
1679 if (!$this->getField('CURRENCY'))
1680 {
1681 $result->addError(new Main\Error(
1682 Localization\Loc::getMessage('SALE_BASKET_ITEM_ERR_CURRENCY_EMPTY')
1683 ));
1684 }
1685
1686 if ($basketPropertyCollection = $this->getPropertyCollection())
1687 {
1688 $r = $basketPropertyCollection->verify();
1689 if (!$r->isSuccess())
1690 {
1691 $result->addErrors($r->getErrors());
1692 }
1693 }
1694
1695 return $result;
1696 }
1697
1701 abstract public function getReservedQuantity();
1702
1708 public function isCustom()
1709 {
1710 $moduleId = trim($this->getField('MODULE'));
1711 $providerClassName = trim($this->getField('PRODUCT_PROVIDER_CLASS'));
1712 $callback = trim($this->getField('CALLBACK_FUNC'));
1713
1714 return (empty($moduleId) && empty($providerClassName) && empty($callback));
1715 }
1716
1722 public static function getEntityEventName()
1723 {
1724 return 'SaleBasketItem';
1725 }
1726
1727 protected function isPriceField(string $name) : bool
1728 {
1729 return
1730 $name === 'BASE_PRICE'
1731 || $name === 'PRICE'
1732 || $name === 'DISCOUNT_PRICE'
1733 ;
1734 }
1735
1741 public function toArray() : array
1742 {
1743 $result = parent::toArray();
1744
1745 $result['PROPERTIES'] = $this->getPropertyCollection()->toArray();
1746
1747 return $result;
1748 }
1749
1757 public function getDefaultPrice()
1758 {
1759 return (float)$this->getField('DEFAULT_PRICE');
1760 }
1761}
Определения error.php:15
Определения event.php:5
static loadMessages($file)
Определения loc.php:65
getVatUnit(bool $withRound=true)
Определения basketitembase.php:912
static getRegistryEntity()
Определения basketitembase.php:73
changeCurrency(string $currency)
Определения basketitembase.php:1062
setPropertyCollection(BasketPropertiesCollectionBase $propertyCollection)
Определения basketitembase.php:1226
static $idBasket
Определения basketitembase.php:37
static getMeaningfulFields()
Определения basketitembase.php:264
static getCalculatedFields()
Определения basketitembase.php:230
static getCustomizableFields()
Определения basketitembase.php:251
__construct(array $fields=[])
Определения basketitembase.php:277
setPrice($value, $custom=false)
Определения basketitembase.php:1238
static getRoundFields()
Определения basketitembase.php:1264
static getEntityEventName()
Определения basketitembase.php:1722
static getSettableFields()
Определения basketitembase.php:195
static getAvailableFields()
Определения basketitembase.php:243
initFields(array $values)
Определения basketitembase.php:1277
updateInternal($primary, array $fields)
addInternal(array $fields)
onBeforeSetFields(array $values)
Определения basketitembase.php:507
isPriceField(string $name)
Определения basketitembase.php:1727
findItemByXmlId($xmlId)
Определения basketitembase.php:60
setFieldNoDemand($name, $value)
Определения basketitembase.php:449
static generateXmlId()
Определения basketitembase.php:173
static create(BasketItemCollection $basketItemCollection, $moduleId, $productId, $basketCode=null)
Определения basketitembase.php:132
findItemByBasketCode($basketCode)
Определения basketitembase.php:44
setField($name, $value)
Определения basketitembase.php:418
static getSettableFieldsMap()
Определения basketitembase.php:215
findItemById($id)
Определения basketitembase.php:83
isExistPropertyCollection()
Определения basketitembase.php:1217
normalizeValue($name, $value)
Определения basketitembase.php:397
setFields(array $fields)
Определения basketitembase.php:533
getField($name)
Определения basketitembase.php:478
isCalculatedField($field)
Определения basketitembase.php:989
static load(BasketItemCollection $basketItemCollection, $data)
Определения basketitembase.php:1651
const EVENT_ON_BASKET_ITEM_BEFORE_SAVED
Определения eventactions.php:27
const EVENT_ON_BASKET_ITEM_SAVED
Определения eventactions.php:28
static getProviderName($module, $name)
Определения provider.php:1406
static getProviderEntity($name)
Определения provider.php:1439
onFieldModify($name, $oldValue, $value)
Определения collectableentity.php:29
static roundPrecision($value)
Определения pricemaths.php:16
const ENTITY_BASKET_ITEM
Определения registry.php:32
static getInstance($type)
Определения registry.php:183
$data['IS_AVAILABLE']
Определения .description.php:13
$orderId
Определения payment.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
$result
Определения get_property_values.php:14
$moduleId
$context
Определения csv_new_setup.php:223
roundEx($value, $prec=0)
Определения tools.php:4635
$name
Определения menu_edit.php:35
$value
Определения Param.php:39
Определения collection.php:2
getErrors()
Определения errorableimplementation.php:34
$order
Определения payment.php:8
$dateInsert
Определения payment.php:19
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$errorMsg
Определения refund.php:16
$custom
Определения z_payment_result.php:22
$currency
Определения template.php:266
const SALE_VALUE_PRECISION
Определения include.php:46
$error
Определения subscription_card_product.php:20
$fields
Определения yandex_run.php:501