%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/sale/lib/delivery/tracking/ |
Current File : /home/bitrix/www/bitrix/modules/sale/lib/delivery/tracking/rus_post.php |
<?php namespace Bitrix\Sale\Delivery\Tracking; use Bitrix\Main\Error; use Bitrix\Sale\Result; use Bitrix\Main\Text\Encoding; use Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); /** * Class RusPost * @package Bitrix\Sale\Delivery\Tracking * https://tracking.pochta.ru/specification */ class RusPost extends Base { /** * @return string */ public function getClassTitle() { return Loc::getMessage("SALE_DELIVERY_TRACKING_RUS_POST_TITLE"); } /** * @return string */ public function getClassDescription() { return Loc::getMessage( "SALE_DELIVERY_TRACKING_RUS_POST_DESCRIPTION", array( '#A1#' => '<a href="https://tracking.pochta.ru/">', '#A2#' => '</a>' ) ); } /** * @param $trackingNumber * @return \Bitrix\Sale\Delivery\Tracking\StatusResult. */ public function getStatus($trackingNumber) { $trackingNumber = trim($trackingNumber); $result = new StatusResult(); if(!$this->checkTrackNumberFormat($trackingNumber)) $result->addError(new Error(Loc::getMessage('SALE_DELIVERY_TRACKING_RUS_POST_ERROR_TRNUM_FORMAT'))); if(empty($this->params['LOGIN'])) $result->addError(new Error(Loc::getMessage("SALE_DELIVERY_TRACKING_RUS_POST_LOGIN_ERROR"))); if(empty($this->params['PASSWORD'])) $result->addError(new Error(Loc::getMessage("SALE_DELIVERY_TRACKING_RUS_POST_PASSWORD_ERROR"))); if($result->isSuccess()) { $t = new RusPostSingle( $this->params['LOGIN'], $this->params['PASSWORD'] ); $result = $t->getOperationHistory($trackingNumber); } return $result; } /** * @param array $trackingNumbers * @return StatusResult[] * todo: by package of 3000 items */ public function getStatuses(array $trackingNumbers) { $data = array(); foreach($trackingNumbers as $number) $data[$number] = $this->getStatus($number); return $data; } /** * @return array */ public function getParamsStructure() { return array( "LOGIN" => array( 'TYPE' => 'STRING', 'LABEL' => Loc::getMessage("SALE_DELIVERY_TRACKING_RUS_POST_LOGIN") ), "PASSWORD" => array( 'TYPE' => 'STRING', 'LABEL' => Loc::getMessage("SALE_DELIVERY_TRACKING_RUS_POST_PASSWORD") ) ); } /** * Checks if tracknumber matches to required format. * 14 - digit, * 13 symbols like pattern XX123456789YY (UPU-S10) * @param string $trackNumber * @return bool */ protected function checkTrackNumberFormat($trackNumber) { if(strlen($trackNumber) == 13) return preg_match('/^[A-Z]{2}?\d{9}?[A-Z]{2}$/i', $trackNumber) == 1; elseif(strlen($trackNumber) == 14) return preg_match('/^\d{14}?$/', $trackNumber) == 1; else return false; } /** * @param string $trackingNumber * @return string Url were we can see tracking information */ public function getTrackingUrl($trackingNumber = '') { return 'https://pochta.ru/tracking'.(strlen($trackingNumber) > 0 ? '#'.$trackingNumber : ''); } } /** * Class RusPostSingle * @package Bitrix\Sale\Delivery\Tracking */ class RusPostSingle { const LANG_RUS = "RUS"; const LANG_ENG = "ENG"; protected $client = null; protected $lang = ""; protected $login = ""; protected $password = ""; protected static $url = 'https://tracking.russianpost.ru/rtm34'; /** * @param string $login * @param string $password * @param string $lang */ public function __construct($login, $password, $lang = self::LANG_RUS) { $this->httpClient = new \Bitrix\Main\Web\HttpClient(array( "version" => "1.1", "socketTimeout" => 15, "streamTimeout" => 15, "redirect" => true, "redirectMax" => 5, )); $this->httpClient->setHeader("Content-Type", "application/soap+xml; charset=utf-8"); $this->lang = $lang; $this->login = $login; $this->password = $password; } public function sendRequest($requestData) { $result = new Result(); if(strtolower(SITE_CHARSET) != 'utf-8') $requestData = Encoding::convertEncoding($requestData, SITE_CHARSET, 'UTF-8'); $httpRes = $this->httpClient->post(self::$url, $requestData); $errors = $this->httpClient->getError(); if (!$httpRes && !empty($errors)) { $strError = ""; foreach($errors as $errorCode => $errMes) $strError .= $errorCode.": ".$errMes; $result->addError(new Error($strError)); } else { $status = $this->httpClient->getStatus(); if(strtolower(SITE_CHARSET) != 'utf-8') $httpRes = Encoding::convertEncoding($httpRes, 'UTF-8', SITE_CHARSET); $objXML = new \CDataXML(); $objXML->LoadString($httpRes); $data = $objXML->GetArray(); $result->addData($data); if ($status != 200) { $result->addError(new Error(Loc::getMessage('SALE_DELIVERY_TRACKING_RUS_POST_ERROR_HTTP_STATUS').': '.$status)); if(!empty($data['Envelope']['#']['Body'][0]['#']['Fault'][0]['#']['Reason'][0]['#']['Text'][0]['#'])) $result->addError(new Error($data['Envelope']['#']['Body'][0]['#']['Fault'][0]['#']['Reason'][0]['#']['Text'][0]['#'])); if(!empty($data['Envelope']['#']['Body'][0]['#']['Fault'][0]['#']['Detail'][0]['#']['AuthorizationFaultReason'][0]['#'])) $result->addError(new Error($data['Envelope']['#']['Body'][0]['#']['Fault'][0]['#']['Detail'][0]['#']['AuthorizationFaultReason'][0]['#'])); } } return $result; } /** * @param string $trackingNumber * @return StatusResult */ public function getOperationHistory($trackingNumber) { $result = new StatusResult(); $requestData = ' <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:oper="http://russianpost.org/operationhistory" xmlns:data="http://russianpost.org/operationhistory/data" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header/> <soap:Body> <oper:getOperationHistory> <data:OperationHistoryRequest> <data:Barcode>'.$trackingNumber.'</data:Barcode> <data:MessageType>0</data:MessageType> <data:Language>'.$this->lang.'</data:Language> </data:OperationHistoryRequest> <data:AuthorizationHeader soapenv:mustUnderstand="1"> <data:login>'.$this->login.'</data:login> <data:password>'.$this->password.'</data:password> </data:AuthorizationHeader> </oper:getOperationHistory> </soap:Body> </soap:Envelope> '; $res = $this->sendRequest($requestData); if(!$res->isSuccess()) { $result->addErrors($res->getErrors()); return $result; } $lastOperation = $this->getLastOperation($res->getData()); if(!$lastOperation) { $result->addError(new Error(Loc::getMessage('SALE_DELIVERY_TRACKING_RUS_POST_ERROR_LAST_OP'))); } else { $result->status = $this->extractStatus($lastOperation); $result->description = $this->createDescription($trackingNumber); $lastOperationTS = $this->extractLastChangeDate($lastOperation); if($lastOperationTS > 0) $result->lastChangeTimestamp = $this->extractLastChangeDate($lastOperation); $result->trackingNumber = $trackingNumber; } return $result; } /** * @param $lastOperation * @return int */ protected function extractLastChangeDate($lastOperation) { if(empty($lastOperation['#']['OperationParameters'][0]['#']['OperDate'][0]['#'])) return 0; $date = new \DateTime($lastOperation['#']['OperationParameters'][0]['#']['OperDate'][0]['#']); return $date->getTimestamp(); } /** * @param $answer * @return mixed|null */ protected function getLastOperation(array $answer) { $history = $answer['Envelope']['#']['Body'][0]['#']['getOperationHistoryResponse'][0]['#']['OperationHistoryData'][0]['#']['historyRecord']; if(!is_array($history) || empty($history)) return null; if(!$lastOperation = end($history)) return null; return $lastOperation; } /** * @param $lastOperation * @return string */ protected function createDescription($trackingNumber) { $link = 'https://pochta.ru/tracking#'.$trackingNumber; return Loc::getMessage('SALE_DELIVERY_TRACKING_RUS_POST_STATUS_DESCR').': '.'<a href="'.$link.'">'.$link.'</a>'; } /** * @param $lastOperation * @return int */ protected function extractStatus(array $lastOperation) { if(!isset($lastOperation['#']['OperationParameters']['0']['#']['OperType']['0']['#']['Id']['0']['#'])) return Statuses::UNKNOWN; if(!isset($lastOperation['#']['OperationParameters'][0]['#']['OperAttr'][0]['#']['Id'][0]['#'])) return Statuses::UNKNOWN; $oper = $lastOperation['#']['OperationParameters'][0]['#']['OperType'][0]['#']['Id'][0]['#']; $att = $lastOperation['#']['OperationParameters'][0]['#']['OperAttr'][0]['#']['Id'][0]['#']; return $this->mapStatus($oper, $att); } /** * Maps outer operationCode and attributeCode to inner status enumerated in class Statuses * @param $oper * @param $attr * @return int */ protected function mapStatus($oper, $attr) { if(strlen($oper) <= 0) return Statuses::UNKNOWN; /* * if innerStatus1 != innerStatus2 != ....... * * opCode1 => array ( * attrCode1 => innerStatus1 * attrCode2 => innerStatus2 * ... * ) * * if innerStatus1 == innerStatus2 == ....... * * opCode => innerStatus * */ $rusPostStatuses = array( 1 => Statuses::WAITING_SHIPMENT, 2 => array( 1 => Statuses::HANDED, 2 => Statuses::RETURNED, 3 => Statuses::HANDED, 4 => Statuses::RETURNED, 5 => Statuses::HANDED, 6 => Statuses::HANDED, 7 => Statuses::RETURNED, 8 => Statuses::HANDED, 9 => Statuses::RETURNED, 10 => Statuses::HANDED, 11 => Statuses::HANDED, 12 => Statuses::HANDED, ), 3 => Statuses::PROBLEM, 4 => Statuses::ON_THE_WAY, 5 => array( 1 => Statuses::PROBLEM, 2 => Statuses::PROBLEM, 3 => Statuses::PROBLEM, 8 => Statuses::PROBLEM, 9 => Statuses::PROBLEM ), 6 => array( 1 => Statuses::ARRIVED, 2 => Statuses::ARRIVED, 3 => Statuses::ARRIVED, 4 => Statuses::ARRIVED, 5 => Statuses::ON_THE_WAY, ), 7 => Statuses::PROBLEM, 8 => array( 0 => Statuses::ON_THE_WAY, 1 => Statuses::ON_THE_WAY, 2 => Statuses::ARRIVED, 3 => Statuses::ON_THE_WAY, 4 => Statuses::ON_THE_WAY, 5 => Statuses::ON_THE_WAY, 6 => Statuses::ON_THE_WAY, 7 => Statuses::ON_THE_WAY, 8 => Statuses::ON_THE_WAY, 9 => Statuses::ARRIVED, 10 => Statuses::ARRIVED, 11 => Statuses::ON_THE_WAY, 12 => Statuses::ARRIVED, 13 => Statuses::ON_THE_WAY, 14 => Statuses::ARRIVED, 15 => Statuses::ON_THE_WAY, 16 => Statuses::ON_THE_WAY, 17 => Statuses::ON_THE_WAY, 18 => Statuses::ON_THE_WAY, 19 => Statuses::ON_THE_WAY, ), 9 => Statuses::ON_THE_WAY, 10 => Statuses::ON_THE_WAY, 11 => Statuses::ON_THE_WAY, 12 => array( 1 => Statuses::ARRIVED, 2 => Statuses::ARRIVED, 3 => Statuses::PROBLEM, 4 => Statuses::PROBLEM, 5 => Statuses::PROBLEM, 6 => Statuses::PROBLEM, 7 => Statuses::PROBLEM, 8 => Statuses::PROBLEM, 9 => Statuses::ARRIVED, 10 => Statuses::PROBLEM, 11 => Statuses::ARRIVED, 12 => Statuses::PROBLEM, 13 => Statuses::PROBLEM, 14 => Statuses::PROBLEM, 15 => Statuses::ARRIVED, 16 => Statuses::PROBLEM, 17 => Statuses::ARRIVED, 18 => Statuses::ARRIVED, 19 => Statuses::PROBLEM, 20 => Statuses::PROBLEM, 21 => Statuses::PROBLEM, 22 => Statuses::ARRIVED, 23 => Statuses::PROBLEM, 24 => Statuses::PROBLEM, 25 => Statuses::ARRIVED, 26 => Statuses::PROBLEM, 27 => Statuses::ARRIVED, 28 => Statuses::PROBLEM, ), 13 => Statuses::ON_THE_WAY, 14 => Statuses::ON_THE_WAY, 15 => Statuses::ARRIVED, 16 => Statuses::PROBLEM, 17 => Statuses::ARRIVED, 18 => Statuses::PROBLEM, 19 => Statuses::ON_THE_WAY, 20 => Statuses::ON_THE_WAY, 21 => Statuses::ON_THE_WAY, 22 => Statuses::PROBLEM, 23 => Statuses::ON_THE_WAY, 24 => Statuses::PROBLEM, 25 => Statuses::ON_THE_WAY, 26 => Statuses::PROBLEM, 27 => Statuses::ON_THE_WAY, 28 => Statuses::NO_INFORMATION, 29 => Statuses::ON_THE_WAY, 30 => Statuses::ON_THE_WAY, 31 => Statuses::ON_THE_WAY, 32 => Statuses::ON_THE_WAY, 33 => Statuses::ON_THE_WAY, ); if(!isset($rusPostStatuses[$oper])) return Statuses::UNKNOWN; if(!is_array($rusPostStatuses[$oper])) return $rusPostStatuses[$oper]; if(strlen($attr) <= 0) return Statuses::UNKNOWN; if(!isset($rusPostStatuses[$oper][$attr])) return Statuses::UNKNOWN; return $rusPostStatuses[$oper][$attr]; } }