Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
basketitembase.php
1<?php
8namespace Bitrix\Sale;
9
10use Bitrix\Main;
15
16Localization\Loc::loadMessages(__FILE__);
17
23{
26
29
31 protected $provider;
32
34 protected $internalId = null;
35
36 protected static $idBasket = 0;
37
43 public function findItemByBasketCode($basketCode)
44 {
45 if ((string)$this->getBasketCode() === (string)$basketCode)
46 {
47 return $this;
48 }
49
50 return null;
51 }
52
59 public function findItemByXmlId($xmlId)
60 {
61 if ((string)$this->getField('XML_ID') === (string)$xmlId)
62 {
63 return $this;
64 }
65
66 return null;
67 }
68
72 public static function getRegistryEntity()
73 {
75 }
76
82 public function findItemById($id)
83 {
84 $id = (int)$id;
85
86 if ($id <= 0)
87 {
88 return null;
89 }
90
91 if ($this->getId() === $id)
92 {
93 return $this;
94 }
95
96 return null;
97 }
98
103 public function getBasketCode()
104 {
105 if ($this->internalId == null)
106 {
107 if ($this->getId() > 0)
108 {
109 $this->internalId = $this->getId();
110 }
111 else
112 {
113 static::$idBasket++;
114 $this->internalId = 'n'.static::$idBasket;
115 }
116 }
117
118 return $this->internalId;
119 }
120
131 public static function create(BasketItemCollection $basketItemCollection, $moduleId, $productId, $basketCode = null)
132 {
133 $fields = [
134 "MODULE" => $moduleId,
135 "BASE_PRICE" => 0,
136 "CAN_BUY" => 'Y',
137 "VAT_RATE" => null,
138 "CUSTOM_PRICE" => 'N',
139 "PRODUCT_ID" => $productId,
140 'XML_ID' => static::generateXmlId(),
141 ];
142
143 $basket = $basketItemCollection->getBasket();
144 if ($basket instanceof BasketBase)
145 {
146 $fields['LID'] = $basket->getSiteId();
147 }
148
149 $basketItem = static::createBasketItemObject($fields);
150
151 if ($basketCode !== null)
152 {
153 $basketItem->internalId = $basketCode;
154 if (mb_strpos($basketCode, 'n') === 0)
155 {
156 $internalId = intval(mb_substr($basketCode, 1));
157 if ($internalId > static::$idBasket)
158 {
159 static::$idBasket = $internalId;
160 }
161 }
162 }
163
164 $basketItem->setCollection($basketItemCollection);
165
166 return $basketItem;
167 }
168
172 protected static function generateXmlId()
173 {
174 return uniqid('bx_');
175 }
176
183 private static function createBasketItemObject(array $fields = [])
184 {
185 $registry = Registry::getInstance(static::getRegistryType());
186 $basketItemClassName = $registry->getBasketItemClassName();
187
188 return new $basketItemClassName($fields);
189 }
190
194 public static function getSettableFields()
195 {
196 $result = [
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",
205 "MARKING_CODE_GROUP"
206 ];
207
208 return array_merge($result, static::getCalculatedFields());
209 }
210
214 public static function getSettableFieldsMap()
215 {
216 static $fieldsMap = null;
217
218 if ($fieldsMap === null)
219 {
220 $fieldsMap = array_fill_keys(static::getSettableFields(), true);
221 }
222
223 return $fieldsMap;
224 }
225
229 public static function getCalculatedFields()
230 {
231 return [
232 'DISCOUNT_PRICE_PERCENT',
233 'IGNORE_CALLBACK_FUNC',
234 'DEFAULT_PRICE',
235 'DISCOUNT_LIST'
236 ];
237 }
238
242 public static function getAvailableFields()
243 {
244 return static::getSettableFields();
245 }
246
250 public static function getCustomizableFields() : array
251 {
252 return [
253 'PRICE' => 'PRICE',
254 'VAT_INCLUDED' => 'VAT_INCLUDED',
255 'VAT_RATE' => 'VAT_RATE'
256 ];
257 }
258
262 protected static function getMeaningfulFields()
263 {
264 return ['QUANTITY', 'PRICE', 'CUSTOM_PRICE'];
265 }
266
275 protected function __construct(array $fields = [])
276 {
277 parent::__construct($fields);
278
279 $this->calculatedFields = new Internals\Fields();
280 }
281
285 protected function checkBeforeDelete()
286 {
287 return new Result();
288 }
289
296 public function delete()
297 {
298 $result = new Result();
299
300 $checkResult = $this->checkBeforeDelete();
301 if (!$checkResult->isSuccess())
302 {
303 $result->addErrors($checkResult->getErrors());
304 return $result;
305 }
306
308 $oldEntityValues = $this->fields->getOriginalValues();
309
311 $event = new Main\Event('sale', "OnBeforeSaleBasketItemEntityDeleted", [
312 'ENTITY' => $this,
313 'VALUES' => $oldEntityValues,
314 ]);
315 $event->send();
316
317 if ($event->getResults())
318 {
320 foreach($event->getResults() as $eventResult)
321 {
322 if($eventResult->getType() == Main\EventResult::ERROR)
323 {
324 $eventResultData = $eventResult->getParameters();
325 if ($eventResultData instanceof ResultError)
326 {
327 $error = $eventResultData;
328 }
329 else
330 {
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'
334 );
335 }
336
337 $result->addError($error);
338 }
339 }
340
341 if (!$result->isSuccess())
342 {
343 return $result;
344 }
345 }
346
347 $this->setFieldNoDemand("QUANTITY", 0);
348
350 $r = parent::delete();
351 if (!$r->isSuccess())
352 {
353 $result->addErrors($r->getErrors());
354 return $result;
355 }
356
358 $oldEntityValues = $this->fields->getOriginalValues();
359
361 $event = new Main\Event('sale', "OnSaleBasketItemEntityDeleted", [
362 'ENTITY' => $this,
363 'VALUES' => $oldEntityValues,
364 ]);
365 $event->send();
366
367 if ($event->getResults())
368 {
370 foreach($event->getResults() as $eventResult)
371 {
372 if($eventResult->getType() == Main\EventResult::ERROR)
373 {
374 $eventResultData = $eventResult->getParameters();
375 if ($eventResultData instanceof ResultError)
376 {
377 $error = $eventResultData;
378 }
379 else
380 {
381 $error = new ResultError(
382 Localization\Loc::getMessage('SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'),
383 'SALE_EVENT_ON_SALEBASKETITEM_ENTITY_DELETED_ERROR'
384 );
385 }
386
387 $result->addError($error);
388 }
389 }
390 }
391
392 return $result;
393 }
394
395 protected function normalizeValue($name, $value)
396 {
397 if ($this->isPriceField($name))
398 {
399 $value = PriceMaths::roundPrecision($value);
400 }
401 elseif ($name === 'VAT_RATE')
402 {
403 $value = is_numeric($value) ? (float)$value : null;
404 }
405
406 return parent::normalizeValue($name, $value);
407 }
408
416 public function setField($name, $value)
417 {
418 if ($this->isCalculatedField($name))
419 {
420 $this->calculatedFields->set($name, $value);
421 return new Result();
422 }
423
424 if ($name === 'CUSTOM_PRICE')
425 {
426 if ($value == 'Y')
427 {
428 $this->markFieldCustom('PRICE');
429 }
430 else
431 {
432 $this->unmarkFieldCustom('PRICE');
433 }
434 }
435
436 return parent::setField($name, $value);
437 }
438
447 public function setFieldNoDemand($name, $value)
448 {
449 if ($this->isCalculatedField($name))
450 {
451 $this->calculatedFields->set($name, $value);
452 return;
453 }
454
455 if ($name === 'CUSTOM_PRICE')
456 {
457 if ($value === 'Y')
458 {
459 $this->markFieldCustom('PRICE');
460 }
461 else
462 {
463 $this->unmarkFieldCustom('PRICE');
464 }
465 }
466
467 parent::setFieldNoDemand($name, $value);
468 }
469
476 public function getField($name)
477 {
478 static $calculatedFields = null;
479
480 if ($calculatedFields === null)
481 {
482 $calculatedFields = array_fill_keys(static::getCalculatedFields(), true);
483 }
484
485 if (isset($calculatedFields[$name]))
486 {
487 if (
488 isset($this->calculatedFields[$name])
489 || (is_array($this->calculatedFields) && array_key_exists($name, $this->calculatedFields))
490 )
491 {
492 return $this->calculatedFields->get($name);
493 }
494
495 return null;
496 }
497
498 return parent::getField($name);
499 }
500
505 protected function onBeforeSetFields(array $values)
506 {
507 $priorityFields = [
508 'CURRENCY', 'CUSTOM_PRICE', 'VAT_RATE', 'VAT_INCLUDED',
509 'PRODUCT_PROVIDER_CLASS', 'SUBSCRIBE', 'TYPE', 'LID', 'FUSER_ID'
510 ];
511
512 $priorityValues = [];
513 foreach ($priorityFields as $field)
514 {
515 if (isset($values[$field]))
516 {
517 $priorityValues[$field] = $values[$field];
518 }
519 }
520
521 return $priorityValues + $values;
522 }
523
531 public function setFields(array $fields)
532 {
533 foreach ($fields as $name => $value)
534 {
535 if ($this->isCalculatedField($name))
536 {
537 $this->calculatedFields[$name] = $value;
538 unset($fields[$name]);
539 }
540 }
541
542 return parent::setFields($fields);
543 }
544
549 public function getProviderName()
550 {
551 return $this->getProvider();
552 }
553
558 public function getCallbackFunction()
559 {
560 $callbackFunction = $this->getField('CALLBACK_FUNC');
561 if (empty($callbackFunction))
562 {
563 return null;
564 }
565
566 $callbackFunction = trim((string)$callbackFunction);
567 if (empty($callbackFunction) || !function_exists($callbackFunction))
568 {
569 return null;
570 }
571
572 return $callbackFunction;
573 }
574
580 public function getProviderEntity()
581 {
582 $module = $this->getField('MODULE');
583 $productProviderName = $this->getField('PRODUCT_PROVIDER_CLASS');
584 if (
585 !isset($module)
586 || !isset($productProviderName)
587 || (strval($productProviderName) == "")
588 )
589 {
590 return false;
591 }
592
593 if (!empty($module) && Main\Loader::includeModule($module))
594 {
595 return Internals\Catalog\Provider::getProviderEntity($productProviderName);
596 }
597
598 return null;
599 }
600
606 public function getProvider()
607 {
608 if ($this->provider !== null)
609 return $this->provider;
610
611 $module = $this->getField('MODULE');
612 $productProviderName = $this->getField('PRODUCT_PROVIDER_CLASS');
613 if (
614 !isset($module)
615 || !isset($productProviderName)
616 || (strval($productProviderName) == "")
617 )
618 {
619 return null;
620 }
621
622 $providerName = Internals\Catalog\Provider::getProviderName($module, $productProviderName);
623 if ($providerName !== null)
624 {
625 $this->provider = $providerName;
626 }
627
628 return $providerName;
629 }
630
644 protected function onFieldModify($name, $oldValue, $value)
645 {
646 $result = new Result();
647
648 if ($name === "QUANTITY")
649 {
650 if ($value == 0)
651 {
652 $result->addError(new Main\Error(
653 Localization\Loc::getMessage(
654 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
655 ['#PRODUCT_NAME#' => $this->getField('NAME')]
656 )
657 ));
658 return $result;
659 }
660
661 $value = (float)$value;
662 $oldValue = (float)$oldValue;
663 $deltaQuantity = $value - $oldValue;
664
666 $basket = $this->getCollection();
667 $context = $basket->getContext();
668
670 $r = Internals\Catalog\Provider::getAvailableQuantityAndPriceByBasketItem($this, $context);
671 if (!$r->isSuccess())
672 {
673 $result->addErrors($r->getErrors());
674 $result->setData($r->getData());
675
676 return $result;
677 }
678
679 $providerData = $r->getData();
680
681 if ($this->getField('SUBSCRIBE') !== 'Y')
682 {
683 if ($providerData)
684 {
685 if (array_key_exists('AVAILABLE_QUANTITY', $providerData) && $providerData['AVAILABLE_QUANTITY'] > 0)
686 {
687 $availableQuantity = $providerData['AVAILABLE_QUANTITY'];
688 }
689 else
690 {
691 $result->addError(
692 new ResultError(
693 Localization\Loc::getMessage(
694 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY_2',
695 ['#PRODUCT_NAME#' => $this->getField('NAME')]
696 ),
697 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
698 )
699 );
700
701 return $result;
702 }
703 }
704 else
705 {
706 $errorMessageCode = 'SALE_BASKET_PRODUCT_NOT_AVAILABLE';
707 if ($this->isService())
708 {
709 $errorMessageCode = 'SALE_BASKET_SERVICE_NOT_AVAILABLE';
710 }
711
712 $result->addError(
713 new ResultError(
714 Localization\Loc::getMessage(
715 $errorMessageCode,
716 ['#PRODUCT_NAME#' => $this->getField('NAME')]
717 ),
718 'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'
719 )
720 );
721
722 return $result;
723 }
724 }
725 else
726 {
727 $availableQuantity = $value;
728 }
729
730 if (isset($providerData['PRICE_DATA']['CUSTOM_PRICE']))
731 {
732 $this->markFieldCustom('PRICE');
733 }
734
735 $notPurchasedQuantity = $this->getNotPurchasedQuantity();
736
737 if ($value != 0
738 && (
739 (
740 $deltaQuantity > 0
741 && roundEx($availableQuantity, SALE_VALUE_PRECISION) < roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
742 ) // plus
743 || (
744 $deltaQuantity < 0
745 && roundEx($availableQuantity, SALE_VALUE_PRECISION) > roundEx($notPurchasedQuantity, SALE_VALUE_PRECISION)
746 ) // minus
747 )
748 )
749 {
750 if ($deltaQuantity > 0)
751 {
752 $canAddCount = $availableQuantity - $this->getReservedQuantity();
753 if ($canAddCount > 0)
754 {
755 $mess = Localization\Loc::getMessage(
756 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_LESS',
757 [
758 '#PRODUCT_NAME#' => $this->getField('NAME'),
759 '#QUANTITY#' => $oldValue,
760 '#ADD#' => $canAddCount,
761 ]
762 );
763 }
764 else
765 {
766 $mess = Localization\Loc::getMessage(
767 'SALE_BASKET_AVAILABLE_FOR_ADDING_QUANTITY_IS_ZERO',
768 [
769 '#PRODUCT_NAME#' => $this->getField('NAME'),
770 '#QUANTITY#' => $oldValue,
771 ]
772 );
773 }
774 }
775 else
776 {
777 $mess = Localization\Loc::getMessage(
778 'SALE_BASKET_AVAILABLE_FOR_DECREASE_QUANTITY',
779 [
780 '#PRODUCT_NAME#' => $this->getField('NAME'),
781 '#AVAILABLE_QUANTITY#' => $availableQuantity
782 ]
783 );
784 }
785
786 $result->addError(new ResultError($mess, "SALE_BASKET_AVAILABLE_QUANTITY"));
787 $result->setData([
788 "AVAILABLE_QUANTITY" => $availableQuantity,
789 "REQUIRED_QUANTITY" => $deltaQuantity
790 ]);
791
792 return $result;
793 }
794
796 $collection = $this->getCollection();
797
799 $basket = $collection->getBasket();
800
801 if ((!$basket->getOrder() || $basket->getOrderId() == 0) && !($collection instanceof BundleCollection))
802 {
803 if (!$this->isMarkedFieldCustom('PRICE') && $value > 0)
804 {
805 $r = $basket->refresh(RefreshFactory::createSingle($this->getBasketCode()));
806 if (!$r->isSuccess())
807 {
808 $result->addErrors($r->getErrors());
809 return $result;
810 }
811 }
812 }
813
814 if (!$this->isMarkedFieldCustom('PRICE'))
815 {
816 $providerName = $this->getProviderName();
817 if (strval($providerName) == '')
818 {
819 $providerName = $this->getCallbackFunction();
820 }
821
822
823 if (!empty($providerData['PRICE_DATA']))
824 {
825 if (isset($providerData['PRICE_DATA']['PRICE']))
826 {
827 $this->setField('PRICE', $providerData['PRICE_DATA']['PRICE']);
828 }
829
830 if (isset($providerData['PRICE_DATA']['BASE_PRICE']))
831 {
832 $this->setField('BASE_PRICE', $providerData['PRICE_DATA']['BASE_PRICE']);
833 }
834
835 if (isset($providerData['PRICE_DATA']['DISCOUNT_PRICE']))
836 {
837 $this->setField('DISCOUNT_PRICE', $providerData['PRICE_DATA']['DISCOUNT_PRICE']);
838 }
839 }
840 elseif ($providerName && !$this->isCustom())
841 {
842 $result->addError(
843 new ResultError(
844 Localization\Loc::getMessage(
845 'SALE_BASKET_ITEM_WRONG_PRICE',
846 ['#PRODUCT_NAME#' => $this->getField('NAME')]
847 ),
848 'SALE_BASKET_ITEM_WRONG_PRICE'
849 )
850 );
851
852 return $result;
853 }
854 }
855 }
856
857 $r = parent::onFieldModify($name, $oldValue, $value);
858 if ($r->isSuccess())
859 {
860 if ($r->hasWarnings())
861 {
862 $result->addWarnings($r->getWarnings());
863 }
864
865 if (
866 $name === 'BASE_PRICE'
867 || $name === 'DISCOUNT_PRICE'
868 )
869 {
870 if (!$this->isCustomPrice())
871 {
872 $price = $this->getField('BASE_PRICE') - $this->getField('DISCOUNT_PRICE');
873 $r = $this->setField('PRICE', $price);
874 if (!$r->isSuccess())
875 $result->addErrors($r->getErrors());
876 }
877 }
878 }
879 else
880 {
881 $result->addErrors($r->getErrors());
882 }
883
884 return $result;
885 }
886
891 public function isVatInPrice()
892 {
893 return $this->getField('VAT_INCLUDED') === 'Y';
894 }
895
900 public function getVat()
901 {
902 $calculator = new VatCalculator((float)$this->getVatRate());
903 $vat = $calculator->calc(
904 $this->getPrice(),
905 $this->isVatInPrice(),
906 false
907 );
908
909 return PriceMaths::roundPrecision($vat * $this->getQuantity());
910 }
911
916 public function getInitialPrice()
917 {
918 $price = PriceMaths::roundPrecision($this->getPrice() * $this->getQuantity());
919
920 if ($this->isVatInPrice())
921 $price -= $this->getVat();
922
923 return $price;
924 }
925
930 public function getBasePriceWithVat()
931 {
932 $price = $this->getBasePrice();
933
934 if (!$this->isVatInPrice())
935 {
936 $vatRate = (float)$this->getVatRate();
937 $price += $this->getBasePrice() * $vatRate;
938 }
939
940 return PriceMaths::roundPrecision($price);
941 }
942
947 public function getPriceWithVat()
948 {
949 $price = $this->getPrice();
950
951 if (!$this->isVatInPrice())
952 {
953 $vatRate = (float)$this->getVatRate();
954 $price += $this->getPrice() * $vatRate;
955 }
956
957 return PriceMaths::roundPrecision($price);
958 }
959
964 public function getFinalPrice()
965 {
966 $price = PriceMaths::roundPrecision($this->getPrice() * $this->getQuantity());
967
968 if (!$this->isVatInPrice())
969 $price += $this->getVat();
970
971 return $price;
972 }
973
978 protected function isCalculatedField($field)
979 {
980 static $calculateFields = null;
981
982 if ($calculateFields === null)
983 {
984 $calculateFields = array_fill_keys(static::getCalculatedFields(), true);
985 }
986
987 return isset($calculateFields[$field]);
988 }
989
993 public function getProductId()
994 {
995 return (int)$this->getField('PRODUCT_ID');
996 }
997
1002 public function getPrice()
1003 {
1004 return (float)$this->getField('PRICE');
1005 }
1006
1011 public function getBasePrice()
1012 {
1013 return (float)$this->getField('BASE_PRICE');
1014 }
1015
1020 public function getDiscountPrice()
1021 {
1022 return (float)$this->getField('DISCOUNT_PRICE');
1023 }
1024
1029 public function isCustomPrice()
1030 {
1031 return $this->isMarkedFieldCustom('PRICE');
1032 }
1033
1039 public function getCurrency()
1040 {
1041 return $this->getField('CURRENCY');
1042 }
1043
1051 public function changeCurrency(string $currency): Main\Result
1052 {
1053 $result = new Main\Result();
1054
1055 $oldCurrency = $this->getCurrency();
1056 if ($oldCurrency === $currency)
1057 {
1058 return $result;
1059 }
1060
1061 $result->addErrors(
1062 $this->setField('CURRENCY', $currency)->getErrors()
1063 );
1064
1065 return $result;
1066 }
1067
1072 public function getQuantity() : float
1073 {
1074 return (float)$this->getField('QUANTITY');
1075 }
1076
1080 public function getNotPurchasedQuantity() : float
1081 {
1082 return (float)$this->getField('QUANTITY');
1083 }
1084
1089 public function getWeight()
1090 {
1091 return $this->getField('WEIGHT');
1092 }
1093
1098 public function getVatRate()
1099 {
1100 return $this->getField('VAT_RATE');
1101 }
1102
1107 public function getFUserId()
1108 {
1109 return $this->getField('FUSER_ID');
1110 }
1111
1117 public function setOrderId($id)
1118 {
1119 $this->setField('ORDER_ID', (int)$id);
1120 }
1121
1126 public function isBarcodeMulti()
1127 {
1128 return $this->getField('BARCODE_MULTI') === "Y";
1129 }
1130
1136 public function canBuy() : bool
1137 {
1138 return $this->getField('CAN_BUY') === 'Y';
1139 }
1140
1146 public function isDelay() : bool
1147 {
1148 return $this->getField('DELAY') === 'Y';
1149 }
1150
1156 public function isSupportedMarkingCode() : bool
1157 {
1158 return $this->getMarkingCodeGroup() !== '';
1159 }
1160
1166 public function getMarkingCodeGroup() : string
1167 {
1168 return (string)$this->getField('MARKING_CODE_GROUP');
1169 }
1170
1178 public function getPropertyCollection()
1179 {
1180 if ($this->propertyCollection === null)
1181 {
1182 $registry = Registry::getInstance(static::getRegistryType());
1183
1185 $basketPropertyCollectionClassName = $registry->getBasketPropertiesCollectionClassName();
1186
1187 if ($this->getId() > 0)
1188 {
1190 $collection = $this->getCollection();
1191 $basketPropertyCollectionClassName::loadByCollection($collection);
1192 }
1193
1194 if ($this->propertyCollection === null)
1195 {
1196 $this->propertyCollection = $basketPropertyCollectionClassName::load($this);
1197 }
1198 }
1199
1201 }
1202
1207 {
1208 return $this->propertyCollection !== null;
1209 }
1210
1216 {
1217 $this->propertyCollection = $propertyCollection;
1218 }
1219
1227 public function setPrice($value, $custom = false)
1228 {
1229 $result = new Result();
1230
1231 if ($custom)
1232 {
1233 $this->markFieldCustom('PRICE');
1234 }
1235 else
1236 {
1237 $this->unmarkFieldCustom('PRICE');
1238 }
1239
1240 $r = $this->setField('PRICE', $value);
1241 if (!$r->isSuccess())
1242 {
1243 $result->addErrors($r->getErrors());
1244 }
1245
1246 return $result;
1247 }
1248
1253 public static function getRoundFields()
1254 {
1255 return [
1256 'BASE_PRICE',
1257 'DISCOUNT_PRICE',
1258 'DISCOUNT_PRICE_PERCENT',
1259 ];
1260 }
1261
1266 public function initFields(array $values)
1267 {
1268 if (!isset($values['BASE_PRICE']) || doubleval($values['BASE_PRICE']) == 0)
1269 $values['BASE_PRICE'] = $values['PRICE'] + $values['DISCOUNT_PRICE'];
1270
1271 parent::initFields($values);
1272 }
1273
1282 public function save()
1283 {
1284 $this->checkCallingContext();
1285
1286 $result = new Result();
1287
1288 $id = $this->getId();
1289 $isNew = $id === 0;
1290
1291 $this->onBeforeSave();
1292
1293 $r = $this->callEventSaleBasketItemBeforeSaved($isNew);
1294 if (!$r->isSuccess())
1295 {
1296 return $r;
1297 }
1298
1299 if (!$this->isChanged())
1300 {
1301 return $result;
1302 }
1303
1304 if ($id > 0)
1305 {
1306 $r = $this->update();
1307 }
1308 else
1309 {
1310 $r = $this->add();
1311 if ($r->getId() > 0)
1312 {
1313 $id = $r->getId();
1314 }
1315 }
1316
1317 if (!$r->isSuccess())
1318 {
1319 return $r;
1320 }
1321
1322 if ($id > 0)
1323 {
1324 $result->setId($id);
1325
1326 $controller = Internals\CustomFieldsController::getInstance();
1327 $controller->save($this);
1328 }
1329
1330 $r = $this->callEventSaleBasketItemSaved($isNew);
1331 if (!$r->isSuccess())
1332 {
1333 return $r;
1334 }
1335
1336 $propertyCollection = $this->getPropertyCollection();
1337 $r = $propertyCollection->save();
1338 if (!$r->isSuccess())
1339 {
1340 $result->addErrors($r->getErrors());
1341 }
1342
1343 $this->callEventOnBasketItemEntitySaved();
1344
1345 return $result;
1346 }
1347
1351 public function getBasket()
1352 {
1354 $collection = $this->getCollection();
1355
1356 return $collection->getBasket();
1357 }
1358
1362 private function checkCallingContext()
1363 {
1364 $basket = $this->getBasket();
1365
1366 $order = $basket->getOrder();
1367
1368 if ($order)
1369 {
1370 if (!$order->isSaveRunning())
1371 {
1372 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1373 }
1374 }
1375 else
1376 {
1377 if (!$basket->isSaveRunning())
1378 {
1379 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Basket entity", E_USER_WARNING);
1380 }
1381 }
1382 }
1383
1390 protected function onBeforeSave()
1391 {
1393 $collection = $this->getCollection();
1394
1395 $basket = $collection->getBasket();
1396
1397 if ($this->getField('ORDER_ID') <= 0)
1398 {
1399 $orderId = $collection->getOrderId();
1400 if ($orderId > 0)
1401 {
1402 $this->setFieldNoDemand('ORDER_ID', $orderId);
1403 }
1404 }
1405
1406 if ($this->getId() <= 0)
1407 {
1408 if ($this->getField('FUSER_ID') <= 0)
1409 {
1410 $fUserId = (int)$basket->getFUserId(true);
1411 if ($fUserId <= 0)
1412 {
1413 throw new Main\ArgumentNullException('FUSER_ID');
1414 }
1415
1416 $this->setFieldNoDemand('FUSER_ID', $fUserId);
1417 }
1418 }
1419 }
1420
1426 protected function add()
1427 {
1428 $result = new Result();
1429
1430 $dateInsert = new Main\Type\DateTime();
1431
1432 $this->setFieldNoDemand('DATE_INSERT', $dateInsert);
1433 $this->setFieldNoDemand('DATE_UPDATE', $dateInsert);
1434
1435 $fields = $this->fields->getValues();
1436
1437 $r = $this->addInternal($fields);
1438 if (!$r->isSuccess())
1439 {
1440 $result->addErrors($r->getErrors());
1441 return $result;
1442 }
1443
1444 if ($resultData = $r->getData())
1445 {
1446 $result->setData($resultData);
1447 }
1448
1449 $id = $r->getId();
1450 $this->setFieldNoDemand('ID', $id);
1451 $result->setId($id);
1452
1453 return $result;
1454 }
1455
1462 protected function update()
1463 {
1464 $result = new Result();
1465
1466 $this->setFieldNoDemand('DATE_UPDATE', new Main\Type\DateTime());
1467
1468 $fields = $this->fields->getChangedValues();
1469
1470 if (!empty($fields))
1471 {
1472 $r = $this->updateInternal($this->getId(), $fields);
1473 if (!$r->isSuccess())
1474 {
1475 $result->addErrors($r->getErrors());
1476 return $result;
1477 }
1478
1479 if ($resultData = $r->getData())
1480 {
1481 $result->setData($resultData);
1482 }
1483 }
1484
1485 return $result;
1486 }
1487
1491 protected function callEventOnBasketItemEntitySaved()
1492 {
1494 $oldEntityValues = $this->fields->getOriginalValues();
1495
1496 if (!empty($oldEntityValues))
1497 {
1499 $event = new Main\Event(
1500 'sale',
1501 'OnSaleBasketItemEntitySaved',
1502 [
1503 'ENTITY' => $this,
1504 'VALUES' => $oldEntityValues,
1505 ]
1506 );
1507
1508 $event->send();
1509 }
1510 }
1511
1516 protected function callEventSaleBasketItemBeforeSaved($isNewEntity)
1517 {
1518 $result = new Result();
1519
1521 $oldEntityValues = $this->fields->getOriginalValues();
1522
1524 $event = new Main\Event('sale', EventActions::EVENT_ON_BASKET_ITEM_BEFORE_SAVED, [
1525 'ENTITY' => $this,
1526 'IS_NEW' => $isNewEntity,
1527 'VALUES' => $oldEntityValues,
1528 ]);
1529 $event->send();
1530
1531 if ($event->getResults())
1532 {
1534 foreach($event->getResults() as $eventResult)
1535 {
1536 if($eventResult->getType() == Main\EventResult::ERROR)
1537 {
1538 $errorMsg = new ResultError(
1539 Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'),
1540 'SALE_EVENT_ON_BEFORE_BASKET_ITEM_SAVED'
1541 );
1542
1543 $eventResultData = $eventResult->getParameters();
1544 if ($eventResultData instanceof ResultError)
1545 $errorMsg = $eventResultData;
1546
1547 $result->addError($errorMsg);
1548 }
1549 }
1550 }
1551
1552 return $result;
1553 }
1554
1559 protected function callEventSaleBasketItemSaved($isNewEntity)
1560 {
1561 $result = new Result();
1562
1564 $oldEntityValues = $this->fields->getOriginalValues();
1565
1567 $event = new Main\Event('sale', EventActions::EVENT_ON_BASKET_ITEM_SAVED, [
1568 'ENTITY' => $this,
1569 'IS_NEW' => $isNewEntity,
1570 'VALUES' => $oldEntityValues,
1571 ]);
1572 $event->send();
1573
1574 if ($event->getResults())
1575 {
1577 foreach($event->getResults() as $eventResult)
1578 {
1579 if($eventResult->getType() == Main\EventResult::ERROR)
1580 {
1581 $errorMsg = new ResultError(
1582 Localization\Loc::getMessage('SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'),
1583 'SALE_EVENT_ON_BASKET_ITEM_SAVED_ERROR'
1584 );
1585 $eventResultData = $eventResult->getParameters();
1586
1587 if ($eventResultData instanceof ResultError)
1588 $errorMsg = $eventResultData;
1589
1590 $result->addError($errorMsg);
1591 }
1592 }
1593 }
1594
1595 return $result;
1596 }
1597
1602 abstract protected function addInternal(array $fields);
1603
1609 abstract protected function updateInternal($primary, array $fields);
1610
1620 public function isChanged()
1621 {
1622 $isChanged = parent::isChanged();
1623
1624 if ($isChanged === false)
1625 {
1626 $propertyCollection = $this->getPropertyCollection();
1627 $isChanged = $propertyCollection->isChanged();
1628 }
1629
1630 return $isChanged;
1631 }
1632
1640 public static function load(BasketItemCollection $basketItemCollection, $data)
1641 {
1642 $basketItem = static::createBasketItemObject($data);
1643 $basketItem->setCollection($basketItemCollection);
1644
1645 return $basketItem;
1646 }
1647
1654 public function verify()
1655 {
1656 $result = new Result();
1657
1658 if ((float)$this->getField('QUANTITY') <= 0)
1659 {
1660 $result->addError(new Main\Error(
1661 Localization\Loc::getMessage(
1662 'SALE_BASKET_ITEM_ERR_QUANTITY_ZERO',
1663 ['#PRODUCT_NAME#' => $this->getField('NAME')]
1664 )
1665 ));
1666 }
1667
1668 if (!$this->getField('CURRENCY'))
1669 {
1670 $result->addError(new Main\Error(
1671 Localization\Loc::getMessage('SALE_BASKET_ITEM_ERR_CURRENCY_EMPTY')
1672 ));
1673 }
1674
1675 if ($basketPropertyCollection = $this->getPropertyCollection())
1676 {
1677 $r = $basketPropertyCollection->verify();
1678 if (!$r->isSuccess())
1679 {
1680 $result->addErrors($r->getErrors());
1681 }
1682 }
1683
1684 return $result;
1685 }
1686
1690 abstract public function getReservedQuantity();
1691
1697 public function isCustom()
1698 {
1699 $moduleId = trim($this->getField('MODULE'));
1700 $providerClassName = trim($this->getField('PRODUCT_PROVIDER_CLASS'));
1701 $callback = trim($this->getField('CALLBACK_FUNC'));
1702
1703 return (empty($moduleId) && empty($providerClassName) && empty($callback));
1704 }
1705
1711 public static function getEntityEventName()
1712 {
1713 return 'SaleBasketItem';
1714 }
1715
1716 protected function isPriceField(string $name) : bool
1717 {
1718 return
1719 $name === 'BASE_PRICE'
1720 || $name === 'PRICE'
1721 || $name === 'DISCOUNT_PRICE'
1722 ;
1723 }
1724
1730 public function toArray() : array
1731 {
1732 $result = parent::toArray();
1733
1734 $result['PROPERTIES'] = $this->getPropertyCollection()->toArray();
1735
1736 return $result;
1737 }
1738
1746 public function getDefaultPrice()
1747 {
1748 return (float)$this->getField('DEFAULT_PRICE');
1749 }
1750}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
changeCurrency(string $currency)
setPropertyCollection(BasketPropertiesCollectionBase $propertyCollection)
__construct(array $fields=[])
setPrice($value, $custom=false)
updateInternal($primary, array $fields)
addInternal(array $fields)
static create(BasketItemCollection $basketItemCollection, $moduleId, $productId, $basketCode=null)
static load(BasketItemCollection $basketItemCollection, $data)
static roundPrecision($value)
static getInstance($type)
Definition registry.php:183