1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
shipment.php
См. документацию.
1<?php
2
3namespace Bitrix\Sale;
4
5use Bitrix\Catalog\VatTable;
6use Bitrix\Main;
7use Bitrix\Main\Entity;
8use Bitrix\Main\Localization\Loc;
9use Bitrix\Sale\Delivery;
10use Bitrix\Sale\Internals;
11use \Bitrix\Sale\Delivery\Requests;
12use Bitrix\Sale\Reservation\Configuration\ReserveCondition;
13
14Loc::loadMessages(__FILE__);
15
21{
24
26 protected $service = null;
27
28 protected $extraServices = null;
29 protected bool $areExtraServicesChanged = false;
30
31 protected $storeId = null;
32 protected bool $isStoreIdChanged = false;
33
35 protected $internalId = 0;
36
37 protected static $idShipment = 0;
38
41
43 protected $isNew = true;
44
48 public static function getRegistryEntity()
49 {
51 }
52
56 public function getShipmentCode()
57 {
58 if ($this->internalId === 0)
59 {
60 if ($this->getId() > 0)
61 {
62 $this->internalId = $this->getId();
63 }
64 else
65 {
66 static::$idShipment++;
67 $this->internalId = static::$idShipment;
68 }
69 }
70 return $this->internalId;
71 }
72
76 public static function getAvailableFields()
77 {
78 return [
79 "STATUS_ID",
80 "BASE_PRICE_DELIVERY",
81 "PRICE_DELIVERY",
82 "ALLOW_DELIVERY",
83 "DATE_ALLOW_DELIVERY",
84 "EMP_ALLOW_DELIVERY_ID",
85 "DEDUCTED",
86 "DATE_DEDUCTED",
87 "EMP_DEDUCTED_ID",
88 "REASON_UNDO_DEDUCTED",
89 "DELIVERY_ID",
90 "DELIVERY_DOC_NUM",
91 "DELIVERY_DOC_DATE",
92 "TRACKING_NUMBER",
93 "XML_ID",
94 "PARAMS",
95 "DELIVERY_NAME",
96 "COMPANY_ID",
97 "MARKED",
98 "WEIGHT",
99 "DATE_MARKED",
100 "EMP_MARKED_ID",
101 "REASON_MARKED",
102 "CANCELED",
103 "DATE_CANCELED",
104 "EMP_CANCELED_ID",
105 "RESPONSIBLE_ID",
106 "DATE_RESPONSIBLE_ID",
107 "EMP_RESPONSIBLE_ID",
108 "COMMENTS",
109 "CURRENCY",
110 "CUSTOM_PRICE_DELIVERY",
111 "UPDATED_1C",
112 "EXTERNAL_DELIVERY",
113 "VERSION_1C","ID_1C",
114 "TRACKING_STATUS",
115 "TRACKING_LAST_CHECK",
116 "TRACKING_DESCRIPTION",
117 "ACCOUNT_NUMBER",
118 'DISCOUNT_PRICE'
119 ];
120 }
121
125 public static function getCustomizableFields() : array
126 {
127 return ['PRICE_DELIVERY' => 'PRICE_DELIVERY', 'WEIGHT' => 'WEIGHT'];
128 }
129
134 protected function onBeforeSetFields(array $values)
135 {
136 if (isset($values['DEDUCTED']))
137 {
138 if ($this->getField('DEDUCTED') === 'Y')
139 {
140 if ($values['DEDUCTED'] === 'N')
141 {
142 $values = ['DEDUCTED' => $values['DEDUCTED']] + $values;
143 }
144 }
145 else
146 {
147 if ($values['DEDUCTED'] === 'Y')
148 {
149 // move to the end of array
150 unset($values['DEDUCTED']);
151 $values['DEDUCTED'] = 'Y';
152 }
153 }
154 }
155
156 return $values;
157 }
158
162 protected static function getMeaningfulFields()
163 {
164 return array('BASE_PRICE_DELIVERY', 'DELIVERY_ID');
165 }
166
174 {
175 $this->service = $service;
176
177 $result = $this->setField("DELIVERY_ID", $service->getId());
178 if ($result->isSuccess())
179 {
180 $this->setField("DELIVERY_NAME", $service->getName());
181 }
182 }
183
191 public static function create(ShipmentCollection $collection, Delivery\Services\Base $service = null)
192 {
194 $fields = [
195 'CURRENCY' => $collection->getOrder()->getCurrency(),
196 'DATE_INSERT' => new Main\Type\DateTime(),
197 'DELIVERY_ID' => $emptyService['ID'],
198 'DELIVERY_NAME' => $emptyService['NAME'],
199 'ALLOW_DELIVERY' => 'N',
200 'DEDUCTED' => 'N',
201 'CUSTOM_PRICE_DELIVERY' => 'N',
202 'MARKED' => 'N',
203 'CANCELED' => 'N',
204 'SYSTEM' => 'N',
205 'XML_ID' => static::generateXmlId(),
206 'RESERVED' => 'N'
207 ];
208
209 $registry = Registry::getInstance(static::getRegistryType());
210
212 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
213 $fields['STATUS_ID'] = $deliveryStatusClassName::getInitialStatus();
214
215 $shipment = static::createShipmentObject();
216 $shipment->setFieldsNoDemand($fields);
217 $shipment->setCollection($collection);
218
219 if ($service !== null)
220 {
221 $shipment->setDeliveryService($service);
222 }
223
224 return $shipment;
225 }
226
230 protected static function generateXmlId()
231 {
232 return uniqid('bx_');
233 }
234
240 private static function createShipmentObject(array $fields = array())
241 {
242 $registry = Registry::getInstance(static::getRegistryType());
243 $shipmentClassName = $registry->getShipmentClassName();
244
245 return new $shipmentClassName($fields);
246 }
247
251 public static function getRegistryType()
252 {
254 }
255
263 public function needReservation()
264 {
266
267 if ($condition === ReserveCondition::ON_CREATE)
268 {
269 return true;
270 }
271
272 if ($condition === ReserveCondition::ON_PAY
273 || $condition === ReserveCondition::ON_FULL_PAY)
274 {
275 $order = $this->getOrder();
276 if ($condition === ReserveCondition::ON_FULL_PAY)
277 {
278 return $order->isPaid();
279 }
280
281 return $order->getPaymentCollection()->hasPaidPayment();
282 }
283
284 if ($this->isSystem())
285 {
286 return false;
287 }
288
289 return
290 (
291 $condition === ReserveCondition::ON_ALLOW_DELIVERY
292 && $this->isAllowDelivery()
293 )
294 || (
295 $condition === ReserveCondition::ON_SHIP
296 && $this->isShipped()
297 )
298 ;
299 }
300
311 private function transferItem2SystemShipment(ShipmentItem $sourceItem, $quantity)
312 {
313 $sourceItemCollection = $sourceItem->getCollection();
314 if ($this !== $sourceItemCollection->getShipment())
315 {
316 throw new Main\ArgumentException("item");
317 }
318
319 $quantity = floatval($quantity);
320
322 $systemShipment = $this->getCollection()->getSystemShipment();
323
325 $basketItem = $sourceItem->getBasketItem();
326
328 $order = $basketItem->getCollection()->getOrder();
329
330 $shipmentItemCode = $sourceItem->getBasketCode();
331
332 if ($quantity === 0)
333 {
334 return new Result();
335 }
336
338 $systemShipmentItemCollection = $systemShipment->getShipmentItemCollection();
339
340 $systemShipmentItem = $systemShipmentItemCollection->getItemByBasketCode($shipmentItemCode);
341 if (is_null($systemShipmentItem))
342 {
343 $systemShipmentItem = $systemShipmentItemCollection->createItem($basketItem);
344 }
345
346 $newSystemShipmentItemQuantity = $systemShipmentItem->getQuantity() + $quantity;
347 if ($newSystemShipmentItemQuantity < 0)
348 {
349 $result = new Result();
350 $result->addError(
351 new ResultError(
352 str_replace(
353 ["#NAME#", "#QUANTITY#"],
354 [$sourceItem->getBasketItem()->getField("NAME"), abs($quantity)],
355 Loc::getMessage('SALE_SHIPMENT_QUANTITY_MISMATCH')
356 ),
357 'SALE_SHIPMENT_QUANTITY_MISMATCH'
358 )
359 );
360 return $result;
361 }
362
363 $systemShipmentItem->setFieldNoDemand('QUANTITY', $newSystemShipmentItemQuantity);
364 if ($newSystemShipmentItemQuantity <= 1e-10)
365 {
366 $systemShipmentItem->delete();
367 }
368
369 $affectedQuantity = 0;
370
371 if ($quantity > 0) // transfer to system shipment
372 {
373 if ($sourceItem->getReservedQuantity() > 0)
374 {
375 $affectedQuantity = $quantity;
376 $originalQuantity = $sourceItem->getQuantity() + $quantity;
377 if ($sourceItem->getReservedQuantity() < $originalQuantity)
378 {
379 $affectedQuantity -= $originalQuantity - $sourceItem->getReservedQuantity();
380 }
381 }
382 }
383 elseif ($quantity < 0) // transfer from system shipment
384 {
385 if ($systemShipmentItem->getReservedQuantity() > 0)
386 {
387 $affectedQuantity = $quantity;
388 if ($systemShipmentItem->getReservedQuantity() < -$affectedQuantity)
389 {
390 $affectedQuantity = -1 * $systemShipmentItem->getReservedQuantity();
391 }
392 }
393 }
394
395 if ($affectedQuantity != 0) // if there are reserved items among transferred
396 {
397 $sourceItem->getFields()->set(
398 'RESERVED_QUANTITY',
399 $sourceItem->getField('RESERVED_QUANTITY') - $affectedQuantity
400 );
401
402 $systemShipmentItem->getFields()->set(
403 'RESERVED_QUANTITY',
404 $systemShipmentItem->getField('RESERVED_QUANTITY') + $affectedQuantity
405 );
406
407 $systemShipment->setFieldNoDemand(
408 'RESERVED',
409 ($systemShipmentItem->getField("RESERVED_QUANTITY") > 0) ? "Y" : "N"
410 );
411
412 $shipmentItemForPool = $sourceItem;
413 $sourceShipmentItemForPool = $systemShipmentItem;
414
415 if ($quantity > 0)
416 {
417 $shipmentItemForPool = $systemShipmentItem;
418 $sourceShipmentItemForPool = $sourceItem;
419 }
420
421 $productId = $basketItem->getProductId();
422
423 $foundItem = false;
424 $poolItems = Internals\ItemsPool::get($order->getInternalId(), $productId);
425 if (!empty($poolItems))
426 {
427 foreach ($poolItems as $poolIndex => $poolItem)
428 {
429 if ($poolItem->getInternalIndex() === $shipmentItemForPool->getInternalIndex())
430 {
431 $foundItem = true;
432 }
433
434 if (
435 $sourceShipmentItemForPool
436 && $poolItem instanceof ShipmentItem
437 && $poolItem->getInternalIndex() === $sourceShipmentItemForPool->getInternalIndex()
438 )
439 {
440 $reserveQuantity = $sourceShipmentItemForPool->getReservedQuantity();
441 if (abs($reserveQuantity) <= 1e-6)
442 {
443 Internals\ItemsPool::delete($order->getInternalId(), $productId, $poolIndex);
444 }
445 }
446 }
447 }
448
449 if (!$foundItem)
450 {
451 Internals\ItemsPool::add($order->getInternalId(), $productId, $shipmentItemForPool);
452 }
453 }
454
455 $tryReserveResult = null;
456
457 if ($quantity > 0)
458 {
460 {
461 if ($systemShipment->needReservation())
462 {
463 $tryReserveResult = Internals\Catalog\Provider::tryReserveShipmentItem($systemShipmentItem);
464 }
465 else
466 {
467 $tryReserveResult = Internals\Catalog\Provider::tryUnreserveShipmentItem($systemShipmentItem);
468 }
469 }
470 }
471 elseif ($quantity < 0) // transfer from system shipment
472 {
473 if (
475 && $sourceItemCollection->getShipment()->needReservation()
476 )
477 {
478 $tryReserveResult = Internals\Catalog\Provider::tryReserveShipmentItem($sourceItem);
479 }
480 }
481
482 $canReserve = false;
483
484 if ($tryReserveResult === null)
485 {
486 $canReserve = true;
487 }
488
489 if ($tryReserveResult !== null && ($tryReserveResult->isSuccess() && ($tryReserveResultData = $tryReserveResult->getData())))
490 {
491 if (array_key_exists('CAN_RESERVE', $tryReserveResultData))
492 {
493 $canReserve = $tryReserveResultData['CAN_RESERVE'];
494 }
495 }
496
497 if (
499 && $systemShipment->needReservation()
500 && $canReserve
501 )
502 {
503 $order = $this->getOrder();
504 if ($order &&
505 !Internals\ActionEntity::isTypeExists(
506 $order->getInternalId(),
508 )
509 )
510 {
511 Internals\ActionEntity::add(
512 $order->getInternalId(),
514 [
515 'METHOD' => 'Bitrix\Sale\ShipmentCollection::updateReservedFlag',
516 'PARAMS' => [$systemShipment->getCollection()]
517 ]
518 );
519 }
520 }
521
522
523 return new Result();
524 }
525
532 public static function updateReservedFlag(Shipment $shipment)
533 {
534 $shipmentReserved = true;
535
536 $shipmentItemList = $shipment->getShipmentItemCollection()->getShippableItems();
537
538 if ($shipmentItemList->count() === 0)
539 {
540 $shipmentReserved = false;
541 }
542
544 foreach ($shipmentItemList as $shipmentItem)
545 {
546 if ($shipmentItem->getQuantity() - $shipmentItem->getReservedQuantity())
547 {
548 $shipmentReserved = false;
549 break;
550 }
551 }
552
553 $shipmentReservedValue = $shipmentReserved ? "Y" : "N";
554 $currentValue = $shipment->getField('RESERVED');
555 if ($shipment->getField('RESERVED') != $shipmentReservedValue)
556 {
557 $eventManager = Main\EventManager::getInstance();
558 $eventsList = $eventManager->findEventHandlers('sale', EventActions::EVENT_ON_BEFORE_SHIPMENT_RESERVE);
559 if (!empty($eventsList))
560 {
563 'ENTITY' => $shipment,
564 'VALUE' => $shipmentReservedValue,
565 ]);
566
567 $event->send();
568
569 if ($event->getResults())
570 {
571 $result = new Result();
573 foreach($event->getResults() as $eventResult)
574 {
575 if($eventResult->getType() === Main\EventResult::ERROR)
576 {
577 $errorMsg = new ResultError(Main\Localization\Loc::getMessage('SALE_EVENT_ON_BEFORE_SHIPMENT_RESERVE_ERROR'), 'SALE_EVENT_ON_BEFORE_SHIPMENT_RESERVE_ERROR');
578
579 $eventResultData = $eventResult->getParameters();
580 if ($eventResultData)
581 {
582 if (isset($eventResultData) && $eventResultData instanceof ResultError)
583 {
585 $errorMsg = $eventResultData;
586 }
587 }
588
589 $result->addError($errorMsg);
590
591 }
592 }
593
594 if (!$result->isSuccess())
595 {
596 return $result;
597 }
598 }
599 }
600
601 $shipment->setFieldNoDemand('RESERVED', $shipmentReserved ? "Y" : "N");
602
603 Internals\EventsPool::addEvent('s'.$shipment->getInternalIndex(), EventActions::EVENT_ON_SHIPMENT_RESERVED, [
604 'ENTITY' => $shipment,
605 'VALUE' => $shipmentReservedValue,
606 'OLD_VALUE' => $currentValue,
607 ]);
608 }
609
610 return new Result();
611 }
612
623 public function onShipmentItemCollectionModify($action, ShipmentItem $shipmentItem, $name = null, $oldValue = null, $value = null)
624 {
626 {
627 return new Result();
628 }
629
630 if ($this->isSystem()
631 && $name != 'RESERVED_QUANTITY'
632 )
633 {
634 throw new Main\NotSupportedException(Loc::getMessage('SALE_SHIPMENT_SYSTEM_SHIPMENT_CHANGE'));
635 }
636
637 if ($name === "QUANTITY")
638 {
639 $result = $this->transferItem2SystemShipment($shipmentItem, $oldValue - $value);
640
641 if (!$this->isMarkedFieldCustom('WEIGHT'))
642 {
643 $this->setField(
644 'WEIGHT',
645 $this->getShipmentItemCollection()->getWeight()
646 );
647 }
648
649 return $result;
650 }
651 elseif ($name === 'RESERVED_QUANTITY')
652 {
653 $order = $this->getParentOrder();
654 if ($order &&
655 !Internals\ActionEntity::isTypeExists(
656 $order->getInternalId(),
658 )
659 )
660 {
662 $order->getInternalId(),
664 [
665 'METHOD' => 'Bitrix\Sale\ShipmentCollection::updateReservedFlag',
666 'PARAMS' => [$this->getCollection()]
667 ]
668 );
669 }
670 }
671
672 return new Result();
673 }
674
684 public static function deleteNoDemand($orderId)
685 {
686 $result = new Result();
687
688 $shipmentDataList = static::getList(
689 [
690 "filter" => ["=ORDER_ID" => $orderId],
691 "select" => ["ID"]
692 ]
693 );
694
695 while ($shipment = $shipmentDataList->fetch())
696 {
697 $res = static::deleteInternal($shipment['ID']);
698
699 if ($res -> isSuccess())
700 {
702 }
703 else
704 {
705 $result->addErrors($res->getErrors());
706 }
707 }
708
709 return $result;
710 }
711
723 public function delete()
724 {
725 if ($this->isShipped())
726 {
727 $result = new Result();
728 return $result->addError(
729 new ResultError(
730 Loc::getMessage('SALE_SHIPMENT_EXIST_SHIPPED'),
731 'SALE_SHIPMENT_EXIST_SHIPPED'
732 )
733 );
734 }
735
736 if (!$this->isSystem())
737 {
738 $this->setField('BASE_PRICE_DELIVERY', 0);
739
740 if ($this->getFields()->isMarkedCustom('PRICE_DELIVERY'))
741 {
742 $this->setField('PRICE_DELIVERY', 0);
743 }
744
745 $this->disallowDelivery();
746 }
747
748 $this->getPropertyCollection()->deleteNoDemand($this->getId());
749 $this->deleteDeliveryRequest();
750
751 $this->getShipmentItemCollection()->clearCollection();
752
753 return parent::delete();
754 }
755
759 protected function deleteDeliveryRequest()
760 {
762 }
763
764 protected function normalizeValue($name, $value)
765 {
766 if ($this->isPriceField($name))
767 {
768 $value = PriceMaths::roundPrecision($value);
769 }
770 elseif ($name === 'REASON_MARKED')
771 {
772 $value = (string)$value;
773 if (mb_strlen($value) > 255)
774 {
775 $value = mb_substr($value, 0, 255);
776 }
777 }
778
779 return parent::normalizeValue($name, $value);
780 }
781
792 public function setField($name, $value)
793 {
794 if ($this->isSystem())
795 {
796 throw new Main\NotSupportedException();
797 }
798
799 if ($name === 'CUSTOM_PRICE_DELIVERY')
800 {
801 if ($value === 'Y')
802 {
803 $this->markFieldCustom('PRICE_DELIVERY');
804 }
805 else
806 {
807 $this->unmarkFieldCustom('PRICE_DELIVERY');
808 }
809 }
810
811 return parent::setField($name, $value);
812 }
813
821 protected function checkValueBeforeSet($name, $value)
822 {
823 $result = parent::checkValueBeforeSet($name, $value);
824
825 if ($name === "DELIVERY_ID")
826 {
827 if (intval($value) > 0 && !Delivery\Services\Manager::isServiceExist($value))
828 {
829 $result->addError(
830 new ResultError(
831 Loc::getMessage('SALE_SHIPMENT_WRONG_DELIVERY_SERVICE'),
832 'SALE_SHIPMENT_WRONG_DELIVERY_SERVICE'
833 )
834 );
835 }
836 }
837 elseif ($name === 'ACCOUNT_NUMBER')
838 {
839 $dbRes = static::getList([
840 'select' => ['ID'],
841 'filter' => ['=ACCOUNT_NUMBER' => $value]
842 ]);
843
844 if ($dbRes->fetch())
845 {
846 $result->addError(
847 new ResultError(
848 Loc::getMessage('SALE_SHIPMENT_ACCOUNT_NUMBER_EXISTS')
849 )
850 );
851 }
852 }
853
854 return $result;
855 }
856
866 public function setFieldNoDemand($name, $value)
867 {
868 if ($name === 'CUSTOM_PRICE_DELIVERY')
869 {
870 if ($value === 'Y')
871 {
872 $this->markFieldCustom('PRICE_DELIVERY');
873 }
874 else
875 {
876 $this->unmarkFieldCustom('PRICE_DELIVERY');
877 }
878 }
879
880 parent::setFieldNoDemand($name, $value);
881 }
882
889 public static function loadForOrder($id)
890 {
891 if (intval($id) <= 0)
892 {
893 throw new Main\ArgumentNullException("id");
894 }
895
896 $shipments = [];
897
898 $shipmentDataList = static::getList(static::getParametersForLoad($id));
899 while ($shipmentData = $shipmentDataList->fetch())
900 {
901 $shipments[] = static::createShipmentObject($shipmentData);
902 }
903
904
905 return $shipments;
906 }
907
908 protected static function getParametersForLoad($id) : array
909 {
910 return [
911 'filter' => [
912 'ORDER_ID' => $id
913 ],
914 'order' => [
915 'SYSTEM' => 'ASC',
916 'DATE_INSERT' => 'ASC',
917 'ID' => 'ASC'
918 ]
919 ];
920 }
921
932 public function save()
933 {
934 $this->checkCallingContext();
935
936 $result = new Result();
937
938 $id = $this->getId();
939 $this->isNew = ($this->getId() === 0);
940
941 $this->callEventOnBeforeEntitySaved();
942
943 if (!$this->isChanged())
944 {
945 return $result;
946 }
947
948 if ($id > 0)
949 {
950 $r = $this->update();
951 }
952 else
953 {
954 $r = $this->add();
955
956 if ($r->getId() > 0)
957 {
958 $id = $r->getId();
959 }
960 }
961
962 if (!$r->isSuccess())
963 {
964 $result->addErrors($r->getErrors());
965 return $result;
966 }
967
968 if ($id > 0)
969 {
970 $result->setId($id);
971
972 $controller = Internals\CustomFieldsController::getInstance();
973 $controller->save($this);
974 }
975
976 if (!$this->isSystem())
977 {
978 $this->saveExtraServices();
979 $this->saveStoreId();
980 }
981
982 $this->callEventOnEntitySaved();
983
984 $this->callDelayedEvents();
985
986 $shipmentItemCollection = $this->getShipmentItemCollection();
987 $r = $shipmentItemCollection->save();
988 if (!$r->isSuccess())
989 {
990 $result->addErrors($r->getErrors());
991 return $result;
992 }
993
994 if (!$this->isSystem())
995 {
996 $registry = Registry::getInstance(static::getRegistryType());
997
999 $orderHistory = $registry->getOrderHistoryClassName();
1000 $orderHistory::collectEntityFields('SHIPMENT', $this->getParentOrderId(), $id);
1001 }
1002
1005
1007 $res = $propertyCollection->save();
1008 if (!$res->isSuccess())
1009 {
1010 $result->addWarnings($res->getErrors());
1011 }
1012
1013 $this->onAfterSave($this->isNew);
1014
1015 $this->isNew = false;
1016
1017 return $result;
1018 }
1019
1023 private function checkCallingContext()
1024 {
1025 $order = $this->getOrder();
1026
1027 if (!$order->isSaveRunning())
1028 {
1029 trigger_error("Incorrect call to the save process. Use method save() on \Bitrix\Sale\Order entity", E_USER_WARNING);
1030 }
1031 }
1032
1041 private function add()
1042 {
1043 $result = new Result();
1044
1045 $registry = Registry::getInstance(static::getRegistryType());
1046
1047 $this->setFieldNoDemand('ORDER_ID', $this->getParentOrderId());
1048
1049 $r = static::addInternal($this->getFields()->getValues());
1050 if (!$r->isSuccess())
1051 {
1053 $orderHistory = $registry->getOrderHistoryClassName();
1054
1055 $orderHistory::addAction(
1056 'SHIPMENT',
1057 $this->getParentOrderId(),
1058 'SHIPMENT_ADD_ERROR',
1059 null,
1060 $this,
1061 ["ERROR" => $r->getErrorMessages()]
1062 );
1063
1064 $result->addErrors($r->getErrors());
1065 return $result;
1066 }
1067
1068 $id = $r->getId();
1069 $this->setFieldNoDemand('ID', $id);
1070 $result->setId($id);
1071
1072 $this->setAccountNumber($id);
1073
1074 if (!$this->isSystem())
1075 {
1077 $orderHistory = $registry->getOrderHistoryClassName();
1078
1079 $orderHistory::addAction(
1080 'SHIPMENT',
1081 $this->getParentOrderId(),
1082 'SHIPMENT_ADDED',
1083 $id,
1084 $this
1085 );
1086 }
1087
1088 return $result;
1089 }
1090
1095 private function update()
1096 {
1097 $result = new Result();
1098
1099 $registry = Registry::getInstance(static::getRegistryType());
1100
1101 $this->setDeliveryRequestMarker();
1102
1103 $r = static::updateInternal($this->getId(), $this->getFields()->getChangedValues());
1104 if (!$r->isSuccess())
1105 {
1107 $orderHistory = $registry->getOrderHistoryClassName();
1108
1109 $orderHistory::addAction(
1110 'SHIPMENT',
1111 $this->getParentOrderId(),
1112 'SHIPMENT_UPDATE_ERROR',
1113 $this->getId(),
1114 $this,
1115 ["ERROR" => $r->getErrorMessages()]
1116 );
1117
1118 $result->addErrors($r->getErrors());
1119 }
1120
1121 return $result;
1122 }
1123
1127 protected function setDeliveryRequestMarker()
1128 {
1129 $order = $this->getParentOrder();
1130
1132 }
1133
1139 private function callDelayedEvents()
1140 {
1141 $eventList = Internals\EventsPool::getEvents('s'.$this->getInternalIndex());
1142
1143 if ($eventList)
1144 {
1145 foreach ($eventList as $eventName => $eventData)
1146 {
1147 $event = new Main\Event('sale', $eventName, $eventData);
1148 $event->send();
1149
1150 $registry = Registry::getInstance(static::getRegistryType());
1151
1153 $notifyClassName = $registry->getNotifyClassName();
1154 $notifyClassName::callNotify($this, $eventName);
1155 }
1156
1157 Internals\EventsPool::resetEvents('s'.$this->getInternalIndex());
1158 }
1159 }
1160
1164 private function callEventOnBeforeEntitySaved()
1165 {
1167 $event = new Main\Event('sale', 'OnBeforeSaleShipmentEntitySaved', [
1168 'ENTITY' => $this,
1169 'VALUES' => $this->fields->getOriginalValues()
1170 ]);
1171
1172 $event->send();
1173 }
1174
1178 private function callEventOnEntitySaved()
1179 {
1181 $event = new Main\Event('sale', 'OnSaleShipmentEntitySaved', [
1182 'ENTITY' => $this,
1183 'VALUES' => $this->fields->getOriginalValues(),
1184 'IS_NEW' => $this->isNew,
1185 ]);
1186
1187 $event->send();
1188 }
1189
1194 protected function onAfterSave($isNew)
1195 {
1196 if (
1197 $this->getFields()->isChanged('DEDUCTED')
1198 && (!$isNew || $this->isShipped())
1199 )
1200 {
1202 }
1203 }
1204
1208 public function getParentOrderId()
1209 {
1210 $order = $this->getParentOrder();
1211 if (!$order)
1212 {
1213 return false;
1214 }
1215
1216 return $order->getId();
1217 }
1218
1222 public function getOrder()
1223 {
1224 return $this->getCollection()->getOrder();
1225 }
1226
1233 public function getShipmentItemCollection()
1234 {
1235 if (empty($this->shipmentItemCollection))
1236 {
1237 $registry = Registry::getInstance(static::getRegistryType());
1238
1240 $itemCollectionClassName = $registry->getShipmentItemCollectionClassName();
1241 $this->shipmentItemCollection = $itemCollectionClassName::load($this);
1242 }
1243
1245 }
1246
1251 protected function markSystem()
1252 {
1253 $this->setFieldNoDemand("SYSTEM", 'Y');
1254 }
1255
1265 public static function createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService = null)
1266 {
1267 $shipment = static::create($collection, $deliveryService);
1268 $shipment->markSystem();
1269
1270 if ($deliveryService === null)
1271 {
1272 $shipment->setFieldNoDemand('DELIVERY_ID', Delivery\Services\Manager::getEmptyDeliveryServiceId());
1273 }
1274
1275 return $shipment;
1276 }
1277
1281 public function getPrice()
1282 {
1283 return (float)$this->getField('PRICE_DELIVERY');
1284 }
1285
1290 public function isCustomPrice()
1291 {
1292 return $this->isMarkedFieldCustom('PRICE_DELIVERY');
1293 }
1294
1295 protected function isPriceField(string $name) : bool
1296 {
1297 return
1298 $name === 'BASE_PRICE_DELIVERY'
1299 || $name === 'PRICE_DELIVERY'
1300 || $name === 'DISCOUNT_PRICE'
1301 ;
1302 }
1303
1307 public function getCurrency()
1308 {
1309 return (string)$this->getField('CURRENCY');
1310 }
1311
1315 public function getDeliveryId()
1316 {
1317 return (int)$this->getField('DELIVERY_ID');
1318 }
1319
1323 public function getDeliveryName()
1324 {
1325 return (string)$this->getField('DELIVERY_NAME');
1326 }
1327
1331 public function setOrderId($orderId)
1332 {
1333 $this->setField('ORDER_ID', $orderId);
1334 }
1335
1341 public function getDelivery()
1342 {
1343 if ($this->service === null)
1344 {
1345 $this->service = $this->loadDeliveryService();
1346 }
1347
1348 return $this->service;
1349 }
1350
1356 protected function loadDeliveryService()
1357 {
1358 if ($deliveryId = $this->getDeliveryId())
1359 {
1360 return Delivery\Services\Manager::getObjectById($deliveryId);
1361 }
1362
1363 return null;
1364 }
1365
1366
1370 public function isSystem()
1371 {
1372 return $this->getField('SYSTEM') === 'Y';
1373 }
1374
1376 public function isCanceled()
1377 {
1378 return $this->getField('CANCELED') === 'Y';
1379 }
1380
1384 public function isShipped()
1385 {
1386 return $this->getField('DEDUCTED') === 'Y';
1387 }
1388
1392 public function getShippedDate()
1393 {
1394 return $this->getField('DATE_DEDUCTED');
1395 }
1396
1400 public function getShippedUserId()
1401 {
1402 return $this->getField('EMP_DEDUCTED_ID');
1403 }
1404
1408 public function getUnshipReason()
1409 {
1410 return (string)$this->getField('REASON_UNDO_DEDUCTED');
1411 }
1412
1416 public function isMarked()
1417 {
1418 return $this->getField('MARKED') === "Y";
1419 }
1420
1427 public function isReserved()
1428 {
1430 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1431 {
1432 if ($shipmentItem->getReservedQuantity() !== $shipmentItem->getQuantity())
1433 {
1434 return false;
1435 }
1436 }
1437
1438 return true;
1439 }
1440
1444 public function isAllowDelivery()
1445 {
1446 return $this->getField('ALLOW_DELIVERY') === "Y";
1447 }
1448
1452 public function isEmpty()
1453 {
1454 return $this->getShipmentItemCollection()->isEmpty();
1455 }
1456
1460 public function getAllowDeliveryDate()
1461 {
1462 return $this->getField('DATE_ALLOW_DELIVERY');
1463 }
1464
1468 public function getAllowDeliveryUserId()
1469 {
1470 return (int)$this->getField('EMP_ALLOW_DELIVERY_ID');
1471 }
1472
1476 public function getCompanyId()
1477 {
1478 return (int)$this->getField('COMPANY_ID');
1479 }
1480
1487 public function tryReserve()
1488 {
1489 return Internals\Catalog\Provider::tryReserveShipment($this);
1490 }
1491
1497 public function tryUnreserve()
1498 {
1499 return Internals\Catalog\Provider::tryUnreserveShipment($this);
1500 }
1501
1507 public function tryShip()
1508 {
1509 $result = new Result();
1510
1512 $r = Internals\Catalog\Provider::tryShipShipment($this);
1513 if ($r->isSuccess())
1514 {
1515 $resultList = $r->getData();
1516
1517 if (!empty($resultList) && is_array($resultList))
1518 {
1520 foreach ($resultList as $resultDat)
1521 {
1522 if (!$resultDat->isSuccess())
1523 {
1524 $result->addErrors( $resultDat->getErrors() );
1525 }
1526 }
1527 }
1528 }
1529 else
1530 {
1531 $result->addErrors( $r->getErrors() );
1532 }
1533
1534 if ($r->hasWarnings())
1535 {
1536 $result->addWarnings( $r->getWarnings() );
1537 }
1538 return $result;
1539 }
1540
1546 public function tryUnship()
1547 {
1548 return $this->tryShip();
1549 }
1550
1554 public function needShip()
1555 {
1556 if ($this->fields->isChanged('DEDUCTED'))
1557 {
1558 if ($this->getField('DEDUCTED') === "Y")
1559 {
1560 return true;
1561 }
1562 elseif ($this->getField('DEDUCTED') === "N" && $this->getId() != 0)
1563 {
1564 return false;
1565 }
1566 }
1567
1568 return null;
1569 }
1570
1575 public function deliver()
1576 {
1577 $result = Internals\Catalog\Provider::deliver($this);
1578 if ($result->isSuccess())
1579 {
1580 Recurring::repeat($this->getOrder(), $result->getData());
1581 }
1582
1583 return $result;
1584 }
1585
1591 public function allowDelivery()
1592 {
1593 return $this->setField('ALLOW_DELIVERY', "Y");
1594 }
1595
1601 public function disallowDelivery()
1602 {
1603 return $this->setField('ALLOW_DELIVERY', "N");
1604 }
1605
1616 public function onBeforeBasketItemDelete(BasketItem $basketItem)
1617 {
1618 $result = new Result();
1619
1620 $shipmentItemCollection = $this->getShipmentItemCollection();
1621 $r = $shipmentItemCollection->onBeforeBasketItemDelete($basketItem);
1622 if (!$r->isSuccess())
1623 {
1624 return $result->addErrors($r->getErrors());
1625 }
1626
1627 if ($this->isSystem())
1628 {
1629 return $this->syncQuantityAfterModify($basketItem);
1630 }
1631
1632 return $result;
1633 }
1634
1649 public function onBasketModify($action, BasketItem $basketItem, $name = null, $oldValue = null, $value = null)
1650 {
1651 $result = new Result();
1652
1653 if ($action === EventActions::ADD)
1654 {
1655 if (!$this->isSystem())
1656 {
1657 return $result;
1658 }
1659
1660 return $this->getShipmentItemCollection()->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1661 }
1663 {
1664 if ($name === "QUANTITY")
1665 {
1666 if ($this->isSystem())
1667 {
1668 return $this->syncQuantityAfterModify($basketItem, $value, $oldValue);
1669 }
1670
1672 $shipmentItemCollection = $this->getShipmentItemCollection();
1673
1674 $r = $shipmentItemCollection->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1675 if ($r->isSuccess())
1676 {
1677 if (!$this->isCustomPrice())
1678 {
1679 $deliveryCalculate = $this->calculateDelivery();
1680 if ($deliveryCalculate->isSuccess())
1681 {
1682 $this->setField('BASE_PRICE_DELIVERY', $deliveryCalculate->getPrice());
1683 }
1684 else
1685 {
1686 $result->addWarnings($deliveryCalculate->getErrors());
1687 }
1688 }
1689 }
1690 else
1691 {
1692 $result->addErrors($r->getErrors());
1693 }
1694 }
1695 elseif ($name === 'WEIGHT')
1696 {
1697 if (!$this->isMarkedFieldCustom('WEIGHT'))
1698 {
1699 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1700 {
1701 $this->setField('WEIGHT', $this->getShipmentItemCollection()->getWeight());
1702 }
1703 }
1704 }
1705 elseif ($name === 'PRICE')
1706 {
1707 if (!$this->isCustomPrice())
1708 {
1709 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1710 {
1711 $r = $this->calculateDelivery();
1712 if ($r->isSuccess())
1713 {
1714 $this->setField('BASE_PRICE_DELIVERY', $r->getPrice());
1715 }
1716 else
1717 {
1718 $result->addErrors($r->getErrors());
1719 }
1720 }
1721 }
1722 }
1723 }
1724
1725 return $result;
1726 }
1727
1740 protected function onFieldModify($name, $oldValue, $value)
1741 {
1742 global $USER;
1743
1744 $result = new Result();
1745
1746 if ($name === 'DELIVERY_ID')
1747 {
1748 if (
1749 $value > 0
1750 && (
1751 $this->service === null
1752 || $this->service->getId() !== (int)$value
1753 )
1754 )
1755 {
1756 $service = Delivery\Services\Manager::getObjectById($value);
1757 if ($service)
1758 {
1759 $this->service = $service;
1760
1761 $this->setField('DELIVERY_NAME', $this->service->getName());
1762 }
1763 }
1764
1765 $this->getPropertyCollection()->refreshRelated();
1766 }
1767 elseif ($name === "MARKED")
1768 {
1769 if ($oldValue != "Y")
1770 {
1771 $this->setField('DATE_MARKED', new Main\Type\DateTime());
1772
1773 if (is_object($USER))
1774 {
1775 $this->setField('EMP_MARKED_ID', $USER->GetID());
1776 }
1777 }
1778 elseif ($value === "N")
1779 {
1780 $this->setField('REASON_MARKED', '');
1781 }
1782 }
1783 elseif ($name === "ALLOW_DELIVERY")
1784 {
1785 $this->setField('DATE_ALLOW_DELIVERY', new Main\Type\DateTime());
1786
1787 if (is_object($USER))
1788 {
1789 $this->setField('EMP_ALLOW_DELIVERY_ID', $USER->GetID());
1790 }
1791
1792 if ($oldValue === 'N')
1793 {
1794 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_allow_delivery', '');
1795
1796 $registry = Registry::getInstance(static::getRegistryType());
1798 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1799
1800 if (
1801 $shipmentStatus !== ''
1802 && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus()
1803 )
1804 {
1805 $r = $this->setStatus($shipmentStatus);
1806 if (!$r->isSuccess())
1807 {
1808 $result->addErrors($r->getErrors());
1809 }
1810 }
1811 }
1812
1813 Internals\EventsPool::addEvent(
1814 's'.$this->getInternalIndex(),
1816 [
1817 'ENTITY' => $this,
1818 'VALUES' => $this->fields->getOriginalValues()
1819 ]
1820 );
1821 }
1822 elseif ($name === "DEDUCTED")
1823 {
1824 $this->setField('DATE_DEDUCTED', new Main\Type\DateTime());
1825
1826 if (is_object($USER))
1827 {
1828 $this->setField('EMP_DEDUCTED_ID', $USER->GetID());
1829 }
1830
1831 if ($oldValue === 'N')
1832 {
1833 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_shipped', '');
1834
1835 $registry = Registry::getInstance(static::getRegistryType());
1837 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1838
1839 if (strval($shipmentStatus) != '' && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus())
1840 {
1841 $r = $this->setStatus($shipmentStatus);
1842 if (!$r->isSuccess())
1843 {
1844 $result->addErrors($r->getErrors());
1845 }
1846 }
1847 }
1848
1849 if ($value === 'Y')
1850 {
1852 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1853 {
1854 $r = $shipmentItem->checkMarkingCodeOnDeducted();
1855 if (!$r->isSuccess())
1856 {
1857 $result->addErrors($r->getErrors());
1858 }
1859 }
1860 }
1861
1862 Internals\EventsPool::addEvent(
1863 's'.$this->getInternalIndex(),
1865 [
1866 'ENTITY' => $this,
1867 'VALUES' => $this->fields->getOriginalValues()
1868 ]
1869 );
1870
1871 Cashbox\Internals\Pool::addDoc($this->getOrder()->getInternalId(), $this);
1872 }
1873 elseif ($name === "STATUS_ID")
1874 {
1875 $event = new Main\Event(
1876 'sale',
1878 [
1879 'ENTITY' => $this,
1880 'VALUE' => $value,
1881 'OLD_VALUE' => $oldValue,
1882 ]
1883 );
1884 $event->send();
1885
1886 Internals\EventsPool::addEvent(
1887 's'.$this->getInternalIndex(),
1889 [
1890 'ENTITY' => $this,
1891 'VALUE' => $value,
1892 'OLD_VALUE' => $oldValue,
1893 ]
1894 );
1895
1896 Internals\EventsPool::addEvent(
1897 's'.$this->getInternalIndex(),
1899 [
1900 'ENTITY' => $this,
1901 'VALUE' => $value,
1902 'OLD_VALUE' => $oldValue,
1903 ]
1904 );
1905 }
1906 elseif ($name === 'RESPONSIBLE_ID')
1907 {
1908 $this->setField('DATE_RESPONSIBLE_ID', new Main\Type\DateTime());
1909 }
1910 elseif ($name === 'TRACKING_NUMBER')
1911 {
1912 if ($value)
1913 {
1914 Internals\EventsPool::addEvent(
1915 's'.$this->getInternalIndex(),
1917 [
1918 'ENTITY' => $this,
1919 'VALUES' => $this->getFields()->getOriginalValues(),
1920 ]
1921 );
1922 }
1923 }
1924
1925 $r = parent::onFieldModify($name, $oldValue, $value);
1926 if (!$r->isSuccess())
1927 {
1928 return $result->addErrors($r->getErrors());
1929 }
1930
1931 if (
1932 $name === 'BASE_PRICE_DELIVERY'
1933 && !$this->isMarkedFieldCustom('PRICE_DELIVERY')
1934 )
1935 {
1936 $value -= $this->getField('DISCOUNT_PRICE');
1937
1938 $r = $this->setField('PRICE_DELIVERY', $value);
1939 if (!$r->isSuccess())
1940 {
1941 $result->addErrors($r->getErrors());
1942 }
1943 }
1944
1945 if ($r->hasWarnings())
1946 {
1947 $result->addWarnings($r->getWarnings());
1948 }
1949
1950 $result->addData($r->getData());
1951
1952 if ($result->isSuccess())
1953 {
1954 $this->setFieldNoDemand('DATE_UPDATE', new Main\Type\DateTime());
1955 }
1956
1957 return $result;
1958 }
1959
1973 protected function syncQuantityAfterModify(BasketItem $basketItem, $value = null, $oldValue = null)
1974 {
1975 $result = new Result();
1976
1978 $shipmentItemCollection = $this->getShipmentItemCollection();
1979
1980 $shipmentItem = $shipmentItemCollection->getItemByBasketCode($basketItem->getBasketCode());
1981
1982 if ($value === 0)
1983 {
1984 if ($shipmentItem !== null)
1985 {
1986 $shipmentItem->setFieldNoDemand('QUANTITY', 0);
1987 }
1988
1989 return $result;
1990 }
1991
1992 if ($shipmentItem === null)
1993 {
1994 $shipmentItem = $shipmentItemCollection->createItem($basketItem);
1995 }
1996
1997 $deltaQuantity = $value - $oldValue;
1998
1999 if ($deltaQuantity > 0) // plus
2000 {
2001 $shipmentItem->setFieldNoDemand(
2002 "QUANTITY",
2003 $shipmentItem->getField("QUANTITY") + $deltaQuantity
2004 );
2005
2006 if (
2008 && $this->needReservation()
2009 )
2010 {
2011 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
2012 }
2013 }
2014 else // minus
2015 {
2016 if (floatval($shipmentItem->getField("QUANTITY")) <= 0)
2017 {
2018 return new Result();
2019 }
2020
2021 if ($value != 0 && roundEx($shipmentItem->getField("QUANTITY"), SALE_VALUE_PRECISION) < roundEx(-$deltaQuantity, SALE_VALUE_PRECISION))
2022 {
2023 $result->addError(
2024 new ResultError(
2025 str_replace(
2026 array("#NAME#", "#QUANTITY#", "#DELTA_QUANTITY#"),
2027 array($basketItem->getField("NAME"), $shipmentItem->getField("QUANTITY"), abs($deltaQuantity)),
2028 Loc::getMessage('SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR')
2029 ),
2030 'SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR'
2031 )
2032 );
2033 return $result;
2034 }
2035
2036 if ($value > 0)
2037 {
2038 $shipmentItem->setFieldNoDemand(
2039 "QUANTITY",
2040 $shipmentItem->getField("QUANTITY") + $deltaQuantity
2041 );
2042
2043 if (
2045 && $this->needReservation()
2046 )
2047 {
2048 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
2049 }
2050 }
2051
2052 }
2053
2054 return $result;
2055 }
2056
2060 public function getServiceParams()
2061 {
2062 $params = $this->getField('PARAMS');
2063 return isset($params["SERVICE_PARAMS"]) ? $params["SERVICE_PARAMS"] : array();
2064 }
2065
2070 public function setServiceParams(array $serviceParams)
2071 {
2072 $params = $this->getField('PARAMS');
2073 $params["SERVICE_PARAMS"] = $serviceParams;
2074 $this->setField("PARAMS", $params);
2075 }
2076
2080 public function getExtraServices()
2081 {
2082 if ($this->extraServices === null)
2083 {
2085 $this->getId(),
2086 $this->getDeliveryId()
2087 );
2088 }
2089
2090 return $this->extraServices;
2091 }
2092
2094 {
2095 $currentExtraServices = $this->getExtraServices();
2096 if (
2097 !empty(array_diff_assoc($currentExtraServices, $extraServices))
2098 || !empty(array_diff_assoc($extraServices, $currentExtraServices))
2099 )
2100 {
2101 $this->areExtraServicesChanged = true;
2102 }
2103
2104 $this->extraServices = $extraServices;
2105 }
2106
2110 public function getExtraServicesObjects()
2111 {
2112 return Delivery\ExtraServices\Manager::getObjectsForShipment(
2113 $this->getId(),
2114 $this->getDeliveryId(),
2115 $this->getCurrency()
2116 );
2117 }
2118
2122 protected function saveExtraServices()
2123 {
2125 }
2126
2130 public function getStoreId()
2131 {
2132 if ($this->storeId === null)
2133 {
2135 $this->getId(),
2136 $this->getDeliveryId()
2137 );
2138 }
2139
2140 return $this->storeId;
2141 }
2142
2143 public function setStoreId(int $storeId)
2144 {
2145 if ($storeId !== $this->getStoreId())
2146 {
2147 $this->isStoreIdChanged = true;
2148 }
2149
2150 $this->storeId = $storeId;
2151 }
2152
2156 protected function saveStoreId()
2157 {
2158 return Delivery\ExtraServices\Manager::saveStoreIdForShipment($this->getId(), $this->getDeliveryId(), $this->getStoreId());
2159 }
2160
2164 public function getWeight() : float
2165 {
2166 return (float)$this->getField('WEIGHT');
2167 }
2168
2173 public function setWeight(float $weight)
2174 {
2175 return $this->setField('WEIGHT', $weight);
2176 }
2177
2182 public function calculateDelivery()
2183 {
2184 if ($this->isSystem())
2185 {
2186 throw new Main\NotSupportedException();
2187 }
2188
2189 if ($this->getDeliveryId() === 0)
2190 {
2191 return new Delivery\CalculationResult();
2192 }
2193
2195 }
2196
2201 public function resetData()
2202 {
2203 if (!$this->isCustomPrice())
2204 {
2205 $this->setField('BASE_PRICE_DELIVERY', 0);
2206 }
2207 }
2208
2216 public function getBasketItemQuantity(BasketItem $basketItem)
2217 {
2219 $shipmentItemCollection = $this->getShipmentItemCollection();
2220
2221 return $shipmentItemCollection->getBasketItemQuantity($basketItem);
2222 }
2223
2230 protected function addChangesToHistory($name, $oldValue = null, $value = null)
2231 {
2232 if ($this->getId() > 0 && !$this->isSystem())
2233 {
2234 $order = $this->getOrder();
2235
2236 if ($order && $order->getId() > 0)
2237 {
2238 $registry = Registry::getInstance(static::getRegistryType());
2239
2241 $orderHistory = $registry->getOrderHistoryClassName();
2242 $orderHistory::addField(
2243 'SHIPMENT',
2244 $order->getId(),
2245 $name,
2246 $oldValue,
2247 $value,
2248 $this->getId(),
2249 $this
2250 );
2251 }
2252 }
2253 }
2254
2261 public function isExistBasketItem(BasketItem $basketItem)
2262 {
2264 if (!$shipmentItemCollection = $this->getShipmentItemCollection())
2265 {
2266 throw new Main\ObjectNotFoundException('Entity "ShipmentItemCollection" not found');
2267 }
2268
2269 return $shipmentItemCollection->isExistBasketItem($basketItem);
2270 }
2271
2278 public function verify()
2279 {
2280 $result = new Result();
2281
2282 if ($this->getDeliveryId() <= 0)
2283 {
2284 $result->addError(
2285 new ResultError(
2286 Loc::getMessage("SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY"),
2287 'SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY'
2288 )
2289 );
2290 }
2291
2293 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2294 {
2296 foreach ($shipmentItemCollection as $shipmentItem)
2297 {
2298 $r = $shipmentItem->verify();
2299 if (!$r->isSuccess())
2300 {
2301 $result->addErrors($r->getErrors());
2302 }
2303 }
2304 }
2305
2306 return $result;
2307 }
2308
2316 public function setAccountNumber($id)
2317 {
2318 $result = new Result();
2319
2320 $id = intval($id);
2321 if ($id <= 0)
2322 {
2323 $result->addError(new ResultError(Loc::getMessage('SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'), 'SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'));
2324 return $result;
2325 }
2326
2327 $value = Internals\AccountNumberGenerator::generateForShipment($this);
2328
2329 try
2330 {
2332 $r = static::updateInternal($id, array("ACCOUNT_NUMBER" => $value));
2333 $res = $r->isSuccess(true);
2334 }
2335 catch (Main\DB\SqlQueryException $exception)
2336 {
2337 $res = false;
2338 }
2339
2340 if ($res)
2341 {
2342 $this->setFieldNoDemand('ACCOUNT_NUMBER', $value);
2343 }
2344
2345 return $result;
2346 }
2347
2352 public function getBusinessValueProviderInstance($mapping)
2353 {
2354 $providerInstance = null;
2355
2356 if (is_array($mapping) && isset($mapping['PROVIDER_KEY']))
2357 {
2358 switch ($mapping['PROVIDER_KEY'])
2359 {
2360 case 'SHIPMENT': $providerInstance = $this; break;
2361 case 'COMPANY' : $providerInstance = $this->getField('COMPANY_ID'); break;
2362 default:
2363 $order = $this->getOrder();
2364 if ($order)
2365 {
2366 $providerInstance = $order->getBusinessValueProviderInstance($mapping);
2367 }
2368 }
2369 }
2370
2371 return $providerInstance;
2372 }
2373
2377 public function getPersonTypeId()
2378 {
2379 $order = $this->getOrder();
2380 if ($order)
2381 {
2382 return $order->getPersonTypeId();
2383 }
2384
2385 return null;
2386 }
2387
2395 public static function getList(array $parameters)
2396 {
2397 return Internals\ShipmentTable::getList($parameters);
2398 }
2399
2410 public function createClone(\SplObjectStorage $cloneEntity)
2411 {
2412 if ($this->isClone() && $cloneEntity->contains($this))
2413 {
2414 return $cloneEntity[$this];
2415 }
2416
2418 $shipmentClone = parent::createClone($cloneEntity);
2419
2421 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2422 {
2423 if (!$cloneEntity->contains($shipmentItemCollection))
2424 {
2425 $cloneEntity[$shipmentItemCollection] = $shipmentItemCollection->createClone($cloneEntity);
2426 }
2427
2428 if ($cloneEntity->contains($shipmentItemCollection))
2429 {
2430 $shipmentClone->shipmentItemCollection = $cloneEntity[$shipmentItemCollection];
2431 }
2432 }
2433
2435 if ($service = $this->getDelivery())
2436 {
2437 if (!$cloneEntity->contains($service))
2438 {
2439 $cloneEntity[$service] = $service->createClone($cloneEntity);
2440 }
2441
2442 if ($cloneEntity->contains($service))
2443 {
2444 $shipmentClone->service = $cloneEntity[$service];
2445 }
2446 }
2447
2448 return $shipmentClone;
2449 }
2450
2459 protected function setStatus($status)
2460 {
2461 global $USER;
2462
2463 $result = new Result();
2464
2465 $registry = Registry::getInstance(static::getRegistryType());
2467 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
2468
2469 if (is_object($USER) && $USER->isAuthorized())
2470 {
2471 $statusesList = $deliveryStatusClassName::getAllowedUserStatuses($USER->getID(), $this->getField('STATUS_ID'));
2472 }
2473 else
2474 {
2475 $statusesList = $deliveryStatusClassName::getAllStatuses();
2476 }
2477
2478 if($this->getField('STATUS_ID') != $status && array_key_exists($status, $statusesList))
2479 {
2481 $r = $this->setField('STATUS_ID', $status);
2482 if (!$r->isSuccess())
2483 {
2484 $result->addErrors($r->getErrors());
2485 return $result;
2486 }
2487 }
2488
2489 return $result;
2490 }
2491
2497 public function getErrorEntity($value)
2498 {
2499 $className = null;
2500 $errorsList = static::getAutoFixErrorsList();
2501 if (is_array($errorsList) && in_array($value, $errorsList))
2502 {
2503 $className = static::getClassName();
2504 }
2505 else
2506 {
2508 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2509 {
2510 $className = $shipmentItemCollection->getErrorEntity($value);
2511 }
2512 }
2513
2514 return $className;
2515 }
2516
2522 public function canAutoFixError($value)
2523 {
2524 $autoFix = false;
2525 $errorsList = static::getAutoFixErrorsList();
2526 if (is_array($errorsList) && in_array($value, $errorsList))
2527 {
2528 $autoFix = true;
2529 }
2530 else
2531 {
2533 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2534 {
2535 $autoFix = $shipmentItemCollection->canAutoFixError($value);
2536 }
2537 }
2538
2539 return $autoFix;
2540 }
2541
2545 public function getAutoFixErrorsList()
2546 {
2547 return array_keys(static::getAutoFixRules());
2548 }
2549
2555 public function tryFixError($code)
2556 {
2557 $result = new Result();
2558
2559 $method = static::getFixMethod($code);
2560 $r = call_user_func_array($method, array($this));
2561 if (!$r->isSuccess())
2562 {
2563 $result->addErrors($r->getErrors());
2564 }
2565 elseif ($r->hasWarnings())
2566 {
2567 $result->addWarnings($r->getWarnings());
2568 }
2569
2570 return $result;
2571 }
2572
2577 protected static function getFixMethod($code)
2578 {
2579 $codeList = static::getAutoFixRules();
2580
2581 if (!empty($codeList[$code]))
2582 {
2583 return $codeList[$code];
2584 }
2585 return null;
2586 }
2587
2594 public static function fixReserveErrors(Shipment $entity)
2595 {
2596 $result = new Result();
2597
2598 $r = $entity->tryReserve();
2599 if (!$r->isSuccess())
2600 {
2601 $result->addErrors($r->getErrors());
2602 }
2603 elseif ($r->hasWarnings())
2604 {
2605 $result->addWarnings($r->getWarnings());
2606 }
2607
2608 return $result;
2609 }
2610
2617 public static function fixShipErrors(Shipment $entity)
2618 {
2619 $result = new Result();
2620
2621 $r = $entity->setField('DEDUCTED', 'Y');
2622 if (!$r->isSuccess())
2623 {
2624 if (!$r->isSuccess())
2625 {
2626 $result->addErrors($r->getErrors());
2627 }
2628 }
2629
2630 $r = $entity->tryShip();
2631 if (!$r->isSuccess())
2632 {
2633 if (!$r->isSuccess())
2634 {
2635 $result->addErrors($r->getErrors());
2636 }
2637 }
2638
2639 return $result;
2640 }
2641
2645 protected static function getAutoFixRules()
2646 {
2647 return [
2648 'PROVIDER_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2649 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2650 'PROVIDER_UNRESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2651 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2652
2653 'SALE_PROVIDER_SHIPMENT_SHIPPED_LESS_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2654 'SALE_PROVIDER_SHIPMENT_SHIPPED_MORE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2655 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2656 'SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2657 'DDCT_DEDUCTION_QUANTITY_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2658 ];
2659 }
2660
2664 public function canMarked()
2665 {
2666 return true;
2667 }
2668
2672 public function getMarkField()
2673 {
2674 return 'MARKED';
2675 }
2676
2680 public function isChanged()
2681 {
2682 if (parent::isChanged())
2683 {
2684 return true;
2685 }
2686
2687 return (
2688 $this->getShipmentItemCollection()->isChanged()
2689 || $this->getPropertyCollection()->isChanged()
2690 || $this->isStoreIdChanged
2691 || $this->areExtraServicesChanged
2692 );
2693 }
2694
2698 public function clearChanged()
2699 {
2700 parent::clearChanged();
2701
2702 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2703 {
2705 foreach ($shipmentItemCollection as $shipmentItem)
2706 {
2707 $shipmentItem->clearChanged();
2708 }
2709 }
2710 }
2711
2720 public function getVatRate()
2721 {
2722 $vatRate = 0;
2723
2724 $service = $this->getDelivery();
2725 if ($service)
2726 {
2727 if (!Main\Loader::includeModule('catalog'))
2728 {
2729 return $vatRate;
2730 }
2731
2732 $vatId = $service->getVatId();
2733 if ($vatId <= 0)
2734 {
2735 return $vatRate;
2736 }
2737
2738 $dbRes = VatTable::getById($vatId);
2739 $vatInfo = $dbRes->fetch();
2740 if ($vatInfo)
2741 {
2742 $vatRate = $vatInfo['RATE'] / 100;
2743 }
2744 }
2745
2746 return $vatRate;
2747 }
2748
2754 public function getVatSum()
2755 {
2756 $vatRate = $this->getVatRate();
2757 $price = $this->getPrice() * $vatRate / (1 + $vatRate);
2758
2759 return PriceMaths::roundPrecision($price);
2760 }
2761
2767 protected function addInternal(array $data)
2768 {
2769 return Internals\ShipmentTable::add($data);
2770 }
2771
2778 protected function updateInternal($primary, array $data)
2779 {
2780 return Internals\ShipmentTable::update($primary, $data);
2781 }
2782
2789 protected static function deleteInternal($primary)
2790 {
2792 }
2793
2797 protected static function getFieldsMap()
2798 {
2800 }
2801
2805 public static function getUfId()
2806 {
2808 }
2809
2819 public function setBasePriceDelivery($value, $custom = false)
2820 {
2821 $result = new Result();
2822
2823 if ($custom === true)
2824 {
2825 $this->markFieldCustom('PRICE_DELIVERY');
2826 }
2827
2828 $r = $this->setField('BASE_PRICE_DELIVERY', $value);
2829 if (!$r->isSuccess())
2830 {
2831 $result->addErrors($r->getErrors());
2832 }
2833
2834 return $result;
2835 }
2836
2842 public static function getEntityEventName()
2843 {
2844 return 'SaleShipment';
2845 }
2846
2852 public function toArray() : array
2853 {
2854 $result = parent::toArray();
2855
2856 $result['ITEMS'] = $this->getShipmentItemCollection()->toArray();
2857 $result['PROPERTIES'] = $this->getPropertyCollection()->toArray();
2858
2859 return $result;
2860 }
2861
2863 {
2864 if(empty($this->propertyCollection))
2865 {
2866 $this->propertyCollection = $this->loadPropertyCollection();
2867 }
2868
2870 }
2871
2872 public function loadPropertyCollection(): ShipmentPropertyValueCollection
2873 {
2874 $registry = Registry::getInstance(static::getRegistryType());
2876 $propertyCollectionClassName = $registry->getShipmentPropertyValueCollectionClassName();
2877
2878 return $propertyCollectionClassName::load($this);
2879 }
2880
2886 public function getParentOrder()
2887 {
2888 return $this->getOrder();
2889 }
2890}
2891
static update($id, array $data)
Определения entity.php:229
static add(array $data)
Определения entity.php:150
Определения event.php:5
static isEnableAutomaticReservation()
Определения configuration.php:86
static getProductReservationCondition()
Определения configuration.php:78
static saveValuesForShipment($shipmentId, $extraServices)
Определения manager.php:315
static saveStoreIdForShipment($shipmentId, $deliveryId, $storeId)
Определения manager.php:463
static getStoreIdForShipment(int $shipmentId, int $deliveryId)
Определения manager.php:430
static getValuesForShipment(int $shipmentId, int $deliveryId)
Определения manager.php:283
static onBeforeShipmentDelete(&$shipment)
Определения manager.php:1275
static onBeforeShipmentSave(&$order, &$shipment)
Определения manager.php:1264
static getObjectById($deliveryId)
Определения manager.php:438
static calculateDeliveryPrice(Shipment $shipment, $deliveryId=0, $extraServices=array())
Определения manager.php:676
static getById($deliveryId)
Определения manager.php:53
const EVENT_ON_SHIPMENT_DEDUCTED
Определения eventactions.php:33
const ADD
Определения eventactions.php:8
const EVENT_ON_BEFORE_SHIPMENT_RESERVE
Определения eventactions.php:35
const EVENT_ON_BEFORE_SHIPMENT_STATUS_CHANGE
Определения eventactions.php:44
const EVENT_ON_SHIPMENT_TRACKING_NUMBER_CHANGE
Определения eventactions.php:31
const UPDATE
Определения eventactions.php:9
const EVENT_ON_SHIPMENT_ALLOW_DELIVERY
Определения eventactions.php:32
const EVENT_ON_SHIPMENT_STATUS_CHANGE_SEND_MAIL
Определения eventactions.php:46
const EVENT_ON_SHIPMENT_RESERVED
Определения eventactions.php:36
const EVENT_ON_SHIPMENT_STATUS_CHANGE
Определения eventactions.php:45
const ACTION_ENTITY_SHIPMENT_COLLECTION_RESERVED_QUANTITY
Определения actionentity.php:11
static changeProductBatchBalance(Sale\Shipment $shipment)
Определения provider.php:1146
static deliver(Sale\Shipment $shipment)
Определения provider.php:1104
onFieldModify($name, $oldValue, $value)
Определения collectableentity.php:29
static getEvents($code)
Определения eventspool.php:8
static add($code, $type, $value)
Определения poolbase.php:51
static getUfId()
Определения shipment.php:78
static deleteWithItems($id)
Определения shipment.php:89
static roundPrecision($value)
Определения pricemaths.php:16
static repeat(Order $order, array $resultList)
Определения recurring.php:26
static getInstance($type)
Определения registry.php:183
const ENTITY_SHIPMENT
Определения registry.php:19
const REGISTRY_TYPE_ORDER
Определения registry.php:16
getStoreId()
Определения shipment.php:2130
onAfterSave($isNew)
Определения shipment.php:1194
getMarkField()
Определения shipment.php:2672
getParentOrderId()
Определения shipment.php:1208
setDeliveryService(Delivery\Services\Base $service)
Определения shipment.php:173
getDeliveryName()
Определения shipment.php:1323
setBasePriceDelivery($value, $custom=false)
Определения shipment.php:2819
disallowDelivery()
Определения shipment.php:1601
getShipmentCode()
Определения shipment.php:56
deleteDeliveryRequest()
Определения shipment.php:759
setWeight(float $weight)
Определения shipment.php:2173
static deleteNoDemand($orderId)
Определения shipment.php:684
getExtraServicesObjects()
Определения shipment.php:2110
getAutoFixErrorsList()
Определения shipment.php:2545
static getRegistryEntity()
Определения shipment.php:48
updateInternal($primary, array $data)
Определения shipment.php:2778
getServiceParams()
Определения shipment.php:2060
getVatSum()
Определения shipment.php:2754
getParentOrder()
Определения shipment.php:2886
setServiceParams(array $serviceParams)
Определения shipment.php:2070
static getUfId()
Определения shipment.php:2805
tryReserve()
Определения shipment.php:1487
checkValueBeforeSet($name, $value)
Определения shipment.php:821
static getFixMethod($code)
Определения shipment.php:2577
getPrice()
Определения shipment.php:1281
getCompanyId()
Определения shipment.php:1476
getBusinessValueProviderInstance($mapping)
Определения shipment.php:2352
isChanged()
Определения shipment.php:2680
getAllowDeliveryUserId()
Определения shipment.php:1468
static getMeaningfulFields()
Определения shipment.php:162
tryUnship()
Определения shipment.php:1546
static getParametersForLoad($id)
Определения shipment.php:908
static getCustomizableFields()
Определения shipment.php:125
getUnshipReason()
Определения shipment.php:1408
static getAutoFixRules()
Определения shipment.php:2645
getOrder()
Определения shipment.php:1222
getCurrency()
Определения shipment.php:1307
static getFieldsMap()
Определения shipment.php:2797
$propertyCollection
Определения shipment.php:40
getPropertyCollection()
Определения shipment.php:2862
tryUnreserve()
Определения shipment.php:1497
$internalId
Определения shipment.php:35
static $idShipment
Определения shipment.php:37
tryFixError($code)
Определения shipment.php:2555
toArray()
Определения shipment.php:2852
needReservation()
Определения shipment.php:263
setStoreId(int $storeId)
Определения shipment.php:2143
saveExtraServices()
Определения shipment.php:2122
deliver()
Определения shipment.php:1575
onBeforeBasketItemDelete(BasketItem $basketItem)
Определения shipment.php:1616
bool $areExtraServicesChanged
Определения shipment.php:29
static loadForOrder($id)
Определения shipment.php:889
getPersonTypeId()
Определения shipment.php:2377
static getEntityEventName()
Определения shipment.php:2842
static createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService=null)
Определения shipment.php:1265
static getAvailableFields()
Определения shipment.php:76
static fixReserveErrors(Shipment $entity)
Определения shipment.php:2594
static getList(array $parameters)
Определения shipment.php:2395
isCanceled()
Определения shipment.php:1376
allowDelivery()
Определения shipment.php:1591
$shipmentItemCollection
Определения shipment.php:23
markSystem()
Определения shipment.php:1251
onBeforeSetFields(array $values)
Определения shipment.php:134
isSystem()
Определения shipment.php:1370
isPriceField(string $name)
Определения shipment.php:1295
needShip()
Определения shipment.php:1554
static fixShipErrors(Shipment $entity)
Определения shipment.php:2617
addInternal(array $data)
Определения shipment.php:2767
getWeight()
Определения shipment.php:2164
setFieldNoDemand($name, $value)
Определения shipment.php:866
resetData()
Определения shipment.php:2201
static generateXmlId()
Определения shipment.php:230
getDelivery()
Определения shipment.php:1341
$extraServices
Определения shipment.php:28
calculateDelivery()
Определения shipment.php:2182
$service
Определения shipment.php:26
static deleteInternal($primary)
Определения shipment.php:2789
getShippedDate()
Определения shipment.php:1392
setDeliveryRequestMarker()
Определения shipment.php:1127
bool $isStoreIdChanged
Определения shipment.php:32
isShipped()
Определения shipment.php:1384
getExtraServices()
Определения shipment.php:2080
getShippedUserId()
Определения shipment.php:1400
getAllowDeliveryDate()
Определения shipment.php:1460
setField($name, $value)
Определения shipment.php:792
isAllowDelivery()
Определения shipment.php:1444
isCustomPrice()
Определения shipment.php:1290
setOrderId($orderId)
Определения shipment.php:1331
normalizeValue($name, $value)
Определения shipment.php:764
saveStoreId()
Определения shipment.php:2156
static getRegistryType()
Определения shipment.php:251
setExtraServices(array $extraServices)
Определения shipment.php:2093
canMarked()
Определения shipment.php:2664
loadDeliveryService()
Определения shipment.php:1356
isEmpty()
Определения shipment.php:1452
$isNew
Определения shipment.php:43
isMarked()
Определения shipment.php:1416
getDeliveryId()
Определения shipment.php:1315
onShipmentItemCollectionModify($action, ShipmentItem $shipmentItem, $name=null, $oldValue=null, $value=null)
Определения shipment.php:623
getVatRate()
Определения shipment.php:2720
$storeId
Определения shipment.php:31
setFieldNoDemand($name, $value)
Определения shipmentitem.php:462
getReservedQuantity()
Определения shipmentitem.php:537
$data['IS_AVAILABLE']
Определения .description.php:13
$orderId
Определения payment.php:5
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
canAutoFixError($value)
getErrorEntity($value)
global $USER
Определения csv_new_run.php:40
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$status
Определения session.php:10
roundEx($value, $prec=0)
Определения tools.php:4635
$name
Определения menu_edit.php:35
$value
Определения Param.php:39
$order
Определения payment.php:8
$service
Определения payment.php:18
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$errorMsg
Определения refund.php:16
$custom
Определения z_payment_result.php:22
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
const SALE_VALUE_PRECISION
Определения include.php:46
if($siteCount > 1) $statusesList
Определения options.php:3473
$method
Определения index.php:27
$eventManager
Определения include.php:412
$action
Определения file_dialog.php:21
$dbRes
Определения yandex_detail.php:168
$fields
Определения yandex_run.php:501