Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
basketbase.php
1<?php
2namespace Bitrix\Sale;
3
9
10Loc::loadMessages(__FILE__);
11
16abstract class BasketBase extends BasketItemCollection
17{
19 protected $siteId = null;
20
22 protected $fUserId = null;
23
25 protected $order = null;
26
28 protected $basketItemIndexMap = array();
29
31 protected $maxItemSort = null;
32
34 private $isLoadForFUserId = false;
35
37 protected $isSaveExecuting = false;
38
45 public function getItemByBasketCode($code)
46 {
47 if (
48 isset($this->basketItemIndexMap[$code])
49 && isset($this->collection[$this->basketItemIndexMap[$code]])
50 )
51 {
52 return $this->collection[$this->basketItemIndexMap[$code]];
53 }
54
55 return parent::getItemByBasketCode($code);
56 }
57
61 protected function getEntityParent()
62 {
63 return $this->getOrder();
64 }
65
71 private static function createBasketObject()
72 {
73 $registry = Registry::getInstance(static::getRegistryType());
74 $basketClassName = $registry->getBasketClassName();
75
76 return new $basketClassName;
77 }
78
87 public static function loadItemsForFUser($fUserId, $siteId)
88 {
90 $basket = static::create($siteId);
91
92 $basket->setFUserId($fUserId);
93
94 $basket->isLoadForFUserId = true;
95
97 return $basket->loadFromDb([
98 "=FUSER_ID" => $fUserId,
99 "=LID" => $siteId,
100 "ORDER_ID" => null
101 ]);
102 }
103
111 protected function loadFromDb(array $filter)
112 {
113 $select = [
114 "ID", "LID", "MODULE", "PRODUCT_ID", "QUANTITY", "WEIGHT",
115 "DELAY", "CAN_BUY", "PRICE", "CUSTOM_PRICE", "BASE_PRICE",
116 'PRODUCT_PRICE_ID', 'PRICE_TYPE_ID', "CURRENCY", 'BARCODE_MULTI',
117 "RESERVED", "RESERVE_QUANTITY", "NAME", "CATALOG_XML_ID",
118 "VAT_RATE", "NOTES", "DISCOUNT_PRICE","PRODUCT_PROVIDER_CLASS",
119 "CALLBACK_FUNC", "ORDER_CALLBACK_FUNC", "PAY_CALLBACK_FUNC",
120 "CANCEL_CALLBACK_FUNC", "DIMENSIONS", "TYPE", "SET_PARENT_ID",
121 "DETAIL_PAGE_URL", "FUSER_ID", 'MEASURE_CODE', 'MEASURE_NAME',
122 'ORDER_ID', 'DATE_INSERT', 'DATE_UPDATE', 'PRODUCT_XML_ID',
123 'SUBSCRIBE', 'RECOMMENDATION', 'VAT_INCLUDED', 'SORT',
124 'DATE_REFRESH', 'DISCOUNT_NAME', 'DISCOUNT_VALUE', 'DISCOUNT_COUPON',
125 'XML_ID', 'MARKING_CODE_GROUP'
126 ];
127
128 $itemList = [];
129 $first = true;
130
131 $res = static::getList([
132 "select" => $select,
133 "filter" => $filter,
134 "order" => ['SORT' => 'ASC', 'ID' => 'ASC'],
135 ]);
136 while ($item = $res->fetch())
137 {
138 if ($first)
139 {
140 $this->setSiteId($item['LID']);
141 $this->setFUserId($item['FUSER_ID']);
142 $first = false;
143 }
144
145 $itemList[$item['ID']] = $item;
146 }
147
148 foreach ($itemList as $id => $item)
149 {
150 if ($item['SET_PARENT_ID'] > 0)
151 {
152 $itemList[$item['SET_PARENT_ID']]['ITEMS'][$id] = &$itemList[$id];
153 }
154 }
155
156 $result = [];
157 foreach ($itemList as $id => $item)
158 {
159 if ($item['SET_PARENT_ID'] == 0)
160 {
161 $result[$id] = $item;
162 }
163 }
164
165 $this->loadFromArray($result);
166
167 return $this;
168 }
169
175 public function setOrder(OrderBase $order)
176 {
177 $this->order = $order;
178 }
179
185 public function getOrder()
186 {
187 return $this->order;
188 }
189
190
196 protected function verifyItemSort(BasketItemBase $item)
197 {
198 $itemSort = (int)$item->getField('SORT') ?: 100;
199
200 if ($this->maxItemSort === null)
201 {
202 $this->maxItemSort = $itemSort;
203 }
204 else
205 {
206 if ($itemSort > $this->maxItemSort)
207 {
208 $this->maxItemSort = $itemSort;
209 }
210 else
211 {
212 $this->maxItemSort += 100 + $this->maxItemSort % 100;
213 }
214 }
215
216 $item->setFieldNoDemand('SORT', $this->maxItemSort);
217 }
218
225 public static function create($siteId)
226 {
227 $basket = static::createBasketObject();
228 $basket->setSiteId($siteId);
229
230 return $basket;
231 }
232
239 public function getPrice()
240 {
241 $orderPrice = 0;
242
244 foreach ($this->collection as $basketItem)
245 {
246 $orderPrice += $basketItem->getFinalPrice();
247 }
248
249 return $orderPrice;
250 }
251
258 public function getBasePrice()
259 {
260 $orderPrice = 0;
261
263 foreach ($this->collection as $basketItem)
264 {
265 $orderPrice += PriceMaths::roundPrecision($basketItem->getBasePriceWithVat() * $basketItem->getQuantity());
266 }
267
268 return $orderPrice;
269 }
270
277 public function getVatSum()
278 {
279 $vatSum = 0;
280
282 foreach ($this->collection as $basketItem)
283 {
284 // BasketItem that is removed is not involved
285 if ($basketItem->getQuantity() == 0)
286 {
287 continue;
288 }
289
290 $vatSum += $basketItem->getVat();
291 }
292
293 return $vatSum;
294 }
295
302 public function getVatRate()
303 {
304 $vatRate = 0;
305
307 foreach ($this->collection as $basketItem)
308 {
309 // BasketItem that is removed is not involved
310 if ($basketItem->getQuantity() == 0)
311 {
312 continue;
313 }
314
315 if ($basketItem->getVatRate() > $vatRate)
316 {
317 $vatRate = $basketItem->getVatRate();
318 }
319 }
320
321 return $vatRate;
322 }
323
330 public function verify()
331 {
332 $result = new Result();
333
335 foreach ($this->collection as $basketItem)
336 {
337 $r = $basketItem->verify();
338 if (!$r->isSuccess())
339 {
340 $result->addErrors($r->getErrors());
341
343 if ($order = $this->getOrder())
344 {
345 $registry = Registry::getInstance(static::getRegistryType());
346
348 $entityMarker = $registry->getEntityMarkerClassName();
349 $entityMarker::addMarker($order, $basketItem, $r);
350 $order->setField('MARKED', 'Y');
351 }
352 }
353 }
354
355 return $result;
356 }
357
363 public function getWeight()
364 {
365 $orderWeight = 0;
366
368 foreach ($this->collection as $basketItem)
369 {
370 $orderWeight += $basketItem->getWeight() * $basketItem->getQuantity();
371 }
372
373 return $orderWeight;
374 }
375
379 private function getOriginalItemsValues()
380 {
381 $result = array();
382
384 $order = $this->getOrder();
385 $isNew = $order && $order->isNew();
386
387 $filter = array();
388 if (!$isNew && $order && $order->getId() > 0)
389 {
390 $filter['ORDER_ID'] = $order->getId();
391 }
392 else
393 {
394 if ($this->isLoadForFUserId)
395 {
396 $filter = array(
397 '=FUSER_ID' => $this->getFUserId(),
398 'ORDER_ID' => null,
399 '=LID' => $this->getSiteId()
400 );
401 }
402
403 if ($isNew)
404 {
405 $fUserId = $this->getFUserId(true);
406 if ($fUserId <= 0)
407 {
408 $userId = $order->getUserId();
409 if (intval($userId) > 0)
410 {
412 if ($fUserId > 0)
413 $this->setFUserId($fUserId);
414 }
415 }
416 }
417 }
418
419 if ($filter)
420 {
421 $dbRes = static::getList(
422 array(
423 "select" => array("ID", 'TYPE', 'SET_PARENT_ID', 'PRODUCT_ID', 'NAME', 'QUANTITY', 'FUSER_ID', 'ORDER_ID'),
424 "filter" => $filter,
425 )
426 );
427
428 while ($item = $dbRes->fetch())
429 {
430 if ((int)$item['SET_PARENT_ID'] > 0 && (int)$item['SET_PARENT_ID'] != $item['ID'])
431 {
432 continue;
433 }
434
435 $result[$item["ID"]] = $item;
436 }
437 }
438
439 return $result;
440 }
441
446 abstract protected function deleteInternal(array $itemValues);
447
457 public function save()
458 {
459 $this->checkCallingContext();
460
461 $result = new Result();
462
463 $this->isSaveExecuting = true;
464
466 $order = $this->getOrder();
467 if (!$order)
468 {
469 $r = $this->verify();
470 if (!$r->isSuccess())
471 {
472 return $result->addErrors($r->getErrors());
473 }
474
475 $r = $this->callEventOnSaleBasketBeforeSaved();
476 if (!$r->isSuccess())
477 {
478 $this->isSaveExecuting = false;
479
480 return $result->addErrors($r->getErrors());
481 }
482 }
483
484 $originalItemsValues = $this->getOriginalItemsValues();
485
487 foreach ($this->collection as $basketItem)
488 {
489 $r = $basketItem->save();
490 if (!$r->isSuccess())
491 {
492 $result->addErrors($r->getErrors());
493 }
494
495 if (isset($originalItemsValues[$basketItem->getId()]))
496 {
497 unset($originalItemsValues[$basketItem->getId()]);
498 }
499 }
500
501 if ($originalItemsValues)
502 {
503 foreach ($originalItemsValues as $itemValues)
504 {
505 $this->callEventOnBeforeSaleBasketItemDeleted($itemValues);
506
507 $this->deleteInternal($itemValues);
508
509 $this->callEventOnSaleBasketItemDeleted($itemValues);
510 }
511 }
512
513 if (!$order)
514 {
515 $r = $this->callEventOnSaleBasketSaved();
516 if (!$r->isSuccess())
517 {
518 $result->addErrors($r->getErrors());
519 }
520 }
521
522 $this->clearChanged();
523
524 $this->isSaveExecuting = false;
525
526 return $result;
527 }
528
532 private function checkCallingContext() : void
533 {
534 $order = $this->getOrder();
535
536 if (
537 $order
538 && !$order->isSaveRunning()
539 )
540 {
541 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity.", E_USER_WARNING);
542 }
543 }
544
549 private function callEventOnBeforeSaleBasketItemDeleted($itemValues)
550 {
551 $itemValues['ENTITY_REGISTRY_TYPE'] = static::getRegistryType();
552
553 $event = new Main\Event('sale', "OnBeforeSaleBasketItemDeleted", array('VALUES' => $itemValues));
554 $event->send();
555 }
556
561 protected function callEventOnSaleBasketItemDeleted($itemValues)
562 {
563 $itemValues['ENTITY_REGISTRY_TYPE'] = static::getRegistryType();
564
565 $event = new Main\Event('sale', "OnSaleBasketItemDeleted", array('VALUES' => $itemValues));
566 $event->send();
567 }
568
572 protected function callEventOnSaleBasketBeforeSaved()
573 {
574 $result = new Result();
575
577 $event = new Main\Event(
578 'sale',
580 array('ENTITY' => $this)
581 );
582 $event->send();
583
584 if ($event->getResults())
585 {
587 foreach ($event->getResults() as $eventResult)
588 {
589 if ($eventResult->getType() == Main\EventResult::ERROR)
590 {
591 $errorMsg = new ResultError(
592 Main\Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_BASKET_SAVED'),
593 'SALE_EVENT_ON_BEFORE_BASKET_SAVED'
594 );
595 if ($eventResultData = $eventResult->getParameters())
596 {
597 if (isset($eventResultData) && $eventResultData instanceof ResultError)
598 {
600 $errorMsg = $eventResultData;
601 }
602 }
603
604 $result->addError($errorMsg);
605 }
606 }
607 }
608
609 return $result;
610 }
611
615 protected function callEventOnSaleBasketSaved()
616 {
617 $result = new Result();
618
620 $event = new Main\Event('sale', EventActions::EVENT_ON_BASKET_SAVED, array(
621 'ENTITY' => $this
622 ));
623 $event->send();
624
625 if ($event->getResults())
626 {
628 foreach($event->getResults() as $eventResult)
629 {
630 if($eventResult->getType() == Main\EventResult::ERROR)
631 {
632 $errorMsg = new ResultError(
633 Main\Localization\Loc::getMessage('SALE_EVENT_ON_BASKET_SAVED'),
634 'SALE_EVENT_ON_BASKET_SAVED'
635 );
636 if ($eventResultData = $eventResult->getParameters())
637 {
638 if (isset($eventResultData) && $eventResultData instanceof ResultError)
639 {
641 $errorMsg = $eventResultData;
642 }
643 }
644
645 $result->addError($errorMsg);
646 }
647 }
648 }
649
650 return $result;
651 }
652
658 public function setFUserId($fUserId)
659 {
660 $this->fUserId = (int)$fUserId > 0 ? (int)$fUserId : null;
661 }
662
668 protected function setSiteId($siteId)
669 {
670 $this->siteId = $siteId;
671 }
672
679 public function getFUserId($skipCreate = false)
680 {
681 if ($this->fUserId === null)
682 {
683 $this->fUserId = Fuser::getId($skipCreate);
684 }
685 return $this->fUserId;
686 }
687
693 public function getSiteId()
694 {
695 return $this->siteId;
696 }
697
703 public static function getList(array $parameters = array())
704 {
706 }
707
716 public function onItemModify(Internals\CollectableEntity $item, $name = null, $oldValue = null, $value = null)
717 {
718 if (!($item instanceof BasketItemBase))
719 throw new Main\ArgumentTypeException($item);
720
721 $result = new Result();
722
724 if ($order = $this->getOrder())
725 {
726 $r = $order->onBasketModify(EventActions::UPDATE, $item, $name, $oldValue, $value);
727 if (!$r->isSuccess())
728 {
729 $result->addErrors($r->getErrors());
730 }
731 elseif ($r->hasWarnings())
732 {
733 $result->addWarnings($r->getWarnings());
734 }
735 }
736
737 return $result;
738 }
739
744 public function refresh(RefreshStrategy $strategy = null)
745 {
746 $isStartField = $this->isStartField();
747
749 $order = $this->getOrder();
750 if ($order)
751 {
752 $r = $order->onBeforeBasketRefresh();
753 if (!$r->isSuccess())
754 {
755 return $r;
756 }
757 }
758
759 if ($strategy === null)
760 {
761 $strategy = RefreshFactory::create();
762 }
763
764 $result = $strategy->refresh($this);
765
766 if ($order)
767 {
768 $r = $order->onAfterBasketRefresh();
769 if (!$r->isSuccess())
770 {
771 return $r;
772 }
773 }
774
775 $changedBasketItems = $result->get('CHANGED_BASKET_ITEMS');
776 if (!empty($changedBasketItems))
777 {
779 $order = $this->getOrder();
780 if ($order)
781 {
782 $r = $order->refreshData(array('PRICE', 'PRICE_DELIVERY'));
783 if (!$r->isSuccess())
784 {
785 $result->addErrors($r->getErrors());
786 }
787 }
788 }
789
790 if ($isStartField)
791 {
792 $hasMeaningfulFields = $this->hasMeaningfulField();
793
795 $r = $this->doFinalAction($hasMeaningfulFields);
796 if (!$r->isSuccess())
797 {
798 $result->addErrors($r->getErrors());
799 }
800 }
801
802 return $result;
803 }
804
809 public function getOrderableItems()
810 {
812 $basket = static::create($this->getSiteId());
813
814 if ($this->isLoadForFUserId)
815 {
816 $basket->setFUserId($this->getFUserId(true));
817 }
818
819 if ($order = $this->getOrder())
820 {
821 $basket->setOrder($order);
822 }
823
824 $sortedCollection = $this->collection;
825 usort($sortedCollection, function(BasketItemBase $a, BasketItemBase $b){
826 return (int)$a->getField('SORT') - (int)$b->getField('SORT');
827 });
828
830 foreach ($sortedCollection as $item)
831 {
832 if (!$item->canBuy() || $item->isDelay())
833 continue;
834
835 $item->setCollection($basket);
836 $basket->addItem($item);
837 }
838
839 return $basket;
840 }
841
845 public function getBasket()
846 {
847 return $this;
848 }
849
855 public static function deleteNoDemand($idOrder)
856 {
858 }
859
863 public function isSaveRunning()
864 {
866 }
867
873 public function getContext()
874 {
875 $context = array();
876
877 $order = $this->getOrder();
879 if ($order)
880 {
881 $context['USER_ID'] = $order->getUserId();
882 $context['SITE_ID'] = $order->getSiteId();
883 $context['CURRENCY'] = $order->getCurrency();
884 }
885 else
886 {
887 $context = parent::getContext();
888 }
889
890 return $context;
891 }
892
899 public function getQuantityList()
900 {
901 $quantityList = array();
902
907 foreach ($this->collection as $basketKey => $basketItem)
908 {
909 $quantityList[$basketItem->getBasketCode()] = $basketItem->getQuantity();
910 }
911
912 return $quantityList;
913 }
914
925 public function deleteItem($index)
926 {
927 $oldItem = parent::deleteItem($index);
928
929 unset($this->basketItemIndexMap[$oldItem->getBasketCode()]);
930
932 if ($order = $this->getOrder())
933 {
934 $order->onBasketModify(EventActions::DELETE, $oldItem);
935 }
936
937 return $oldItem;
938 }
939
949 public function applyDiscount(array $basketRows)
950 {
951 $result = new Result();
952
953 if ($this->count() == 0 || empty($basketRows))
954 return $result;
955
957 foreach ($this->collection as $basketItem)
958 {
959 if ($basketItem->isCustomPrice())
960 continue;
961 $basketCode = $basketItem->getBasketCode();
962 if (!isset($basketRows[$basketCode]))
963 continue;
964
965 $fields = $basketRows[$basketCode];
966
967 if (isset($fields['PRICE']) && isset($fields['DISCOUNT_PRICE']))
968 {
969 $fields['PRICE'] = (float)$fields['PRICE'];
970 $fields['DISCOUNT_PRICE'] = (float)$fields['DISCOUNT_PRICE'];
971
972 if ($fields['PRICE'] >= 0
973 && $basketItem->getPrice() != $fields['PRICE'])
974 {
975 $fields['PRICE'] = PriceMaths::roundPrecision($fields['PRICE']);
976 $basketItem->setFieldNoDemand('PRICE', $fields['PRICE']);
977 }
978
979 if ($basketItem->getDiscountPrice() != $fields['DISCOUNT_PRICE'])
980 {
981 $fields['DISCOUNT_PRICE'] = PriceMaths::roundPrecision($fields['DISCOUNT_PRICE']);
982 $basketItem->setFieldNoDemand('DISCOUNT_PRICE', $fields['DISCOUNT_PRICE']);
983 }
984
985 if (isset($fields['DISCOUNT_VALUE']))
986 $basketItem->setFieldNoDemand('DISCOUNT_VALUE', $fields['DISCOUNT_VALUE']);
987 }
988 }
989 unset($fields, $basketCode, $basketItem);
990
991 return $result;
992 }
993
1000 public function createClone(\SplObjectStorage $cloneEntity = null)
1001 {
1002 if ($cloneEntity === null)
1003 {
1004 $cloneEntity = new \SplObjectStorage();
1005 }
1006
1008 $basketClone = parent::createClone($cloneEntity);
1009
1010 if ($this->order)
1011 {
1012 if ($cloneEntity->contains($this->order))
1013 {
1014 $basketClone->order = $cloneEntity[$this->order];
1015 }
1016 }
1017
1018 return $basketClone;
1019 }
1020
1033 public function copy()
1034 {
1035 if($this->order !== null)
1036 {
1037 throw new Main\SystemException('Could not clone basket which has order.');
1038 }
1039
1040 $basket = static::create($this->siteId);
1042 foreach($this as $originalItem)
1043 {
1044 $item = $basket->createItem($originalItem->getField("MODULE"), $originalItem->getProductId());
1045 $item->initFields($originalItem->getFields()->getValues());
1046 }
1047
1048 return $basket;
1049 }
1050
1062 public static function loadItemsForOrder(OrderBase $order)
1063 {
1064 $basket = static::createBasketObject();
1065 $basket->setOrder($order);
1066 $basket->setSiteId($order->getSiteId());
1067
1068 return $basket->loadFromDb(array("=ORDER_ID" => $order->getId()));
1069 }
1070
1083 public function addItem(Internals\CollectableEntity $basketItem)
1084 {
1086 $basketItem = parent::addItem($basketItem);
1087
1088 $this->basketItemIndexMap[$basketItem->getBasketCode()] = $basketItem->getInternalIndex();
1089
1090 $this->verifyItemSort($basketItem);
1091
1092 $basketItem->setCollection($this);
1093
1095 if ($order = $this->getOrder())
1096 {
1097 $order->onBasketModify(EventActions::ADD, $basketItem);
1098 }
1099 }
1100
1109 public function refreshData($select = array(), BasketItemBase $refreshItem = null)
1110 {
1111 if ($refreshItem !== null)
1112 {
1113 $strategy = RefreshFactory::createSingle($refreshItem->getBasketCode());
1114 }
1115 else
1116 {
1117 $strategy = RefreshFactory::create(RefreshFactory::TYPE_FULL);
1118 }
1119
1120 return $this->refresh($strategy);
1121 }
1122
1133 {
1134 $propertyList = [];
1135 $propertyCollection = $item->getPropertyCollection();
1136 if ($propertyCollection)
1137 {
1138 $propertyList = $propertyCollection->getPropertyValues();
1139 }
1140
1141 return $this->getExistsItem($item->getField('MODULE'), $item->getField('PRODUCT_ID'), $propertyList);
1142 }
1143}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static create($siteId)
refreshData($select=array(), BasketItemBase $refreshItem=null)
getExistsItemByItem(BasketItemBase $item)
loadFromDb(array $filter)
static getList(array $parameters=array())
deleteInternal(array $itemValues)
static loadItemsForOrder(OrderBase $order)
verifyItemSort(BasketItemBase $item)
callEventOnSaleBasketItemDeleted($itemValues)
static deleteNoDemand($idOrder)
setOrder(OrderBase $order)
getFUserId($skipCreate=false)
getExistsItem($moduleId, $productId, array $properties=array())
static getId($skipCreate=false)
Definition fuser.php:33
static getIdByUserId($userId)
Definition fuser.php:151
doFinalAction($hasMeaningfulField=false)
onItemModify(CollectableEntity $item, $name=null, $oldValue=null, $value=null)
static roundPrecision($value)
static getInstance($type)
Definition registry.php:183