diff --git a/README.md b/README.md index 8b3459d..e581d18 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,13 @@ require '/src/Delivery/NovaPoshtaApi2.php'; Класс по умолчанию находится в namespace `\LisDev\Delivery`. При создании экземпляра класса необходимо или использовать Full Qualified Class Name: ```php -$np = new \LisDev\Delivery\NovaPoshtaApi2('Ваш_ключ_API_2.0'); +$np = new \LisDev\NovaPoshtaClient(['apiKey' => 'Ваш_ключ_API_2.0']); ``` или указать используемый namespace в секции use: ```php -use LisDev\Delivery\NovaPoshtaApi2; +use LisDev\NovaPoshtaClient; ... -$np = new NovaPoshtaApi2('Ваш_ключ_API_2.0'); +$np = new NovaPoshtaClient(['apiKey' => 'Ваш_ключ_API_2.0']); ``` Более подробную информацию по работе с namespace можно получить [на сайте документации php](https://www.php.net/manual/ru/language.namespaces.rationale.php) @@ -73,148 +73,58 @@ $np = new NovaPoshtaApi2('Ваш_ключ_API_2.0'); ## Создание экземпляра класса (с расширенными параметрами) Рекомендуется использовать, если необходимо получать данные на языке, отличном от русского, выбрасывать Exception при ошибке запроса, или при отсутствии установленной библиотеки curl на сервере ```php -$np = new NovaPoshtaApi2( - 'Ваш_ключ_API_2.0', - 'ru', // Язык возвращаемых данных: ru (default) | ua | en - FALSE, // При ошибке в запросе выбрасывать Exception: FALSE (default) | TRUE - 'curl' // Используемый механизм запроса: curl (defalut) | file_get_content +$np =new NovaPoshtaClient([ + 'apiKey' => 'YOUR_KEY', + 'language' => \LisDev\Language::Ru, + 'format' => \LisDev\DataFormat::Json, + 'connectionType' => \LisDev\ConnectionType::Curl, + 'throwErrors' => false ); ``` ## Получение информации о трек-номере ```php -$result = $np->documentsTracking('59000000000000'); +$result = $np->trackingDocument->documentsTracking('59000000000000'); ``` ## Получение сроков доставки ```php // Получение кода города по названию города и области -$sender_city = $np->getCity('Белгород-Днестровский', 'Одесская'); +$sender_city = $np->address->getCity('Белгород-Днестровский', 'Одесская'); $sender_city_ref = $sender_city['data'][0]['Ref']; // Получение кода города по названию города и области -$recipient_city = $np->getCity('Киев', 'Киевская'); +$recipient_city = $np->address->getCity('Киев', 'Киевская'); $recipient_city_ref = $recipient_city['data'][0]['Ref']; // Дата отправки груза $date = date('d.m.Y'); // Получение ориентировочной даты прибытия груза между складами в разных городах -$result = $np->getDocumentDeliveryDate($sender_city_ref, $recipient_city_ref, 'WarehouseWarehouse', $date); +$result = $np->internetDocument->getDocumentDeliveryDate($sender_city_ref, $recipient_city_ref, 'WarehouseWarehouse', $date); ``` ## Получение стоимости доставки ```php // Получение кода города по названию города и области -$sender_city = $np->getCity('Белгород-Днестровский', 'Одесская'); +$sender_city = $np->address->getCity('Белгород-Днестровский', 'Одесская'); $sender_city_ref = $sender_city['data'][0]['Ref']; // Получение кода города по названию города и области -$recipient_city = $np->getCity('Киев', 'Киевская'); +$recipient_city = $np->address->getCity('Киев', 'Киевская'); $recipient_city_ref = $recipient_city['data'][0]['Ref']; // Вес товара $weight = 7; // Цена в грн $price = 5450; // Получение стоимости доставки груза с указанным весом и стоимостью между складами в разных городах -$result = $np->getDocumentPrice($sender_city_ref, $recipient_city_ref, 'WarehouseWarehouse', $weight, $price); -``` -## Генерирование новой электронной накладной -```php -// Перед генерированием ЭН необходимо получить данные отправителя -// Получение всех отправителей -$senderInfo = $np->getCounterparties('Sender', 1, '', ''); -// Выбор отправителя в конкретном городе (в данном случае - в первом попавшемся) -$sender = $senderInfo['data'][0]; -// Информация о складе отправителя -$senderWarehouses = $np->getWarehouses($sender['City']); -// Генерирование новой накладной -$result = $np->newInternetDocument( - // Данные отправителя - array( - // Данные пользователя - 'FirstName' => $sender['FirstName'], - 'MiddleName' => $sender['MiddleName'], - 'LastName' => $sender['LastName'], - // Вместо FirstName, MiddleName, LastName можно ввести зарегистрированные ФИО отправителя или название фирмы для юрлиц - // (можно получить, вызвав метод getCounterparties('Sender', 1, '', '')) - // 'Description' => $sender['Description'], - // Необязательное поле, в случае отсутствия будет использоваться из данных контакта - // 'Phone' => '0631112233', - // Город отправления - // 'City' => 'Белгород-Днестровский', - // Область отправления - // 'Region' => 'Одесская', - 'CitySender' => $sender['City'], - // Отделение отправления по ID (в данном случае - в первом попавшемся) - 'SenderAddress' => $senderWarehouses['data'][0]['Ref'], - // Отделение отправления по адресу - // 'Warehouse' => $senderWarehouses['data'][0]['DescriptionRu'], - ), - // Данные получателя - array( - 'FirstName' => 'Сидор', - 'MiddleName' => 'Сидорович', - 'LastName' => 'Сиродов', - 'Phone' => '0509998877', - 'City' => 'Киев', - 'Region' => 'Киевская', - 'Warehouse' => 'Отделение №3: ул. Калачевская, 13 (Старая Дарница)', - ), - array( - // Дата отправления - 'DateTime' => date('d.m.Y'), - // Тип доставки, дополнительно - getServiceTypes() - 'ServiceType' => 'WarehouseWarehouse', - // Тип оплаты, дополнительно - getPaymentForms() - 'PaymentMethod' => 'Cash', - // Кто оплачивает за доставку - 'PayerType' => 'Recipient', - // Стоимость груза в грн - 'Cost' => '500', - // Кол-во мест - 'SeatsAmount' => '1', - // Описание груза - 'Description' => 'Кастрюля', - // Тип доставки, дополнительно - getCargoTypes - 'CargoType' => 'Cargo', - // Вес груза - 'Weight' => '10', - // Объем груза в куб.м. - 'VolumeGeneral' => '0.5', - // Обратная доставка - 'BackwardDeliveryData' => array( - array( - // Кто оплачивает обратную доставку - 'PayerType' => 'Recipient', - // Тип доставки - 'CargoType' => 'Money', - // Значение обратной доставки - 'RedeliveryString' => 4552, - ) - ) - ) -); +$result = $np->internetDocument->getDocumentPrice($sender_city_ref, $recipient_city_ref, 'WarehouseWarehouse', $weight, $price); ``` ## Получение складов в определенном городе ```php // В параметрах указывается город и область (для более точного поиска) -$city = $np->getCity('Киев', 'Киевская'); -$result = $np->getWarehouses($city['data'][0]['Ref']); -``` -## Вызов произвольного метода -```php -$result = $np - ->model('Имя_модели') - ->method('Имя_метода') - ->params(array( - 'Имя_параметра_1' => 'Значение_параметра_1', - 'Имя_параметра_2' => 'Значение_параметра_2', - )) - ->execute(); +$city = $np->address->getCity('Киев', 'Киевская'); +$result = $np->address->getWarehouses($city['data'][0]['Ref']); ``` # Реализованные методы для работы с моделями ## Модель InternetDocument -* save -* update -* delete * getDocumentPrice * getDocumentDeliveryDate * getDocumentList @@ -222,13 +132,9 @@ $result = $np * printDocument * printMarkings * documentsTracking -* newInternetDocument * generateReport ## Модель Counterparty -* save -* update -* delete * cloneLoyaltyCounterpartySender * getCounterparties * getCounterpartyAddresses @@ -236,15 +142,7 @@ $result = $np * getCounterpartyByEDRPOU * getCounterpartyOptions -## Модель ContactPerson -* save -* update -* delete - ## Модель Address -* save -* update -* delete * getCities * getStreet * getWarehouses @@ -267,11 +165,3 @@ $result = $np * getTypesOfPayers * getTypesOfPayersForRedelivery -# Тесты -Актуальные тесты и примеры использования класса находятся в файле `tests/NovaPoshtaApi2Test.php` - -Для запуска тестов локально необходимо выполнить в командной строке -``` -composer install -NOVA_POSHTA_API2_KEY=Ваш_ключ_API_2.0 vendor/phpunit/phpunit/phpunit tests -``` diff --git a/composer.json b/composer.json index e6773ad..4d448d4 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,9 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=8.1", + "ext-simplexml": "*", + "ext-mbstring": "*" }, "require-dev": { "phpunit/phpunit": "~4.4" diff --git a/src/ApiDefaultDataPreparator.php b/src/ApiDefaultDataPreparator.php new file mode 100644 index 0000000..f18be01 --- /dev/null +++ b/src/ApiDefaultDataPreparator.php @@ -0,0 +1,30 @@ +prepareUrl(); + $data = $this->prepareData($model, $method, $params); + $result = []; + if ($this->config['format'] === DataFormat::Xml) { + $post = $this->arrayToXml($data); + } else { + $post = json_encode($data); + } + + if (ConnectionType::Curl === $this->config['connectionType']) { + $ch = curl_init($url); + if (is_resource($ch)) { + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt( + $ch, + CURLOPT_HTTPHEADER, + ['Content-Type: '.(DataFormat::Xml === $this->config['format'] ? 'text/xml' : 'application/json')] + ); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + + if ($this->timeout > 0) { + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); + } + + $result = curl_exec($ch); + curl_close($ch); + } + } else { + $httpOptions = [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded;\r\n", + 'content' => $post, + ]; + + if ($this->config['timeout'] > 0) { + $httpOptions['timeout'] = $this->config['timeout']; + } + + $result = file_get_contents( + $url, + false, + stream_context_create(array( + 'http' => $httpOptions, + )) + ); + } + + return $result; + } + + private function prepareUrl(): string + { + return ($this->config['format'] === DataFormat::Xml) ? $this->apiUrl.'/xml/' : $this->apiUrl.'/json/'; + } + + private function prepareData(string $model, string $method, ?array $params): array + { + return [ + 'apiKey' => $this->apiKey, + 'modelName' => $model, + 'calledMethod' => $method, + 'language' => $this->config['language']->value, + 'methodProperties' => $params, + ]; + } + + /** + * @param array $array + * @param \SimpleXMLElement|null $xml + * @return bool|string + */ + private function arrayToXml(array $array, \SimpleXMLElement $xml = null): bool|string + { + (null === $xml) and $xml = new \SimpleXMLElement(''); + foreach ($array as $key => $value) { + if (is_numeric($key)) { + $key = 'item'; + } + if (is_array($value)) { + $this->arrayToXml($value, $xml->addChild($key)); + } else { + $xml->addChild($key, $value); + } + } + + return $xml->asXML(); + } +} \ No newline at end of file diff --git a/src/AreasList.php b/src/AreasList.php new file mode 100644 index 0000000..8768ae2 --- /dev/null +++ b/src/AreasList.php @@ -0,0 +1,155 @@ + [ + 'Description' => 'Вінниця', + 'DescriptionRu' => 'Винница', + 'Area' => 'Вінницька', + 'AreaRu' => 'Винницкая', + ], + '7150812b-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Дніпропетровськ', + 'DescriptionRu' => 'Днепропетровск', + 'Area' => 'Дніпропетровська', + 'AreaRu' => 'Днепропетровская', + ], + '7150812c-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Донецьк', + 'DescriptionRu' => 'Донецк', + 'Area' => 'Донецька', + 'AreaRu' => 'Донецкая', + ], + '7150812d-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Житомир', + 'DescriptionRu' => 'Житомир', + 'Area' => 'Житомирська', + 'AreaRu' => 'Житомирская', + ], + '7150812f-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Запоріжжя', + 'DescriptionRu' => 'Запорожье', + 'Area' => 'Запорізька', + 'AreaRu' => 'Запорожская', + ], + '71508130-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Івано-Франківськ', + 'DescriptionRu' => 'Ивано-Франковск', + 'Area' => 'Івано-Франківська', + 'AreaRu' => 'Ивано-Франковская', + ], + '71508131-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Київ', + 'DescriptionRu' => 'Киев', + 'Area' => 'Київська', + 'AreaRu' => 'Киевская', + ], + '71508132-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Кіровоград', + 'DescriptionRu' => 'Кировоград', + 'Area' => 'Кіровоградська', + 'AreaRu' => 'Кировоградская', + ], + '71508133-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Луганськ', + 'DescriptionRu' => 'Луганск', + 'Area' => 'Луганська', + 'AreaRu' => 'Луганская', + ], + '7150812a-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Луцьк', + 'DescriptionRu' => 'Луцк', + 'Area' => 'Волинська', + 'AreaRu' => 'Волынская', + ], + '71508134-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Львів', + 'DescriptionRu' => 'Львов', + 'Area' => 'Львівська', + 'AreaRu' => 'Львовская', + ], + '71508135-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Миколаїв', + 'DescriptionRu' => 'Николаев', + 'Area' => 'Миколаївська', + 'AreaRu' => 'Николаевская', + ], + '71508136-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Одеса', + 'DescriptionRu' => 'Одесса', + 'Area' => 'Одеська', + 'AreaRu' => 'Одесская', + ], + '71508137-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Полтава', + 'DescriptionRu' => 'Полтава', + 'Area' => 'Полтавська', + 'AreaRu' => 'Полтавская', + ], + '71508138-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Рівне', + 'DescriptionRu' => 'Ровно', + 'Area' => 'Рівненська', + 'AreaRu' => 'Ровненская', + ], + '71508139-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Суми', + 'DescriptionRu' => 'Сумы', + 'Area' => 'Сумська', + 'AreaRu' => 'Сумская', + ], + '7150813a-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Тернопіль', + 'DescriptionRu' => 'Тернополь', + 'Area' => 'Тернопільська', + 'AreaRu' => 'Тернопольская', + ], + '7150812e-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Ужгород', + 'DescriptionRu' => 'Ужгород', + 'Area' => 'Закарпатська', + 'AreaRu' => 'Закарпатская', + ], + '7150813b-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Харків', + 'DescriptionRu' => 'Харьков', + 'Area' => 'Харківська', + 'AreaRu' => 'Харьковская', + ], + '7150813c-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Херсон', + 'DescriptionRu' => 'Херсон', + 'Area' => 'Херсонська', + 'AreaRu' => 'Херсонская', + ], + '7150813d-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Хмельницький', + 'DescriptionRu' => 'Хмельницкий', + 'Area' => 'Хмельницька', + 'AreaRu' => 'Хмельницкая', + ], + '7150813e-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Черкаси', + 'DescriptionRu' => 'Черкассы', + 'Area' => 'Черкаська', + 'AreaRu' => 'Черкасская', + ], + '71508140-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Чернігів', + 'DescriptionRu' => 'Чернигов', + 'Area' => 'Чернігівська', + 'AreaRu' => 'Черниговская', + ], + '7150813f-9b87-11de-822f-000c2965ae0e' => [ + 'Description' => 'Чернівці', + 'DescriptionRu' => 'Черновцы', + 'Area' => 'Чернівецька', + 'AreaRu' => 'Черновицкая', + ], + ]; +} \ No newline at end of file diff --git a/src/BaseNovaPoshtaClient.php b/src/BaseNovaPoshtaClient.php new file mode 100644 index 0000000..14cf759 --- /dev/null +++ b/src/BaseNovaPoshtaClient.php @@ -0,0 +1,106 @@ +getDefaultConfig(), $config); + $this->validateConfig($config); + $this->config = $config; + } + + public function getApiKey(): ?string + { + return $this->config['apiKey']; + } + + public function getApiUrl(): string + { + return $this->config['apiUrl']; + } + + public function isThrowErrors(): bool + { + return $this->config['throwErrors']; + } + + public function getFormat(): DataFormat + { + return $this->config['format']; + } + + public function getLanguage(): Language + { + return $this->config['language']; + } + + public function getConnectionType(): ConnectionType + { + return $this->config['connectionType']; + } + + public function getTimeout(): int + { + return $this->config['timeout']; + } + + public function getModel(): Model + { + return $this->config['model']; + } + + public function request(string $model, string $method, array $params = null) + { + $requestor = new ApiRequestor($this->getApiKey(), $this->getApiUrl(), $this->config); + $result = $requestor->request($model, $method, $params); + $preparator = new ApiDefaultDataPreparator(); + $result = $preparator->prepare($result, $this->getFormat(), $this->isThrowErrors()); + + return $result; + } + + protected function getDefaultConfig(): array + { + return [ + 'apiUrl' => self::DEFAULT_API_URL, + 'apiKey' => '', + 'throwErrors' => false, + 'format' => DataFormat::Array, + 'language' => Language::Ru, + 'connectionType' => ConnectionType::Curl, + 'timeout' => 0, + 'model' => self::DEFAULT_MODEL, + ]; + } + + private function validateConfig(array $config): void + { + if (null !== ($config['apiKey'])) { + if (!is_string($config['apiKey'])) { + throw new InvalidArgumentException('apiKey must be null or a string'); + } + if ('' === $config['apiKey']) { + throw new InvalidArgumentException('apiKey cannot be the empty string'); + } + if (preg_match('/\s/', $config['apiKey'])) { + throw new InvalidArgumentException('apiKey cannot contain whitespace'); + } + } + if (!is_string($config['apiUrl'])) { + throw new InvalidArgumentException('apiUrl must be a string'); + } + } +} \ No newline at end of file diff --git a/src/BaseNovaPoshtaClientInterface.php b/src/BaseNovaPoshtaClientInterface.php new file mode 100644 index 0000000..783df01 --- /dev/null +++ b/src/BaseNovaPoshtaClientInterface.php @@ -0,0 +1,27 @@ +coreServiceFactory) { + $this->coreServiceFactory = new CoreServiceFactory($this); + } + + return $this->coreServiceFactory->__get($name); + } +} \ No newline at end of file diff --git a/src/NovaPoshtaClientInterface.php b/src/NovaPoshtaClientInterface.php new file mode 100644 index 0000000..c7d8c64 --- /dev/null +++ b/src/NovaPoshtaClientInterface.php @@ -0,0 +1,17 @@ +client; + } + + protected function request(Model $model, string $method, array $params = null) + { + return $this->getClient()->request($model->value, $method, static::formatParams($params)); + } + + private static function formatParams(array $params) + { + if (null === $params) { + return null; + } + array_walk_recursive($params, function (&$value, $key) { + if (null === $value) { + $value = ''; + } + }); + + return $params; + } +} \ No newline at end of file diff --git a/src/Service/AbstractServiceFactory.php b/src/Service/AbstractServiceFactory.php new file mode 100644 index 0000000..b947eb1 --- /dev/null +++ b/src/Service/AbstractServiceFactory.php @@ -0,0 +1,38 @@ +services = []; + } + + abstract protected function getServiceClass(string $name); + + public function __get(string $name) + { + $serviceClass = $this->getServiceClass($name); + if (null !== $serviceClass) { + if (!\array_key_exists($name, $this->services)) { + $this->services[$name] = new $serviceClass($this->client); + } + + return $this->services[$name]; + } + + trigger_error('Undefined property: '.static::class.'::$'.$name); + + return null; + } +} \ No newline at end of file diff --git a/src/Service/AddressService.php b/src/Service/AddressService.php new file mode 100644 index 0000000..52f7aa4 --- /dev/null +++ b/src/Service/AddressService.php @@ -0,0 +1,263 @@ +request(Model::Address, 'getCities', [ + 'Page' => $page, + 'FindByString' => $findByString, + 'Ref' => $ref, + ]); + } + + /** + * Get warehouses by city. + * @param string $cityRef + * @param int $page + * @return mixed + */ + public function getWarehouses(string $cityRef, int $page = 0) + { + return $this->request(Model::Address, 'getWarehouses', [ + 'CityRef' => $cityRef, + 'Page' => $page, + ]); + } + + /** + * Get warehouse types. + * @return mixed + */ + public function getWarehouseTypes() + { + return $this->request(Model::Address, 'getWarehouseTypes'); + } + + /** + * Get 5 nearest warehouses by array of strings. + * @param array $searchStringArray + * @return mixed + */ + public function findNearestWarehouse(array $searchStringArray) + { + return $this->request(Model::Address, 'findNearestWarehouse', [ + 'SearchStringArray' => $searchStringArray, + ]); + } + + /** + * Get streets list by city and/or search string. + * @param string $cityRef + * @param int $page + * @param string $findByString + * @return mixed + */ + public function getStreet(string $cityRef, int $page = 0, string $findByString = '') + { + return $this->request(Model::Address, 'getStreet', [ + 'FindByString' => $findByString, + 'CityRef' => $cityRef, + 'Page' => $page, + ]); + } + + /** + * Get areas list by city and/or search string. + * @param string $ref + * @param int $page + * @return mixed + */ + public function getAreas(string $ref = '', int $page = 0) + { + return $this->request(Model::Address, 'getAreas', [ + 'Ref' => $ref, + 'Page' => $page, + ]); + } + + /** + * Get area by name or by ID. + * @param string $findByString Find area by russian or ukrainian word + * @param string $ref Get area by ID + * @return array + * @throws \Exception + */ + public function getArea(string $findByString = '', string $ref = ''): array + { + $areas = AreasList::AREAS; + $data = $this->findArea($areas, $findByString, $ref); + // Error + $error = []; + empty($data) and $error = ['Area was not found']; + + // Return data in same format like NovaPoshta API + + return (new ApiDefaultDataPreparator())->prepare([ + 'success' => empty($error), + 'data' => [$data], + 'errors' => (array)$error, + 'warnings' => [], + 'info' => [], + ], $this->client->getFormat(), $this->client->isThrowErrors()); + } + + /** + * Get one warehouse by city name and warehouse's description. + * @param string $cityRef + * @param string $description + * @return array + * @throws \Exception + */ + public function getWarehouse(string $cityRef, string $description = '') + { + $warehouses = $this->getWarehouses($cityRef); + $error = []; + $data = []; + if (is_array($warehouses['data'])) { + $data = $warehouses['data'][0]; + if (count($warehouses['data']) > 1 && $description) { + foreach ($warehouses['data'] as $warehouse) { + if (false !== mb_stripos($warehouse['Description'], $description) + or false !== mb_stripos($warehouse['DescriptionRu'], $description)) { + $data = $warehouse; + break; + } + } + } + } + // Error + (!$data) and $error = 'Warehouse was not found'; + + // Return data in same format like NovaPoshta API + return (new ApiDefaultDataPreparator())->prepare([ + 'success' => empty($error), + 'data' => [$data], + 'errors' => (array)$error, + 'warnings' => [], + 'info' => [], + ], $this->client->getFormat(), $this->client->isThrowErrors()); + } + + /** + * Get city by name and region (if it needs). + * @param string $cityName + * @param string $areaName + * @param string $warehouseDescription + * @return mixed + * @throws \Exception + */ + public function getCity(string $cityName, string $areaName = '', string $warehouseDescription = '') + { + // Get cities by name + $cities = $this->getCities($cityName); + $data = []; + if (is_array($cities) && is_array($cities['data'])) { + // If cities more then one, calculate current by area name + $data = (count($cities['data']) > 1) + ? $this->findCityByRegion($cities, $areaName) + : array($cities['data'][0]); + } + // Try to identify city by one of warehouses descriptions + if (count($data) > 1 && $warehouseDescription) { + foreach ($data as $cityData) { + $warehouseData = $this->getWarehouse($cityData['Ref'], $warehouseDescription); + $warehouseDescriptions = [ + $warehouseData['data'][0]['Description'], + $warehouseData['data'][0]['DescriptionRu'], + ]; + if (in_array($warehouseDescription, $warehouseDescriptions, true)) { + $data = [$cityData]; + break; + } + } + } + // Error + $error = []; + (!$data) and $error = ['City was not found']; + + // Return data in same format like NovaPoshta API + return (new ApiDefaultDataPreparator())->prepare([ + 'success' => empty($error), + 'data' => [$data], + 'errors' => (array)$error, + 'warnings' => [], + 'info' => [], + ], $this->client->getFormat(), $this->client->isThrowErrors()); + } + + /** + * Find city from list by name of region. + * @param array $cities + * @param string $areaName + * @return array + * @throws \Exception + */ + protected function findCityByRegion(array $cities, string $areaName) + { + $data = []; + $areaRef = ''; + // Get region id + $area = $this->getArea($areaName); + $area['success'] and $areaRef = $area['data'][0]['Ref']; + if ($areaRef and is_array($cities['data'])) { + foreach ($cities['data'] as $city) { + if ($city['Area'] === $areaRef) { + $data[] = $city; + } + } + } + + return $data; + } + + /** + * Find current area in list of areas. + * @param array $areas + * @param string $findByString + * @param string $ref + * @return array + */ + protected function findArea(array $areas, string $findByString = '', string $ref = ''): array + { + $data = []; + if (!$findByString and !$ref) { + return $data; + } + // Try to find current region + foreach ($areas as $key => $area) { + // Is current area found by string or by key + $found = $findByString + ? ((false !== mb_stripos($area['Description'], $findByString)) + or (false !== mb_stripos($area['DescriptionRu'], $findByString)) + or (false !== mb_stripos($area['Area'], $findByString)) + or (false !== mb_stripos($area['AreaRu'], $findByString))) + : ($key === $ref); + if ($found) { + $area['Ref'] = $key; + $data[] = $area; + break; + } + } + + return $data; + } + + +} \ No newline at end of file diff --git a/src/Service/CommonService.php b/src/Service/CommonService.php new file mode 100644 index 0000000..99b701d --- /dev/null +++ b/src/Service/CommonService.php @@ -0,0 +1,59 @@ +request(Model::Common, $method, null); + } + } +} \ No newline at end of file diff --git a/src/Service/CoreServiceFactory.php b/src/Service/CoreServiceFactory.php new file mode 100644 index 0000000..4d3eff5 --- /dev/null +++ b/src/Service/CoreServiceFactory.php @@ -0,0 +1,21 @@ +request(Model::Counterparty, 'getCounterparties', $params); + } + + /** + * cloneLoyaltyCounterpartySender() function of model Counterparty + * The counterparty will be not created immediately, you can wait a long time + * @param string $cityRef + * @return mixed + */ + public function cloneLoyaltyCounterpartySender(string $cityRef) + { + return $this->request(Model::Counterparty, 'cloneLoyaltyCounterpartySender', ['CityRef' => $cityRef]); + } + + /** + * getCounterpartyContactPersons() function of model Counterparty. + * @param string $ref + * @return mixed + */ + public function getCounterpartyContactPersons(string $ref) + { + return $this->request(Model::Counterparty, 'getCounterpartyContactPersons', ['ref' => $ref]); + } + + /** + * getCounterpartyAddresses() function of model Counterparty. + * @param string $ref + * @param int $page + * @return mixed + */ + public function getCounterpartyAddresses(string $ref, int $page = 0) + { + return $this->request(Model::Counterparty, 'getCounterpartyContactPersons', ['Ref' => $ref, 'Page' => $page]); + } + + /** + * getCounterpartyOptions() function of model Counterparty. + * @param $ref + * @return mixed + */ + public function getCounterpartyOptions($ref) + { + return $this->request(Model::Counterparty, 'getCounterpartyOptions', ['Ref' => $ref]); + } + + /** + * getCounterpartyByEDRPOU() function of model Counterparty. + * @param string $edrpou + * @param string $cityRef + * @return mixed + */ + public function getCounterpartyByEDRPOU(string $edrpou, string $cityRef) + { + return $this->request( + Model::Counterparty, + 'getCounterpartyByEDRPOU', + ['EDRPOU' => $edrpou, 'cityRef' => $cityRef] + ); + } +} \ No newline at end of file diff --git a/src/Service/InternetDocumentService.php b/src/Service/InternetDocumentService.php new file mode 100644 index 0000000..1208674 --- /dev/null +++ b/src/Service/InternetDocumentService.php @@ -0,0 +1,157 @@ +request(Model::InternetDocument, 'getDocumentPrice', [ + 'CitySender' => $citySender, + 'CityRecipient' => $cityRecipient, + 'ServiceType' => $serviceType, + 'Weight' => $weight, + 'Cost' => $cost, + ]); + } + + /** + * Get approximately date of delivery between two cities. + * @param $citySender + * @param $cityRecipient + * @param $serviceType + * @param $dateTime + * @return mixed + */ + public function getDocumentDeliveryDate($citySender, $cityRecipient, $serviceType, $dateTime) + { + return $this->request(Model::InternetDocument, 'getDocumentDeliveryDate', array( + 'CitySender' => $citySender, + 'CityRecipient' => $cityRecipient, + 'ServiceType' => $serviceType, + 'DateTime' => $dateTime, + )); + } + + /** + * Get documents list. + * @param array|null $params + * @return mixed + */ + public function getDocumentList(array $params = null) + { + return $this->request(Model::InternetDocument, 'getDocumentList', $params); + } + + /** + * Get document info by ID. + * @param $ref + * @return mixed + */ + public function getDocument($ref) + { + return $this->request(Model::InternetDocument, 'getDocument', array( + 'Ref' => $ref, + )); + } + + /** + * Generetes report by Document refs. + * @param array $params + * @return mixed + */ + public function generateReport(array $params) + { + return $this->request(Model::InternetDocument, 'generateReport', $params); + } + + /** + * printDocument method of InternetDocument model. + * @param array $documentRefs + * @param InternetDocumentType $type + * @return array + */ + public function printDocument(array $documentRefs, InternetDocumentType $type = InternetDocumentType::Html) + { + // If needs link + if (InternetDocumentType::HtmlLink === $type || InternetDocumentType::PdfLink === $type) { + return $this->printGetLink('printDocument', $documentRefs, $type->value); + } + + // If needs data + return $this->request( + Model::InternetDocument, + 'printDocument', + array('DocumentRefs' => $documentRefs, 'Type' => $type) + ); + } + + /** + * printMarkings method of InternetDocument model. + * @param array $documentRefs + * @param PrintMarkingType $type + * @param string $size + * @return array + */ + public function printMarkings( + array $documentRefs, + PrintMarkingType $type = PrintMarkingType::NewHtml, + string $size = '85x85' + ) { + $documentSize = $size === '85x85' ? '85x85' : '100x100'; + $method = 'printMarking'.$documentSize; + // If needs link + if (PrintMarkingType::HtmlLink === $type || PrintMarkingType::PdfLink === $type) { + return $this->printGetLink($method, $documentRefs, $type->value); + } + + // If needs data + return $this->request(Model::InternetDocument, $method, array('DocumentRefs' => $documentRefs, 'Type' => $type) + ); + } + + /** + * Get only link on internet document for printing + * @param string $method + * @param array $documentRefs + * @param string $type + * @return array + */ + protected function printGetLink(string $method, array $documentRefs, string $type) + { + $data = 'https://my.novaposhta.ua/orders/'.$method.'/orders[]/'.implode(',', $documentRefs) + .'/type/'.str_replace('_link', '', $type) + .'/apiKey/'.$this->client->getApiKey(); + + // Return data in same format like NovaPoshta API + return (new ApiDefaultDataPreparator())->prepare([ + 'success' => true, + 'data' => [$data], + 'errors' => [], + 'warnings' => [], + 'info' => [], + + ], $this->client->getFormat(), $this->client->isThrowErrors()); + } +} \ No newline at end of file diff --git a/src/Service/TrackingDocumentService.php b/src/Service/TrackingDocumentService.php new file mode 100644 index 0000000..d29a1e7 --- /dev/null +++ b/src/Service/TrackingDocumentService.php @@ -0,0 +1,24 @@ +request( + Model::TrackingDocument, + 'getStatusDocuments', + ['Documents' => [['DocumentNumber' => $track]]] + ); + } +} \ No newline at end of file diff --git a/tests/NovaPoshta/BaseNovaPoshtaClientTest.php b/tests/NovaPoshta/BaseNovaPoshtaClientTest.php new file mode 100644 index 0000000..58a2a5e --- /dev/null +++ b/tests/NovaPoshta/BaseNovaPoshtaClientTest.php @@ -0,0 +1,14 @@ +assertNotNull($client); + $this->assertNull($client->getApiKey()); + } + + //todo: test for exceptions +} \ No newline at end of file diff --git a/tests/NovaPoshta/NovaPoshtaClientTest.php b/tests/NovaPoshta/NovaPoshtaClientTest.php new file mode 100644 index 0000000..9953466 --- /dev/null +++ b/tests/NovaPoshta/NovaPoshtaClientTest.php @@ -0,0 +1,16 @@ + 'YOUR_KEY']); + $this->assertInstanceOf(\LisDev\Service\AddressService::class, $client->address); + $this->assertInstanceOf(\LisDev\Service\CommonService::class, $client->common); + $this->assertInstanceOf(\LisDev\Service\CounterpartyService::class, $client->counterparty); + $this->assertInstanceOf(\LisDev\Service\InternetDocumentService::class, $client->internetDocument); + $this->assertInstanceOf(\LisDev\Service\TrackingDocumentService::class, $client->trackingDocument); + } +} \ No newline at end of file diff --git a/tests/NovaPoshta/Service/AddressServiceTest.php b/tests/NovaPoshta/Service/AddressServiceTest.php new file mode 100644 index 0000000..03d5d0e --- /dev/null +++ b/tests/NovaPoshta/Service/AddressServiceTest.php @@ -0,0 +1,21 @@ +client = new \LisDev\NovaPoshtaClient(['apiKey' => 'YOUR_KEY']); + parent::setUp(); + } + + public function testGetWarehouses() + { + $result = $this->client->address->getWarehouses('a9280688-94c0-11e3-b441-0050568002cf'); + $this->assertTrue($result['success']); + } + + //todo: transfer all test +} \ No newline at end of file diff --git a/tests/NovaPoshta/Service/CommonServiceTest.php b/tests/NovaPoshta/Service/CommonServiceTest.php new file mode 100644 index 0000000..9a530d9 --- /dev/null +++ b/tests/NovaPoshta/Service/CommonServiceTest.php @@ -0,0 +1,15 @@ +client = new \LisDev\NovaPoshtaClient(['apiKey' => 'YOUR_KEY']); + parent::setUp(); + } + + //todo: write tests +} \ No newline at end of file diff --git a/tests/NovaPoshta/Service/CounterpartyServiceTest.php b/tests/NovaPoshta/Service/CounterpartyServiceTest.php new file mode 100644 index 0000000..7653c2c --- /dev/null +++ b/tests/NovaPoshta/Service/CounterpartyServiceTest.php @@ -0,0 +1,16 @@ +client = new \LisDev\NovaPoshtaClient(['apiKey' => 'YOUR_KEY']); + parent::setUp(); + } + + //todo: write tests + +} \ No newline at end of file diff --git a/tests/NovaPoshta/Service/InternetDocumentServiceTest.php b/tests/NovaPoshta/Service/InternetDocumentServiceTest.php new file mode 100644 index 0000000..836d075 --- /dev/null +++ b/tests/NovaPoshta/Service/InternetDocumentServiceTest.php @@ -0,0 +1,16 @@ +client = new \LisDev\NovaPoshtaClient(['apiKey' => 'YOUR_KEY']); + parent::setUp(); + } + + //todo: write tests + +} \ No newline at end of file diff --git a/tests/NovaPoshta/Service/TrackingDocumentServiceTest.php b/tests/NovaPoshta/Service/TrackingDocumentServiceTest.php new file mode 100644 index 0000000..1853470 --- /dev/null +++ b/tests/NovaPoshta/Service/TrackingDocumentServiceTest.php @@ -0,0 +1,16 @@ +client = new \LisDev\NovaPoshtaClient(['apiKey' => 'YOUR_KEY']); + parent::setUp(); + } + + // todo: write tests + +} \ No newline at end of file