Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
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 if (
1189 $this->getFields()->isChanged('DEDUCTED')
1190 && (!$isNew || $this->isShipped())
1191 )
1192 {
1193 Internals\Catalog\Provider::changeProductBatchBalance($this);
1194 }
1195 }
1196
1200 public function getParentOrderId()
1201 {
1202 $order = $this->getParentOrder();
1203 if (!$order)
1204 {
1205 return false;
1206 }
1207
1208 return $order->getId();
1209 }
1210
1214 public function getOrder()
1215 {
1216 return $this->getCollection()->getOrder();
1217 }
1218
1225 public function getShipmentItemCollection()
1226 {
1227 if (empty($this->shipmentItemCollection))
1228 {
1229 $registry = Registry::getInstance(static::getRegistryType());
1230
1232 $itemCollectionClassName = $registry->getShipmentItemCollectionClassName();
1233 $this->shipmentItemCollection = $itemCollectionClassName::load($this);
1234 }
1235
1237 }
1238
1243 protected function markSystem()
1244 {
1245 $this->setFieldNoDemand("SYSTEM", 'Y');
1246 }
1247
1257 public static function createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService = null)
1258 {
1259 $shipment = static::create($collection, $deliveryService);
1260 $shipment->markSystem();
1261
1262 if ($deliveryService === null)
1263 {
1264 $shipment->setFieldNoDemand('DELIVERY_ID', Delivery\Services\Manager::getEmptyDeliveryServiceId());
1265 }
1266
1267 return $shipment;
1268 }
1269
1273 public function getPrice()
1274 {
1275 return (float)$this->getField('PRICE_DELIVERY');
1276 }
1277
1282 public function isCustomPrice()
1283 {
1284 return $this->isMarkedFieldCustom('PRICE_DELIVERY');
1285 }
1286
1287 protected function isPriceField(string $name) : bool
1288 {
1289 return
1290 $name === 'BASE_PRICE_DELIVERY'
1291 || $name === 'PRICE_DELIVERY'
1292 || $name === 'DISCOUNT_PRICE'
1293 ;
1294 }
1295
1299 public function getCurrency()
1300 {
1301 return (string)$this->getField('CURRENCY');
1302 }
1303
1307 public function getDeliveryId()
1308 {
1309 return (int)$this->getField('DELIVERY_ID');
1310 }
1311
1315 public function getDeliveryName()
1316 {
1317 return (string)$this->getField('DELIVERY_NAME');
1318 }
1319
1323 public function setOrderId($orderId)
1324 {
1325 $this->setField('ORDER_ID', $orderId);
1326 }
1327
1333 public function getDelivery()
1334 {
1335 if ($this->service === null)
1336 {
1337 $this->service = $this->loadDeliveryService();
1338 }
1339
1340 return $this->service;
1341 }
1342
1348 protected function loadDeliveryService()
1349 {
1350 if ($deliveryId = $this->getDeliveryId())
1351 {
1352 return Delivery\Services\Manager::getObjectById($deliveryId);
1353 }
1354
1355 return null;
1356 }
1357
1358
1362 public function isSystem()
1363 {
1364 return $this->getField('SYSTEM') === 'Y';
1365 }
1366
1368 public function isCanceled()
1369 {
1370 return $this->getField('CANCELED') === 'Y';
1371 }
1372
1376 public function isShipped()
1377 {
1378 return $this->getField('DEDUCTED') === 'Y';
1379 }
1380
1384 public function getShippedDate()
1385 {
1386 return $this->getField('DATE_DEDUCTED');
1387 }
1388
1392 public function getShippedUserId()
1393 {
1394 return $this->getField('EMP_DEDUCTED_ID');
1395 }
1396
1400 public function getUnshipReason()
1401 {
1402 return (string)$this->getField('REASON_UNDO_DEDUCTED');
1403 }
1404
1408 public function isMarked()
1409 {
1410 return $this->getField('MARKED') === "Y";
1411 }
1412
1419 public function isReserved()
1420 {
1422 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1423 {
1424 if ($shipmentItem->getReservedQuantity() !== $shipmentItem->getQuantity())
1425 {
1426 return false;
1427 }
1428 }
1429
1430 return true;
1431 }
1432
1436 public function isAllowDelivery()
1437 {
1438 return $this->getField('ALLOW_DELIVERY') === "Y";
1439 }
1440
1444 public function isEmpty()
1445 {
1446 return $this->getShipmentItemCollection()->isEmpty();
1447 }
1448
1452 public function getAllowDeliveryDate()
1453 {
1454 return $this->getField('DATE_ALLOW_DELIVERY');
1455 }
1456
1460 public function getAllowDeliveryUserId()
1461 {
1462 return (int)$this->getField('EMP_ALLOW_DELIVERY_ID');
1463 }
1464
1468 public function getCompanyId()
1469 {
1470 return (int)$this->getField('COMPANY_ID');
1471 }
1472
1479 public function tryReserve()
1480 {
1481 return Internals\Catalog\Provider::tryReserveShipment($this);
1482 }
1483
1489 public function tryUnreserve()
1490 {
1491 return Internals\Catalog\Provider::tryUnreserveShipment($this);
1492 }
1493
1499 public function tryShip()
1500 {
1501 $result = new Result();
1502
1504 $r = Internals\Catalog\Provider::tryShipShipment($this);
1505 if ($r->isSuccess())
1506 {
1507 $resultList = $r->getData();
1508
1509 if (!empty($resultList) && is_array($resultList))
1510 {
1512 foreach ($resultList as $resultDat)
1513 {
1514 if (!$resultDat->isSuccess())
1515 {
1516 $result->addErrors( $resultDat->getErrors() );
1517 }
1518 }
1519 }
1520 }
1521 else
1522 {
1523 $result->addErrors( $r->getErrors() );
1524 }
1525
1526 if ($r->hasWarnings())
1527 {
1528 $result->addWarnings( $r->getWarnings() );
1529 }
1530 return $result;
1531 }
1532
1538 public function tryUnship()
1539 {
1540 return $this->tryShip();
1541 }
1542
1546 public function needShip()
1547 {
1548 if ($this->fields->isChanged('DEDUCTED'))
1549 {
1550 if ($this->getField('DEDUCTED') === "Y")
1551 {
1552 return true;
1553 }
1554 elseif ($this->getField('DEDUCTED') === "N" && $this->getId() != 0)
1555 {
1556 return false;
1557 }
1558 }
1559
1560 return null;
1561 }
1562
1567 public function deliver()
1568 {
1569 $result = Internals\Catalog\Provider::deliver($this);
1570 if ($result->isSuccess())
1571 {
1572 Recurring::repeat($this->getOrder(), $result->getData());
1573 }
1574
1575 return $result;
1576 }
1577
1583 public function allowDelivery()
1584 {
1585 return $this->setField('ALLOW_DELIVERY', "Y");
1586 }
1587
1593 public function disallowDelivery()
1594 {
1595 return $this->setField('ALLOW_DELIVERY', "N");
1596 }
1597
1608 public function onBeforeBasketItemDelete(BasketItem $basketItem)
1609 {
1610 $result = new Result();
1611
1612 $shipmentItemCollection = $this->getShipmentItemCollection();
1613 $r = $shipmentItemCollection->onBeforeBasketItemDelete($basketItem);
1614 if (!$r->isSuccess())
1615 {
1616 return $result->addErrors($r->getErrors());
1617 }
1618
1619 if ($this->isSystem())
1620 {
1621 return $this->syncQuantityAfterModify($basketItem);
1622 }
1623
1624 return $result;
1625 }
1626
1641 public function onBasketModify($action, BasketItem $basketItem, $name = null, $oldValue = null, $value = null)
1642 {
1643 $result = new Result();
1644
1645 if ($action === EventActions::ADD)
1646 {
1647 if (!$this->isSystem())
1648 {
1649 return $result;
1650 }
1651
1652 return $this->getShipmentItemCollection()->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1653 }
1654 elseif ($action === EventActions::UPDATE)
1655 {
1656 if ($name === "QUANTITY")
1657 {
1658 if ($this->isSystem())
1659 {
1660 return $this->syncQuantityAfterModify($basketItem, $value, $oldValue);
1661 }
1662
1664 $shipmentItemCollection = $this->getShipmentItemCollection();
1665
1666 $r = $shipmentItemCollection->onBasketModify($action, $basketItem, $name, $oldValue, $value);
1667 if ($r->isSuccess())
1668 {
1669 if (!$this->isCustomPrice())
1670 {
1671 $deliveryCalculate = $this->calculateDelivery();
1672 if ($deliveryCalculate->isSuccess())
1673 {
1674 $this->setField('BASE_PRICE_DELIVERY', $deliveryCalculate->getPrice());
1675 }
1676 else
1677 {
1678 $result->addWarnings($deliveryCalculate->getErrors());
1679 }
1680 }
1681 }
1682 else
1683 {
1684 $result->addErrors($r->getErrors());
1685 }
1686 }
1687 elseif ($name === 'WEIGHT')
1688 {
1689 if (!$this->isMarkedFieldCustom('WEIGHT'))
1690 {
1691 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1692 {
1693 $this->setField('WEIGHT', $this->getShipmentItemCollection()->getWeight());
1694 }
1695 }
1696 }
1697 elseif ($name === 'PRICE')
1698 {
1699 if (!$this->isCustomPrice())
1700 {
1701 if ($this->getShipmentItemCollection()->isExistBasketItem($basketItem))
1702 {
1703 $r = $this->calculateDelivery();
1704 if ($r->isSuccess())
1705 {
1706 $this->setField('BASE_PRICE_DELIVERY', $r->getPrice());
1707 }
1708 else
1709 {
1710 $result->addErrors($r->getErrors());
1711 }
1712 }
1713 }
1714 }
1715 }
1716
1717 return $result;
1718 }
1719
1732 protected function onFieldModify($name, $oldValue, $value)
1733 {
1734 global $USER;
1735
1736 $result = new Result();
1737
1738 if ($name === 'DELIVERY_ID')
1739 {
1740 if (
1741 $value > 0
1742 && (
1743 $this->service === null
1744 || $this->service->getId() !== (int)$value
1745 )
1746 )
1747 {
1748 $service = Delivery\Services\Manager::getObjectById($value);
1749 if ($service)
1750 {
1751 $this->service = $service;
1752
1753 $this->setField('DELIVERY_NAME', $this->service->getName());
1754 }
1755 }
1756
1757 $this->getPropertyCollection()->refreshRelated();
1758 }
1759 elseif ($name === "MARKED")
1760 {
1761 if ($oldValue != "Y")
1762 {
1763 $this->setField('DATE_MARKED', new Main\Type\DateTime());
1764
1765 if (is_object($USER))
1766 {
1767 $this->setField('EMP_MARKED_ID', $USER->GetID());
1768 }
1769 }
1770 elseif ($value === "N")
1771 {
1772 $this->setField('REASON_MARKED', '');
1773 }
1774 }
1775 elseif ($name === "ALLOW_DELIVERY")
1776 {
1777 $this->setField('DATE_ALLOW_DELIVERY', new Main\Type\DateTime());
1778
1779 if (is_object($USER))
1780 {
1781 $this->setField('EMP_ALLOW_DELIVERY_ID', $USER->GetID());
1782 }
1783
1784 if ($oldValue === 'N')
1785 {
1786 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_allow_delivery', '');
1787
1788 $registry = Registry::getInstance(static::getRegistryType());
1790 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1791
1792 if (
1793 $shipmentStatus !== ''
1794 && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus()
1795 )
1796 {
1797 $r = $this->setStatus($shipmentStatus);
1798 if (!$r->isSuccess())
1799 {
1800 $result->addErrors($r->getErrors());
1801 }
1802 }
1803 }
1804
1805 Internals\EventsPool::addEvent(
1806 's'.$this->getInternalIndex(),
1808 [
1809 'ENTITY' => $this,
1810 'VALUES' => $this->fields->getOriginalValues()
1811 ]
1812 );
1813 }
1814 elseif ($name === "DEDUCTED")
1815 {
1816 $this->setField('DATE_DEDUCTED', new Main\Type\DateTime());
1817
1818 if (is_object($USER))
1819 {
1820 $this->setField('EMP_DEDUCTED_ID', $USER->GetID());
1821 }
1822
1823 if ($oldValue === 'N')
1824 {
1825 $shipmentStatus = Main\Config\Option::get('sale', 'shipment_status_on_shipped', '');
1826
1827 $registry = Registry::getInstance(static::getRegistryType());
1829 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
1830
1831 if (strval($shipmentStatus) != '' && $this->getField('STATUS_ID') != $deliveryStatusClassName::getFinalStatus())
1832 {
1833 $r = $this->setStatus($shipmentStatus);
1834 if (!$r->isSuccess())
1835 {
1836 $result->addErrors($r->getErrors());
1837 }
1838 }
1839 }
1840
1841 if ($value === 'Y')
1842 {
1844 foreach ($this->getShipmentItemCollection() as $shipmentItem)
1845 {
1846 $r = $shipmentItem->checkMarkingCodeOnDeducted();
1847 if (!$r->isSuccess())
1848 {
1849 $result->addErrors($r->getErrors());
1850 }
1851 }
1852 }
1853
1854 Internals\EventsPool::addEvent(
1855 's'.$this->getInternalIndex(),
1857 [
1858 'ENTITY' => $this,
1859 'VALUES' => $this->fields->getOriginalValues()
1860 ]
1861 );
1862
1863 Cashbox\Internals\Pool::addDoc($this->getOrder()->getInternalId(), $this);
1864 }
1865 elseif ($name === "STATUS_ID")
1866 {
1867 $event = new Main\Event(
1868 'sale',
1870 [
1871 'ENTITY' => $this,
1872 'VALUE' => $value,
1873 'OLD_VALUE' => $oldValue,
1874 ]
1875 );
1876 $event->send();
1877
1878 Internals\EventsPool::addEvent(
1879 's'.$this->getInternalIndex(),
1881 [
1882 'ENTITY' => $this,
1883 'VALUE' => $value,
1884 'OLD_VALUE' => $oldValue,
1885 ]
1886 );
1887
1888 Internals\EventsPool::addEvent(
1889 's'.$this->getInternalIndex(),
1891 [
1892 'ENTITY' => $this,
1893 'VALUE' => $value,
1894 'OLD_VALUE' => $oldValue,
1895 ]
1896 );
1897 }
1898 elseif ($name === 'RESPONSIBLE_ID')
1899 {
1900 $this->setField('DATE_RESPONSIBLE_ID', new Main\Type\DateTime());
1901 }
1902 elseif ($name === 'TRACKING_NUMBER')
1903 {
1904 if ($value)
1905 {
1906 Internals\EventsPool::addEvent(
1907 's'.$this->getInternalIndex(),
1909 [
1910 'ENTITY' => $this,
1911 'VALUES' => $this->getFields()->getOriginalValues(),
1912 ]
1913 );
1914 }
1915 }
1916
1917 $r = parent::onFieldModify($name, $oldValue, $value);
1918 if (!$r->isSuccess())
1919 {
1920 return $result->addErrors($r->getErrors());
1921 }
1922
1923 if (
1924 $name === 'BASE_PRICE_DELIVERY'
1925 && !$this->isMarkedFieldCustom('PRICE_DELIVERY')
1926 )
1927 {
1928 $value -= $this->getField('DISCOUNT_PRICE');
1929
1930 $r = $this->setField('PRICE_DELIVERY', $value);
1931 if (!$r->isSuccess())
1932 {
1933 $result->addErrors($r->getErrors());
1934 }
1935 }
1936
1937 if ($r->hasWarnings())
1938 {
1939 $result->addWarnings($r->getWarnings());
1940 }
1941
1942 $result->addData($r->getData());
1943
1944 if ($result->isSuccess())
1945 {
1946 $this->setFieldNoDemand('DATE_UPDATE', new Main\Type\DateTime());
1947 }
1948
1949 return $result;
1950 }
1951
1965 protected function syncQuantityAfterModify(BasketItem $basketItem, $value = null, $oldValue = null)
1966 {
1967 $result = new Result();
1968
1970 $shipmentItemCollection = $this->getShipmentItemCollection();
1971
1972 $shipmentItem = $shipmentItemCollection->getItemByBasketCode($basketItem->getBasketCode());
1973
1974 if ($value === 0)
1975 {
1976 if ($shipmentItem !== null)
1977 {
1978 $shipmentItem->setFieldNoDemand('QUANTITY', 0);
1979 }
1980
1981 return $result;
1982 }
1983
1984 if ($shipmentItem === null)
1985 {
1986 $shipmentItem = $shipmentItemCollection->createItem($basketItem);
1987 }
1988
1989 $deltaQuantity = $value - $oldValue;
1990
1991 if ($deltaQuantity > 0) // plus
1992 {
1993 $shipmentItem->setFieldNoDemand(
1994 "QUANTITY",
1995 $shipmentItem->getField("QUANTITY") + $deltaQuantity
1996 );
1997
1998 if (
2000 && $this->needReservation()
2001 )
2002 {
2003 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
2004 }
2005 }
2006 else // minus
2007 {
2008 if (floatval($shipmentItem->getField("QUANTITY")) <= 0)
2009 {
2010 return new Result();
2011 }
2012
2013 if ($value != 0 && roundEx($shipmentItem->getField("QUANTITY"), SALE_VALUE_PRECISION) < roundEx(-$deltaQuantity, SALE_VALUE_PRECISION))
2014 {
2015 $result->addError(
2016 new ResultError(
2017 str_replace(
2018 array("#NAME#", "#QUANTITY#", "#DELTA_QUANTITY#"),
2019 array($basketItem->getField("NAME"), $shipmentItem->getField("QUANTITY"), abs($deltaQuantity)),
2020 Loc::getMessage('SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR')
2021 ),
2022 'SALE_SHIPMENT_SYSTEM_QUANTITY_ERROR'
2023 )
2024 );
2025 return $result;
2026 }
2027
2028 if ($value > 0)
2029 {
2030 $shipmentItem->setFieldNoDemand(
2031 "QUANTITY",
2032 $shipmentItem->getField("QUANTITY") + $deltaQuantity
2033 );
2034
2035 if (
2037 && $this->needReservation()
2038 )
2039 {
2040 Internals\Catalog\Provider::tryReserveShipmentItem($shipmentItem);
2041 }
2042 }
2043
2044 }
2045
2046 return $result;
2047 }
2048
2052 public function getServiceParams()
2053 {
2054 $params = $this->getField('PARAMS');
2055 return isset($params["SERVICE_PARAMS"]) ? $params["SERVICE_PARAMS"] : array();
2056 }
2057
2062 public function setServiceParams(array $serviceParams)
2063 {
2064 $params = $this->getField('PARAMS');
2065 $params["SERVICE_PARAMS"] = $serviceParams;
2066 $this->setField("PARAMS", $params);
2067 }
2068
2072 public function getExtraServices()
2073 {
2074 if($this->extraServices === null)
2075 {
2076 $this->setExtraServices(
2077 Delivery\ExtraServices\Manager::getValuesForShipment(
2078 $this->getId(),
2079 $this->getDeliveryId()
2080 )
2081 );
2082 }
2083
2084 return $this->extraServices;
2085 }
2086
2090 public function setExtraServices(array $extraServices)
2091 {
2092 $this->extraServices = $extraServices;
2093 }
2094
2098 public function getExtraServicesObjects()
2099 {
2100 return Delivery\ExtraServices\Manager::getObjectsForShipment(
2101 $this->getId(),
2102 $this->getDeliveryId(),
2103 $this->getCurrency()
2104 );
2105 }
2106
2110 protected function saveExtraServices()
2111 {
2112 return Delivery\ExtraServices\Manager::saveValuesForShipment($this->getId(), $this->getExtraServices());
2113 }
2114
2118 public function getStoreId()
2119 {
2120 if($this->storeId === null)
2121 {
2122 $this->setStoreId(
2123 Delivery\ExtraServices\Manager::getStoreIdForShipment(
2124 $this->getId(),
2125 $this->getDeliveryId()
2126 ));
2127 }
2128
2129 return $this->storeId;
2130 }
2131
2135 public function setStoreId($storeId)
2136 {
2137 $this->storeId = (int)$storeId;
2138 }
2139
2143 protected function saveStoreId()
2144 {
2145 return Delivery\ExtraServices\Manager::saveStoreIdForShipment($this->getId(), $this->getDeliveryId(), $this->getStoreId());
2146 }
2147
2151 public function getWeight() : float
2152 {
2153 return (float)$this->getField('WEIGHT');
2154 }
2155
2160 public function setWeight(float $weight)
2161 {
2162 return $this->setField('WEIGHT', $weight);
2163 }
2164
2169 public function calculateDelivery()
2170 {
2171 if ($this->isSystem())
2172 {
2173 throw new Main\NotSupportedException();
2174 }
2175
2176 if ($this->getDeliveryId() === 0)
2177 {
2178 return new Delivery\CalculationResult();
2179 }
2180
2181 return Delivery\Services\Manager::calculateDeliveryPrice($this);
2182 }
2183
2188 public function resetData()
2189 {
2190 if (!$this->isCustomPrice())
2191 {
2192 $this->setField('BASE_PRICE_DELIVERY', 0);
2193 }
2194 }
2195
2203 public function getBasketItemQuantity(BasketItem $basketItem)
2204 {
2206 $shipmentItemCollection = $this->getShipmentItemCollection();
2207
2208 return $shipmentItemCollection->getBasketItemQuantity($basketItem);
2209 }
2210
2217 protected function addChangesToHistory($name, $oldValue = null, $value = null)
2218 {
2219 if ($this->getId() > 0 && !$this->isSystem())
2220 {
2221 $order = $this->getOrder();
2222
2223 if ($order && $order->getId() > 0)
2224 {
2225 $registry = Registry::getInstance(static::getRegistryType());
2226
2228 $orderHistory = $registry->getOrderHistoryClassName();
2229 $orderHistory::addField(
2230 'SHIPMENT',
2231 $order->getId(),
2232 $name,
2233 $oldValue,
2234 $value,
2235 $this->getId(),
2236 $this
2237 );
2238 }
2239 }
2240 }
2241
2248 public function isExistBasketItem(BasketItem $basketItem)
2249 {
2251 if (!$shipmentItemCollection = $this->getShipmentItemCollection())
2252 {
2253 throw new Main\ObjectNotFoundException('Entity "ShipmentItemCollection" not found');
2254 }
2255
2256 return $shipmentItemCollection->isExistBasketItem($basketItem);
2257 }
2258
2265 public function verify()
2266 {
2267 $result = new Result();
2268
2269 if ($this->getDeliveryId() <= 0)
2270 {
2271 $result->addError(
2272 new ResultError(
2273 Loc::getMessage("SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY"),
2274 'SALE_SHIPMENT_DELIVERY_SERVICE_EMPTY'
2275 )
2276 );
2277 }
2278
2280 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2281 {
2283 foreach ($shipmentItemCollection as $shipmentItem)
2284 {
2285 $r = $shipmentItem->verify();
2286 if (!$r->isSuccess())
2287 {
2288 $result->addErrors($r->getErrors());
2289 }
2290 }
2291 }
2292
2293 return $result;
2294 }
2295
2303 public function setAccountNumber($id)
2304 {
2305 $result = new Result();
2306
2307 $id = intval($id);
2308 if ($id <= 0)
2309 {
2310 $result->addError(new ResultError(Loc::getMessage('SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'), 'SALE_PAYMENT_GENERATE_ACCOUNT_NUMBER_ORDER_NUMBER_WRONG_ID'));
2311 return $result;
2312 }
2313
2314 $value = Internals\AccountNumberGenerator::generateForShipment($this);
2315
2316 try
2317 {
2319 $r = static::updateInternal($id, array("ACCOUNT_NUMBER" => $value));
2320 $res = $r->isSuccess(true);
2321 }
2322 catch (Main\DB\SqlQueryException $exception)
2323 {
2324 $res = false;
2325 }
2326
2327 if ($res)
2328 {
2329 $this->setFieldNoDemand('ACCOUNT_NUMBER', $value);
2330 }
2331
2332 return $result;
2333 }
2334
2339 public function getBusinessValueProviderInstance($mapping)
2340 {
2341 $providerInstance = null;
2342
2343 if (is_array($mapping) && isset($mapping['PROVIDER_KEY']))
2344 {
2345 switch ($mapping['PROVIDER_KEY'])
2346 {
2347 case 'SHIPMENT': $providerInstance = $this; break;
2348 case 'COMPANY' : $providerInstance = $this->getField('COMPANY_ID'); break;
2349 default:
2350 $order = $this->getOrder();
2351 if ($order)
2352 {
2353 $providerInstance = $order->getBusinessValueProviderInstance($mapping);
2354 }
2355 }
2356 }
2357
2358 return $providerInstance;
2359 }
2360
2364 public function getPersonTypeId()
2365 {
2366 $order = $this->getOrder();
2367 if ($order)
2368 {
2369 return $order->getPersonTypeId();
2370 }
2371
2372 return null;
2373 }
2374
2382 public static function getList(array $parameters)
2383 {
2384 return Internals\ShipmentTable::getList($parameters);
2385 }
2386
2397 public function createClone(\SplObjectStorage $cloneEntity)
2398 {
2399 if ($this->isClone() && $cloneEntity->contains($this))
2400 {
2401 return $cloneEntity[$this];
2402 }
2403
2405 $shipmentClone = parent::createClone($cloneEntity);
2406
2408 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2409 {
2410 if (!$cloneEntity->contains($shipmentItemCollection))
2411 {
2412 $cloneEntity[$shipmentItemCollection] = $shipmentItemCollection->createClone($cloneEntity);
2413 }
2414
2415 if ($cloneEntity->contains($shipmentItemCollection))
2416 {
2417 $shipmentClone->shipmentItemCollection = $cloneEntity[$shipmentItemCollection];
2418 }
2419 }
2420
2422 if ($service = $this->getDelivery())
2423 {
2424 if (!$cloneEntity->contains($service))
2425 {
2426 $cloneEntity[$service] = $service->createClone($cloneEntity);
2427 }
2428
2429 if ($cloneEntity->contains($service))
2430 {
2431 $shipmentClone->service = $cloneEntity[$service];
2432 }
2433 }
2434
2435 return $shipmentClone;
2436 }
2437
2446 protected function setStatus($status)
2447 {
2448 global $USER;
2449
2450 $result = new Result();
2451
2452 $registry = Registry::getInstance(static::getRegistryType());
2454 $deliveryStatusClassName = $registry->getDeliveryStatusClassName();
2455
2456 if (is_object($USER) && $USER->isAuthorized())
2457 {
2458 $statusesList = $deliveryStatusClassName::getAllowedUserStatuses($USER->getID(), $this->getField('STATUS_ID'));
2459 }
2460 else
2461 {
2462 $statusesList = $deliveryStatusClassName::getAllStatuses();
2463 }
2464
2465 if($this->getField('STATUS_ID') != $status && array_key_exists($status, $statusesList))
2466 {
2468 $r = $this->setField('STATUS_ID', $status);
2469 if (!$r->isSuccess())
2470 {
2471 $result->addErrors($r->getErrors());
2472 return $result;
2473 }
2474 }
2475
2476 return $result;
2477 }
2478
2484 public function getErrorEntity($value)
2485 {
2486 $className = null;
2487 $errorsList = static::getAutoFixErrorsList();
2488 if (is_array($errorsList) && in_array($value, $errorsList))
2489 {
2490 $className = static::getClassName();
2491 }
2492 else
2493 {
2495 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2496 {
2497 $className = $shipmentItemCollection->getErrorEntity($value);
2498 }
2499 }
2500
2501 return $className;
2502 }
2503
2509 public function canAutoFixError($value)
2510 {
2511 $autoFix = false;
2512 $errorsList = static::getAutoFixErrorsList();
2513 if (is_array($errorsList) && in_array($value, $errorsList))
2514 {
2515 $autoFix = true;
2516 }
2517 else
2518 {
2520 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2521 {
2522 $autoFix = $shipmentItemCollection->canAutoFixError($value);
2523 }
2524 }
2525
2526 return $autoFix;
2527 }
2528
2532 public function getAutoFixErrorsList()
2533 {
2534 return array_keys(static::getAutoFixRules());
2535 }
2536
2542 public function tryFixError($code)
2543 {
2544 $result = new Result();
2545
2546 $method = static::getFixMethod($code);
2547 $r = call_user_func_array($method, array($this));
2548 if (!$r->isSuccess())
2549 {
2550 $result->addErrors($r->getErrors());
2551 }
2552 elseif ($r->hasWarnings())
2553 {
2554 $result->addWarnings($r->getWarnings());
2555 }
2556
2557 return $result;
2558 }
2559
2564 protected static function getFixMethod($code)
2565 {
2566 $codeList = static::getAutoFixRules();
2567
2568 if (!empty($codeList[$code]))
2569 {
2570 return $codeList[$code];
2571 }
2572 return null;
2573 }
2574
2581 public static function fixReserveErrors(Shipment $entity)
2582 {
2583 $result = new Result();
2584
2585 $r = $entity->tryReserve();
2586 if (!$r->isSuccess())
2587 {
2588 $result->addErrors($r->getErrors());
2589 }
2590 elseif ($r->hasWarnings())
2591 {
2592 $result->addWarnings($r->getWarnings());
2593 }
2594
2595 return $result;
2596 }
2597
2604 public static function fixShipErrors(Shipment $entity)
2605 {
2606 $result = new Result();
2607
2608 $r = $entity->setField('DEDUCTED', 'Y');
2609 if (!$r->isSuccess())
2610 {
2611 if (!$r->isSuccess())
2612 {
2613 $result->addErrors($r->getErrors());
2614 }
2615 }
2616
2617 $r = $entity->tryShip();
2618 if (!$r->isSuccess())
2619 {
2620 if (!$r->isSuccess())
2621 {
2622 $result->addErrors($r->getErrors());
2623 }
2624 }
2625
2626 return $result;
2627 }
2628
2632 protected static function getAutoFixRules()
2633 {
2634 return [
2635 'PROVIDER_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2636 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2637 'PROVIDER_UNRESERVE_SHIPMENT_ITEM_WRONG_AVAILABLE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2638 'SALE_PROVIDER_RESERVE_SHIPMENT_ITEM_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixReserveErrors"],
2639
2640 'SALE_PROVIDER_SHIPMENT_SHIPPED_LESS_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2641 'SALE_PROVIDER_SHIPMENT_SHIPPED_MORE_QUANTITY' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2642 'DDCT_DEDUCTION_QUANTITY_STORE_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2643 'SALE_PROVIDER_SHIPMENT_QUANTITY_NOT_ENOUGH' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2644 'DDCT_DEDUCTION_QUANTITY_ERROR' => ['\Bitrix\Sale\Shipment', "fixShipErrors"],
2645 ];
2646 }
2647
2651 public function canMarked()
2652 {
2653 return true;
2654 }
2655
2659 public function getMarkField()
2660 {
2661 return 'MARKED';
2662 }
2663
2670 public function isChanged()
2671 {
2672 if (parent::isChanged())
2673 {
2674 return true;
2675 }
2676
2677 return $this->getShipmentItemCollection()->isChanged();
2678 }
2679
2683 public function clearChanged()
2684 {
2685 parent::clearChanged();
2686
2687 if ($shipmentItemCollection = $this->getShipmentItemCollection())
2688 {
2690 foreach ($shipmentItemCollection as $shipmentItem)
2691 {
2692 $shipmentItem->clearChanged();
2693 }
2694 }
2695 }
2696
2705 public function getVatRate()
2706 {
2707 $vatRate = 0;
2708
2709 $service = $this->getDelivery();
2710 if ($service)
2711 {
2712 if (!Main\Loader::includeModule('catalog'))
2713 {
2714 return $vatRate;
2715 }
2716
2717 $vatId = $service->getVatId();
2718 if ($vatId <= 0)
2719 {
2720 return $vatRate;
2721 }
2722
2723 $dbRes = VatTable::getById($vatId);
2724 $vatInfo = $dbRes->fetch();
2725 if ($vatInfo)
2726 {
2727 $vatRate = $vatInfo['RATE'] / 100;
2728 }
2729 }
2730
2731 return $vatRate;
2732 }
2733
2739 public function getVatSum()
2740 {
2741 $vatRate = $this->getVatRate();
2742 $price = $this->getPrice() * $vatRate / (1 + $vatRate);
2743
2744 return PriceMaths::roundPrecision($price);
2745 }
2746
2752 protected function addInternal(array $data)
2753 {
2754 return Internals\ShipmentTable::add($data);
2755 }
2756
2763 protected function updateInternal($primary, array $data)
2764 {
2765 return Internals\ShipmentTable::update($primary, $data);
2766 }
2767
2774 protected static function deleteInternal($primary)
2775 {
2776 return Internals\ShipmentTable::deleteWithItems($primary);
2777 }
2778
2782 protected static function getFieldsMap()
2783 {
2784 return Internals\ShipmentTable::getMap();
2785 }
2786
2790 public static function getUfId()
2791 {
2792 return Internals\ShipmentTable::getUfId();
2793 }
2794
2804 public function setBasePriceDelivery($value, $custom = false)
2805 {
2806 $result = new Result();
2807
2808 if ($custom === true)
2809 {
2810 $this->markFieldCustom('PRICE_DELIVERY');
2811 }
2812
2813 $r = $this->setField('BASE_PRICE_DELIVERY', $value);
2814 if (!$r->isSuccess())
2815 {
2816 $result->addErrors($r->getErrors());
2817 }
2818
2819 return $result;
2820 }
2821
2827 public static function getEntityEventName()
2828 {
2829 return 'SaleShipment';
2830 }
2831
2837 public function toArray() : array
2838 {
2839 $result = parent::toArray();
2840
2841 $result['ITEMS'] = $this->getShipmentItemCollection()->toArray();
2842
2843 return $result;
2844 }
2845
2847 {
2848 if(empty($this->propertyCollection))
2849 {
2850 $this->propertyCollection = $this->loadPropertyCollection();
2851 }
2852
2854 }
2855
2856 public function loadPropertyCollection(): ShipmentPropertyValueCollection
2857 {
2858 $registry = Registry::getInstance(static::getRegistryType());
2860 $propertyCollectionClassName = $registry->getShipmentPropertyValueCollectionClassName();
2861
2862 return $propertyCollectionClassName::load($this);
2863 }
2864
2870 public function getParentOrder()
2871 {
2872 return $this->getOrder();
2873 }
2874}
2875
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
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)
static repeat(Order $order, array $resultList)
Definition recurring.php:26
static getInstance($type)
Definition registry.php:183
setDeliveryService(Delivery\Services\Base $service)
Definition shipment.php:171
setBasePriceDelivery($value, $custom=false)
setWeight(float $weight)
static deleteNoDemand($orderId)
Definition shipment.php:681
static getRegistryEntity()
Definition shipment.php:46
updateInternal($primary, array $data)
setServiceParams(array $serviceParams)
checkValueBeforeSet($name, $value)
Definition shipment.php:818
static getFixMethod($code)
getBusinessValueProviderInstance($mapping)
static getMeaningfulFields()
Definition shipment.php:160
static getParametersForLoad($id)
Definition shipment.php:905
static getCustomizableFields()
Definition shipment.php:123
onBeforeBasketItemDelete(BasketItem $basketItem)
static loadForOrder($id)
Definition shipment.php:886
static getEntityEventName()
static createSystem(ShipmentCollection $collection, Delivery\Services\Base $deliveryService=null)
static getAvailableFields()
Definition shipment.php:74
static fixReserveErrors(Shipment $entity)
static getList(array $parameters)
onBeforeSetFields(array $values)
Definition shipment.php:132
isPriceField(string $name)
static fixShipErrors(Shipment $entity)
addInternal(array $data)
setFieldNoDemand($name, $value)
Definition shipment.php:863
static deleteInternal($primary)
setField($name, $value)
Definition shipment.php:789
normalizeValue($name, $value)
Definition shipment.php:761
static getRegistryType()
Definition shipment.php:248
setExtraServices(array $extraServices)
onShipmentItemCollectionModify($action, ShipmentItem $shipmentItem, $name=null, $oldValue=null, $value=null)
Definition shipment.php:620
setFieldNoDemand($name, $value)
canAutoFixError($value)
getErrorEntity($value)