Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
manager.php
1<?php
2
4
15use Bitrix\Sale;
19
20Loc::loadMessages(__FILE__);
21
29{
30 const NO_INFORMATION = 0; //No tracking (may be yet)
31 const WAITING_SHIPMENT = 10;
32 const ON_THE_WAY = 20;
33 const ARRIVED = 30;
34 const HANDED = 40; //shipping definitely finished
35 const PROBLEM = 50;
36 const UNKNOWN = 60; //Incorrect status mapping made by tracking handler.
37 const RETURNED = 70;
38}
39
44class StatusResult extends Result
45{
47 public $status;
54}
55
77
87{
88 protected static $instance = null;
89 protected static $classNames = null;
90 //If status didn't changed for a long time let's stop update it.
91 protected static $activeStatusLiveTime = 5184000; //60 days
92
94 protected $isClone = false;
95
96 protected function __clone(){}
97
98 protected function __construct()
99 {
100 self::initClassNames();
101 }
102
106 public static function getInstance()
107 {
108 if (self::$instance === null)
109 self::$instance = new static();
110
111 return self::$instance;
112 }
113
118 public static function getStatusName($status)
119 {
120 $statusesNames = self::getStatusesList();
121 $status = intval($status);
122
123 if(empty($statusesNames[$status]))
124 return Loc::getMessage("SALE_DTM_STATUS_NAME_UNKNOWN");
125
126 return $statusesNames[$status];
127 }
128
132 public static function getStatusesList()
133 {
134 return array(
135 Statuses::NO_INFORMATION => Loc::getMessage("SALE_DTM_STATUS_NAME_NO_INFORMATION"),
136 Statuses::WAITING_SHIPMENT => Loc::getMessage("SALE_DTM_STATUS_NAME_WAITING_SHIPMENT"),
137 Statuses::ON_THE_WAY => Loc::getMessage("SALE_DTM_STATUS_NAME_ON_THE_WAY"),
138 Statuses::ARRIVED => Loc::getMessage("SALE_DTM_STATUS_NAME_ARRIVED"),
139 Statuses::HANDED => Loc::getMessage("SALE_DTM_STATUS_NAME_HANDED"),
140 Statuses::PROBLEM => Loc::getMessage("SALE_DTM_STATUS_NAME_PROBLEM"),
141 Statuses::UNKNOWN => Loc::getMessage("SALE_DTM_STATUS_NAME_UNKNOWN"),
142 Statuses::RETURNED => Loc::getMessage("SALE_DTM_STATUS_NAME_RETURNED"),
143 );
144 }
145
152 public function getTrackingUrl($deliveryId, $trackingNumber = '')
153 {
154 if(!$deliveryId)
155 return '';
156
157 $trackingObject = $this->getTrackingObjectByDeliveryId($deliveryId);
158
159 if(!$trackingObject)
160 return '';
161
162 return $trackingObject->getTrackingUrl($trackingNumber);
163 }
164
173 public function getStatusByShipmentId($shipmentId, $trackingNumber = '')
174 {
175 if(intval($shipmentId) <= 0)
176 throw new ArgumentNullException('shipmentId');
177
178 $result = new StatusResult();
179
180 $res = ShipmentTable::getList(array(
181 'filter' => array(
182 'ID'=>$shipmentId
183 ),
184 'select' => array(
185 'ID', 'ORDER_ID', 'DELIVERY_ID', 'TRACKING_STATUS', 'TRACKING_NUMBER'
186 )
187 ));
188
189 if(!$shipment = $res->fetch())
190 {
191 $result->addError(new Error("Can't find shipment with id:\"".$shipmentId.'"'));
192 return $result;
193 }
194
195 if($trackingNumber <> '' && $trackingNumber != $shipment['TRACKING_NUMBER'])
196 $shipment['TRACKING_NUMBER'] = $trackingNumber;
197
198 if($shipment['TRACKING_NUMBER'] == '')
199 return $result;
200
201 $result = $this->getStatus($shipment);
202
203 if($result->isSuccess())
204 {
205 if($shipment['TRACKING_STATUS'] != $result->status)
206 {
207 $eventParams = new StatusChangeEventParam();
208 $eventParams->orderId = $shipment['ORDER_ID'];
209 $eventParams->shipmentId = $shipmentId;
210 $eventParams->status = $result->status;
211 $eventParams->trackingNumber = $shipment['TRACKING_NUMBER'];
212 $eventParams->description = $result->description;
213 $eventParams->lastChangeTimestamp = $result->lastChangeTimestamp;
214 $eventParams->deliveryId = $shipment['DELIVERY_ID'];
215 $res = $this->processStatusChange(array($eventParams));
216
217 if(!$res->isSuccess())
218 {
219 $result->addErrors($res->getErrors());
220 }
221 }
222 }
223
224 return $result;
225 }
226
232 protected static function getMappedStatuses()
233 {
234 $result = unserialize(Option::get('sale', 'tracking_map_statuses',''), ['allowed_classes' => false]);
235
236 if(!is_array($result))
237 $result = array();
238
239 return $result;
240 }
241
242 protected static function getCheckPeriod()
243 {
244 return (int)Option::get('sale', 'tracking_check_period', '24');
245 }
246
253 protected function getStatus($shipment)
254 {
255 $result = new StatusResult();
256
257 if(intval($shipment['DELIVERY_ID']) <= 0)
258 throw new ArgumentNullException('deliveryId');
259
260 $trackingObject = $this->getTrackingObjectByDeliveryId($shipment['DELIVERY_ID']);
261
262 if(!$trackingObject)
263 return $result;
264
265 return $trackingObject->getStatusShipment($shipment);
266 }
267
268
276 public function getTrackingObjectByDeliveryId($deliveryId)
277 {
278 if(intval($deliveryId) <= 0)
279 throw new ArgumentNullException('deliveryId');
280
281 $result = null;
282
283 $deliveryService = Services\Manager::getObjectById($deliveryId);
284
285 if(!$deliveryService)
286 return null;
287
288 $class = $deliveryService->getTrackingClass();
289
290 if($class <> '')
291 {
292 $result = $this->createTrackingObject(
293 $class,
294 $deliveryService->getTrackingParams(),
295 $deliveryService
296 );
297 }
298
299 return $result;
300 }
301
310 protected function createTrackingObject($className, array $params, Services\Base $deliveryService)
311 {
312 if($className == '')
313 throw new ArgumentNullException('className');
314
315 if(!class_exists($className))
316 throw new SystemException('Class "'.$className.'" does not exist!');
317
318 if(get_parent_class($className) != 'Bitrix\Sale\Delivery\Tracking\Base')
319 throw new SystemException($className.' is not inherited from \"\Bitrix\Sale\Delivery\Tracking\Base\"!');
320
321 return new $className($params, $deliveryService);
322 }
323
327 public static function startRefreshingStatuses()
328 {
329 $manager = self::getInstance();
330 $result = $manager->updateStatuses();
331
332 if(!$result->isSuccess())
333 {
334 $eventLog = new \CEventLog;
335
336 $eventLog->Add(array(
337 "SEVERITY" => \CEventLog::SEVERITY_ERROR,
338 "AUDIT_TYPE_ID" => 'SALE_DELIVERY_TRACKING_REFRESHING_STATUS_ERROR',
339 "MODULE_ID" => "sale",
340 "ITEM_ID" => time(),
341 "DESCRIPTION" => implode('\n', $result->getErrorMessages())
342 ));
343 }
344
345 $data = $result->getData();
346
347 if(!empty($data))
348 {
349 $result = $manager->processStatusChange($data);
350
351 if(!$result->isSuccess())
352 {
353 $eventLog = new \CEventLog;
354
355 $eventLog->Add(array(
356 "SEVERITY" => \CEventLog::SEVERITY_ERROR,
357 "AUDIT_TYPE_ID" => 'SALE_DELIVERY_TRACKING_REFRESHING_STATUS_ERROR',
358 "MODULE_ID" => "sale",
359 "ITEM_ID" => time(),
360 "DESCRIPTION" => implode('\n', $result->getErrorMessages())
361 ));
362 }
363 }
364
365 return '\Bitrix\Sale\Delivery\Tracking\Manager::startRefreshingStatuses();';
366 }
367
372 protected function updateStatuses()
373 {
374 $result = new Result();
375 $checkPeriod = self::getCheckPeriod();
376
377 if($checkPeriod <= 0)
378 return $result;
379
380 $lastChage = \Bitrix\Main\Type\DateTime::createFromTimestamp(time()-self::$activeStatusLiveTime);
381 $lastUpdate = \Bitrix\Main\Type\DateTime::createFromTimestamp(time()-$checkPeriod*60*60);
382
383 $dbRes = ShipmentTable::getList(array(
384 'filter' => array(
385 '!=TRACKING_NUMBER' => false,
386 '!=DELIVERY_ID' => false,
387 array(
388 'LOGIC' => 'OR',
389 array(
390 'LOGIC' => 'AND',
391 array('!=TRACKING_STATUS' => Statuses::HANDED),
392 array('!=TRACKING_STATUS' => Statuses::RETURNED),
393 ),
394 array('=TRACKING_STATUS' => false)
395 ),
396 array(
397 'LOGIC' => 'OR',
398 array(
399 'LOGIC' => 'AND',
400 array('=TRACKING_LAST_CHANGE' => false),
401 array('>=DATE_INSERT' => $lastChage),
402 ),
403 array('>=TRACKING_LAST_CHANGE' => $lastChage)
404 ),
405 array(
406 'LOGIC' => 'OR',
407 array('=TRACKING_LAST_CHECK' => false),
408 array('<=TRACKING_LAST_CHECK' => $lastUpdate)
409 )
410 ),
411 'select' => array(
412 'ID', 'ORDER_ID', 'DELIVERY_ID', 'TRACKING_STATUS', 'TRACKING_NUMBER'
413 ),
414 'order' => array(
415 'DELIVERY_ID' => 'ASC'
416 )
417 ));
418
419 $deliveryId = 0;
420 $shipmentsData = array();
421
422 while($shipment = $dbRes->fetch())
423 {
424 if(!isset($shipmentsData[$shipment['DELIVERY_ID']]))
425 $shipmentsData[$shipment['DELIVERY_ID']] = array();
426
427 if($shipment['TRACKING_NUMBER'] == '')
428 continue;
429
430 $shipmentsData[$shipment['DELIVERY_ID']][$shipment['TRACKING_NUMBER']] = array(
431 'SHIPMENT_ID' => $shipment['ID'],
432 'ORDER_ID' => $shipment['ORDER_ID'],
433 'DELIVERY_ID' => $shipment['DELIVERY_ID'],
434 'TRACKING_STATUS' => $shipment['TRACKING_STATUS']
435 );
436
437 if($shipment['DELIVERY_ID'] != $deliveryId && $deliveryId > 0)
438 {
439 $res = $this->processStatusesByDelivery($deliveryId, $shipmentsData[$deliveryId]);
440
441 if($res->isSuccess())
442 {
443 $data = $res->getData();
444
445 if(!empty($data))
446 $result->setData(array_merge($result->getData(), $data));
447 }
448 else
449 {
450 $result->addErrors($res->getErrors());
451 }
452
453 $deliveryId = $shipment['DELIVERY_ID'];
454 }
455
456 if($deliveryId <= 0)
457 $deliveryId = $shipment['DELIVERY_ID'];
458 }
459
460 if($deliveryId > 0)
461 {
462 $res = $this->processStatusesByDelivery($deliveryId, $shipmentsData[$deliveryId]);
463
464 if($res->isSuccess())
465 {
466 $data = $res->getData();
467
468 if(!empty($data))
469 $result->setData(array_merge($result->getData(), $data));
470 }
471 else
472 {
473 $result->addErrors($res->getErrors());
474 }
475 }
476
477 return $result;
478 }
479
480 protected function processStatusesByDelivery($deliveryId, $shipmentsData)
481 {
482 $result = new Result();
483 $trackingObject = $this->getTrackingObjectByDeliveryId($deliveryId);
484
485 if($trackingObject)
486 {
487 $statusResults = $trackingObject->getStatusesShipment($shipmentsData);
488 $eventsParams = array();
489
491 foreach($statusResults as $number => $statusResult)
492 {
493 $eventParams = null;
494
495 if(empty($shipmentsData[$number]))
496 continue;
497
498 if(!$statusResult->isSuccess())
499 {
500 $eventLog = new \CEventLog;
501
502 $eventLog->Add(array(
503 "SEVERITY" => \CEventLog::SEVERITY_ERROR,
504 "AUDIT_TYPE_ID" => 'SALE_DELIVERY_TRACKING_STATUS_RESULT',
505 "MODULE_ID" => "sale",
506 "ITEM_ID" => $shipmentsData[$number]['SHIPMENT_ID'],
507 "DESCRIPTION" => implode('\n', $statusResult->getErrorMessages())
508 ));
509
510 continue;
511 }
512
513 if(($statusResult->status != $shipmentsData[$number]['TRACKING_STATUS']))
514 {
515 $eventParams = new StatusChangeEventParam();
516 $eventParams->orderId = $shipmentsData[$number]['ORDER_ID'];
517 $eventParams->shipmentId = $shipmentsData[$number]['SHIPMENT_ID'];
518 $eventParams->status = $statusResult->status;
519 $eventParams->trackingNumber = $number;
520 $eventParams->description = $statusResult->description;
521 $eventParams->lastChangeTimestamp = $statusResult->lastChangeTimestamp;
522 $eventParams->deliveryId = $deliveryId;
523 $eventsParams[] = $eventParams;
524 }
525
526 $res = $this->updateShipment(
527 $shipmentsData[$number]['SHIPMENT_ID'],
528 $statusResult
529 );
530
531 if(!$res->isSuccess())
532 {
533 $eventLog = new \CEventLog;
534
535 $eventLog->Add(array(
536 "SEVERITY" => \CEventLog::SEVERITY_ERROR,
537 "AUDIT_TYPE_ID" => 'SALE_DELIVERY_TRACKING_UPDATE_SHIPMENT',
538 "MODULE_ID" => "sale",
539 "ITEM_ID" => $shipmentsData[$number]['SHIPMENT_ID'],
540 "DESCRIPTION" => implode('\n', $res->getErrorMessages())
541 ));
542 }
543 }
544
545 $result->setData($eventsParams);
546 }
547
548 return $result;
549 }
550
557 protected function processStatusChange($params)
558 {
559 $result = new Result();
560
561 $registry = Sale\Registry::getInstance(Sale\Registry::REGISTRY_TYPE_ORDER);
563 $orderClass = $registry->getOrderClassName();
564
565 foreach($params as $param)
566 {
567 if(intval($param->status) <= 0 && $param->description == '')
568 continue;
569
570 $mappedStatuses = $this->getMappedStatuses();
571
572 if(!empty($mappedStatuses[$param->status]))
573 {
575 $order = $orderClass::load($param->orderId);
576 $shipmentCollection = null;
577 $oShipment = null;
578
579 if($order)
580 {
582 $shipmentCollection = $order->getShipmentCollection();
583 }
584
585 if($shipmentCollection)
586 {
588 $oShipment = $shipmentCollection->getItemById($param->shipmentId);
589 }
590
591 if($oShipment)
592 {
593 $res = $oShipment->setField('STATUS_ID', $mappedStatuses[$param->status]);
594
595 if($res->isSuccess())
596 {
597 $res = $order->save();
598
599 if(!$res->isSuccess())
600 $result->addErrors($res->getErrors());
601 }
602 else
603 {
604 $result->addErrors($res->getErrors());
605 }
606 }
607 }
608 }
609
610 $this->sendOnStatusesChangedEvent($params);
611 $this->sendStatusChangedMail($params);
612 return $result;
613 }
614
619 protected function sendStatusChangedMail($params)
620 {
621 if(empty($params))
622 return true;
623
625 $paramsByShipmentId = array();
626
627 foreach($params as $status)
628 $paramsByShipmentId[$status->shipmentId] = $status;
629
630 $registry = Sale\Registry::getInstance(Sale\Registry::REGISTRY_TYPE_ORDER);
632 $orderClass = $registry->getOrderClassName();
633
634 $res = ShipmentTable::getList(array(
635 'filter' => array(
636 '=ID' => array_keys($paramsByShipmentId)
637 ),
638 'select' => array(
639 'DELIVERY_NAME',
640 'SITE_ID' => 'ORDER.LID',
641 'SITE_NAME' => 'SITE.NAME',
642 'SHIPMENT_NO' => 'ID',
643 'SHIPMENT_DATE' => 'DATE_INSERT',
644 'ORDER_NO' => 'ORDER.ACCOUNT_NUMBER',
645 'ORDER_DATE' => 'ORDER.DATE_INSERT',
646 'USER_NAME' => 'ORDER.USER.NAME',
647 'USER_LAST_NAME' => 'ORDER.USER.LAST_NAME',
648 'EMAIL' => 'ORDER.USER.EMAIL'
649 ),
650 'runtime' => array(
651 'SITE' => array(
652 'data_type' => 'Bitrix\Main\SiteTable',
653 'reference' => array(
654 'ref.LID' => 'this.ORDER.LID',
655 ),
656 'join_type' => 'left'
657 ),
658 )
659 ));
660
661 $event = new \CEvent;
662
663 while($data = $res->fetch())
664 {
665 $userEmail = '';
666 $userName = '';
667 $order = $orderClass::load($paramsByShipmentId[$data['SHIPMENT_NO']]->orderId);
668
670 if ($propertyCollection = $order->getPropertyCollection())
671 {
672 if ($propUserEmail = $propertyCollection->getUserEmail())
673 $userEmail = $propUserEmail->getValue();
674
675 if ($propPayerName = $propertyCollection->getPayerName())
676 $userName = $propPayerName->getValue();
677 }
678
679 if(empty($userEmail))
680 $userEmail = $data['EMAIL'];
681
682 if(empty($userName))
683 $userName = $data["USER_NAME"].(($data["USER_NAME"] == '' || $data["USER_LAST_NAME"] == '') ? "" : " ").$data["USER_LAST_NAME"];
684
685 $siteFields = \CEvent::GetSiteFieldsArray($data['SITE_ID']);
686
687 $fields = array(
688 'SITE_NAME' => $data['SITE_NAME'],
689 'ORDER_NO' => $data['ORDER_NO'],
690 'ORDER_DATE' => $data['ORDER_DATE']->toString(),
691 'ORDER_USER' => $userName,
692 'SHIPMENT_NO' => $data['SHIPMENT_NO'],
693 'SHIPMENT_DATE' => $data['SHIPMENT_DATE']->toString(),
694 'EMAIL' => $userEmail,
695 'STATUS_NAME' => self::getStatusName($paramsByShipmentId[$data['SHIPMENT_NO']]->status),
696 'STATUS_DESCRIPTION' => $paramsByShipmentId[$data['SHIPMENT_NO']]->description,
697 'TRACKING_NUMBER' => $paramsByShipmentId[$data['SHIPMENT_NO']]->trackingNumber,
698 'DELIVERY_NAME' => $data['DELIVERY_NAME'],
699 "ORDER_ACCOUNT_NUMBER_ENCODE" => urlencode(urlencode($data['ORDER_NO'])),
700 "SALE_EMAIL" => Option::get("sale", "order_email", "order@".$_SERVER["SERVER_NAME"]),
701 );
702
703 $fields['ORDER_DETAIL_URL'] = Loc::getMessage(
704 'SALE_DTM_ORDER_DETAIL_URL',
705 array(
706 '#A1#' => '<a href="http://'.$siteFields['SERVER_NAME'].'/personal/order/detail/'.$fields['ORDER_ACCOUNT_NUMBER_ENCODE'].'/">',
707 '#A2#' => '</a>'
708 )
709 ).'.';
710
711 $trackingUrl = self::getTrackingUrl(
712 $paramsByShipmentId[$data['SHIPMENT_NO']]->deliveryId,
713 $paramsByShipmentId[$data['SHIPMENT_NO']]->trackingNumber
714 );
715
716 $deliveryTrackingUrl = '';
717
718 if($trackingUrl <> '')
719 {
720 $deliveryTrackingUrl = Loc::getMessage(
721 'SALE_DTM_SHIPMENT_STATUS_TRACKING_URL',
722 array(
723 '#A1#' => '<a href="'.$trackingUrl.'">',
724 '#A2#' => '</a>'
725 )
726 ).".<br><br>";
727 }
728
729 $fields['DELIVERY_TRACKING_URL'] = $deliveryTrackingUrl;
730 $event->Send("SALE_ORDER_SHIPMENT_STATUS_CHANGED", $data['SITE_ID'], $fields, "N");
731 }
732
733 return true;
734 }
735
739 protected function sendOnStatusesChangedEvent(array $params)
740 {
741 $event = new Event('sale', 'onSaleShipmentsTrackingStatusesChanged', $params);
742 $event->send();
743 }
744
765 protected function initClassNames()
766 {
767 if(self::$classNames !== null)
768 return true;
769
770 Services\Manager::getHandlersList();
771
772 $classes = array(
773 '\Bitrix\Sale\Delivery\Tracking\RusPost' => 'lib/delivery/tracking/rus_post.php',
774 );
775
776 \Bitrix\Main\Loader::registerAutoLoadClasses('sale', $classes);
777
778 $event = new Event('sale', 'onSaleDeliveryTrackingClassNamesBuildList');
779 $event->send();
780 $resultList = $event->getResults();
781
782 if (is_array($resultList) && !empty($resultList))
783 {
784 $customClasses = array();
785
786 foreach ($resultList as $eventResult)
787 {
788 $params = $eventResult->getParameters();
789
790 if(!empty($params) && is_array($params))
791 $customClasses = array_merge($customClasses, $params);
792 }
793
794 if(!empty($customClasses))
795 {
796 \Bitrix\Main\Loader::registerAutoLoadClasses(null, $customClasses);
797 $classes = array_merge($customClasses, $classes);
798 }
799 }
800
801 self::$classNames = array_keys($classes);
802 return true;
803 }
804
809 public function getClassNames()
810 {
811 return self::$classNames;
812 }
813
821 public function updateShipment($shipmentId, StatusResult $params)
822 {
823 if($shipmentId <= 0)
824 throw new ArgumentNullException('id');
825
826 $updParams = array();
827
828 if($params->status !== null)
829 $updParams['TRACKING_STATUS'] = $params->status;
830
831 $updParams['TRACKING_LAST_CHECK'] = new \Bitrix\Main\Type\DateTime();
832
833 if(intval($params->lastChangeTimestamp) > 0)
834 {
835 $updParams['TRACKING_LAST_CHANGE'] = \Bitrix\Main\Type\DateTime::createFromTimestamp(
836 $params->lastChangeTimestamp
837 );
838 }
839 else
840 {
841 $updParams['TRACKING_LAST_CHANGE'] = null;
842 }
843
844 if($params->trackingNumber !== null)
845 $updParams['TRACKING_NUMBER'] = $params->trackingNumber;
846
847 $updParams['TRACKING_DESCRIPTION'] = $params->description;
848
849 if(!$params->isSuccess())
850 $updParams['TRACKING_DESCRIPTION'] .= ' '.implode(', ', $params->getErrorMessages());
851
852 return ShipmentTable::update($shipmentId, $updParams);
853 }
854
861 public function createClone(\SplObjectStorage $cloneEntity)
862 {
863 if ($this->isClone() && $cloneEntity->contains($this))
864 {
865 return $cloneEntity[$this];
866 }
867
868 $trackingClone = clone $this;
869 $trackingClone->isClone = true;
870
871 if (!$cloneEntity->contains($this))
872 {
873 $cloneEntity[$this] = $trackingClone;
874 }
875
876 return $trackingClone;
877 }
878
882 public function isClone()
883 {
884 return $this->isClone;
885 }
886}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
createClone(\SplObjectStorage $cloneEntity)
Definition manager.php:861
createTrackingObject($className, array $params, Services\Base $deliveryService)
Definition manager.php:310
updateShipment($shipmentId, StatusResult $params)
Definition manager.php:821
getTrackingUrl($deliveryId, $trackingNumber='')
Definition manager.php:152
getStatusByShipmentId($shipmentId, $trackingNumber='')
Definition manager.php:173