Bitrix-D7 22.6
 
Загрузка...
Поиск...
Не найдено
shipment.php
1<?php
2
3namespace Bitrix\Sale;
4
11use \Bitrix\Sale\Delivery\Requests;
13
14Loc::loadMessages(__FILE__);
15
21{
24
26 protected $service = null;
27
28 protected $extraServices = null;
29
30 protected $storeId = null;
31
33 protected $internalId = 0;
34
35 protected static $idShipment = 0;
36
39
41 protected $isNew = true;
42
46 public static function getRegistryEntity()
47 {
49 }
50
54 public function getShipmentCode()
55 {
56 if ($this->internalId === 0)
57 {
58 if ($this->getId() > 0)
59 {
60 $this->internalId = $this->getId();
61 }
62 else
63 {
64 static::$idShipment++;
65 $this->internalId = static::$idShipment;
66 }
67 }
68 return $this->internalId;
69 }
70
74 public static function getAvailableFields()
75 {
76 return [
77 "STATUS_ID",
78 "BASE_PRICE_DELIVERY",
79 "PRICE_DELIVERY",
80 "ALLOW_DELIVERY",
81 "DATE_ALLOW_DELIVERY",
82 "EMP_ALLOW_DELIVERY_ID",
83 "DEDUCTED",
84 "DATE_DEDUCTED",
85 "EMP_DEDUCTED_ID",
86 "REASON_UNDO_DEDUCTED",
87 "DELIVERY_ID",
88 "DELIVERY_DOC_NUM",
89 "DELIVERY_DOC_DATE",
90 "TRACKING_NUMBER",
91 "XML_ID",
92 "PARAMS",
93 "DELIVERY_NAME",
94 "COMPANY_ID",
95 "MARKED",
96 "WEIGHT",
97 "DATE_MARKED",
98 "EMP_MARKED_ID",
99 "REASON_MARKED",
100 "CANCELED",
101 "DATE_CANCELED",
102 "EMP_CANCELED_ID",
103 "RESPONSIBLE_ID",
104 "DATE_RESPONSIBLE_ID",
105 "EMP_RESPONSIBLE_ID",
106 "COMMENTS",
107 "CURRENCY",
108 "CUSTOM_PRICE_DELIVERY",
109 "UPDATED_1C",
110 "EXTERNAL_DELIVERY",
111 "VERSION_1C","ID_1C",
112 "TRACKING_STATUS",
113 "TRACKING_LAST_CHECK",
114 "TRACKING_DESCRIPTION",
115 "ACCOUNT_NUMBER",
116 'DISCOUNT_PRICE'
117 ];
118 }
119
123 public static function getCustomizableFields() : array
124 {
125 return ['PRICE_DELIVERY' => 'PRICE_DELIVERY', 'WEIGHT' => 'WEIGHT'];
126 }
127
132 protected function onBeforeSetFields(array $values)
133 {
134 if (isset($values['DEDUCTED']))
135 {
136 if ($this->getField('DEDUCTED') === 'Y')
137 {
138 if ($values['DEDUCTED'] === 'N')
139 {
140 $values = ['DEDUCTED' => $values['DEDUCTED']] + $values;
141 }
142 }
143 else
144 {
145 if ($values['DEDUCTED'] === 'Y')
146 {
147 // move to the end of array
148 unset($values['DEDUCTED']);
149 $values['DEDUCTED'] = 'Y';
150 }
151 }
152 }
153
154 return $values;
155 }
156
160 protected static function getMeaningfulFields()
161 {
162 return array('BASE_PRICE_DELIVERY', 'DELIVERY_ID');
163 }
164
171 public function setDeliveryService(Delivery\Services\Base $service)
172 {
173 $this->service = $service;
174
175 $result = $this->setField("DELIVERY_ID", $service->getId());
176 if ($result->isSuccess())
177 {
178 $this->setField("DELIVERY_NAME", $service->getName());
179 }
180 }
181
189 public static function create(ShipmentCollection $collection, Delivery\Services\Base $service = null)
190 {
191 $emptyService = Delivery\Services\Manager::getById(Delivery\Services\EmptyDeliveryService::getEmptyDeliveryServiceId());
192 $fields = [
193 'DATE_INSERT' => new Main\Type\DateTime(),
194 'DELIVERY_ID' => $emptyService['ID'],
195 'DELIVERY_NAME' => $emptyService['NAME'],
196 'ALLOW_DELIVERY' => 'N',
197 'DEDUCTED' => 'N',
198 'CUSTOM_PRICE_DELIVERY' => 'N',
199 'MARKED' => 'N',
200 'CANCELED' => 'N',
201 'SYSTEM' => 'N',
202 'XML_ID' => static::generateXmlId(),
203 'RESERVED' => 'N'
204 ];
205
206 $registry = Registry::getInstance(static::getRegistryType());
207
209 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
210 $fields['STATUS_ID'] = $deliveryStatusClassName::getInitialStatus();
211
212 $shipment = static::createShipmentObject();
213 $shipment->setFieldsNoDemand($fields);
214 $shipment->setCollection($collection);
215
216 if ($service !== null)
217 {
218 $shipment->setDeliveryService($service);
219 }
220
221 return $shipment;
222 }
223
227 protected static function generateXmlId()
228 {
229 return uniqid('bx_');
230 }
231
237 private static function createShipmentObject(array $fields = array())
238 {
239 $registry = Registry::getInstance(static::getRegistryType());
240 $shipmentClassName = $registry->getShipmentClassName();
241
242 return new $shipmentClassName($fields);
243 }
244
248 public static function getRegistryType()
249 {
251 }
252
260 public function needReservation()
261 {
263
264 if ($condition === ReserveCondition::ON_CREATE)
265 {
266 return true;
267 }
268
269 if ($condition === ReserveCondition::ON_PAY
270 || $condition === ReserveCondition::ON_FULL_PAY)
271 {
272 $order = $this->getOrder();
273 if ($condition === ReserveCondition::ON_FULL_PAY)
274 {
275 return $order->isPaid();
276 }
277
278 return $order->getPaymentCollection()->hasPaidPayment();
279 }
280
281 if ($this->isSystem())
282 {
283 return false;
284 }
285
286 return
287 (
288 $condition === ReserveCondition::ON_ALLOW_DELIVERY
289 && $this->isAllowDelivery()
290 )
291 || (
292 $condition === ReserveCondition::ON_SHIP
293 && $this->isShipped()
294 )
295 ;
296 }
297
308 private function transferItem2SystemShipment(ShipmentItem $sourceItem, $quantity)
309 {
310 $sourceItemCollection = $sourceItem->getCollection();
311 if ($this !== $sourceItemCollection->getShipment())
312 {
313 throw new Main\ArgumentException("item");
314 }
315
316 $quantity = floatval($quantity);
317
319 $systemShipment = $this->getCollection()->getSystemShipment();
320
322 $basketItem = $sourceItem->getBasketItem();
323
325 $order = $basketItem->getCollection()->getOrder();
326
327 $shipmentItemCode = $sourceItem->getBasketCode();
328
329 if ($quantity === 0)
330 {
331 return new Result();
332 }
333
335 $systemShipmentItemCollection = $systemShipment->getShipmentItemCollection();
336
337 $systemShipmentItem = $systemShipmentItemCollection->getItemByBasketCode($shipmentItemCode);
338 if (is_null($systemShipmentItem))
339 {
340 $systemShipmentItem = $systemShipmentItemCollection->createItem($basketItem);
341 }
342
343 $newSystemShipmentItemQuantity = $systemShipmentItem->getQuantity() + $quantity;
344 if ($newSystemShipmentItemQuantity < 0)
345 {
346 $result = new Result();
347 $result->addError(
348 new ResultError(
349 str_replace(
350 ["#NAME#", "#QUANTITY#"],
351 [$sourceItem->getBasketItem()->getField("NAME"), abs($quantity)],
352 Loc::getMessage('SALE_SHIPMENT_QUANTITY_MISMATCH')
353 ),
354 'SALE_SHIPMENT_QUANTITY_MISMATCH'
355 )
356 );
357 return $result;
358 }
359
360 $systemShipmentItem->setFieldNoDemand('QUANTITY', $newSystemShipmentItemQuantity);
361 if ($newSystemShipmentItemQuantity <= 1e-10)
362 {
363 $systemShipmentItem->delete();
364 }
365
366 $affectedQuantity = 0;
367
368 if ($quantity > 0) // transfer to system shipment
369 {
370 if ($sourceItem->getReservedQuantity() > 0)
371 {
372 $affectedQuantity = $quantity;
373 $originalQuantity = $sourceItem->getQuantity() + $quantity;
374 if ($sourceItem->getReservedQuantity() < $originalQuantity)
375 {
376 $affectedQuantity -= $originalQuantity - $sourceItem->getReservedQuantity();
377 }
378 }
379 }
380 elseif ($quantity < 0) // transfer from system shipment
381 {
382 if ($systemShipmentItem->getReservedQuantity() > 0)
383 {
384 $affectedQuantity = $quantity;
385 if ($systemShipmentItem->getReservedQuantity() < -$affectedQuantity)
386 {
387 $affectedQuantity = -1 * $systemShipmentItem->getReservedQuantity();
388 }
389 }
390 }
391
392 if ($affectedQuantity != 0) // if there are reserved items among transferred
393 {
394 $sourceItem->getFields()->set(
395 'RESERVED_QUANTITY',
396 $sourceItem->getField('RESERVED_QUANTITY') - $affectedQuantity
397 );
398
399 $systemShipmentItem->getFields()->set(
400 'RESERVED_QUANTITY',
401 $systemShipmentItem->getField('RESERVED_QUANTITY') + $affectedQuantity
402 );
403
404 $systemShipment->setFieldNoDemand(
405 'RESERVED',
406 ($systemShipmentItem->getField("RESERVED_QUANTITY") > 0) ? "Y" : "N"
407 );
408
409 $shipmentItemForPool = $sourceItem;
410 $sourceShipmentItemForPool = $systemShipmentItem;
411
412 if ($quantity > 0)
413 {
414 $shipmentItemForPool = $systemShipmentItem;
415 $sourceShipmentItemForPool = $sourceItem;
416 }
417
418 $productId = $basketItem->getProductId();
419
420 $foundItem = false;
421 $poolItems = Internals\ItemsPool::get($order->getInternalId(), $productId);
422 if (!empty($poolItems))
423 {
424 foreach ($poolItems as $poolIndex => $poolItem)
425 {
426 if ($poolItem->getInternalIndex() === $shipmentItemForPool->getInternalIndex())
427 {
428 $foundItem = true;
429 }
430
431 if (
432 $sourceShipmentItemForPool
433 && $poolItem instanceof ShipmentItem
434 && $poolItem->getInternalIndex() === $sourceShipmentItemForPool->getInternalIndex()
435 )
436 {
437 $reserveQuantity = $sourceShipmentItemForPool->getReservedQuantity();
438 if (abs($reserveQuantity) <= 1e-6)
439 {
440 Internals\ItemsPool::delete($order->getInternalId(), $productId, $poolIndex);
441 }
442 }
443 }
444 }
445
446 if (!$foundItem)
447 {
448 Internals\ItemsPool::add($order->getInternalId(), $productId, $shipmentItemForPool);
449 }
450 }
451
452 $tryReserveResult = null;
453
454 if ($quantity > 0)
455 {
457 {
458 if ($systemShipment->needReservation())
459 {
460 $tryReserveResult = Internals\Catalog\Provider::tryReserveShipmentItem($systemShipmentItem);
461 }
462 else
463 {
464 $tryReserveResult = Internals\Catalog\Provider::tryUnreserveShipmentItem($systemShipmentItem);
465 }
466 }
467 }
468 elseif ($quantity < 0) // transfer from system shipment
469 {
470 if (
472 && $sourceItemCollection->getShipment()->needReservation()
473 )
474 {
475 $tryReserveResult = Internals\Catalog\Provider::tryReserveShipmentItem($sourceItem);
476 }
477 }
478
479 $canReserve = false;
480
481 if ($tryReserveResult === null)
482 {
483 $canReserve = true;
484 }
485
486 if ($tryReserveResult !== null && ($tryReserveResult->isSuccess() && ($tryReserveResultData = $tryReserveResult->getData())))
487 {
488 if (array_key_exists('CAN_RESERVE', $tryReserveResultData))
489 {
490 $canReserve = $tryReserveResultData['CAN_RESERVE'];
491 }
492 }
493
494 if (
496 && $systemShipment->needReservation()
497 && $canReserve
498 )
499 {
500 $order = $this->getOrder();
501 if ($order &&
502 !Internals\ActionEntity::isTypeExists(
503 $order->getInternalId(),
505 )
506 )
507 {
508 Internals\ActionEntity::add(
509 $order->getInternalId(),
511 [
512 'METHOD' => 'Bitrix\Sale\ShipmentCollection::updateReservedFlag',
513 'PARAMS' => [$systemShipment->getCollection()]
514 ]
515 );
516 }
517 }
518
519
520 return new Result();
521 }
522
529 public static function updateReservedFlag(Shipment $shipment)
530 {
531 $shipmentReserved = true;
532
533 $shipmentItemList = $shipment->getShipmentItemCollection()->getShippableItems();
534
535 if ($shipmentItemList->count() === 0)
536 {
537 $shipmentReserved = false;
538 }
539
541 foreach ($shipmentItemList as $shipmentItem)
542 {
543 if ($shipmentItem->getQuantity() - $shipmentItem->getReservedQuantity())
544 {
545 $shipmentReserved = false;
546 break;
547 }
548 }
549
550 $shipmentReservedValue = $shipmentReserved ? "Y" : "N";
551 $currentValue = $shipment->getField('RESERVED');
552 if ($shipment->getField('RESERVED') != $shipmentReservedValue)
553 {
554 $eventManager = Main\EventManager::getInstance();
555 $eventsList = $eventManager->findEventHandlers('sale', EventActions::EVENT_ON_BEFORE_SHIPMENT_RESERVE);
556 if (!empty($eventsList))
557 {
559 $event = new Main\Event('sale', EventActions::EVENT_ON_BEFORE_SHIPMENT_RESERVE, [
560 'ENTITY' => $shipment,
561 'VALUE' => $shipmentReservedValue,
562 ]);
563
564 $event->send();
565
566 if ($event->getResults())
567 {
568 $result = new Result();
570 foreach($event->getResults() as $eventResult)
571 {
572 if($eventResult->getType() === Main\EventResult::ERROR)
573 {
574 $errorMsg = new ResultError(Main\Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_SHIPMENT_RESERVE_ERROR'), 'SALE_EVENT_ON_BEFORE_SHIPMENT_RESERVE_ERROR');
575
576 $eventResultData = $eventResult->getParameters();
577 if ($eventResultData)
578 {
579 if (isset($eventResultData) && $eventResultData instanceof ResultError)
580 {
582 $errorMsg = $eventResultData;
583 }
584 }
585
586 $result->addError($errorMsg);
587
588 }
589 }
590
591 if (!$result->isSuccess())
592 {
593 return $result;
594 }
595 }
596 }
597
598 $shipment->setFieldNoDemand('RESERVED', $shipmentReserved ? "Y" : "N");
599
600 Internals\EventsPool::addEvent('s'.$shipment->getInternalIndex(), EventActions::EVENT_ON_SHIPMENT_RESERVED, [
601 'ENTITY' => $shipment,
602 'VALUE' => $shipmentReservedValue,
603 'OLD_VALUE' => $currentValue,
604 ]);
605 }
606
607 return new Result();
608 }
609
620 public function onShipmentItemCollectionModify($action, ShipmentItem $shipmentItem, $name = null, $oldValue = null, $value = null)
621 {
622 if ($action != EventActions::UPDATE)
623 {
624 return new Result();
625 }
626
627 if ($this->isSystem()
628 && $name != 'RESERVED_QUANTITY'
629 )
630 {
631 throw new Main\NotSupportedException(Loc::getMessage('SALE_SHIPMENT_SYSTEM_SHIPMENT_CHANGE'));
632 }
633
634 if ($name === "QUANTITY")
635 {
636 $result = $this->transferItem2SystemShipment($shipmentItem, $oldValue - $value);
637
638 if (!$this->isMarkedFieldCustom('WEIGHT'))
639 {
640 $this->setField(
641 'WEIGHT',
642 $this->getShipmentItemCollection()->getWeight()
643 );
644 }
645
646 return $result;
647 }
648 elseif ($name === 'RESERVED_QUANTITY')
649 {
650 $order = $this->getParentOrder();
651 if ($order &&
652 !Internals\ActionEntity::isTypeExists(
653 $order->getInternalId(),
655 )
656 )
657 {
658 Internals\ActionEntity::add(
659 $order->getInternalId(),
661 [
662 'METHOD' => 'Bitrix\Sale\ShipmentCollection::updateReservedFlag',
663 'PARAMS' => [$this->getCollection()]
664 ]
665 );
666 }
667 }
668
669 return new Result();
670 }
671
681 public static function deleteNoDemand($orderId)
682 {
683 $result = new Result();
684
685 $shipmentDataList = static::getList(
686 [
687 "filter" => ["=ORDER_ID" => $orderId],
688 "select" => ["ID"]
689 ]
690 );
691
692 while ($shipment = $shipmentDataList->fetch())
693 {
694 $res = static::deleteInternal($shipment['ID']);
695
696 if ($res -> isSuccess())
697 {
698 Internals\ShipmentExtraServiceTable::deleteByShipmentId($shipment['ID']);
699 }
700 else
701 {
702 $result->addErrors($res->getErrors());
703 }
704 }
705
706 return $result;
707 }
708
720 public function delete()
721 {
722 if ($this->isShipped())
723 {
724 $result = new Result();
725 return $result->addError(
726 new ResultError(
727 Loc::getMessage('SALE_SHIPMENT_EXIST_SHIPPED'),
728 'SALE_SHIPMENT_EXIST_SHIPPED'
729 )
730 );
731 }
732
733 if (!$this->isSystem())
734 {
735 $this->setField('BASE_PRICE_DELIVERY', 0);
736
737 if ($this->getFields()->isMarkedCustom('PRICE_DELIVERY'))
738 {
739 $this->setField('PRICE_DELIVERY', 0);
740 }
741
742 $this->disallowDelivery();
743 }
744
745 $this->getPropertyCollection()->deleteNoDemand($this->getId());
746 $this->deleteDeliveryRequest();
747
748 $this->getShipmentItemCollection()->clearCollection();
749
750 return parent::delete();
751 }
752
756 protected function deleteDeliveryRequest()
757 {
758 Requests\Manager::onBeforeShipmentDelete($this);
759 }
760
761 protected function normalizeValue($name, $value)
762 {
763 if ($this->isPriceField($name))
764 {
765 $value = PriceMaths::roundPrecision($value);
766 }
767 elseif ($name === 'REASON_MARKED')
768 {
769 $value = (string)$value;
770 if (mb_strlen($value) > 255)
771 {
772 $value = mb_substr($value, 0, 255);
773 }
774 }
775
776 return parent::normalizeValue($name, $value);
777 }
778
789 public function setField($name, $value)
790 {
791 if ($this->isSystem())
792 {
793 throw new Main\NotSupportedException();
794 }
795
796 if ($name === 'CUSTOM_PRICE_DELIVERY')
797 {
798 if ($value === 'Y')
799 {
800 $this->markFieldCustom('PRICE_DELIVERY');
801 }
802 else
803 {
804 $this->unmarkFieldCustom('PRICE_DELIVERY');
805 }
806 }
807
808 return parent::setField($name, $value);
809 }
810
818 protected function checkValueBeforeSet($name, $value)
819 {
820 $result = parent::checkValueBeforeSet($name, $value);
821
822 if ($name === "DELIVERY_ID")
823 {
824 if (intval($value) > 0 && !Delivery\Services\Manager::isServiceExist($value))
825 {
826 $result->addError(
827 new ResultError(
828 Loc::getMessage('SALE_SHIPMENT_WRONG_DELIVERY_SERVICE'),
829 'SALE_SHIPMENT_WRONG_DELIVERY_SERVICE'
830 )
831 );
832 }
833 }
834 elseif ($name === 'ACCOUNT_NUMBER')
835 {
836 $dbRes = static::getList([
837 'select' => ['ID'],
838 'filter' => ['=ACCOUNT_NUMBER' => $value]
839 ]);
840
841 if ($dbRes->fetch())
842 {
843 $result->addError(
844 new ResultError(
845 Loc::getMessage('SALE_SHIPMENT_ACCOUNT_NUMBER_EXISTS')
846 )
847 );
848 }
849 }
850
851 return $result;
852 }
853
863 public function setFieldNoDemand($name, $value)
864 {
865 if ($name === 'CUSTOM_PRICE_DELIVERY')
866 {
867 if ($value === 'Y')
868 {
869 $this->markFieldCustom('PRICE_DELIVERY');
870 }
871 else
872 {
873 $this->unmarkFieldCustom('PRICE_DELIVERY');
874 }
875 }
876
877 parent::setFieldNoDemand($name, $value);
878 }
879
886 public static function loadForOrder($id)
887 {
888 if (intval($id) <= 0)
889 {
890 throw new Main\ArgumentNullException("id");
891 }
892
893 $shipments = [];
894
895 $shipmentDataList = static::getList(static::getParametersForLoad($id));
896 while ($shipmentData = $shipmentDataList->fetch())
897 {
898 $shipments[] = static::createShipmentObject($shipmentData);
899 }
900
901
902 return $shipments;
903 }
904
905 protected static function getParametersForLoad($id) : array
906 {
907 return [
908 'filter' => [
909 'ORDER_ID' => $id
910 ],
911 'order' => [
912 'SYSTEM' => 'ASC',
913 'DATE_INSERT' => 'ASC',
914 'ID' => 'ASC'
915 ]
916 ];
917 }
918
929 public function save()
930 {
931 $this->checkCallingContext();
932
933 $result = new Result();
934
935 $id = $this->getId();
936 $this->isNew = ($this->getId() === 0);
937
938 $this->callEventOnBeforeEntitySaved();
939
940 if ($id > 0)
941 {
942 $r = $this->update();
943 }
944 else
945 {
946 $r = $this->add();
947
948 if ($r->getId() > 0)
949 {
950 $id = $r->getId();
951 }
952 }
953
954 if (!$r->isSuccess())
955 {
956 $result->addErrors($r->getErrors());
957 return $result;
958 }
959
960 if ($id > 0)
961 {
962 $result->setId($id);
963
964 $controller = Internals\CustomFieldsController::getInstance();
965 $controller->save($this);
966 }
967
968 if (!$this->isSystem())
969 {
970 $this->saveExtraServices();
971 $this->saveStoreId();
972 }
973
974 $this->callEventOnEntitySaved();
975
976 $this->callDelayedEvents();
977
978 $shipmentItemCollection = $this->getShipmentItemCollection();
979 $r = $shipmentItemCollection->save();
980 if (!$r->isSuccess())
981 {
982 $result->addErrors($r->getErrors());
983 return $result;
984 }
985
986 if (!$this->isSystem())
987 {
988 $registry = Registry::getInstance(static::getRegistryType());
989
991 $orderHistory = $registry->getOrderHistoryClassName();
992 $orderHistory::collectEntityFields('SHIPMENT', $this->getParentOrderId(), $id);
993 }
994
997
999 $res = $propertyCollection->save();
1000 if (!$res->isSuccess())
1001 {
1002 $result->addWarnings($res->getErrors());
1003 }
1004
1005 $this->onAfterSave($this->isNew);
1006
1007 $this->isNew = false;
1008
1009 return $result;
1010 }
1011
1015 private function checkCallingContext()
1016 {
1017 $order = $this->getOrder();
1018
1019 if (!$order->isSaveRunning())
1020 {
1021 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1022 }
1023 }
1024
1033 private function add()
1034 {
1035 $result = new Result();
1036
1037 $registry = Registry::getInstance(static::getRegistryType());
1038
1039 $this->setFieldNoDemand('ORDER_ID', $this->getParentOrderId());
1040
1041 $r = static::addInternal($this->getFields()->getValues());
1042 if (!$r->isSuccess())
1043 {
1045 $orderHistory = $registry->getOrderHistoryClassName();
1046
1047 $orderHistory::addAction(
1048 'SHIPMENT',
1049 $this->getParentOrderId(),
1050 'SHIPMENT_ADD_ERROR',
1051 null,
1052 $this,
1053 ["ERROR" => $r->getErrorMessages()]
1054 );
1055
1056 $result->addErrors($r->getErrors());
1057 return $result;
1058 }
1059
1060 $id = $r->getId();
1061 $this->setFieldNoDemand('ID', $id);
1062 $result->setId($id);
1063
1064 $this->setAccountNumber($id);
1065
1066 if (!$this->isSystem())
1067 {
1069 $orderHistory = $registry->getOrderHistoryClassName();
1070
1071 $orderHistory::addAction(
1072 'SHIPMENT',
1073 $this->getParentOrderId(),
1074 'SHIPMENT_ADDED',
1075 $id,
1076 $this
1077 );
1078 }
1079
1080 return $result;
1081 }
1082
1087 private function update()
1088 {
1089 $result = new Result();
1090
1091 $registry = Registry::getInstance(static::getRegistryType());
1092
1093 $this->setDeliveryRequestMarker();
1094
1095 $r = static::updateInternal($this->getId(), $this->getFields()->getChangedValues());
1096 if (!$r->isSuccess())
1097 {
1099 $orderHistory = $registry->getOrderHistoryClassName();
1100
1101 $orderHistory::addAction(
1102 'SHIPMENT',
1103 $this->getParentOrderId(),
1104 'SHIPMENT_UPDATE_ERROR',
1105 $this->getId(),
1106 $this,
1107 ["ERROR" => $r->getErrorMessages()]
1108 );
1109
1110 $result->addErrors($r->getErrors());
1111 }
1112
1113 return $result;
1114 }
1115
1119 protected function setDeliveryRequestMarker()
1120 {
1121 $order = $this->getParentOrder();
1122
1123 Requests\Manager::onBeforeShipmentSave($order, $this);
1124 }
1125
1131 private function callDelayedEvents()
1132 {
1133 $eventList = Internals\EventsPool::getEvents('s'.$this->getInternalIndex());
1134
1135 if ($eventList)
1136 {
1137 foreach ($eventList as $eventName => $eventData)
1138 {
1139 $event = new Main\Event('sale', $eventName, $eventData);
1140 $event->send();
1141
1142 $registry = Registry::getInstance(static::getRegistryType());
1143
1145 $notifyClassName = $registry->getNotifyClassName();
1146 $notifyClassName::callNotify($this, $eventName);
1147 }
1148
1149 Internals\EventsPool::resetEvents('s'.$this->getInternalIndex());
1150 }
1151 }
1152
1156 private function callEventOnBeforeEntitySaved()
1157 {
1159 $event = new Main\Event('sale', 'OnBeforeSaleShipmentEntitySaved', [
1160 'ENTITY' => $this,
1161 'VALUES' => $this->fields->getOriginalValues()
1162 ]);
1163
1164 $event->send();
1165 }
1166
1170 private function callEventOnEntitySaved()
1171 {
1173 $event = new Main\Event('sale', 'OnSaleShipmentEntitySaved', [
1174 'ENTITY' => $this,
1175 'VALUES' => $this->fields->getOriginalValues(),
1176 'IS_NEW' => $this->isNew,
1177 ]);
1178
1179 $event->send();
1180 }
1181
1186 protected function onAfterSave($isNew)
1187 {
1188 return;
1189 }
1190
1194 public function getParentOrderId()
1195 {
1196 $order = $this->getParentOrder();
1197 if (!$order)
1198 {
1199 return false;
1200 }
1201
1202 return $order->getId();
1203 }
1204
1208 public function getOrder()
1209 {
1210 return $this->getCollection()->getOrder();
1211 }
1212
1219 public function getShipmentItemCollection()
1220 {
1221 if (empty($this->shipmentItemCollection))
1222 {
1223 $registry = Registry::getInstance(static::getRegistryType());
1224
1226 $itemCollectionClassName = $registry->getShipmentItemCollectionClassName();
1227 $this->shipmentItemCollection = $itemCollectionClassName::load($this);
1228 }
1229
1231 }
1232
1237 protected function markSystem()
1238 {
1239 $this->setFieldNoDemand("SYSTEM", 'Y');
1240 }
1241
1251 public static function createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService = null)
1252 {
1253 $shipment = static::create($collection, $deliveryService);
1254 $shipment->markSystem();
1255
1256 if ($deliveryService === null)
1257 {
1258 $shipment->setFieldNoDemand('DELIVERY_ID', Delivery\Services\Manager::getEmptyDeliveryServiceId());
1259 }
1260
1261 return $shipment;
1262 }
1263
1267 public function getPrice()
1268 {
1269 return (float)$this->getField('PRICE_DELIVERY');
1270 }
1271
1276 public function isCustomPrice()
1277 {
1278 return $this->isMarkedFieldCustom('PRICE_DELIVERY');
1279 }
1280
1281 protected function isPriceField(string $name) : bool
1282 {
1283 return
1284 $name === 'BASE_PRICE_DELIVERY'
1285 || $name === 'PRICE_DELIVERY'
1286 || $name === 'DISCOUNT_PRICE'
1287 ;
1288 }
1289
1293 public function getCurrency()
1294 {
1295 return (string)$this->getField('CURRENCY');
1296 }
1297
1301 public function getDeliveryId()
1302 {
1303 return (int)$this->getField('DELIVERY_ID');
1304 }
1305
1309 public function getDeliveryName()
1310 {
1311 return (string)$this->getField('DELIVERY_NAME');
1312 }
1313
1317 public function setOrderId($orderId)
1318 {
1319 $this->setField('ORDER_ID', $orderId);
1320 }
1321
1327 public function getDelivery()
1328 {
1329 if ($this->service === null)
1330 {
1331 $this->service = $this->loadDeliveryService();
1332 }
1333
1334 return $this->service;
1335 }
1336
1342 protected function loadDeliveryService()
1343 {
1344 if ($deliveryId = $this->getDeliveryId())
1345 {
1346 return Delivery\Services\Manager::getObjectById($deliveryId);
1347 }
1348
1349 return null;
1350 }
1351
1352
1356 public function isSystem()
1357 {
1358 return $this->getField('SYSTEM') === 'Y';
1359 }
1360
1362 public function isCanceled()
1363 {
1364 return $this->getField('CANCELED') === 'Y';
1365 }
1366
1370 public function isShipped()
1371 {
1372 return $this->getField('DEDUCTED') === 'Y';
1373 }
1374
1378 public function getShippedDate()
1379 {
1380 return $this->getField('DATE_DEDUCTED');
1381 }
1382
1386 public function getShippedUserId()
1387 {
1388 return $this->getField('EMP_DEDUCTED_ID');
1389 }
1390
1394 public function getUnshipReason()
1395 {
1396 return (string)$this->getField('REASON_UNDO_DEDUCTED');
1397 }
1398
1402 public function isMarked()
1403 {
1404 return $this->getField('MARKED') === "Y";
1405 }
1406
1413 public function isReserved()
1414 {
1416 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1417 {
1418 if ($shipmentItem->getReservedQuantity() !== $shipmentItem->getQuantity())
1419 {
1420 return false;
1421 }
1422 }
1423
1424 return true;
1425 }
1426
1430 public function isAllowDelivery()
1431 {
1432 return $this->getField('ALLOW_DELIVERY') === "Y";
1433 }
1434
1438 public function isEmpty()
1439 {
1440 return $this->getShipmentItemCollection()->isEmpty();
1441 }
1442
1446 public function getAllowDeliveryDate()
1447 {
1448 return $this->getField('DATE_ALLOW_DELIVERY');
1449 }
1450
1454 public function getAllowDeliveryUserId()
1455 {
1456 return (int)$this->getField('EMP_ALLOW_DELIVERY_ID');
1457 }
1458
1462 public function getCompanyId()
1463 {
1464 return (int)$this->getField('COMPANY_ID');
1465 }
1466
1473 public function tryReserve()
1474 {
1475 return Internals\Catalog\Provider::tryReserveShipment($this);
1476 }
1477
1483 public function tryUnreserve()
1484 {
1485 return Internals\Catalog\Provider::tryUnreserveShipment($this);
1486 }
1487
1493 public function tryShip()
1494 {
1495 $result = new Result();
1496
1498 $r = Internals\Catalog\Provider::tryShipShipment($this);
1499 if ($r->isSuccess())
1500 {
1501 $resultList = $r->getData();
1502
1503 if (!empty($resultList) && is_array($resultList))
1504 {
1506 foreach ($resultList as $resultDat)
1507 {
1508 if (!$resultDat->isSuccess())
1509 {
1510 $result->addErrors( $resultDat->getErrors() );
1511 }
1512 }
1513 }
1514 }
1515 else
1516 {
1517 $result->addErrors( $r->getErrors() );
1518 }
1519
1520 if ($r->hasWarnings())
1521 {
1522 $result->addWarnings( $r->getWarnings() );
1523 }
1524 return $result;
1525 }
1526
1532 public function tryUnship()
1533 {
1534 return $this->tryShip();
1535 }
1536
1540 public function needShip()
1541 {
1542 if ($this->fields->isChanged('DEDUCTED'))
1543 {
1544 if ($this->getField('DEDUCTED') === "Y")
1545 {
1546 return true;
1547 }
1548 elseif ($this->getField('DEDUCTED') === "N" && $this->getId() != 0)
1549 {
1550 return false;
1551 }
1552 }
1553
1554 return null;
1555 }
1556
1561 public function deliver()
1562 {
1563 $result = Internals\Catalog\Provider::deliver($this);
1564 if ($result->isSuccess())
1565 {
1566 Recurring::repeat($this->getOrder(), $result->getData());
1567 }
1568
1569 return $result;
1570 }
1571
1577 public function allowDelivery()
1578 {
1579 return $this->setField('ALLOW_DELIVERY', "Y");
1580 }
1581
1587 public function disallowDelivery()
1588 {
1589 return $this->setField('ALLOW_DELIVERY', "N");
1590 }
1591
1602 public function onBeforeBasketItemDelete(BasketItem $basketItem)
1603 {
1604 $result = new Result();
1605
1606 $shipmentItemCollection = $this->getShipmentItemCollection();
1607 $r = $shipmentItemCollection->onBeforeBasketItemDelete($basketItem);
1608 if (!$r->isSuccess())
1609 {
1610 return $result->addErrors($r->getErrors());
1611 }
1612
1613 if ($this->isSystem())
1614 {
1615 return $this->syncQuantityAfterModify($basketItem);
1616 }
1617
1618 return $result;
1619 }
1620
1635 public function onBasketModify($action, BasketItem $basketItem, $name = null, $oldValue = null, $value = null)
1636 {
1637 $result = new Result();
1638
1639 if ($action === EventActions::ADD)
1640 {
1641 if (!$this->isSystem())
1642 {
1643 return $result;
1644 }
1645
1646 return $this->getShipmentItemCollection()->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1647 }
1648 elseif ($action === EventActions::UPDATE)
1649 {
1650 if ($name === "QUANTITY")
1651 {
1652 if ($this->isSystem())
1653 {
1654 return $this->syncQuantityAfterModify($basketItem, $value, $oldValue);
1655 }
1656
1658 $shipmentItemCollection = $this->getShipmentItemCollection();
1659
1660 $r = $shipmentItemCollection->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1661 if ($r->isSuccess())
1662 {
1663 if (!$this->isCustomPrice())
1664 {
1665 $deliveryCalculate = $this->calculateDelivery();
1666 if ($deliveryCalculate->isSuccess())
1667 {
1668 $this->setField('BASE_PRICE_DELIVERY', $deliveryCalculate->getPrice());
1669 }
1670 else
1671 {
1672 $result->addWarnings($deliveryCalculate->getErrors());
1673 }
1674 }
1675 }
1676 else
1677 {
1678 $result->addErrors($r->getErrors());
1679 }
1680 }
1681 elseif ($name === 'WEIGHT')
1682 {
1683 if (!$this->isMarkedFieldCustom('WEIGHT'))
1684 {
1685 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1686 {
1687 $this->setField('WEIGHT', $this->getShipmentItemCollection()->getWeight());
1688 }
1689 }
1690 }
1691 elseif ($name === 'PRICE')
1692 {
1693 if (!$this->isCustomPrice())
1694 {
1695 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1696 {
1697 $r = $this->calculateDelivery();
1698 if ($r->isSuccess())
1699 {
1700 $this->setField('BASE_PRICE_DELIVERY', $r->getPrice());
1701 }
1702 else
1703 {
1704 $result->addErrors($r->getErrors());
1705 }
1706 }
1707 }
1708 }
1709 }
1710
1711 return $result;
1712 }
1713
1726 protected function onFieldModify($name, $oldValue, $value)
1727 {
1728 global $USER;
1729
1730 $result = new Result();
1731
1732 if ($name === 'DELIVERY_ID')
1733 {
1734 if (
1735 $value > 0
1736 && (
1737 $this->service === null
1738 || $this->service->getId() !== (int)$value
1739 )
1740 )
1741 {
1742 $service = Delivery\Services\Manager::getObjectById($value);
1743 if ($service)
1744 {
1745 $this->service = $service;
1746
1747 $this->setField('DELIVERY_NAME', $this->service->getName());
1748 }
1749 }
1750
1751 $this->getPropertyCollection()->refreshRelated();
1752 }
1753 elseif ($name === "MARKED")
1754 {
1755 if ($oldValue != "Y")
1756 {
1757 $this->setField('DATE_MARKED', new Main\Type\DateTime());
1758
1759 if (is_object($USER))
1760 {
1761 $this->setField('EMP_MARKED_ID', $USER->GetID());
1762 }
1763 }
1764 elseif ($value === "N")
1765 {
1766 $this->setField('REASON_MARKED', '');
1767 }
1768 }
1769 elseif ($name === "ALLOW_DELIVERY")
1770 {
1771 $this->setField('DATE_ALLOW_DELIVERY', new Main\Type\DateTime());
1772
1773 if (is_object($USER))
1774 {
1775 $this->setField('EMP_ALLOW_DELIVERY_ID', $USER->GetID());
1776 }
1777
1778 if ($oldValue === 'N')
1779 {
1780 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_allow_delivery', '');
1781
1782 $registry = Registry::getInstance(static::getRegistryType());
1784 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1785
1786 if (
1787 $shipmentStatus !== ''
1788 && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus()
1789 )
1790 {
1791 $r = $this->setStatus($shipmentStatus);
1792 if (!$r->isSuccess())
1793 {
1794 $result->addErrors($r->getErrors());
1795 }
1796 }
1797 }
1798
1799 Internals\EventsPool::addEvent(
1800 's'.$this->getInternalIndex(),
1802 [
1803 'ENTITY' => $this,
1804 'VALUES' => $this->fields->getOriginalValues()
1805 ]
1806 );
1807 }
1808 elseif ($name === "DEDUCTED")
1809 {
1810 $this->setField('DATE_DEDUCTED', new Main\Type\DateTime());
1811
1812 if (is_object($USER))
1813 {
1814 $this->setField('EMP_DEDUCTED_ID', $USER->GetID());
1815 }
1816
1817 if ($oldValue === 'N')
1818 {
1819 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_shipped', '');
1820
1821 $registry = Registry::getInstance(static::getRegistryType());
1823 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1824
1825 if (strval($shipmentStatus) != '' && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus())
1826 {
1827 $r = $this->setStatus($shipmentStatus);
1828 if (!$r->isSuccess())
1829 {
1830 $result->addErrors($r->getErrors());
1831 }
1832 }
1833 }
1834
1835 if ($value === 'Y')
1836 {
1838 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1839 {
1840 $r = $shipmentItem->checkMarkingCodeOnDeducted();
1841 if (!$r->isSuccess())
1842 {
1843 $result->addErrors($r->getErrors());
1844 }
1845 }
1846 }
1847
1848 Internals\EventsPool::addEvent(
1849 's'.$this->getInternalIndex(),
1851 [
1852 'ENTITY' => $this,
1853 'VALUES' => $this->fields->getOriginalValues()
1854 ]
1855 );
1856
1857 Cashbox\Internals\Pool::addDoc($this->getOrder()->getInternalId(), $this);
1858 }
1859 elseif ($name === "STATUS_ID")
1860 {
1861 $event = new Main\Event(
1862 'sale',
1864 [
1865 'ENTITY' => $this,
1866 'VALUE' => $value,
1867 'OLD_VALUE' => $oldValue,
1868 ]
1869 );
1870 $event->send();
1871
1872 Internals\EventsPool::addEvent(
1873 's'.$this->getInternalIndex(),
1875 [
1876 'ENTITY' => $this,
1877 'VALUE' => $value,
1878 'OLD_VALUE' => $oldValue,
1879 ]
1880 );
1881
1882 Internals\EventsPool::addEvent(
1883 's'.$this->getInternalIndex(),
1885 [
1886 'ENTITY' => $this,
1887 'VALUE' => $value,
1888 'OLD_VALUE' => $oldValue,
1889 ]
1890 );
1891 }
1892 elseif ($name === 'RESPONSIBLE_ID')
1893 {
1894 $this->setField('DATE_RESPONSIBLE_ID', new Main\Type\DateTime());
1895 }
1896 elseif ($name === 'TRACKING_NUMBER')
1897 {
1898 if ($value)
1899 {
1900 Internals\EventsPool::addEvent(
1901 's'.$this->getInternalIndex(),
1903 [
1904 'ENTITY' => $this,
1905 'VALUES' => $this->getFields()->getOriginalValues(),
1906 ]
1907 );
1908 }
1909 }
1910
1911 $r = parent::onFieldModify($name, $oldValue, $value);
1912 if (!$r->isSuccess())
1913 {
1914 return $result->addErrors($r->getErrors());
1915 }
1916
1917 if (
1918 $name === 'BASE_PRICE_DELIVERY'
1919 && !$this->isMarkedFieldCustom('PRICE_DELIVERY')
1920 )
1921 {
1922 $value -= $this->getField('DISCOUNT_PRICE');
1923
1924 $r = $this->setField('PRICE_DELIVERY', $value);
1925 if (!$r->isSuccess())
1926 {
1927 $result->addErrors($r->getErrors());
1928 }
1929 }
1930
1931 if ($r->hasWarnings())
1932 {
1933 $result->addWarnings($r->getWarnings());
1934 }
1935
1936 $result->addData($r->getData());
1937
1938 if ($result->isSuccess())
1939 {
1940 $this->setFieldNoDemand('DATE_UPDATE', new Main\Type\DateTime());
1941 }
1942
1943 return $result;
1944 }
1945
1959 protected function syncQuantityAfterModify(BasketItem $basketItem, $value = null, $oldValue = null)
1960 {
1961 $result = new Result();
1962
1964 $shipmentItemCollection = $this->getShipmentItemCollection();
1965
1966 $shipmentItem = $shipmentItemCollection->getItemByBasketCode($basketItem->getBasketCode());
1967
1968 if ($value === 0)
1969 {
1970 if ($shipmentItem !== null)
1971 {
1972 $shipmentItem->setFieldNoDemand('QUANTITY', 0);
1973 }
1974
1975 return $result;
1976 }
1977
1978 if ($shipmentItem === null)
1979 {
1980 $shipmentItem = $shipmentItemCollection->createItem($basketItem);
1981 }
1982
1983 $deltaQuantity = $value - $oldValue;
1984
1985 if ($deltaQuantity > 0) // plus
1986 {
1987 $shipmentItem->setFieldNoDemand(
1988 "QUANTITY",
1989 $shipmentItem->getField("QUANTITY") + $deltaQuantity
1990 );
1991
1992 if (
1994 && $this->needReservation()
1995 )
1996 {
1997 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
1998 }
1999 }
2000 else // minus
2001 {
2002 if (floatval($shipmentItem->getField("QUANTITY")) <= 0)
2003 {
2004 return new Result();
2005 }
2006
2007 if ($value != 0 && roundEx($shipmentItem->getField("QUANTITY"), SALE_VALUE_PRECISION) < roundEx(-$deltaQuantity, SALE_VALUE_PRECISION))
2008 {
2009 $result->addError(
2010 new ResultError(
2011 str_replace(
2012 array("#NAME#", "#QUANTITY#", "#DELTA_QUANTITY#"),
2013 array($basketItem->getField("NAME"), $shipmentItem->getField("QUANTITY"), abs($deltaQuantity)),
2014 Loc::getMessage('SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR')
2015 ),
2016 'SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR'
2017 )
2018 );
2019 return $result;
2020 }
2021
2022 if ($value > 0)
2023 {
2024 $shipmentItem->setFieldNoDemand(
2025 "QUANTITY",
2026 $shipmentItem->getField("QUANTITY") + $deltaQuantity
2027 );
2028
2029 if (
2031 && $this->needReservation()
2032 )
2033 {
2034 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
2035 }
2036 }
2037
2038 }
2039
2040 return $result;
2041 }
2042
2046 public function getServiceParams()
2047 {
2048 $params = $this->getField('PARAMS');
2049 return isset($params["SERVICE_PARAMS"]) ? $params["SERVICE_PARAMS"] : array();
2050 }
2051
2056 public function setServiceParams(array $serviceParams)
2057 {
2058 $params = $this->getField('PARAMS');
2059 $params["SERVICE_PARAMS"] = $serviceParams;
2060 $this->setField("PARAMS", $params);
2061 }
2062
2066 public function getExtraServices()
2067 {
2068 if($this->extraServices === null)
2069 {
2070 $this->setExtraServices(
2071 Delivery\ExtraServices\Manager::getValuesForShipment(
2072 $this->getId(),
2073 $this->getDeliveryId()
2074 )
2075 );
2076 }
2077
2078 return $this->extraServices;
2079 }
2080
2084 public function setExtraServices(array $extraServices)
2085 {
2086 $this->extraServices = $extraServices;
2087 }
2088
2092 public function getExtraServicesObjects()
2093 {
2094 return Delivery\ExtraServices\Manager::getObjectsForShipment(
2095 $this->getId(),
2096 $this->getDeliveryId(),
2097 $this->getCurrency()
2098 );
2099 }
2100
2104 protected function saveExtraServices()
2105 {
2106 return Delivery\ExtraServices\Manager::saveValuesForShipment($this->getId(), $this->getExtraServices());
2107 }
2108
2112 public function getStoreId()
2113 {
2114 if($this->storeId === null)
2115 {
2116 $this->setStoreId(
2117 Delivery\ExtraServices\Manager::getStoreIdForShipment(
2118 $this->getId(),
2119 $this->getDeliveryId()
2120 ));
2121 }
2122
2123 return $this->storeId;
2124 }
2125
2129 public function setStoreId($storeId)
2130 {
2131 $this->storeId = (int)$storeId;
2132 }
2133
2137 protected function saveStoreId()
2138 {
2139 return Delivery\ExtraServices\Manager::saveStoreIdForShipment($this->getId(), $this->getDeliveryId(), $this->getStoreId());
2140 }
2141
2145 public function getWeight() : float
2146 {
2147 return (float)$this->getField('WEIGHT');
2148 }
2149
2154 public function setWeight(float $weight)
2155 {
2156 return $this->setField('WEIGHT', $weight);
2157 }
2158
2163 public function calculateDelivery()
2164 {
2165 if ($this->isSystem())
2166 {
2167 throw new Main\NotSupportedException();
2168 }
2169
2170 if ($this->getDeliveryId() === 0)
2171 {
2172 return new Delivery\CalculationResult();
2173 }
2174
2175 return Delivery\Services\Manager::calculateDeliveryPrice($this);
2176 }
2177
2182 public function resetData()
2183 {
2184 if (!$this->isCustomPrice())
2185 {
2186 $this->setField('BASE_PRICE_DELIVERY', 0);
2187 }
2188 }
2189
2197 public function getBasketItemQuantity(BasketItem $basketItem)
2198 {
2200 $shipmentItemCollection = $this->getShipmentItemCollection();
2201
2202 return $shipmentItemCollection->getBasketItemQuantity($basketItem);
2203 }
2204
2211 protected function addChangesToHistory($name, $oldValue = null, $value = null)
2212 {
2213 if ($this->getId() > 0 && !$this->isSystem())
2214 {
2215 $order = $this->getOrder();
2216
2217 if ($order && $order->getId() > 0)
2218 {
2219 $registry = Registry::getInstance(static::getRegistryType());
2220
2222 $orderHistory = $registry->getOrderHistoryClassName();
2223 $orderHistory::addField(
2224 'SHIPMENT',
2225 $order->getId(),
2226 $name,
2227 $oldValue,
2228 $value,
2229 $this->getId(),
2230 $this
2231 );
2232 }
2233 }
2234 }
2235
2242 public function isExistBasketItem(BasketItem $basketItem)
2243 {
2245 if (!$shipmentItemCollection = $this->getShipmentItemCollection())
2246 {
2247 throw new Main\ObjectNotFoundException('Entity "ShipmentItemCollection" not found');
2248 }
2249
2250 return $shipmentItemCollection->isExistBasketItem($basketItem);
2251 }
2252
2259 public function verify()
2260 {
2261 $result = new Result();
2262
2263 if ($this->getDeliveryId() <= 0)
2264 {
2265 $result->addError(
2266 new ResultError(
2267 Loc::getMessage("SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY"),
2268 'SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY'
2269 )
2270 );
2271 }
2272
2274 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2275 {
2277 foreach ($shipmentItemCollection as $shipmentItem)
2278 {
2279 $r = $shipmentItem->verify();
2280 if (!$r->isSuccess())
2281 {
2282 $result->addErrors($r->getErrors());
2283 }
2284 }
2285 }
2286
2287 return $result;
2288 }
2289
2297 public function setAccountNumber($id)
2298 {
2299 $result = new Result();
2300
2301 $id = intval($id);
2302 if ($id <= 0)
2303 {
2304 $result->addError(new ResultError(Loc::getMessage('SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'), 'SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'));
2305 return $result;
2306 }
2307
2308 $value = Internals\AccountNumberGenerator::generateForShipment($this);
2309
2310 try
2311 {
2313 $r = static::updateInternal($id, array("ACCOUNT_NUMBER" => $value));
2314 $res = $r->isSuccess(true);
2315 }
2316 catch (Main\DB\SqlQueryException $exception)
2317 {
2318 $res = false;
2319 }
2320
2321 if ($res)
2322 {
2323 $this->setFieldNoDemand('ACCOUNT_NUMBER', $value);
2324 }
2325
2326 return $result;
2327 }
2328
2333 public function getBusinessValueProviderInstance($mapping)
2334 {
2335 $providerInstance = null;
2336
2337 if (is_array($mapping) && isset($mapping['PROVIDER_KEY']))
2338 {
2339 switch ($mapping['PROVIDER_KEY'])
2340 {
2341 case 'SHIPMENT': $providerInstance = $this; break;
2342 case 'COMPANY' : $providerInstance = $this->getField('COMPANY_ID'); break;
2343 default:
2344 $order = $this->getOrder();
2345 if ($order)
2346 {
2347 $providerInstance = $order->getBusinessValueProviderInstance($mapping);
2348 }
2349 }
2350 }
2351
2352 return $providerInstance;
2353 }
2354
2358 public function getPersonTypeId()
2359 {
2360 $order = $this->getOrder();
2361 if ($order)
2362 {
2363 return $order->getPersonTypeId();
2364 }
2365
2366 return null;
2367 }
2368
2376 public static function getList(array $parameters)
2377 {
2378 return Internals\ShipmentTable::getList($parameters);
2379 }
2380
2391 public function createClone(\SplObjectStorage $cloneEntity)
2392 {
2393 if ($this->isClone() && $cloneEntity->contains($this))
2394 {
2395 return $cloneEntity[$this];
2396 }
2397
2399 $shipmentClone = parent::createClone($cloneEntity);
2400
2402 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2403 {
2404 if (!$cloneEntity->contains($shipmentItemCollection))
2405 {
2406 $cloneEntity[$shipmentItemCollection] = $shipmentItemCollection->createClone($cloneEntity);
2407 }
2408
2409 if ($cloneEntity->contains($shipmentItemCollection))
2410 {
2411 $shipmentClone->shipmentItemCollection = $cloneEntity[$shipmentItemCollection];
2412 }
2413 }
2414
2416 if ($service = $this->getDelivery())
2417 {
2418 if (!$cloneEntity->contains($service))
2419 {
2420 $cloneEntity[$service] = $service->createClone($cloneEntity);
2421 }
2422
2423 if ($cloneEntity->contains($service))
2424 {
2425 $shipmentClone->service = $cloneEntity[$service];
2426 }
2427 }
2428
2429 return $shipmentClone;
2430 }
2431
2440 protected function setStatus($status)
2441 {
2442 global $USER;
2443
2444 $result = new Result();
2445
2446 $registry = Registry::getInstance(static::getRegistryType());
2448 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
2449
2450 if (is_object($USER) && $USER->isAuthorized())
2451 {
2452 $statusesList = $deliveryStatusClassName::getAllowedUserStatuses($USER->getID(), $this->getField('STATUS_ID'));
2453 }
2454 else
2455 {
2456 $statusesList = $deliveryStatusClassName::getAllStatuses();
2457 }
2458
2459 if($this->getField('STATUS_ID') != $status && array_key_exists($status, $statusesList))
2460 {
2462 $r = $this->setField('STATUS_ID', $status);
2463 if (!$r->isSuccess())
2464 {
2465 $result->addErrors($r->getErrors());
2466 return $result;
2467 }
2468 }
2469
2470 return $result;
2471 }
2472
2478 public function getErrorEntity($value)
2479 {
2480 $className = null;
2481 $errorsList = static::getAutoFixErrorsList();
2482 if (is_array($errorsList) && in_array($value, $errorsList))
2483 {
2484 $className = static::getClassName();
2485 }
2486 else
2487 {
2489 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2490 {
2491 $className = $shipmentItemCollection->getErrorEntity($value);
2492 }
2493 }
2494
2495 return $className;
2496 }
2497
2503 public function canAutoFixError($value)
2504 {
2505 $autoFix = false;
2506 $errorsList = static::getAutoFixErrorsList();
2507 if (is_array($errorsList) && in_array($value, $errorsList))
2508 {
2509 $autoFix = true;
2510 }
2511 else
2512 {
2514 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2515 {
2516 $autoFix = $shipmentItemCollection->canAutoFixError($value);
2517 }
2518 }
2519
2520 return $autoFix;
2521 }
2522
2526 public function getAutoFixErrorsList()
2527 {
2528 return array_keys(static::getAutoFixRules());
2529 }
2530
2536 public function tryFixError($code)
2537 {
2538 $result = new Result();
2539
2540 $method = static::getFixMethod($code);
2541 $r = call_user_func_array($method, array($this));
2542 if (!$r->isSuccess())
2543 {
2544 $result->addErrors($r->getErrors());
2545 }
2546 elseif ($r->hasWarnings())
2547 {
2548 $result->addWarnings($r->getWarnings());
2549 }
2550
2551 return $result;
2552 }
2553
2558 protected static function getFixMethod($code)
2559 {
2560 $codeList = static::getAutoFixRules();
2561
2562 if (!empty($codeList[$code]))
2563 {
2564 return $codeList[$code];
2565 }
2566 return null;
2567 }
2568
2575 public static function fixReserveErrors(Shipment $entity)
2576 {
2577 $result = new Result();
2578
2579 $r = $entity->tryReserve();
2580 if (!$r->isSuccess())
2581 {
2582 $result->addErrors($r->getErrors());
2583 }
2584 elseif ($r->hasWarnings())
2585 {
2586 $result->addWarnings($r->getWarnings());
2587 }
2588
2589 return $result;
2590 }
2591
2598 public static function fixShipErrors(Shipment $entity)
2599 {
2600 $result = new Result();
2601
2602 $r = $entity->setField('DEDUCTED', 'Y');
2603 if (!$r->isSuccess())
2604 {
2605 if (!$r->isSuccess())
2606 {
2607 $result->addErrors($r->getErrors());
2608 }
2609 }
2610
2611 $r = $entity->tryShip();
2612 if (!$r->isSuccess())
2613 {
2614 if (!$r->isSuccess())
2615 {
2616 $result->addErrors($r->getErrors());
2617 }
2618 }
2619
2620 return $result;
2621 }
2622
2626 protected static function getAutoFixRules()
2627 {
2628 return [
2629 'PROVIDER_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2630 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2631 'PROVIDER_UNRESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2632 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2633
2634 'SALE_PROVIDER_SHIPMENT_SHIPPED_LESS_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2635 'SALE_PROVIDER_SHIPMENT_SHIPPED_MORE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2636 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2637 'SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2638 'DDCT_DEDUCTION_QUANTITY_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2639 ];
2640 }
2641
2645 public function canMarked()
2646 {
2647 return true;
2648 }
2649
2653 public function getMarkField()
2654 {
2655 return 'MARKED';
2656 }
2657
2664 public function isChanged()
2665 {
2666 if (parent::isChanged())
2667 {
2668 return true;
2669 }
2670
2671 return $this->getShipmentItemCollection()->isChanged();
2672 }
2673
2677 public function clearChanged()
2678 {
2679 parent::clearChanged();
2680
2681 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2682 {
2684 foreach ($shipmentItemCollection as $shipmentItem)
2685 {
2686 $shipmentItem->clearChanged();
2687 }
2688 }
2689 }
2690
2699 public function getVatRate()
2700 {
2701 $vatRate = 0;
2702
2703 $service = $this->getDelivery();
2704 if ($service)
2705 {
2706 if (!Main\Loader::includeModule('catalog'))
2707 {
2708 return $vatRate;
2709 }
2710
2711 $vatId = $service->getVatId();
2712 if ($vatId <= 0)
2713 {
2714 return $vatRate;
2715 }
2716
2717 $dbRes = VatTable::getById($vatId);
2718 $vatInfo = $dbRes->fetch();
2719 if ($vatInfo)
2720 {
2721 $vatRate = $vatInfo['RATE'] / 100;
2722 }
2723 }
2724
2725 return $vatRate;
2726 }
2727
2733 public function getVatSum()
2734 {
2735 $vatRate = $this->getVatRate();
2736 $price = $this->getPrice() * $vatRate / (1 + $vatRate);
2737
2738 return PriceMaths::roundPrecision($price);
2739 }
2740
2746 protected function addInternal(array $data)
2747 {
2748 return Internals\ShipmentTable::add($data);
2749 }
2750
2757 protected function updateInternal($primary, array $data)
2758 {
2759 return Internals\ShipmentTable::update($primary, $data);
2760 }
2761
2768 protected static function deleteInternal($primary)
2769 {
2770 return Internals\ShipmentTable::deleteWithItems($primary);
2771 }
2772
2776 protected static function getFieldsMap()
2777 {
2778 return Internals\ShipmentTable::getMap();
2779 }
2780
2784 public static function getUfId()
2785 {
2786 return Internals\ShipmentTable::getUfId();
2787 }
2788
2798 public function setBasePriceDelivery($value, $custom = false)
2799 {
2800 $result = new Result();
2801
2802 if ($custom === true)
2803 {
2804 $this->markFieldCustom('PRICE_DELIVERY');
2805 }
2806
2807 $r = $this->setField('BASE_PRICE_DELIVERY', $value);
2808 if (!$r->isSuccess())
2809 {
2810 $result->addErrors($r->getErrors());
2811 }
2812
2813 return $result;
2814 }
2815
2821 public static function getEntityEventName()
2822 {
2823 return 'SaleShipment';
2824 }
2825
2831 public function toArray() : array
2832 {
2833 $result = parent::toArray();
2834
2835 $result['ITEMS'] = $this->getShipmentItemCollection()->toArray();
2836
2837 return $result;
2838 }
2839
2841 {
2842 if(empty($this->propertyCollection))
2843 {
2844 $this->propertyCollection = $this->loadPropertyCollection();
2845 }
2846
2848 }
2849
2850 public function loadPropertyCollection(): ShipmentPropertyValueCollection
2851 {
2852 $registry = Registry::getInstance(static::getRegistryType());
2854 $propertyCollectionClassName = $registry->getShipmentPropertyValueCollectionClassName();
2855
2856 return $propertyCollectionClassName::load($this);
2857 }
2858
2864 public function getParentOrder()
2865 {
2866 return $this->getOrder();
2867 }
2868}
2869
static update($id, array $data)
Definition: entity.php:229
static add(array $data)
Definition: entity.php:150
static loadMessages($file)
Definition: loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition: loc.php:29
static getProductReservationCondition()
const EVENT_ON_BEFORE_SHIPMENT_STATUS_CHANGE
const EVENT_ON_SHIPMENT_TRACKING_NUMBER_CHANGE
const EVENT_ON_SHIPMENT_STATUS_CHANGE_SEND_MAIL
static roundPrecision($value)
Definition: pricemaths.php:17
static repeat(Order $order, array $resultList)
Definition: recurring.php:26
static getInstance($type)
Definition: registry.php:183
const REGISTRY_TYPE_ORDER
Definition: registry.php:16
setDeliveryService(Delivery\Services\Base $service)
Definition: shipment.php:171
setBasePriceDelivery($value, $custom=false)
Definition: shipment.php:2798
setWeight(float $weight)
Definition: shipment.php:2154
static deleteNoDemand($orderId)
Definition: shipment.php:681
static getRegistryEntity()
Definition: shipment.php:46
updateInternal($primary, array $data)
Definition: shipment.php:2757
setServiceParams(array $serviceParams)
Definition: shipment.php:2056
checkValueBeforeSet($name, $value)
Definition: shipment.php:818
static getFixMethod($code)
Definition: shipment.php:2558
getBusinessValueProviderInstance($mapping)
Definition: shipment.php:2333
static getMeaningfulFields()
Definition: shipment.php:160
static getParametersForLoad($id)
Definition: shipment.php:905
static getCustomizableFields()
Definition: shipment.php:123
static getAutoFixRules()
Definition: shipment.php:2626
onBeforeBasketItemDelete(BasketItem $basketItem)
Definition: shipment.php:1602
static loadForOrder($id)
Definition: shipment.php:886
static getEntityEventName()
Definition: shipment.php:2821
static createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService=null)
Definition: shipment.php:1251
static getAvailableFields()
Definition: shipment.php:74
static fixReserveErrors(Shipment $entity)
Definition: shipment.php:2575
static getList(array $parameters)
Definition: shipment.php:2376
setStoreId($storeId)
Definition: shipment.php:2129
onBeforeSetFields(array $values)
Definition: shipment.php:132
isPriceField(string $name)
Definition: shipment.php:1281
static fixShipErrors(Shipment $entity)
Definition: shipment.php:2598
addInternal(array $data)
Definition: shipment.php:2746
setFieldNoDemand($name, $value)
Definition: shipment.php:863
static generateXmlId()
Definition: shipment.php:227
static deleteInternal($primary)
Definition: shipment.php:2768
setField($name, $value)
Definition: shipment.php:789
setOrderId($orderId)
Definition: shipment.php:1317
normalizeValue($name, $value)
Definition: shipment.php:761
static getRegistryType()
Definition: shipment.php:248
setExtraServices(array $extraServices)
Definition: shipment.php:2084
onShipmentItemCollectionModify($action, ShipmentItem $shipmentItem, $name=null, $oldValue=null, $value=null)
Definition: shipment.php:620
setFieldNoDemand($name, $value)
canAutoFixError($value)
getErrorEntity($value)